server/src/common/gamecode/economy.c

3430 lines
84 KiB
C
Raw Normal View History

2001-01-25 10:37:55 +01:00
/* vi: set ts=2:
*
*
2001-01-25 10:37:55 +01:00
* Eressea PB(E)M host Copyright (C) 1998-2000
* 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.
*/
#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? */
#define TWOPASS_GIVE 0 /* give items in two passes: first to others, than to self */
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;
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 struct scramble {
int index;
int rnd;
} * vec;
int
scramblecmp(const void *p1, const void *p2)
{
return ((struct scramble *)p1)->rnd - ((struct scramble *)p2)->rnd;
}
void
scramble(void *data, int n, size_t width)
{
int i;
static int vecsize = 0;
void *buffer = NULL, *temp = NULL;
if (n > vecsize) {
vecsize = n;
vec = (struct scramble *) realloc(vec, vecsize * sizeof(struct scramble));
}
for (i = 0; i != n; i++) {
vec[i].rnd = rand();
vec[i].index = i;
}
qsort(vec, n, sizeof(struct scramble), scramblecmp);
for (i = 0; i != n; i++) {
/* in vec[k].index steht, wohin der block k soll */
/* src soll nach target geschoben werden. daf<61>r wird target gemerkt,
* src verschoben, und target zum neuen src. ende, wenn k wieder i ist */
if (vec[i].index!=i) {
char * src = ((char*)data)+width*i;
int k = i;
int dest = vec[k].index;
if (temp==NULL) {
temp = malloc(width);
}
buffer = temp;
do {
char * target = ((char*)data)+width*dest;
memcpy(buffer, target, width);
memcpy(target, src, width);
k = dest; /* wo das gerettete target hin soll */
dest = vec[dest].index;
vec[k].index = k; /* dest ist an der richtigen stelle */
/* swap buffer and src. misuse target as intermediate var. */
target = buffer;
buffer = src;
src = target;
} while (vec[i].index!=i);
}
}
if (buffer!=NULL) free(temp);
}
#if 0
#define MAX 6
int oi[MAX];
void
test_scramble(void)
{
int i;
for (i=0;i!=MAX;++i) {
oi[i] = i;
}
scramble(oi, MAX, sizeof(int));
for (i=0;i!=MAX;++i) {
int j;
for (j=0;j!=MAX;++j) {
if (oi[j]==i) break;
}
assert(j!=MAX);
}
}
#endif
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 */
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++;
}
}
}
scramble(oa, norders, sizeof(request));
} else {
oa = NULL;
}
#if 0
freelist(requests);
#else
while (requests) {
request * o = requests->next;
free(requests);
requests = o;
}
#endif
}
/* ------------------------------------------------------------- */
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
}
2002-02-16 18:13:50 +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
}
}
}
}
void
recruit(region * r, unit * u, strlist * S,
request ** recruitorders)
{
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, FL_WARMTH)) {
usepotion = true;
break;
}
if (!usepotion)
#endif
{
cmistake(u, S->s, 98, MSG_EVENT);
return;
}
}
/* 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)) {
2001-04-26 19:41:06 +02:00
add_message(&u->faction->msgs,
msg_error(u, S->s, "error_pflnorecruit", ""));
2001-04-26 19:41:06 +02:00
return;
}
2001-01-25 10:37:55 +01:00
if (get_pooled(u, r, R_SILVER) < recruitcost) {
2001-04-26 19:41:06 +02:00
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);
2001-01-25 10:37:55 +01:00
return;
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
2002-09-02 22:36:12 +02:00
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);
2002-09-02 22:36:12 +02:00
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
}
}
2002-09-02 22:36:12 +02:00
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
* */
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);
}
}
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;
assert(itype!=NULL);
n = new_get_pooled(src, item2resource(itype), GET_DEFAULT);
n = min(want, n);
2002-01-13 13:36:25 +01:00
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
}
2002-09-02 22:36:12 +02:00
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;
2002-01-13 13:36:25 +01:00
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) {
2001-01-25 10:37:55 +01:00
error = 10;
#if RACE_ADJUSTMENTS
} else if (!u2 && u->race == new_race[RC_SNOTLING]) {
2002-02-18 22:54:45 +01:00
/* Snotlings k<>nnen nicht an Bauern <20>bergeben werden. */
error = 307;
#endif
2001-01-25 10:37:55 +01:00
} else if ((u && unit_has_cursed_item(u)) || (u2 && unit_has_cursed_item(u2))) {
error = 78;
} else if (fval(u, FL_LOCKED) || fval(u, FL_HUNGER) || is_cursed(u->attribs, C_SLAVE, 0)) {
error = 74;
} else if (u2 && (fval(u2, FL_LOCKED)|| is_cursed(u2->attribs, C_SLAVE, 0))) {
error = 75;
} else if (u2 != (unit *) NULL
&& u2->faction != u->faction
&& ucontact(u2, u) == 0) {
2001-01-25 10:37:55 +01:00
error = 73;
2002-03-03 19:13:08 +01:00
} else if (u2 && (has_skill(u, SK_MAGIC) || has_skill(u2, SK_MAGIC))) {
2001-01-25 10:37:55 +01:00
error = 158;
2002-04-14 12:59:06 +02:00
} else if (u2 && fval(u, UFL_WERE) != fval(u2, UFL_WERE)) {
2002-04-12 14:46:16 +02:00
error = 312;
2001-01-25 10:37:55 +01:00
} 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;
2001-01-25 10:37:55 +01:00
} else if (count_migrants(u2->faction) + n > count_max_migrants(u2->faction)) {
error = 128;
}
else if (teure_talente(u) || teure_talente(u2)) {
2001-01-25 10:37:55 +01:00
error = 154;
} else if (u2->number!=0) {
error = 139;
}
}
}
}
2002-03-03 19:13:08 +01:00
if (u2 && (has_skill(u, SK_ALCHEMY) || has_skill(u2, SK_ALCHEMY))) {
2001-01-25 10:37:55 +01:00
k = count_skill(u2->faction, SK_ALCHEMY);
/* Falls die Zieleinheit keine Alchemisten sind, werden sie nun
* welche. */
2002-03-03 19:13:08 +01:00
if (!has_skill(u2, SK_ALCHEMY) && has_skill(u, SK_ALCHEMY))
2001-01-25 10:37:55 +01:00
k += u2->number;
/* Wenn in eine Alchemisteneinheit Personen verschoben werden */
2002-03-03 19:13:08 +01:00
if (has_skill(u2, SK_ALCHEMY) && !has_skill(u, SK_ALCHEMY))
2001-01-25 10:37:55 +01:00
k += n;
/* Wenn Parteigrenzen <20>berschritten werden */
if (u2->faction != u->faction)
k += n;
/* wird das Alchemistenmaximum ueberschritten ? */
if (k > max_skill(u2->faction, SK_ALCHEMY)) {
error = 156;
}
}
if (!error) {
if (u2 && u2->number == 0) {
set_racename(&u2->attribs, get_racename(u->attribs));
2001-01-25 10:37:55 +01:00
u2->race = u->race;
u2->irace = u->irace;
} else if (u2 && u2->race != u->race) {
error = 139;
}
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)) {
2001-01-25 10:37:55 +01:00
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, FL_LOCKED) || fval(u, FL_HUNGER)) {
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, boolean liefere, 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;
char *s;
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;
#if TWOPASS_GIVE
case 2:
/* not to peasants or others */
if (!u2 || u2->faction!=u->faction) return;
break;
#endif
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, FL_TAKEALL))) {
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;
}
/* FL_TAKEALL ist ein grober Hack. Generalisierung tut not, ist aber nicht
* wirklich einfach. */
if (r->planep && fval(r->planep, PFL_NOGIVE) && (!u2 || !fval(u2, FL_TAKEALL))) {
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, FL_OWNER)) {
cmistake(u, S->s, 49, MSG_EVENT);
return;
}
if (!ucontact(u2, u)) {
cmistake(u, S->s, 40, MSG_EVENT);
return;
}
freset(u, FL_OWNER);
fset(u2, FL_OWNER);
add_message(&u->faction->msgs,
msg_message("givecommand", "unit receipient", u, u2));
if (u->faction != u2->faction) {
add_message(&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,FL_LOCKED)) {*/
if (u->race->ec_flags & NOGIVE) {
add_message(&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)) {
add_message(&u->faction->msgs,
msg_error(u, S->s, "race_notake", "race", u2->race));
2001-01-25 10:37:55 +01:00
return;
}
*/
2001-01-25 10:37:55 +01:00
/* <20>bergabe aller Kr<4B>uter */
if (findparam(s, u->faction->locale) == P_HERBS) {
2002-10-27 09:51:37 +01:00
boolean given = false;
if (!(u->race->ec_flags & GIVEITEM)) {
add_message(&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 */
2002-10-27 10:06:56 +01:00
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 */
2001-01-25 10:37:55 +01:00
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)) {
add_message(&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)) {
add_message(&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)) {
add_message(&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)) {
add_message(&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 (!(u->race->ec_flags & GIVEITEM)) {
add_message(&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) {
give_item(n, itype, u, u2, S->s);
return;
}
cmistake(u, S->s, 123, MSG_COMMERCE);
}
/* ------------------------------------------------------------- */
2002-09-02 22:36:12 +02:00
void
2001-01-25 10:37:55 +01:00
forgetskill(unit * u)
{
skill_t talent;
char *s;
s = getstrtoken();
if ((talent = findskill(s, u->faction->locale)) != NOSKILL) {
struct message * m = add_message(&u->faction->msgs,
msg_message("forget", "unit skill", u, talent));
msg_release(m);
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;
if (b->type->maintenance==NULL) return true;
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 */
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);
2001-01-25 10:37:55 +01:00
freset(u, FL_OWNER);
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. */
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:
dogive(r, u, S, S->s[0]=='@'?true:false, TWOPASS_GIVE);
break;
2001-01-25 10:37:55 +01:00
case K_LIEFERE:
dogive(r, u, S, true, TWOPASS_GIVE);
break;
2001-01-25 10:37:55 +01:00
case K_FORGET:
forgetskill(u);
break;
2001-01-25 10:37:55 +01:00
}
}
}
#if TWOPASS_GIVE
for (u = r->units; u; u = u->next) {
for (S = u->orders; S; S = S->next) {
switch (igetkeyword(S->s, u->faction->locale)) {
case K_GIVE:
dogive(r, u, S, S->s[0]=='@'?true:false, 2);
break;
case K_LIEFERE:
dogive(r, u, S, true, 2);
break;
}
}
}
#endif
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
* materialverbruach 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. */
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)
2002-09-02 22:36:12 +02:00
&& !alliedunit(u2, u->faction, HELP_GUARD)
2001-01-25 10:37:55 +01:00
#ifdef WACH_WAFF
&& armedmen(u2)
#endif
) {
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;
}
}
}
/* 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)
2002-09-02 22:36:12 +02:00
&& !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;
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)
{
char *s;
const building_type * btype;
const ship_type * stype;
param_t p;
int m;
const item_type * itype;
s = getstrtoken();
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 */
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. */
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. */
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>. */
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. */
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
};
2002-09-02 22:36:12 +02:00
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. */
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 */
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 && !fval(u, FL_TRADER)) fset(u, FL_TRADER);
}
/* ------------------------------------------------------------- */
/* 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. */
for (b = rbuildings(r); b; b = b->next) {
if (b->size > maxsize && buildingowner(r, b) != NULL
&& b->type == bt_find("castle")) {
2001-01-25 10:37:55 +01:00
maxb = b;
maxsize = b->size;
maxowner = buildingowner(r, b);
} else if (b->size == maxsize && b->type == bt_find("castle")) {
2001-01-25 10:37:55 +01:00
maxb = (building *) NULL;
maxowner = (unit *) NULL;
}
}
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. */
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. */
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. */
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);
}
}
2002-09-02 22:36:12 +02:00
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;
char *s;
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. */
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. */
/* 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 */
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 && !fval(u, FL_TRADER)) fset(u, FL_TRADER);
}
}
/* ------------------------------------------------------------- */
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.
*/
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);
2003-03-09 14:29:01 +01:00
add_message(&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);
}
/* ------------------------------------------------------------- */
2002-09-02 22:36:12 +02:00
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 */
2002-09-02 22:36:12 +02:00
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
2002-09-02 22:36:12 +02:00
void
pflanze(region *r, unit *u)
{
int m;
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 */
2002-09-02 22:36:12 +02:00
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
}
2002-09-02 22:36:12 +02:00
void
zuechte(region *r, unit *u)
{
int m;
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)
{
char *s;
s = getstrtoken();
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
}
}
}
2002-09-02 22:36:12 +02:00
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) {
if (u->faction == f
#ifdef HELFE_WAHRNEHMUNG
2002-09-02 22:36:12 +02:00
|| alliedunit(u, f, HELP_OBSERVE)
2001-01-25 10:37:55 +01:00
#endif
) {
2001-01-25 10:37:55 +01:00
if (eff_skill(u, SK_OBSERVATION, r) > w) {
w = eff_skill(u, SK_OBSERVATION, r);
}
}
}
return w;
}
2002-09-02 22:36:12 +02:00
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 */
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;
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;
}
2002-09-02 22:36:12 +02:00
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. */
#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");
}
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. */
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. */
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, FL_LONGACTION))
2001-01-25 10:37:55 +01:00
continue;
if (u->race == new_race[RC_INSECT] && r_insectstalled(r) &&
2001-01-25 10:37:55 +01:00
!is_cursed(u->attribs, C_KAELTESCHUTZ,0))
continue;
if (attacked(u) && *(u->thisorder) != 0) {
cmistake(u, findorder(u, u->thisorder), 52, MSG_PRODUCE);
continue;
}
for (s=u->orders;s;s=s->next) {
todo = igetkeyword(s->s, u->faction->locale);
2001-01-25 10:37:55 +01:00
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, FL_TRADER)) {
attrib * a = a_find(u->attribs, &at_trades);
if (a && a->data.i) {
produceexp(u, SK_TRADE, u->number);
2001-01-25 10:37:55 +01:00
}
u->thisorder[0]=0;
continue;
}
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
}
}
/* 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. */
split_allocations(r);
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. */
if (buyorders) expandbuying(r, buyorders);
if (sellorders) expandselling(r, sellorders);
/* Die Spieler sollen alles Geld verdienen, bevor sie beklaut werden
* (expandstealing). */
if (stealorders) expandstealing(r, stealorders);
assert(rmoney(r) >= 0);
assert(rpeasants(r) >= 0);
}
if (vec) {
free(vec);
vec = 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
}