check MAX_ENTERTAINERS.

extract recruit from economy.
Bug 2600 (WIP).
This commit is contained in:
Enno Rehling 2019-07-29 23:53:59 +02:00
parent 28a4cfa55d
commit fdfe0d3b35
8 changed files with 566 additions and 419 deletions

View file

@ -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

View file

@ -101,6 +101,7 @@ set (ERESSEA_SRC
creport.c
direction.c
donations.c
recruit.c
economy.c
eressea.c
exparse.c

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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
View 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
View 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