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.
|
|
|
|
**/
|
|
|
|
|
2017-12-30 07:34:17 +01:00
|
|
|
#ifdef _MSC_VER
|
2010-08-08 10:06:34 +02:00
|
|
|
#include <platform.h>
|
2017-12-30 07:34:17 +01:00
|
|
|
#endif
|
2018-09-29 19:32:39 +02:00
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
#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"
|
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 */
|
2018-02-14 20:00:48 +01:00
|
|
|
#include "kernel/ally.h"
|
2018-09-29 19:32:39 +02:00
|
|
|
#include "kernel/attrib.h"
|
2018-02-14 20:00:48 +01:00
|
|
|
#include "kernel/building.h"
|
|
|
|
#include "kernel/calendar.h"
|
2018-09-29 19:32:39 +02:00
|
|
|
#include "kernel/config.h"
|
2018-02-14 20:00:48 +01:00
|
|
|
#include "kernel/curse.h"
|
|
|
|
#include "kernel/equipment.h"
|
2018-09-29 19:32:39 +02:00
|
|
|
#include "kernel/event.h"
|
2018-02-14 20:00:48 +01:00
|
|
|
#include "kernel/faction.h"
|
|
|
|
#include "kernel/item.h"
|
|
|
|
#include "kernel/messages.h"
|
|
|
|
#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"
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
/* util includes */
|
|
|
|
#include <util/base36.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>
|
2018-09-29 19:32:39 +02:00
|
|
|
#include "util/param.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>
|
|
|
|
|
|
|
|
static int working;
|
|
|
|
|
2017-12-04 20:01:08 +01:00
|
|
|
static econ_request entertainers[1024];
|
|
|
|
static econ_request *nextentertainer;
|
2015-05-15 20:56:43 +02:00
|
|
|
static int entertaining;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2018-02-03 20:45:19 +01:00
|
|
|
static econ_request **g_requests; /* TODO: no need for this to be module-global */
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-09 17:10:18 +02:00
|
|
|
#define ENTERTAINFRACTION 20
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2018-02-25 17:28:42 +01:00
|
|
|
int expand_production(region * r, econ_request * requests, econ_request ***results)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
unit *u;
|
2017-12-04 20:01:08 +01:00
|
|
|
econ_request *o;
|
2018-02-25 17:28:42 +01:00
|
|
|
int norders = 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2017-12-04 19:20:48 +01:00
|
|
|
/* Alle Units ohne production haben ein -1, alle units mit orders haben ein
|
2014-08-08 01:03:46 +02:00
|
|
|
* 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
|
|
|
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;
|
2018-02-03 20:45:19 +01:00
|
|
|
econ_request **split;
|
2018-12-15 19:38:40 +01:00
|
|
|
split = (econ_request **)calloc(norders, sizeof(econ_request *));
|
|
|
|
if (!split) abort();
|
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--) {
|
2018-02-03 20:45:19 +01:00
|
|
|
split[i] = o;
|
|
|
|
o->unit->n = 0;
|
2014-08-08 01:03:46 +02:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-02-03 20:45:19 +01:00
|
|
|
scramble(split, norders, sizeof(econ_request *));
|
2017-12-04 20:01:08 +01:00
|
|
|
*results = split;
|
2014-08-08 01:03:46 +02:00
|
|
|
}
|
|
|
|
else {
|
2017-12-04 20:01:08 +01:00
|
|
|
*results = NULL;
|
2014-08-08 01:03:46 +02:00
|
|
|
}
|
2018-02-03 20:45:19 +01:00
|
|
|
return norders;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_requests(econ_request *requests) {
|
2014-08-08 01:03:46 +02:00
|
|
|
while (requests) {
|
2017-12-29 06:13:28 +01:00
|
|
|
econ_request *req = requests->next;
|
2014-08-08 01:03:46 +02:00
|
|
|
free(requests);
|
2017-12-29 06:13:28 +01:00
|
|
|
requests = req;
|
2014-08-08 01:03:46 +02:00
|
|
|
}
|
2017-12-04 20:01:08 +01:00
|
|
|
}
|
|
|
|
|
2018-02-25 17:28:42 +01:00
|
|
|
static int expandorders(region * r, econ_request * requests) {
|
2017-12-29 06:13:28 +01:00
|
|
|
return expand_production(r, requests, &g_requests);
|
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;
|
2017-12-04 20:01:08 +01:00
|
|
|
econ_request *requests;
|
2014-08-08 01:03:46 +02:00
|
|
|
int total, assigned;
|
2010-08-08 10:06:34 +02:00
|
|
|
} recruitment;
|
|
|
|
|
2017-12-04 19:20:48 +01:00
|
|
|
/** Creates a list of recruitment structs, one for each faction. Adds every quantifyable production
|
2012-07-26 10:35:09 +02:00
|
|
|
* to the faction's struct and to total.
|
|
|
|
*/
|
2017-12-04 20:01:08 +01:00
|
|
|
static recruitment *select_recruitment(econ_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;
|
2017-12-04 20:01:08 +01:00
|
|
|
econ_request *ro = *rop;
|
2014-08-08 01:03:46 +02:00
|
|
|
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) {
|
2018-12-15 19:38:40 +01:00
|
|
|
rec = (recruitment *)malloc(sizeof(recruitment));
|
|
|
|
if (!rec) abort();
|
2014-08-08 01:03:46 +02:00
|
|
|
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];
|
2017-12-10 20:54:02 +01:00
|
|
|
int len;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
2017-12-10 20:54:02 +01:00
|
|
|
len = snprintf(equipment, sizeof(equipment), "new_%s", u_race(u)->_name);
|
|
|
|
if (len > 0 && (size_t)len < sizeof(equipment)) {
|
2018-05-11 21:30:26 +02:00
|
|
|
equip_unit(unew, equipment);
|
2017-12-10 20:54:02 +01: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;
|
|
|
|
|
2017-12-04 19:20:48 +01:00
|
|
|
/* find smallest production */
|
2014-08-08 01:03:46 +02:00
|
|
|
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;
|
|
|
|
|
2017-12-04 19:20:48 +01:00
|
|
|
/* assign size of smallest production for everyone if possible; in the end roll dice to assign
|
2014-08-08 01:03:46 +02:00
|
|
|
* 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) {
|
2017-12-04 20:01:08 +01:00
|
|
|
econ_request *req;
|
2014-08-08 01:03:46 +02:00
|
|
|
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() */
|
2018-02-11 15:57:31 +01:00
|
|
|
int number;
|
2015-05-15 13:32:22 +02:00
|
|
|
double multi = 2.0 * rc->recruit_multi;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
2017-12-30 07:34:17 +01:00
|
|
|
number = (int)(get / multi);
|
|
|
|
if (number > req->qty) number = req->qty;
|
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-12-30 07:34:17 +01:00
|
|
|
if (number > afford) number = afford;
|
2014-08-08 01:03:46 +02:00
|
|
|
}
|
|
|
|
if (u->number + number > UNIT_MAXSIZE) {
|
2018-02-03 20:52:24 +01:00
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, req->type.recruit.ord, "error_unit_size",
|
2014-08-08 01:03:46 +02:00
|
|
|
"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) {
|
2018-02-11 15:57:31 +01:00
|
|
|
int dec = (int)(number * multi);
|
2014-08-08 01:03:46 +02:00
|
|
|
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) {
|
2017-12-04 20:01:08 +01:00
|
|
|
econ_request *req = rec->requests;
|
2014-08-08 01:03:46 +02:00
|
|
|
rec->requests = req->next;
|
|
|
|
free(req);
|
|
|
|
}
|
|
|
|
free(rec);
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Rekrutierung */
|
2017-12-04 20:01:08 +01:00
|
|
|
static void expandrecruit(region * r, econ_request * recruitorders)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2018-02-25 17:28:42 +01:00
|
|
|
recruitment *recruits;
|
2014-08-08 01:03:46 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2018-08-02 14:31:00 +02:00
|
|
|
message *can_recruit(unit *u, const race *rc, order *ord, int now)
|
|
|
|
{
|
|
|
|
region *r = u->region;
|
|
|
|
|
|
|
|
/* 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(). */
|
|
|
|
if (is_guarded(r, u)) {
|
|
|
|
return msg_error(u, ord, 70);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rc == get_race(RC_INSECT)) {
|
|
|
|
gamedate date;
|
|
|
|
get_gamedate(now, &date);
|
|
|
|
if (date.season == SEASON_WINTER && r->terrain != newterrain(T_DESERT)) {
|
|
|
|
bool usepotion = false;
|
|
|
|
unit *u2;
|
|
|
|
|
|
|
|
for (u2 = r->units; u2; u2 = u2->next) {
|
|
|
|
if (fval(u2, UFL_WARMTH)) {
|
|
|
|
usepotion = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!usepotion) {
|
|
|
|
return msg_error(u, ord, 98);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* in Gletschern, Eisbergen gar nicht rekrutieren */
|
|
|
|
if (r_insectstalled(r)) {
|
|
|
|
return msg_error(u, ord, 97);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (is_cursed(r->attribs, &ct_riotzone)) {
|
|
|
|
/* Die Region befindet sich in Aufruhr */
|
|
|
|
return msg_error(u, ord, 237);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rc && !playerrace(rc)) {
|
|
|
|
return msg_error(u, ord, 139);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fval(u, UFL_HERO)) {
|
|
|
|
return msg_feedback(u, ord, "error_herorecruit", "");
|
|
|
|
}
|
|
|
|
if (has_skill(u, SK_MAGIC)) {
|
|
|
|
/* error158;de;{unit} in {region}: '{command}' - Magier arbeiten
|
2019-02-09 12:50:12 +01:00
|
|
|
* grundsaetzlich nur alleine! */
|
2018-08-02 14:31:00 +02:00
|
|
|
return msg_error(u, ord, 158);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-12-04 20:01:08 +01:00
|
|
|
static void recruit(unit * u, struct order *ord, econ_request ** recruitorders)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
region *r = u->region;
|
2017-12-04 20:01:08 +01:00
|
|
|
econ_request *o;
|
2014-08-08 01:03:46 +02:00
|
|
|
int recruitcost = -1;
|
|
|
|
const faction *f = u->faction;
|
|
|
|
const struct race *rc = u_race(u);
|
2015-05-15 13:32:22 +02:00
|
|
|
int n;
|
2018-08-02 14:31:00 +02:00
|
|
|
message *msg;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
2017-10-09 20:33:47 +02:00
|
|
|
init_order_depr(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];
|
2018-02-25 17:28:42 +01:00
|
|
|
const char *str;
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
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
|
|
|
}
|
2018-08-02 14:31:00 +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
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2018-08-02 14:31:00 +02:00
|
|
|
if (recruitcost > 0) {
|
|
|
|
int pool;
|
2018-02-25 17:28:42 +01:00
|
|
|
plane *pl = getplane(r);
|
2018-08-02 14:31:00 +02:00
|
|
|
|
|
|
|
if (pl && (pl->flags & PFL_NORECRUITS)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_pflnorecruit", ""));
|
|
|
|
return;
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2018-08-02 14:31:00 +02:00
|
|
|
pool = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, recruitcost * n);
|
|
|
|
if (pool < recruitcost) {
|
2014-08-08 01:03:46 +02:00
|
|
|
cmistake(u, ord, 142, MSG_EVENT);
|
|
|
|
return;
|
|
|
|
}
|
2018-08-02 14:31:00 +02:00
|
|
|
pool /= recruitcost;
|
|
|
|
if (n > pool) n = pool;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2018-08-02 14:31:00 +02:00
|
|
|
if (!n) {
|
|
|
|
cmistake(u, ord, 142, MSG_EVENT);
|
2014-08-08 01:03:46 +02:00
|
|
|
return;
|
|
|
|
}
|
2018-08-02 14:31:00 +02:00
|
|
|
if (has_skill(u, SK_ALCHEMY)) {
|
|
|
|
if (count_skill(u->faction, SK_ALCHEMY) + n > skill_limit(u->faction, SK_ALCHEMY)) {
|
|
|
|
cmistake(u, ord, 156, MSG_EVENT);
|
|
|
|
return;
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2018-08-02 14:31:00 +02:00
|
|
|
assert(rc);
|
|
|
|
msg = can_recruit(u, rc, ord, turn);
|
|
|
|
if (msg) {
|
|
|
|
add_message(&u->faction->msgs, msg);
|
|
|
|
msg_release(msg);
|
2014-08-08 01:03:46 +02:00
|
|
|
return;
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2018-08-02 14:31:00 +02:00
|
|
|
u_setrace(u, rc);
|
|
|
|
u->wants = n;
|
2017-12-04 20:01:08 +01:00
|
|
|
o = (econ_request *)calloc(1, sizeof(econ_request));
|
2018-12-15 19:38:40 +01:00
|
|
|
if (!o) abort();
|
2014-08-08 01:03:46 +02:00
|
|
|
o->qty = n;
|
|
|
|
o->unit = u;
|
2018-02-03 20:52:24 +01:00
|
|
|
o->type.recruit.ord = ord;
|
2014-08-08 01:03:46 +02:00
|
|
|
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;
|
|
|
|
|
2017-10-09 20:33:47 +02:00
|
|
|
init_order_depr(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;
|
|
|
|
}
|
|
|
|
|
2017-10-09 20:33:47 +02:00
|
|
|
init_order_depr(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;
|
2017-12-04 20:01:08 +01:00
|
|
|
econ_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-12-29 06:13:28 +01:00
|
|
|
static void mod_skill(const resource_mod *mod, skill_t sk, int *value) {
|
2017-04-02 14:43:53 +02:00
|
|
|
skill_t msk;
|
|
|
|
assert(mod->type == RMT_PROD_SKILL);
|
|
|
|
msk = (skill_t)mod->value.sa[0];
|
|
|
|
if (msk == NOSKILL || msk == sk) {
|
2017-12-29 06:13:28 +01:00
|
|
|
*value += mod->value.sa[1];
|
2017-04-02 14:43:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2018-04-29 13:46:17 +02:00
|
|
|
const race * rc = u_race(u);
|
|
|
|
if (mod->race_mask == 0 || (mod->race_mask & rc->mask_item)) {
|
2017-04-02 14:43:53 +02:00
|
|
|
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:
|
2018-04-29 13:46:17 +02:00
|
|
|
if (mod->race_mask) 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) {
|
2018-04-29 13:46:17 +02:00
|
|
|
if (mod->race_mask) 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;
|
2018-12-15 19:38:40 +01:00
|
|
|
#define new_allocation() (allocation *)calloc(1, sizeof(allocation))
|
2010-08-08 10:06:34 +02:00
|
|
|
#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
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* momentan kann man keine ressourcen abbauen, wenn man dafuer
|
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
|
|
|
|
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
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Bergwaechter koennen Abbau von Eisen/Laen durch Bewachen verhindern.
|
|
|
|
* Als magische Wesen 'sehen' Bergwaechter 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
|
|
|
|
2018-02-11 15:57:31 +01:00
|
|
|
assert(sk != NOSKILL || !"limited resource needs a required skill for making it");
|
2019-02-02 20:36:23 +01:00
|
|
|
skill = effskill(u, sk, NULL);
|
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) {
|
2017-12-29 06:13:28 +01:00
|
|
|
int more = i_get(u->items, rring->itype);
|
|
|
|
if (more > u->number) more = u->number;
|
|
|
|
amount += skill * more * (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-12-30 07:34:17 +01:00
|
|
|
if (dm > u->number) 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) {
|
2018-12-15 19:38:40 +01:00
|
|
|
alist = calloc(1, sizeof(struct allocation_list));
|
2018-12-15 20:01:51 +01:00
|
|
|
if (!alist) abort();
|
2014-08-08 01:03:46 +02:00
|
|
|
alist->next = allocations;
|
|
|
|
alist->type = rtype;
|
|
|
|
allocations = alist;
|
|
|
|
}
|
|
|
|
al = new_allocation();
|
2018-12-15 19:38:40 +01:00
|
|
|
if (!al) abort();
|
2014-08-08 01:03:46 +02:00
|
|
|
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);
|
|
|
|
|
|
|
|
if (rm != NULL) {
|
2018-02-11 15:57:31 +01:00
|
|
|
int need;
|
2018-02-25 17:28:42 +01:00
|
|
|
bool first = true;
|
2014-08-08 01:03:46 +02:00
|
|
|
do {
|
2018-02-11 15:57:31 +01:00
|
|
|
int avail = rm->amount, 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);
|
|
|
|
|
2018-02-11 15:57:31 +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
|
|
|
int req = required(al->want - al->get, al->save);
|
|
|
|
assert(al->get <= al->want && al->get >= 0);
|
2019-02-02 20:36:23 +01:00
|
|
|
if (effskill(al->unit, itype->construction->skill, NULL)
|
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
|
|
|
}
|
|
|
|
}
|
2018-02-11 15:57:31 +01:00
|
|
|
}
|
2017-01-10 18:07:36 +01:00
|
|
|
need = nreq;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
2017-12-30 07:34:17 +01:00
|
|
|
if (avail > nreq) 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-12-29 06:13:28 +01:00
|
|
|
int req = (avail * want) % nreq;
|
2017-02-15 21:16:54 +01:00
|
|
|
/* Wenn Rest, dann wuerfeln, ob ich etwas bekomme: */
|
2017-12-29 06:13:28 +01:00
|
|
|
if (req > 0 && rng_int() % nreq < req) ++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-12-30 07:34:17 +01:00
|
|
|
al->get = al->get + x * al->save.sa[1] / al->save.sa[0];
|
|
|
|
if (al->get > al->want) al->get = al->want;
|
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-12-30 07:34:17 +01:00
|
|
|
if (avail > nreq) 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-12-30 07:34:17 +01:00
|
|
|
if (al->want < al->get) al->get = al->want;
|
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
|
|
|
}
|
|
|
|
|
2018-02-11 16:14:00 +01:00
|
|
|
static void allocate(const resource_type *rtype, region *r, allocation *data) {
|
2017-02-26 13:19:47 +01:00
|
|
|
if (rtype->raw) {
|
2018-02-11 16:14:00 +01:00
|
|
|
leveled_allocation(rtype, r, data);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
attrib_allocation(rtype, r, data);
|
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;
|
|
|
|
const item_type *itype = resource2item(rtype);
|
|
|
|
allocation **p_al = &alist->data;
|
|
|
|
|
2018-02-11 16:14:00 +01:00
|
|
|
allocate(rtype, r, alist->data);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2018-01-12 09:17:01 +01:00
|
|
|
static void create_potion(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 built;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
if (want == 0) {
|
2018-01-12 09:17:01 +01:00
|
|
|
want = maxbuild(u, itype->construction);
|
2014-08-08 01:03:46 +02:00
|
|
|
}
|
2018-01-12 09:17:01 +01:00
|
|
|
built = build(u, 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,
|
2018-01-12 09:17:01 +01:00
|
|
|
itype->construction, want));
|
2014-08-08 01:03:46 +02:00
|
|
|
break;
|
|
|
|
default:
|
2018-01-12 09:17:01 +01:00
|
|
|
i_change(&u->items, itype, built);
|
2014-08-08 01:03:46 +02:00
|
|
|
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,
|
2018-01-12 09:17:01 +01:00
|
|
|
itype->rtype));
|
2014-08-08 01:03:46 +02:00
|
|
|
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 {
|
2018-01-12 09:17:01 +01:00
|
|
|
if (itype->flags & ITF_POTION) {
|
|
|
|
create_potion(u, itype, want);
|
|
|
|
}
|
|
|
|
else if (itype->construction && itype->construction->materials) {
|
2014-08-08 01:03:46 +02:00
|
|
|
manufacture(u, itype, want);
|
2018-01-12 09:17:01 +01:00
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
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
|
|
|
{
|
2017-12-29 06:13:28 +01:00
|
|
|
char token[32];
|
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;
|
2014-08-23 09:17:58 +02:00
|
|
|
keyword_t kwd;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
2017-10-09 20:33:47 +02:00
|
|
|
kwd = init_order_depr(ord);
|
2014-08-23 09:17:58 +02:00
|
|
|
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) {
|
2018-02-25 17:28:42 +01:00
|
|
|
char ibuf[16];
|
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 {
|
2017-12-29 06:13:28 +01:00
|
|
|
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
|
|
|
}
|
2018-05-01 15:32:06 +02:00
|
|
|
else if (btype->stages && btype->stages->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
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
2018-02-09 21:20:43 +01:00
|
|
|
static void free_luxuries(variant *var)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2018-02-09 21:20:43 +01:00
|
|
|
item *itm = (item *)var->v;
|
|
|
|
var->v = NULL;
|
2014-08-08 01:03:46 +02:00
|
|
|
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
|
|
|
};
|
|
|
|
|
2017-12-04 20:01:08 +01:00
|
|
|
static void expandbuying(region * r, econ_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
|
2019-02-09 12:50:12 +01:00
|
|
|
* Verkaufspreis. Fuer max_products Produkte kauft man das Produkt zum
|
|
|
|
* einfachen Verkaufspreis, danach erhoeht sich der Multiplikator um 1.
|
|
|
|
* counter ist ein Zaehler, der die gekauften Produkte zaehlt. money
|
|
|
|
* wird fuer die debug message gebraucht. */
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
|
|
max_products = rpeasants(r) / TRADE_FRACTION;
|
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Kauf - auch so programmiert, dass er leicht erweiterbar auf mehrere
|
|
|
|
* Gueter 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) {
|
2017-12-29 06:13:28 +01:00
|
|
|
unsigned int norders = expandorders(r, buyorders);
|
|
|
|
|
2018-02-03 20:45:19 +01:00
|
|
|
if (norders) {
|
|
|
|
unsigned int j;
|
|
|
|
for (j = 0; j != norders; j++) {
|
|
|
|
int price, multi;
|
|
|
|
ltype = g_requests[j]->type.trade.ltype;
|
|
|
|
trade = trades;
|
|
|
|
while (trade->type && trade->type != ltype)
|
|
|
|
++trade;
|
|
|
|
multi = trade->multi;
|
|
|
|
price = ltype->price * multi;
|
|
|
|
|
|
|
|
if (get_pooled(g_requests[j]->unit, rsilver, GET_DEFAULT,
|
|
|
|
price) >= price) {
|
|
|
|
item *items;
|
2019-02-09 12:50:12 +01:00
|
|
|
/* litems zaehlt die Gueter, die verkauft wurden, u->n das Geld, das
|
|
|
|
* verdient wurde. Dies muss gemacht werden, weil der Preis staendig sinkt,
|
2018-02-03 20:45:19 +01:00
|
|
|
* man sich also das verdiente Geld und die verkauften Produkte separat
|
2019-02-09 12:50:12 +01:00
|
|
|
* merken muss. */
|
2018-02-03 20:45:19 +01:00
|
|
|
attrib *a;
|
|
|
|
|
|
|
|
u = g_requests[j]->unit;
|
|
|
|
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;
|
|
|
|
i_change(&g_requests[j]->unit->items, ltype->itype, 1);
|
|
|
|
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
|
2019-02-09 12:50:12 +01:00
|
|
|
* der Preis Multiplikator fuer das Produkt um den Faktor 1. Der Zaehler
|
2018-02-03 20:45:19 +01:00
|
|
|
* wird wieder auf 0 gesetzt. */
|
|
|
|
if (++trade->number == max_products) {
|
|
|
|
trade->number = 0;
|
|
|
|
++trade->multi;
|
|
|
|
}
|
|
|
|
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
|
2014-08-08 01:03:46 +02:00
|
|
|
}
|
|
|
|
}
|
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
|
|
|
};
|
|
|
|
|
2018-03-04 20:15:03 +01:00
|
|
|
static bool trade_needs_castle(const region *r, const race *rc) {
|
|
|
|
static int rc_change, terrain_change;
|
|
|
|
static const race *rc_insect;
|
|
|
|
static const terrain_type *t_desert, *t_swamp;
|
|
|
|
if (rc_changed(&rc_change)) {
|
|
|
|
rc_insect = get_race(RC_INSECT);
|
|
|
|
}
|
|
|
|
if (terrain_changed(&terrain_change)) {
|
|
|
|
t_swamp = newterrain(T_SWAMP);
|
|
|
|
t_desert = newterrain(T_DESERT);
|
|
|
|
}
|
|
|
|
return rc != rc_insect && (r->terrain == t_swamp || r->terrain == t_desert);
|
|
|
|
}
|
|
|
|
|
2017-12-04 20:01:08 +01:00
|
|
|
static void buy(unit * u, econ_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;
|
2017-12-04 20:01:08 +01:00
|
|
|
econ_request *o;
|
2014-08-08 01:03:46 +02:00
|
|
|
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
|
2019-02-09 12:50:12 +01:00
|
|
|
* schon dafuer ausgeruestet, mehrere Produkte zu kaufen. */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2017-10-09 20:33:47 +02:00
|
|
|
kwd = init_order_depr(ord);
|
2014-08-23 09:17:58 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-11-05 19:15:43 +01:00
|
|
|
/* Entweder man ist Insekt in Sumpf/Wueste, oder es muss
|
|
|
|
* einen Handelsposten in der Region geben: */
|
2018-03-04 20:15:03 +01:00
|
|
|
if (trade_needs_castle(r, u_race(u))) {
|
2017-11-05 19:15:43 +01:00
|
|
|
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
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Ein Haendler kann nur 10 Gueter pro Talentpunkt handeln. */
|
2019-02-02 20:36:23 +01:00
|
|
|
k = u->number * 10 * effskill(u, SK_TRADE, NULL);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* hat der Haendler bereits gehandelt, muss die Menge der bereits
|
|
|
|
* verkauften/gekauften Gueter 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-12-30 07:34:17 +01:00
|
|
|
if (n > k) 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);
|
2019-02-09 12:50:12 +01:00
|
|
|
/* die Menge der verkauften Gueter 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
|
|
|
}
|
2018-09-09 07:41:30 +02:00
|
|
|
if (!r->land || r_demand(r, ltype)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "luxury_notsold", ""));
|
|
|
|
return;
|
|
|
|
}
|
2017-12-04 20:01:08 +01:00
|
|
|
o = (econ_request *)calloc(1, sizeof(econ_request));
|
2018-12-15 19:38:40 +01:00
|
|
|
if (!o) abort();
|
2018-02-03 20:24:16 +01:00
|
|
|
o->type.trade.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
|
|
|
/* ------------------------------------------------------------- */
|
2017-12-04 20:01:08 +01:00
|
|
|
void add_income(unit * u, income_t type, int want, int qty)
|
2014-08-31 08:58:55 +02:00
|
|
|
{
|
|
|
|
if (want == INT_MAX)
|
|
|
|
want = qty;
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("income",
|
2017-12-04 20:01:08 +01:00
|
|
|
"unit region mode wanted amount", u, u->region, (int)type, want, qty));
|
2014-08-31 08:58:55 +02:00
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Steuersaetze in % bei Burggroesse */
|
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
|
|
|
|
2017-12-04 20:01:08 +01:00
|
|
|
static void expandselling(region * r, econ_request * sellorders, int limit)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2018-02-25 17:28:42 +01:00
|
|
|
int money, max_products;
|
|
|
|
int norders;
|
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
|
|
|
|
2018-09-09 07:41:30 +02:00
|
|
|
assert(r->land);
|
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
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
if (!sellorders) { /* NEIN, denn Insekten koennen in || !r->buildings) */
|
|
|
|
return; /* Suempfen und Wuesten auch so handeln */
|
2014-08-08 01:03:46 +02:00
|
|
|
}
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Stelle Eigentuemer der groessten Burg fest. Bekommt Steuern aus jedem
|
|
|
|
* Verkauf. Wenn zwei Burgen gleicher Groesse 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;
|
|
|
|
}
|
|
|
|
}
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Die Region muss genug Geld haben, um die Produkte kaufen zu koennen. */
|
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
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* max_products sind 1/100 der Bevoelkerung, falls soviele Produkte
|
2014-08-08 01:03:46 +02:00
|
|
|
* verkauft werden - counter[] - sinkt die Nachfrage um 1 Punkt.
|
2019-02-09 12:50:12 +01:00
|
|
|
* multiplier speichert r->demand fuer 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
|
|
|
|
2017-12-29 06:13:28 +01:00
|
|
|
norders = expandorders(r, sellorders);
|
2018-02-03 20:45:19 +01:00
|
|
|
if (norders > 0) {
|
2018-02-25 17:28:42 +01:00
|
|
|
int j;
|
2018-02-03 20:45:19 +01:00
|
|
|
for (j = 0; j != norders; j++) {
|
|
|
|
const luxury_type *search = NULL;
|
|
|
|
const luxury_type *ltype = g_requests[j]->type.trade.ltype;
|
|
|
|
int multi = r_demand(r, ltype);
|
2018-02-25 17:28:42 +01:00
|
|
|
int i, price;
|
2018-02-03 20:45:19 +01:00
|
|
|
int use = 0;
|
|
|
|
for (i = 0, search = luxurytypes; search != ltype; search = search->next) {
|
|
|
|
/* TODO: this is slow and lame! */
|
|
|
|
++i;
|
2017-12-29 06:13:28 +01:00
|
|
|
}
|
2018-02-03 20:45:19 +01:00
|
|
|
if (counter[i] >= limit)
|
|
|
|
continue;
|
|
|
|
if (counter[i] + 1 > max_products && multi > 1)
|
|
|
|
--multi;
|
|
|
|
price = ltype->price * multi;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
2018-02-03 20:45:19 +01:00
|
|
|
if (money >= price) {
|
|
|
|
item *itm;
|
|
|
|
attrib *a;
|
|
|
|
u = g_requests[j]->unit;
|
|
|
|
a = a_find(u->attribs, &at_luxuries);
|
|
|
|
if (!a) {
|
|
|
|
a = a_add(&u->attribs, a_new(&at_luxuries));
|
2014-06-25 08:13:18 +02:00
|
|
|
}
|
2018-02-03 20:45:19 +01:00
|
|
|
itm = (item *)a->data.v;
|
|
|
|
i_change(&itm, ltype->itype, 1);
|
|
|
|
a->data.v = itm;
|
|
|
|
++use;
|
|
|
|
if (u->n < 0) {
|
|
|
|
u->n = 0;
|
2014-06-25 08:13:18 +02:00
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
|
2018-02-03 20:45:19 +01:00
|
|
|
if (hafenowner) {
|
|
|
|
if (hafenowner->faction != u->faction) {
|
|
|
|
int abgezogenhafen = price / 10;
|
|
|
|
hafencollected += abgezogenhafen;
|
|
|
|
price -= abgezogenhafen;
|
|
|
|
money -= abgezogenhafen;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (maxb) {
|
|
|
|
if (maxowner->faction != u->faction) {
|
|
|
|
int 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);
|
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* r->money -= price; --- dies wird eben nicht ausgefuehrt, denn die
|
|
|
|
* Produkte koennen auch als Steuern eingetrieben werden. In der Region
|
|
|
|
* wurden Silberstuecke gegen Luxusgueter des selben Wertes eingetauscht!
|
2018-02-03 20:45:19 +01:00
|
|
|
* Falls mehr als max_products Kunden ein Produkt gekauft haben, sinkt
|
2019-02-09 12:50:12 +01:00
|
|
|
* die Nachfrage fuer das Produkt um 1. Der Zaehler wird wieder auf 0
|
2018-02-03 20:45:19 +01:00
|
|
|
* gesetzt. */
|
|
|
|
|
|
|
|
if (++counter[i] > max_products) {
|
|
|
|
int d = r_demand(r, ltype);
|
|
|
|
if (d > 1) {
|
|
|
|
r_setdemand(r, ltype, d - 1);
|
|
|
|
}
|
|
|
|
counter[i] = 0;
|
2014-06-25 08:13:18 +02:00
|
|
|
}
|
|
|
|
}
|
2018-02-03 20:45:19 +01:00
|
|
|
if (use > 0) {
|
|
|
|
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
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Steuern. Hier werden die Steuern dem Besitzer der groessten 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
|
|
|
|
2017-12-04 20:01:08 +01:00
|
|
|
static bool sell(unit * u, econ_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;
|
2017-12-30 07:34:17 +01:00
|
|
|
int n, i;
|
2014-08-08 01:03:46 +02:00
|
|
|
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;
|
|
|
|
}
|
2019-02-09 12:50:12 +01:00
|
|
|
/* sellorders sind KEIN array, weil fuer 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
|
|
|
|
2017-10-09 20:33:47 +02:00
|
|
|
kwd = init_order_depr(ord);
|
2014-08-23 09:17:58 +02:00
|
|
|
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
|
|
|
}
|
2019-02-09 12:50:12 +01:00
|
|
|
/* In der Region muss 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 {
|
2019-02-09 12:50:12 +01:00
|
|
|
/* ...oder in der Region muss 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
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Ein Haendler kann nur 10 Gueter pro Talentpunkt verkaufen. */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
2019-02-02 20:36:23 +01:00
|
|
|
i = u->number * 10 * effskill(u, SK_TRADE, NULL);
|
2017-12-30 07:34:17 +01:00
|
|
|
if (n > i) n = i;
|
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;
|
2017-12-04 20:01:08 +01:00
|
|
|
econ_request *o;
|
2014-08-08 01:03:46 +02:00
|
|
|
int k, available;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
2018-09-09 07:41:30 +02:00
|
|
|
if (!r->land || !r_demand(r, ltype)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
cmistake(u, ord, 263, MSG_COMMERCE);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
available = get_pooled(u, itype->rtype, GET_DEFAULT, INT_MAX);
|
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Wenn andere Einheiten das selbe verkaufen, muss 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) {
|
2018-02-03 20:24:16 +01:00
|
|
|
if (o->type.trade.ltype == ltype && o->unit->faction == u->faction) {
|
2014-08-08 01:03:46 +02:00
|
|
|
int fpool =
|
|
|
|
o->qty - get_pooled(o->unit, itype->rtype, GET_RESERVE, INT_MAX);
|
2017-12-30 07:34:17 +01:00
|
|
|
if (fpool < 0) fpool = 0;
|
|
|
|
available -= fpool;
|
2014-08-08 01:03:46 +02:00
|
|
|
}
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
2017-12-30 07:34:17 +01:00
|
|
|
if (n > available) 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;
|
|
|
|
}
|
2017-12-04 19:20:48 +01:00
|
|
|
/* Hier wird production->type verwendet, weil die obere limit durch
|
2019-02-09 12:50:12 +01:00
|
|
|
* das silber gegeben wird (region->money), welches fuer alle
|
2014-08-08 01:03:46 +02:00
|
|
|
* (!) produkte als summe gilt, als nicht wie bei der
|
2019-02-09 12:50:12 +01:00
|
|
|
* produktion, wo fuer jedes produkt einzeln eine obere limite
|
2014-08-08 01:03:46 +02:00
|
|
|
* existiert, so dass man arrays von orders machen kann. */
|
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Ein Haendler kann nur 10 Gueter pro Talentpunkt handeln. */
|
2019-02-02 20:36:23 +01:00
|
|
|
k = u->number * 10 * effskill(u, SK_TRADE, NULL);
|
2014-08-08 01:03:46 +02:00
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* hat der Haendler bereits gehandelt, muss die Menge der bereits
|
|
|
|
* verkauften/gekauften Gueter 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-12-30 07:34:17 +01:00
|
|
|
if (n > k) n = k;
|
2014-08-08 01:03:46 +02:00
|
|
|
assert(n >= 0);
|
2019-02-09 12:50:12 +01:00
|
|
|
/* die Menge der verkauften Gueter merken */
|
2014-08-08 01:03:46 +02:00
|
|
|
a->data.i += n;
|
2017-12-04 20:01:08 +01:00
|
|
|
o = (econ_request *)calloc(1, sizeof(econ_request));
|
2018-12-15 19:38:40 +01:00
|
|
|
if (!o) abort();
|
2014-08-08 01:03:46 +02:00
|
|
|
o->unit = u;
|
|
|
|
o->qty = n;
|
2018-02-03 20:24:16 +01:00
|
|
|
o->type.trade.ltype = ltype;
|
2014-08-08 01:03:46 +02:00
|
|
|
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
|
|
|
/* ------------------------------------------------------------- */
|
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
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Skill pruefen */
|
2019-02-02 20:36:23 +01:00
|
|
|
skill = effskill(u, SK_HERBALISM, NULL);
|
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;
|
|
|
|
}
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Wasser des Lebens pruefen */
|
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);
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Kraeuter pruefen */
|
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-12-30 07:34:17 +01:00
|
|
|
i = skill * u->number;
|
|
|
|
if (i > raw) i = raw;
|
|
|
|
if (n > i) n = i;
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Fuer 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);
|
2018-01-21 11:14:55 +01:00
|
|
|
rsetherbs(r, 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
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Mallornbaeume kann man nur in Mallornregionen zuechten */
|
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
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Skill pruefen */
|
2019-02-02 20:36:23 +01:00
|
|
|
skill = effskill(u, SK_HERBALISM, NULL);
|
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-12-30 07:34:17 +01:00
|
|
|
if (raw > skill * u->number) 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-12-30 07:34:17 +01:00
|
|
|
if (n > raw) n = raw;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Fuer 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
|
|
|
}
|
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* zuechte baeume */
|
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
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Baeume zuechten geht nur im Fruehling */
|
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
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Mallornbaeume kann man nur in Mallornregionen zuechten */
|
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
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Skill pruefen */
|
2019-02-02 20:36:23 +01:00
|
|
|
skill = effskill(u, SK_HERBALISM, NULL);
|
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-12-30 07:34:17 +01:00
|
|
|
i = skill * u->number;
|
|
|
|
if (raw > i) raw = i;
|
2014-08-08 01:03:46 +02:00
|
|
|
n = get_pooled(u, rtype, GET_DEFAULT, raw);
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Samen pruefen */
|
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-12-30 07:34:17 +01:00
|
|
|
if (n > raw) n = raw;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Fuer 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
|
|
|
}
|
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* zuechte 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;
|
|
|
|
}
|
2019-02-02 20:36:23 +01:00
|
|
|
effsk = effskill(u, SK_HORSE_TRAINING, NULL);
|
2015-08-27 16:16:55 +02:00
|
|
|
n = u->number * effsk;
|
2017-12-30 07:34:17 +01:00
|
|
|
if (n > horses) 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;
|
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
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* zuechte [<anzahl>] <parameter> */
|
2017-10-09 20:33:47 +02:00
|
|
|
(void)init_order_depr(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) {
|
2018-12-09 03:42:08 +01:00
|
|
|
const resource_type *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
|
|
|
|
2017-10-09 20:33:47 +02:00
|
|
|
kwd = init_order_depr(ord);
|
2014-08-23 09:17:58 +02:00
|
|
|
assert(kwd == K_RESEARCH);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2019-02-02 20:36:23 +01:00
|
|
|
if (effskill(u, SK_HERBALISM, NULL) < 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 void expandentertainment(region * r)
|
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
int m = entertainmoney(r);
|
2017-12-04 20:01:08 +01:00
|
|
|
econ_request *o;
|
2014-08-08 01:03:46 +02:00
|
|
|
|
|
|
|
for (o = &entertainers[0]; o != nextentertainer; ++o) {
|
|
|
|
double part = m / (double)entertaining;
|
2018-02-25 17:28:42 +01:00
|
|
|
unit *u = o->unit;
|
|
|
|
|
2014-08-08 01:03:46 +02:00
|
|
|
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;
|
|
|
|
|
2019-02-09 12:50:12 +01:00
|
|
|
/* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
|
2017-12-30 07:34:17 +01:00
|
|
|
produceexp(u, SK_ENTERTAINMENT, (u->n < u->number) ? 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;
|
2017-12-04 20:01:08 +01:00
|
|
|
econ_request *o;
|
2014-08-08 01:03:46 +02:00
|
|
|
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
|
|
|
|
2017-10-09 20:33:47 +02:00
|
|
|
kwd = init_order_depr(ord);
|
2014-08-23 09:17:58 +02:00
|
|
|
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;
|
|
|
|
}
|
2019-02-02 20:36:23 +01:00
|
|
|
if (!effskill(u, SK_ENTERTAINMENT, NULL)) {
|
2014-08-08 01:03:46 +02:00
|
|
|
cmistake(u, ord, 58, 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
|
|
|
|
2019-02-02 20:36:23 +01:00
|
|
|
u->wants = u->number * (entertainbase + effskill(u, SK_ENTERTAINMENT, NULL)
|
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-12-30 07:34:17 +01:00
|
|
|
if (u->wants > max_e) 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
|
2017-12-04 20:01:08 +01:00
|
|
|
expandwork(region * r, econ_request * work_begin, econ_request * work_end, int maxwork)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
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);
|
2017-12-04 20:01:08 +01:00
|
|
|
econ_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-12-29 06:13:28 +01:00
|
|
|
int req = (u->number * jobs) % working;
|
2014-08-08 01:03:46 +02:00
|
|
|
workers = u->number * jobs / working;
|
2017-12-29 06:13:28 +01:00
|
|
|
if (req > 0 && rng_int() % working < req)
|
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
|
|
|
}
|
|
|
|
|
2017-12-04 20:01:08 +01:00
|
|
|
static int do_work(unit * u, order * ord, econ_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;
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2017-12-04 20:01:08 +01:00
|
|
|
static void expandloot(region * r, econ_request * lootorders)
|
2014-09-15 15:51:40 +02:00
|
|
|
{
|
2017-12-29 06:13:28 +01:00
|
|
|
unsigned int norders;
|
2014-09-15 15:51:40 +02:00
|
|
|
|
2017-12-29 06:13:28 +01:00
|
|
|
norders = expandorders(r, lootorders);
|
|
|
|
if (norders > 0) {
|
|
|
|
unit *u;
|
|
|
|
unsigned int i;
|
|
|
|
int m, looted = 0;
|
|
|
|
int startmoney = rmoney(r);
|
|
|
|
|
|
|
|
for (i = 0; i != norders && startmoney > looted + TAXFRACTION * 2; i++) {
|
2018-02-03 20:45:19 +01:00
|
|
|
change_money(g_requests[i]->unit, TAXFRACTION);
|
|
|
|
g_requests[i]->unit->n += TAXFRACTION;
|
2017-12-29 06:13:28 +01:00
|
|
|
/*Looting destroys double the money*/
|
|
|
|
looted += TAXFRACTION * 2;
|
|
|
|
}
|
|
|
|
rsetmoney(r, startmoney - looted);
|
|
|
|
free(g_requests);
|
2015-01-30 20:37:14 +01:00
|
|
|
|
2017-12-29 06:13:28 +01:00
|
|
|
/* Lowering morale by 1 depending on the looted money (+20%) */
|
|
|
|
m = region_get_morale(r);
|
|
|
|
if (m && startmoney > 0) {
|
|
|
|
if (rng_int() % 100 < 20 + (looted * 80) / startmoney) {
|
|
|
|
/*Nur Moral -1, turns is not changed, so the first time nothing happens if the morale is good*/
|
|
|
|
region_set_morale(r, m - 1, -1);
|
|
|
|
}
|
2014-09-15 15:51:40 +02:00
|
|
|
}
|
2017-12-29 06:13:28 +01: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);
|
|
|
|
}
|
2014-09-15 15:51:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-04 20:01:08 +01:00
|
|
|
void expandtax(region * r, econ_request * taxorders)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-08 01:03:46 +02:00
|
|
|
unit *u;
|
2017-12-29 06:13:28 +01:00
|
|
|
unsigned int norders;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2017-12-29 06:13:28 +01:00
|
|
|
norders = expandorders(r, taxorders);
|
|
|
|
if (norders > 0) {
|
|
|
|
unsigned int i;
|
|
|
|
for (i = 0; i != norders && rmoney(r) > TAXFRACTION; i++) {
|
2018-02-03 20:45:19 +01:00
|
|
|
change_money(g_requests[i]->unit, TAXFRACTION);
|
|
|
|
g_requests[i]->unit->n += TAXFRACTION;
|
2017-12-29 06:13:28 +01:00
|
|
|
rsetmoney(r, rmoney(r) - TAXFRACTION);
|
|
|
|
}
|
|
|
|
free(g_requests);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2017-12-29 06:13:28 +01: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);
|
|
|
|
}
|
2014-08-08 01:03:46 +02:00
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-04 20:01:08 +01:00
|
|
|
void tax_cmd(unit * u, struct order *ord, econ_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;
|
2017-12-04 20:01:08 +01:00
|
|
|
econ_request *o;
|
2014-08-08 01:03:46 +02:00
|
|
|
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
|
|
|
|
2017-10-09 20:33:47 +02:00
|
|
|
kwd = init_order_depr(ord);
|
2014-08-23 09:17:58 +02:00
|
|
|
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
|
|
|
n = armedmen(u, false);
|
|
|
|
|
|
|
|
if (!n) {
|
|
|
|
cmistake(u, ord, 48, MSG_INCOME);
|
|
|
|
return;
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2019-02-02 20:36:23 +01:00
|
|
|
if (effskill(u, SK_TAXING, NULL) <= 0) {
|
2015-11-14 15:32:31 +01:00
|
|
|
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-12-30 07:34:17 +01:00
|
|
|
u->wants = income(u);
|
2014-08-08 01:03:46 +02:00
|
|
|
}
|
|
|
|
else {
|
2019-02-02 20:36:23 +01:00
|
|
|
u->wants = n * effskill(u, SK_TAXING, NULL) * taxperlevel;
|
2014-08-08 01:03:46 +02:00
|
|
|
}
|
2017-12-30 07:34:17 +01:00
|
|
|
if (u->wants > max) u->wants = max;
|
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
|
|
|
|
2017-12-04 20:01:08 +01:00
|
|
|
o = (econ_request *)calloc(1, sizeof(econ_request));
|
2018-12-15 19:38:40 +01:00
|
|
|
if (!o) abort();
|
2014-08-08 01:03:46 +02:00
|
|
|
o->qty = u->wants / TAXFRACTION;
|
|
|
|
o->unit = u;
|
|
|
|
addlist(taxorders, o);
|
|
|
|
return;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2017-12-04 20:01:08 +01:00
|
|
|
void loot_cmd(unit * u, struct order *ord, econ_request ** lootorders)
|
2014-09-15 15:51:40 +02:00
|
|
|
{
|
|
|
|
region *r = u->region;
|
|
|
|
unit *u2;
|
|
|
|
int n;
|
|
|
|
int max;
|
2017-12-04 20:01:08 +01:00
|
|
|
econ_request *o;
|
2014-09-15 15:51:40 +02:00
|
|
|
keyword_t kwd;
|
|
|
|
|
2017-10-09 20:33:47 +02:00
|
|
|
kwd = init_order_depr(ord);
|
2014-09-15 15:51:40 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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-12-30 07:34:17 +01:00
|
|
|
u->wants = income(u);
|
|
|
|
if (u->wants > max) u->wants = max;
|
2014-09-15 15:51:40 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* For player start with 20 Silver +10 every 5 level of close combat skill*/
|
2019-02-02 20:36:23 +01:00
|
|
|
int skm = effskill(u, SK_MELEE, NULL);
|
|
|
|
int sks = effskill(u, SK_SPEAR, NULL);
|
2017-12-30 07:34:17 +01:00
|
|
|
int skbonus = ((skm > sks ? skm : sks) * 2 / 10) + 2;
|
|
|
|
u->wants = n * skbonus * 10;
|
|
|
|
if (u->wants > max) u->wants = max;
|
2014-09-15 15:51:40 +02:00
|
|
|
}
|
|
|
|
|
2017-12-04 20:01:08 +01:00
|
|
|
o = (econ_request *)calloc(1, sizeof(econ_request));
|
2018-12-15 19:38:40 +01:00
|
|
|
if (!o) abort();
|
2014-09-15 15:51:40 +02:00
|
|
|
o->qty = u->wants / TAXFRACTION;
|
|
|
|
o->unit = u;
|
|
|
|
addlist(lootorders, o);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-12-15 19:38:40 +01:00
|
|
|
#define MAX_WORKERS 512
|
2011-03-07 08:02:35 +01:00
|
|
|
void auto_work(region * r)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2017-12-04 20:01:08 +01:00
|
|
|
econ_request workers[MAX_WORKERS];
|
|
|
|
econ_request *nextworker = workers;
|
2014-08-08 01:03:46 +02:00
|
|
|
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
|
|
|
{
|
2017-12-04 20:01:08 +01:00
|
|
|
econ_request workers[MAX_WORKERS];
|
|
|
|
econ_request *taxorders, *lootorders, *sellorders, *stealorders, *buyorders;
|
2014-08-08 01:03:46 +02:00
|
|
|
unit *u;
|
|
|
|
bool limited = true;
|
2017-12-04 20:01:08 +01:00
|
|
|
econ_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
|
2019-02-09 12:50:12 +01:00
|
|
|
* letzten Runde berechnen kann, wieviel die Bauern fuer 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
|
|
|
}
|
2018-02-03 20:45:19 +01:00
|
|
|
if (taxorders) {
|
2014-08-08 01:03:46 +02:00
|
|
|
expandtax(r, taxorders);
|
2018-02-03 20:45:19 +01:00
|
|
|
free_requests(taxorders);
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2018-02-03 20:45:19 +01:00
|
|
|
if (lootorders) {
|
2014-09-15 15:51:40 +02:00
|
|
|
expandloot(r, lootorders);
|
2018-02-03 20:45:19 +01:00
|
|
|
free_requests(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
|
2019-02-09 12:50:12 +01:00
|
|
|
* koennen. */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2018-02-03 20:45:19 +01:00
|
|
|
if (buyorders) {
|
2014-08-08 01:03:46 +02:00
|
|
|
expandbuying(r, buyorders);
|
2018-02-03 20:45:19 +01:00
|
|
|
free_requests(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);
|
2018-02-03 20:45:19 +01:00
|
|
|
free_requests(sellorders);
|
2014-08-08 01:03:46 +02:00
|
|
|
}
|
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
|
|
|
|
2018-02-03 20:45:19 +01:00
|
|
|
if (stealorders) {
|
2014-08-08 01:03:46 +02:00
|
|
|
expandstealing(r, stealorders);
|
2018-02-03 20:45:19 +01:00
|
|
|
free_requests(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
|
|
|
}
|