server/src/common/gamecode/economy.c

3090 lines
76 KiB
C
Raw Normal View History

2001-01-25 10:37:55 +01:00
/* vi: set ts=2:
*
*
* Eressea PB(E)M host Copyright (C) 1998-2003
* Christian Schlittchen (corwin@amber.kn-bremen.de)
* Katja Zedel (katze@felidae.kn-bremen.de)
* Henning Peters (faroul@beyond.kn-bremen.de)
* Enno Rehling (enno@eressea-pbem.de)
* Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
*
* based on:
*
* Atlantis v1.0 13 September 1993 Copyright 1993 by Russell Wallace
* Atlantis v1.7 Copyright 1996 by Alex Schr<EFBFBD>der
*
* This program may not be used, modified or distributed without
* prior permission by the authors of Eressea.
* This program may not be sold or used commercially without prior written
* permission from the authors.
*/
2001-01-25 10:37:55 +01:00
#include <config.h>
#include "eressea.h"
#include "economy.h"
2001-02-10 11:40:12 +01:00
/* gamecode includes */
#include "laws.h"
2004-06-21 18:45:27 +02:00
#include "randenc.h"
/* kernel includes */
#include <kernel/alchemy.h>
#include <kernel/building.h>
#include <kernel/faction.h>
#include <kernel/give.h>
#include <kernel/item.h>
#include <kernel/karma.h>
#include <kernel/magic.h>
#include <kernel/message.h>
#include <kernel/movement.h>
#include <kernel/order.h>
#include <kernel/plane.h>
#include <kernel/pool.h>
#include <kernel/race.h>
#include <kernel/region.h>
#include <kernel/reports.h>
#include <kernel/resources.h>
#include <kernel/ship.h>
#include <kernel/skill.h>
#include <kernel/spy.h>
#include <kernel/unit.h>
2001-02-10 11:40:12 +01:00
2001-01-25 10:37:55 +01:00
/* util includes */
2004-06-21 18:45:27 +02:00
#include <util/attrib.h>
#include <util/base36.h>
#include <util/event.h>
#include <util/goodies.h>
#include <util/message.h>
2001-01-25 10:37:55 +01:00
/* libs includes */
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#include <attributes/reduceproduction.h>
#include <attributes/racename.h>
#include <attributes/orcification.h>
2001-01-25 10:37:55 +01:00
#if GROWING_TREES
# include <items/seed.h>
#endif
2001-01-25 10:37:55 +01:00
/* - static global symbols ------------------------------------- */
typedef struct spende {
struct spende *next;
struct faction *f1, *f2;
struct region *region;
int betrag;
} spende;
static spende *spenden;
/* ------------------------------------------------------------- */
typedef struct request {
struct request * next;
struct unit *unit;
int qty;
int no;
union {
boolean goblin; /* stealing */
const struct luxury_type * ltype; /* trading */
} type;
} request;
2001-01-25 10:37:55 +01:00
static request workers[1024];
static request *nextworker;
static int working;
static request entertainers[1024];
static request *nextentertainer;
static int entertaining;
static int norders;
static request *oa;
int
2001-01-25 10:37:55 +01:00
income(const unit * u)
{
switch(old_race(u->race)) {
2001-01-25 10:37:55 +01:00
case RC_FIREDRAGON:
return 150 * u->number;
case RC_DRAGON:
return 1000 * u->number;
case RC_WYRM:
return 5000 * u->number;
}
return 20 * u->number;
}
static void
2001-01-25 10:37:55 +01:00
scramble(void *data, int n, size_t width)
{
int j;
char temp[64];
assert(width<=sizeof(temp));
for (j=0;j!=n;++j) {
int k = rand() % n;
if (k==j) continue;
memcpy(temp, (char*)data+j*width, width);
memcpy((char*)data+j*width, (char*)data+k*width, width);
memcpy((char*)data+k*width, temp, width);
}
2001-01-25 10:37:55 +01:00
}
static void
expandorders(region * r, request * requests)
{
unit *u;
request *o;
/* Alle Units ohne request haben ein -1, alle units mit orders haben ein
* 0 hier stehen */
2001-01-25 10:37:55 +01:00
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;
}
}
2001-01-25 10:37:55 +01:00
if (norders > 0) {
int i = 0;
oa = (request *) calloc(norders, sizeof(request));
for (o = requests; o; o = o->next) {
if (o->qty > 0) {
int j;
for (j = o->qty; j; j--) {
oa[i] = *o;
oa[i].unit->n = 0;
i++;
2001-01-25 10:37:55 +01:00
}
}
}
scramble(oa, norders, sizeof(request));
} else {
oa = NULL;
}
while (requests) {
request * o = requests->next;
free(requests);
requests = o;
}
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
static void
change_level(unit * u, skill_t sk, int bylevel)
{
skill * sv = get_skill(u, sk);
assert(bylevel>0);
2002-02-23 17:18:26 +01:00
if (sv==0) sv = add_skill(u, sk);
sk_set(sv, sv->level+bylevel);
}
2001-01-25 10:37:55 +01:00
static void
expandrecruit(region * r, request * recruitorders)
{
/* Rekrutierung */
int i, n, p = rpeasants(r), h = rhorses(r), uruks = 0;
int rfrac = p / RECRUITFRACTION;
unit * u;
2001-01-25 10:37:55 +01:00
expandorders(r, recruitorders);
if (!norders) return;
for (i = 0, n = 0; i != norders; i++) {
unit * u = oa[i].unit;
const race * rc = u->faction->race;
int recruitcost = rc->recruitcost;
/* check if recruiting is limited.
* either horses or peasant fraction or not at all */
if ((rc->ec_flags & ECF_REC_UNLIMITED)==0) {
/* not unlimited, and everything's gone: */
if (rc->ec_flags & ECF_REC_HORSES) {
/* recruit from horses if not all gone */
if (h <= 0) continue;
2002-09-02 22:36:12 +02:00
} else {
/* recruit, watch peasants if any space left */
if (n - (uruks+1)/2 >= rfrac) continue;
}
2001-01-25 10:37:55 +01:00
}
if (recruitcost) {
if (get_pooled(oa[i].unit, r, R_SILVER) < recruitcost) continue;
use_pooled(oa[i].unit, r, R_SILVER, recruitcost);
}
if ((rc->ec_flags & ECF_REC_UNLIMITED)==0) {
if (rc->ec_flags & ECF_REC_HORSES) h--; /* use a horse */
else {
if ((rc->ec_flags & ECF_REC_ETHEREAL)==0) {
p--; /* use a peasant */
2002-09-02 22:36:12 +02:00
if (rc == new_race[RC_URUK]) uruks++;
}
n++;
}
2001-01-25 10:37:55 +01:00
}
/* set_number(u, u->number + 1); */
2001-04-26 19:41:06 +02:00
u->race = rc;
u->n++;
2001-01-25 10:37:55 +01:00
}
assert(p>=0 && h>=0);
rsetpeasants(r, p+uruks/2);
2001-01-25 10:37:55 +01:00
rsethorses(r, h);
free(oa);
for (u = r->units; u; u = u->next) {
if (u->n >= 0) {
unit * unew;
2002-02-18 18:57:27 +01:00
if (u->number==0) {
set_number(u, u->n);
u->hp = u->n * unit_max_hp(u);
unew = u;
} else {
unew = createunit(r, u->faction, u->n, u->race);
}
if (unew->race == new_race[RC_URUK]) {
change_level(unew, SK_SWORD, 1);
change_level(unew, SK_SPEAR, 1);
2001-01-25 10:37:55 +01:00
}
if (unew->race->ec_flags & ECF_REC_HORSES) {
change_level(unew, SK_RIDING, 1);
2001-01-25 10:37:55 +01:00
}
i = fspecial(unew->faction, FS_MILITIA);
2001-01-25 10:37:55 +01:00
if (i > 0) {
if (unew->race->bonus[SK_SPEAR] >= 0)
change_level(unew, SK_SPEAR, i);
if (unew->race->bonus[SK_SWORD] >= 0)
change_level(unew, SK_SWORD, i);
if (unew->race->bonus[SK_LONGBOW] >= 0)
change_level(unew, SK_LONGBOW, i);
if (unew->race->bonus[SK_CROSSBOW] >= 0)
change_level(unew, SK_CROSSBOW, i);
if (unew->race->bonus[SK_RIDING] >= 0)
change_level(unew, SK_RIDING, i);
if (unew->race->bonus[SK_AUSDAUER] >= 0)
change_level(unew, SK_AUSDAUER, i);
}
if (unew!=u) {
transfermen(unew, u, unew->number);
2002-02-16 16:16:31 +01:00
destroy_unit(unew);
2001-01-25 10:37:55 +01:00
}
if (u->n < u->wants) {
ADDMSG(&u->faction->msgs, msg_message("recruit",
"unit region amount want", u, r, u->n, u->wants));
2001-01-25 10:37:55 +01:00
}
}
}
}
static void
2004-06-21 18:45:27 +02:00
recruit(unit * u, struct order * ord, request ** recruitorders)
2001-01-25 10:37:55 +01:00
{
int n;
2004-06-21 18:45:27 +02:00
region * r = u->region;
2001-01-25 10:37:55 +01:00
plane * pl;
request *o;
int recruitcost;
const struct race * rc = u->faction->race;
2001-01-25 10:37:55 +01:00
#if GUARD_DISABLES_RECRUIT == 1
/* 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, GUARD_RECRUIT)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 70, MSG_EVENT);
return;
}
#endif
if (rc == new_race[RC_INSECT]) {
if (month_season[month(0)] == 0 && rterrain(r) != T_DESERT) {
2001-01-25 10:37:55 +01:00
#ifdef INSECT_POTION
boolean usepotion = false;
unit *u2;
for (u2 = r->units; u2; u2 = u2->next)
if (fval(u2, UFL_WARMTH)) {
2001-01-25 10:37:55 +01:00
usepotion = true;
break;
}
if (!usepotion)
2001-01-25 10:37:55 +01:00
#endif
{
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 98, MSG_EVENT);
return;
}
2001-01-25 10:37:55 +01:00
}
/* in Gletschern, Eisbergen gar nicht rekrutieren */
if (r_insectstalled(r)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 97, MSG_EVENT);
2001-01-25 10:37:55 +01:00
return;
}
}
if (is_cursed(r->attribs, C_RIOT, 0)) {
/* Die Region befindet sich in Aufruhr */
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 237, MSG_EVENT);
2001-01-25 10:37:55 +01:00
return;
}
if (!(rc->ec_flags & ECF_REC_HORSES) && fval(r, RF_ORCIFIED)) {
#if RACE_ADJUSTMENTS
if (rc != new_race[RC_URUK])
#else
if (rc != new_race[RC_ORC])
#endif
{
cmistake(u, ord, 238, MSG_EVENT);
return;
}
}
recruitcost = rc->recruitcost;
if (recruitcost) {
pl = getplane(r);
if (pl && fval(pl, PFL_NORECRUITS)) {
add_message(&u->faction->msgs,
msg_feedback(u, ord, "error_pflnorecruit", ""));
return;
}
2001-01-25 10:37:55 +01:00
if (get_pooled(u, r, R_SILVER) < recruitcost) {
cmistake(u, ord, 142, MSG_EVENT);
return;
}
}
if (!playerrace(u->race) || idle(u->faction)) {
cmistake(u, ord, 139, MSG_EVENT);
return;
}
/* snotlinge sollten hiermit bereits abgefangen werden, die
* parteirasse ist uruk oder ork*/
if (u->race != rc) {
if (u->number != 0) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 139, MSG_EVENT);
return;
}
else u->race = rc;
}
2004-06-21 18:45:27 +02:00
init_tokens(ord);
skip_token();
n = geti();
2001-01-25 10:37:55 +01:00
if (has_skill(u, SK_MAGIC)) {
/* error158;de;{unit} in {region}: '{command}' - Magier arbeiten
* grunds<EFBFBD>tzlich nur alleine! */
cmistake(u, ord, 158, MSG_EVENT);
return;
}
if (has_skill(u, SK_ALCHEMY)
&& count_skill(u->faction, SK_ALCHEMY) + n >
max_skill(u->faction, SK_ALCHEMY))
{
cmistake(u, ord, 156, MSG_EVENT);
return;
}
if (recruitcost) n = min(n, get_pooled(u, r, R_SILVER) / recruitcost);
2001-01-25 10:37:55 +01:00
u->wants = n;
2001-01-25 10:37:55 +01:00
if (!n) {
cmistake(u, ord, 142, MSG_EVENT);
return;
}
o = (request *) calloc(1, sizeof(request));
o->qty = n;
o->unit = u;
addlist(recruitorders, o);
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
extern const char* resname(resource_t res, int i);
static void
2004-06-21 18:45:27 +02:00
give_cmd(unit * u, order * ord)
2001-01-25 10:37:55 +01:00
{
2004-06-21 18:45:27 +02:00
region * r = u->region;
2001-01-25 10:37:55 +01:00
unit *u2;
const char *s;
2001-01-25 10:37:55 +01:00
int i, n;
const item_type * itype;
2003-03-09 14:29:01 +01:00
int notfound_error = 63;
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
init_tokens(ord);
skip_token();
2001-04-26 19:41:06 +02:00
u2 = getunit(r, u->faction);
2001-01-25 10:37:55 +01:00
if (!u2 && !getunitpeasants) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, notfound_error, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
return;
}
/* Damit Tarner nicht durch die Fehlermeldung enttarnt werden k<>nnen */
if (u2 && (!cansee(u->faction,r,u2,0) && !ucontact(u2, u) && !fval(u2, UFL_TAKEALL))) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, notfound_error, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
return;
}
if (u == u2) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 8, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
return;
}
/* UFL_TAKEALL ist ein grober Hack. Generalisierung tut not, ist aber nicht
* wirklich einfach. */
if (r->planep && fval(r->planep, PFL_NOGIVE) && (!u2 || !fval(u2, UFL_TAKEALL))) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 268, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
return;
}
s = getstrtoken();
if (findparam(s, u->faction->locale) == P_CONTROL) {
2001-01-25 10:37:55 +01:00
if (!u2) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, notfound_error, MSG_EVENT);
2001-01-25 10:37:55 +01:00
return;
}
if (!u->building && !u->ship) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 140, MSG_EVENT);
2001-01-25 10:37:55 +01:00
return;
}
if (u->building && u2->building != u->building) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 33, MSG_EVENT);
2001-01-25 10:37:55 +01:00
return;
}
if (u->ship && u2->ship != u->ship) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 32, MSG_EVENT);
2001-01-25 10:37:55 +01:00
return;
}
if (!fval(u, UFL_OWNER)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 49, MSG_EVENT);
2001-01-25 10:37:55 +01:00
return;
}
if (!ucontact(u2, u)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 40, MSG_EVENT);
2001-01-25 10:37:55 +01:00
return;
}
freset(u, UFL_OWNER);
fset(u2, UFL_OWNER);
2001-01-25 10:37:55 +01:00
ADDMSG(&u->faction->msgs,
msg_message("givecommand", "unit receipient", u, u2));
if (u->faction != u2->faction) {
ADDMSG(&u2->faction->msgs,
msg_message("givecommand", "unit receipient",
ucansee(u2->faction, u, u_unknown()), u2));
}
2001-01-25 10:37:55 +01:00
return;
}
if (u2 && u2->race == new_race[RC_SPELL]) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, notfound_error, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
return;
}
/* if ((u->race->ec_flags & NOGIVE) || fval(u,UFL_LOCKED)) {*/
if (u->race->ec_flags & NOGIVE && u2!=NULL) {
ADDMSG(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, ord, "race_nogive", "race", u->race));
2001-01-25 10:37:55 +01:00
return;
}
/* sperrt hier auch personen<65>bergaben!
if (u2 && !(u2->race->ec_flags & GETITEM)) {
ADDMSG(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, ord, "race_notake", "race", u2->race));
return;
2001-01-25 10:37:55 +01:00
}
*/
2001-01-25 10:37:55 +01:00
/* <20>bergabe aller Kr<4B>uter */
if (findparam(s, u->faction->locale) == P_HERBS) {
boolean given = false;
if (!(u->race->ec_flags & GIVEITEM) && u2!=NULL) {
ADDMSG(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, ord, "race_nogive", "race", u->race));
2001-01-25 10:37:55 +01:00
return;
}
if (u2 && !(u2->race->ec_flags & GETITEM)) {
ADDMSG(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, ord, "race_notake", "race", u2->race));
return;
}
2001-01-25 10:37:55 +01:00
if (!u2) {
if (!getunitpeasants) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, notfound_error, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
return;
}
}
if (u->items) {
item **itmp=&u->items;
while (*itmp) {
const herb_type * htype = resource2herb((*itmp)->type->rtype);
if (htype && (*itmp)->number>0) {
2002-10-27 11:15:07 +01:00
/* give_item <20>ndert im fall,das man alles <20>bergibt, die
* item-liste der unit, darum continue vor pointerumsetzten */
2004-06-21 18:45:27 +02:00
if (give_item((*itmp)->number, (*itmp)->type, u, u2, ord)==0) {
given = true;
continue;
}
}
itmp = &(*itmp)->next;
2001-01-25 10:37:55 +01:00
}
}
2004-06-21 18:45:27 +02:00
if (!given) cmistake(u, ord, 38, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
return;
}
if (findparam(s, u->faction->locale) == P_ZAUBER) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 7, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
/* geht nimmer */
return;
}
if (findparam(s, u->faction->locale) == P_UNIT) { /* Einheiten uebergeben */
if (!(u->race->ec_flags & GIVEUNIT)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 167, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
return;
}
if (u2 && !ucontact(u2, u)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 40, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
return;
}
give_unit(u, u2, ord);
2001-01-25 10:37:55 +01:00
return;
}
if (findparam(s, u->faction->locale) == P_ANY) { /* Alle Gegenst<73>nde <20>bergeben */
const char * s = getstrtoken();
if(u2 && !ucontact(u2, u)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 40, MSG_COMMERCE);
return;
}
2001-01-25 10:37:55 +01:00
if (*s == 0) {
if (!(u->race->ec_flags & GIVEITEM) && u2!=NULL) {
ADDMSG(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, ord, "race_nogive", "race", u->race));
2001-01-25 10:37:55 +01:00
return;
}
if (u2 && !(u2->race->ec_flags & GETITEM)) {
ADDMSG(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, ord, "race_notake", "race", u2->race));
return;
}
/* f<>r alle items einmal pr<70>fen, ob wir mehr als von diesem Typ
* reserviert ist besitzen und diesen Teil dann <EFBFBD>bergeben */
if (u->items) {
item **itmp=&u->items;
while (*itmp) {
if ((*itmp)->number > 0
&& (*itmp)->number - new_get_resvalue(u, (*itmp)->type->rtype) > 0) {
n = (*itmp)->number - new_get_resvalue(u, (*itmp)->type->rtype);
2004-06-21 18:45:27 +02:00
if (give_item(n, (*itmp)->type, u, u2, ord)==0) continue;
}
itmp = &(*itmp)->next;
2001-01-25 10:37:55 +01:00
}
}
return;
}
i = findparam(s, u->faction->locale);
2001-01-25 10:37:55 +01:00
if (i == P_PERSON) {
if (!(u->race->ec_flags & GIVEPERSON)) {
ADDMSG(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, ord, "race_noregroup", "race", u->race));
2001-01-25 10:37:55 +01:00
return;
}
n = u->number;
give_men(n, u, u2, ord);
2001-01-25 10:37:55 +01:00
return;
}
if (!(u->race->ec_flags & GIVEITEM) && u2!=NULL) {
ADDMSG(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, ord, "race_nogive", "race", u->race));
2001-01-25 10:37:55 +01:00
return;
}
if (u2 && !(u2->race->ec_flags & GETITEM)) {
ADDMSG(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, ord, "race_notake", "race", u2->race));
return;
}
2001-01-25 10:37:55 +01:00
itype = finditemtype(s, u->faction->locale);
if (itype!=NULL) {
item * i = *i_find(&u->items, itype);
if (i!=NULL) {
n = i->number - new_get_resvalue(u, itype->rtype);
2004-06-21 18:45:27 +02:00
give_item(n, itype, u, u2, ord);
2001-01-25 10:37:55 +01:00
return;
}
}
}
n = atoip(s); /* n: anzahl */
s = getstrtoken();
if (s == NULL) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 113, MSG_COMMERCE);
return;
2001-01-25 10:37:55 +01:00
}
if (u2 && !ucontact(u2, u)) {
const resource_type * rtype = findresourcetype(s, u->faction->locale);
if (rtype==NULL || !fval(rtype, RTF_SNEAK))
{
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 40, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
return;
}
}
i = findparam(s, u->faction->locale);
2001-01-25 10:37:55 +01:00
if (i == P_PERSON) {
if (!(u->race->ec_flags & GIVEPERSON)) {
ADDMSG(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, ord, "race_noregroup", "race", u->race));
2001-01-25 10:37:55 +01:00
return;
}
give_men(n, u, u2, ord);
2001-01-25 10:37:55 +01:00
return;
}
if (u2!=NULL) {
if (!(u->race->ec_flags & GIVEITEM) && u2!=NULL) {
ADDMSG(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, ord, "race_nogive", "race", u->race));
return;
}
if (!(u2->race->ec_flags & GETITEM)) {
ADDMSG(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, ord, "race_notake", "race", u2->race));
return;
}
}
2001-01-25 10:37:55 +01:00
itype = finditemtype(s, u->faction->locale);
if (itype!=NULL) {
2004-06-21 18:45:27 +02:00
give_item(n, itype, u, u2, ord);
2001-01-25 10:37:55 +01:00
return;
}
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 123, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
}
2004-06-21 18:45:27 +02:00
static int
forget_cmd(unit * u, order * ord)
2001-01-25 10:37:55 +01:00
{
skill_t talent;
2004-06-21 18:45:27 +02:00
const char *s;
init_tokens(ord);
skip_token();
s = getstrtoken();
2001-01-25 10:37:55 +01:00
if ((talent = findskill(s, u->faction->locale)) != NOSKILL) {
ADDMSG(&u->faction->msgs,
msg_message("forget", "unit skill", u, talent));
set_level(u, talent, 0);
2001-01-25 10:37:55 +01:00
}
2004-06-21 18:45:27 +02:00
return 0;
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
2002-09-02 22:36:12 +02:00
void
2001-01-25 10:37:55 +01:00
report_donations(void)
{
spende * sp;
for (sp = spenden; sp; sp = sp->next) {
region * r = sp->region;
if (sp->betrag > 0) {
struct message * msg = msg_message("donation",
"from to amount", sp->f1, sp->f2, sp->betrag);
r_addmessage(r, sp->f1, msg);
r_addmessage(r, sp->f2, msg);
msg_release(msg);
2001-01-25 10:37:55 +01:00
}
}
}
2002-09-02 22:36:12 +02:00
void
2001-01-25 10:37:55 +01:00
add_spende(faction * f1, faction * f2, int betrag, region * r)
{
spende *sp;
sp = spenden;
while (sp) {
if (sp->f1 == f1 && sp->f2 == f2 && sp->region == r) {
sp->betrag += betrag;
return;
}
sp = sp->next;
}
sp = calloc(1, sizeof(spende));
sp->f1 = f1;
sp->f2 = f2;
sp->region = r;
sp->betrag = betrag;
sp->next = spenden;
spenden = sp;
}
2002-09-02 22:36:12 +02:00
static boolean
maintain(building * b, boolean first)
/* first==false -> take money from wherever you can */
2001-01-25 10:37:55 +01:00
{
int c;
region * r = b->region;
boolean paid = true, work = first;
2001-01-25 10:37:55 +01:00
unit * u;
if (fval(b, BLD_MAINTAINED)) return true;
2003-12-14 11:02:29 +01:00
if (b->type==NULL || b->type->maintenance==NULL) return true;
2001-01-25 10:37:55 +01:00
if (is_cursed(b->attribs, C_NOCOST, 0)) {
fset(b, BLD_MAINTAINED);
fset(b, BLD_WORKING);
2001-01-25 10:37:55 +01:00
return true;
}
u = buildingowner(r, b);
if (u==NULL) return false;
for (c=0;b->type->maintenance[c].number;++c) {
const maintenance * m = b->type->maintenance+c;
int need = m->number;
if (fval(m, MTF_VARIABLE)) need = need * b->size;
if (u) {
/* first ist im ersten versuch true, im zweiten aber false! Das
* bedeutet, das in der Runde in die Region geschafften Resourcen
* nicht genutzt werden k<EFBFBD>nnen, weil die reserviert sind! */
if (!first) need -= get_all(u, m->rtype);
else need -= new_get_pooled(u, m->rtype, GET_DEFAULT);
if (!first && need > 0) {
2001-01-25 10:37:55 +01:00
unit * ua;
for (ua=r->units;ua;ua=ua->next) freset(ua->faction, FL_DH);
fset(u->faction, FL_DH); /* hat schon */
for (ua=r->units;ua;ua=ua->next) {
2002-09-02 22:36:12 +02:00
if (!fval(ua->faction, FL_DH) && (ua->faction == u->faction || alliedunit(ua, u->faction, HELP_MONEY))) {
need -= get_all(ua, m->rtype);
2001-01-25 10:37:55 +01:00
fset(ua->faction, FL_DH);
if (need<=0) break;
}
}
}
if (need > 0) {
if (!fval(m, MTF_VITAL)) work = false;
else {
paid = false;
break;
}
}
}
}
if (paid && c>0) {
/* TODO: wieviel von was wurde bezahlt */
message * msg;
if (first) {
msg = add_message(&u->faction->msgs,
msg_message("maintenance", "unit building", u, b));
} else {
msg = add_message(&u->faction->msgs,
msg_message("maintenance_late", "building", b));
}
msg_release(msg);
2001-01-25 10:37:55 +01:00
fset(b, BLD_MAINTAINED);
if (work) fset(b, BLD_WORKING);
for (c=0;b->type->maintenance[c].number;++c) {
const maintenance * m = b->type->maintenance+c;
int cost = m->number;
if (!fval(m, MTF_VITAL) && !work) continue;
if (fval(m, MTF_VARIABLE)) cost = cost * b->size;
if (!first) cost -= use_all(u, m->rtype, cost);
else cost -= new_use_pooled(u, m->rtype, GET_SLACK|GET_RESERVE|GET_POOLED_SLACK, cost);
if (!first && cost > 0) {
2001-01-25 10:37:55 +01:00
unit * ua;
for (ua=r->units;ua;ua=ua->next) freset(ua->faction, FL_DH);
fset(u->faction, FL_DH); /* hat schon */
for (ua=r->units;ua;ua=ua->next) {
2002-09-02 22:36:12 +02:00
if (!fval(ua->faction, FL_DH) && alliedunit(ua, u->faction, HELP_MONEY)) {
int give = use_all(ua, m->rtype, cost);
2001-01-25 10:37:55 +01:00
if (!give) continue;
cost -= give;
fset(ua->faction, FL_DH);
if (m->rtype==r_silver) add_spende(ua->faction, u->faction, give, r);
2001-01-25 10:37:55 +01:00
if (cost<=0) break;
}
}
}
assert(cost==0);
}
} else {
message * msg = add_message(&u->faction->msgs,
msg_message("maintenancefail", "unit building", u, b));
msg_release(msg);
2001-01-25 10:37:55 +01:00
return false;
}
return true;
}
2002-09-02 22:36:12 +02:00
static void
2001-01-25 10:37:55 +01:00
gebaeude_stuerzt_ein(region * r, building * b)
{
unit *u;
int n, i;
direction_t d;
int opfer = 0;
int road = 0;
struct message * msg;
2001-01-25 10:37:55 +01:00
for (u = r->units; u; u = u->next) {
if (u->building == b) {
int loss = 0;
fset(u->faction, FL_MARK);
freset(u, UFL_OWNER);
2001-01-25 10:37:55 +01:00
leave(r,u);
n = u->number;
for (i = 0; i < n; i++) {
if (rand() % 100 >= EINSTURZUEBERLEBEN) {
++loss;
}
}
scale_number(u, u->number - loss);
opfer += loss;
}
}
/* Falls Karawanserei, Damm oder Tunnel einst<73>rzen, wird die schon
* gebaute Stra<EFBFBD>e zur H<EFBFBD>lfte vernichtet */
if (b->type == bt_find("caravan") || b->type == bt_find("dam") || b->type == bt_find("tunnel")) {
for (d=0;d!=MAXDIRECTIONS;++d) if (rroad(r, d) > 0) {
road = 1;
/* vernichtung findet erst in destroy_building statt! */
break;
}
}
msg = msg_message("buildingcrash", "region building opfer road", r, b, opfer, road);
add_message(&r->msgs, msg);
for (u=r->units; u; u=u->next) {
faction * f = u->faction;
if (fval(f, FL_MARK)) {
freset(u->faction, FL_MARK);
add_message(&f->msgs, msg);
}
}
msg_release(msg);
2001-01-25 10:37:55 +01:00
destroy_building(b);
}
2002-09-02 22:36:12 +02:00
void
2001-01-25 10:37:55 +01:00
maintain_buildings(boolean crash)
{
region * r;
for (r = regions; r; r = r->next) {
building **bp = &r->buildings;
while (*bp) {
building * b = *bp;
if (!maintain(b, !crash) && crash) {
if (rand() % 100 < EINSTURZCHANCE) {
gebaeude_stuerzt_ein(r, b);
continue;
} else {
unit * u = buildingowner(r, b);
struct message * msg = msg_message("nomaintenance", "building", b);
2001-01-25 10:37:55 +01:00
if (u) {
add_message(&u->faction->msgs, msg);
r_addmessage(r, u->faction, msg);
2001-01-25 10:37:55 +01:00
}
}
}
bp=&b->next;
}
}
}
2004-06-21 18:45:27 +02:00
void
2001-01-25 10:37:55 +01:00
economics(void)
{
region *r;
unit *u;
/* Geben vor Selbstmord (doquit)! Hier alle unmittelbaren Befehle.
* Rekrutieren vor allen Einnahmequellen. Bewachen JA vor Steuern
* eintreiben. */
for (r = regions; r; r = r->next) if (r->units) {
request *recruitorders = NULL;
for (u = r->units; u; u = u->next) {
2004-06-21 18:45:27 +02:00
order * ord;
boolean destroyed = false;
for (ord = u->orders; ord; ord = ord->next) {
switch (get_keyword(ord)) {
case K_DESTROY:
2004-06-21 18:45:27 +02:00
if (!destroyed) {
if (destroy_cmd(u, ord)!=0) ord = NULL;
destroyed = true;
}
break;
case K_GIVE:
case K_LIEFERE:
2004-06-21 18:45:27 +02:00
give_cmd(u, ord);
break;
case K_FORGET:
2004-06-21 18:45:27 +02:00
forget_cmd(u, ord);
break;
2001-01-25 10:37:55 +01:00
}
2004-06-21 18:45:27 +02:00
if (u->orders==NULL) break;
}
}
/* RECRUIT orders */
2001-01-25 10:37:55 +01:00
for (u = r->units; u; u = u->next) {
2004-06-21 18:45:27 +02:00
order * ord;
for (ord = u->orders; ord; ord = ord->next) {
if (get_keyword(ord) == K_RECRUIT) {
recruit(u, ord, &recruitorders);
break;
}
}
}
2001-01-25 10:37:55 +01:00
if (recruitorders) expandrecruit(r, recruitorders);
2001-01-25 10:37:55 +01:00
}
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
static void
2001-01-25 10:37:55 +01:00
manufacture(unit * u, const item_type * itype, int want)
{
int n;
int skill;
int minskill = itype->construction->minskill;
skill_t sk = itype->construction->skill;
skill = effskill(u, sk);
skill = skillmod(itype->rtype->attribs, u, u->region, sk, skill, SMF_PRODUCTION);
if (skill < 0) {
/* an error occured */
int err = -skill;
2004-06-21 18:45:27 +02:00
cmistake(u, u->thisorder, err, MSG_PRODUCE);
return;
}
2001-01-25 10:37:55 +01:00
if(want==0)
want=maxbuild(u, itype->construction);
n = build(u, itype->construction, 0, want);
switch (n) {
case ENEEDSKILL:
ADDMSG(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, u->thisorder, "skill_needed", "skill", sk));
return;
case ELOWSKILL:
ADDMSG(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, u->thisorder, "manufacture_skills", "skill minskill product",
sk, minskill, itype->rtype, 1));
return;
case ENOMATERIALS:
/* something missing from the list of materials */
strcpy(buf, "Daf<EFBFBD>r braucht man mindestens:");
{
int c, n;
const construction * cons = itype->construction;
char * ch = buf+strlen(buf);
assert(cons);
for (c=0;cons->materials[c].number; c++) {
if (c!=0)
strcat(ch++, ",");
n=cons->materials[c].number / cons->reqsize;
sprintf(ch, " %d %s", n?n:1,
locale_string(u->faction->locale,
resname(cons->materials[c].type, cons->materials[c].number!=1)));
ch = ch+strlen(ch);
}
2004-06-21 18:45:27 +02:00
mistake(u, u->thisorder, buf, MSG_PRODUCE);
return;
}
}
if (n>0) {
i_change(&u->items, itype, n);
if (want==INT_MAX) want = n;
ADDMSG(&u->faction->msgs, msg_message("manufacture",
"unit region amount wanted resource", u, u->region, n, want, itype->rtype));
} else {
2004-06-21 18:45:27 +02:00
cmistake(u, u->thisorder, 125, MSG_PRODUCE);
}
2001-01-25 10:37:55 +01:00
}
typedef struct allocation {
struct allocation * next;
int want, get;
double save;
unsigned int flags;
2001-01-25 10:37:55 +01:00
unit * unit;
} allocation;
#define new_allocation() calloc(sizeof(allocation), 1)
#define free_allocation(a) free(a)
typedef struct allocation_list {
struct allocation_list * next;
allocation * data;
const resource_type * type;
} allocation_list;
static allocation_list * allocations;
2002-09-02 22:36:12 +02:00
static void
2001-01-25 10:37:55 +01:00
allocate_resource(unit * u, const resource_type * rtype, int want)
{
const item_type * itype = resource2item(rtype);
region * r = u->region;
int busy = u->number;
int dm = 0;
allocation_list * alist;
allocation * al;
unit *u2;
int amount, skill;
/* momentan kann man keine ressourcen abbauen, wenn man daf<61>r
* Materialverbrauch hat: */
2001-01-25 10:37:55 +01:00
assert(itype!=NULL && itype->construction->materials==NULL);
if (itype == olditemtype[I_WOOD] && fval(r, RF_MALLORN)) {
2004-06-21 18:45:27 +02:00
cmistake(u, u->thisorder, 92, MSG_PRODUCE);
2001-01-25 10:37:55 +01:00
return;
}
if (itype == olditemtype[I_MALLORN] && !fval(r, RF_MALLORN)) {
2004-06-21 18:45:27 +02:00
cmistake(u, u->thisorder, 91, MSG_PRODUCE);
2001-01-25 10:37:55 +01:00
return;
}
if (itype == olditemtype[I_LAEN]) {
2001-01-25 10:37:55 +01:00
struct building * b = inside_building(u);
const struct building_type * btype = b?b->type:NULL;
if (btype != bt_find("mine")) {
2004-06-21 18:45:27 +02:00
cmistake(u, u->thisorder, 104, MSG_PRODUCE);
2001-01-25 10:37:55 +01:00
return;
}
}
if (besieged(u)) {
2004-06-21 18:45:27 +02:00
cmistake(u, u->thisorder, 60, MSG_PRODUCE);
2001-01-25 10:37:55 +01:00
return;
}
/* Elfen k<>nnen Holzf<7A>llen durch Bewachen verhindern, wenn sie
die Holzf<EFBFBD>ller sehen. */
2001-01-25 10:37:55 +01:00
if (itype == olditemtype[I_WOOD] || itype == olditemtype[I_MALLORN]) {
for (u2 = r->units; u2; u2 = u2->next) {
if (getguard(u2) & GUARD_TREES
&& u2->number
&& cansee(u2->faction,r,u,0)
&& !besieged(u2)
&& !alliedunit(u2, u->faction, HELP_GUARD)
&& armedmen(u2)
) {
add_message(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, u->thisorder, "region_guarded", "guard", u2));
return;
}
2001-01-25 10:37:55 +01:00
}
}
/* Bergw<67>chter k<>nnen Abbau von Eisen/Laen durch Bewachen verhindern.
* Als magische Wesen 'sehen' Bergw<EFBFBD>chter alles und werden durch
* Belagerung nicht aufgehalten. (Ansonsten wie oben bei Elfen anpassen).
*/
if (itype == olditemtype[I_IRON] || itype == olditemtype[I_LAEN])
2001-01-25 10:37:55 +01:00
{
for (u2 = r->units; u2; u2 = u2->next ) {
if (getguard(u) & GUARD_MINING
&& !fval(u2, UFL_ISNEW)
&& u2->number
&& !alliedunit(u2, u->faction, HELP_GUARD))
2001-01-25 10:37:55 +01:00
{
add_message(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, u->thisorder, "region_guarded", "guard", u2));
2001-01-25 10:37:55 +01:00
return;
}
}
}
assert(itype->construction->skill!=0 || "limited resource needs a required skill for making it");
skill = eff_skill(u, itype->construction->skill, u->region);
if (skill == 0) {
skill_t sk = itype->construction->skill;
add_message(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, u->thisorder, "skill_needed", "skill", sk));
2001-01-25 10:37:55 +01:00
return;
}
if (skill < itype->construction->minskill) {
skill_t sk = itype->construction->skill;
add_message(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, u->thisorder, "manufacture_skills", "skill minskill product",
sk, itype->construction->minskill, itype->rtype));
2001-01-25 10:37:55 +01:00
return;
} else {
struct building * b = inside_building(u);
const struct building_type * btype = b?b->type:NULL;
if (itype == olditemtype[I_IRON] && btype == bt_find("mine")) {
2001-01-25 10:37:55 +01:00
++skill;
}
else if (itype == olditemtype[I_STONE] && btype == bt_find("quarry")) {
2001-01-25 10:37:55 +01:00
++skill;
}
else if (itype == olditemtype[I_WOOD] && btype == bt_find("sawmill")) {
2001-01-25 10:37:55 +01:00
++skill;
}
else if (itype == olditemtype[I_MALLORN] && btype == bt_find("sawmill")) {
2001-01-25 10:37:55 +01:00
++skill;
}
}
amount = skill * u->number;
/* nun ist amount die Gesamtproduktion der Einheit (in punkten) */
/* mit Flinkfingerring verzehnfacht sich die Produktion */
amount += skill * min(u->number, get_item(u,I_RING_OF_NIMBLEFINGER)) * 9;
/* Schaffenstrunk: */
if ((dm = get_effect(u, oldpotiontype[P_DOMORE])) != 0) {
dm = min(dm, u->number);
change_effect(u, oldpotiontype[P_DOMORE], -dm);
amount += dm * skill; /* dm Personen produzieren doppelt */
}
amount /= itype->construction->minskill;
/* Limitierung durch Parameter m. */
if (want > 0 && want < amount) amount = want;
busy = (amount + skill - 1) / skill; /* wieviel leute tun etwas? */
alist = allocations;
while (alist && alist->type!=rtype) alist = alist->next;
if (!alist) {
alist = calloc(sizeof(struct allocation_list), 1);
alist->next = allocations;
alist->type = rtype;
allocations = alist;
}
al = new_allocation();
al->want = amount;
al->save = 1.0;
2001-01-25 10:37:55 +01:00
al->next = alist->data;
al->unit = u;
alist->data = al;
if (itype==olditemtype[I_IRON]) {
struct building * b = inside_building(u);
const struct building_type * btype = b?b->type:NULL;
if (btype==bt_find("mine"))
al->save *= 0.5;
if (u->race == new_race[RC_DWARF]) {
#if RACE_ADJUSTMENTS
al->save *= 0.75;
#else
al->save *= 0.5;
#endif
}
2001-01-25 10:37:55 +01:00
} else if (itype==olditemtype[I_STONE]) {
struct building * b = inside_building(u);
const struct building_type * btype = b?b->type:NULL;
if (btype==bt_find("quarry"))
al->save = al->save*0.5;
#if RACE_ADJUSTMENTS
if (u->race == new_race[RC_TROLL])
al->save = al->save*0.75;
#endif
2001-01-25 10:37:55 +01:00
} else if (itype==olditemtype[I_MALLORN]) {
struct building * b = inside_building(u);
const struct building_type * btype = b?b->type:NULL;
if (btype==bt_find("sawmill"))
al->save *= 0.5;
2001-01-25 10:37:55 +01:00
} else if (itype==olditemtype[I_WOOD]) {
struct building * b = inside_building(u);
const struct building_type * btype = b?b->type:NULL;
if (btype==bt_find("sawmill"))
al->save *= 0.5;
2001-01-25 10:37:55 +01:00
}
}
typedef struct allocator {
const struct resource_type * type;
void (*allocate)(const struct allocator *, region *, allocation *);
struct allocator * next;
} allocator;
static struct allocator * allocators;
2002-09-02 22:36:12 +02:00
static const allocator *
get_allocator(const struct resource_type * type)
{
const struct allocator * alloc = allocators;
while (alloc && alloc->type!=type) alloc = alloc->next;
return alloc;
}
2002-09-02 22:36:12 +02:00
static allocator *
make_allocator(const struct resource_type * type, void (*allocate)(const struct allocator *, region *, allocation *))
{
allocator * alloc = (allocator *)malloc(sizeof(allocator));
alloc->type = type;
alloc->allocate = allocate;
return alloc;
}
2002-09-02 22:36:12 +02:00
static void
add_allocator(allocator * alloc)
{
alloc->next = allocators;
allocators = alloc;
}
enum {
AFL_DONE = 1<<0,
AFL_LOWSKILL = 1<<1
};
2002-09-02 22:36:12 +02:00
static int
required(int want, double save)
{
int norders = (int)(want * save);
if (norders < want*save) ++norders;
return norders;
}
#if NEW_RESOURCEGROWTH
2002-09-02 22:36:12 +02:00
static void
leveled_allocation(const allocator * self, region * r, allocation * alist)
{
const resource_type * rtype = self->type;
const item_type * itype = resource2item(rtype);
rawmaterial * rm = rm_get(r, rtype);
int need;
boolean first = true;
if (rm!=NULL) {
do {
int avail = rm->amount;
int norders = 0;
allocation * al;
if(avail <= 0) {
for (al=alist;al;al=al->next) {
al->get = 0;
}
break;
}
assert(avail>0);
for (al=alist;al;al=al->next) if (!fval(al, AFL_DONE)) {
int req = required(al->want-al->get, al->save);
assert(al->get<=al->want && al->get >= 0);
if (eff_skill(al->unit, itype->construction->skill, r)
>= rm->level+itype->construction->minskill-1) {
if (req) {
norders += req;
} else {
fset(al, AFL_DONE);
}
} else {
fset(al, AFL_DONE);
if (first) fset(al, AFL_LOWSKILL);
}
}
need = norders;
avail = min(avail, norders);
if (need>0) {
int use = 0;
for (al=alist;al;al=al->next) if (!fval(al, AFL_DONE)) {
if (avail > 0) {
int want = required(al->want-al->get, al->save);
int x = avail*want/norders;
/* Wenn Rest, dann w<>rfeln, ob ich was bekomme: */
if (rand() % norders < (avail*want) % norders)
++x;
avail -= x;
use += x;
norders -= want;
need -= x;
al->get = min(al->want, al->get+(int)(x/al->save));
}
}
if (use) {
assert(use<=rm->amount);
rm->type->use(rm, r, use);
}
assert(avail==0 || norders==0);
}
first = false;
} while (need>0);
}
}
#endif
2002-09-02 22:36:12 +02:00
static void
attrib_allocation(const allocator * self, region * r, allocation * alist)
{
allocation * al;
const resource_type * rtype = self->type;
int avail = 0;
int norders = 0;
attrib * a = a_find(rtype->attribs, &at_resourcelimit);
resource_limit * rdata = (resource_limit*)a->data.v;
for (al=alist;al;al=al->next) {
norders += required(al->want, al->save);
}
if (rdata->limit) {
avail = rdata->limit(r, rtype);
if (avail < 0) avail = 0;
}
else avail = rdata->value;
avail = min(avail, norders);
for (al=alist;al;al=al->next) {
if (avail > 0) {
int want = required(al->want, al->save);
int x = avail*want/norders;
/* Wenn Rest, dann w<>rfeln, ob ich was bekomme: */
if (rand() % norders < (avail*want) % norders)
++x;
avail -= x;
norders -= want;
al->get = min(al->want, (int)(x/al->save));
if (rdata->use) {
2003-03-25 17:20:38 +01:00
int use = required(al->get, al->save);
if (use) rdata->use(r, rtype, use);
}
}
}
assert(avail==0 || norders==0);
}
2002-09-02 22:36:12 +02:00
static void
2001-01-25 10:37:55 +01:00
split_allocations(region * r)
{
allocation_list ** p_alist=&allocations;
freset(r, RF_DH);
2001-01-25 10:37:55 +01:00
while (*p_alist) {
allocation_list * alist = *p_alist;
const resource_type * rtype = alist->type;
const allocator * alloc = get_allocator(rtype);
2001-01-25 10:37:55 +01:00
const item_type * itype = resource2item(rtype);
allocation ** p_al = &alist->data;
freset(r, RF_DH);
alloc->allocate(alloc, r, alist->data);
2001-01-25 10:37:55 +01:00
while (*p_al) {
allocation * al = *p_al;
if (al->get) {
assert(itype || !"not implemented for non-items");
i_change(&al->unit->items, itype, al->get);
produceexp(al->unit, itype->construction->skill, al->unit->number);
#if NEW_RESOURCEGROWTH
fset(r, RF_DH);
#endif
}
if (al->want==INT_MAX) al->want = al->get;
if (fval(al, AFL_LOWSKILL)) {
add_message(&al->unit->faction->msgs,
msg_message("produce_lowskill", "unit region resource",
al->unit, al->unit->region, rtype));
} else {
add_message(&al->unit->faction->msgs,
msg_message("produce", "unit region amount wanted resource",
al->unit, al->unit->region, al->get, al->want, rtype));
2001-01-25 10:37:55 +01:00
}
*p_al=al->next;
free_allocation(al);
}
*p_alist=alist->next;
free(alist);
}
allocations = NULL;
}
2002-09-02 22:36:12 +02:00
static void
2001-01-25 10:37:55 +01:00
create_potion(unit * u, const potion_type * ptype, int want)
{
int built;
if(want==0)
want=maxbuild(u, ptype->itype->construction);
built = build(u, ptype->itype->construction, 0, want);
switch (built) {
case ELOWSKILL:
case ENEEDSKILL:
/* no skill, or not enough skill points to build */
2004-06-21 18:45:27 +02:00
cmistake(u, u->thisorder, 50, MSG_PRODUCE);
break;
case ECOMPLETE:
assert(0);
break;
case ENOMATERIALS:
/* something missing from the list of materials */
strcpy(buf, "Daf<EFBFBD>r braucht man mindestens:");
{
int c, n;
const construction * cons = ptype->itype->construction;
char * ch = buf+strlen(buf);
assert(cons);
for (c=0;cons->materials[c].number; c++) {
if (c!=0)
strcat(ch++, ",");
n=cons->materials[c].number / cons->reqsize;
sprintf(ch, " %d %s", n?n:1,
locale_string(u->faction->locale,
resname(cons->materials[c].type, cons->materials[c].number!=1)));
ch = ch+strlen(ch);
}
strcat(ch,".");
2004-06-21 18:45:27 +02:00
mistake(u, u->thisorder, buf, MSG_PRODUCE);
return;
2001-01-25 10:37:55 +01:00
}
break;
default:
i_change(&u->items, ptype->itype, built);
if (want==INT_MAX) want = built;
ADDMSG(&u->faction->msgs, msg_message("manufacture",
"unit region amount wanted resource", u, u->region, built, want, ptype->itype->rtype));
break;
2001-01-25 10:37:55 +01:00
}
}
static void
2001-01-25 10:37:55 +01:00
create_item(unit * u, const item_type * itype, int want)
{
if (fval(itype->rtype, RTF_LIMITED)) {
#if GUARD_DISABLES_PRODUCTION == 1
if(is_guarded(u->region, u, GUARD_PRODUCE)) {
2004-06-21 18:45:27 +02:00
cmistake(u, u->thisorder, 70, MSG_EVENT);
return;
}
#endif
2001-01-25 10:37:55 +01:00
allocate_resource(u, itype->rtype, want);
} else {
2001-01-25 10:37:55 +01:00
const potion_type * ptype = resource2potion(itype->rtype);
if (ptype!=NULL) create_potion(u, ptype, want);
2002-03-10 11:09:16 +01:00
else if (itype->construction && itype->construction->materials) manufacture(u, itype, want);
2004-06-21 18:45:27 +02:00
else cmistake(u, u->thisorder, 125, MSG_PRODUCE);
2001-01-25 10:37:55 +01:00
}
}
2002-09-02 22:36:12 +02:00
static void
2004-06-21 18:45:27 +02:00
make_cmd(unit * u, struct order * ord)
2001-01-25 10:37:55 +01:00
{
2004-06-21 18:45:27 +02:00
region * r = u->region;
2001-01-25 10:37:55 +01:00
const building_type * btype;
const ship_type * stype;
param_t p;
int m;
const item_type * itype;
2004-06-21 18:45:27 +02:00
const char *s;
const struct locale * lang = u->faction->locale;
2004-06-21 18:45:27 +02:00
init_tokens(ord);
skip_token();
s = getstrtoken();
2001-01-25 10:37:55 +01:00
m = atoi(s);
sprintf(buf, "%d", m);
if (!strcmp(buf, s)) {
/* first came a want-paramter */
s = getstrtoken();
} else {
m = INT_MAX;
}
p = findparam(s, u->faction->locale);
2001-01-25 10:37:55 +01:00
/* MACHE TEMP kann hier schon gar nicht auftauchen, weil diese nicht in
* thisorder abgespeichert werden - und auf den ist getstrtoken() beim
* aufruf von make geeicht */
2001-01-25 10:37:55 +01:00
if (p == P_ROAD) {
2001-01-25 10:37:55 +01:00
direction_t d;
if(r->planep && fval(r->planep, PFL_NOBUILD)) {
2004-06-21 18:45:27 +02:00
cmistake(u, u->thisorder, 275, MSG_PRODUCE);
2001-01-25 10:37:55 +01:00
return;
}
d = finddirection(getstrtoken(), u->faction->locale);
2001-01-25 10:37:55 +01:00
if (d!=NODIRECTION) {
if(r->planep && fval(r->planep, PFL_NOBUILD)) {
2004-06-21 18:45:27 +02:00
cmistake(u, u->thisorder, 94, MSG_PRODUCE);
2001-01-25 10:37:55 +01:00
return;
}
build_road(r, u, m, d);
2004-06-21 18:45:27 +02:00
} else cmistake(u, u->thisorder, 71, MSG_PRODUCE);
2001-01-25 10:37:55 +01:00
return;
} else if (p == P_SHIP) {
if(r->planep && fval(r->planep, PFL_NOBUILD)) {
2004-06-21 18:45:27 +02:00
cmistake(u, u->thisorder, 276, MSG_PRODUCE);
2001-01-25 10:37:55 +01:00
return;
}
continue_ship(r, u, m);
return;
} else if (p == P_HERBS) {
herbsearch(r, u, m);
return;
2001-01-25 10:37:55 +01:00
}
/* since the string can match several objects, like in 'academy' and
* 'academy of arts', we need to figure out what the player meant.
* This is not 100% safe.
*/
stype = findshiptype(s, lang);
btype = findbuildingtype(s, lang);
itype = finditemtype(s, lang);
if (itype!=NULL && (btype!=NULL || stype!=NULL)) {
if (itype->construction==NULL) {
/* if the item cannot be made, we probably didn't mean to make it */
itype = NULL;
} else if (stype!=NULL) {
const char * sname = LOC(lang, stype->name[0]);
const char * iname = LOC(lang, resourcename(itype->rtype, 0));
if (strlen(iname)<strlen(sname)) stype = NULL;
else itype = NULL;
} else {
const char * bname = LOC(lang, btype->_name);
const char * iname = LOC(lang, resourcename(itype->rtype, 0));
if (strlen(iname)<strlen(bname)) btype = NULL;
else itype = NULL;
}
}
if (btype!=NULL && stype!=NULL) {
const char * bname = LOC(lang, btype->_name);
const char * sname = LOC(lang, stype->name[0]);
if (strlen(sname)<strlen(bname)) btype = NULL;
else stype = NULL;
}
2001-01-25 10:37:55 +01:00
if (stype != NOSHIP) {
if(r->planep && fval(r->planep, PFL_NOBUILD)) {
2004-06-21 18:45:27 +02:00
cmistake(u, u->thisorder, 276, MSG_PRODUCE);
2001-01-25 10:37:55 +01:00
return;
}
create_ship(r, u, stype, m);
return;
}
if (btype != NOBUILDING) {
if(r->planep && fval(r->planep, PFL_NOBUILD)) {
2004-06-21 18:45:27 +02:00
cmistake(u, u->thisorder, 94, MSG_PRODUCE);
2001-01-25 10:37:55 +01:00
return;
}
build_building(u, btype, m);
return;
}
if (itype!=NULL) {
create_item(u, itype, m);
return;
}
2004-06-21 18:45:27 +02:00
cmistake(u, u->thisorder, 125, MSG_PRODUCE);
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
const attrib_type at_luxuries = {
"luxuries", NULL, NULL, NULL, NULL, NULL
};
2002-09-02 22:36:12 +02:00
static void
2001-01-25 10:37:55 +01:00
expandbuying(region * r, request * buyorders)
{
int max_products;
unit *u;
static struct trade {
const luxury_type * type;
int number;
int multi;
} *trades, *trade;
static int ntrades=0;
int i, j;
const luxury_type * ltype;
if (ntrades==0) {
for (ltype=luxurytypes;ltype;ltype=ltype->next)
++ntrades;
trades = gc_add(calloc(sizeof(struct trade), ntrades));
for (i=0, ltype=luxurytypes;i!=ntrades;++i, ltype=ltype->next)
trades[i].type = ltype;
}
for (i=0;i!=ntrades;++i) {
trades[i].number = 0;
trades[i].multi = 1;
}
if (!buyorders) return;
/* Initialisation. multiplier ist der Multiplikator auf den
* Verkaufspreis. F<EFBFBD>r max_products Produkte kauft man das Produkt zum
* einfachen Verkaufspreis, danach erh<EFBFBD>ht sich der Multiplikator um 1.
* counter ist ein Z<EFBFBD>hler, der die gekauften Produkte z<EFBFBD>hlt. money
* wird f<EFBFBD>r die debug message gebraucht. */
2001-01-25 10:37:55 +01:00
max_products = rpeasants(r) / TRADE_FRACTION;
/* Kauf - auch so programmiert, da<64> er leicht erweiterbar auf mehrere
* G<EFBFBD>ter pro Monat ist. j sind die Befehle, i der Index des
* gehandelten Produktes. */
if (max_products>0) {
2001-01-25 10:37:55 +01:00
expandorders(r, buyorders);
if (!norders) return;
for (j = 0; j != norders; j++) {
int price, multi;
ltype = oa[j].type.ltype;
trade = trades;
while (trade->type!=ltype) ++trade;
multi = trade->multi;
if (trade->number + 1 > max_products) ++multi;
price = ltype->price * multi;
if (get_pooled(oa[j].unit, r, R_SILVER) >= price) {
unit * u = oa[j].unit;
/* litems z<>hlt die G<>ter, die verkauft wurden, u->n das Geld, das
* verdient wurde. Dies mu<EFBFBD> gemacht werden, weil der Preis st<EFBFBD>ndig sinkt,
* man sich also das verdiente Geld und die verkauften Produkte separat
* merken mu<EFBFBD>. */
2001-01-25 10:37:55 +01:00
attrib * a = a_find(u->attribs, &at_luxuries);
if (a==NULL) a = a_add(&u->attribs, a_new(&at_luxuries));
i_change((item**)&a->data.v, ltype->itype, 1);
i_change(&oa[j].unit->items, ltype->itype, 1);
use_pooled(u, r, R_SILVER, price);
if (u->n < 0)
u->n = 0;
u->n += price;
rsetmoney(r, rmoney(r) + price);
/* Falls mehr als max_products Bauern ein Produkt verkauft haben, steigt
* der Preis Multiplikator f<EFBFBD>r das Produkt um den Faktor 1. Der Z<EFBFBD>hler
* wird wieder auf 0 gesetzt. */
2001-01-25 10:37:55 +01:00
if (++trade->number > max_products) {
trade->number = 0;
++trade->multi;
}
}
}
free(oa);
/* Ausgabe an Einheiten */
for (u = r->units; u; u = u->next) {
attrib * a = a_find(u->attribs, &at_luxuries);
item * itm;
if (a==NULL) continue;
add_message(&u->faction->msgs, new_message(u->faction,
"buy%u:unit%i:money", u, u->n));
2001-01-25 10:37:55 +01:00
for (itm=(item*)a->data.v; itm; itm=itm->next) {
if (itm->number) {
add_message(&u->faction->msgs, new_message(u->faction,
"buyamount%u:unit%i:amount%X:resource", u, itm->number, itm->type->rtype));
2001-01-25 10:37:55 +01:00
}
}
a_remove(&u->attribs, a);
}
}
2001-01-25 10:37:55 +01:00
}
attrib_type at_trades = {
"trades",
DEFAULT_INIT,
DEFAULT_FINALIZE,
DEFAULT_AGE,
NO_WRITE,
NO_READ
2001-01-25 10:37:55 +01:00
};
static void
2004-06-21 18:45:27 +02:00
buy(unit * u, request ** buyorders, struct order * ord)
2001-01-25 10:37:55 +01:00
{
2004-06-21 18:45:27 +02:00
region * r = u->region;
2001-01-25 10:37:55 +01:00
int n, k;
request *o;
attrib * a;
const item_type * itype = NULL;
const luxury_type * ltype = NULL;
if (u->ship && is_guarded(r, u, GUARD_CREWS)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 69, MSG_INCOME);
2001-01-25 10:37:55 +01:00
return;
}
if (u->ship && is_guarded(r, u, GUARD_CREWS)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 69, MSG_INCOME);
2001-01-25 10:37:55 +01:00
return;
}
/* Im Augenblick kann man nur 1 Produkt kaufen. expandbuying ist aber
* schon daf<EFBFBD>r ausger<EFBFBD>stet, mehrere Produkte zu kaufen. */
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
init_tokens(ord);
skip_token();
2001-01-25 10:37:55 +01:00
n = geti();
if (!n) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 26, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
return;
}
if (besieged(u)) {
/* Belagerte Einheiten k<>nnen nichts kaufen. */
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 60, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
return;
}
if (u->race == new_race[RC_INSECT]) {
2001-01-25 10:37:55 +01:00
/* entweder man ist insekt, oder... */
if (rterrain(r) != T_SWAMP && rterrain(r) != T_DESERT && !rbuildings(r)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 119, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
return;
}
} else {
/* ...oder in der Region mu<6D> es eine Burg geben. */
if (!rbuildings(r)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 119, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
return;
}
}
/* Ein H<>ndler kann nur 10 G<>ter pro Talentpunkt handeln. */
k = u->number * 10 * eff_skill(u, SK_TRADE, r);
/* hat der H<>ndler bereits gehandelt, muss die Menge der bereits
* verkauften/gekauften G<EFBFBD>ter abgezogen werden */
2001-01-25 10:37:55 +01:00
a = a_find(u->attribs, &at_trades);
if (!a) {
a = a_add(&u->attribs, a_new(&at_trades));
} else {
k -= a->data.i;
}
n = min(n, k);
if (!n) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 102, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
return;
}
assert(n>=0);
/* die Menge der verkauften G<>ter merken */
a->data.i += n;
itype = finditemtype(getstrtoken(), u->faction->locale);
if (itype!=NULL) {
ltype = resource2luxury(itype->rtype);
if (ltype==NULL) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 124, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
return;
}
}
if (r_demand(r, ltype)) {
add_message(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, ord, "luxury_notsold", ""));
2001-01-25 10:37:55 +01:00
return;
}
o = (request *) calloc(1, sizeof(request));
o->type.ltype = ltype; /* sollte immer gleich sein */
o->unit = u;
o->qty = n;
addlist(buyorders, o);
if (n) fset(u, UFL_TRADER);
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
/* Steuers<72>tze in % bei Burggr<67><72>e */
static int tax_per_size[7] =
{0, 6, 12, 18, 24, 30, 36};
2001-01-25 10:37:55 +01:00
2002-09-02 22:36:12 +02:00
static void
expandselling(region * r, request * sellorders, int limit)
2001-01-25 10:37:55 +01:00
{
int money, price, j, max_products;
/* int m, n = 0; */
int maxsize = 0, maxeffsize = 0;
int taxcollected = 0;
int hafencollected = 0;
unit *maxowner = (unit *) NULL;
building *maxb = (building *) NULL;
building *b;
unit *u;
unit *hafenowner;
static int *counter;
static int ncounter = 0;
if (ncounter==0) {
const luxury_type * ltype;
for (ltype=luxurytypes;ltype;ltype=ltype->next) ++ncounter;
counter=(int*)gc_add(calloc(sizeof(int), ncounter));
} else {
memset(counter, 0, sizeof(int)*ncounter);
}
if (!sellorders) /* NEIN, denn Insekten k<>nnen in || !r->buildings) */
return; /* S<>mpfen und W<>sten auch so handeln */
/* Stelle Eigent<6E>mer der gr<67><72>ten Burg fest. Bekommt Steuern aus jedem
* Verkauf. Wenn zwei Burgen gleicher Gr<EFBFBD><EFBFBD>e bekommt gar keiner etwas. */
2001-01-25 10:37:55 +01:00
for (b = rbuildings(r); b; b = b->next) {
if (b->size > maxsize && buildingowner(r, b) != NULL
&& b->type == bt_find("castle")) {
maxb = b;
maxsize = b->size;
maxowner = buildingowner(r, b);
} else if (b->size == maxsize && b->type == bt_find("castle")) {
maxb = (building *) NULL;
maxowner = (unit *) NULL;
}
2001-01-25 10:37:55 +01:00
}
hafenowner = owner_buildingtyp(r, bt_find("harbour"));
2001-01-25 10:37:55 +01:00
if (maxb != (building *) NULL && maxowner != (unit *) NULL) {
maxeffsize = buildingeffsize(maxb, false);
if (maxeffsize == 0) {
maxb = (building *) NULL;
maxowner = (unit *) NULL;
}
}
/* Die Region muss genug Geld haben, um die Produkte kaufen zu k<>nnen. */
money = rmoney(r);
/* max_products sind 1/100 der Bev<65>lkerung, falls soviele Produkte
* verkauft werden - counter[] - sinkt die Nachfrage um 1 Punkt.
* multiplier speichert r->demand f<EFBFBD>r die debug message ab. */
2001-01-25 10:37:55 +01:00
max_products = rpeasants(r) / TRADE_FRACTION;
if (max_products <= 0) return;
if (rterrain(r) == T_DESERT && buildingtype_exists(r, bt_find("caravan"))) {
2001-01-25 10:37:55 +01:00
max_products = rpeasants(r) * 2 / TRADE_FRACTION;
}
/* Verkauf: so programmiert, dass er leicht auf mehrere Gueter pro
* Runde erweitert werden kann. */
2001-01-25 10:37:55 +01:00
expandorders(r, sellorders);
if (!norders) return;
for (j = 0; j != norders; j++) {
static const luxury_type * search=NULL;
const luxury_type * ltype = oa[j].type.ltype;
int multi = r_demand(r, ltype);
static int i=-1;
int use = 0;
if (search!=ltype) {
i=0;
for (search=luxurytypes;search!=ltype;search=search->next) ++i;
}
if (counter[i]>=limit) continue;
2001-01-25 10:37:55 +01:00
if (counter[i]+1 > max_products && multi > 1) --multi;
price = ltype->price * multi;
if (money >= price) {
int abgezogenhafen = 0;
int abgezogensteuer = 0;
unit * u = oa[j].unit;
attrib * a = a_find(u->attribs, &at_luxuries);
if (a==NULL) a = a_add(&u->attribs, a_new(&at_luxuries));
i_change((item**)&a->data.v, ltype->itype, 1);
++use;
if (u->n < 0)
u->n = 0;
if (hafenowner != NULL) {
if (hafenowner->faction != u->faction) {
abgezogenhafen = price / 10;
hafencollected += abgezogenhafen;
price -= abgezogenhafen;
money -= abgezogenhafen;
}
}
if (maxb != NULL) {
if (maxowner->faction != u->faction) {
abgezogensteuer = price * tax_per_size[maxeffsize] / 100;
taxcollected += abgezogensteuer;
price -= abgezogensteuer;
money -= abgezogensteuer;
}
}
u->n += price;
change_money(u, price);
/* r->money -= price; --- dies wird eben nicht ausgef<65>hrt, denn die
* Produkte k<EFBFBD>nnen auch als Steuern eingetrieben werden. In der Region
* wurden Silberst<EFBFBD>cke gegen Luxusg<EFBFBD>ter des selben Wertes eingetauscht!
* Falls mehr als max_products Kunden ein Produkt gekauft haben, sinkt
* die Nachfrage f<EFBFBD>r das Produkt um 1. Der Z<EFBFBD>hler wird wieder auf 0
* gesetzt. */
2001-01-25 10:37:55 +01:00
if (++counter[i] > max_products) {
int d = r_demand(r, ltype);
if (d > 1)
r_setdemand(r, ltype, d-1);
counter[i] = 0;
}
}
if (use>0) {
#ifdef NDEBUG
new_use_pooled(oa[j].unit, ltype->itype->rtype, GET_DEFAULT, use);
#else
/* int i = */ new_use_pooled(oa[j].unit, ltype->itype->rtype, GET_DEFAULT, use);
/* assert(i==use); */
#endif
}
}
free(oa);
/* Steuern. Hier werden die Steuern dem Besitzer der gr<67><72>ten Burg gegeben. */
if (maxowner) {
if (taxcollected > 0) {
change_money(maxowner, (int) taxcollected);
add_income(maxowner, IC_TRADETAX, taxcollected, taxcollected);
/* sprintf(buf, "%s verdient %d Silber durch den Handel in %s.",
unitname(maxowner), (int) taxcollected, regionid(r)); */
2001-01-25 10:37:55 +01:00
}
}
if (hafenowner) {
if (hafencollected > 0) {
change_money(hafenowner, (int) hafencollected);
add_income(hafenowner, IC_TRADETAX, hafencollected, hafencollected);
}
}
/* Berichte an die Einheiten */
for (u = r->units; u; u = u->next) {
attrib * a = a_find(u->attribs, &at_luxuries);
item * itm;
if (a==NULL) continue;
for (itm=(item*)a->data.v; itm; itm=itm->next) {
if (itm->number) {
ADDMSG(&u->faction->msgs, msg_message("sellamount",
"unit amount resource", u, itm->number, itm->type->rtype));
2001-01-25 10:37:55 +01:00
}
}
a_remove(&u->attribs, a);
add_income(u, IC_TRADE, u->n, u->n);
}
}
static boolean
2004-06-21 18:45:27 +02:00
sell(unit * u, request ** sellorders, struct order * ord)
2001-01-25 10:37:55 +01:00
{
boolean unlimited = true;
2001-01-25 10:37:55 +01:00
const item_type * itype;
const luxury_type * ltype=NULL;
int n;
2004-06-21 18:45:27 +02:00
region * r = u->region;
const char *s;
2001-01-25 10:37:55 +01:00
if (u->ship && is_guarded(r, u, GUARD_CREWS)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 69, MSG_INCOME);
return false;
2001-01-25 10:37:55 +01:00
}
/* sellorders sind KEIN array, weil f<>r alle items DIE SELBE resource
* (das geld der region) aufgebraucht wird. */
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
init_tokens(ord);
skip_token();
2001-01-25 10:37:55 +01:00
s = getstrtoken();
2004-06-21 18:45:27 +02:00
if (findparam(s, u->faction->locale) == P_ANY) {
unlimited = false;
2002-02-17 09:57:39 +01:00
n = rpeasants(r) / TRADE_FRACTION;
if (rterrain(r) == T_DESERT && buildingtype_exists(r, bt_find("caravan")))
2001-01-25 10:37:55 +01:00
n *= 2;
if (n==0) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 303, MSG_COMMERCE);
return false;
}
} else {
2001-01-25 10:37:55 +01:00
n = atoi(s);
if (n==0) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 27, MSG_COMMERCE);
return false;
}
2001-01-25 10:37:55 +01:00
}
/* Belagerte Einheiten k<>nnen nichts verkaufen. */
if (besieged(u)) {
add_message(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, ord, "error60", ""));
return false;
2001-01-25 10:37:55 +01:00
}
/* In der Region mu<6D> es eine Burg geben. */
if (u->race == new_race[RC_INSECT]) {
2001-01-25 10:37:55 +01:00
if (rterrain(r) != T_SWAMP && rterrain(r) != T_DESERT && !rbuildings(r)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 119, MSG_COMMERCE);
return false;
2001-01-25 10:37:55 +01:00
}
} else {
if (!rbuildings(r)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 119, MSG_COMMERCE);
return false;
2001-01-25 10:37:55 +01:00
}
}
/* Ein H<>ndler kann nur 10 G<>ter pro Talentpunkt verkaufen. */
n = min(n, u->number * 10 * eff_skill(u, SK_TRADE, r));
if (!n) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 54, MSG_COMMERCE);
return false;
2001-01-25 10:37:55 +01:00
}
s=getstrtoken();
itype = finditemtype(s, u->faction->locale);
if (itype!=NULL) ltype = resource2luxury(itype->rtype);
if (ltype==NULL) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 126, MSG_COMMERCE);
return false;
2001-01-25 10:37:55 +01:00
}
else {
attrib * a;
request *o;
2001-01-25 10:37:55 +01:00
int k, available;
2001-01-25 10:37:55 +01:00
if (!r_demand(r, ltype)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 263, MSG_COMMERCE);
return false;
2001-01-25 10:37:55 +01:00
}
available = new_get_pooled(u, itype->rtype, GET_DEFAULT);
/* Wenn andere Einheiten das selbe verkaufen, mu<6D> ihr Zeug abgezogen
* werden damit es nicht zweimal verkauft wird: */
for (o=*sellorders;o;o=o->next) {
2001-01-25 10:37:55 +01:00
if (o->type.ltype==ltype && o->unit->faction == u->faction) {
int fpool = o->qty - new_get_pooled(o->unit, itype->rtype, GET_RESERVE);
available -= max(0, fpool);
}
}
n = min(n, available);
if (n <= 0) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 264, MSG_COMMERCE);
return false;
2001-01-25 10:37:55 +01:00
}
/* Hier wird request->type verwendet, weil die obere limit durch
* das silber gegeben wird (region->money), welches f<EFBFBD>r alle
* (!) produkte als summe gilt, als nicht wie bei der
* produktion, wo f<EFBFBD>r jedes produkt einzeln eine obere limite
* existiert, so dass man arrays von orders machen kann. */
2001-01-25 10:37:55 +01:00
/* Ein H<>ndler kann nur 10 G<>ter pro Talentpunkt handeln. */
k = u->number * 10 * eff_skill(u, SK_TRADE, r);
/* hat der H<>ndler bereits gehandelt, muss die Menge der bereits
* verkauften/gekauften G<EFBFBD>ter abgezogen werden */
2001-01-25 10:37:55 +01:00
a = a_find(u->attribs, &at_trades);
if (!a) {
a = a_add(&u->attribs, a_new(&at_trades));
} else {
k -= a->data.i;
}
n = min(n, k);
assert(n>=0);
/* die Menge der verkauften G<>ter merken */
a->data.i += n;
o = (request *) calloc(1, sizeof(request));
o->unit = u;
o->qty = n;
o->type.ltype = ltype;
addlist(sellorders, o);
if (n) fset(u, UFL_TRADER);
return unlimited;
2001-01-25 10:37:55 +01:00
}
}
/* ------------------------------------------------------------- */
2002-09-02 22:36:12 +02:00
static void
2001-01-25 10:37:55 +01:00
expandstealing(region * r, request * stealorders)
{
int i;
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<EFBFBD> einzeln behandelt werden.
*
* u ist die beklaute unit. oa.unit ist die klauende unit.
*/
2001-01-25 10:37:55 +01:00
for (i = 0; i != norders && oa[i].unit->n <= oa[i].unit->wants; i++) {
unit *u = findunitg(oa[i].no, r);
int n = 0;
if (u && u->region==r) n = get_all(u, r_silver);
2001-01-25 10:37:55 +01:00
#ifndef GOBLINKILL
if (oa[i].type.goblin) { /* Goblin-Spezialklau */
int uct = 0;
unit *u2;
assert(effskill(oa[i].unit, SK_STEALTH)>=4 || !"this goblin\'s talent is too low");
for (u2 = r->units; u2; u2 = u2->next)
if (u2->faction == u->faction)
uct += u2->number;
n -= uct * 2 * MAINTENANCE;
}
#endif
if (n>10 && rplane(r) && (rplane(r)->flags & PFL_NOALLIANCES)) {
/* In Questen nur reduziertes Klauen */
n = 10;
}
if (n > 0) {
n = min(n, oa[i].unit->wants);
use_all(u, r_silver, n);
2001-01-25 10:37:55 +01:00
oa[i].unit->n = n;
change_money(oa[i].unit, n);
ADDMSG(&u->faction->msgs, msg_message("stealeffect", "unit region amount", u, u->region, n));
2001-01-25 10:37:55 +01:00
}
add_income(oa[i].unit, IC_STEAL, oa[i].unit->wants, oa[i].unit->n);
}
free(oa);
}
/* ------------------------------------------------------------- */
static void
plant(region *r, unit *u, int raw)
2001-01-25 10:37:55 +01:00
{
int n, i, skill, planted = 0;
const herb_type * htype;
if (rterrain(r) == T_OCEAN) {
return;
}
if (rherbtype(r) == NULL) {
2004-06-21 18:45:27 +02:00
cmistake(u, u->thisorder, 108, MSG_PRODUCE);
2001-01-25 10:37:55 +01:00
return;
}
/* Skill pr<70>fen */
skill = eff_skill(u, SK_HERBALISM, r);
htype = rherbtype(r);
2001-01-25 10:37:55 +01:00
if (skill < 6) {
add_message(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, u->thisorder, "plant_skills",
"skill minskill product", SK_HERBALISM, 6, htype->itype->rtype, 1));
2001-01-25 10:37:55 +01:00
return;
}
/* Wasser des Lebens pr<70>fen */
if (get_pooled(u, r, R_TREES) == 0) {
add_message(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, u->thisorder, "resource_missing", "missing",
oldresourcetype[R_TREES]));
2001-01-25 10:37:55 +01:00
return;
}
n = new_get_pooled(u, htype->itype->rtype, GET_DEFAULT);
/* Kr<4B>uter pr<70>fen */
if (n==0) {
add_message(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, u->thisorder, "resource_missing", "missing",
htype->itype->rtype));
2001-01-25 10:37:55 +01:00
return;
}
n = min(skill*u->number, n);
n = min(raw, n);
2001-01-25 10:37:55 +01:00
/* F<>r jedes Kraut Talent*10% Erfolgschance. */
for(i = n; i>0; i--) {
if (rand()%10 < skill) planted++;
}
produceexp(u, SK_HERBALISM, u->number);
2001-01-25 10:37:55 +01:00
/* Alles ok. Abziehen. */
new_use_pooled(u, oldresourcetype[R_TREES], GET_DEFAULT, 1);
new_use_pooled(u, htype->itype->rtype, GET_DEFAULT, n);
rsetherbs(r, rherbs(r)+planted);
add_message(&u->faction->msgs, new_message(u->faction,
"plant%u:unit%r:region%i:amount%X:herb", u, r, planted, htype->itype->rtype));
2001-01-25 10:37:55 +01:00
}
#if GROWING_TREES
2002-09-02 22:36:12 +02:00
static void
planttrees(region *r, unit *u, int raw)
2001-01-25 10:37:55 +01:00
{
int n, i, skill, planted = 0;
const item_type * itype;
if (rterrain(r) == T_OCEAN) {
return;
}
/* Mallornb<6E>ume kann man nur in Mallornregionen z<>chten */
if (fval(r, RF_MALLORN)) {
itype = &it_mallornseed;
} else {
itype = &it_seed;
}
2001-01-25 10:37:55 +01:00
/* Skill pr<70>fen */
skill = eff_skill(u, SK_HERBALISM, r);
if (skill < 6) {
add_message(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, u->thisorder, "plant_skills",
"skill minskill product", SK_HERBALISM, 6, itype->rtype, 1));
2001-01-25 10:37:55 +01:00
return;
}
if (fval(r, RF_MALLORN) && skill < 7 ) {
add_message(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, u->thisorder, "plant_skills",
"skill minskill product", SK_HERBALISM, 7, itype->rtype, 1));
return;
}
n = new_get_pooled(u, itype->rtype, GET_DEFAULT);
/* Samen pr<70>fen */
if (n==0) {
add_message(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, u->thisorder, "resource_missing", "missing",
itype->rtype));
return;
}
/* wenn eine Anzahl angegeben wurde, nur soviel verbrauchen */
n = min(raw, n);
n = min(skill*u->number, n);
/* F<>r jeden Samen Talent*10% Erfolgschance. */
for(i = n; i>0; i--) {
if (rand()%10 < skill) planted++;
}
rsettrees(r, 0, rtrees(r, 0)+planted);
/* Alles ok. Abziehen. */
produceexp(u, SK_HERBALISM, u->number);
new_use_pooled(u, itype->rtype, GET_DEFAULT, n);
ADDMSG(&u->faction->msgs, msg_message("plant",
"unit region amount herb", u, r, planted, itype->rtype));
}
/* z<>chte b<>ume */
static void
breedtrees(region *r, unit *u, int raw)
{
int n, i, skill, planted = 0;
const item_type * itype;
int current_season = season(turn);
/* B<>ume z<>chten geht nur im Fr<46>hling */
if (current_season != SEASON_SPRING){
planttrees(r, u, raw);
return;
}
if (rterrain(r) == T_OCEAN) {
return;
}
/* Mallornb<6E>ume kann man nur in Mallornregionen z<>chten */
if (fval(r, RF_MALLORN)) {
itype = &it_mallornseed;
} else {
itype = &it_seed;
}
/* Skill pr<70>fen */
skill = eff_skill(u, SK_HERBALISM, r);
if (skill < 12) {
planttrees(r, u, raw);
return;
}
n = new_get_pooled(u, itype->rtype, GET_DEFAULT);
/* Samen pr<70>fen */
if (n==0) {
add_message(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, u->thisorder, "resource_missing", "missing",
itype->rtype));
return;
}
/* wenn eine Anzahl angegeben wurde, nur soviel verbrauchen */
n = min(raw, n);
n = min(skill*u->number, n);
/* F<>r jeden Samen Talent*5% Erfolgschance. */
for(i = n; i>0; i--) {
if (rand()%100 < skill*5) planted++;
}
rsettrees(r, 1, rtrees(r, 1)+planted);
/* Alles ok. Abziehen. */
produceexp(u, SK_HERBALISM, u->number);
new_use_pooled(u, itype->rtype, GET_DEFAULT, n);
add_message(&u->faction->msgs, new_message(u->faction,
"plant%u:unit%r:region%i:amount%X:herb", u, r, planted, itype->rtype));
}
#endif
static void
2004-06-21 18:45:27 +02:00
plant_cmd(unit *u, struct order * ord)
{
2004-06-21 18:45:27 +02:00
region * r = u->region;
int m;
const char *s;
param_t p;
const item_type * itype = NULL;
if (r->land==NULL) {
2004-06-21 18:45:27 +02:00
/* TODO: error message here */
return;
}
2004-06-21 18:45:27 +02:00
/* pflanze [<anzahl>] <parameter> */
init_tokens(ord);
skip_token();
s = getstrtoken();
m = atoi(s);
sprintf(buf, "%d", m);
if (!strcmp(buf, s)) {
/* first came a want-paramter */
s = getstrtoken();
} else {
m = INT_MAX;
}
if(!s[0]){
p = P_ANY;
} else {
p = findparam(s, u->faction->locale);
itype = finditemtype(s, u->faction->locale);
}
if (p==P_HERBS){
plant(r, u, m);
return;
}
#if GROWING_TREES
else if (p==P_TREES){
breedtrees(r, u, m);
return;
}
else if (itype!=NULL){
if (itype==&it_mallornseed || itype==&it_seed) {
breedtrees(r, u, m);
return;
}
}
#endif
}
/* z<>chte pferde */
static void
breedhorses(region *r, unit *u)
{
int n, c;
int gezuechtet = 0;
struct building * b = inside_building(u);
const struct building_type * btype = b?b->type:NULL;
if (btype!=bt_find("stables")) {
2004-06-21 18:45:27 +02:00
cmistake(u, u->thisorder, 122, MSG_PRODUCE);
return;
2001-01-25 10:37:55 +01:00
}
if (get_item(u, I_HORSE) < 2) {
2004-06-21 18:45:27 +02:00
cmistake(u, u->thisorder, 107, MSG_PRODUCE);
2001-01-25 10:37:55 +01:00
return;
}
n = min(u->number * eff_skill(u, SK_HORSE_TRAINING, r), get_item(u, I_HORSE));
for (c = 0; c < n; c++) {
if (rand() % 100 < eff_skill(u, SK_HORSE_TRAINING, r)) {
change_item(u, I_HORSE, 1);
gezuechtet++;
}
}
if (gezuechtet > 0) {
produceexp(u, SK_HORSE_TRAINING, u->number);
}
2001-01-25 10:37:55 +01:00
add_message(&u->faction->msgs, new_message(u->faction,
"raised%u:unit%i:amount", u, gezuechtet));
2001-01-25 10:37:55 +01:00
}
static void
2004-06-21 18:45:27 +02:00
breed_cmd(unit *u, struct order * ord)
{
int m;
const char *s;
param_t p;
2004-06-21 18:45:27 +02:00
region *r = u->region;
/* z<>chte [<anzahl>] <parameter> */
2004-06-21 18:45:27 +02:00
init_tokens(ord);
skip_token();
s = getstrtoken();
2004-06-21 18:45:27 +02:00
m = atoi(s);
sprintf(buf, "%d", m);
if (!strcmp(buf, s)) {
/* first came a want-paramter */
s = getstrtoken();
} else {
m = INT_MAX;
}
if(!s[0]){
p = P_ANY;
} else {
p = findparam(s, u->faction->locale);
}
switch (p) {
case P_HERBS:
plant(r, u, m);
return;
#if GROWING_TREES
case P_TREES:
breedtrees(r, u, m);
return;
#endif
default:
breedhorses(r, u);
return;
}
}
2002-09-02 22:36:12 +02:00
static const char *
2001-01-25 10:37:55 +01:00
rough_amount(int a, int m)
{
int p = (a * 100)/m;
if (p < 10) {
return "sehr wenige";
} else if (p < 30) {
return "wenige";
} else if (p < 60) {
return "relativ viele";
} else if (p < 90) {
return "viele";
}
return "sehr viele";
}
2002-09-02 22:36:12 +02:00
static void
2004-06-21 18:45:27 +02:00
research_cmd(unit *u, struct order * ord)
2001-01-25 10:37:55 +01:00
{
2004-06-21 18:45:27 +02:00
region *r = u->region;
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
init_tokens(ord);
skip_token();
/*
const char *s = getstrtoken();
2001-01-25 10:37:55 +01:00
if (findparam(s, u->faction->locale) == P_HERBS) { */
2001-01-25 10:37:55 +01:00
if (eff_skill(u, SK_HERBALISM, r) < 7) {
cmistake(u, ord, 227, MSG_EVENT);
return;
}
produceexp(u, SK_HERBALISM, u->number);
2001-01-25 10:37:55 +01:00
if (rherbs(r) > 0) {
const herb_type *rht = rherbtype(r);
if (rht != NULL) {
add_message(&u->faction->msgs, new_message(u->faction,
"researchherb%u:unit%r:region%s:amount%X:herb", u, r,
rough_amount(rherbs(r), 100), rht->itype->rtype));
} else {
add_message(&u->faction->msgs, new_message(u->faction,
"researchherb_none%u:unit%r:region", u, r));
}
} else {
add_message(&u->faction->msgs, new_message(u->faction,
"researchherb_none%u:unit%r:region", u, r));
}
2001-01-25 10:37:55 +01:00
}
static int
2001-01-25 10:37:55 +01:00
wahrnehmung(region * r, faction * f)
{
unit *u;
int w = 0;
for (u = r->units; u; u = u->next) {
magicskillboost -- implementiert. Kann nur einmal pro Partei verwendet werden, und nur von einem Magier. Müssen wir erklären. Amulett des wahren Sehens und Ring der Unsichtbarkeit geben lediglich einen Bonus auf Wahrnehmung (+2) und Tarnung (+4). -- das define war nicht aktiviert Halblinge haben Armbrustschiessen +0 (statt +1). -- spearates Rassenfile für WDW Es gibt spezielle Regeln für Allianzen, und der HELFE und KONTAKTIERE Befehl sind eingeschränkt. Konkret kann kein HELP_FIGHT HELP_GUARD HELP_FSTEALTH HELP_MONEY gegeben werden (die anderen sind erlaubt). -- mit #defeine ALLIES_ONLY definiert Jede Allianz kann bis zu 15 Magier und 15 Alchemisten haben. Jede einzelne Partei der Allianz kann dabei nicht mehr als 15/Anzahl_Parteien (aufgerundet) Magier bzw. Alchemisten haben, und die Gesamtsumme darf 15 nicht übersteigen. -- mit #define ALLIANCE_LIMITS gemacht. Die Startgeschenke (Personen, Silber, ...) werden pro Allianz, nicht pro Spieler verteilt. Größere Allianzen bekommen also weniger pro Spieler. -- Nochmal geändert: Die Allianzen kriegen jede 168 Personen zum Start, weil sich das gut aufteilen lässt. Das wird auf 28 Einheiten pro Partei gesplittet, jede Einheit hat eines der Talente, außer der Starteinheit, die hat den magicskillboost. Einige Skills kommen öfter vor als andere, das ist nicht einfach vermeidbar. Sollte aber auch wurscht sein, es geht primär darum, lehren zu können. Es gibt ein Einheitenlimit von 1000 Einheiten pro Allianz. -- die Regel sparen wir uns einfach mal.
2003-12-14 22:45:47 +01:00
if (u->faction == f) {
if (eff_skill(u, SK_OBSERVATION, r) > w) {
w = eff_skill(u, SK_OBSERVATION, r);
}
2001-01-25 10:37:55 +01:00
}
}
return w;
}
static void
2004-06-21 18:45:27 +02:00
steal_cmd(unit * u, struct order * ord, request ** stealorders)
2001-01-25 10:37:55 +01:00
{
int n, i, id;
boolean goblin = false;
request * o;
unit * u2 = NULL;
2004-06-21 18:45:27 +02:00
region * r = u->region;
2001-01-25 10:37:55 +01:00
faction * f = NULL;
if (r->planep && fval(r->planep, PFL_NOATTACK)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 270, MSG_INCOME);
2001-01-25 10:37:55 +01:00
return;
}
2004-06-21 18:45:27 +02:00
init_tokens(ord);
skip_token();
id = read_unitid(u->faction, r);
2001-01-25 10:37:55 +01:00
u2 = findunitr(r, id);
if (u2 && u2->region==u->region) {
f = u2->faction;
} else {
f = dfindhash(id);
}
for (u2=r->units;u2;u2=u2->next) {
if (u2->faction == f && cansee(u->faction, r, u2, 0)) break;
}
if (!u2) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 63, MSG_INCOME);
2001-01-25 10:37:55 +01:00
return;
}
if (u2->faction->age < IMMUN_GEGEN_ANGRIFF) {
add_message(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, ord, "newbie_immunity_error",
"turns", IMMUN_GEGEN_ANGRIFF));
return;
}
if (u->faction->alliance!=NULL && u->faction->alliance == u2->faction->alliance) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 47, MSG_INCOME);
2001-01-25 10:37:55 +01:00
return;
}
assert(u->region==u2->region);
if (!can_contact(r, u, u2)) {
2004-06-21 18:45:27 +02:00
add_message(&u->faction->msgs, msg_feedback(u, ord, "error60", ""));
2001-01-25 10:37:55 +01:00
return;
}
2001-01-25 10:37:55 +01:00
n = eff_skill(u, SK_STEALTH, r) - wahrnehmung(r, f);
if (n == 0) {
/* Wahrnehmung == Tarnung */
if (u->race != new_race[RC_GOBLIN] || eff_skill(u, SK_STEALTH, r) <= 3) {
2001-01-25 10:37:55 +01:00
add_message(&u->faction->msgs, new_message(u->faction,
"stealfail%u:unit%u:target", u, u2));
2001-01-25 10:37:55 +01:00
add_message(&u2->faction->msgs, new_message(u2->faction,
"stealdetect%u:unit", u2));
2001-01-25 10:37:55 +01:00
return;
} else {
add_message(&u2->faction->msgs, new_message(u2->faction,
"thiefdiscover%u:unit%u:target", u, u2));
2001-01-25 10:37:55 +01:00
add_message(&u->faction->msgs, new_message(u->faction,
"stealfatal%u:unit%u:target", u, u2));
2001-01-25 10:37:55 +01:00
n = 1;
goblin = true;
}
} else if (n < 0) {
/* Wahrnehmung > Tarnung */
if (u->race != new_race[RC_GOBLIN] || eff_skill(u, SK_STEALTH, r) <= 3) {
2001-01-25 10:37:55 +01:00
add_message(&u->faction->msgs, new_message(u->faction,
"stealfatal%u:unit%u:target", u, u2));
2001-01-25 10:37:55 +01:00
add_message(&u2->faction->msgs, new_message(u2->faction,
"thiefdiscover%u:unit%u:target", u, u2));
2001-01-25 10:37:55 +01:00
return;
} else { /* Goblin-Spezialdiebstahl, Meldung an Beklauten */
add_message(&u2->faction->msgs, new_message(u2->faction,
"thiefdiscover%u:unit%u:target", u, u2));
2001-01-25 10:37:55 +01:00
n = 1;
goblin = true;
}
}
n = max(0, n);
i = min(u->number, get_item(u,I_RING_OF_NIMBLEFINGER));
if (i > 0) {
n *= STEALINCOME * (u->number + i * 9);
} 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 */
2001-01-25 10:37:55 +01:00
o = (request *) calloc(1, sizeof(request));
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));
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
2002-09-02 22:36:12 +02:00
static void
2001-01-25 10:37:55 +01:00
expandentertainment(region * r)
{
unit *u;
int m = entertainmoney(r);
request *o;
for (o = &entertainers[0]; o != nextentertainer; ++o) {
double part = m / (double) entertaining;
u = o->unit;
if (entertaining <= m)
u->n = o->qty;
else
u->n = (int) (o->qty * part);
change_money(u, u->n);
rsetmoney(r, rmoney(r) - u->n);
m -= u->n;
entertaining -= o->qty;
2001-01-25 10:37:55 +01:00
/* Nur soviel PRODUCEEXP wie auch tats<74>chlich gemacht wurde */
produceexp(u, SK_ENTERTAINMENT, min(u->n, u->number));
2001-01-25 10:37:55 +01:00
add_income(u, IC_ENTERTAIN, o->qty, u->n);
}
}
2002-09-02 22:36:12 +02:00
void
2004-06-21 18:45:27 +02:00
entertain_cmd(unit * u, struct order * ord)
2001-01-25 10:37:55 +01:00
{
2004-06-21 18:45:27 +02:00
region * r = u->region;
int max_e;
request *o;
static int entertainbase = 0;
static int entertainperlevel = 0;
2004-05-08 12:32:46 +02:00
if (!entertainbase) {
const char * str = get_param(global.parameters, "entertain.base");
entertainbase = str?atoi(str):0;
}
if (!entertainperlevel) {
const char * str = get_param(global.parameters, "entertain.perlevel");
entertainperlevel = str?atoi(str):0;
}
if (fval(u, UFL_WERE)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 58, MSG_INCOME);
return;
}
if (!effskill(u, SK_ENTERTAINMENT)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 58, MSG_INCOME);
return;
}
if (besieged(u)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 60, MSG_INCOME);
return;
}
if (u->ship && is_guarded(r, u, GUARD_CREWS)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 69, MSG_INCOME);
return;
}
if (is_cursed(r->attribs, C_DEPRESSION, 0)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 28, MSG_INCOME);
return;
}
2001-01-25 10:37:55 +01:00
2004-05-08 12:32:46 +02:00
u->wants = u->number * (entertainbase + effskill(u, SK_ENTERTAINMENT)
* entertainperlevel);
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
init_tokens(ord);
skip_token();
max_e = geti();
if (max_e != 0) {
u->wants = min(u->wants,max_e);
}
o = nextentertainer++;
o->unit = u;
o->qty = u->wants;
entertaining += o->qty;
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
2002-09-02 22:36:12 +02:00
static void
2001-01-25 10:37:55 +01:00
expandwork(region * r)
{
int n, earnings;
/* n: verbleibende Einnahmen */
/* m: maximale Arbeiter */
int m = maxworkingpeasants(r);
int p_wage = wage(r,NULL,false);
int verdienst = 0;
request *o;
for (o = &workers[0]; o != nextworker; ++o) {
unit * u = o->unit;
int workers;
if (u->number == 0) continue;
if (m>=working) workers = u->number;
else {
workers = u->number * m / working;
if (rand() % working < (u->number * m) % working) workers++;
}
assert(workers>=0);
u->n = workers * wage(u->region, u, false);
m -= workers;
assert(m>=0);
change_money(u, u->n);
working -= o->unit->number;
add_income(u, IC_WORK, o->qty, u->n);
}
n = m * p_wage;
/* Der Rest wird von den Bauern verdient. n ist das uebriggebliebene
* Geld. */
2001-01-25 10:37:55 +01:00
#if AFFECT_SALARY
{
attrib * a = a_find(r->attribs, &at_salary);
if (a) verdienst = a->data.i;
}
#endif
2004-05-14 12:07:36 +02:00
2001-01-25 10:37:55 +01:00
earnings = min(n, rpeasants(r) * p_wage) + verdienst;
/* Mehr oder weniger durch Trank "Riesengrass" oder "Faulobstschnaps" */
if (verdienst) {
sprintf(buf, "%d Bauern verdienen ein Silber %s durch %s.",
abs(verdienst), (verdienst > 0) ? "mehr" : "weniger",
(verdienst > 0) ? "Riesengras" : "Faulobstschnaps");
2001-01-25 10:37:55 +01:00
}
rsetmoney(r, rmoney(r) + earnings);
}
2004-06-21 18:45:27 +02:00
static void
work(unit * u, order * ord)
2001-01-25 10:37:55 +01:00
{
2004-06-21 18:45:27 +02:00
region * r = u->region;
2001-01-25 10:37:55 +01:00
request *o;
int w;
2002-04-12 14:46:16 +02:00
if(fval(u, UFL_WERE)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 313, MSG_INCOME);
2002-04-12 14:46:16 +02:00
return;
}
2001-01-25 10:37:55 +01:00
if (besieged(u)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 60, MSG_INCOME);
2001-01-25 10:37:55 +01:00
return;
}
if (u->ship && is_guarded(r, u, GUARD_CREWS)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 69, MSG_INCOME);
2001-01-25 10:37:55 +01:00
return;
}
w = wage(r,u,false);
u->wants = u->number * w;
o = nextworker++;
o->unit = u;
o->qty = u->number * w;
working += u->number;
}
/* ------------------------------------------------------------- */
2002-09-02 22:36:12 +02:00
static void
2001-01-25 10:37:55 +01:00
expandtax(region * r, request * taxorders)
{
unit *u;
int i;
expandorders(r, taxorders);
if (!norders) return;
for (i = 0; i != norders && rmoney(r) > TAXFRACTION; i++) {
change_money(oa[i].unit, TAXFRACTION);
oa[i].unit->n += TAXFRACTION;
rsetmoney(r, rmoney(r) -TAXFRACTION);
}
free(oa);
for (u = r->units; u; u = u->next)
if (u->n >= 0)
add_income(u, IC_TAX, u->wants, u->n);
}
2002-09-02 22:36:12 +02:00
void
2004-06-21 18:45:27 +02:00
tax_cmd(unit * u, struct order * ord, request ** taxorders)
2001-01-25 10:37:55 +01:00
{
/* Steuern werden noch vor der Forschung eingetrieben */
2004-06-21 18:45:27 +02:00
region * r = u->region;
2001-01-25 10:37:55 +01:00
unit *u2;
int n;
request *o;
int max;
if (!humanoidrace(u->race) && u->faction != findfaction(MONSTER_FACTION)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 228, MSG_INCOME);
2001-01-25 10:37:55 +01:00
return;
}
2002-04-12 14:46:16 +02:00
if (fval(u, UFL_WERE)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 228, MSG_INCOME);
2002-04-12 14:46:16 +02:00
return;
}
2001-01-25 10:37:55 +01:00
if (besieged(u)) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 60, MSG_INCOME);
2001-01-25 10:37:55 +01:00
return;
}
n = armedmen(u);
if (!n) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 48, MSG_INCOME);
2001-01-25 10:37:55 +01:00
return;
}
2004-06-21 18:45:27 +02:00
init_tokens(ord);
skip_token();
max = geti();
if (max == 0) max = INT_MAX;
if (!playerrace(u->race)) {
2001-01-25 10:37:55 +01:00
u->wants = min(income(u), max);
} else {
u->wants = min(n * eff_skill(u, SK_TAXING, r) * 20, max);
}
u2 = is_guarded(r, u, GUARD_TAX);
if (u2) {
add_message(&u->faction->msgs,
2004-06-21 18:45:27 +02:00
msg_feedback(u, ord, "region_guarded", "guard", u2));
2001-01-25 10:37:55 +01:00
return;
}
/* die einnahmen werden in fraktionen von 10 silber eingeteilt: diese
* fraktionen werden dann bei eintreiben unter allen eintreibenden
* einheiten aufgeteilt. */
2001-01-25 10:37:55 +01:00
o = (request *) calloc(1, sizeof(request));
o->qty = u->wants / TAXFRACTION;
o->unit = u;
addlist(taxorders, o);
return;
}
/* ------------------------------------------------------------- */
2002-09-02 22:36:12 +02:00
void
2001-01-25 10:37:55 +01:00
produce(void)
{
2004-06-21 18:45:27 +02:00
region *r;
request *taxorders, *sellorders, *stealorders, *buyorders;
unit *u;
int todo;
/* das sind alles befehle, die 30 tage brauchen, und die in thisorder
* stehen! von allen 30-tage befehlen wird einfach der letzte verwendet
* (dosetdefaults).
*
* kaufen vor einnahmequellen. da man in einer region dasselbe produkt
* nicht kaufen und verkaufen kann, ist die reihenfolge wegen der
* produkte egal. nicht so wegen dem geld.
*
* lehren vor lernen. */
for (r = regions; r; r = r->next) {
boolean limited = true;
2004-06-21 18:45:27 +02:00
assert(rmoney(r) >= 0);
assert(rpeasants(r) >= 0);
buyorders = 0;
sellorders = 0;
nextworker = &workers[0];
working = 0;
nextentertainer = &entertainers[0];
entertaining = 0;
taxorders = 0;
stealorders = 0;
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
for (u = r->units; u; u = u->next) {
if (u->race == new_race[RC_SPELL] || fval(u, UFL_LONGACTION))
continue;
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
if (u->race == new_race[RC_INSECT] && r_insectstalled(r) &&
!is_cursed(u->attribs, C_KAELTESCHUTZ,0))
continue;
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
if (attacked(u) && u->thisorder==NULL) {
cmistake(u, u->thisorder, 52, MSG_PRODUCE);
continue;
}
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
if (!TradeDisabled()) {
order * ord;
for (ord = u->orders;ord;ord=ord->next) {
switch (get_keyword(ord)) {
case K_BUY:
buy(u, &buyorders, ord);
break;
case K_SELL:
/* sell returns true if the sale is not limited
* by the region limit */
limited &= !sell(u, &sellorders, ord);
2004-06-21 18:45:27 +02:00
break;
}
}
if (fval(u, UFL_TRADER)) {
attrib * a = a_find(u->attribs, &at_trades);
if (a && a->data.i) {
produceexp(u, SK_TRADE, u->number);
}
set_order(&u->thisorder, NULL);
continue;
}
}
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
todo = get_keyword(u->thisorder);
if (todo == NOKEYWORD) continue;
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
if (rterrain(r) == T_OCEAN && u->race != new_race[RC_AQUARIAN]
&& !(u->race->flags & RCF_SWIM)
&& todo != K_STEAL && todo != K_SPY && todo != K_SABOTAGE)
continue;
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
switch (todo) {
case K_MAKE:
make_cmd(u, u->thisorder);
break;
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
case K_ENTERTAIN:
entertain_cmd(u, u->thisorder);
break;
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
case K_WORK:
if (playerrace(u->race)) work(u, u->thisorder);
else if (playerrace(u->faction->race)) {
add_message(&u->faction->msgs,
msg_feedback(u, u->thisorder, "race_cantwork", "race", u->race));
}
break;
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
case K_TAX:
tax_cmd(u, u->thisorder, &taxorders);
break;
2004-06-21 18:45:27 +02:00
case K_STEAL:
steal_cmd(u, u->thisorder, &stealorders);
break;
2004-06-21 18:45:27 +02:00
case K_SPY:
spy_cmd(u, u->thisorder);
break;
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
case K_SABOTAGE:
sabotage_cmd(u, u->thisorder);
break;
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
case K_ZUECHTE:
breed_cmd(u, u->thisorder);
break;
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
case K_PFLANZE:
plant_cmd(u, u->thisorder);
break;
2004-06-21 18:45:27 +02:00
case K_RESEARCH:
research_cmd(u, u->thisorder);
break;
}
}
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
split_allocations(r);
/* Entertainment (expandentertainment) und Besteuerung (expandtax) vor den
* Befehlen, die den Bauern mehr Geld geben, damit man aus den Zahlen der
* letzten Runde berechnen kann, wieviel die Bauern f<EFBFBD>r Unterhaltung
* auszugeben bereit sind. */
if (entertaining) expandentertainment(r);
expandwork(r);
if (taxorders) expandtax(r, taxorders);
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
/* An erster Stelle Kaufen (expandbuying), die Bauern so Geld bekommen, um
* nachher zu beim Verkaufen (expandselling) den Spielern abkaufen zu
* k<EFBFBD>nnen. */
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
if (buyorders) expandbuying(r, buyorders);
if (sellorders) {
int limit = rpeasants(r) / TRADE_FRACTION;
if (rterrain(r) == T_DESERT && buildingtype_exists(r, bt_find("caravan")))
limit *= 2;
expandselling(r, sellorders, limited?limit:INT_MAX);
}
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
/* Die Spieler sollen alles Geld verdienen, bevor sie beklaut werden
* (expandstealing). */
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
if (stealorders) expandstealing(r, stealorders);
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
assert(rmoney(r) >= 0);
assert(rpeasants(r) >= 0);
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
}
2001-01-25 10:37:55 +01:00
}
2002-09-02 22:36:12 +02:00
void
init_economy(void)
{
add_allocator(make_allocator(item2resource(olditemtype[I_HORSE]), attrib_allocation));
add_allocator(make_allocator(item2resource(olditemtype[I_WOOD]), attrib_allocation));
add_allocator(make_allocator(item2resource(olditemtype[I_MALLORN]), attrib_allocation));
#if NEW_RESOURCEGROWTH
add_allocator(make_allocator(item2resource(olditemtype[I_STONE]), leveled_allocation));
add_allocator(make_allocator(item2resource(olditemtype[I_IRON]), leveled_allocation));
add_allocator(make_allocator(item2resource(olditemtype[I_LAEN]), leveled_allocation));
#if GROWING_TREES
add_allocator(make_allocator(&rt_seed, attrib_allocation));
add_allocator(make_allocator(&rt_mallornseed, attrib_allocation));
#endif
#else
add_allocator(make_allocator(item2resource(olditemtype[I_STONE]), attrib_allocation));
add_allocator(make_allocator(item2resource(olditemtype[I_IRON]), attrib_allocation));
add_allocator(make_allocator(item2resource(olditemtype[I_LAEN]), attrib_allocation));
#endif
}