server/src/common/gamecode/economy.c

3353 lines
83 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
#define RESERVE_DONATIONS 1 /* shall we reserve objects given to us by other factions? */
#define RESERVE_GIVE 1 /* reserve anything that's given from one unit to another? */
2001-01-25 10:37:55 +01:00
#include <config.h>
#include "eressea.h"
#include "economy.h"
#include "randenc.h"
2001-01-25 10:37:55 +01:00
#include <ship.h>
#include <unit.h>
#include <building.h>
#include <faction.h>
#include <plane.h>
#include <alchemy.h>
#include <item.h>
#include <magic.h>
#include <build.h>
#include <goodies.h>
#include <study.h>
#include <movement.h>
#include <race.h>
#include <spy.h>
#include <build.h>
#include <pool.h>
#include <region.h>
#include <resources.h>
#include <unit.h>
#include <skill.h>
#include <message.h>
#include <reports.h>
#include <karma.h>
2001-01-25 10:37:55 +01:00
2001-02-10 11:40:12 +01:00
/* gamecode includes */
#include "laws.h"
2001-01-25 10:37:55 +01:00
/* util includes */
#include <attrib.h>
#include <base36.h>
2001-01-25 10:37:55 +01:00
#include <event.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;
/* ------------------------------------------------------------- */
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;
static int
giverestriction(void) {
static int value = -1;
if (value<0) {
value = atoi(get_param(global.parameters, "GiveRestriction"));
}
return value;
}
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)
{
int i, j;
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;
}
if (norders > 0) {
oa = (request *) calloc(norders, sizeof(request));
i = 0;
for (o = requests; o; o = o->next) {
if (o->qty > 0) {
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;
2001-01-25 10:37:55 +01:00
}
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
2001-01-25 10:37:55 +01:00
recruit(region * r, unit * u, strlist * S,
request ** recruitorders)
2001-01-25 10:37:55 +01:00
{
int n;
plane * pl;
request *o;
int recruitcost;
#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)) {
cmistake(u, S->s, 70, MSG_EVENT);
return;
}
#endif
if (u->faction->race == 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
{
cmistake(u, S->s, 98, MSG_EVENT);
return;
}
2001-01-25 10:37:55 +01:00
}
/* in Gletschern, Eisbergen gar nicht rekrutieren */
if (r_insectstalled(r)) {
2001-01-25 10:37:55 +01:00
cmistake(u, S->s, 97, MSG_EVENT);
return;
}
}
if (is_cursed(r->attribs, C_RIOT, 0)) {
/* Die Region befindet sich in Aufruhr */
cmistake(u, S->s, 237, MSG_EVENT);
return;
}
#if RACE_ADJUSTMENTS
if (fval(r, RF_ORCIFIED) && u->faction->race != new_race[RC_URUK] &&
#else
if (fval(r, RF_ORCIFIED) && u->faction->race != new_race[RC_ORC] &&
#endif
!(u->faction->race->ec_flags & ECF_REC_HORSES)) {
cmistake(u, S->s, 238, MSG_EVENT);
return;
}
2001-01-25 10:37:55 +01:00
recruitcost = u->faction->race->recruitcost;
if (recruitcost) {
pl = getplane(r);
if (pl && fval(pl, PFL_NORECRUITS)) {
add_message(&u->faction->msgs,
msg_error(u, S->s, "error_pflnorecruit", ""));
return;
}
2001-01-25 10:37:55 +01:00
if (get_pooled(u, r, R_SILVER) < recruitcost) {
cmistake(u, S->s, 142, MSG_EVENT);
return;
}
}
if (!playerrace(u->race) || idle(u->faction)) {
cmistake(u, S->s, 139, MSG_EVENT);
return;
}
/* snotlinge sollten hiermit bereits abgefangen werden, die
* parteirasse ist uruk oder ork*/
if (u->race != u->faction->race) {
if (u->number != 0) {
cmistake(u, S->s, 139, MSG_EVENT);
return;
}
else u->race = u->faction->race;
}
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, S->s, 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, S->s, 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, S->s, 142, MSG_EVENT);
return;
}
o = (request *) calloc(1, sizeof(request));
o->qty = n;
o->unit = u;
addlist(recruitorders, o);
return;
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
static int
2001-01-25 10:37:55 +01:00
count_max_migrants(faction * f)
{
int x = (int)(log10(count_all(f) / 50.0) * 20);
return max(0, x);
}
extern const char* resname(resource_t res, int i);
static void
2001-01-25 10:37:55 +01:00
add_give(unit * u, unit * u2, int n, const resource_type * rtype, const char * cmd, int error)
{
if (error)
cmistake(u, cmd, error, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
else if (!u2 || u2->faction!=u->faction) {
assert(rtype);
add_message(&u->faction->msgs,
msg_message("give", "unit target resource amount",
u, u2?ucansee(u->faction, u2, u_unknown()):u_peasants(), rtype, n));
if (u2) {
add_message(&u2->faction->msgs,
msg_message("give", "unit target resource amount",
ucansee(u2->faction, u, u_unknown()), u2, rtype, n));
}
2001-01-25 10:37:55 +01:00
}
}
static void
2001-01-25 10:37:55 +01:00
addgive(unit * u, unit * u2, int n, resource_t res, const char * cmd, int error)
{
add_give(u, u2, n, oldresourcetype[res], cmd, error);
}
/***************/
/* Derzeit fallen nur Silber und Pferde an die Region
* */
static void
give_peasants(int n, const item_type * itype, unit * src)
{
region *r = src->region;
/* horses are given to the region via itype->give! */
if (itype->rtype==r_silver) {
rsetmoney(r, rmoney(r) + n);
}
}
static int
2001-01-25 10:37:55 +01:00
give_item(int want, const item_type * itype, unit * src, unit * dest, const char * cmd)
{
short error = 0;
int n;
2001-01-25 10:37:55 +01:00
assert(itype!=NULL);
n = new_get_pooled(src, item2resource(itype), GET_DEFAULT);
n = min(want, n);
if (dest && src->faction != dest->faction && src->faction->age < giverestriction()) {
ADDMSG(&src->faction->msgs, msg_error(src, cmd, "giverestriction",
"turns", giverestriction()));
return -1;
} else if (n == 0) {
2001-01-25 10:37:55 +01:00
error = 36;
} else if (itype->flags & ITF_CURSED) {
error = 25;
} else if (itype->give && !itype->give(src, dest, itype, n, cmd)) {
return -1;
2001-01-25 10:37:55 +01:00
} else {
int use = new_use_pooled(src, item2resource(itype), GET_SLACK, n);
if (use<n) use += new_use_pooled(src, item2resource(itype), GET_RESERVE|GET_POOLED_SLACK, n-use);
2001-01-25 10:37:55 +01:00
if (dest) {
i_change(&dest->items, itype, n);
#if RESERVE_DONATIONS
2001-01-25 10:37:55 +01:00
new_change_resvalue(dest, item2resource(itype), n);
#elif RESERVE_GIVE
if (src->faction==dest->faction) {
new_change_resvalue(dest, item2resource(itype), n);
}
#endif
2001-01-25 10:37:55 +01:00
handle_event(&src->attribs, "give", dest);
handle_event(&dest->attribs, "receive", src);
2002-09-02 22:36:12 +02:00
#if defined(MUSEUM_MODULE) && defined(TODO)
TODO: Einen Trigger benutzen!
if (a_find(dest->attribs, &at_warden)) {
/* warden_add_give(src, dest, itype, n); */
}
2001-01-25 10:37:55 +01:00
#endif
}else{
/* gib bauern */
give_peasants(use, itype, src);
2001-01-25 10:37:55 +01:00
}
}
add_give(src, dest, n, item2resource(itype), cmd, error);
if (error) return -1;
return 0;
2001-01-25 10:37:55 +01:00
}
static void
givemen(int n, unit * u, unit * u2, const char * cmd)
2001-01-25 10:37:55 +01:00
{
ship *sh;
int k = 0;
int error = 0;
if (u2 && u->faction != u2->faction && u->faction->age < giverestriction()) {
ADDMSG(&u->faction->msgs, msg_error(u, cmd, "giverestriction",
"turns", giverestriction()));
return;
} else if (u == u2) {
error = 10;
#if RACE_ADJUSTMENTS
} else if (!u2 && u->race == new_race[RC_SNOTLING]) {
/* Snotlings k<>nnen nicht an Bauern <20>bergeben werden. */
error = 307;
#endif
} else if ((u && unit_has_cursed_item(u)) || (u2 && unit_has_cursed_item(u2))) {
error = 78;
} else if (fval(u, UFL_LOCKED) || fval(u, UFL_HUNGER) || is_cursed(u->attribs, C_SLAVE, 0)) {
error = 74;
} else if (u2 && (fval(u2, UFL_LOCKED)|| is_cursed(u2->attribs, C_SLAVE, 0))) {
error = 75;
} else if (u2 && u2->faction != u->faction && ucontact(u2, u) == 0) {
error = 73;
} else if (u2 && (has_skill(u, SK_MAGIC) || has_skill(u2, SK_MAGIC))) {
error = 158;
} else if (u2 && fval(u, UFL_WERE) != fval(u2, UFL_WERE)) {
error = 312;
} else if (u2 && u2->number!=0 && u2->race != u->race) {
log_warning(("Partei %s versucht %s an %s zu <20>bergeben.\n",
itoa36(u->faction->no), u->race->_name[0],
u2->race->_name[1]));
error = 139;
} else {
if (n > u->number) n = u->number;
if (n == 0) {
error = 96;
} else if (u2 && u->faction != u2->faction) {
if (u2->faction->newbies + n > MAXNEWBIES) {
error = 129;
} else if (u->race != u2->faction->race) {
if (u2->faction->race != new_race[RC_HUMAN]) {
error = 120;
} else if (count_migrants(u2->faction) + n > count_max_migrants(u2->faction)) {
error = 128;
}
else if (teure_talente(u) || teure_talente(u2)) {
error = 154;
} else if (u2->number!=0) {
error = 139;
}
}
}
}
2001-01-25 10:37:55 +01:00
if (u2 && (has_skill(u, SK_ALCHEMY) || has_skill(u2, SK_ALCHEMY))) {
k = count_skill(u2->faction, SK_ALCHEMY);
2001-01-25 10:37:55 +01:00
/* Falls die Zieleinheit keine Alchemisten sind, werden sie nun
* welche. */
if (!has_skill(u2, SK_ALCHEMY) && has_skill(u, SK_ALCHEMY))
k += u2->number;
2001-01-25 10:37:55 +01:00
/* Wenn in eine Alchemisteneinheit Personen verschoben werden */
if (has_skill(u2, SK_ALCHEMY) && !has_skill(u, SK_ALCHEMY))
k += n;
2001-01-25 10:37:55 +01:00
/* Wenn Parteigrenzen <20>berschritten werden */
if (u2->faction != u->faction)
k += n;
2001-01-25 10:37:55 +01:00
/* wird das Alchemistenmaximum ueberschritten ? */
2001-01-25 10:37:55 +01:00
if (k > max_skill(u2->faction, SK_ALCHEMY)) {
error = 156;
}
}
2001-01-25 10:37:55 +01:00
if (error==0) {
if (u2 && u2->number == 0) {
set_racename(&u2->attribs, get_racename(u->attribs));
u2->race = u->race;
u2->irace = u->irace;
2004-02-01 19:19:49 +01:00
}
if (u2) {
/* Einheiten von Schiffen k<>nnen nicht NACH in von
* Nicht-alliierten bewachten Regionen ausf<EFBFBD>hren */
sh = leftship(u);
if (sh) set_leftship(u2, sh);
transfermen(u, u2, n);
if (u->faction != u2->faction) {
u2->faction->newbies += n;
}
} else {
if (getunitpeasants) {
#ifdef ORCIFICATION
if (u->race == new_race[RC_ORC] && !fval(u->region, RF_ORCIFIED)) {
attrib *a = a_find(u->region->attribs, &at_orcification);
if (!a) a = a_add(&u->region->attribs, a_new(&at_orcification));
a->data.i += n;
}
#endif
transfermen(u, NULL, n);
} else {
error = 159;
}
}
}
addgive(u, u2, n, R_PERSON, cmd, error);
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
giveunit(region * r, unit * u, unit * u2, strlist * S)
{
int n = u->number;
if ((u && unit_has_cursed_item(u)) || (u2 && unit_has_cursed_item(u2))) {
cmistake(u, S->s, 78, MSG_COMMERCE);
return;
}
if (fval(u, UFL_LOCKED) || fval(u, UFL_HUNGER)) {
2001-01-25 10:37:55 +01:00
cmistake(u, S->s, 74, MSG_COMMERCE);
return;
}
if (u2 == NULL) {
if (rterrain(r) == T_OCEAN) {
cmistake(u, S->s, 152, MSG_COMMERCE);
} else if (getunitpeasants) {
unit *u3;
for(u3 = r->units; u3; u3 = u3->next)
if(u3->faction == u->faction && u != u3) break;
if(u3) {
while (u->items) {
item * iold = i_remove(&u->items, u->items);
item * inew = *i_find(&u3->items, iold->type);
if (inew==NULL) i_add(&u3->items, iold);
2001-01-25 10:37:55 +01:00
else {
inew->number += iold->number;
i_free(iold);
}
}
}
givemen(u->number, u, NULL, S->s);
2001-01-25 10:37:55 +01:00
cmistake(u, S->s, 153, MSG_COMMERCE);
} else {
cmistake(u, S->s, 63, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
}
return;
}
if (ucontact(u2, u) == 0) {
cmistake(u, S->s, 73, MSG_COMMERCE);
return;
}
if (u->number == 0) {
cmistake(u, S->s, 105, MSG_COMMERCE);
return;
}
if (u2->faction->newbies + n > MAXNEWBIES) {
cmistake(u, S->s, 129, MSG_COMMERCE);
return;
}
if (u->race != u2->faction->race) {
if (u2->faction->race != new_race[RC_HUMAN]) {
2001-01-25 10:37:55 +01:00
cmistake(u, S->s, 120, MSG_COMMERCE);
return;
}
if (count_migrants(u2->faction) + u->number > count_max_migrants(u2->faction)) {
cmistake(u, S->s, 128, MSG_COMMERCE);
return;
}
if (teure_talente(u)) {
2001-01-25 10:37:55 +01:00
cmistake(u, S->s, 154, MSG_COMMERCE);
return;
}
}
2002-03-03 19:13:08 +01:00
if (has_skill(u, SK_MAGIC)) {
2001-01-25 10:37:55 +01:00
if (count_skill(u2->faction, SK_MAGIC) + u->number >
max_skill(u2->faction, SK_MAGIC))
2001-01-25 10:37:55 +01:00
{
cmistake(u, S->s, 155, MSG_COMMERCE);
return;
}
if (u2->faction->magiegebiet != find_magetype(u)) {
cmistake(u, S->s, 157, MSG_COMMERCE);
return;
}
}
2002-03-03 19:13:08 +01:00
if (has_skill(u, SK_ALCHEMY)
&& count_skill(u2->faction, SK_ALCHEMY) + u->number >
max_skill(u2->faction, SK_ALCHEMY))
2001-01-25 10:37:55 +01:00
{
cmistake(u, S->s, 156, MSG_COMMERCE);
return;
}
add_give(u, u2, 1, r_unit, S->s, 0);
2001-02-10 15:18:01 +01:00
u_setfaction(u, u2->faction);
2001-01-25 10:37:55 +01:00
u2->faction->newbies += n;
/* "Ich gebe einer Partei eine Einheit, die den Befehl hat, effektiv
* alles Silber der Partei an mich zu <EFBFBD>bergeben. Zack, bin ich reich."
* Bl<EFBFBD>dsinn, da Silberpool nicht auf geben wirkt. Aber Rekrutierungen,
* teure Talente lernen und Gegenst<EFBFBD>nde erschaffen! Kritisch sind:
* BIETE, FORSCHEN, KAUFE, LERNE, REKRUTIERE, ZAUBERE,
* HELFE, PASSWORT, STIRB (Katja)
* Nat<EFBFBD>rlich wirkt der Silberpool bei gib!
* Und damit nicht irgendein zuk<EFBFBD>nftiger Befehl vergessen wird,
* l<EFBFBD>schen wir der Einfachheit halber alle! (Enno).
*/
2001-01-25 10:37:55 +01:00
/* freelist(u->orders);
* FEHLER! Das darf nicht, weil es auf orders noch pointer geben kann!
*/
while (u->orders) {
/* dieser code _kennt_ die implementaion von strlists... */
strlist * o = u->orders;
u->orders = o->next;
gc_add(o->s); /* delete it later */
o->s = NULL;
}
}
2001-01-25 10:37:55 +01:00
static void
dogive(region * r, unit * u, strlist * S, int mode)
/*
* mode=0: give to any units
* mode=1: give to other units and peasants only
* mode=2: give to own units only
*/
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
2001-04-26 19:41:06 +02:00
u2 = getunit(r, u->faction);
2001-01-25 10:37:55 +01:00
switch (mode) {
case 0:
/* any destination is okay */
break;
case 1:
/* not to myself */
if (u2 && u2->faction==u->faction) return;
break;
default:
assert(!"invalid mode for dogive");
}
2001-01-25 10:37:55 +01:00
if (!u2 && !getunitpeasants) {
cmistake(u, S->s, notfound_error, MSG_COMMERCE);
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))) {
2001-01-25 10:37:55 +01:00
cmistake(u, S->s, notfound_error, MSG_COMMERCE);
return;
}
if (u == u2) {
2001-01-25 10:37:55 +01:00
cmistake(u, S->s, 8, MSG_COMMERCE);
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))) {
2001-01-25 10:37:55 +01:00
cmistake(u, S->s, 268, MSG_COMMERCE);
return;
}
s = getstrtoken();
if (findparam(s, u->faction->locale) == P_CONTROL) {
2001-01-25 10:37:55 +01:00
if (!u2) {
cmistake(u, S->s, notfound_error, MSG_EVENT);
return;
}
if (!u->building && !u->ship) {
cmistake(u, S->s, 140, MSG_EVENT);
return;
}
if (u->building && u2->building != u->building) {
cmistake(u, S->s, 33, MSG_EVENT);
return;
}
if (u->ship && u2->ship != u->ship) {
cmistake(u, S->s, 32, MSG_EVENT);
return;
}
if (!fval(u, UFL_OWNER)) {
2001-01-25 10:37:55 +01:00
cmistake(u, S->s, 49, MSG_EVENT);
return;
}
if (!ucontact(u2, u)) {
cmistake(u, S->s, 40, MSG_EVENT);
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]) {
2001-01-25 10:37:55 +01:00
cmistake(u, S->s, notfound_error, MSG_COMMERCE);
return;
}
/* if ((u->race->ec_flags & NOGIVE) || fval(u,UFL_LOCKED)) {*/
if (u->race->ec_flags & NOGIVE && u2!=NULL) {
ADDMSG(&u->faction->msgs,
msg_error(u, S->s, "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,
msg_error(u, S->s, "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,
msg_error(u, S->s, "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,
msg_error(u, S->s, "race_notake", "race", u2->race));
return;
}
2001-01-25 10:37:55 +01:00
if (!u2) {
if (!getunitpeasants) {
cmistake(u, S->s, notfound_error, MSG_COMMERCE);
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 */
if (give_item((*itmp)->number, (*itmp)->type, u, u2, S->s)==0) {
given = true;
continue;
}
}
itmp = &(*itmp)->next;
2001-01-25 10:37:55 +01:00
}
}
2002-10-27 09:51:37 +01:00
if (!given) cmistake(u, S->s, 38, MSG_COMMERCE);
2001-01-25 10:37:55 +01:00
return;
}
if (findparam(s, u->faction->locale) == P_ZAUBER) {
2001-01-25 10:37:55 +01:00
cmistake(u, S->s, 7, MSG_COMMERCE);
/* geht nimmer */
return;
}
if (findparam(s, u->faction->locale) == P_UNIT) { /* Einheiten uebergeben */
if (!(u->race->ec_flags & GIVEUNIT)) {
2001-01-25 10:37:55 +01:00
cmistake(u, S->s, 167, MSG_COMMERCE);
return;
}
if (u2 && !ucontact(u2, u)) {
cmistake(u, S->s, 40, MSG_COMMERCE);
return;
}
giveunit(r, u, u2, S);
return;
}
if (findparam(s, u->faction->locale) == P_ANY) { /* Alle Gegenst<73>nde <20>bergeben */
const char * s = getstrtoken();
if(u2 && !ucontact(u2, u)) {
cmistake(u, S->s, 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,
msg_error(u, S->s, "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,
msg_error(u, S->s, "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);
if (give_item(n, (*itmp)->type, u, u2, S->s)==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,
msg_error(u, S->s, "race_noregroup", "race", u->race));
2001-01-25 10:37:55 +01:00
return;
}
n = u->number;
givemen(n, u, u2, S->s);
2001-01-25 10:37:55 +01:00
return;
}
if (!(u->race->ec_flags & GIVEITEM) && u2!=NULL) {
ADDMSG(&u->faction->msgs,
msg_error(u, S->s, "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,
msg_error(u, S->s, "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);
give_item(n, itype, u, u2, S->s);
return;
}
}
}
n = atoip(s); /* n: anzahl */
s = getstrtoken();
if (s == NULL) {
cmistake(u, S->s, 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))
{
cmistake(u, S->s, 40, MSG_COMMERCE);
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,
msg_error(u, S->s, "race_noregroup", "race", u->race));
2001-01-25 10:37:55 +01:00
return;
}
givemen(n, u, u2, S->s);
2001-01-25 10:37:55 +01:00
return;
}
if (u2!=NULL) {
if (!(u->race->ec_flags & GIVEITEM) && u2!=NULL) {
ADDMSG(&u->faction->msgs,
msg_error(u, S->s, "race_nogive", "race", u->race));
return;
}
if (!(u2->race->ec_flags & GETITEM)) {
ADDMSG(&u->faction->msgs,
msg_error(u, S->s, "race_notake", "race", u2->race));
return;
}
}
2001-01-25 10:37:55 +01:00
itype = finditemtype(s, u->faction->locale);
if (itype!=NULL) {
give_item(n, itype, u, u2, S->s);
return;
}
cmistake(u, S->s, 123, MSG_COMMERCE);
}
/* ------------------------------------------------------------- */
static void
2001-01-25 10:37:55 +01:00
forgetskill(unit * u)
{
skill_t talent;
const char *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
}
}
/* ------------------------------------------------------------- */
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->type);
2001-01-25 10:37:55 +01:00
else need -= get_pooled(u, r, m->type);
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))) {
2001-01-25 10:37:55 +01:00
need -= new_get_pooled(ua, oldresourcetype[m->type], GET_SLACK|GET_RESERVE|GET_POOLED_SLACK|GET_POOLED_RESERVE|GET_POOLED_FORCE);
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 -= new_use_pooled(u, oldresourcetype[m->type], GET_SLACK|GET_RESERVE|GET_POOLED_SLACK|GET_POOLED_RESERVE|GET_POOLED_FORCE, cost);
2001-01-25 10:37:55 +01:00
else cost -= new_use_pooled(u, oldresourcetype[m->type], 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)) {
2001-01-25 10:37:55 +01:00
int give = use_all(ua, m->type, cost);
if (!give) continue;
cost -= give;
fset(ua->faction, FL_DH);
if (m->type==R_SILVER) add_spende(ua->faction, u->faction, give, r);
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;
/*
"$building($crashed) st<73>rzte ein.$if($road," Beim Einsturz wurde die halbe Stra<EFBFBD>e vernichtet.","")$if($victims,"$int($victims) $if($eq($victims,1),"ist","sind"),"") zu beklagen."
*/
2001-01-25 10:37:55 +01:00
/* Falls Karawanserei, Damm oder Tunnel einst<73>rzen, wird die schon
* gebaute Stra<EFBFBD>e zur H<EFBFBD>lfte vernichtet */
2001-01-25 10:37:55 +01:00
for (d=0;d!=MAXDIRECTIONS;++d) if (rroad(r, d) > 0 &&
(b->type == bt_find("caravan") ||
b->type == bt_find("dam") ||
b->type == bt_find("tunnel")))
2001-01-25 10:37:55 +01:00
{
rsetroad(r, d, rroad(r, d) / 2);
road = 1;
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;
}
}
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;
}
}
}
void
2001-01-25 10:37:55 +01:00
economics(void)
{
region *r;
unit *u;
strlist *S;
/* Geben vor Selbstmord (doquit)! Hier alle unmittelbaren Befehle.
* Rekrutieren vor allen Einnahmequellen. Bewachen JA vor Steuern
* eintreiben. */
2001-01-25 10:37:55 +01:00
for (r = regions; r; r = r->next) if (r->units) {
request *recruitorders = NULL;
for (u = r->units; u; u = u->next) {
for (S = u->orders; S; S = S->next) {
switch (igetkeyword(S->s, u->faction->locale)) {
2001-01-25 10:37:55 +01:00
case K_DESTROY:
destroy(r, u, S->s);
break;
2001-01-25 10:37:55 +01:00
case K_GIVE:
case K_LIEFERE:
dogive(r, u, S, 0);
break;
2001-01-25 10:37:55 +01:00
case K_FORGET:
forgetskill(u);
break;
2001-01-25 10:37:55 +01:00
}
}
}
/* RECRUIT orders */
for (u = r->units; u; u = u->next) {
for (S = u->orders; S; S = S->next) {
if (igetkeyword(S->s, u->faction->locale) == K_RECRUIT) {
2001-01-25 10:37:55 +01:00
recruit(r, u, S, &recruitorders);
break;
}
}
}
if (recruitorders) expandrecruit(r, recruitorders);
}
}
/* ------------------------------------------------------------- */
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;
cmistake(u, findorder(u, u->thisorder), err, MSG_PRODUCE);
return;
}
if(want==0)
want=maxbuild(u, itype->construction);
n = build(u, itype->construction, 0, want);
switch (n) {
case ENEEDSKILL:
add_message(&u->faction->msgs,
msg_error(u, findorder(u, u->thisorder), "skill_needed", "skill", sk));
2001-01-25 10:37:55 +01:00
return;
case ELOWSKILL:
add_message(&u->faction->msgs,
msg_error(u, findorder(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);
}
mistake(u, findorder(u, u->thisorder), buf, MSG_PRODUCE);
return;
}
2001-01-25 10:37:55 +01:00
}
if (n>0) {
i_change(&u->items, itype, n);
if (want==INT_MAX) want = n;
2002-01-20 12:35:06 +01:00
ADDMSG(&u->faction->msgs, msg_message("manufacture",
"unit region amount wanted resource", u, u->region, n, want, itype->rtype));
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)) {
cmistake(u, findorder(u, u->thisorder), 92, MSG_PRODUCE);
return;
}
if (itype == olditemtype[I_MALLORN] && !fval(r, RF_MALLORN)) {
cmistake(u, findorder(u, u->thisorder), 91, MSG_PRODUCE);
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")) {
2001-01-25 10:37:55 +01:00
cmistake(u, findorder(u, u->thisorder), 104, MSG_PRODUCE);
return;
}
}
if (besieged(u)) {
cmistake(u, findorder(u, u->thisorder), 60, MSG_PRODUCE);
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)
&& !ucontact(u2, u)
&& !besieged(u2)
&& !alliedunit(u2, u->faction, HELP_GUARD)
2001-01-25 10:37:55 +01:00
#ifdef WACH_WAFF
&& armedmen(u2)
2001-01-25 10:37:55 +01:00
#endif
) {
add_message(&u->faction->msgs,
msg_error(u, findorder(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
&& !ucontact(u2, u)
&& !alliedunit(u2, u->faction, HELP_GUARD))
2001-01-25 10:37:55 +01:00
{
add_message(&u->faction->msgs,
msg_error(u, findorder(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,
msg_error(u, findorder(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,
msg_error(u, findorder(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 */
cmistake(u, findorder(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,".");
mistake(u, findorder(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)) {
cmistake(u, findorder(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);
else cmistake(u, findorder(u, u->thisorder), 125, MSG_PRODUCE);
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
make(region * r, unit * u)
{
const building_type * btype;
const ship_type * stype;
param_t p;
int m;
const item_type * itype;
const char *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
itype = finditemtype(s, u->faction->locale);
if (itype!=NULL) {
create_item(u, itype, m);
return;
}
if (p == P_ROAD) {
2001-01-25 10:37:55 +01:00
direction_t d;
if(r->planep && fval(r->planep, PFL_NOBUILD)) {
cmistake(u, findorder(u, u->thisorder), 275, MSG_PRODUCE);
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)) {
cmistake(u, findorder(u, u->thisorder), 94, MSG_PRODUCE);
return;
}
build_road(r, u, m, d);
} else cmistake(u, findorder(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)) {
cmistake(u, findorder(u, u->thisorder), 276, MSG_PRODUCE);
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
}
stype = findshiptype(s, u->faction->locale);
if (stype != NOSHIP) {
if(r->planep && fval(r->planep, PFL_NOBUILD)) {
cmistake(u, findorder(u, u->thisorder), 276, MSG_PRODUCE);
return;
}
create_ship(r, u, stype, m);
return;
}
btype = findbuildingtype(s, u->faction->locale);
if (btype != NOBUILDING) {
if(r->planep && fval(r->planep, PFL_NOBUILD)) {
cmistake(u, findorder(u, u->thisorder), 94, MSG_PRODUCE);
return;
}
build_building(u, btype, m);
return;
}
cmistake(u, findorder(u, u->thisorder), 125, MSG_PRODUCE);
}
/* ------------------------------------------------------------- */
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. */
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);
}
}
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
2001-01-25 10:37:55 +01:00
buy(region * r, unit * u, request ** buyorders, const char * cmd)
{
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)) {
cmistake(u, cmd, 69, MSG_INCOME);
return;
}
if (u->ship && is_guarded(r, u, GUARD_CREWS)) {
cmistake(u, cmd, 69, MSG_INCOME);
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
n = geti();
if (!n) {
cmistake(u, cmd, 26, MSG_COMMERCE);
return;
}
if (besieged(u)) {
/* Belagerte Einheiten k<>nnen nichts kaufen. */
cmistake(u, cmd, 60, MSG_COMMERCE);
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)) {
cmistake(u, cmd, 119, MSG_COMMERCE);
return;
}
} else {
/* ...oder in der Region mu<6D> es eine Burg geben. */
if (!rbuildings(r)) {
cmistake(u, cmd, 119, MSG_COMMERCE);
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) {
cmistake(u, cmd, 102, MSG_COMMERCE);
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) {
cmistake(u, cmd, 124, MSG_COMMERCE);
return;
}
}
if (r_demand(r, ltype)) {
add_message(&u->faction->msgs,
msg_error(u, cmd, "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
2001-01-25 10:37:55 +01:00
expandselling(region * r, request * sellorders)
{
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]+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) {
add_message(&u->faction->msgs, new_message(u->faction,
"sellamount%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);
add_income(u, IC_TRADE, u->n, u->n);
}
}
static void
2001-01-25 10:37:55 +01:00
sell(region * r, unit * u, request ** sellorders, const char * cmd)
{
const item_type * itype;
const luxury_type * ltype=NULL;
int n;
request *o;
const char *s;
2001-01-25 10:37:55 +01:00
if (u->ship && is_guarded(r, u, GUARD_CREWS)) {
cmistake(u, cmd, 69, MSG_INCOME);
return;
}
/* 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
s = getstrtoken();
if (findparam(s, u->faction->locale) == P_ANY) {
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) {
cmistake(u, cmd, 303, MSG_COMMERCE);
return;
}
} else {
2001-01-25 10:37:55 +01:00
n = atoi(s);
if (n==0) {
cmistake(u, cmd, 27, MSG_COMMERCE);
return;
}
2001-01-25 10:37:55 +01:00
}
/* Belagerte Einheiten k<>nnen nichts verkaufen. */
if (besieged(u)) {
add_message(&u->faction->msgs,
msg_error(u, cmd, "error60", ""));
2001-01-25 10:37:55 +01:00
return;
}
/* 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)) {
cmistake(u, cmd, 119, MSG_COMMERCE);
return;
}
} else {
if (!rbuildings(r)) {
cmistake(u, cmd, 119, MSG_COMMERCE);
return;
}
}
/* 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) {
cmistake(u, cmd, 54, MSG_COMMERCE);
return;
}
s=getstrtoken();
itype = finditemtype(s, u->faction->locale);
if (itype!=NULL) ltype = resource2luxury(itype->rtype);
if (ltype==NULL) {
cmistake(u, cmd, 126, MSG_COMMERCE);
return;
}
else {
attrib * a;
int k, available;
if (!r_demand(r, ltype)) {
cmistake(u, cmd, 263, MSG_COMMERCE);
return;
}
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) {
cmistake(u, cmd, 264, MSG_COMMERCE);
return;
}
/* 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);
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);
#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);
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) {
cmistake(u, findorder(u, u->thisorder), 108, MSG_PRODUCE);
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,
msg_error(u, findorder(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,
msg_error(u, findorder(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,
msg_error(u, findorder(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,
msg_error(u, findorder(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,
msg_error(u, findorder(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,
msg_error(u, findorder(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,
msg_error(u, findorder(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
pflanze(region *r, unit *u)
{
int m;
const char *s;
param_t p;
const item_type * itype = NULL;
/* pflanze [<anzahl>] <parameter> */
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")) {
cmistake(u, findorder(u, u->thisorder), 122, MSG_PRODUCE);
return;
2001-01-25 10:37:55 +01:00
}
if (get_item(u, I_HORSE) < 2) {
cmistake(u, findorder(u, u->thisorder), 107, MSG_PRODUCE);
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
zuechte(region *r, unit *u)
{
int m;
const char *s;
param_t p;
/* z<>chte [<anzahl>] <parameter> */
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);
}
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
2001-01-25 10:37:55 +01:00
research(region *r, unit *u)
{
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, findorder(u, u->thisorder), 227, MSG_EVENT);
2001-01-25 10:37:55 +01:00
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));
2001-01-25 10:37:55 +01:00
} 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
}
} 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
2001-01-25 10:37:55 +01:00
steal(region * r, unit * u, request ** stealorders)
{
int n, i, id;
boolean goblin = false;
request * o;
unit * u2 = NULL;
faction * f = NULL;
if (r->planep && fval(r->planep, PFL_NOATTACK)) {
cmistake(u, findorder(u, u->thisorder), 270, MSG_INCOME);
return;
}
id = read_unitid(u->faction, r);
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) {
cmistake(u, findorder(u, u->thisorder), 63, MSG_INCOME);
2001-01-25 10:37:55 +01:00
return;
}
if (u2->faction->age < IMMUN_GEGEN_ANGRIFF) {
add_message(&u->faction->msgs,
msg_error(u, findorder(u, u->thisorder), "newbie_immunity_error",
"turns", IMMUN_GEGEN_ANGRIFF));
return;
}
#ifdef ALLIANCES
if(u->faction->alliance == u2->faction->alliance) {
cmistake(u, findorder(u, u->thisorder), 47, MSG_INCOME);
2001-01-25 10:37:55 +01:00
return;
}
#endif
2001-01-25 10:37:55 +01:00
assert(u->region==u2->region);
if (!can_contact(r, u, u2)) {
add_message(&u->faction->msgs, msg_error(u, findorder(u, u->thisorder),
"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
int
entertainmoney(const region *r)
2001-01-25 10:37:55 +01:00
{
int n;
if (is_cursed(r->attribs, C_DEPRESSION, 0)) {
return 0;
}
n = rmoney(r) / ENTERTAINFRACTION;
if (is_cursed(r->attribs, C_GENEROUS, 0)) {
n *= get_curseeffect(r->attribs, C_GENEROUS, 0);
}
return n;
}
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 -= u->n;
/* 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
2001-01-25 10:37:55 +01:00
entertain(region * r, unit * u)
{
int max_e;
request *o;
static int entertainbase = 0;
static int entertainperlevel = 0;
if (!entertainbase) entertainbase = atoi(get_param(global.parameters, "entertain.base"));
if (!entertainperlevel) entertainperlevel = atoi(get_param(global.parameters, "entertain.perlevel"));
2001-01-25 10:37:55 +01:00
2002-04-12 14:46:16 +02:00
if (fval(u, UFL_WERE)) {
cmistake(u, findorder(u, u->thisorder), 58, MSG_INCOME);
return;
}
2001-01-25 10:37:55 +01:00
if (!effskill(u, SK_ENTERTAINMENT)) {
cmistake(u, findorder(u, u->thisorder), 58, MSG_INCOME);
return;
}
if (besieged(u)) {
cmistake(u, findorder(u, u->thisorder), 60, MSG_INCOME);
return;
}
if (u->ship && is_guarded(r, u, GUARD_CREWS)) {
cmistake(u, findorder(u, u->thisorder), 69, MSG_INCOME);
return;
}
if (is_cursed(r->attribs, C_DEPRESSION, 0)) {
cmistake(u, findorder(u, u->thisorder), 28, MSG_INCOME);
return;
}
u->wants = u->number * (entertainbase + effskill(u, SK_ENTERTAINMENT) * entertainperlevel);
2001-01-25 10:37:55 +01:00
if ((max_e = geti()) != 0)
u->wants = min(u->wants,max_e);
o = nextentertainer++;
o->unit = u;
o->qty = u->wants;
entertaining += o->qty;
}
/* ------------------------------------------------------------- */
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
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);
}
2002-09-02 22:36:12 +02:00
void
2001-01-25 10:37:55 +01:00
work(region * r, unit * u)
{
request *o;
int w;
2002-04-12 14:46:16 +02:00
if(fval(u, UFL_WERE)) {
cmistake(u, findorder(u, u->thisorder), 313, MSG_INCOME);
return;
}
2001-01-25 10:37:55 +01:00
if (besieged(u)) {
cmistake(u, findorder(u, u->thisorder), 60, MSG_INCOME);
return;
}
if (u->ship && is_guarded(r, u, GUARD_CREWS)) {
cmistake(u, findorder(u, u->thisorder), 69, MSG_INCOME);
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
2001-01-25 10:37:55 +01:00
tax(region * r, unit * u, request ** taxorders)
{
/* Steuern werden noch vor der Forschung eingetrieben */
unit *u2;
int n;
request *o;
int max;
if (!humanoidrace(u->race) && u->faction != findfaction(MONSTER_FACTION)) {
2001-01-25 10:37:55 +01:00
cmistake(u, findorder(u, u->thisorder), 228, MSG_INCOME);
return;
}
2002-04-12 14:46:16 +02:00
if (fval(u, UFL_WERE)) {
cmistake(u, findorder(u, u->thisorder), 228, MSG_INCOME);
return;
}
2001-01-25 10:37:55 +01:00
if (besieged(u)) {
cmistake(u, findorder(u, u->thisorder), 60, MSG_INCOME);
return;
}
n = armedmen(u);
if (!n) {
cmistake(u, findorder(u, u->thisorder), 48, MSG_INCOME);
return;
}
if ((max = geti()) == 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,
msg_error(u, findorder(u, u->thisorder), "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)
{
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. */
2001-01-25 10:37:55 +01:00
for (r = regions; r; r = r->next) {
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;
for (u = r->units; u; u = u->next) {
strlist * s;
if (u->race == new_race[RC_SPELL] || fval(u, UFL_LONGACTION))
2001-01-25 10:37:55 +01:00
continue;
if (u->race == new_race[RC_INSECT] && r_insectstalled(r) &&
!is_cursed(u->attribs, C_KAELTESCHUTZ,0))
2001-01-25 10:37:55 +01:00
continue;
if (attacked(u) && *(u->thisorder) != 0) {
cmistake(u, findorder(u, u->thisorder), 52, MSG_PRODUCE);
continue;
}
if (!TradeDisabled()) {
for (s=u->orders;s;s=s->next) {
todo = igetkeyword(s->s, u->faction->locale);
switch (todo) {
case K_BUY:
buy(r, u, &buyorders, s->s);
break;
case K_SELL:
sell(r, u, &sellorders, s->s);
break;
}
2001-01-25 10:37:55 +01:00
}
if (fval(u, UFL_TRADER)) {
attrib * a = a_find(u->attribs, &at_trades);
if (a && a->data.i) {
produceexp(u, SK_TRADE, u->number);
}
u->thisorder[0]=0;
continue;
2001-01-25 10:37:55 +01:00
}
}
todo = igetkeyword(u->thisorder, u->faction->locale);
2001-01-25 10:37:55 +01:00
if (todo == NOKEYWORD) continue;
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)
2001-01-25 10:37:55 +01:00
continue;
switch (todo) {
case K_MAKE:
make(r, u);
break;
2001-01-25 10:37:55 +01:00
case K_ENTERTAIN:
entertain(r, u);
break;
2001-01-25 10:37:55 +01:00
case K_WORK:
if (!!playerrace(u->race))
work(r, u);
else if (u->faction->no > 0) {
add_message(&u->faction->msgs,
msg_error(u, findorder(u, u->thisorder), "race_cantwork", "race", u->race));
}
break;
2001-01-25 10:37:55 +01:00
case K_TAX:
tax(r, u, &taxorders);
break;
case K_STEAL:
steal(r, u, &stealorders);
break;
case K_SPY:
spy(r, u);
break;
2001-01-25 10:37:55 +01:00
case K_SABOTAGE:
sabotage(r, u);
break;
2001-01-25 10:37:55 +01:00
case K_ZUECHTE:
zuechte(r, u);
break;
2001-01-25 10:37:55 +01:00
case K_PFLANZE:
pflanze(r, u);
break;
case K_RESEARCH:
research(r, u);
break;
2001-01-25 10:37:55 +01:00
}
}
split_allocations(r);
2001-01-25 10:37:55 +01:00
/* 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. */
2001-01-25 10:37:55 +01:00
if (entertaining) expandentertainment(r);
expandwork(r);
if (taxorders) expandtax(r, taxorders);
/* 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
if (buyorders) expandbuying(r, buyorders);
if (sellorders) expandselling(r, sellorders);
/* Die Spieler sollen alles Geld verdienen, bevor sie beklaut werden
* (expandstealing). */
2001-01-25 10:37:55 +01:00
if (stealorders) expandstealing(r, stealorders);
assert(rmoney(r) >= 0);
assert(rpeasants(r) >= 0);
}
}
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
}