forked from github/server
check MAX_ENTERTAINERS.
extract recruit from economy. Bug 2600 (WIP).
This commit is contained in:
parent
28a4cfa55d
commit
fdfe0d3b35
8 changed files with 566 additions and 419 deletions
|
@ -70,15 +70,15 @@ end
|
|||
function test_give_temp()
|
||||
u.number = 2
|
||||
u:add_order("GIB TEMP 123 1 PERSON")
|
||||
u:add_order("MACHE TEMP 123 'Herpderp'")
|
||||
u:add_order("MACHE TEMP 123 'Lorax'")
|
||||
u:add_order("ENDE")
|
||||
_G.process_orders()
|
||||
assert_equal(1, u.number)
|
||||
|
||||
for x in f.units do
|
||||
if x.name == 'Herpderp' then u=x end
|
||||
if x.name == 'Lorax' then u=x end
|
||||
end
|
||||
assert_equal('Herpderp', u.name)
|
||||
assert_equal('Lorax', u.name)
|
||||
assert_equal(1, u.number)
|
||||
end
|
||||
|
||||
|
|
|
@ -101,6 +101,7 @@ set (ERESSEA_SRC
|
|||
creport.c
|
||||
direction.c
|
||||
donations.c
|
||||
recruit.c
|
||||
economy.c
|
||||
eressea.c
|
||||
exparse.c
|
||||
|
|
419
src/economy.c
419
src/economy.c
|
@ -86,25 +86,16 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
|
||||
static int working;
|
||||
|
||||
static econ_request entertainers[1024];
|
||||
#define MAX_WORKERS 1024
|
||||
static struct econ_request workers[MAX_WORKERS];
|
||||
|
||||
#define MAX_ENTERTAINERS 256
|
||||
static econ_request entertainers[MAX_ENTERTAINERS];
|
||||
static econ_request *nextentertainer;
|
||||
static int entertaining;
|
||||
|
||||
static econ_request **g_requests; /* TODO: no need for this to be module-global */
|
||||
|
||||
#define RECRUIT_MERGE 1
|
||||
static int rules_recruit = -1;
|
||||
|
||||
static void recruit_init(void)
|
||||
{
|
||||
if (rules_recruit < 0) {
|
||||
rules_recruit = 0;
|
||||
if (config_get_int("recruit.allow_merge", 1)) {
|
||||
rules_recruit |= RECRUIT_MERGE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define ENTERTAINFRACTION 20
|
||||
|
||||
int entertainmoney(const region * r)
|
||||
|
@ -201,377 +192,6 @@ static int expandorders(region * r, econ_request * requests) {
|
|||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
typedef struct recruitment {
|
||||
struct recruitment *next;
|
||||
faction *f;
|
||||
econ_request *requests;
|
||||
int total, assigned;
|
||||
} recruitment;
|
||||
|
||||
/** Creates a list of recruitment structs, one for each faction. Adds every quantifyable production
|
||||
* to the faction's struct and to total.
|
||||
*/
|
||||
static recruitment *select_recruitment(econ_request ** rop,
|
||||
int(*quantify) (const struct race *, int), int *total)
|
||||
{
|
||||
recruitment *recruits = NULL;
|
||||
|
||||
while (*rop) {
|
||||
recruitment *rec = recruits;
|
||||
econ_request *ro = *rop;
|
||||
unit *u = ro->unit;
|
||||
const race *rc = u_race(u);
|
||||
int qty = quantify(rc, ro->qty);
|
||||
|
||||
if (qty < 0) {
|
||||
rop = &ro->next; /* skip this one */
|
||||
}
|
||||
else {
|
||||
*rop = ro->next; /* remove this one */
|
||||
while (rec && rec->f != u->faction)
|
||||
rec = rec->next;
|
||||
if (rec == NULL) {
|
||||
rec = (recruitment *)malloc(sizeof(recruitment));
|
||||
if (!rec) abort();
|
||||
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;
|
||||
}
|
||||
}
|
||||
return recruits;
|
||||
}
|
||||
|
||||
void add_recruits(unit * u, int number, int wanted)
|
||||
{
|
||||
region *r = u->region;
|
||||
assert(number <= wanted);
|
||||
if (number > 0) {
|
||||
unit *unew;
|
||||
char equipment[64];
|
||||
int len;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
len = snprintf(equipment, sizeof(equipment), "new_%s", u_race(u)->_name);
|
||||
if (len > 0 && (size_t)len < sizeof(equipment)) {
|
||||
equip_unit(unew, equipment);
|
||||
}
|
||||
if (unew != u) {
|
||||
transfermen(unew, u, unew->number);
|
||||
remove_unit(&r->units, unew);
|
||||
}
|
||||
}
|
||||
if (number < wanted) {
|
||||
ADDMSG(&u->faction->msgs, msg_message("recruit",
|
||||
"unit region amount want", u, r, number, wanted));
|
||||
}
|
||||
}
|
||||
|
||||
static int any_recruiters(const struct race *rc, int qty)
|
||||
{
|
||||
return (int)(qty * 2 * rc->recruit_multi);
|
||||
}
|
||||
|
||||
static int do_recruiting(recruitment * recruits, int available)
|
||||
{
|
||||
recruitment *rec;
|
||||
int recruited = 0;
|
||||
|
||||
/* try to assign recruits to factions fairly */
|
||||
while (available > 0) {
|
||||
int n = 0;
|
||||
int rest, mintotal = INT_MAX;
|
||||
|
||||
/* find smallest production */
|
||||
for (rec = recruits; rec != NULL; rec = rec->next) {
|
||||
int want = rec->total - rec->assigned;
|
||||
if (want > 0) {
|
||||
if (mintotal > want)
|
||||
mintotal = want;
|
||||
++n;
|
||||
}
|
||||
}
|
||||
if (n == 0)
|
||||
break;
|
||||
if (mintotal * n > available) {
|
||||
mintotal = available / n;
|
||||
}
|
||||
rest = available - mintotal * n;
|
||||
|
||||
/* assign size of smallest production for everyone if possible; in the end roll dice to assign
|
||||
* small rest */
|
||||
for (rec = recruits; rec != NULL; rec = rec->next) {
|
||||
int want = rec->total - rec->assigned;
|
||||
|
||||
if (want > 0) {
|
||||
int get = mintotal;
|
||||
if (want > mintotal && rest < n && (rng_int() % n) < rest) {
|
||||
--rest;
|
||||
++get;
|
||||
}
|
||||
assert(get <= want);
|
||||
available -= get;
|
||||
rec->assigned += get;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* do actual recruiting */
|
||||
for (rec = recruits; rec != NULL; rec = rec->next) {
|
||||
econ_request *req;
|
||||
int get = rec->assigned;
|
||||
|
||||
for (req = rec->requests; req; req = req->next) {
|
||||
unit *u = req->unit;
|
||||
const race *rc = u_race(u); /* race is set in recruit() */
|
||||
int number;
|
||||
double multi = 2.0 * rc->recruit_multi;
|
||||
|
||||
number = (int)(get / multi);
|
||||
if (number > req->qty) number = req->qty;
|
||||
if (rc->recruitcost) {
|
||||
int afford = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
|
||||
number * rc->recruitcost) / rc->recruitcost;
|
||||
if (number > afford) number = afford;
|
||||
}
|
||||
if (u->number + number > UNIT_MAXSIZE) {
|
||||
ADDMSG(&u->faction->msgs, msg_feedback(u, req->type.recruit.ord, "error_unit_size",
|
||||
"maxsize", UNIT_MAXSIZE));
|
||||
number = UNIT_MAXSIZE - u->number;
|
||||
assert(number >= 0);
|
||||
}
|
||||
if (rc->recruitcost) {
|
||||
use_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
|
||||
rc->recruitcost * number);
|
||||
}
|
||||
if (u->number == 0 && fval(u, UFL_DEAD)) {
|
||||
/* unit is empty, dead, and cannot recruit */
|
||||
number = 0;
|
||||
}
|
||||
add_recruits(u, number, req->qty);
|
||||
if (number > 0) {
|
||||
int dec = (int)(number * multi);
|
||||
if ((rc->ec_flags & ECF_REC_ETHEREAL) == 0) {
|
||||
recruited += dec;
|
||||
}
|
||||
|
||||
get -= dec;
|
||||
}
|
||||
}
|
||||
}
|
||||
return recruited;
|
||||
}
|
||||
|
||||
void free_recruitments(recruitment * recruits)
|
||||
{
|
||||
while (recruits) {
|
||||
recruitment *rec = recruits;
|
||||
recruits = rec->next;
|
||||
while (rec->requests) {
|
||||
econ_request *req = rec->requests;
|
||||
rec->requests = req->next;
|
||||
free(req);
|
||||
}
|
||||
free(rec);
|
||||
}
|
||||
}
|
||||
|
||||
/* Rekrutierung */
|
||||
static void expandrecruit(region * r, econ_request * recruitorders)
|
||||
{
|
||||
recruitment *recruits;
|
||||
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);
|
||||
}
|
||||
|
||||
assert(recruitorders == NULL);
|
||||
}
|
||||
|
||||
static int recruit_cost(const faction * f, const race * rc)
|
||||
{
|
||||
if (is_monsters(f) || valid_race(f, rc)) {
|
||||
return rc->recruitcost;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
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
|
||||
* grundsaetzlich nur alleine! */
|
||||
return msg_error(u, ord, 158);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void recruit(unit * u, struct order *ord, econ_request ** recruitorders)
|
||||
{
|
||||
region *r = u->region;
|
||||
econ_request *o;
|
||||
int recruitcost = -1;
|
||||
const faction *f = u->faction;
|
||||
const struct race *rc = u_race(u);
|
||||
int n;
|
||||
message *msg;
|
||||
|
||||
init_order_depr(ord);
|
||||
n = getint();
|
||||
if (n <= 0) {
|
||||
syntax_error(u, ord);
|
||||
return;
|
||||
}
|
||||
|
||||
if (u->number == 0) {
|
||||
char token[128];
|
||||
const char *str;
|
||||
|
||||
str = gettoken(token, sizeof(token));
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recruitcost < 0) {
|
||||
rc = u_race(u);
|
||||
recruitcost = recruit_cost(f, rc);
|
||||
if (recruitcost < 0) {
|
||||
recruitcost = INT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
if (recruitcost > 0) {
|
||||
int pool;
|
||||
plane *pl = getplane(r);
|
||||
|
||||
if (pl && (pl->flags & PFL_NORECRUITS)) {
|
||||
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_pflnorecruit", ""));
|
||||
return;
|
||||
}
|
||||
|
||||
pool = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, recruitcost * n);
|
||||
if (pool < recruitcost) {
|
||||
cmistake(u, ord, 142, MSG_EVENT);
|
||||
return;
|
||||
}
|
||||
pool /= recruitcost;
|
||||
if (n > pool) n = pool;
|
||||
}
|
||||
|
||||
if (!n) {
|
||||
cmistake(u, ord, 142, MSG_EVENT);
|
||||
return;
|
||||
}
|
||||
if (has_skill(u, SK_ALCHEMY)) {
|
||||
if (faction_count_skill(u->faction, SK_ALCHEMY) + n > faction_skill_limit(u->faction, SK_ALCHEMY)) {
|
||||
cmistake(u, ord, 156, MSG_EVENT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(rc);
|
||||
msg = can_recruit(u, rc, ord, turn);
|
||||
if (msg) {
|
||||
add_message(&u->faction->msgs, msg);
|
||||
msg_release(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
u_setrace(u, rc);
|
||||
u->wants = n;
|
||||
o = (econ_request *)calloc(1, sizeof(econ_request));
|
||||
if (!o) abort();
|
||||
o->qty = n;
|
||||
o->unit = u;
|
||||
o->type.recruit.ord = ord;
|
||||
addlist(recruitorders, o);
|
||||
}
|
||||
|
||||
static void friendly_takeover(region * r, faction * f)
|
||||
{
|
||||
region_set_owner(r, f, turn);
|
||||
|
@ -761,7 +381,6 @@ void maintain_buildings(region * r)
|
|||
void economics(region * r)
|
||||
{
|
||||
unit *u;
|
||||
econ_request *recruitorders = NULL;
|
||||
|
||||
/* Geben vor Selbstmord (doquit)! Hier alle unmittelbaren Befehle.
|
||||
* Rekrutieren vor allen Einnahmequellen. Bewachen JA vor Steuern
|
||||
|
@ -784,28 +403,10 @@ void economics(region * r)
|
|||
}
|
||||
}
|
||||
}
|
||||
/* RECRUIT orders */
|
||||
|
||||
if (rules_recruit < 0)
|
||||
recruit_init();
|
||||
for (u = r->units; u; u = u->next) {
|
||||
order *ord;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recruitorders) {
|
||||
expandrecruit(r, recruitorders);
|
||||
}
|
||||
remove_empty_units_in_region(r);
|
||||
}
|
||||
|
||||
void destroy(region *r) {
|
||||
unit *u;
|
||||
for (u = r->units; u; u = u->next) {
|
||||
order *ord = u->thisorder;
|
||||
keyword_t kwd = getkeyword(ord);
|
||||
|
@ -2418,6 +2019,7 @@ void entertain_cmd(unit * u, struct order *ord)
|
|||
if (u->wants > max_e) u->wants = max_e;
|
||||
}
|
||||
o = nextentertainer++;
|
||||
assert(nextentertainer - entertainers < MAX_ENTERTAINERS);
|
||||
o->unit = u;
|
||||
o->qty = u->wants;
|
||||
entertaining += o->qty;
|
||||
|
@ -2709,9 +2311,6 @@ void loot_cmd(unit * u, struct order *ord, econ_request ** lootorders)
|
|||
return;
|
||||
}
|
||||
|
||||
#define MAX_WORKERS 1024
|
||||
static struct econ_request workers[MAX_WORKERS];
|
||||
|
||||
void auto_work(region * r)
|
||||
{
|
||||
econ_request *nextworker = workers;
|
||||
|
|
|
@ -73,6 +73,7 @@ extern "C" {
|
|||
int entertainmoney(const struct region *r);
|
||||
|
||||
void economics(struct region *r);
|
||||
void destroy(struct region *r);
|
||||
void produce(struct region *r);
|
||||
void auto_work(struct region *r);
|
||||
|
||||
|
@ -95,9 +96,6 @@ extern "C" {
|
|||
void steal_cmd(struct unit * u, struct order *ord, struct econ_request ** stealorders);
|
||||
void expandstealing(struct region * r, struct econ_request * stealorders);
|
||||
|
||||
struct message *can_recruit(struct unit *u, const struct race *rc, struct order *ord, int now);
|
||||
void add_recruits(struct unit * u, int number, int wanted);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#endif
|
||||
#include <kernel/config.h>
|
||||
#include "economy.h"
|
||||
#include "recruit.h"
|
||||
|
||||
#include <util/message.h>
|
||||
#include <kernel/building.h>
|
||||
|
@ -162,7 +163,7 @@ static void test_heroes_dont_recruit(CuTest * tc) {
|
|||
fset(u, UFL_HERO);
|
||||
unit_addorder(u, create_order(K_RECRUIT, default_locale, "1"));
|
||||
|
||||
economics(u->region);
|
||||
recruit(u->region);
|
||||
|
||||
CuAssertIntEquals(tc, 1, u->number);
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error_herorecruit"));
|
||||
|
@ -178,7 +179,7 @@ static void test_normals_recruit(CuTest * tc) {
|
|||
u = create_recruiter();
|
||||
unit_addorder(u, create_order(K_RECRUIT, default_locale, "1"));
|
||||
|
||||
economics(u->region);
|
||||
recruit(u->region);
|
||||
|
||||
CuAssertIntEquals(tc, 2, u->number);
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include "monsters.h"
|
||||
#include "move.h"
|
||||
#include "randenc.h"
|
||||
#include "recruit.h"
|
||||
#include "renumber.h"
|
||||
#include "spy.h"
|
||||
#include "study.h"
|
||||
|
@ -3942,7 +3943,9 @@ void init_processor(void)
|
|||
if (rule_force_leave(FORCE_LEAVE_ALL)) {
|
||||
add_proc_region(p, do_force_leave, "kick non-allies out of buildings/ships");
|
||||
}
|
||||
add_proc_region(p, economics, "Zerstoeren, Geben, Rekrutieren, Vergessen");
|
||||
add_proc_region(p, economics, "Geben, Vergessen");
|
||||
add_proc_region(p+1, recruit, "Rekrutieren");
|
||||
add_proc_region(p+2, destroy, "Zerstoeren");
|
||||
|
||||
/* all recruitment must be finished before we can calculate
|
||||
* promotion cost of ability */
|
||||
|
|
505
src/recruit.c
Normal file
505
src/recruit.c
Normal file
|
@ -0,0 +1,505 @@
|
|||
/*
|
||||
Copyright (c) 1998-2019,
|
||||
Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
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.
|
||||
**/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <platform.h>
|
||||
#endif
|
||||
|
||||
#include "recruit.h"
|
||||
|
||||
#include "alchemy.h"
|
||||
#include "direction.h"
|
||||
#include "donations.h"
|
||||
#include "guard.h"
|
||||
#include "give.h"
|
||||
#include "laws.h"
|
||||
#include "randenc.h"
|
||||
#include "spy.h"
|
||||
#include "study.h"
|
||||
#include "move.h"
|
||||
#include "monsters.h"
|
||||
#include "morale.h"
|
||||
#include "reports.h"
|
||||
|
||||
#include <attributes/reduceproduction.h>
|
||||
#include <attributes/racename.h>
|
||||
#include <spells/buildingcurse.h>
|
||||
#include <spells/regioncurse.h>
|
||||
#include <spells/unitcurse.h>
|
||||
|
||||
/* kernel includes */
|
||||
#include "kernel/ally.h"
|
||||
#include "kernel/attrib.h"
|
||||
#include "kernel/building.h"
|
||||
#include "kernel/calendar.h"
|
||||
#include "kernel/config.h"
|
||||
#include "kernel/curse.h"
|
||||
#include "kernel/equipment.h"
|
||||
#include "kernel/event.h"
|
||||
#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"
|
||||
|
||||
/* util includes */
|
||||
#include <util/base36.h>
|
||||
#include <util/goodies.h>
|
||||
#include <util/language.h>
|
||||
#include <util/lists.h>
|
||||
#include <util/log.h>
|
||||
#include "util/param.h"
|
||||
#include <util/parser.h>
|
||||
#include <util/rng.h>
|
||||
|
||||
/* libs includes */
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define RECRUIT_MERGE 1
|
||||
static int rules_recruit = -1;
|
||||
|
||||
typedef struct recruit_request {
|
||||
struct recruit_request *next;
|
||||
struct unit *unit;
|
||||
struct order *ord;
|
||||
int qty;
|
||||
} recruit_request;
|
||||
|
||||
typedef struct recruitment {
|
||||
struct recruitment *next;
|
||||
faction *f;
|
||||
recruit_request *requests;
|
||||
int total, assigned;
|
||||
} recruitment;
|
||||
|
||||
static void recruit_init(void)
|
||||
{
|
||||
if (rules_recruit < 0) {
|
||||
rules_recruit = 0;
|
||||
if (config_get_int("recruit.allow_merge", 1)) {
|
||||
rules_recruit |= RECRUIT_MERGE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void free_requests(recruit_request *requests) {
|
||||
while (requests) {
|
||||
recruit_request *req = requests->next;
|
||||
free(requests);
|
||||
requests = req;
|
||||
}
|
||||
}
|
||||
|
||||
void free_recruitments(recruitment * recruits)
|
||||
{
|
||||
while (recruits) {
|
||||
recruitment *rec = recruits;
|
||||
recruits = rec->next;
|
||||
free_requests(rec->requests);
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a list of recruitment structs, one for each faction. Adds every quantifyable production
|
||||
* to the faction's struct and to total.
|
||||
*/
|
||||
static recruitment *select_recruitment(recruit_request ** rop,
|
||||
int(*quantify) (const struct race *, int), int *total)
|
||||
{
|
||||
recruitment *recruits = NULL;
|
||||
|
||||
while (*rop) {
|
||||
recruitment *rec = recruits;
|
||||
recruit_request *ro = *rop;
|
||||
unit *u = ro->unit;
|
||||
const race *rc = u_race(u);
|
||||
int qty = quantify(rc, ro->qty);
|
||||
|
||||
if (qty < 0) {
|
||||
rop = &ro->next; /* skip this one */
|
||||
}
|
||||
else {
|
||||
*rop = ro->next; /* remove this one */
|
||||
while (rec && rec->f != u->faction)
|
||||
rec = rec->next;
|
||||
if (rec == NULL) {
|
||||
rec = (recruitment *)malloc(sizeof(recruitment));
|
||||
if (!rec) abort();
|
||||
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;
|
||||
}
|
||||
}
|
||||
return recruits;
|
||||
}
|
||||
|
||||
void add_recruits(unit * u, int number, int wanted)
|
||||
{
|
||||
region *r = u->region;
|
||||
assert(number <= wanted);
|
||||
if (number > 0) {
|
||||
unit *unew;
|
||||
char equipment[64];
|
||||
int len;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
len = snprintf(equipment, sizeof(equipment), "new_%s", u_race(u)->_name);
|
||||
if (len > 0 && (size_t)len < sizeof(equipment)) {
|
||||
equip_unit(unew, equipment);
|
||||
}
|
||||
if (unew != u) {
|
||||
transfermen(unew, u, unew->number);
|
||||
remove_unit(&r->units, unew);
|
||||
}
|
||||
}
|
||||
if (number < wanted) {
|
||||
ADDMSG(&u->faction->msgs, msg_message("recruit",
|
||||
"unit region amount want", u, r, number, wanted));
|
||||
}
|
||||
}
|
||||
|
||||
static int any_recruiters(const struct race *rc, int qty)
|
||||
{
|
||||
return (int)(qty * 2 * rc->recruit_multi);
|
||||
}
|
||||
|
||||
static int do_recruiting(recruitment * recruits, int available)
|
||||
{
|
||||
recruitment *rec;
|
||||
int recruited = 0;
|
||||
|
||||
/* try to assign recruits to factions fairly */
|
||||
while (available > 0) {
|
||||
int n = 0;
|
||||
int rest, mintotal = INT_MAX;
|
||||
|
||||
/* find smallest production */
|
||||
for (rec = recruits; rec != NULL; rec = rec->next) {
|
||||
int want = rec->total - rec->assigned;
|
||||
if (want > 0) {
|
||||
if (mintotal > want)
|
||||
mintotal = want;
|
||||
++n;
|
||||
}
|
||||
}
|
||||
if (n == 0)
|
||||
break;
|
||||
if (mintotal * n > available) {
|
||||
mintotal = available / n;
|
||||
}
|
||||
rest = available - mintotal * n;
|
||||
|
||||
/* assign size of smallest production for everyone if possible; in the end roll dice to assign
|
||||
* small rest */
|
||||
for (rec = recruits; rec != NULL; rec = rec->next) {
|
||||
int want = rec->total - rec->assigned;
|
||||
|
||||
if (want > 0) {
|
||||
int get = mintotal;
|
||||
if (want > mintotal && rest < n && (rng_int() % n) < rest) {
|
||||
--rest;
|
||||
++get;
|
||||
}
|
||||
assert(get <= want);
|
||||
available -= get;
|
||||
rec->assigned += get;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* do actual recruiting */
|
||||
for (rec = recruits; rec != NULL; rec = rec->next) {
|
||||
recruit_request *req;
|
||||
int get = rec->assigned;
|
||||
|
||||
for (req = rec->requests; req; req = req->next) {
|
||||
unit *u = req->unit;
|
||||
const race *rc = u_race(u); /* race is set in recruit() */
|
||||
int number;
|
||||
double multi = 2.0 * rc->recruit_multi;
|
||||
|
||||
number = (int)(get / multi);
|
||||
if (number > req->qty) number = req->qty;
|
||||
if (rc->recruitcost) {
|
||||
int afford = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
|
||||
number * rc->recruitcost) / rc->recruitcost;
|
||||
if (number > afford) number = afford;
|
||||
}
|
||||
if (u->number + number > UNIT_MAXSIZE) {
|
||||
ADDMSG(&u->faction->msgs, msg_feedback(u, req->ord, "error_unit_size",
|
||||
"maxsize", UNIT_MAXSIZE));
|
||||
number = UNIT_MAXSIZE - u->number;
|
||||
assert(number >= 0);
|
||||
}
|
||||
if (rc->recruitcost) {
|
||||
use_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
|
||||
rc->recruitcost * number);
|
||||
}
|
||||
if (u->number == 0 && fval(u, UFL_DEAD)) {
|
||||
/* unit is empty, dead, and cannot recruit */
|
||||
number = 0;
|
||||
}
|
||||
add_recruits(u, number, req->qty);
|
||||
if (number > 0) {
|
||||
int dec = (int)(number * multi);
|
||||
if ((rc->ec_flags & ECF_REC_ETHEREAL) == 0) {
|
||||
recruited += dec;
|
||||
}
|
||||
|
||||
get -= dec;
|
||||
}
|
||||
}
|
||||
}
|
||||
return recruited;
|
||||
}
|
||||
|
||||
/* Rekrutierung */
|
||||
static void expandrecruit(region * r, recruit_request * recruitorders)
|
||||
{
|
||||
recruitment *recruits;
|
||||
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);
|
||||
}
|
||||
|
||||
assert(recruitorders == NULL);
|
||||
}
|
||||
|
||||
static int recruit_cost(const faction * f, const race * rc)
|
||||
{
|
||||
if (is_monsters(f) || valid_race(f, rc)) {
|
||||
return rc->recruitcost;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
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
|
||||
* grundsaetzlich nur alleine! */
|
||||
return msg_error(u, ord, 158);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void recruit_cmd(unit * u, struct order *ord, recruit_request ** recruitorders)
|
||||
{
|
||||
region *r = u->region;
|
||||
recruit_request *o;
|
||||
int recruitcost = -1;
|
||||
const faction *f = u->faction;
|
||||
const struct race *rc = u_race(u);
|
||||
int n;
|
||||
message *msg;
|
||||
|
||||
init_order_depr(ord);
|
||||
n = getint();
|
||||
if (n <= 0) {
|
||||
syntax_error(u, ord);
|
||||
return;
|
||||
}
|
||||
|
||||
if (u->number == 0) {
|
||||
char token[128];
|
||||
const char *str;
|
||||
|
||||
str = gettoken(token, sizeof(token));
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recruitcost < 0) {
|
||||
rc = u_race(u);
|
||||
recruitcost = recruit_cost(f, rc);
|
||||
if (recruitcost < 0) {
|
||||
recruitcost = INT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
if (recruitcost > 0) {
|
||||
int pool;
|
||||
plane *pl = getplane(r);
|
||||
|
||||
if (pl && (pl->flags & PFL_NORECRUITS)) {
|
||||
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_pflnorecruit", ""));
|
||||
return;
|
||||
}
|
||||
|
||||
pool = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, recruitcost * n);
|
||||
if (pool < recruitcost) {
|
||||
cmistake(u, ord, 142, MSG_EVENT);
|
||||
return;
|
||||
}
|
||||
pool /= recruitcost;
|
||||
if (n > pool) n = pool;
|
||||
}
|
||||
|
||||
if (!n) {
|
||||
cmistake(u, ord, 142, MSG_EVENT);
|
||||
return;
|
||||
}
|
||||
if (has_skill(u, SK_ALCHEMY)) {
|
||||
if (faction_count_skill(u->faction, SK_ALCHEMY) + n > faction_skill_limit(u->faction, SK_ALCHEMY)) {
|
||||
cmistake(u, ord, 156, MSG_EVENT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(rc);
|
||||
msg = can_recruit(u, rc, ord, turn);
|
||||
if (msg) {
|
||||
add_message(&u->faction->msgs, msg);
|
||||
msg_release(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
u_setrace(u, rc);
|
||||
u->wants = n;
|
||||
o = (recruit_request *)calloc(1, sizeof(recruit_request));
|
||||
if (!o) abort();
|
||||
o->qty = n;
|
||||
o->unit = u;
|
||||
o->ord = ord;
|
||||
addlist(recruitorders, o);
|
||||
}
|
||||
|
||||
void recruit(region * r)
|
||||
{
|
||||
unit *u;
|
||||
recruit_request *recruitorders = NULL;
|
||||
|
||||
if (rules_recruit < 0)
|
||||
recruit_init();
|
||||
for (u = r->units; u; u = u->next) {
|
||||
order *ord;
|
||||
|
||||
if ((rules_recruit & RECRUIT_MERGE) || u->number == 0) {
|
||||
for (ord = u->orders; ord; ord = ord->next) {
|
||||
if (getkeyword(ord) == K_RECRUIT) {
|
||||
recruit_cmd(u, ord, &recruitorders);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recruitorders) {
|
||||
expandrecruit(r, recruitorders);
|
||||
}
|
||||
remove_empty_units_in_region(r);
|
||||
}
|
40
src/recruit.h
Normal file
40
src/recruit.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
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.
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
#ifndef H_GC_RECRUIT
|
||||
#define H_GC_RECRUIT
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct message;
|
||||
struct order;
|
||||
struct race;
|
||||
struct region;
|
||||
struct unit;
|
||||
|
||||
struct message *can_recruit(struct unit *u, const struct race *rc, struct order *ord, int now);
|
||||
void add_recruits(struct unit * u, int number, int wanted);
|
||||
void recruit(struct region * r);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
Loading…
Add table
Reference in a new issue