diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 07242b312..89e9b90c4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -109,6 +109,7 @@ set (ERESSEA_SRC json.c creport.c report.c + steal.c economy.c give.c items.c diff --git a/src/economy.c b/src/economy.c index bfe916ebb..55e0fafb1 100644 --- a/src/economy.c +++ b/src/economy.c @@ -83,12 +83,12 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. static int working; -static production entertainers[1024]; -static production *nextentertainer; +static econ_request entertainers[1024]; +static econ_request *nextentertainer; static int entertaining; static unsigned int norders; -static production *g_requests; +static econ_request *g_requests; #define RECRUIT_MERGE 1 static int rules_recruit = -1; @@ -141,10 +141,11 @@ static void scramble(void *data, unsigned int n, size_t width) } } -static void expandorders(region * r, production * requests) +unsigned int expand_production(region * r, econ_request * requests, econ_request **results) { unit *u; - production *o; + econ_request *o; + unsigned int norders = 0; /* Alle Units ohne production haben ein -1, alle units mit orders haben ein * 0 hier stehen */ @@ -152,8 +153,6 @@ static void expandorders(region * r, production * requests) for (u = r->units; u; u = u->next) u->n = -1; - norders = 0; - for (o = requests; o; o = o->next) { if (o->qty > 0) { norders += o->qty; @@ -162,28 +161,35 @@ static void expandorders(region * r, production * requests) if (norders > 0) { int i = 0; - g_requests = (production *)calloc(norders, sizeof(production)); + econ_request *split; + split = (econ_request *)calloc(norders, sizeof(econ_request)); for (o = requests; o; o = o->next) { if (o->qty > 0) { unsigned int j; for (j = o->qty; j; j--) { - g_requests[i] = *o; - g_requests[i].unit->n = 0; + split[i] = *o; + split[i].unit->n = 0; i++; } } } - scramble(g_requests, norders, sizeof(production)); + scramble(split, norders, sizeof(econ_request)); + *results = split; } else { - g_requests = NULL; + *results = NULL; } while (requests) { - production *o = requests->next; + econ_request *o = requests->next; free_order(requests->ord); free(requests); requests = o; } + return norders; +} + +static void expandorders(region * r, econ_request * requests) { + norders = expand_production(r, requests, &g_requests); } /* ------------------------------------------------------------- */ @@ -191,21 +197,21 @@ static void expandorders(region * r, production * requests) typedef struct recruitment { struct recruitment *next; faction *f; - production *requests; + 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(production ** rop, +static recruitment *select_recruitment(econ_request ** rop, int(*quantify) (const struct race *, int), int *total) { recruitment *recruits = NULL; while (*rop) { recruitment *rec = recruits; - production *ro = *rop; + econ_request *ro = *rop; unit *u = ro->unit; const race *rc = u_race(u); int qty = quantify(rc, ro->qty); @@ -318,7 +324,7 @@ static int do_recruiting(recruitment * recruits, int available) /* do actual recruiting */ for (rec = recruits; rec != NULL; rec = rec->next) { - production *req; + econ_request *req; int get = rec->assigned; for (req = rec->requests; req; req = req->next) { @@ -367,7 +373,7 @@ void free_recruitments(recruitment * recruits) recruitment *rec = recruits; recruits = rec->next; while (rec->requests) { - production *req = rec->requests; + econ_request *req = rec->requests; rec->requests = req->next; free_order(req->ord); free(req); @@ -377,7 +383,7 @@ void free_recruitments(recruitment * recruits) } /* Rekrutierung */ -static void expandrecruit(region * r, production * recruitorders) +static void expandrecruit(region * r, econ_request * recruitorders) { recruitment *recruits = NULL; @@ -418,11 +424,11 @@ static int recruit_cost(const faction * f, const race * rc) return -1; } -static void recruit(unit * u, struct order *ord, production ** recruitorders) +static void recruit(unit * u, struct order *ord, econ_request ** recruitorders) { region *r = u->region; plane *pl; - production *o; + econ_request *o; int recruitcost = -1; const faction *f = u->faction; const struct race *rc = u_race(u); @@ -543,7 +549,7 @@ static void recruit(unit * u, struct order *ord, production ** recruitorders) return; } - o = (production *)calloc(1, sizeof(production)); + o = (econ_request *)calloc(1, sizeof(econ_request)); o->qty = n; o->unit = u; o->ord = copy_order(ord); @@ -739,7 +745,7 @@ void maintain_buildings(region * r) void economics(region * r) { unit *u; - production *recruitorders = NULL; + econ_request *recruitorders = NULL; /* Geben vor Selbstmord (doquit)! Hier alle unmittelbaren Befehle. * Rekrutieren vor allen Einnahmequellen. Bewachen JA vor Steuern @@ -1429,7 +1435,7 @@ const attrib_type at_luxuries = { "luxuries", NULL, free_luxuries, NULL, NULL, NULL }; -static void expandbuying(region * r, production * buyorders) +static void expandbuying(region * r, econ_request * buyorders) { const resource_type *rsilver = get_resourcetype(R_SILVER); int max_products; @@ -1542,12 +1548,12 @@ attrib_type at_trades = { NO_READ }; -static void buy(unit * u, production ** buyorders, struct order *ord) +static void buy(unit * u, econ_request ** buyorders, struct order *ord) { char token[128]; region *r = u->region; int n, k; - production *o; + econ_request *o; attrib *a; const item_type *itype = NULL; const luxury_type *ltype = NULL; @@ -1638,7 +1644,7 @@ static void buy(unit * u, production ** buyorders, struct order *ord) ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "luxury_notsold", "")); return; } - o = (production *)calloc(1, sizeof(production)); + o = (econ_request *)calloc(1, sizeof(econ_request)); o->type.ltype = ltype; /* sollte immer gleich sein */ o->unit = u; @@ -1647,18 +1653,18 @@ static void buy(unit * u, production ** buyorders, struct order *ord) } /* ------------------------------------------------------------- */ -static void add_income(unit * u, int type, int want, int qty) +void add_income(unit * u, income_t type, int want, int qty) { if (want == INT_MAX) want = qty; ADDMSG(&u->faction->msgs, msg_message("income", - "unit region mode wanted amount", u, u->region, type, want, qty)); + "unit region mode wanted amount", u, u->region, (int)type, want, qty)); } /* Steuers�tze in % bei Burggr��e */ static int tax_per_size[7] = { 0, 6, 12, 18, 24, 30, 36 }; -static void expandselling(region * r, production * sellorders, int limit) +static void expandselling(region * r, econ_request * sellorders, int limit) { int money, price, max_products; unsigned int j; @@ -1847,7 +1853,7 @@ static void expandselling(region * r, production * sellorders, int limit) } } -static bool sell(unit * u, production ** sellorders, struct order *ord) +static bool sell(unit * u, econ_request ** sellorders, struct order *ord) { char token[128]; bool unlimited = true; @@ -1940,7 +1946,7 @@ static bool sell(unit * u, production ** sellorders, struct order *ord) } else { attrib *a; - production *o; + econ_request *o; int k, available; if (!r_demand(r, ltype)) { @@ -1988,7 +1994,7 @@ static bool sell(unit * u, production ** sellorders, struct order *ord) assert(n >= 0); /* die Menge der verkauften G�ter merken */ a->data.i += n; - o = (production *)calloc(1, sizeof(production)); + o = (econ_request *)calloc(1, sizeof(econ_request)); o->unit = u; o->qty = n; o->type.ltype = ltype; @@ -1998,48 +2004,6 @@ static bool sell(unit * u, production ** sellorders, struct order *ord) } } -/* ------------------------------------------------------------- */ - -static void expandstealing(region * r, production * stealorders) -{ - const resource_type *rsilver = get_resourcetype(R_SILVER); - unsigned int j; - - assert(rsilver); - - expandorders(r, stealorders); - if (!norders) return; - - /* F�r jede unit in der Region wird Geld geklaut, wenn sie Opfer eines - * Beklauen-Orders ist. Jedes Opfer mu� einzeln behandelt werden. - * - * u ist die beklaute unit. oa.unit ist die klauende unit. - */ - - for (j = 0; j != norders && g_requests[j].unit->n <= g_requests[j].unit->wants; j++) { - unit *u = findunitg(g_requests[j].no, r); - int n = 0; - if (u && u->region == r) { - n = get_pooled(u, rsilver, GET_ALL, INT_MAX); - } - if (n > 10 && rplane(r) && (rplane(r)->flags & PFL_NOALLIANCES)) { - /* In Questen nur reduziertes Klauen */ - n = 10; - } - if (n > 0) { - n = MIN(n, g_requests[j].unit->wants); - use_pooled(u, rsilver, GET_ALL, n); - g_requests[j].unit->n = n; - change_money(g_requests[j].unit, n); - ADDMSG(&u->faction->msgs, msg_message("stealeffect", "unit region amount", - u, u->region, n)); - } - add_income(g_requests[j].unit, IC_STEAL, g_requests[j].unit->wants, g_requests[j].unit->n); - fset(g_requests[j].unit, UFL_LONGACTION | UFL_NOTMOVING); - } - free(g_requests); -} - /* ------------------------------------------------------------- */ static void plant(unit * u, int raw) { @@ -2359,162 +2323,11 @@ static void research_cmd(unit * u, struct order *ord) } } -static int max_skill(region * r, faction * f, skill_t sk) -{ - unit *u; - int w = 0; - - for (u = r->units; u; u = u->next) { - if (u->faction == f) { - int effsk = effskill(u, sk, 0); - if (effsk > w) { - w = effsk; - } - } - } - - return w; -} - -message * check_steal(const unit * u, struct order *ord) { - plane *pl; - - if (fval(u_race(u), RCF_NOSTEAL)) { - return msg_feedback(u, ord, "race_nosteal", "race", u_race(u)); - } - - if (fval(u->region->terrain, SEA_REGION) && u_race(u) != get_race(RC_AQUARIAN)) { - return msg_feedback(u, ord, "error_onlandonly", ""); - } - - pl = rplane(u->region); - if (pl && fval(pl, PFL_NOATTACK)) { - return msg_feedback(u, ord, "error270", ""); - } - return 0; -} - -static void steal_cmd(unit * u, struct order *ord, production ** stealorders) -{ - const resource_type *rring = get_resourcetype(R_RING_OF_NIMBLEFINGER); - int n, i, id, effsk; - bool goblin = false; - production *o; - unit *u2 = NULL; - region *r = u->region; - faction *f = NULL; - message * msg; - keyword_t kwd; - - kwd = init_order_depr(ord); - assert(kwd == K_STEAL); - - assert(skill_enabled(SK_PERCEPTION) && skill_enabled(SK_STEALTH)); - - msg = check_steal(u, ord); - if (msg) { - ADDMSG(&u->faction->msgs, msg); - return; - } - id = read_unitid(u->faction, r); - if (id > 0) { - u2 = findunitr(r, id); - } - if (u2 && u2->region == u->region) { - f = u2->faction; - } - else { - /* TODO: is this really necessary? it's the only time we use faction.c/deadhash - * it allows stealing from a unit in a dead faction, but why? */ - f = dfindhash(id); - } - - for (u2 = r->units; u2; u2 = u2->next) { - if (u2->faction == f && cansee(u->faction, r, u2, 0)) - break; - } - - if (!u2) { - ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found", - "")); - return; - } - - if (IsImmune(u2->faction)) { - ADDMSG(&u->faction->msgs, - msg_feedback(u, ord, "newbie_immunity_error", "turns", NewbieImmunity())); - return; - } - - if (u->faction->alliance && u->faction->alliance == u2->faction->alliance) { - cmistake(u, ord, 47, MSG_INCOME); - return; - } - - assert(u->region == u2->region); - if (!can_contact(r, u, u2)) { - ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error60", "")); - return; - } - - effsk = effskill(u, SK_STEALTH, 0); - n = effsk - max_skill(r, f, SK_PERCEPTION); - - if (n <= 0) { - /* Wahrnehmung == Tarnung */ - if (u_race(u) != get_race(RC_GOBLIN) || effsk <= 3) { - ADDMSG(&u->faction->msgs, msg_message("stealfail", "unit target", u, u2)); - if (n == 0) { - ADDMSG(&u2->faction->msgs, msg_message("stealdetect", "unit", u2)); - } - else { - ADDMSG(&u2->faction->msgs, msg_message("thiefdiscover", "unit target", - u, u2)); - } - return; - } - else { - ADDMSG(&u->faction->msgs, msg_message("stealfatal", "unit target", u, - u2)); - ADDMSG(&u2->faction->msgs, msg_message("thiefdiscover", "unit target", u, - u2)); - n = 1; - goblin = true; - } - } - - i = MIN(u->number, i_get(u->items, rring->itype)); - if (i > 0) { - n *= STEALINCOME * (u->number + i * (roqf_factor() - 1)); - } - else { - n *= u->number * STEALINCOME; - } - - u->wants = n; - - /* wer dank unsichtbarkeitsringen klauen kann, muss nicht unbedingt ein - * guter dieb sein, schliesslich macht man immer noch sehr viel laerm */ - - o = (production *)calloc(1, sizeof(production)); - o->unit = u; - o->qty = 1; /* Betrag steht in u->wants */ - o->no = u2->no; - o->type.goblin = goblin; /* Merken, wenn Goblin-Spezialklau */ - addlist(stealorders, o); - - /* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */ - - produceexp(u, SK_STEALTH, MIN(n, u->number)); -} - -/* ------------------------------------------------------------- */ - static void expandentertainment(region * r) { unit *u; int m = entertainmoney(r); - production *o; + econ_request *o; for (o = &entertainers[0]; o != nextentertainer; ++o) { double part = m / (double)entertaining; @@ -2539,7 +2352,7 @@ void entertain_cmd(unit * u, struct order *ord) { region *r = u->region; int max_e; - production *o; + econ_request *o; static int entertainbase = 0; static int entertainperlevel = 0; keyword_t kwd; @@ -2592,7 +2405,7 @@ void entertain_cmd(unit * u, struct order *ord) * \return number of working spaces taken by players */ static void -expandwork(region * r, production * work_begin, production * work_end, int maxwork) +expandwork(region * r, econ_request * work_begin, econ_request * work_end, int maxwork) { int earnings; /* n: verbleibende Einnahmen */ @@ -2600,7 +2413,7 @@ expandwork(region * r, production * work_begin, production * work_end, int maxwo int jobs = maxwork; int p_wage = wage(r, NULL, NULL, turn); int money = rmoney(r); - production *o; + econ_request *o; for (o = work_begin; o != work_end; ++o) { unit *u = o->unit; @@ -2643,7 +2456,7 @@ expandwork(region * r, production * work_begin, production * work_end, int maxwo rsetmoney(r, money + earnings); } -static int do_work(unit * u, order * ord, production * o) +static int do_work(unit * u, order * ord, econ_request * o) { if (playerrace(u_race(u))) { region *r = u->region; @@ -2678,7 +2491,7 @@ static int do_work(unit * u, order * ord, production * o) return -1; } -static void expandloot(region * r, production * lootorders) +static void expandloot(region * r, econ_request * lootorders) { unit *u; unsigned int i; @@ -2715,7 +2528,7 @@ static void expandloot(region * r, production * lootorders) } } -void expandtax(region * r, production * taxorders) +void expandtax(region * r, econ_request * taxorders) { unit *u; unsigned int i; @@ -2739,13 +2552,13 @@ void expandtax(region * r, production * taxorders) } } -void tax_cmd(unit * u, struct order *ord, production ** taxorders) +void tax_cmd(unit * u, struct order *ord, econ_request ** taxorders) { /* Steuern werden noch vor der Forschung eingetrieben */ region *r = u->region; unit *u2; int n; - production *o; + econ_request *o; int max; keyword_t kwd; static int taxperlevel = 0; @@ -2807,20 +2620,20 @@ void tax_cmd(unit * u, struct order *ord, production ** taxorders) * fraktionen werden dann bei eintreiben unter allen eintreibenden * einheiten aufgeteilt. */ - o = (production *)calloc(1, sizeof(production)); + o = (econ_request *)calloc(1, sizeof(econ_request)); o->qty = u->wants / TAXFRACTION; o->unit = u; addlist(taxorders, o); return; } -void loot_cmd(unit * u, struct order *ord, production ** lootorders) +void loot_cmd(unit * u, struct order *ord, econ_request ** lootorders) { region *r = u->region; unit *u2; int n; int max; - production *o; + econ_request *o; keyword_t kwd; kwd = init_order_depr(ord); @@ -2872,7 +2685,7 @@ void loot_cmd(unit * u, struct order *ord, production ** lootorders) u->wants = MIN(n * skbonus * 10, max); } - o = (production *)calloc(1, sizeof(production)); + o = (econ_request *)calloc(1, sizeof(econ_request)); o->qty = u->wants / TAXFRACTION; o->unit = u; addlist(lootorders, o); @@ -2883,8 +2696,8 @@ void loot_cmd(unit * u, struct order *ord, production ** lootorders) #define MAX_WORKERS 2048 void auto_work(region * r) { - production workers[MAX_WORKERS]; - production *nextworker = workers; + econ_request workers[MAX_WORKERS]; + econ_request *nextworker = workers; unit *u; for (u = r->units; u; u = u->next) { @@ -2952,11 +2765,11 @@ static bool rule_autowork(void) { void produce(struct region *r) { - production workers[MAX_WORKERS]; - production *taxorders, *lootorders, *sellorders, *stealorders, *buyorders; + econ_request workers[MAX_WORKERS]; + econ_request *taxorders, *lootorders, *sellorders, *stealorders, *buyorders; unit *u; bool limited = true; - production *nextworker = workers; + econ_request *nextworker = workers; static int bt_cache; static const struct building_type *caravan_bt; static int rc_cache; diff --git a/src/economy.h b/src/economy.h index 8628a0813..2689a654b 100644 --- a/src/economy.h +++ b/src/economy.h @@ -46,8 +46,8 @@ extern "C" { struct order; struct message; - typedef struct production { - struct production *next; + typedef struct econ_request { + struct econ_request *next; struct unit *unit; struct order *ord; int qty; @@ -56,7 +56,7 @@ extern "C" { bool goblin; /* stealing */ const struct luxury_type *ltype; /* trading */ } type; - } production; + } econ_request; int income(const struct unit *u); int entertainmoney(const struct region *r); @@ -65,17 +65,26 @@ extern "C" { void produce(struct region *r); void auto_work(struct region *r); - enum { IC_WORK, IC_ENTERTAIN, IC_TAX, IC_TRADE, IC_TRADETAX, IC_STEAL, IC_MAGIC, IC_LOOT }; + unsigned int expand_production(struct region * r, struct econ_request * requests, struct econ_request **results); + + typedef enum income_t { IC_WORK, IC_ENTERTAIN, IC_TAX, IC_TRADE, IC_TRADETAX, IC_STEAL, IC_MAGIC, IC_LOOT } income_t; + void add_income(struct unit * u, income_t type, int want, int qty); + void maintain_buildings(struct region *r); void make_item(struct unit * u, const struct item_type * itype, int want); int make_cmd(struct unit *u, struct order *ord); void split_allocations(struct region *r); int give_control_cmd(struct unit *u, struct order *ord); void give_control(struct unit * u, struct unit * u2); - void tax_cmd(struct unit * u, struct order *ord, struct production ** taxorders); - void expandtax(struct region * r, struct production * taxorders); + + void tax_cmd(struct unit * u, struct order *ord, struct econ_request ** taxorders); + void expandtax(struct region * r, struct econ_request * taxorders); + + struct message * steal_message(const struct unit * u, struct order *ord); + void steal_cmd(struct unit * u, struct order *ord, struct econ_request ** stealorders); + void expandstealing(struct region * r, struct econ_request * stealorders); + void add_recruits(struct unit * u, int number, int wanted); - struct message * check_steal(const struct unit * u, struct order *ord); #ifdef __cplusplus } diff --git a/src/economy.test.c b/src/economy.test.c index c3456714b..437f59d79 100644 --- a/src/economy.test.c +++ b/src/economy.test.c @@ -88,7 +88,7 @@ static void test_steal_okay(CuTest * tc) { rc = test_create_race("human"); rc->flags = 0; setup_steal(&env, ter, rc); - CuAssertPtrEquals(tc, 0, check_steal(env.u, 0)); + CuAssertPtrEquals(tc, 0, steal_message(env.u, 0)); test_cleanup(); } @@ -103,7 +103,7 @@ static void test_steal_nosteal(CuTest * tc) { rc = test_create_race("human"); rc->flags = RCF_NOSTEAL; setup_steal(&env, ter, rc); - CuAssertPtrNotNull(tc, msg = check_steal(env.u, 0)); + CuAssertPtrNotNull(tc, msg = steal_message(env.u, 0)); msg_release(msg); test_cleanup(); } @@ -118,7 +118,7 @@ static void test_steal_ocean(CuTest * tc) { ter = test_create_terrain("ocean", SEA_REGION); rc = test_create_race("human"); setup_steal(&env, ter, rc); - CuAssertPtrNotNull(tc, msg = check_steal(env.u, 0)); + CuAssertPtrNotNull(tc, msg = steal_message(env.u, 0)); msg_release(msg); test_cleanup(); } @@ -306,7 +306,7 @@ static void test_tax_cmd(CuTest *tc) { region *r; unit *u; item_type *sword, *silver; - production *taxorders = 0; + econ_request *taxorders = 0; test_setup(); init_resources(); diff --git a/src/steal.c b/src/steal.c new file mode 100644 index 000000000..13df2c4ce --- /dev/null +++ b/src/steal.c @@ -0,0 +1,237 @@ +/* +Copyright (c) 1998-2014, +Enno Rehling +Katja Zedel + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +**/ + +#include +#include +#include "economy.h" + +#include "laws.h" +#include "skill.h" +#include "study.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +void expandstealing(region * r, econ_request * stealorders) +{ + const resource_type *rsilver = get_resourcetype(R_SILVER); + unsigned int j; + unsigned int norders; + econ_request *requests; + + assert(rsilver); + + norders = expand_production(r, stealorders, &requests); + if (!norders) return; + + /* F�r jede unit in der Region wird Geld geklaut, wenn sie Opfer eines + * Beklauen-Orders ist. Jedes Opfer mu� einzeln behandelt werden. + * + * u ist die beklaute unit. oa.unit ist die klauende unit. + */ + + for (j = 0; j != norders && requests[j].unit->n <= requests[j].unit->wants; j++) { + unit *u = findunitg(requests[j].no, r); + int n = 0; + if (u && u->region == r) { + n = get_pooled(u, rsilver, GET_ALL, INT_MAX); + } + if (n > 10 && rplane(r) && (rplane(r)->flags & PFL_NOALLIANCES)) { + /* In Questen nur reduziertes Klauen */ + n = 10; + } + if (n > 0) { + n = MIN(n, requests[j].unit->wants); + use_pooled(u, rsilver, GET_ALL, n); + requests[j].unit->n = n; + change_money(requests[j].unit, n); + ADDMSG(&u->faction->msgs, msg_message("stealeffect", "unit region amount", + u, u->region, n)); + } + add_income(requests[j].unit, IC_STEAL, requests[j].unit->wants, requests[j].unit->n); + fset(requests[j].unit, UFL_LONGACTION | UFL_NOTMOVING); + } + free(requests); +} + +static int max_skill(region * r, struct faction * f, skill_t sk) +{ + unit *u; + int w = 0; + + for (u = r->units; u; u = u->next) { + if (u->faction == f) { + int effsk = effskill(u, sk, 0); + if (effsk > w) { + w = effsk; + } + } + } + + return w; +} + +message * steal_message(const unit * u, struct order *ord) { + plane *pl; + + if (fval(u_race(u), RCF_NOSTEAL)) { + return msg_feedback(u, ord, "race_nosteal", "race", u_race(u)); + } + + if (fval(u->region->terrain, SEA_REGION) && u_race(u) != get_race(RC_AQUARIAN)) { + return msg_feedback(u, ord, "error_onlandonly", ""); + } + + pl = rplane(u->region); + if (pl && fval(pl, PFL_NOATTACK)) { + return msg_feedback(u, ord, "error270", ""); + } + return 0; +} + +void steal_cmd(unit * u, struct order *ord, econ_request ** stealorders) +{ + const resource_type *rring = get_resourcetype(R_RING_OF_NIMBLEFINGER); + int n, i, id, effsk; + bool goblin = false; + econ_request *o; + unit *u2 = NULL; + region *r = u->region; + faction *f = NULL; + message * msg; + keyword_t kwd; + + kwd = init_order_depr(ord); + assert(kwd == K_STEAL); + + assert(skill_enabled(SK_PERCEPTION) && skill_enabled(SK_STEALTH)); + + msg = steal_message(u, ord); + if (msg) { + ADDMSG(&u->faction->msgs, msg); + return; + } + id = read_unitid(u->faction, r); + if (id > 0) { + u2 = findunitr(r, id); + } + if (u2 && u2->region == u->region) { + f = u2->faction; + } + else { + /* TODO: is this really necessary? it's the only time we use faction.c/deadhash + * it allows stealing from a unit in a dead faction, but why? */ + f = dfindhash(id); + } + + for (u2 = r->units; u2; u2 = u2->next) { + if (u2->faction == f && cansee(u->faction, r, u2, 0)) + break; + } + + if (!u2) { + ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found", + "")); + return; + } + + if (IsImmune(u2->faction)) { + ADDMSG(&u->faction->msgs, + msg_feedback(u, ord, "newbie_immunity_error", "turns", NewbieImmunity())); + return; + } + + if (u->faction->alliance && u->faction->alliance == u2->faction->alliance) { + cmistake(u, ord, 47, MSG_INCOME); + return; + } + + assert(u->region == u2->region); + if (!can_contact(r, u, u2)) { + ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error60", "")); + return; + } + + effsk = effskill(u, SK_STEALTH, 0); + n = effsk - max_skill(r, f, SK_PERCEPTION); + + if (n <= 0) { + /* Wahrnehmung == Tarnung */ + if (u_race(u) != get_race(RC_GOBLIN) || effsk <= 3) { + ADDMSG(&u->faction->msgs, msg_message("stealfail", "unit target", u, u2)); + if (n == 0) { + ADDMSG(&u2->faction->msgs, msg_message("stealdetect", "unit", u2)); + } + else { + ADDMSG(&u2->faction->msgs, msg_message("thiefdiscover", "unit target", + u, u2)); + } + return; + } + else { + ADDMSG(&u->faction->msgs, msg_message("stealfatal", "unit target", u, + u2)); + ADDMSG(&u2->faction->msgs, msg_message("thiefdiscover", "unit target", u, + u2)); + n = 1; + goblin = true; + } + } + + i = MIN(u->number, i_get(u->items, rring->itype)); + if (i > 0) { + n *= STEALINCOME * (u->number + i * (roqf_factor() - 1)); + } + else { + n *= u->number * STEALINCOME; + } + + u->wants = n; + + /* wer dank unsichtbarkeitsringen klauen kann, muss nicht unbedingt ein + * guter dieb sein, schliesslich macht man immer noch sehr viel laerm */ + + o = (econ_request *)calloc(1, sizeof(econ_request)); + o->unit = u; + o->qty = 1; /* Betrag steht in u->wants */ + o->no = u2->no; + o->type.goblin = goblin; /* Merken, wenn Goblin-Spezialklau */ + o->next = *stealorders; + *stealorders = o; + + /* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */ + + produceexp(u, SK_STEALTH, MIN(n, u->number)); +}