diff --git a/scripts/tests/orders.lua b/scripts/tests/orders.lua index 63abaed00..6dc191d6c 100644 --- a/scripts/tests/orders.lua +++ b/scripts/tests/orders.lua @@ -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 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bd0ed2bff..76764f0bd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -101,6 +101,7 @@ set (ERESSEA_SRC creport.c direction.c donations.c + recruit.c economy.c eressea.c exparse.c diff --git a/src/economy.c b/src/economy.c index 72e623542..393d9b023 100644 --- a/src/economy.c +++ b/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; diff --git a/src/economy.h b/src/economy.h index 9456a4c93..130792e09 100644 --- a/src/economy.h +++ b/src/economy.h @@ -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 diff --git a/src/economy.test.c b/src/economy.test.c index 65059ffb2..024ccadbd 100644 --- a/src/economy.test.c +++ b/src/economy.test.c @@ -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); diff --git a/src/laws.c b/src/laws.c index 519c268e4..7a61a8389 100644 --- a/src/laws.c +++ b/src/laws.c @@ -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 */ diff --git a/src/recruit.c b/src/recruit.c new file mode 100644 index 000000000..ff7f19ef4 --- /dev/null +++ b/src/recruit.c @@ -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); +} diff --git a/src/recruit.h b/src/recruit.h new file mode 100644 index 000000000..347ea99f9 --- /dev/null +++ b/src/recruit.h @@ -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