2010-08-08 10:06:34 +02:00
|
|
|
|
/*
|
2014-08-08 01:03:46 +02:00
|
|
|
|
Copyright (c) 1998-2014,
|
|
|
|
|
Enno Rehling <enno@eressea.de>
|
|
|
|
|
Katja Zedel <katze@felidae.kn-bremen.de
|
|
|
|
|
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
|
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
**/
|
|
|
|
|
|
|
|
|
|
#include <platform.h>
|
|
|
|
|
#include <kernel/config.h>
|
|
|
|
|
#include "economy.h"
|
|
|
|
|
|
2014-08-27 06:40:18 +02:00
|
|
|
|
#include "alchemy.h"
|
2014-06-16 03:34:39 +02:00
|
|
|
|
#include "direction.h"
|
2015-09-09 15:45:20 +02:00
|
|
|
|
#include "donations.h"
|
2016-11-01 22:11:10 +01:00
|
|
|
|
#include "guard.h"
|
2010-08-08 10:06:34 +02:00
|
|
|
|
#include "give.h"
|
|
|
|
|
#include "laws.h"
|
|
|
|
|
#include "randenc.h"
|
|
|
|
|
#include "spy.h"
|
2016-03-08 14:09:22 +01:00
|
|
|
|
#include "study.h"
|
2014-08-27 06:40:18 +02:00
|
|
|
|
#include "move.h"
|
2016-12-23 18:05:38 +01:00
|
|
|
|
#include "monsters.h"
|
2014-12-10 19:04:02 +01:00
|
|
|
|
#include "morale.h"
|
2014-08-27 06:40:18 +02:00
|
|
|
|
#include "reports.h"
|
2015-09-12 12:54:49 +02:00
|
|
|
|
#include "calendar.h"
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2017-08-21 20:18:19 +02:00
|
|
|
|
#include <attributes/reduceproduction.h>
|
|
|
|
|
#include <attributes/racename.h>
|
|
|
|
|
#include <spells/buildingcurse.h>
|
|
|
|
|
#include <spells/regioncurse.h>
|
2017-08-21 20:48:00 +02:00
|
|
|
|
#include <spells/unitcurse.h>
|
2017-08-21 20:18:19 +02:00
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* kernel includes */
|
2015-11-24 18:52:09 +01:00
|
|
|
|
#include <kernel/ally.h>
|
2010-08-08 10:06:34 +02:00
|
|
|
|
#include <kernel/building.h>
|
2012-06-03 22:39:42 +02:00
|
|
|
|
#include <kernel/curse.h>
|
2010-08-08 10:06:34 +02:00
|
|
|
|
#include <kernel/equipment.h>
|
|
|
|
|
#include <kernel/faction.h>
|
|
|
|
|
#include <kernel/item.h>
|
2014-06-09 18:54:48 +02:00
|
|
|
|
#include <kernel/messages.h>
|
2010-08-08 10:06:34 +02:00
|
|
|
|
#include <kernel/order.h>
|
|
|
|
|
#include <kernel/plane.h>
|
|
|
|
|
#include <kernel/pool.h>
|
|
|
|
|
#include <kernel/race.h>
|
|
|
|
|
#include <kernel/region.h>
|
|
|
|
|
#include <kernel/resources.h>
|
|
|
|
|
#include <kernel/ship.h>
|
|
|
|
|
#include <kernel/terrain.h>
|
|
|
|
|
#include <kernel/terrainid.h>
|
|
|
|
|
#include <kernel/unit.h>
|
|
|
|
|
|
|
|
|
|
/* util includes */
|
|
|
|
|
#include <util/attrib.h>
|
|
|
|
|
#include <util/base36.h>
|
2012-05-29 21:17:25 +02:00
|
|
|
|
#include <util/bsdstring.h>
|
2010-08-08 10:06:34 +02:00
|
|
|
|
#include <util/event.h>
|
|
|
|
|
#include <util/goodies.h>
|
|
|
|
|
#include <util/language.h>
|
|
|
|
|
#include <util/lists.h>
|
2014-10-16 07:34:09 +02:00
|
|
|
|
#include <util/log.h>
|
2010-08-08 10:06:34 +02:00
|
|
|
|
#include <util/parser.h>
|
|
|
|
|
#include <util/rng.h>
|
|
|
|
|
|
|
|
|
|
/* libs includes */
|
|
|
|
|
#include <math.h>
|
|
|
|
|
#include <stdio.h>
|
2014-03-15 19:29:11 +01:00
|
|
|
|
#include <stdlib.h>
|
2010-08-08 10:06:34 +02:00
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
|
|
|
|
|
typedef struct request {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
struct request *next;
|
|
|
|
|
struct unit *unit;
|
|
|
|
|
struct order *ord;
|
2015-05-15 20:56:43 +02:00
|
|
|
|
int qty;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
int no;
|
|
|
|
|
union {
|
|
|
|
|
bool goblin; /* stealing */
|
|
|
|
|
const struct luxury_type *ltype; /* trading */
|
|
|
|
|
} type;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
} request;
|
|
|
|
|
|
|
|
|
|
static int working;
|
|
|
|
|
|
|
|
|
|
static request entertainers[1024];
|
|
|
|
|
static request *nextentertainer;
|
2015-05-15 20:56:43 +02:00
|
|
|
|
static int entertaining;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2015-05-15 13:32:22 +02:00
|
|
|
|
static unsigned int norders;
|
2016-11-20 19:08:39 +01:00
|
|
|
|
static request *g_requests;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
#define RECRUIT_MERGE 1
|
|
|
|
|
static int rules_recruit = -1;
|
|
|
|
|
|
|
|
|
|
static void recruit_init(void)
|
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (rules_recruit < 0) {
|
|
|
|
|
rules_recruit = 0;
|
2015-11-22 10:44:46 +01:00
|
|
|
|
if (config_get_int("recruit.allow_merge", 1)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
rules_recruit |= RECRUIT_MERGE;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-22 12:22:07 +01:00
|
|
|
|
int entertainmoney(const region * r)
|
|
|
|
|
{
|
|
|
|
|
double n;
|
|
|
|
|
|
2017-08-21 20:48:00 +02:00
|
|
|
|
if (is_cursed(r->attribs, &ct_depression)) {
|
2016-11-22 12:22:07 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n = rmoney(r) / (double)ENTERTAINFRACTION;
|
|
|
|
|
|
2017-08-21 20:48:00 +02:00
|
|
|
|
if (is_cursed(r->attribs, &ct_generous)) {
|
|
|
|
|
n *= get_curseeffect(r->attribs, &ct_generous);
|
2016-11-22 12:22:07 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (int)n;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
int income(const unit * u)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2016-10-04 09:14:49 +02:00
|
|
|
|
const race *rc = u_race(u);
|
|
|
|
|
return rc->income * u->number;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-15 13:32:22 +02:00
|
|
|
|
static void scramble(void *data, unsigned int n, size_t width)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2015-05-15 13:32:22 +02:00
|
|
|
|
unsigned int j;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
char temp[64];
|
|
|
|
|
assert(width <= sizeof(temp));
|
|
|
|
|
for (j = 0; j != n; ++j) {
|
2015-05-15 13:32:22 +02:00
|
|
|
|
unsigned int k = rng_uint() % n;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (k == j)
|
|
|
|
|
continue;
|
|
|
|
|
memcpy(temp, (char *)data + j * width, width);
|
|
|
|
|
memcpy((char *)data + j * width, (char *)data + k * width, width);
|
|
|
|
|
memcpy((char *)data + k * width, temp, width);
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void expandorders(region * r, request * requests)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
unit *u;
|
|
|
|
|
request *o;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* Alle Units ohne request haben ein -1, alle units mit orders haben ein
|
|
|
|
|
* 0 hier stehen */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (u = r->units; u; u = u->next)
|
|
|
|
|
u->n = -1;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
norders = 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (o = requests; o; o = o->next) {
|
|
|
|
|
if (o->qty > 0) {
|
|
|
|
|
norders += o->qty;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (norders > 0) {
|
|
|
|
|
int i = 0;
|
2016-11-20 19:08:39 +01:00
|
|
|
|
g_requests = (request *)calloc(norders, sizeof(request));
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (o = requests; o; o = o->next) {
|
|
|
|
|
if (o->qty > 0) {
|
2015-05-15 13:32:22 +02:00
|
|
|
|
unsigned int j;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (j = o->qty; j; j--) {
|
2016-11-20 19:08:39 +01:00
|
|
|
|
g_requests[i] = *o;
|
|
|
|
|
g_requests[i].unit->n = 0;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-20 19:08:39 +01:00
|
|
|
|
scramble(g_requests, norders, sizeof(request));
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
2016-11-20 19:08:39 +01:00
|
|
|
|
g_requests = NULL;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
while (requests) {
|
|
|
|
|
request *o = requests->next;
|
|
|
|
|
free_order(requests->ord);
|
|
|
|
|
free(requests);
|
|
|
|
|
requests = o;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
typedef struct recruitment {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
struct recruitment *next;
|
|
|
|
|
faction *f;
|
|
|
|
|
request *requests;
|
|
|
|
|
int total, assigned;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
} recruitment;
|
|
|
|
|
|
2012-07-26 10:35:09 +02:00
|
|
|
|
/** Creates a list of recruitment structs, one for each faction. Adds every quantifyable request
|
|
|
|
|
* to the faction's struct and to total.
|
|
|
|
|
*/
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static recruitment *select_recruitment(request ** rop,
|
2014-08-08 01:03:46 +02:00
|
|
|
|
int(*quantify) (const struct race *, int), int *total)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
recruitment *recruits = NULL;
|
|
|
|
|
|
|
|
|
|
while (*rop) {
|
|
|
|
|
recruitment *rec = recruits;
|
|
|
|
|
request *ro = *rop;
|
|
|
|
|
unit *u = ro->unit;
|
|
|
|
|
const race *rc = u_race(u);
|
|
|
|
|
int qty = quantify(rc, ro->qty);
|
|
|
|
|
|
|
|
|
|
if (qty < 0) {
|
|
|
|
|
rop = &ro->next; /* skip this one */
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
*rop = ro->next; /* remove this one */
|
|
|
|
|
while (rec && rec->f != u->faction)
|
|
|
|
|
rec = rec->next;
|
|
|
|
|
if (rec == NULL) {
|
|
|
|
|
rec = malloc(sizeof(recruitment));
|
|
|
|
|
rec->f = u->faction;
|
|
|
|
|
rec->total = 0;
|
|
|
|
|
rec->assigned = 0;
|
|
|
|
|
rec->requests = NULL;
|
|
|
|
|
rec->next = recruits;
|
|
|
|
|
recruits = rec;
|
|
|
|
|
}
|
|
|
|
|
*total += qty;
|
|
|
|
|
rec->total += qty;
|
|
|
|
|
ro->next = rec->requests;
|
|
|
|
|
rec->requests = ro;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
return recruits;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-09-18 11:34:54 +02:00
|
|
|
|
void add_recruits(unit * u, int number, int wanted)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
region *r = u->region;
|
|
|
|
|
assert(number <= wanted);
|
|
|
|
|
if (number > 0) {
|
|
|
|
|
unit *unew;
|
|
|
|
|
char equipment[64];
|
|
|
|
|
|
|
|
|
|
if (u->number == 0) {
|
|
|
|
|
set_number(u, number);
|
|
|
|
|
u->hp = number * unit_max_hp(u);
|
|
|
|
|
unew = u;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
unew = create_unit(r, u->faction, number, u_race(u), 0, NULL, u);
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
strlcpy(equipment, "new_", sizeof(equipment));
|
2014-08-24 21:49:55 +02:00
|
|
|
|
strlcat(equipment, u_race(u)->_name, sizeof(equipment));
|
2014-08-08 01:03:46 +02:00
|
|
|
|
equip_unit(unew, get_equipment(equipment));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (unew != u) {
|
|
|
|
|
transfermen(unew, u, unew->number);
|
|
|
|
|
remove_unit(&r->units, unew);
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (number < wanted) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("recruit",
|
|
|
|
|
"unit region amount want", u, r, number, wanted));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static int any_recruiters(const struct race *rc, int qty)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
return (int)(qty * 2 * rc->recruit_multi);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static int do_recruiting(recruitment * recruits, int available)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
recruitment *rec;
|
|
|
|
|
int recruited = 0;
|
|
|
|
|
|
|
|
|
|
/* try to assign recruits to factions fairly */
|
|
|
|
|
while (available > 0) {
|
|
|
|
|
int n = 0;
|
|
|
|
|
int rest, mintotal = INT_MAX;
|
|
|
|
|
|
|
|
|
|
/* find smallest request */
|
|
|
|
|
for (rec = recruits; rec != NULL; rec = rec->next) {
|
|
|
|
|
int want = rec->total - rec->assigned;
|
|
|
|
|
if (want > 0) {
|
|
|
|
|
if (mintotal > want)
|
|
|
|
|
mintotal = want;
|
|
|
|
|
++n;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (n == 0)
|
|
|
|
|
break;
|
|
|
|
|
if (mintotal * n > available) {
|
|
|
|
|
mintotal = available / n;
|
|
|
|
|
}
|
|
|
|
|
rest = available - mintotal * n;
|
|
|
|
|
|
|
|
|
|
/* assign size of smallest request for everyone if possible; in the end roll dice to assign
|
|
|
|
|
* small rest */
|
|
|
|
|
for (rec = recruits; rec != NULL; rec = rec->next) {
|
|
|
|
|
int want = rec->total - rec->assigned;
|
|
|
|
|
|
|
|
|
|
if (want > 0) {
|
|
|
|
|
int get = mintotal;
|
|
|
|
|
if (want > mintotal && rest < n && (rng_int() % n) < rest) {
|
|
|
|
|
--rest;
|
|
|
|
|
++get;
|
|
|
|
|
}
|
|
|
|
|
assert(get <= want);
|
|
|
|
|
available -= get;
|
|
|
|
|
rec->assigned += get;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* do actual recruiting */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
for (rec = recruits; rec != NULL; rec = rec->next) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
request *req;
|
|
|
|
|
int get = rec->assigned;
|
|
|
|
|
|
|
|
|
|
for (req = rec->requests; req; req = req->next) {
|
|
|
|
|
unit *u = req->unit;
|
|
|
|
|
const race *rc = u_race(u); /* race is set in recruit() */
|
|
|
|
|
int number, dec;
|
2015-05-15 13:32:22 +02:00
|
|
|
|
double multi = 2.0 * rc->recruit_multi;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2017-01-10 16:31:05 +01:00
|
|
|
|
number = MIN(req->qty, (int)(get / multi));
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (rc->recruitcost) {
|
|
|
|
|
int afford = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
|
|
|
|
|
number * rc->recruitcost) / rc->recruitcost;
|
2017-01-10 16:31:05 +01:00
|
|
|
|
number = MIN(number, afford);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
if (u->number + number > UNIT_MAXSIZE) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, req->ord, "error_unit_size",
|
|
|
|
|
"maxsize", UNIT_MAXSIZE));
|
|
|
|
|
number = UNIT_MAXSIZE - u->number;
|
|
|
|
|
assert(number >= 0);
|
|
|
|
|
}
|
|
|
|
|
if (rc->recruitcost) {
|
|
|
|
|
use_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
|
|
|
|
|
rc->recruitcost * number);
|
|
|
|
|
}
|
|
|
|
|
if (u->number == 0 && fval(u, UFL_DEAD)) {
|
|
|
|
|
/* unit is empty, dead, and cannot recruit */
|
|
|
|
|
number = 0;
|
|
|
|
|
}
|
2016-11-05 18:20:56 +01:00
|
|
|
|
add_recruits(u, number, req->qty);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (number > 0) {
|
|
|
|
|
dec = (int)(number * multi);
|
|
|
|
|
if ((rc->ec_flags & ECF_REC_ETHEREAL) == 0) {
|
|
|
|
|
recruited += dec;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
get -= dec;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
return recruited;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
void free_recruitments(recruitment * recruits)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
while (recruits) {
|
|
|
|
|
recruitment *rec = recruits;
|
|
|
|
|
recruits = rec->next;
|
|
|
|
|
while (rec->requests) {
|
|
|
|
|
request *req = rec->requests;
|
|
|
|
|
rec->requests = req->next;
|
2015-10-14 11:58:54 +02:00
|
|
|
|
free_order(req->ord);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
free(req);
|
|
|
|
|
}
|
|
|
|
|
free(rec);
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Rekrutierung */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void expandrecruit(region * r, request * recruitorders)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
recruitment *recruits = NULL;
|
|
|
|
|
|
|
|
|
|
int orc_total = 0;
|
|
|
|
|
|
|
|
|
|
/* peasant limited: */
|
|
|
|
|
recruits = select_recruitment(&recruitorders, any_recruiters, &orc_total);
|
|
|
|
|
if (recruits) {
|
|
|
|
|
int orc_recruited, orc_peasants = rpeasants(r) * 2;
|
|
|
|
|
int orc_frac = orc_peasants / RECRUITFRACTION; /* anzahl orks. 2 ork = 1 bauer */
|
|
|
|
|
if (orc_total < orc_frac)
|
|
|
|
|
orc_frac = orc_total;
|
|
|
|
|
orc_recruited = do_recruiting(recruits, orc_frac);
|
|
|
|
|
assert(orc_recruited <= orc_frac);
|
|
|
|
|
rsetpeasants(r, (orc_peasants - orc_recruited) / 2);
|
|
|
|
|
free_recruitments(recruits);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* no limit: */
|
|
|
|
|
recruits = select_recruitment(&recruitorders, any_recruiters, &orc_total);
|
|
|
|
|
if (recruits) {
|
|
|
|
|
int recruited, peasants = rpeasants(r) * 2;
|
|
|
|
|
recruited = do_recruiting(recruits, INT_MAX);
|
|
|
|
|
if (recruited > 0) {
|
|
|
|
|
rsetpeasants(r, (peasants - recruited) / 2);
|
|
|
|
|
}
|
|
|
|
|
free_recruitments(recruits);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
assert(recruitorders == NULL);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static int recruit_cost(const faction * f, const race * rc)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2017-02-05 16:55:51 +01:00
|
|
|
|
if (is_monsters(f) || valid_race(f, rc)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
return rc->recruitcost;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void recruit(unit * u, struct order *ord, request ** recruitorders)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
region *r = u->region;
|
|
|
|
|
plane *pl;
|
|
|
|
|
request *o;
|
|
|
|
|
int recruitcost = -1;
|
|
|
|
|
const faction *f = u->faction;
|
|
|
|
|
const struct race *rc = u_race(u);
|
|
|
|
|
const char *str;
|
2015-05-15 13:32:22 +02:00
|
|
|
|
int n;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2014-08-23 09:17:58 +02:00
|
|
|
|
init_order(ord);
|
2015-05-15 13:32:22 +02:00
|
|
|
|
n = getint();
|
2016-09-19 18:42:14 +02:00
|
|
|
|
if (n <= 0) {
|
2015-05-15 13:32:22 +02:00
|
|
|
|
syntax_error(u, ord);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
|
|
|
|
if (u->number == 0) {
|
2014-12-22 16:28:17 +01:00
|
|
|
|
char token[128];
|
|
|
|
|
str = gettoken(token, sizeof(token));
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (str && str[0]) {
|
|
|
|
|
/* Monsters can RECRUIT 15 DRACOID
|
|
|
|
|
* also: secondary race */
|
|
|
|
|
rc = findrace(str, f->locale);
|
|
|
|
|
if (rc != NULL) {
|
|
|
|
|
recruitcost = recruit_cost(f, rc);
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2012-04-16 06:56:44 +02:00
|
|
|
|
if (recruitcost < 0) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
rc = u_race(u);
|
|
|
|
|
recruitcost = recruit_cost(f, rc);
|
|
|
|
|
if (recruitcost < 0) {
|
|
|
|
|
recruitcost = INT_MAX;
|
|
|
|
|
}
|
2012-04-16 06:56:44 +02:00
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
assert(rc);
|
|
|
|
|
u_setrace(u, rc);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* this is a very special case because the recruiting unit may be empty
|
|
|
|
|
* at this point and we have to look at the creating unit instead. This
|
|
|
|
|
* is done in cansee, which is called indirectly by is_guarded(). */
|
2016-10-31 07:45:06 +01:00
|
|
|
|
if (is_guarded(r, u)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
cmistake(u, ord, 70, MSG_EVENT);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (rc == get_race(RC_INSECT)) {
|
|
|
|
|
gamedate date;
|
|
|
|
|
get_gamedate(turn, &date);
|
|
|
|
|
if (date.season == 0 && r->terrain != newterrain(T_DESERT)) {
|
|
|
|
|
bool usepotion = false;
|
|
|
|
|
unit *u2;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (u2 = r->units; u2; u2 = u2->next)
|
|
|
|
|
if (fval(u2, UFL_WARMTH)) {
|
2015-01-30 20:37:14 +01:00
|
|
|
|
usepotion = true;
|
|
|
|
|
break;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
if (!usepotion)
|
|
|
|
|
{
|
|
|
|
|
cmistake(u, ord, 98, MSG_EVENT);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* in Gletschern, Eisbergen gar nicht rekrutieren */
|
|
|
|
|
if (r_insectstalled(r)) {
|
|
|
|
|
cmistake(u, ord, 97, MSG_EVENT);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2017-08-21 20:48:00 +02:00
|
|
|
|
if (is_cursed(r->attribs, &ct_riotzone)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* Die Region befindet sich in Aufruhr */
|
|
|
|
|
cmistake(u, ord, 237, MSG_EVENT);
|
|
|
|
|
return;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (recruitcost) {
|
|
|
|
|
pl = getplane(r);
|
|
|
|
|
if (pl && fval(pl, PFL_NORECRUITS)) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_pflnorecruit", ""));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
|
|
|
|
|
recruitcost) < recruitcost) {
|
|
|
|
|
cmistake(u, ord, 142, MSG_EVENT);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-08-04 22:47:55 +02:00
|
|
|
|
if (!playerrace(rc)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
cmistake(u, ord, 139, MSG_EVENT);
|
|
|
|
|
return;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-02-10 18:16:39 +01:00
|
|
|
|
if (fval(u, UFL_HERO)) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_herorecruit", ""));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (has_skill(u, SK_MAGIC)) {
|
|
|
|
|
/* error158;de;{unit} in {region}: '{command}' - Magier arbeiten
|
2017-02-15 17:09:23 +01:00
|
|
|
|
* grunds<EFBFBD>tzlich nur alleine! */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
cmistake(u, ord, 158, MSG_EVENT);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (has_skill(u, SK_ALCHEMY)
|
|
|
|
|
&& count_skill(u->faction, SK_ALCHEMY) + n >
|
|
|
|
|
skill_limit(u->faction, SK_ALCHEMY)) {
|
|
|
|
|
cmistake(u, ord, 156, MSG_EVENT);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (recruitcost > 0) {
|
|
|
|
|
int pooled =
|
|
|
|
|
get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, recruitcost * n);
|
2017-01-10 16:31:05 +01:00
|
|
|
|
n = MIN(n, pooled / recruitcost);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
u->wants = n;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (!n) {
|
|
|
|
|
cmistake(u, ord, 142, MSG_EVENT);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
o = (request *)calloc(1, sizeof(request));
|
|
|
|
|
o->qty = n;
|
|
|
|
|
o->unit = u;
|
|
|
|
|
o->ord = copy_order(ord);
|
|
|
|
|
addlist(recruitorders, o);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void friendly_takeover(region * r, faction * f)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
region_set_owner(r, f, turn);
|
2014-12-10 19:04:02 +01:00
|
|
|
|
morale_change(r, MORALE_TRANSFER);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2012-05-27 17:38:17 +02:00
|
|
|
|
void give_control(unit * u, unit * u2)
|
2011-03-07 08:02:35 +01:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (u->building) {
|
|
|
|
|
if (u->faction != u2->faction && rule_region_owners()) {
|
|
|
|
|
region *r = u->region;
|
|
|
|
|
faction *f = region_get_owner(r);
|
|
|
|
|
|
|
|
|
|
assert(u->building == u2->building);
|
|
|
|
|
if (f == u->faction) {
|
2017-04-28 21:29:42 +02:00
|
|
|
|
building *b = largestbuilding(r, cmp_current_owner, false);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (b == u->building) {
|
|
|
|
|
friendly_takeover(r, u2->faction);
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-05-27 17:38:17 +02:00
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
building_set_owner(u2);
|
|
|
|
|
}
|
|
|
|
|
if (u->ship) {
|
|
|
|
|
assert(u->ship == u2->ship);
|
|
|
|
|
ship_set_owner(u2);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
int give_control_cmd(unit * u, order * ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-12-22 16:28:17 +01:00
|
|
|
|
char token[128];
|
2014-08-08 01:03:46 +02:00
|
|
|
|
region *r = u->region;
|
|
|
|
|
unit *u2;
|
|
|
|
|
const char *s;
|
|
|
|
|
|
2014-08-23 09:17:58 +02:00
|
|
|
|
init_order(ord);
|
2014-12-12 20:53:39 +01:00
|
|
|
|
getunit(r, u->faction, &u2);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
s = gettoken(token, sizeof(token));
|
2014-12-13 18:40:15 +01:00
|
|
|
|
if (s && isparam(s, u->faction->locale, P_CONTROL)) {
|
2017-06-24 22:02:07 +02:00
|
|
|
|
bool okay = false;
|
2014-12-12 20:53:39 +01:00
|
|
|
|
if (!can_give_to(u, u2)) {
|
|
|
|
|
ADDMSG(&u->faction->msgs,
|
|
|
|
|
msg_feedback(u, ord, "feedback_unit_not_found", ""));
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
else if (!u->building && !u->ship) {
|
2017-06-24 22:02:07 +02:00
|
|
|
|
cmistake(u, ord, 140, MSG_EVENT);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
else if (u->building) {
|
|
|
|
|
if (u != building_owner(u->building)) {
|
2017-06-24 22:02:07 +02:00
|
|
|
|
cmistake(u, ord, 49, MSG_EVENT);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
else if (u2->building != u->building) {
|
2017-06-24 22:02:07 +02:00
|
|
|
|
cmistake(u, ord, 33, MSG_EVENT);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
okay = true;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (u->ship) {
|
|
|
|
|
if (u != ship_owner(u->ship)) {
|
2017-06-24 22:02:07 +02:00
|
|
|
|
cmistake(u, ord, 49, MSG_EVENT);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
else if (u2->ship != u->ship) {
|
2017-06-24 22:02:07 +02:00
|
|
|
|
cmistake(u, ord, 32, MSG_EVENT);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
okay = true;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-06-24 22:02:07 +02:00
|
|
|
|
if (okay) {
|
|
|
|
|
message *msg = msg_message("givecommand", "unit recipient", u, u2);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
add_message(&u->faction->msgs, msg);
|
|
|
|
|
if (u->faction != u2->faction) {
|
|
|
|
|
add_message(&u2->faction->msgs, msg);
|
|
|
|
|
}
|
|
|
|
|
msg_release(msg);
|
2017-06-24 22:02:07 +02:00
|
|
|
|
give_control(u, u2);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
return 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
static int forget_cmd(unit * u, order * ord)
|
|
|
|
|
{
|
2014-12-22 16:28:17 +01:00
|
|
|
|
char token[128];
|
2014-08-08 01:03:46 +02:00
|
|
|
|
skill_t sk;
|
|
|
|
|
const char *s;
|
|
|
|
|
|
2017-08-21 20:48:00 +02:00
|
|
|
|
if (is_cursed(u->attribs, &ct_slavery)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* charmed units shouldn't be losing their skills */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-23 09:17:58 +02:00
|
|
|
|
init_order(ord);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
s = gettoken(token, sizeof(token));
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2017-08-27 19:55:04 +02:00
|
|
|
|
sk = get_skill(s, u->faction->locale);
|
|
|
|
|
if (sk != NOSKILL) {
|
2017-08-31 21:19:25 +02:00
|
|
|
|
if (sk == SK_MAGIC && is_familiar(u)) {
|
|
|
|
|
/* some units cannot forget their innate magical abilities */
|
2017-08-27 19:55:04 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("forget", "unit skill", u, sk));
|
|
|
|
|
set_level(u, sk, 0);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-08-21 18:06:38 +02:00
|
|
|
|
static int maintain(building * b)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-06-24 16:42:45 +02:00
|
|
|
|
int c;
|
|
|
|
|
region *r = b->region;
|
2016-10-01 18:34:38 +02:00
|
|
|
|
bool paid = true;
|
2014-06-24 16:42:45 +02:00
|
|
|
|
unit *u;
|
2014-07-30 16:13:14 +02:00
|
|
|
|
|
2016-08-21 17:19:22 +02:00
|
|
|
|
if (fval(b, BLD_MAINTAINED) || b->type == NULL || b->type->maintenance == NULL) {
|
2016-08-21 20:12:28 +02:00
|
|
|
|
return BLD_MAINTAINED;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
if (fval(b, BLD_DONTPAY)) {
|
2016-08-21 17:19:22 +02:00
|
|
|
|
return 0;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
u = building_owner(b);
|
2014-12-14 11:48:15 +01:00
|
|
|
|
if (u == NULL) {
|
|
|
|
|
/* no owner - send a message to the entire region */
|
|
|
|
|
ADDMSG(&r->msgs, msg_message("maintenance_noowner", "building", b));
|
2016-08-21 17:19:22 +02:00
|
|
|
|
return 0;
|
2014-12-14 11:48:15 +01:00
|
|
|
|
}
|
2014-10-16 14:40:22 +02:00
|
|
|
|
/* If the owner is the region owner, check if dontpay flag is set for the building where he is in */
|
2015-11-22 12:20:33 +01:00
|
|
|
|
if (config_token("rules.region_owner_pay_building", b->type->_name)) {
|
2014-10-16 14:40:22 +02:00
|
|
|
|
if (fval(u->building, BLD_DONTPAY)) {
|
2016-08-21 17:19:22 +02:00
|
|
|
|
return 0;
|
2014-08-08 17:27:13 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-10-01 18:34:38 +02:00
|
|
|
|
for (c = 0; b->type->maintenance[c].number && paid; ++c) {
|
2014-08-13 19:55:10 +02:00
|
|
|
|
const maintenance *m = b->type->maintenance + c;
|
|
|
|
|
int need = m->number;
|
|
|
|
|
|
|
|
|
|
if (fval(m, MTF_VARIABLE))
|
|
|
|
|
need = need * b->size;
|
2016-10-01 18:34:38 +02:00
|
|
|
|
need -= get_pooled(u, m->rtype, GET_DEFAULT, need);
|
2014-08-13 19:55:10 +02:00
|
|
|
|
if (need > 0) {
|
2016-10-01 18:34:38 +02:00
|
|
|
|
paid = false;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-08-13 19:44:57 +02:00
|
|
|
|
if (fval(b, BLD_DONTPAY)) {
|
2016-10-01 18:34:38 +02:00
|
|
|
|
ADDMSG(&r->msgs, msg_message("maintenance_nowork", "building", b));
|
2016-08-21 17:19:22 +02:00
|
|
|
|
return 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2016-10-01 18:34:38 +02:00
|
|
|
|
if (!paid) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("maintenancefail", "unit building", u, b));
|
|
|
|
|
ADDMSG(&r->msgs, msg_message("maintenance_nowork", "building", b));
|
2016-08-21 17:19:22 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
for (c = 0; b->type->maintenance[c].number; ++c) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
const maintenance *m = b->type->maintenance + c;
|
2016-10-01 18:34:38 +02:00
|
|
|
|
int cost = m->number;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2016-10-01 18:34:38 +02:00
|
|
|
|
if (fval(m, MTF_VARIABLE)) {
|
|
|
|
|
cost = cost * b->size;
|
2016-08-21 17:19:22 +02:00
|
|
|
|
}
|
2016-10-01 18:34:38 +02:00
|
|
|
|
cost -=
|
|
|
|
|
use_pooled(u, m->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
|
|
|
|
|
cost);
|
|
|
|
|
assert(cost == 0);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
2016-10-01 18:34:38 +02:00
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("maintenance", "unit building", u, b));
|
|
|
|
|
return BLD_MAINTAINED;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-08-21 18:06:38 +02:00
|
|
|
|
void maintain_buildings(region * r)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
building **bp = &r->buildings;
|
|
|
|
|
while (*bp) {
|
|
|
|
|
building *b = *bp;
|
2016-08-21 20:12:28 +02:00
|
|
|
|
int flags = BLD_MAINTAINED;
|
2016-09-19 18:42:14 +02:00
|
|
|
|
|
2017-08-21 19:43:35 +02:00
|
|
|
|
if (!curse_active(get_curse(b->attribs, &ct_nocostbuilding))) {
|
2016-08-21 18:06:38 +02:00
|
|
|
|
flags = maintain(b);
|
2016-08-21 17:19:22 +02:00
|
|
|
|
}
|
|
|
|
|
fset(b, flags);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
bp = &b->next;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
void economics(region * r)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
unit *u;
|
|
|
|
|
request *recruitorders = NULL;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* Geben vor Selbstmord (doquit)! Hier alle unmittelbaren Befehle.
|
|
|
|
|
* Rekrutieren vor allen Einnahmequellen. Bewachen JA vor Steuern
|
|
|
|
|
* eintreiben. */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (u = r->units; u; u = u->next) {
|
|
|
|
|
order *ord;
|
|
|
|
|
if (u->number > 0) {
|
|
|
|
|
for (ord = u->orders; ord; ord = ord->next) {
|
|
|
|
|
keyword_t kwd = getkeyword(ord);
|
|
|
|
|
if (kwd == K_GIVE) {
|
|
|
|
|
give_cmd(u, ord);
|
|
|
|
|
}
|
|
|
|
|
else if (kwd == K_FORGET) {
|
|
|
|
|
forget_cmd(u, ord);
|
|
|
|
|
}
|
|
|
|
|
if (u->orders == NULL) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-08 08:44:20 +01:00
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* RECRUIT orders */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (rules_recruit < 0)
|
|
|
|
|
recruit_init();
|
|
|
|
|
for (u = r->units; u; u = u->next) {
|
|
|
|
|
order *ord;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if ((rules_recruit & RECRUIT_MERGE) || u->number == 0) {
|
|
|
|
|
for (ord = u->orders; ord; ord = ord->next) {
|
|
|
|
|
if (getkeyword(ord) == K_RECRUIT) {
|
|
|
|
|
recruit(u, ord, &recruitorders);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-14 11:58:54 +02:00
|
|
|
|
if (recruitorders) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
expandrecruit(r, recruitorders);
|
2015-10-14 11:58:54 +02:00
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
remove_empty_units_in_region(r);
|
|
|
|
|
|
|
|
|
|
for (u = r->units; u; u = u->next) {
|
2015-08-04 22:47:55 +02:00
|
|
|
|
order *ord = u->thisorder;
|
|
|
|
|
keyword_t kwd = getkeyword(ord);
|
|
|
|
|
if (kwd == K_DESTROY) {
|
|
|
|
|
if (destroy_cmd(u, ord) == 0) {
|
|
|
|
|
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-07-18 15:21:10 +02:00
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2017-04-02 14:43:53 +02:00
|
|
|
|
static void mod_skill(const resource_mod *mod, skill_t sk, int *skill) {
|
|
|
|
|
skill_t msk;
|
|
|
|
|
assert(mod->type == RMT_PROD_SKILL);
|
|
|
|
|
msk = (skill_t)mod->value.sa[0];
|
|
|
|
|
if (msk == NOSKILL || msk == sk) {
|
|
|
|
|
*skill += mod->value.sa[1];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct message * get_modifiers(unit *u, skill_t sk, const resource_type *rtype, variant *savep, int *skillp) {
|
|
|
|
|
struct building *b = inside_building(u);
|
|
|
|
|
const struct building_type *btype = building_is_active(b) ? b->type : NULL;
|
|
|
|
|
int save_n = 1, save_d = 1;
|
|
|
|
|
int skill = 0;
|
|
|
|
|
int need_race = 0, need_bldg = 0;
|
|
|
|
|
resource_mod *mod;
|
2017-05-06 15:53:21 +02:00
|
|
|
|
const struct building_type *btype_needed = NULL;
|
2017-04-02 14:43:53 +02:00
|
|
|
|
|
|
|
|
|
if (btype && btype->modifiers) {
|
|
|
|
|
for (mod = btype->modifiers; mod && mod->type != RMT_END; ++mod) {
|
|
|
|
|
if (mod->type == RMT_PROD_SKILL) {
|
|
|
|
|
mod_skill(mod, sk, &skill);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (mod = rtype->modifiers; mod && mod->type != RMT_END; ++mod) {
|
|
|
|
|
if (mod->btype == NULL || mod->btype == btype) {
|
|
|
|
|
if (mod->race == NULL || mod->race == u_race(u)) {
|
|
|
|
|
switch (mod->type) {
|
|
|
|
|
case RMT_PROD_SAVE:
|
|
|
|
|
if (savep) {
|
|
|
|
|
save_n *= mod->value.sa[0];
|
|
|
|
|
save_d *= mod->value.sa[1];
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case RMT_PROD_SKILL:
|
|
|
|
|
mod_skill(mod, sk, &skill);
|
|
|
|
|
break;
|
|
|
|
|
case RMT_PROD_REQUIRE:
|
|
|
|
|
if (mod->race) need_race |= 1;
|
2017-05-06 15:53:21 +02:00
|
|
|
|
if (mod->btype) {
|
|
|
|
|
need_bldg |= 1;
|
|
|
|
|
}
|
2017-04-02 14:43:53 +02:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
/* is not a production modifier, ignore it */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (mod->type == RMT_PROD_REQUIRE) {
|
|
|
|
|
if (mod->race) need_race |= 2;
|
2017-05-06 15:53:21 +02:00
|
|
|
|
if (mod->btype) {
|
|
|
|
|
btype_needed = mod->btype;
|
|
|
|
|
need_bldg |= 2;
|
|
|
|
|
}
|
2017-04-02 14:43:53 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (need_race == 2) {
|
|
|
|
|
return msg_error(u, u->thisorder, 117);
|
|
|
|
|
}
|
2017-05-06 15:53:21 +02:00
|
|
|
|
if (btype_needed && need_bldg == 2) {
|
|
|
|
|
return msg_feedback(u, u->thisorder, "building_needed", "building", btype_needed->_name);
|
2017-04-02 14:43:53 +02:00
|
|
|
|
}
|
|
|
|
|
*skillp = skill;
|
|
|
|
|
if (savep) *savep = frac_make(save_n, save_d);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void manufacture(unit * u, const item_type * itype, int want)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
int n;
|
|
|
|
|
int minskill = itype->construction->minskill;
|
|
|
|
|
skill_t sk = itype->construction->skill;
|
2017-04-02 14:43:53 +02:00
|
|
|
|
message *msg;
|
|
|
|
|
int skill_mod;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2017-04-02 14:43:53 +02:00
|
|
|
|
msg = get_modifiers(u, sk, itype->rtype, NULL, &skill_mod);
|
|
|
|
|
if (msg) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (want == 0) {
|
|
|
|
|
want = maxbuild(u, itype->construction);
|
|
|
|
|
}
|
2017-04-02 14:43:53 +02:00
|
|
|
|
n = build(u, itype->construction, 0, want, skill_mod);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
switch (n) {
|
|
|
|
|
case ENEEDSKILL:
|
|
|
|
|
ADDMSG(&u->faction->msgs,
|
|
|
|
|
msg_feedback(u, u->thisorder, "skill_needed", "skill", sk));
|
|
|
|
|
return;
|
|
|
|
|
case ELOWSKILL:
|
|
|
|
|
ADDMSG(&u->faction->msgs,
|
|
|
|
|
msg_feedback(u, u->thisorder, "manufacture_skills",
|
2016-09-19 18:42:14 +02:00
|
|
|
|
"skill minskill product", sk, minskill, itype->rtype, 1));
|
2014-08-08 01:03:46 +02:00
|
|
|
|
return;
|
|
|
|
|
case ENOMATERIALS:
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_materials_required(u, u->thisorder,
|
|
|
|
|
itype->construction, want));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (n > 0) {
|
|
|
|
|
i_change(&u->items, itype, n);
|
|
|
|
|
if (want == INT_MAX)
|
|
|
|
|
want = n;
|
2015-08-04 22:47:55 +02:00
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("produce",
|
2014-08-08 01:03:46 +02:00
|
|
|
|
"unit region amount wanted resource", u, u->region, n, want,
|
|
|
|
|
itype->rtype));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder, "error_cannotmake",
|
|
|
|
|
""));
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef struct allocation {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
struct allocation *next;
|
|
|
|
|
int want, get;
|
2017-02-17 21:45:10 +01:00
|
|
|
|
variant save;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
unsigned int flags;
|
|
|
|
|
unit *unit;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
} allocation;
|
|
|
|
|
#define new_allocation() calloc(sizeof(allocation), 1)
|
|
|
|
|
#define free_allocation(a) free(a)
|
|
|
|
|
|
|
|
|
|
typedef struct allocation_list {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
struct allocation_list *next;
|
|
|
|
|
allocation *data;
|
|
|
|
|
const resource_type *type;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
} allocation_list;
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static allocation_list *allocations;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
enum {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
AFL_DONE = 1 << 0,
|
|
|
|
|
AFL_LOWSKILL = 1 << 1
|
2010-08-08 10:06:34 +02:00
|
|
|
|
};
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void allocate_resource(unit * u, const resource_type * rtype, int want)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
const item_type *itype = resource2item(rtype);
|
|
|
|
|
region *r = u->region;
|
|
|
|
|
int dm = 0;
|
|
|
|
|
allocation_list *alist;
|
|
|
|
|
allocation *al;
|
2014-06-23 16:28:10 +02:00
|
|
|
|
const resource_type *rring;
|
2017-02-16 19:38:09 +01:00
|
|
|
|
int amount, skill, skill_mod = 0;
|
2017-03-23 19:28:56 +01:00
|
|
|
|
variant save_mod;
|
2017-04-02 14:43:53 +02:00
|
|
|
|
skill_t sk;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* momentan kann man keine ressourcen abbauen, wenn man daf<61>r
|
2014-08-08 01:03:46 +02:00
|
|
|
|
* Materialverbrauch hat: */
|
|
|
|
|
assert(itype != NULL && (itype->construction == NULL
|
|
|
|
|
|| itype->construction->materials == NULL));
|
|
|
|
|
|
2017-04-02 14:43:53 +02:00
|
|
|
|
sk = itype->construction->skill;
|
2017-02-26 13:19:47 +01:00
|
|
|
|
if (!rtype->raw) {
|
|
|
|
|
int avail = limit_resource(r, rtype);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (avail <= 0) {
|
|
|
|
|
cmistake(u, u->thisorder, 121, MSG_PRODUCE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (besieged(u)) {
|
|
|
|
|
cmistake(u, u->thisorder, 60, MSG_PRODUCE);
|
|
|
|
|
return;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-02-26 13:55:19 +01:00
|
|
|
|
if (rtype->modifiers) {
|
2017-04-02 14:43:53 +02:00
|
|
|
|
message *msg = get_modifiers(u, sk, rtype, &save_mod, &skill_mod);
|
2017-02-15 09:30:36 +01:00
|
|
|
|
if (msg) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg);
|
|
|
|
|
return;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-03-23 19:28:56 +01:00
|
|
|
|
else {
|
|
|
|
|
save_mod.sa[0] = 1;
|
|
|
|
|
save_mod.sa[1] = 1;
|
2017-02-17 21:45:10 +01:00
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Bergw<67>chter k<>nnen Abbau von Eisen/Laen durch Bewachen verhindern.
|
|
|
|
|
* Als magische Wesen 'sehen' Bergw<EFBFBD>chter alles und werden durch
|
2014-08-08 01:03:46 +02:00
|
|
|
|
* Belagerung nicht aufgehalten. (Ansonsten wie oben bei Elfen anpassen).
|
|
|
|
|
*/
|
|
|
|
|
if (itype->rtype && (itype->rtype == get_resourcetype(R_IRON) || itype->rtype == rt_find("laen"))) {
|
|
|
|
|
unit *u2;
|
|
|
|
|
for (u2 = r->units; u2; u2 = u2->next) {
|
2016-10-31 07:45:06 +01:00
|
|
|
|
if (is_guard(u)
|
2014-08-08 01:03:46 +02:00
|
|
|
|
&& !fval(u2, UFL_ISNEW)
|
|
|
|
|
&& u2->number && !alliedunit(u2, u->faction, HELP_GUARD)) {
|
|
|
|
|
ADDMSG(&u->faction->msgs,
|
|
|
|
|
msg_feedback(u, u->thisorder, "region_guarded", "guard", u2));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2017-04-02 14:43:53 +02:00
|
|
|
|
assert(sk != NOSKILL || "limited resource needs a required skill for making it");
|
|
|
|
|
skill = effskill(u, sk, 0);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (skill == 0) {
|
|
|
|
|
add_message(&u->faction->msgs,
|
|
|
|
|
msg_feedback(u, u->thisorder, "skill_needed", "skill", sk));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (skill < itype->construction->minskill) {
|
|
|
|
|
add_message(&u->faction->msgs,
|
|
|
|
|
msg_feedback(u, u->thisorder, "manufacture_skills",
|
2016-09-19 18:42:14 +02:00
|
|
|
|
"skill minskill product", sk, itype->construction->minskill,
|
|
|
|
|
itype->rtype));
|
2014-08-08 01:03:46 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2017-02-15 09:30:36 +01:00
|
|
|
|
skill += skill_mod;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
amount = skill * u->number;
|
|
|
|
|
/* nun ist amount die Gesamtproduktion der Einheit (in punkten) */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* mit Flinkfingerring verzehnfacht sich die Produktion */
|
2014-06-23 16:28:10 +02:00
|
|
|
|
rring = get_resourcetype(R_RING_OF_NIMBLEFINGER);
|
|
|
|
|
if (rring) {
|
|
|
|
|
int dm = i_get(u->items, rring->itype);
|
2017-01-10 16:31:05 +01:00
|
|
|
|
amount += skill * MIN(u->number, dm) * (roqf_factor() - 1);
|
2014-06-23 16:28:10 +02:00
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-06-23 16:28:10 +02:00
|
|
|
|
/* Schaffenstrunk: */
|
|
|
|
|
if ((dm = get_effect(u, oldpotiontype[P_DOMORE])) != 0) {
|
2017-01-10 16:31:05 +01:00
|
|
|
|
dm = MIN(dm, u->number);
|
2014-06-23 16:28:10 +02:00
|
|
|
|
change_effect(u, oldpotiontype[P_DOMORE], -dm);
|
|
|
|
|
amount += dm * skill; /* dm Personen produzieren doppelt */
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
amount /= itype->construction->minskill;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* Limitierung durch Parameter m. */
|
|
|
|
|
if (want > 0 && want < amount)
|
|
|
|
|
amount = want;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
alist = allocations;
|
|
|
|
|
while (alist && alist->type != rtype)
|
|
|
|
|
alist = alist->next;
|
|
|
|
|
if (!alist) {
|
|
|
|
|
alist = calloc(sizeof(struct allocation_list), 1);
|
|
|
|
|
alist->next = allocations;
|
|
|
|
|
alist->type = rtype;
|
|
|
|
|
allocations = alist;
|
|
|
|
|
}
|
|
|
|
|
al = new_allocation();
|
|
|
|
|
al->want = amount;
|
2017-02-15 09:30:36 +01:00
|
|
|
|
al->save = save_mod;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
al->next = alist->data;
|
|
|
|
|
al->unit = u;
|
|
|
|
|
alist->data = al;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-02-17 21:45:10 +01:00
|
|
|
|
static int required(int want, variant save)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2017-02-17 21:45:10 +01:00
|
|
|
|
int req = (int)(want * save.sa[0] / save.sa[1]);
|
2017-02-18 11:19:42 +01:00
|
|
|
|
int r = want * save.sa[0] % save.sa[1];
|
2017-11-05 19:15:43 +01:00
|
|
|
|
if (r > 0) ++req;
|
2017-01-10 18:07:36 +01:00
|
|
|
|
return req;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
leveled_allocation(const resource_type * rtype, region * r, allocation * alist)
|
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
const item_type *itype = resource2item(rtype);
|
|
|
|
|
rawmaterial *rm = rm_get(r, rtype);
|
|
|
|
|
int need;
|
|
|
|
|
bool first = true;
|
|
|
|
|
|
|
|
|
|
if (rm != NULL) {
|
|
|
|
|
do {
|
|
|
|
|
int avail = rm->amount;
|
2017-01-10 18:07:36 +01:00
|
|
|
|
int nreq = 0;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
allocation *al;
|
|
|
|
|
|
|
|
|
|
if (avail <= 0) {
|
|
|
|
|
for (al = alist; al; al = al->next) {
|
|
|
|
|
al->get = 0;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
assert(avail > 0);
|
|
|
|
|
|
|
|
|
|
for (al = alist; al; al = al->next)
|
|
|
|
|
if (!fval(al, AFL_DONE)) {
|
2015-01-30 20:37:14 +01:00
|
|
|
|
int req = required(al->want - al->get, al->save);
|
|
|
|
|
assert(al->get <= al->want && al->get >= 0);
|
2015-08-27 16:16:55 +02:00
|
|
|
|
if (effskill(al->unit, itype->construction->skill, 0)
|
2015-01-30 20:37:14 +01:00
|
|
|
|
>= rm->level + itype->construction->minskill - 1) {
|
|
|
|
|
if (req) {
|
2017-01-10 18:07:36 +01:00
|
|
|
|
nreq += req;
|
2015-01-30 20:37:14 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
fset(al, AFL_DONE);
|
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
fset(al, AFL_DONE);
|
2015-01-30 20:37:14 +01:00
|
|
|
|
if (first)
|
|
|
|
|
fset(al, AFL_LOWSKILL);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-10 18:07:36 +01:00
|
|
|
|
need = nreq;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2017-01-10 18:07:36 +01:00
|
|
|
|
avail = MIN(avail, nreq);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (need > 0) {
|
|
|
|
|
int use = 0;
|
2017-03-22 20:37:09 +01:00
|
|
|
|
for (al = alist; al; al = al->next) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (!fval(al, AFL_DONE)) {
|
2015-01-30 20:37:14 +01:00
|
|
|
|
if (avail > 0) {
|
|
|
|
|
int want = required(al->want - al->get, al->save);
|
2017-01-10 18:07:36 +01:00
|
|
|
|
int x = avail * want / nreq;
|
2017-02-15 21:16:54 +01:00
|
|
|
|
int r = (avail * want) % nreq;
|
|
|
|
|
/* Wenn Rest, dann wuerfeln, ob ich etwas bekomme: */
|
|
|
|
|
if (r > 0 && rng_int() % nreq < r) ++x;
|
2015-01-30 20:37:14 +01:00
|
|
|
|
avail -= x;
|
|
|
|
|
use += x;
|
2017-01-10 18:07:36 +01:00
|
|
|
|
nreq -= want;
|
2015-01-30 20:37:14 +01:00
|
|
|
|
need -= x;
|
2017-02-17 21:45:10 +01:00
|
|
|
|
al->get = MIN(al->want, al->get + x * al->save.sa[1] / al->save.sa[0]);
|
2015-01-30 20:37:14 +01:00
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
2017-03-22 20:37:09 +01:00
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (use) {
|
2017-03-22 20:37:09 +01:00
|
|
|
|
rawmaterial_type *raw = rmt_get(rm->rtype);
|
|
|
|
|
if (raw && raw->use) {
|
|
|
|
|
assert(use <= rm->amount);
|
|
|
|
|
raw->use(rm, r, use);
|
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
2017-01-10 18:07:36 +01:00
|
|
|
|
assert(avail == 0 || nreq == 0);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
first = false;
|
|
|
|
|
} while (need > 0);
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
attrib_allocation(const resource_type * rtype, region * r, allocation * alist)
|
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
allocation *al;
|
2017-01-10 18:07:36 +01:00
|
|
|
|
int nreq = 0;
|
2017-03-02 19:21:11 +01:00
|
|
|
|
int avail = INT_MAX;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (al = alist; al; al = al->next) {
|
2017-01-10 18:07:36 +01:00
|
|
|
|
nreq += required(al->want, al->save);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2017-02-26 13:19:47 +01:00
|
|
|
|
if (!rtype->raw) {
|
|
|
|
|
avail = limit_resource(r, rtype);
|
|
|
|
|
if (avail < 0) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
avail = 0;
|
2017-02-26 13:19:47 +01:00
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2017-01-10 18:07:36 +01:00
|
|
|
|
avail = MIN(avail, nreq);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (al = alist; al; al = al->next) {
|
|
|
|
|
if (avail > 0) {
|
|
|
|
|
int want = required(al->want, al->save);
|
2017-01-10 18:07:36 +01:00
|
|
|
|
int x = avail * want / nreq;
|
2017-02-17 21:45:10 +01:00
|
|
|
|
int rx = (avail * want) % nreq;
|
|
|
|
|
/* Wenn Rest, dann wuerfeln, ob ich was bekomme: */
|
2017-11-05 19:15:43 +01:00
|
|
|
|
if (rx > 0 && rng_int() % nreq < rx) ++x;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
avail -= x;
|
2017-01-10 18:07:36 +01:00
|
|
|
|
nreq -= want;
|
2017-03-02 19:21:11 +01:00
|
|
|
|
al->get = x * al->save.sa[1] / al->save.sa[0];
|
2017-02-17 21:45:10 +01:00
|
|
|
|
al->get = MIN(al->want, al->get);
|
2017-02-26 13:19:47 +01:00
|
|
|
|
if (!rtype->raw) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
int use = required(al->get, al->save);
|
2017-02-26 13:19:47 +01:00
|
|
|
|
if (use) {
|
|
|
|
|
produce_resource(r, rtype, use);
|
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-10 18:07:36 +01:00
|
|
|
|
assert(avail == 0 || nreq == 0);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
typedef void(*allocate_function) (const resource_type *, struct region *,
|
2016-09-19 18:42:14 +02:00
|
|
|
|
struct allocation *);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static allocate_function get_allocator(const struct resource_type *rtype)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2017-02-26 13:19:47 +01:00
|
|
|
|
if (rtype->raw) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
return leveled_allocation;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2017-02-26 13:19:47 +01:00
|
|
|
|
return attrib_allocation;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
void split_allocations(region * r)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
allocation_list **p_alist = &allocations;
|
|
|
|
|
while (*p_alist) {
|
|
|
|
|
allocation_list *alist = *p_alist;
|
|
|
|
|
const resource_type *rtype = alist->type;
|
|
|
|
|
allocate_function alloc = get_allocator(rtype);
|
|
|
|
|
const item_type *itype = resource2item(rtype);
|
|
|
|
|
allocation **p_al = &alist->data;
|
|
|
|
|
|
|
|
|
|
alloc(rtype, r, alist->data);
|
|
|
|
|
|
|
|
|
|
while (*p_al) {
|
|
|
|
|
allocation *al = *p_al;
|
|
|
|
|
if (al->get) {
|
|
|
|
|
assert(itype || !"not implemented for non-items");
|
|
|
|
|
i_change(&al->unit->items, itype, al->get);
|
|
|
|
|
produceexp(al->unit, itype->construction->skill, al->unit->number);
|
|
|
|
|
}
|
|
|
|
|
if (al->want == INT_MAX)
|
|
|
|
|
al->want = al->get;
|
|
|
|
|
ADDMSG(&al->unit->faction->msgs, msg_message("produce",
|
|
|
|
|
"unit region amount wanted resource",
|
|
|
|
|
al->unit, al->unit->region, al->get, al->want, rtype));
|
|
|
|
|
*p_al = al->next;
|
|
|
|
|
free_allocation(al);
|
|
|
|
|
}
|
|
|
|
|
*p_alist = alist->next;
|
|
|
|
|
free(alist);
|
|
|
|
|
}
|
|
|
|
|
allocations = NULL;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void create_potion(unit * u, const potion_type * ptype, int want)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
int built;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (want == 0) {
|
|
|
|
|
want = maxbuild(u, ptype->itype->construction);
|
|
|
|
|
}
|
2017-04-02 14:43:53 +02:00
|
|
|
|
built = build(u, ptype->itype->construction, 0, want, 0);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
switch (built) {
|
|
|
|
|
case ELOWSKILL:
|
|
|
|
|
case ENEEDSKILL:
|
|
|
|
|
/* no skill, or not enough skill points to build */
|
|
|
|
|
cmistake(u, u->thisorder, 50, MSG_PRODUCE);
|
|
|
|
|
break;
|
|
|
|
|
case ECOMPLETE:
|
|
|
|
|
assert(0);
|
|
|
|
|
break;
|
|
|
|
|
case ENOMATERIALS:
|
|
|
|
|
/* something missing from the list of materials */
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_materials_required(u, u->thisorder,
|
|
|
|
|
ptype->itype->construction, want));
|
|
|
|
|
return;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
i_change(&u->items, ptype->itype, built);
|
|
|
|
|
if (want == INT_MAX)
|
|
|
|
|
want = built;
|
2015-08-04 22:47:55 +02:00
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("produce",
|
2014-08-08 01:03:46 +02:00
|
|
|
|
"unit region amount wanted resource", u, u->region, built, want,
|
|
|
|
|
ptype->itype->rtype));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
void make_item(unit * u, const item_type * itype, int want)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (itype->construction && fval(itype->rtype, RTF_LIMITED)) {
|
2016-10-31 07:45:06 +01:00
|
|
|
|
if (is_guarded(u->region, u)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
cmistake(u, u->thisorder, 70, MSG_EVENT);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
allocate_resource(u, itype->rtype, want);
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
else {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
const potion_type *ptype = resource2potion(itype->rtype);
|
|
|
|
|
if (ptype != NULL)
|
|
|
|
|
create_potion(u, ptype, want);
|
|
|
|
|
else if (itype->construction && itype->construction->materials)
|
|
|
|
|
manufacture(u, itype, want);
|
|
|
|
|
else {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder,
|
|
|
|
|
"error_cannotmake", ""));
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
int make_cmd(unit * u, struct order *ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-12-22 14:21:24 +01:00
|
|
|
|
char token[128];
|
2014-08-08 01:03:46 +02:00
|
|
|
|
region *r = u->region;
|
2014-11-16 12:46:55 +01:00
|
|
|
|
const building_type *btype = 0;
|
|
|
|
|
const ship_type *stype = 0;
|
|
|
|
|
const item_type *itype = 0;
|
|
|
|
|
param_t p = NOPARAM;
|
|
|
|
|
int m = INT_MAX;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
const char *s;
|
|
|
|
|
const struct locale *lang = u->faction->locale;
|
|
|
|
|
char ibuf[16];
|
2014-08-23 09:17:58 +02:00
|
|
|
|
keyword_t kwd;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2014-08-23 09:17:58 +02:00
|
|
|
|
kwd = init_order(ord);
|
|
|
|
|
assert(kwd == K_MAKE);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
s = gettoken(token, sizeof(token));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-11-16 12:46:55 +01:00
|
|
|
|
if (s) {
|
2016-11-14 22:27:44 +01:00
|
|
|
|
m = atoip(s);
|
2014-11-16 12:46:55 +01:00
|
|
|
|
sprintf(ibuf, "%d", m);
|
|
|
|
|
if (!strcmp(ibuf, (const char *)s)) {
|
|
|
|
|
/* a quantity was given */
|
2014-12-22 16:28:17 +01:00
|
|
|
|
s = gettoken(token, sizeof(token));
|
2014-12-22 14:21:24 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
2014-11-16 12:46:55 +01:00
|
|
|
|
m = INT_MAX;
|
|
|
|
|
}
|
|
|
|
|
if (s) {
|
|
|
|
|
p = findparam(s, u->faction->locale);
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (p == P_ROAD) {
|
|
|
|
|
plane *pl = rplane(r);
|
|
|
|
|
if (pl && fval(pl, PFL_NOBUILD)) {
|
|
|
|
|
cmistake(u, ord, 275, MSG_PRODUCE);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2014-12-22 16:28:17 +01:00
|
|
|
|
const char * s = gettoken(token, sizeof(token));
|
2014-09-21 16:43:17 +02:00
|
|
|
|
direction_t d = s ? get_direction(s, u->faction->locale) : NODIRECTION;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (d != NODIRECTION) {
|
2015-08-27 16:59:39 +02:00
|
|
|
|
build_road(u, m, d);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* Die Richtung wurde nicht erkannt */
|
|
|
|
|
cmistake(u, ord, 71, MSG_PRODUCE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else if (p == P_SHIP) {
|
|
|
|
|
plane *pl = rplane(r);
|
|
|
|
|
if (pl && fval(pl, PFL_NOBUILD)) {
|
|
|
|
|
cmistake(u, ord, 276, MSG_PRODUCE);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2015-08-27 16:59:39 +02:00
|
|
|
|
continue_ship(u, m);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else if (p == P_HERBS) {
|
2015-08-27 14:46:08 +02:00
|
|
|
|
herbsearch(u, m);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* since the string can match several objects, like in 'academy' and
|
|
|
|
|
* 'academy of arts', we need to figure out what the player meant.
|
|
|
|
|
* This is not 100% safe.
|
|
|
|
|
*/
|
2014-11-16 12:46:55 +01:00
|
|
|
|
if (s) {
|
|
|
|
|
stype = findshiptype(s, lang);
|
|
|
|
|
btype = findbuildingtype(s, lang);
|
|
|
|
|
itype = finditemtype(s, lang);
|
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (itype != NULL && (btype != NULL || stype != NULL)) {
|
|
|
|
|
if (itype->construction == NULL) {
|
|
|
|
|
/* if the item cannot be made, we probably didn't mean to make it */
|
|
|
|
|
itype = NULL;
|
|
|
|
|
}
|
|
|
|
|
else if (stype != NULL) {
|
|
|
|
|
const char *sname = LOC(lang, stype->_name);
|
|
|
|
|
const char *iname = LOC(lang, resourcename(itype->rtype, 0));
|
|
|
|
|
if (strlen(iname) < strlen(sname))
|
|
|
|
|
stype = NULL;
|
|
|
|
|
else
|
|
|
|
|
itype = NULL;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const char *bname = LOC(lang, btype->_name);
|
|
|
|
|
const char *iname = LOC(lang, resourcename(itype->rtype, 0));
|
|
|
|
|
if (strlen(iname) < strlen(bname))
|
|
|
|
|
btype = NULL;
|
|
|
|
|
else
|
|
|
|
|
itype = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (btype != NULL && stype != NULL) {
|
|
|
|
|
const char *bname = LOC(lang, btype->_name);
|
|
|
|
|
const char *sname = LOC(lang, stype->_name);
|
|
|
|
|
if (strlen(sname) < strlen(bname))
|
|
|
|
|
btype = NULL;
|
|
|
|
|
else
|
|
|
|
|
stype = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (stype != NOSHIP) {
|
|
|
|
|
plane *pl = rplane(r);
|
|
|
|
|
if (pl && fval(pl, PFL_NOBUILD)) {
|
|
|
|
|
cmistake(u, ord, 276, MSG_PRODUCE);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2015-08-27 16:59:39 +02:00
|
|
|
|
create_ship(u, stype, m, ord);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (btype != NOBUILDING) {
|
|
|
|
|
plane *pl = rplane(r);
|
|
|
|
|
if (pl && fval(pl, PFL_NOBUILD)) {
|
2014-08-16 11:41:19 +02:00
|
|
|
|
cmistake(u, ord, 275, MSG_PRODUCE);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
2014-08-16 11:41:19 +02:00
|
|
|
|
else if (btype->construction) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
int id = getid();
|
|
|
|
|
build_building(u, btype, id, m, ord);
|
|
|
|
|
}
|
2014-08-16 11:41:19 +02:00
|
|
|
|
else {
|
|
|
|
|
cmistake(u, ord, 275, MSG_PRODUCE);
|
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
else if (itype != NULL) {
|
2017-02-15 17:09:23 +01:00
|
|
|
|
make_item(u, itype, m);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_cannotmake", ""));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void free_luxuries(struct attrib *a)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
item *itm = (item *)a->data.v;
|
|
|
|
|
a->data.v = NULL;
|
|
|
|
|
i_freeall(&itm);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const attrib_type at_luxuries = {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
"luxuries", NULL, free_luxuries, NULL, NULL, NULL
|
2010-08-08 10:06:34 +02:00
|
|
|
|
};
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void expandbuying(region * r, request * buyorders)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-06-24 16:42:45 +02:00
|
|
|
|
const resource_type *rsilver = get_resourcetype(R_SILVER);
|
|
|
|
|
int max_products;
|
|
|
|
|
unit *u;
|
2017-11-05 15:11:02 +01:00
|
|
|
|
struct trade {
|
2014-06-24 16:42:45 +02:00
|
|
|
|
const luxury_type *type;
|
|
|
|
|
int number;
|
|
|
|
|
int multi;
|
2014-12-25 22:38:01 +01:00
|
|
|
|
} trades[MAXLUXURIES], *trade;
|
2017-11-05 15:11:02 +01:00
|
|
|
|
int ntrades = 0;
|
2014-06-24 16:42:45 +02:00
|
|
|
|
const luxury_type *ltype;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2017-11-05 15:11:02 +01:00
|
|
|
|
for (ntrades = 0, ltype = luxurytypes; ltype; ltype = ltype->next) {
|
|
|
|
|
assert(ntrades < MAXLUXURIES);
|
|
|
|
|
trades[ntrades].number = 0;
|
|
|
|
|
trades[ntrades].multi = 1;
|
|
|
|
|
trades[ntrades++].type = ltype;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (!buyorders)
|
|
|
|
|
return;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* Initialisation. multiplier ist der Multiplikator auf den
|
2017-02-15 17:09:23 +01:00
|
|
|
|
* Verkaufspreis. F<EFBFBD>r max_products Produkte kauft man das Produkt zum
|
|
|
|
|
* einfachen Verkaufspreis, danach erh<EFBFBD>ht sich der Multiplikator um 1.
|
|
|
|
|
* counter ist ein Z<EFBFBD>hler, der die gekauften Produkte z<EFBFBD>hlt. money
|
|
|
|
|
* wird f<EFBFBD>r die debug message gebraucht. */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
|
|
|
|
max_products = rpeasants(r) / TRADE_FRACTION;
|
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Kauf - auch so programmiert, da<64> er leicht erweiterbar auf mehrere
|
|
|
|
|
* G<EFBFBD>ter pro Monat ist. j sind die Befehle, i der Index des
|
2014-08-08 01:03:46 +02:00
|
|
|
|
* gehandelten Produktes. */
|
|
|
|
|
if (max_products > 0) {
|
2015-05-15 20:56:43 +02:00
|
|
|
|
unsigned int j;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
expandorders(r, buyorders);
|
|
|
|
|
if (!norders)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for (j = 0; j != norders; j++) {
|
|
|
|
|
int price, multi;
|
2016-11-20 19:08:39 +01:00
|
|
|
|
ltype = g_requests[j].type.ltype;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
trade = trades;
|
2017-11-05 15:11:02 +01:00
|
|
|
|
while (trade->type && trade->type != ltype)
|
2014-08-08 01:03:46 +02:00
|
|
|
|
++trade;
|
|
|
|
|
multi = trade->multi;
|
|
|
|
|
price = ltype->price * multi;
|
|
|
|
|
|
2016-11-20 19:08:39 +01:00
|
|
|
|
if (get_pooled(g_requests[j].unit, rsilver, GET_DEFAULT,
|
2014-08-08 01:03:46 +02:00
|
|
|
|
price) >= price) {
|
2016-11-20 19:08:39 +01:00
|
|
|
|
unit *u = g_requests[j].unit;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
item *items;
|
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* litems z<>hlt die G<>ter, die verkauft wurden, u->n das Geld, das
|
|
|
|
|
* verdient wurde. Dies mu<EFBFBD> gemacht werden, weil der Preis st<EFBFBD>ndig sinkt,
|
2014-08-08 01:03:46 +02:00
|
|
|
|
* man sich also das verdiente Geld und die verkauften Produkte separat
|
2017-02-15 17:09:23 +01:00
|
|
|
|
* merken mu<EFBFBD>. */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
attrib *a = a_find(u->attribs, &at_luxuries);
|
|
|
|
|
if (a == NULL)
|
|
|
|
|
a = a_add(&u->attribs, a_new(&at_luxuries));
|
|
|
|
|
|
|
|
|
|
items = a->data.v;
|
|
|
|
|
i_change(&items, ltype->itype, 1);
|
|
|
|
|
a->data.v = items;
|
2016-11-20 19:08:39 +01:00
|
|
|
|
i_change(&g_requests[j].unit->items, ltype->itype, 1);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
use_pooled(u, rsilver, GET_DEFAULT, price);
|
|
|
|
|
if (u->n < 0)
|
|
|
|
|
u->n = 0;
|
|
|
|
|
u->n += price;
|
|
|
|
|
|
|
|
|
|
rsetmoney(r, rmoney(r) + price);
|
|
|
|
|
|
|
|
|
|
/* Falls mehr als max_products Bauern ein Produkt verkauft haben, steigt
|
2017-02-15 17:09:23 +01:00
|
|
|
|
* der Preis Multiplikator f<EFBFBD>r das Produkt um den Faktor 1. Der Z<EFBFBD>hler
|
2014-08-08 01:03:46 +02:00
|
|
|
|
* wird wieder auf 0 gesetzt. */
|
|
|
|
|
if (++trade->number == max_products) {
|
|
|
|
|
trade->number = 0;
|
|
|
|
|
++trade->multi;
|
|
|
|
|
}
|
|
|
|
|
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
}
|
2016-11-20 19:08:39 +01:00
|
|
|
|
free(g_requests);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* Ausgabe an Einheiten */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (u = r->units; u; u = u->next) {
|
|
|
|
|
attrib *a = a_find(u->attribs, &at_luxuries);
|
|
|
|
|
item *itm;
|
|
|
|
|
if (a == NULL)
|
|
|
|
|
continue;
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("buy", "unit money", u, u->n));
|
|
|
|
|
for (itm = (item *)a->data.v; itm; itm = itm->next) {
|
|
|
|
|
if (itm->number) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("buyamount",
|
|
|
|
|
"unit amount resource", u, itm->number, itm->type->rtype));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
a_remove(&u->attribs, a);
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attrib_type at_trades = {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
"trades",
|
|
|
|
|
DEFAULT_INIT,
|
|
|
|
|
DEFAULT_FINALIZE,
|
|
|
|
|
DEFAULT_AGE,
|
|
|
|
|
NO_WRITE,
|
|
|
|
|
NO_READ
|
2010-08-08 10:06:34 +02:00
|
|
|
|
};
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void buy(unit * u, request ** buyorders, struct order *ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-12-22 16:28:17 +01:00
|
|
|
|
char token[128];
|
2014-08-08 01:03:46 +02:00
|
|
|
|
region *r = u->region;
|
|
|
|
|
int n, k;
|
|
|
|
|
request *o;
|
|
|
|
|
attrib *a;
|
|
|
|
|
const item_type *itype = NULL;
|
|
|
|
|
const luxury_type *ltype = NULL;
|
2014-08-23 09:17:58 +02:00
|
|
|
|
keyword_t kwd;
|
2014-09-21 16:43:17 +02:00
|
|
|
|
const char *s;
|
2014-08-23 09:17:58 +02:00
|
|
|
|
|
2016-10-31 07:45:06 +01:00
|
|
|
|
if (u->ship && is_guarded(r, u)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
cmistake(u, ord, 69, MSG_INCOME);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2016-10-31 07:45:06 +01:00
|
|
|
|
if (u->ship && is_guarded(r, u)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
cmistake(u, ord, 69, MSG_INCOME);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
/* Im Augenblick kann man nur 1 Produkt kaufen. expandbuying ist aber
|
2017-02-15 17:09:23 +01:00
|
|
|
|
* schon daf<EFBFBD>r ausger<EFBFBD>stet, mehrere Produkte zu kaufen. */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-23 09:17:58 +02:00
|
|
|
|
kwd = init_order(ord);
|
|
|
|
|
assert(kwd == K_BUY);
|
2015-05-15 13:32:22 +02:00
|
|
|
|
n = getint();
|
2016-09-19 18:42:14 +02:00
|
|
|
|
if (n <= 0) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
cmistake(u, ord, 26, MSG_COMMERCE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (besieged(u)) {
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Belagerte Einheiten k<>nnen nichts kaufen. */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
cmistake(u, ord, 60, MSG_COMMERCE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-05 19:15:43 +01:00
|
|
|
|
/* Entweder man ist Insekt in Sumpf/Wueste, oder es muss
|
|
|
|
|
* einen Handelsposten in der Region geben: */
|
|
|
|
|
if (u_race(u) != get_race(RC_INSECT) || (r->terrain == newterrain(T_SWAMP) || r->terrain == newterrain(T_DESERT))) {
|
|
|
|
|
building *b = NULL;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (r->buildings) {
|
2016-09-19 04:15:12 +02:00
|
|
|
|
static int cache;
|
|
|
|
|
static const struct building_type *bt_castle;
|
|
|
|
|
if (bt_changed(&cache)) {
|
|
|
|
|
bt_castle = bt_find("castle");
|
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
|
|
|
|
for (b = r->buildings; b; b = b->next) {
|
|
|
|
|
if (b->type == bt_castle && b->size >= 2) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (b == NULL) {
|
|
|
|
|
cmistake(u, ord, 119, MSG_COMMERCE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Ein H<>ndler kann nur 10 G<>ter pro Talentpunkt handeln. */
|
2015-08-27 16:16:55 +02:00
|
|
|
|
k = u->number * 10 * effskill(u, SK_TRADE, 0);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* hat der H<>ndler bereits gehandelt, muss die Menge der bereits
|
|
|
|
|
* verkauften/gekauften G<EFBFBD>ter abgezogen werden */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
a = a_find(u->attribs, &at_trades);
|
|
|
|
|
if (!a) {
|
|
|
|
|
a = a_add(&u->attribs, a_new(&at_trades));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
k -= a->data.i;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2017-01-10 16:31:05 +01:00
|
|
|
|
n = MIN(n, k);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (!n) {
|
|
|
|
|
cmistake(u, ord, 102, MSG_COMMERCE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
assert(n >= 0);
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* die Menge der verkauften G<>ter merken */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
a->data.i += n;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
s = gettoken(token, sizeof(token));
|
2014-09-21 16:43:17 +02:00
|
|
|
|
itype = s ? finditemtype(s, u->faction->locale) : 0;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (itype != NULL) {
|
|
|
|
|
ltype = resource2luxury(itype->rtype);
|
|
|
|
|
if (ltype == NULL) {
|
|
|
|
|
cmistake(u, ord, 124, MSG_COMMERCE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (r_demand(r, ltype)) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "luxury_notsold", ""));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
o = (request *)calloc(1, sizeof(request));
|
|
|
|
|
o->type.ltype = ltype; /* sollte immer gleich sein */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
o->unit = u;
|
|
|
|
|
o->qty = n;
|
|
|
|
|
addlist(buyorders, o);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* ------------------------------------------------------------- */
|
2014-08-31 08:58:55 +02:00
|
|
|
|
static void add_income(unit * u, int type, int want, int qty)
|
|
|
|
|
{
|
|
|
|
|
if (want == INT_MAX)
|
|
|
|
|
want = qty;
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("income",
|
|
|
|
|
"unit region mode wanted amount", u, u->region, type, want, qty));
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Steuers<72>tze in % bei Burggr<67><72>e */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static int tax_per_size[7] = { 0, 6, 12, 18, 24, 30, 36 };
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void expandselling(region * r, request * sellorders, int limit)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2015-05-15 20:56:43 +02:00
|
|
|
|
int money, price, max_products;
|
|
|
|
|
unsigned int j;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* int m, n = 0; */
|
|
|
|
|
int maxsize = 0, maxeffsize = 0;
|
|
|
|
|
int taxcollected = 0;
|
|
|
|
|
int hafencollected = 0;
|
|
|
|
|
unit *maxowner = (unit *)NULL;
|
|
|
|
|
building *maxb = (building *)NULL;
|
|
|
|
|
building *b;
|
|
|
|
|
unit *u;
|
|
|
|
|
unit *hafenowner;
|
2014-12-25 22:38:01 +01:00
|
|
|
|
static int counter[MAXLUXURIES];
|
2014-08-08 01:03:46 +02:00
|
|
|
|
static int ncounter = 0;
|
2016-09-19 04:15:12 +02:00
|
|
|
|
static int bt_cache;
|
|
|
|
|
static const struct building_type *castle_bt, *harbour_bt, *caravan_bt;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2016-09-19 04:15:12 +02:00
|
|
|
|
if (bt_changed(&bt_cache)) {
|
|
|
|
|
castle_bt = bt_find("castle");
|
|
|
|
|
harbour_bt = bt_find("harbour");
|
|
|
|
|
caravan_bt = bt_find("caravan");
|
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (ncounter == 0) {
|
|
|
|
|
const luxury_type *ltype;
|
2014-12-25 22:38:01 +01:00
|
|
|
|
for (ltype = luxurytypes; ltype; ltype = ltype->next) {
|
|
|
|
|
assert(ncounter < MAXLUXURIES);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
++ncounter;
|
2014-12-25 22:38:01 +01:00
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2014-12-25 22:38:01 +01:00
|
|
|
|
memset(counter, 0, sizeof(int) * ncounter);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
if (!sellorders) { /* NEIN, denn Insekten k<>nnen in || !r->buildings) */
|
|
|
|
|
return; /* S<>mpfen und W<>sten auch so handeln */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Stelle Eigent<6E>mer der gr<67><72>ten Burg fest. Bekommt Steuern aus jedem
|
|
|
|
|
* Verkauf. Wenn zwei Burgen gleicher Gr<EFBFBD><EFBFBD>e bekommt gar keiner etwas. */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (b = rbuildings(r); b; b = b->next) {
|
|
|
|
|
if (b->size > maxsize && building_owner(b) != NULL
|
2016-08-30 10:19:19 +02:00
|
|
|
|
&& b->type == castle_bt) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
maxb = b;
|
|
|
|
|
maxsize = b->size;
|
|
|
|
|
maxowner = building_owner(b);
|
|
|
|
|
}
|
2016-08-30 10:19:19 +02:00
|
|
|
|
else if (b->size == maxsize && b->type == castle_bt) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
maxb = (building *)NULL;
|
|
|
|
|
maxowner = (unit *)NULL;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2016-09-19 04:15:12 +02:00
|
|
|
|
hafenowner = owner_buildingtyp(r, harbour_bt);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (maxb != (building *)NULL && maxowner != (unit *)NULL) {
|
|
|
|
|
maxeffsize = buildingeffsize(maxb, false);
|
|
|
|
|
if (maxeffsize == 0) {
|
|
|
|
|
maxb = (building *)NULL;
|
|
|
|
|
maxowner = (unit *)NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Die Region muss genug Geld haben, um die Produkte kaufen zu k<>nnen. */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
money = rmoney(r);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* max_products sind 1/100 der Bev<65>lkerung, falls soviele Produkte
|
2014-08-08 01:03:46 +02:00
|
|
|
|
* verkauft werden - counter[] - sinkt die Nachfrage um 1 Punkt.
|
2017-02-15 17:09:23 +01:00
|
|
|
|
* multiplier speichert r->demand f<EFBFBD>r die debug message ab. */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
|
|
|
|
max_products = rpeasants(r) / TRADE_FRACTION;
|
|
|
|
|
if (max_products <= 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (r->terrain == newterrain(T_DESERT)
|
2016-09-19 04:15:12 +02:00
|
|
|
|
&& buildingtype_exists(r, caravan_bt, true)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
max_products = rpeasants(r) * 2 / TRADE_FRACTION;
|
|
|
|
|
}
|
|
|
|
|
/* Verkauf: so programmiert, dass er leicht auf mehrere Gueter pro
|
|
|
|
|
* Runde erweitert werden kann. */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-06-25 08:13:18 +02:00
|
|
|
|
expandorders(r, sellorders);
|
|
|
|
|
if (!norders)
|
|
|
|
|
return;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-06-25 08:13:18 +02:00
|
|
|
|
for (j = 0; j != norders; j++) {
|
|
|
|
|
const luxury_type *search = NULL;
|
2016-11-20 19:08:39 +01:00
|
|
|
|
const luxury_type *ltype = g_requests[j].type.ltype;
|
2014-06-25 08:13:18 +02:00
|
|
|
|
int multi = r_demand(r, ltype);
|
|
|
|
|
int i;
|
|
|
|
|
int use = 0;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (i = 0, search = luxurytypes; search != ltype; search = search->next) {
|
2017-02-18 21:15:14 +01:00
|
|
|
|
/* TODO: this is slow and lame! */
|
2014-06-25 08:13:18 +02:00
|
|
|
|
++i;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2014-06-25 08:13:18 +02:00
|
|
|
|
if (counter[i] >= limit)
|
|
|
|
|
continue;
|
|
|
|
|
if (counter[i] + 1 > max_products && multi > 1)
|
|
|
|
|
--multi;
|
|
|
|
|
price = ltype->price * multi;
|
|
|
|
|
|
|
|
|
|
if (money >= price) {
|
|
|
|
|
int abgezogenhafen = 0;
|
|
|
|
|
int abgezogensteuer = 0;
|
2016-11-20 19:08:39 +01:00
|
|
|
|
unit *u = g_requests[j].unit;
|
2014-06-25 08:13:18 +02:00
|
|
|
|
item *itm;
|
|
|
|
|
attrib *a = a_find(u->attribs, &at_luxuries);
|
|
|
|
|
if (a == NULL)
|
|
|
|
|
a = a_add(&u->attribs, a_new(&at_luxuries));
|
2014-08-08 01:03:46 +02:00
|
|
|
|
itm = (item *)a->data.v;
|
2014-06-25 08:13:18 +02:00
|
|
|
|
i_change(&itm, ltype->itype, 1);
|
|
|
|
|
a->data.v = itm;
|
|
|
|
|
++use;
|
|
|
|
|
if (u->n < 0)
|
|
|
|
|
u->n = 0;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2014-06-25 08:13:18 +02:00
|
|
|
|
if (hafenowner != NULL) {
|
|
|
|
|
if (hafenowner->faction != u->faction) {
|
|
|
|
|
abgezogenhafen = price / 10;
|
|
|
|
|
hafencollected += abgezogenhafen;
|
|
|
|
|
price -= abgezogenhafen;
|
|
|
|
|
money -= abgezogenhafen;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (maxb != NULL) {
|
|
|
|
|
if (maxowner->faction != u->faction) {
|
|
|
|
|
abgezogensteuer = price * tax_per_size[maxeffsize] / 100;
|
|
|
|
|
taxcollected += abgezogensteuer;
|
|
|
|
|
price -= abgezogensteuer;
|
|
|
|
|
money -= abgezogensteuer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
u->n += price;
|
|
|
|
|
change_money(u, price);
|
|
|
|
|
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* r->money -= price; --- dies wird eben nicht ausgef<65>hrt, denn die
|
|
|
|
|
* Produkte k<EFBFBD>nnen auch als Steuern eingetrieben werden. In der Region
|
|
|
|
|
* wurden Silberst<EFBFBD>cke gegen Luxusg<EFBFBD>ter des selben Wertes eingetauscht!
|
2014-06-25 08:13:18 +02:00
|
|
|
|
* Falls mehr als max_products Kunden ein Produkt gekauft haben, sinkt
|
2017-02-15 17:09:23 +01:00
|
|
|
|
* die Nachfrage f<EFBFBD>r das Produkt um 1. Der Z<EFBFBD>hler wird wieder auf 0
|
2014-06-25 08:13:18 +02:00
|
|
|
|
* gesetzt. */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2014-06-25 08:13:18 +02:00
|
|
|
|
if (++counter[i] > max_products) {
|
|
|
|
|
int d = r_demand(r, ltype);
|
|
|
|
|
if (d > 1) {
|
|
|
|
|
r_setdemand(r, ltype, d - 1);
|
|
|
|
|
}
|
|
|
|
|
counter[i] = 0;
|
|
|
|
|
}
|
2014-08-13 19:55:10 +02:00
|
|
|
|
}
|
2014-06-25 08:13:18 +02:00
|
|
|
|
if (use > 0) {
|
2016-11-20 19:08:39 +01:00
|
|
|
|
use_pooled(g_requests[j].unit, ltype->itype->rtype, GET_DEFAULT, use);
|
2014-06-25 08:13:18 +02:00
|
|
|
|
}
|
2014-08-13 19:55:10 +02:00
|
|
|
|
}
|
2016-11-20 19:08:39 +01:00
|
|
|
|
free(g_requests);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Steuern. Hier werden die Steuern dem Besitzer der gr<67><72>ten Burg gegeben. */
|
2014-06-25 08:13:18 +02:00
|
|
|
|
if (maxowner) {
|
|
|
|
|
if (taxcollected > 0) {
|
|
|
|
|
change_money(maxowner, (int)taxcollected);
|
|
|
|
|
add_income(maxowner, IC_TRADETAX, taxcollected, taxcollected);
|
|
|
|
|
/* TODO: Meldung
|
|
|
|
|
* "%s verdient %d Silber durch den Handel in %s.",
|
|
|
|
|
* unitname(maxowner), (int) taxcollected, regionname(r)); */
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2014-06-25 08:13:18 +02:00
|
|
|
|
if (hafenowner) {
|
|
|
|
|
if (hafencollected > 0) {
|
|
|
|
|
change_money(hafenowner, (int)hafencollected);
|
|
|
|
|
add_income(hafenowner, IC_TRADETAX, hafencollected, hafencollected);
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* Berichte an die Einheiten */
|
|
|
|
|
|
2014-06-25 08:13:18 +02:00
|
|
|
|
for (u = r->units; u; u = u->next) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2014-06-25 08:13:18 +02:00
|
|
|
|
attrib *a = a_find(u->attribs, &at_luxuries);
|
|
|
|
|
item *itm;
|
|
|
|
|
if (a == NULL)
|
|
|
|
|
continue;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (itm = (item *)a->data.v; itm; itm = itm->next) {
|
2014-06-25 08:13:18 +02:00
|
|
|
|
if (itm->number) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("sellamount",
|
2014-08-08 01:03:46 +02:00
|
|
|
|
"unit amount resource", u, itm->number, itm->type->rtype));
|
2014-06-25 08:13:18 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
a_remove(&u->attribs, a);
|
|
|
|
|
add_income(u, IC_TRADE, u->n, u->n);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2014-08-13 19:55:10 +02:00
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2012-06-24 07:41:07 +02:00
|
|
|
|
static bool sell(unit * u, request ** sellorders, struct order *ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-12-22 16:28:17 +01:00
|
|
|
|
char token[128];
|
2014-08-08 01:03:46 +02:00
|
|
|
|
bool unlimited = true;
|
|
|
|
|
const item_type *itype;
|
2014-08-23 09:17:58 +02:00
|
|
|
|
const luxury_type *ltype;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
int n;
|
|
|
|
|
region *r = u->region;
|
|
|
|
|
const char *s;
|
2014-08-23 09:17:58 +02:00
|
|
|
|
keyword_t kwd;
|
2016-09-19 04:15:12 +02:00
|
|
|
|
static int bt_cache;
|
|
|
|
|
static const struct building_type *castle_bt, *caravan_bt;
|
|
|
|
|
|
|
|
|
|
if (bt_changed(&bt_cache)) {
|
|
|
|
|
castle_bt = bt_find("castle");
|
|
|
|
|
caravan_bt = bt_find("caravan");
|
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2016-10-31 07:45:06 +01:00
|
|
|
|
if (u->ship && is_guarded(r, u)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
cmistake(u, ord, 69, MSG_INCOME);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* sellorders sind KEIN array, weil f<>r alle items DIE SELBE resource
|
2014-08-08 01:03:46 +02:00
|
|
|
|
* (das geld der region) aufgebraucht wird. */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-23 09:17:58 +02:00
|
|
|
|
kwd = init_order(ord);
|
|
|
|
|
assert(kwd == K_SELL);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
s = gettoken(token, sizeof(token));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (findparam(s, u->faction->locale) == P_ANY) {
|
|
|
|
|
unlimited = false;
|
|
|
|
|
n = rpeasants(r) / TRADE_FRACTION;
|
|
|
|
|
if (r->terrain == newterrain(T_DESERT)
|
2016-09-19 04:15:12 +02:00
|
|
|
|
&& buildingtype_exists(r, caravan_bt, true))
|
2014-08-08 01:03:46 +02:00
|
|
|
|
n *= 2;
|
|
|
|
|
if (n == 0) {
|
|
|
|
|
cmistake(u, ord, 303, MSG_COMMERCE);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
else {
|
2016-11-14 22:27:44 +01:00
|
|
|
|
n = s ? atoip(s) : 0;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (n == 0) {
|
|
|
|
|
cmistake(u, ord, 27, MSG_COMMERCE);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
}
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Belagerte Einheiten k<>nnen nichts verkaufen. */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (besieged(u)) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error60", ""));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* In der Region mu<6D> es eine Burg geben. */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (u_race(u) == get_race(RC_INSECT)) {
|
|
|
|
|
if (r->terrain != newterrain(T_SWAMP) && r->terrain != newterrain(T_DESERT)
|
|
|
|
|
&& !rbuildings(r)) {
|
|
|
|
|
cmistake(u, ord, 119, MSG_COMMERCE);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* ...oder in der Region mu<6D> es eine Burg geben. */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
building *b = 0;
|
|
|
|
|
if (r->buildings) {
|
|
|
|
|
for (b = r->buildings; b; b = b->next) {
|
2016-09-19 04:15:12 +02:00
|
|
|
|
if (b->type == castle_bt && b->size >= 2) break;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!b) {
|
|
|
|
|
cmistake(u, ord, 119, MSG_COMMERCE);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Ein H<>ndler kann nur 10 G<>ter pro Talentpunkt verkaufen. */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2017-01-10 16:31:05 +01:00
|
|
|
|
n = MIN(n, u->number * 10 * effskill(u, SK_TRADE, 0));
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (!n) {
|
|
|
|
|
cmistake(u, ord, 54, MSG_COMMERCE);
|
|
|
|
|
return false;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2014-12-22 16:28:17 +01:00
|
|
|
|
s = gettoken(token, sizeof(token));
|
2014-08-23 09:17:58 +02:00
|
|
|
|
itype = s ? finditemtype(s, u->faction->locale) : 0;
|
|
|
|
|
ltype = itype ? resource2luxury(itype->rtype) : 0;
|
2016-11-20 19:08:39 +01:00
|
|
|
|
if (ltype == NULL || itype == NULL) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
cmistake(u, ord, 126, MSG_COMMERCE);
|
|
|
|
|
return false;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
else {
|
|
|
|
|
attrib *a;
|
|
|
|
|
request *o;
|
|
|
|
|
int k, available;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (!r_demand(r, ltype)) {
|
|
|
|
|
cmistake(u, ord, 263, MSG_COMMERCE);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
available = get_pooled(u, itype->rtype, GET_DEFAULT, INT_MAX);
|
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Wenn andere Einheiten das selbe verkaufen, mu<6D> ihr Zeug abgezogen
|
2014-08-08 01:03:46 +02:00
|
|
|
|
* werden damit es nicht zweimal verkauft wird: */
|
|
|
|
|
for (o = *sellorders; o; o = o->next) {
|
|
|
|
|
if (o->type.ltype == ltype && o->unit->faction == u->faction) {
|
|
|
|
|
int fpool =
|
|
|
|
|
o->qty - get_pooled(o->unit, itype->rtype, GET_RESERVE, INT_MAX);
|
2017-01-10 16:31:05 +01:00
|
|
|
|
available -= MAX(0, fpool);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2017-01-10 16:31:05 +01:00
|
|
|
|
n = MIN(n, available);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (n <= 0) {
|
|
|
|
|
cmistake(u, ord, 264, MSG_COMMERCE);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
/* Hier wird request->type verwendet, weil die obere limit durch
|
2017-02-15 17:09:23 +01:00
|
|
|
|
* das silber gegeben wird (region->money), welches f<EFBFBD>r alle
|
2014-08-08 01:03:46 +02:00
|
|
|
|
* (!) produkte als summe gilt, als nicht wie bei der
|
2017-02-15 17:09:23 +01:00
|
|
|
|
* produktion, wo f<EFBFBD>r jedes produkt einzeln eine obere limite
|
2014-08-08 01:03:46 +02:00
|
|
|
|
* existiert, so dass man arrays von orders machen kann. */
|
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Ein H<>ndler kann nur 10 G<>ter pro Talentpunkt handeln. */
|
2015-08-27 16:16:55 +02:00
|
|
|
|
k = u->number * 10 * effskill(u, SK_TRADE, 0);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* hat der H<>ndler bereits gehandelt, muss die Menge der bereits
|
|
|
|
|
* verkauften/gekauften G<EFBFBD>ter abgezogen werden */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
a = a_find(u->attribs, &at_trades);
|
|
|
|
|
if (!a) {
|
|
|
|
|
a = a_add(&u->attribs, a_new(&at_trades));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
k -= a->data.i;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2017-01-10 16:31:05 +01:00
|
|
|
|
n = MIN(n, k);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
assert(n >= 0);
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* die Menge der verkauften G<>ter merken */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
a->data.i += n;
|
|
|
|
|
o = (request *)calloc(1, sizeof(request));
|
|
|
|
|
o->unit = u;
|
|
|
|
|
o->qty = n;
|
|
|
|
|
o->type.ltype = ltype;
|
|
|
|
|
addlist(sellorders, o);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
return unlimited;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void expandstealing(region * r, request * stealorders)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-06-24 16:42:45 +02:00
|
|
|
|
const resource_type *rsilver = get_resourcetype(R_SILVER);
|
2015-05-15 20:56:43 +02:00
|
|
|
|
unsigned int j;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2014-06-24 16:42:45 +02:00
|
|
|
|
assert(rsilver);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-06-24 16:42:45 +02:00
|
|
|
|
expandorders(r, stealorders);
|
|
|
|
|
if (!norders) return;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* F<>r jede unit in der Region wird Geld geklaut, wenn sie Opfer eines
|
|
|
|
|
* Beklauen-Orders ist. Jedes Opfer mu<EFBFBD> einzeln behandelt werden.
|
2014-08-08 01:03:46 +02:00
|
|
|
|
*
|
|
|
|
|
* u ist die beklaute unit. oa.unit ist die klauende unit.
|
|
|
|
|
*/
|
|
|
|
|
|
2016-11-20 19:08:39 +01:00
|
|
|
|
for (j = 0; j != norders && g_requests[j].unit->n <= g_requests[j].unit->wants; j++) {
|
|
|
|
|
unit *u = findunitg(g_requests[j].no, r);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
int n = 0;
|
|
|
|
|
if (u && u->region == r) {
|
|
|
|
|
n = get_pooled(u, rsilver, GET_ALL, INT_MAX);
|
|
|
|
|
}
|
|
|
|
|
if (n > 10 && rplane(r) && (rplane(r)->flags & PFL_NOALLIANCES)) {
|
|
|
|
|
/* In Questen nur reduziertes Klauen */
|
|
|
|
|
n = 10;
|
|
|
|
|
}
|
|
|
|
|
if (n > 0) {
|
2017-01-10 16:31:05 +01:00
|
|
|
|
n = MIN(n, g_requests[j].unit->wants);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
use_pooled(u, rsilver, GET_ALL, n);
|
2016-11-20 19:08:39 +01:00
|
|
|
|
g_requests[j].unit->n = n;
|
|
|
|
|
change_money(g_requests[j].unit, n);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("stealeffect", "unit region amount",
|
|
|
|
|
u, u->region, n));
|
|
|
|
|
}
|
2016-11-20 19:08:39 +01:00
|
|
|
|
add_income(g_requests[j].unit, IC_STEAL, g_requests[j].unit->wants, g_requests[j].unit->n);
|
|
|
|
|
fset(g_requests[j].unit, UFL_LONGACTION | UFL_NOTMOVING);
|
2014-08-13 19:55:10 +02:00
|
|
|
|
}
|
2016-11-20 19:08:39 +01:00
|
|
|
|
free(g_requests);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
2015-08-27 16:53:36 +02:00
|
|
|
|
static void plant(unit * u, int raw)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-06-24 16:42:45 +02:00
|
|
|
|
int n, i, skill, planted = 0;
|
|
|
|
|
const item_type *itype;
|
|
|
|
|
const resource_type *rt_water = get_resourcetype(R_WATER_OF_LIFE);
|
2015-08-27 16:53:36 +02:00
|
|
|
|
region *r = u->region;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-06-24 16:42:45 +02:00
|
|
|
|
assert(rt_water != NULL);
|
|
|
|
|
if (!fval(r->terrain, LAND_REGION)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2016-11-20 19:08:39 +01:00
|
|
|
|
itype = rherbtype(r);
|
|
|
|
|
if (itype == NULL) {
|
2014-06-24 16:42:45 +02:00
|
|
|
|
cmistake(u, u->thisorder, 108, MSG_PRODUCE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Skill pr<70>fen */
|
2015-08-27 16:16:55 +02:00
|
|
|
|
skill = effskill(u, SK_HERBALISM, 0);
|
2014-06-24 16:42:45 +02:00
|
|
|
|
if (skill < 6) {
|
|
|
|
|
ADDMSG(&u->faction->msgs,
|
2014-08-08 01:03:46 +02:00
|
|
|
|
msg_feedback(u, u->thisorder, "plant_skills",
|
2016-09-19 18:42:14 +02:00
|
|
|
|
"skill minskill product", SK_HERBALISM, 6, itype->rtype, 1));
|
2014-06-24 16:42:45 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Wasser des Lebens pr<70>fen */
|
2014-06-24 16:42:45 +02:00
|
|
|
|
if (get_pooled(u, rt_water, GET_DEFAULT, 1) == 0) {
|
|
|
|
|
ADDMSG(&u->faction->msgs,
|
2014-08-08 01:03:46 +02:00
|
|
|
|
msg_feedback(u, u->thisorder, "resource_missing", "missing", rt_water));
|
2014-06-24 16:42:45 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
n = get_pooled(u, itype->rtype, GET_DEFAULT, skill * u->number);
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Kr<4B>uter pr<70>fen */
|
2014-06-24 16:42:45 +02:00
|
|
|
|
if (n == 0) {
|
|
|
|
|
ADDMSG(&u->faction->msgs,
|
2014-08-08 01:03:46 +02:00
|
|
|
|
msg_feedback(u, u->thisorder, "resource_missing", "missing",
|
2016-09-19 18:42:14 +02:00
|
|
|
|
itype->rtype));
|
2014-06-24 16:42:45 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2017-01-10 16:31:05 +01:00
|
|
|
|
n = MIN(skill * u->number, n);
|
|
|
|
|
n = MIN(raw, n);
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* F<>r jedes Kraut Talent*10% Erfolgschance. */
|
2014-06-24 16:42:45 +02:00
|
|
|
|
for (i = n; i > 0; i--) {
|
|
|
|
|
if (rng_int() % 10 < skill)
|
|
|
|
|
planted++;
|
|
|
|
|
}
|
|
|
|
|
produceexp(u, SK_HERBALISM, u->number);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2014-06-24 16:42:45 +02:00
|
|
|
|
/* Alles ok. Abziehen. */
|
|
|
|
|
use_pooled(u, rt_water, GET_DEFAULT, 1);
|
|
|
|
|
use_pooled(u, itype->rtype, GET_DEFAULT, n);
|
2016-09-19 18:42:14 +02:00
|
|
|
|
rsetherbs(r, (short)(rherbs(r) + planted));
|
2014-06-24 16:42:45 +02:00
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("plant", "unit region amount herb",
|
2014-08-08 01:03:46 +02:00
|
|
|
|
u, r, planted, itype->rtype));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-08-27 16:53:36 +02:00
|
|
|
|
static void planttrees(unit * u, int raw)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
int n, i, skill, planted = 0;
|
|
|
|
|
const resource_type *rtype;
|
2015-08-27 16:53:36 +02:00
|
|
|
|
region * r = u->region;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (!fval(r->terrain, LAND_REGION)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Mallornb<6E>ume kann man nur in Mallornregionen z<>chten */
|
2017-03-05 11:10:59 +01:00
|
|
|
|
rtype = get_resourcetype(fval(r, RF_MALLORN) ? R_MALLORN_SEED : R_SEED);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Skill pr<70>fen */
|
2015-08-27 16:16:55 +02:00
|
|
|
|
skill = effskill(u, SK_HERBALISM, 0);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (skill < 6) {
|
|
|
|
|
ADDMSG(&u->faction->msgs,
|
|
|
|
|
msg_feedback(u, u->thisorder, "plant_skills",
|
2016-09-19 18:42:14 +02:00
|
|
|
|
"skill minskill product", SK_HERBALISM, 6, rtype, 1));
|
2014-08-08 01:03:46 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (fval(r, RF_MALLORN) && skill < 7) {
|
|
|
|
|
ADDMSG(&u->faction->msgs,
|
|
|
|
|
msg_feedback(u, u->thisorder, "plant_skills",
|
2016-09-19 18:42:14 +02:00
|
|
|
|
"skill minskill product", SK_HERBALISM, 7, rtype, 1));
|
2014-08-08 01:03:46 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* wenn eine Anzahl angegeben wurde, nur soviel verbrauchen */
|
2017-01-10 16:31:05 +01:00
|
|
|
|
raw = MIN(raw, skill * u->number);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
n = get_pooled(u, rtype, GET_DEFAULT, raw);
|
|
|
|
|
if (n == 0) {
|
|
|
|
|
ADDMSG(&u->faction->msgs,
|
|
|
|
|
msg_feedback(u, u->thisorder, "resource_missing", "missing", rtype));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-01-10 16:31:05 +01:00
|
|
|
|
n = MIN(raw, n);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* F<>r jeden Samen Talent*10% Erfolgschance. */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (i = n; i > 0; i--) {
|
|
|
|
|
if (rng_int() % 10 < skill)
|
|
|
|
|
planted++;
|
|
|
|
|
}
|
|
|
|
|
rsettrees(r, 0, rtrees(r, 0) + planted);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* Alles ok. Abziehen. */
|
|
|
|
|
produceexp(u, SK_HERBALISM, u->number);
|
|
|
|
|
use_pooled(u, rtype, GET_DEFAULT, n);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("plant",
|
|
|
|
|
"unit region amount herb", u, r, planted, rtype));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* z<>chte b<>ume */
|
2015-08-27 16:53:36 +02:00
|
|
|
|
static void breedtrees(unit * u, int raw)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
int n, i, skill, planted = 0;
|
|
|
|
|
const resource_type *rtype;
|
2015-11-21 12:28:20 +01:00
|
|
|
|
int current_season;
|
2015-08-27 16:53:36 +02:00
|
|
|
|
region *r = u->region;
|
2015-11-21 12:28:20 +01:00
|
|
|
|
gamedate date;
|
2016-09-19 18:42:14 +02:00
|
|
|
|
|
2015-11-21 12:28:20 +01:00
|
|
|
|
get_gamedate(turn, &date);
|
|
|
|
|
current_season = date.season;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* B<>ume z<>chten geht nur im Fr<46>hling */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (current_season != SEASON_SPRING) {
|
2015-08-27 16:53:36 +02:00
|
|
|
|
planttrees(u, raw);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (!fval(r->terrain, LAND_REGION)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Mallornb<6E>ume kann man nur in Mallornregionen z<>chten */
|
2017-03-05 11:10:59 +01:00
|
|
|
|
rtype = get_resourcetype(fval(r, RF_MALLORN) ? R_MALLORN_SEED : R_SEED);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Skill pr<70>fen */
|
2015-08-27 16:16:55 +02:00
|
|
|
|
skill = effskill(u, SK_HERBALISM, 0);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (skill < 12) {
|
2015-08-27 16:53:36 +02:00
|
|
|
|
planttrees(u, raw);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* wenn eine Anzahl angegeben wurde, nur soviel verbrauchen */
|
2017-01-10 16:31:05 +01:00
|
|
|
|
raw = MIN(skill * u->number, raw);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
n = get_pooled(u, rtype, GET_DEFAULT, raw);
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Samen pr<70>fen */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (n == 0) {
|
|
|
|
|
ADDMSG(&u->faction->msgs,
|
|
|
|
|
msg_feedback(u, u->thisorder, "resource_missing", "missing", rtype));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-01-10 16:31:05 +01:00
|
|
|
|
n = MIN(raw, n);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* F<>r jeden Samen Talent*5% Erfolgschance. */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (i = n; i > 0; i--) {
|
|
|
|
|
if (rng_int() % 100 < skill * 5)
|
|
|
|
|
planted++;
|
|
|
|
|
}
|
|
|
|
|
rsettrees(r, 1, rtrees(r, 1) + planted);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* Alles ok. Abziehen. */
|
|
|
|
|
produceexp(u, SK_HERBALISM, u->number);
|
|
|
|
|
use_pooled(u, rtype, GET_DEFAULT, n);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("plant",
|
|
|
|
|
"unit region amount herb", u, r, planted, rtype));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* z<>chte pferde */
|
2015-08-27 16:59:39 +02:00
|
|
|
|
static void breedhorses(unit * u)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-06-23 16:28:10 +02:00
|
|
|
|
int n, c, breed = 0;
|
2014-06-24 16:42:45 +02:00
|
|
|
|
const struct resource_type *rhorse = get_resourcetype(R_HORSE);
|
2015-08-27 16:16:55 +02:00
|
|
|
|
int horses, effsk;
|
2016-09-19 04:15:12 +02:00
|
|
|
|
static int bt_cache;
|
|
|
|
|
static const struct building_type *stables_bt;
|
|
|
|
|
|
|
|
|
|
if (bt_changed(&bt_cache)) {
|
|
|
|
|
stables_bt = bt_find("stables");
|
|
|
|
|
}
|
2015-08-27 16:59:39 +02:00
|
|
|
|
|
2014-06-24 16:42:45 +02:00
|
|
|
|
assert(rhorse && rhorse->itype);
|
2016-09-19 04:15:12 +02:00
|
|
|
|
if (!active_building(u, stables_bt)) {
|
2014-06-23 16:28:10 +02:00
|
|
|
|
cmistake(u, u->thisorder, 122, MSG_PRODUCE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-06-24 16:42:45 +02:00
|
|
|
|
horses = i_get(u->items, rhorse->itype);
|
|
|
|
|
if (horses < 2) {
|
2014-06-23 16:28:10 +02:00
|
|
|
|
cmistake(u, u->thisorder, 107, MSG_PRODUCE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-08-27 16:16:55 +02:00
|
|
|
|
effsk = effskill(u, SK_HORSE_TRAINING, 0);
|
|
|
|
|
n = u->number * effsk;
|
2017-01-10 16:31:05 +01:00
|
|
|
|
n = MIN(n, horses);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-06-23 16:28:10 +02:00
|
|
|
|
for (c = 0; c < n; c++) {
|
2015-08-27 16:16:55 +02:00
|
|
|
|
if (rng_int() % 100 < effsk) {
|
2014-06-24 16:42:45 +02:00
|
|
|
|
i_change(&u->items, rhorse->itype, 1);
|
2014-06-23 16:28:10 +02:00
|
|
|
|
++breed;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-06-23 16:28:10 +02:00
|
|
|
|
produceexp(u, SK_HORSE_TRAINING, u->number);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-06-23 16:28:10 +02:00
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("raised", "unit amount", u, breed));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void breed_cmd(unit * u, struct order *ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-12-22 16:28:17 +01:00
|
|
|
|
char token[128];
|
2014-08-08 01:03:46 +02:00
|
|
|
|
int m;
|
|
|
|
|
const char *s;
|
|
|
|
|
param_t p;
|
|
|
|
|
region *r = u->region;
|
|
|
|
|
const resource_type *rtype = NULL;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (r->land == NULL) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_onlandonly", ""));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* z<>chte [<anzahl>] <parameter> */
|
2014-08-23 09:17:58 +02:00
|
|
|
|
(void)init_order(ord);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
s = gettoken(token, sizeof(token));
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2016-11-14 22:27:44 +01:00
|
|
|
|
m = s ? atoip(s) : 0;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (m != 0) {
|
|
|
|
|
/* first came a want-paramter */
|
2014-12-22 16:28:17 +01:00
|
|
|
|
s = gettoken(token, sizeof(token));
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
m = INT_MAX;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-24 12:41:39 +02:00
|
|
|
|
if (!s || !s[0]) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
p = P_ANY;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
p = findparam(s, u->faction->locale);
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
switch (p) {
|
|
|
|
|
case P_HERBS:
|
2015-08-27 16:53:36 +02:00
|
|
|
|
plant(u, m);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
break;
|
|
|
|
|
case P_TREES:
|
2015-08-27 16:53:36 +02:00
|
|
|
|
breedtrees(u, m);
|
2011-03-08 08:44:20 +01:00
|
|
|
|
break;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
default:
|
|
|
|
|
if (p != P_ANY) {
|
|
|
|
|
rtype = findresourcetype(s, u->faction->locale);
|
2017-03-05 11:10:59 +01:00
|
|
|
|
if (rtype == get_resourcetype(R_SEED) || rtype == get_resourcetype(R_MALLORN_SEED)) {
|
2015-08-27 16:53:36 +02:00
|
|
|
|
breedtrees(u, m);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (rtype != get_resourcetype(R_HORSE)) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_cannotmake", ""));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-08-27 16:59:39 +02:00
|
|
|
|
breedhorses(u);
|
2011-03-08 08:44:20 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static const char *rough_amount(int a, int m)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
int p = (a * 100) / m;
|
|
|
|
|
|
|
|
|
|
if (p < 10) {
|
|
|
|
|
return "sehr wenige";
|
|
|
|
|
}
|
|
|
|
|
else if (p < 30) {
|
|
|
|
|
return "wenige";
|
|
|
|
|
}
|
|
|
|
|
else if (p < 60) {
|
|
|
|
|
return "relativ viele";
|
|
|
|
|
}
|
|
|
|
|
else if (p < 90) {
|
|
|
|
|
return "viele";
|
|
|
|
|
}
|
|
|
|
|
return "sehr viele";
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void research_cmd(unit * u, struct order *ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
region *r = u->region;
|
2014-08-23 09:17:58 +02:00
|
|
|
|
keyword_t kwd;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-23 09:17:58 +02:00
|
|
|
|
kwd = init_order(ord);
|
|
|
|
|
assert(kwd == K_RESEARCH);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2015-08-27 16:16:55 +02:00
|
|
|
|
if (effskill(u, SK_HERBALISM, 0) < 7) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
cmistake(u, ord, 227, MSG_EVENT);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
produceexp(u, SK_HERBALISM, u->number);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (rherbs(r) > 0) {
|
|
|
|
|
const item_type *itype = rherbtype(r);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (itype != NULL) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("researchherb",
|
|
|
|
|
"unit region amount herb",
|
|
|
|
|
u, r, rough_amount(rherbs(r), 100), itype->rtype));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("researchherb_none",
|
|
|
|
|
"unit region", u, r));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("researchherb_none",
|
|
|
|
|
"unit region", u, r));
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static int max_skill(region * r, faction * f, skill_t sk)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
unit *u;
|
|
|
|
|
int w = 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (u = r->units; u; u = u->next) {
|
|
|
|
|
if (u->faction == f) {
|
2015-08-27 16:16:55 +02:00
|
|
|
|
int effsk = effskill(u, sk, 0);
|
|
|
|
|
if (effsk > w) {
|
|
|
|
|
w = effsk;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
return w;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-07-06 06:12:34 +02:00
|
|
|
|
message * check_steal(const unit * u, struct order *ord) {
|
2014-07-06 05:14:11 +02:00
|
|
|
|
plane *pl;
|
|
|
|
|
|
|
|
|
|
if (fval(u_race(u), RCF_NOSTEAL)) {
|
|
|
|
|
return msg_feedback(u, ord, "race_nosteal", "race", u_race(u));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fval(u->region->terrain, SEA_REGION) && u_race(u) != get_race(RC_AQUARIAN)) {
|
|
|
|
|
return msg_feedback(u, ord, "error_onlandonly", "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pl = rplane(u->region);
|
|
|
|
|
if (pl && fval(pl, PFL_NOATTACK)) {
|
|
|
|
|
return msg_feedback(u, ord, "error270", "");
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void steal_cmd(unit * u, struct order *ord, request ** stealorders)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
const resource_type *rring = get_resourcetype(R_RING_OF_NIMBLEFINGER);
|
2015-08-27 16:16:55 +02:00
|
|
|
|
int n, i, id, effsk;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
bool goblin = false;
|
|
|
|
|
request *o;
|
|
|
|
|
unit *u2 = NULL;
|
|
|
|
|
region *r = u->region;
|
|
|
|
|
faction *f = NULL;
|
|
|
|
|
message * msg;
|
2014-08-23 09:17:58 +02:00
|
|
|
|
keyword_t kwd;
|
|
|
|
|
|
|
|
|
|
kwd = init_order(ord);
|
|
|
|
|
assert(kwd == K_STEAL);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
assert(skill_enabled(SK_PERCEPTION) && skill_enabled(SK_STEALTH));
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
msg = check_steal(u, ord);
|
|
|
|
|
if (msg) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
id = read_unitid(u->faction, r);
|
2016-09-19 18:42:14 +02:00
|
|
|
|
if (id > 0) {
|
2015-09-09 13:29:58 +02:00
|
|
|
|
u2 = findunitr(r, id);
|
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (u2 && u2->region == u->region) {
|
|
|
|
|
f = u2->faction;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2017-02-18 21:15:14 +01:00
|
|
|
|
/* TODO: is this really necessary? it's the only time we use faction.c/deadhash
|
|
|
|
|
* it allows stealing from a unit in a dead faction, but why? */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
f = dfindhash(id);
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (u2 = r->units; u2; u2 = u2->next) {
|
|
|
|
|
if (u2->faction == f && cansee(u->faction, r, u2, 0))
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (!u2) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
|
|
|
|
|
""));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (IsImmune(u2->faction)) {
|
|
|
|
|
ADDMSG(&u->faction->msgs,
|
|
|
|
|
msg_feedback(u, ord, "newbie_immunity_error", "turns", NewbieImmunity()));
|
|
|
|
|
return;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (u->faction->alliance && u->faction->alliance == u2->faction->alliance) {
|
|
|
|
|
cmistake(u, ord, 47, MSG_INCOME);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
assert(u->region == u2->region);
|
|
|
|
|
if (!can_contact(r, u, u2)) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error60", ""));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2015-08-27 16:16:55 +02:00
|
|
|
|
effsk = effskill(u, SK_STEALTH, 0);
|
|
|
|
|
n = effsk - max_skill(r, f, SK_PERCEPTION);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (n <= 0) {
|
|
|
|
|
/* Wahrnehmung == Tarnung */
|
2015-08-27 16:16:55 +02:00
|
|
|
|
if (u_race(u) != get_race(RC_GOBLIN) || effsk <= 3) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("stealfail", "unit target", u, u2));
|
|
|
|
|
if (n == 0) {
|
|
|
|
|
ADDMSG(&u2->faction->msgs, msg_message("stealdetect", "unit", u2));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ADDMSG(&u2->faction->msgs, msg_message("thiefdiscover", "unit target",
|
|
|
|
|
u, u2));
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("stealfatal", "unit target", u,
|
|
|
|
|
u2));
|
|
|
|
|
ADDMSG(&u2->faction->msgs, msg_message("thiefdiscover", "unit target", u,
|
|
|
|
|
u2));
|
|
|
|
|
n = 1;
|
|
|
|
|
goblin = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-10 16:31:05 +01:00
|
|
|
|
i = MIN(u->number, i_get(u->items, rring->itype));
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (i > 0) {
|
|
|
|
|
n *= STEALINCOME * (u->number + i * (roqf_factor() - 1));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
n *= u->number * STEALINCOME;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
u->wants = n;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* wer dank unsichtbarkeitsringen klauen kann, muss nicht unbedingt ein
|
|
|
|
|
* guter dieb sein, schliesslich macht man immer noch sehr viel laerm */
|
|
|
|
|
|
|
|
|
|
o = (request *)calloc(1, sizeof(request));
|
|
|
|
|
o->unit = u;
|
|
|
|
|
o->qty = 1; /* Betrag steht in u->wants */
|
|
|
|
|
o->no = u2->no;
|
|
|
|
|
o->type.goblin = goblin; /* Merken, wenn Goblin-Spezialklau */
|
|
|
|
|
addlist(stealorders, o);
|
|
|
|
|
|
|
|
|
|
/* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
|
|
|
|
|
|
2017-01-10 16:31:05 +01:00
|
|
|
|
produceexp(u, SK_STEALTH, MIN(n, u->number));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void expandentertainment(region * r)
|
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
unit *u;
|
|
|
|
|
int m = entertainmoney(r);
|
|
|
|
|
request *o;
|
|
|
|
|
|
|
|
|
|
for (o = &entertainers[0]; o != nextentertainer; ++o) {
|
|
|
|
|
double part = m / (double)entertaining;
|
|
|
|
|
u = o->unit;
|
|
|
|
|
if (entertaining <= m)
|
|
|
|
|
u->n = o->qty;
|
|
|
|
|
else
|
|
|
|
|
u->n = (int)(o->qty * part);
|
|
|
|
|
change_money(u, u->n);
|
|
|
|
|
rsetmoney(r, rmoney(r) - u->n);
|
|
|
|
|
m -= u->n;
|
|
|
|
|
entertaining -= o->qty;
|
|
|
|
|
|
2017-02-15 17:09:23 +01:00
|
|
|
|
/* Nur soviel PRODUCEEXP wie auch tats<74>chlich gemacht wurde */
|
2017-01-10 16:31:05 +01:00
|
|
|
|
produceexp(u, SK_ENTERTAINMENT, MIN(u->n, u->number));
|
2014-08-08 01:03:46 +02:00
|
|
|
|
add_income(u, IC_ENTERTAIN, o->qty, u->n);
|
|
|
|
|
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void entertain_cmd(unit * u, struct order *ord)
|
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
region *r = u->region;
|
|
|
|
|
int max_e;
|
|
|
|
|
request *o;
|
|
|
|
|
static int entertainbase = 0;
|
|
|
|
|
static int entertainperlevel = 0;
|
2014-08-23 09:17:58 +02:00
|
|
|
|
keyword_t kwd;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
2014-08-23 09:17:58 +02:00
|
|
|
|
kwd = init_order(ord);
|
|
|
|
|
assert(kwd == K_ENTERTAIN);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (!entertainbase) {
|
2015-11-22 10:44:46 +01:00
|
|
|
|
const char *str = config_get("entertain.base");
|
2014-08-08 01:03:46 +02:00
|
|
|
|
entertainbase = str ? atoi(str) : 0;
|
|
|
|
|
}
|
|
|
|
|
if (!entertainperlevel) {
|
2015-11-22 10:44:46 +01:00
|
|
|
|
const char *str = config_get("entertain.perlevel");
|
2014-08-08 01:03:46 +02:00
|
|
|
|
entertainperlevel = str ? atoi(str) : 0;
|
|
|
|
|
}
|
|
|
|
|
if (fval(u, UFL_WERE)) {
|
|
|
|
|
cmistake(u, ord, 58, MSG_INCOME);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-08-27 16:16:55 +02:00
|
|
|
|
if (!effskill(u, SK_ENTERTAINMENT, 0)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
cmistake(u, ord, 58, MSG_INCOME);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (besieged(u)) {
|
|
|
|
|
cmistake(u, ord, 60, MSG_INCOME);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2016-10-31 07:45:06 +01:00
|
|
|
|
if (u->ship && is_guarded(r, u)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
cmistake(u, ord, 69, MSG_INCOME);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-08-21 20:48:00 +02:00
|
|
|
|
if (is_cursed(r->attribs, &ct_depression)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
cmistake(u, ord, 28, MSG_INCOME);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2015-08-27 16:16:55 +02:00
|
|
|
|
u->wants = u->number * (entertainbase + effskill(u, SK_ENTERTAINMENT, 0)
|
2014-08-08 01:03:46 +02:00
|
|
|
|
* entertainperlevel);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
max_e = getuint();
|
|
|
|
|
if (max_e != 0) {
|
2017-01-10 16:31:05 +01:00
|
|
|
|
u->wants = MIN(u->wants, max_e);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
o = nextentertainer++;
|
|
|
|
|
o->unit = u;
|
|
|
|
|
o->qty = u->wants;
|
|
|
|
|
entertaining += o->qty;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2014-08-08 01:03:46 +02:00
|
|
|
|
* \return number of working spaces taken by players
|
2010-08-08 10:06:34 +02:00
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
expandwork(region * r, request * work_begin, request * work_end, int maxwork)
|
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
int earnings;
|
|
|
|
|
/* n: verbleibende Einnahmen */
|
|
|
|
|
/* fishes: maximale Arbeiter */
|
|
|
|
|
int jobs = maxwork;
|
|
|
|
|
int p_wage = wage(r, NULL, NULL, turn);
|
|
|
|
|
int money = rmoney(r);
|
|
|
|
|
request *o;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (o = work_begin; o != work_end; ++o) {
|
|
|
|
|
unit *u = o->unit;
|
|
|
|
|
int workers;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (u->number == 0)
|
|
|
|
|
continue;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (jobs >= working)
|
|
|
|
|
workers = u->number;
|
|
|
|
|
else {
|
2017-02-17 21:45:10 +01:00
|
|
|
|
int r = (u->number * jobs) % working;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
workers = u->number * jobs / working;
|
2017-02-17 21:45:10 +01:00
|
|
|
|
if (r > 0 && rng_int() % working < r)
|
2014-08-08 01:03:46 +02:00
|
|
|
|
workers++;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
assert(workers >= 0);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
u->n = workers * wage(u->region, u->faction, u_race(u), turn);
|
|
|
|
|
|
|
|
|
|
jobs -= workers;
|
|
|
|
|
assert(jobs >= 0);
|
|
|
|
|
|
|
|
|
|
change_money(u, u->n);
|
|
|
|
|
working -= o->unit->number;
|
|
|
|
|
add_income(u, IC_WORK, o->qty, u->n);
|
|
|
|
|
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
|
|
|
|
if (jobs > rpeasants(r)) {
|
|
|
|
|
jobs = rpeasants(r);
|
|
|
|
|
}
|
|
|
|
|
earnings = jobs * p_wage;
|
2017-08-06 18:52:09 +02:00
|
|
|
|
if (jobs > 0 && r->attribs && rule_blessed_harvest() == HARVEST_TAXES) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* E3 rules */
|
2017-08-06 18:52:09 +02:00
|
|
|
|
int happy = harvest_effect(r);
|
|
|
|
|
earnings += happy * jobs;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
rsetmoney(r, money + earnings);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static int do_work(unit * u, order * ord, request * o)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (playerrace(u_race(u))) {
|
|
|
|
|
region *r = u->region;
|
|
|
|
|
int w;
|
|
|
|
|
|
|
|
|
|
if (fval(u, UFL_WERE)) {
|
|
|
|
|
if (ord)
|
|
|
|
|
cmistake(u, ord, 313, MSG_INCOME);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (besieged(u)) {
|
|
|
|
|
if (ord)
|
|
|
|
|
cmistake(u, ord, 60, MSG_INCOME);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2016-10-31 07:45:06 +01:00
|
|
|
|
if (u->ship && is_guarded(r, u)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (ord)
|
|
|
|
|
cmistake(u, ord, 69, MSG_INCOME);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
w = wage(r, u->faction, u_race(u), turn);
|
|
|
|
|
u->wants = u->number * w;
|
|
|
|
|
o->unit = u;
|
|
|
|
|
o->qty = u->number * w;
|
|
|
|
|
working += u->number;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else if (ord && !is_monsters(u->faction)) {
|
|
|
|
|
ADDMSG(&u->faction->msgs,
|
|
|
|
|
msg_feedback(u, ord, "race_cantwork", "race", u_race(u)));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
return -1;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-15 15:51:40 +02:00
|
|
|
|
static void expandloot(region * r, request * lootorders)
|
|
|
|
|
{
|
|
|
|
|
unit *u;
|
2015-05-15 20:56:43 +02:00
|
|
|
|
unsigned int i;
|
2017-02-14 16:23:40 +01:00
|
|
|
|
int m, looted = 0;
|
2014-09-15 15:51:40 +02:00
|
|
|
|
int startmoney = rmoney(r);
|
|
|
|
|
|
|
|
|
|
expandorders(r, lootorders);
|
|
|
|
|
if (!norders)
|
|
|
|
|
return;
|
|
|
|
|
|
2015-11-10 10:44:17 +01:00
|
|
|
|
for (i = 0; i != norders && startmoney > looted + TAXFRACTION * 2; i++) {
|
2016-11-20 19:08:39 +01:00
|
|
|
|
change_money(g_requests[i].unit, TAXFRACTION);
|
|
|
|
|
g_requests[i].unit->n += TAXFRACTION;
|
2014-09-15 15:51:40 +02:00
|
|
|
|
/*Looting destroys double the money*/
|
2015-11-10 10:44:17 +01:00
|
|
|
|
looted += TAXFRACTION * 2;
|
2014-09-15 15:51:40 +02:00
|
|
|
|
}
|
2015-11-10 10:44:17 +01:00
|
|
|
|
rsetmoney(r, startmoney - looted);
|
2016-11-20 19:08:39 +01:00
|
|
|
|
free(g_requests);
|
2015-01-30 20:37:14 +01:00
|
|
|
|
|
2014-09-15 15:51:40 +02:00
|
|
|
|
/* Lowering morale by 1 depending on the looted money (+20%) */
|
2017-02-14 16:23:40 +01:00
|
|
|
|
m = region_get_morale(r);
|
2017-11-05 19:15:43 +01:00
|
|
|
|
if (m && startmoney > 0) {
|
2017-02-14 16:23:40 +01:00
|
|
|
|
if (rng_int() % 100 < 20 + (looted * 80) / startmoney) {
|
2014-09-15 15:51:40 +02:00
|
|
|
|
/*Nur Moral -1, turns is not changed, so the first time nothing happens if the morale is good*/
|
2015-01-30 20:37:14 +01:00
|
|
|
|
region_set_morale(r, m - 1, -1);
|
2014-09-15 15:51:40 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-01-30 20:37:14 +01:00
|
|
|
|
|
2014-09-15 15:51:40 +02:00
|
|
|
|
for (u = r->units; u; u = u->next) {
|
|
|
|
|
if (u->n >= 0) {
|
|
|
|
|
add_income(u, IC_LOOT, u->wants, u->n);
|
|
|
|
|
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-14 15:32:31 +01:00
|
|
|
|
void expandtax(region * r, request * taxorders)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
unit *u;
|
2015-05-15 20:56:43 +02:00
|
|
|
|
unsigned int i;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
expandorders(r, taxorders);
|
|
|
|
|
if (!norders)
|
|
|
|
|
return;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (i = 0; i != norders && rmoney(r) > TAXFRACTION; i++) {
|
2016-11-20 19:08:39 +01:00
|
|
|
|
change_money(g_requests[i].unit, TAXFRACTION);
|
|
|
|
|
g_requests[i].unit->n += TAXFRACTION;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
rsetmoney(r, rmoney(r) - TAXFRACTION);
|
|
|
|
|
}
|
2016-11-20 19:08:39 +01:00
|
|
|
|
free(g_requests);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (u = r->units; u; u = u->next) {
|
|
|
|
|
if (u->n >= 0) {
|
|
|
|
|
add_income(u, IC_TAX, u->wants, u->n);
|
|
|
|
|
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
void tax_cmd(unit * u, struct order *ord, request ** taxorders)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* Steuern werden noch vor der Forschung eingetrieben */
|
|
|
|
|
region *r = u->region;
|
|
|
|
|
unit *u2;
|
|
|
|
|
int n;
|
|
|
|
|
request *o;
|
|
|
|
|
int max;
|
2014-08-23 09:17:58 +02:00
|
|
|
|
keyword_t kwd;
|
2015-11-14 15:32:31 +01:00
|
|
|
|
static int taxperlevel = 0;
|
|
|
|
|
|
|
|
|
|
if (!taxperlevel) {
|
2015-11-26 18:48:21 +01:00
|
|
|
|
taxperlevel = config_get_int("taxing.perlevel", 0);
|
2015-11-14 15:32:31 +01:00
|
|
|
|
}
|
2014-08-23 09:17:58 +02:00
|
|
|
|
|
|
|
|
|
kwd = init_order(ord);
|
|
|
|
|
assert(kwd == K_TAX);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (!humanoidrace(u_race(u)) && !is_monsters(u->faction)) {
|
|
|
|
|
cmistake(u, ord, 228, MSG_INCOME);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (fval(u, UFL_WERE)) {
|
|
|
|
|
cmistake(u, ord, 228, MSG_INCOME);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (besieged(u)) {
|
|
|
|
|
cmistake(u, ord, 60, MSG_INCOME);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
n = armedmen(u, false);
|
|
|
|
|
|
|
|
|
|
if (!n) {
|
|
|
|
|
cmistake(u, ord, 48, MSG_INCOME);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2015-11-14 15:32:31 +01:00
|
|
|
|
if (effskill(u, SK_TAXING, 0) <= 0) {
|
|
|
|
|
ADDMSG(&u->faction->msgs,
|
|
|
|
|
msg_feedback(u, ord, "error_no_tax_skill", ""));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-15 13:32:22 +02:00
|
|
|
|
max = getint();
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2015-05-15 13:32:22 +02:00
|
|
|
|
if (max <= 0) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
max = INT_MAX;
|
2015-05-15 13:32:22 +02:00
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (!playerrace(u_race(u))) {
|
2017-01-10 16:31:05 +01:00
|
|
|
|
u->wants = MIN(income(u), max);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
2017-01-10 16:31:05 +01:00
|
|
|
|
u->wants = MIN(n * effskill(u, SK_TAXING, 0) * taxperlevel, max);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2016-10-31 07:45:06 +01:00
|
|
|
|
u2 = is_guarded(r, u);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (u2) {
|
|
|
|
|
ADDMSG(&u->faction->msgs,
|
|
|
|
|
msg_feedback(u, ord, "region_guarded", "guard", u2));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* die einnahmen werden in fraktionen von 10 silber eingeteilt: diese
|
|
|
|
|
* fraktionen werden dann bei eintreiben unter allen eintreibenden
|
|
|
|
|
* einheiten aufgeteilt. */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
o = (request *)calloc(1, sizeof(request));
|
|
|
|
|
o->qty = u->wants / TAXFRACTION;
|
|
|
|
|
o->unit = u;
|
|
|
|
|
addlist(taxorders, o);
|
|
|
|
|
return;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-15 15:51:40 +02:00
|
|
|
|
void loot_cmd(unit * u, struct order *ord, request ** lootorders)
|
|
|
|
|
{
|
|
|
|
|
region *r = u->region;
|
|
|
|
|
unit *u2;
|
|
|
|
|
int n;
|
|
|
|
|
int max;
|
|
|
|
|
request *o;
|
|
|
|
|
keyword_t kwd;
|
|
|
|
|
|
|
|
|
|
kwd = init_order(ord);
|
|
|
|
|
assert(kwd == K_LOOT);
|
|
|
|
|
|
2015-11-22 10:44:46 +01:00
|
|
|
|
if (config_get_int("rules.enable_loot", 0) == 0 && !is_monsters(u->faction)) {
|
2014-09-15 15:51:40 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!humanoidrace(u_race(u)) && !is_monsters(u->faction)) {
|
|
|
|
|
cmistake(u, ord, 228, MSG_INCOME);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fval(u, UFL_WERE)) {
|
|
|
|
|
cmistake(u, ord, 228, MSG_INCOME);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (besieged(u)) {
|
|
|
|
|
cmistake(u, ord, 60, MSG_INCOME);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
n = armedmen(u, false);
|
|
|
|
|
|
|
|
|
|
if (!n) {
|
|
|
|
|
cmistake(u, ord, 48, MSG_INCOME);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-31 07:45:06 +01:00
|
|
|
|
u2 = is_guarded(r, u);
|
2014-09-15 15:51:40 +02:00
|
|
|
|
if (u2) {
|
|
|
|
|
ADDMSG(&u->faction->msgs,
|
|
|
|
|
msg_feedback(u, ord, "region_guarded", "guard", u2));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-15 13:32:22 +02:00
|
|
|
|
max = getint();
|
2014-09-15 15:51:40 +02:00
|
|
|
|
|
2015-05-15 13:32:22 +02:00
|
|
|
|
if (max <= 0) {
|
2014-09-15 15:51:40 +02:00
|
|
|
|
max = INT_MAX;
|
2015-05-15 13:32:22 +02:00
|
|
|
|
}
|
2014-09-15 15:51:40 +02:00
|
|
|
|
if (!playerrace(u_race(u))) {
|
2017-01-10 16:31:05 +01:00
|
|
|
|
u->wants = MIN(income(u), max);
|
2014-09-15 15:51:40 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* For player start with 20 Silver +10 every 5 level of close combat skill*/
|
2017-01-10 16:31:05 +01:00
|
|
|
|
int skbonus = (MAX(effskill(u, SK_MELEE, 0), effskill(u, SK_SPEAR, 0)) * 2 / 10) + 2;
|
|
|
|
|
u->wants = MIN(n * skbonus * 10, max);
|
2014-09-15 15:51:40 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
o = (request *)calloc(1, sizeof(request));
|
|
|
|
|
o->qty = u->wants / TAXFRACTION;
|
|
|
|
|
o->unit = u;
|
|
|
|
|
addlist(lootorders, o);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
#define MAX_WORKERS 2048
|
2011-03-07 08:02:35 +01:00
|
|
|
|
void auto_work(region * r)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
request workers[MAX_WORKERS];
|
|
|
|
|
request *nextworker = workers;
|
|
|
|
|
unit *u;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (u = r->units; u; u = u->next) {
|
|
|
|
|
if (!(u->flags & UFL_LONGACTION) && !is_monsters(u->faction)) {
|
|
|
|
|
if (do_work(u, NULL, nextworker) == 0) {
|
|
|
|
|
assert(nextworker - workers < MAX_WORKERS);
|
|
|
|
|
++nextworker;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (nextworker != workers) {
|
2016-11-15 20:43:36 +01:00
|
|
|
|
expandwork(r, workers, nextworker, region_maxworkers(r));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void peasant_taxes(region * r)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
faction *f;
|
|
|
|
|
unit *u;
|
|
|
|
|
building *b;
|
|
|
|
|
int money;
|
2017-04-29 19:21:48 +02:00
|
|
|
|
int level;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
f = region_get_owner(r);
|
|
|
|
|
if (f == NULL || is_mourning(r, turn)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
money = rmoney(r);
|
|
|
|
|
if (money <= 0)
|
|
|
|
|
return;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
b = largestbuilding(r, cmp_taxes, false);
|
|
|
|
|
if (b == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
u = building_owner(b);
|
|
|
|
|
if (u == NULL || u->faction != f)
|
|
|
|
|
return;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2017-04-29 19:21:48 +02:00
|
|
|
|
level = buildingeffsize(b, false);
|
|
|
|
|
if (level > 0) {
|
2017-05-06 16:48:32 +02:00
|
|
|
|
double taxfactor = (double)money * level / building_taxes(b);
|
|
|
|
|
double morale = (double)money * region_get_morale(r) / MORALE_TAX_FACTOR;
|
2016-11-20 19:08:39 +01:00
|
|
|
|
if (taxfactor > morale) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
taxfactor = morale;
|
2016-11-20 19:08:39 +01:00
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (taxfactor > 0) {
|
|
|
|
|
int taxmoney = (int)taxfactor;
|
|
|
|
|
change_money(u, taxmoney);
|
|
|
|
|
rsetmoney(r, money - taxmoney);
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("income_tax",
|
|
|
|
|
"unit region amount", u, r, taxmoney));
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-21 18:13:16 +01:00
|
|
|
|
static bool rule_auto_taxation(void)
|
|
|
|
|
{
|
2015-11-22 16:14:27 +01:00
|
|
|
|
return config_get_int("rules.economy.taxation", 0) != 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool rule_autowork(void) {
|
|
|
|
|
return config_get_int("work.auto", 0) != 0;
|
2015-11-21 18:13:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
void produce(struct region *r)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
|
request workers[MAX_WORKERS];
|
2014-09-15 15:51:40 +02:00
|
|
|
|
request *taxorders, *lootorders, *sellorders, *stealorders, *buyorders;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
unit *u;
|
|
|
|
|
bool limited = true;
|
|
|
|
|
request *nextworker = workers;
|
2016-09-19 04:15:12 +02:00
|
|
|
|
static int bt_cache;
|
|
|
|
|
static const struct building_type *caravan_bt;
|
2016-09-22 15:51:11 +02:00
|
|
|
|
static int rc_cache;
|
2017-05-24 08:52:19 +02:00
|
|
|
|
static const race *rc_insect, *rc_aquarian;
|
2017-11-05 19:15:43 +01:00
|
|
|
|
|
2016-09-19 04:15:12 +02:00
|
|
|
|
if (bt_changed(&bt_cache)) {
|
|
|
|
|
caravan_bt = bt_find("caravan");
|
|
|
|
|
}
|
2016-09-22 15:51:11 +02:00
|
|
|
|
if (rc_changed(&rc_cache)) {
|
|
|
|
|
rc_insect = get_race(RC_INSECT);
|
|
|
|
|
rc_aquarian = get_race(RC_AQUARIAN);
|
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
assert(r);
|
|
|
|
|
|
|
|
|
|
/* das sind alles befehle, die 30 tage brauchen, und die in thisorder
|
|
|
|
|
* stehen! von allen 30-tage befehlen wird einfach der letzte verwendet
|
|
|
|
|
* (dosetdefaults).
|
|
|
|
|
*
|
|
|
|
|
* kaufen vor einnahmequellen. da man in einer region dasselbe produkt
|
|
|
|
|
* nicht kaufen und verkaufen kann, ist die reihenfolge wegen der
|
|
|
|
|
* produkte egal. nicht so wegen dem geld.
|
|
|
|
|
*
|
|
|
|
|
* lehren vor lernen. */
|
|
|
|
|
|
|
|
|
|
assert(rmoney(r) >= 0);
|
|
|
|
|
assert(rpeasants(r) >= 0);
|
|
|
|
|
|
|
|
|
|
if (r->land && rule_auto_taxation()) {
|
|
|
|
|
/* new taxation rules, region owners make money based on morale and building */
|
|
|
|
|
peasant_taxes(r);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buyorders = 0;
|
|
|
|
|
sellorders = 0;
|
|
|
|
|
working = 0;
|
|
|
|
|
nextentertainer = &entertainers[0];
|
|
|
|
|
entertaining = 0;
|
|
|
|
|
taxorders = 0;
|
2014-09-15 15:51:40 +02:00
|
|
|
|
lootorders = 0;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
stealorders = 0;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
for (u = r->units; u; u = u->next) {
|
|
|
|
|
order *ord;
|
|
|
|
|
bool trader = false;
|
2014-11-02 11:10:26 +01:00
|
|
|
|
keyword_t todo;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2017-05-24 08:52:19 +02:00
|
|
|
|
if (fval(u, UFL_LONGACTION))
|
2014-08-08 01:03:46 +02:00
|
|
|
|
continue;
|
|
|
|
|
|
2016-09-22 15:51:11 +02:00
|
|
|
|
if (u_race(u) == rc_insect && r_insectstalled(r) &&
|
2017-08-21 20:48:00 +02:00
|
|
|
|
!is_cursed(u->attribs, &ct_insectfur))
|
2014-08-08 01:03:46 +02:00
|
|
|
|
continue;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (fval(u, UFL_LONGACTION) && u->thisorder == NULL) {
|
2015-08-04 22:47:55 +02:00
|
|
|
|
/* this message was already given in laws.c:update_long_order
|
2014-08-08 01:03:46 +02:00
|
|
|
|
cmistake(u, u->thisorder, 52, MSG_PRODUCE);
|
|
|
|
|
*/
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (ord = u->orders; ord; ord = ord->next) {
|
|
|
|
|
keyword_t kwd = getkeyword(ord);
|
|
|
|
|
if (kwd == K_BUY) {
|
|
|
|
|
buy(u, &buyorders, ord);
|
|
|
|
|
trader = true;
|
|
|
|
|
}
|
|
|
|
|
else if (kwd == K_SELL) {
|
|
|
|
|
/* sell returns true if the sale is not limited
|
|
|
|
|
* by the region limit */
|
|
|
|
|
limited &= !sell(u, &sellorders, ord);
|
|
|
|
|
trader = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (trader) {
|
|
|
|
|
attrib *a = a_find(u->attribs, &at_trades);
|
|
|
|
|
if (a && a->data.i) {
|
|
|
|
|
produceexp(u, SK_TRADE, u->number);
|
|
|
|
|
}
|
|
|
|
|
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
todo = getkeyword(u->thisorder);
|
|
|
|
|
if (todo == NOKEYWORD)
|
|
|
|
|
continue;
|
|
|
|
|
|
2016-09-22 15:51:11 +02:00
|
|
|
|
if (fval(r->terrain, SEA_REGION) && u_race(u) != rc_aquarian
|
2014-08-08 01:03:46 +02:00
|
|
|
|
&& !(u_race(u)->flags & RCF_SWIM)
|
|
|
|
|
&& todo != K_STEAL && todo != K_SPY && todo != K_SABOTAGE)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
switch (todo) {
|
|
|
|
|
case K_ENTERTAIN:
|
|
|
|
|
entertain_cmd(u, u->thisorder);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case K_WORK:
|
2015-11-22 16:14:27 +01:00
|
|
|
|
if (!rule_autowork() && do_work(u, u->thisorder, nextworker) == 0) {
|
2014-08-08 01:03:46 +02:00
|
|
|
|
assert(nextworker - workers < MAX_WORKERS);
|
|
|
|
|
++nextworker;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case K_TAX:
|
|
|
|
|
tax_cmd(u, u->thisorder, &taxorders);
|
|
|
|
|
break;
|
|
|
|
|
|
2014-09-15 15:51:40 +02:00
|
|
|
|
case K_LOOT:
|
|
|
|
|
loot_cmd(u, u->thisorder, &lootorders);
|
|
|
|
|
break;
|
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
case K_STEAL:
|
|
|
|
|
steal_cmd(u, u->thisorder, &stealorders);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case K_SPY:
|
|
|
|
|
spy_cmd(u, u->thisorder);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case K_SABOTAGE:
|
|
|
|
|
sabotage_cmd(u, u->thisorder);
|
|
|
|
|
break;
|
|
|
|
|
|
2017-06-24 13:36:18 +02:00
|
|
|
|
case K_PLANT:
|
|
|
|
|
case K_GROW:
|
2014-08-08 01:03:46 +02:00
|
|
|
|
breed_cmd(u, u->thisorder);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case K_RESEARCH:
|
|
|
|
|
research_cmd(u, u->thisorder);
|
|
|
|
|
break;
|
2014-11-02 12:45:20 +01:00
|
|
|
|
default:
|
2014-11-02 12:54:49 +01:00
|
|
|
|
/* not handled here */
|
|
|
|
|
break;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* Entertainment (expandentertainment) und Besteuerung (expandtax) vor den
|
|
|
|
|
* Befehlen, die den Bauern mehr Geld geben, damit man aus den Zahlen der
|
2017-02-15 17:09:23 +01:00
|
|
|
|
* letzten Runde berechnen kann, wieviel die Bauern f<EFBFBD>r Unterhaltung
|
2014-08-08 01:03:46 +02:00
|
|
|
|
* auszugeben bereit sind. */
|
|
|
|
|
if (entertaining)
|
|
|
|
|
expandentertainment(r);
|
2015-11-22 16:14:27 +01:00
|
|
|
|
if (!rule_autowork()) {
|
2016-11-15 20:43:36 +01:00
|
|
|
|
expandwork(r, workers, nextworker, region_maxworkers(r));
|
2014-08-08 01:03:46 +02:00
|
|
|
|
}
|
|
|
|
|
if (taxorders)
|
|
|
|
|
expandtax(r, taxorders);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-09-15 15:51:40 +02:00
|
|
|
|
if (lootorders)
|
|
|
|
|
expandloot(r, lootorders);
|
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* An erster Stelle Kaufen (expandbuying), die Bauern so Geld bekommen, um
|
|
|
|
|
* nachher zu beim Verkaufen (expandselling) den Spielern abkaufen zu
|
2017-02-15 17:09:23 +01:00
|
|
|
|
* k<EFBFBD>nnen. */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (buyorders)
|
|
|
|
|
expandbuying(r, buyorders);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (sellorders) {
|
|
|
|
|
int limit = rpeasants(r) / TRADE_FRACTION;
|
|
|
|
|
if (r->terrain == newterrain(T_DESERT)
|
2016-09-19 04:15:12 +02:00
|
|
|
|
&& buildingtype_exists(r, caravan_bt, true))
|
2014-08-08 01:03:46 +02:00
|
|
|
|
limit *= 2;
|
|
|
|
|
expandselling(r, sellorders, limited ? limit : INT_MAX);
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
/* Die Spieler sollen alles Geld verdienen, bevor sie beklaut werden
|
|
|
|
|
* (expandstealing). */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
if (stealorders)
|
|
|
|
|
expandstealing(r, stealorders);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
|
assert(rmoney(r) >= 0);
|
|
|
|
|
assert(rpeasants(r) >= 0);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|