server/src/common/kernel/build.c

1296 lines
34 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
2001-01-25 10:37:55 +01:00
* 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.
*/
#include <config.h>
#include "eressea.h"
#include "build.h"
/* kernel includes */
#include "alchemy.h"
#include "border.h"
#include "building.h"
#include "curse.h"
#include "faction.h"
#include "group.h"
#include "item.h"
#include "magic.h"
#include "message.h"
#include "movement.h"
2004-06-21 18:45:27 +02:00
#include "order.h"
2001-01-25 10:37:55 +01:00
#include "pool.h"
#include "race.h"
#include "region.h"
#include "ship.h"
#include "skill.h"
#include "unit.h"
/* from libutil */
#include <attrib.h>
#include <base36.h>
#include <event.h>
#include <goodies.h>
#include <resolve.h>
#include <xml.h>
2001-01-25 10:37:55 +01:00
/* from libc */
#include <assert.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* attributes inclues */
#include <attributes/matmod.h>
#define STONERECYCLE 50
/* Name, MaxGroesse, MinBauTalent, Kapazitaet, {Eisen, Holz, Stein, BauSilber,
* Laen, Mallorn}, UnterSilber, UnterSpezialTyp, UnterSpezial */
static boolean
2004-05-04 21:34:35 +02:00
CheckOverload(void)
{
static int value = -1;
if (value<0) {
const char * str = get_param(global.parameters, "rules.check_overload");
value = str?atoi(str):0;
}
return value;
}
2001-01-25 10:37:55 +01:00
static int
2001-04-26 19:41:06 +02:00
slipthru(const region * r, const unit * u, const building * b)
2001-01-25 10:37:55 +01:00
{
unit *u2;
int n, o;
2001-01-25 10:37:55 +01:00
/* b ist die burg, in die man hinein oder aus der man heraus will. */
2001-01-25 10:37:55 +01:00
if (!b) {
return 1;
}
if (b->besieged < b->size * SIEGEFACTOR) {
return 1;
}
/* u wird am hinein- oder herausschluepfen gehindert, wenn STEALTH <=
* OBSERVATION +2 der belagerer u2 ist */
2001-01-25 10:37:55 +01:00
n = eff_skill(u, SK_STEALTH, r);
2001-01-25 10:37:55 +01:00
for (u2 = r->units; u2; u2 = u2->next)
if (usiege(u2) == b) {
if (invisible(u, u2) >= u->number) continue;
2001-01-25 10:37:55 +01:00
o = eff_skill(u2, SK_OBSERVATION, r);
2001-01-25 10:37:55 +01:00
if (o + 2 >= n)
return 0; /* entdeckt! */
}
return 1;
2001-01-25 10:37:55 +01:00
}
boolean
2001-04-26 19:41:06 +02:00
can_contact(const region * r, const unit * u, const unit * u2)
2001-01-25 10:37:55 +01:00
{
/* hier geht es nur um die belagerung von burgen */
2001-01-25 10:37:55 +01:00
if (u->building == u2->building)
return true;
2001-01-25 10:37:55 +01:00
/* unit u is trying to contact u2 - unasked for contact. wenn u oder u2
* nicht in einer burg ist, oder die burg nicht belagert ist, ist
* slipthru () == 1. ansonsten ist es nur 1, wenn man die belagerer */
2001-01-25 10:37:55 +01:00
if (slipthru(u->region, u, u->building)
&& slipthru(u->region, u2, u2->building))
return true;
2001-01-25 10:37:55 +01:00
if (alliedunit(u, u2->faction, HELP_GIVE))
return true;
2001-01-25 10:37:55 +01:00
return false;
2001-01-25 10:37:55 +01:00
}
static void
2004-06-21 18:45:27 +02:00
contact_cmd(unit * u, order * ord, boolean tries)
2001-01-25 10:37:55 +01:00
{
/* unit u kontaktiert unit u2. Dies setzt den contact einfach auf 1 -
* ein richtiger toggle ist (noch?) nicht noetig. die region als
* parameter ist nur deswegen wichtig, weil er an getunit ()
* weitergegeben wird. dies wird fuer das auffinden von tempunits in
* getnewunit () verwendet! */
unit *u2;
2004-06-21 18:45:27 +02:00
region * r = u->region;
2004-06-21 18:45:27 +02:00
init_tokens(ord);
skip_token();
u2 = getunitg(r, u->faction);
2001-01-25 10:37:55 +01:00
if (u2!=NULL) {
if (!can_contact(r, u, u2)) {
if (tries) cmistake(u, u->thisorder, 23, MSG_EVENT);
return;
}
usetcontact(u, u2);
}
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
/* ------------------------------------------------------------- */
/* ------------------------------------------------------------- */
struct building *
getbuilding(const struct region * r)
{
building * b = findbuilding(getid());
if (b==NULL || r!=b->region) return NULL;
return b;
2001-01-25 10:37:55 +01:00
}
ship *
getship(const struct region * r)
{
ship *sh, *sx = findship(getshipid());
for (sh = r->ships; sh; sh = sh->next) {
if (sh == sx) return sh;
}
return NULL;
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
static void
2004-06-21 18:45:27 +02:00
siege_cmd(unit * u, order * ord)
2001-01-25 10:37:55 +01:00
{
2004-06-21 18:45:27 +02:00
region * r = u->region;
unit *u2;
building *b;
int d;
int bewaffnete, katapultiere = 0;
static boolean init = false;
static const curse_type * magicwalls_ct;
static item_type * it_catapultammo = NULL;
if (!init) {
init = true;
magicwalls_ct = ct_find("magicwalls");
it_catapultammo = it_find("catapultammo");
}
2004-06-21 18:45:27 +02:00
/* gibt es ueberhaupt Burgen? */
init_tokens(ord);
skip_token();
b = getbuilding(r);
if (!b) {
cmistake(u, ord, 31, MSG_BATTLE);
return;
}
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
if (!playerrace(u->race)) {
/* keine Drachen, Illusionen, Untote etc */
cmistake(u, ord, 166, MSG_BATTLE);
return;
}
/* schaden durch katapulte */
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
d = min(u->number,
min(new_get_pooled(u, it_catapultammo->rtype, GET_SLACK|GET_RESERVE|GET_POOLED_SLACK), get_item(u, I_CATAPULT)));
2004-06-21 18:45:27 +02:00
if (eff_skill(u, SK_CATAPULT, r) >= 1) {
katapultiere = d;
d *= eff_skill(u, SK_CATAPULT, r);
} else {
d = 0;
}
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
if ((bewaffnete = armedmen(u)) == 0 && d == 0) {
/* abbruch, falls unbewaffnet oder unfaehig, katapulte zu benutzen */
cmistake(u, ord, 80, MSG_EVENT);
return;
}
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
if (!(getguard(u) & GUARD_TRAVELTHRU)) {
/* abbruch, wenn die einheit nicht vorher die region bewacht - als
* warnung fuer alle anderen! */
cmistake(u, ord, 81, MSG_EVENT);
return;
}
/* einheit und burg markieren - spart zeit beim behandeln der einheiten
* in der burg, falls die burg auch markiert ist und nicht alle
* einheiten wieder abgesucht werden muessen! */
usetsiege(u, b);
b->besieged += max(bewaffnete, katapultiere);
/* definitiver schaden eingeschraenkt */
d = min(d, b->size - 1);
/* meldung, schaden anrichten */
if (d && !curse_active(get_curse(b->attribs, magicwalls_ct))) {
b->size -= d;
new_use_pooled(u, it_catapultammo->rtype, GET_SLACK|GET_RESERVE|GET_POOLED_SLACK, d);
2004-06-21 18:45:27 +02:00
d = 100 * d / b->size;
} else d = 0;
/* meldung fuer belagerer */
ADDMSG(&u->faction->msgs, msg_message("siege",
"unit building destruction", u, b, d));
2004-06-21 18:45:27 +02:00
for (u2 = r->units; u2; u2 = u2->next) freset(u2->faction, FL_DH);
fset(u->faction, FL_DH);
/* Meldung fuer Burginsassen */
for (u2 = r->units; u2; u2 = u2->next) {
if (u2->building == b && !fval(u2->faction, FL_DH)) {
fset(u2->faction, FL_DH);
ADDMSG(&u2->faction->msgs, msg_message("siege",
"unit building destruction", u, b, d));
2004-06-21 18:45:27 +02:00
}
}
2001-01-25 10:37:55 +01:00
}
void
do_siege(void)
{
2004-06-21 18:45:27 +02:00
region *r;
for (r = regions; r; r = r->next) {
if (rterrain(r) != T_OCEAN) {
unit *u;
2004-06-21 18:45:27 +02:00
for (u = r->units; u; u = u->next) {
if (get_keyword(u->thisorder) == K_BESIEGE) {
siege_cmd(u, u->thisorder);
}
}
}
}
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
static void
2005-06-12 11:48:19 +02:00
destroy_road(unit *u, int nmax, struct order * ord)
{
direction_t d = getdirection(u->faction->locale);
unit *u2;
region *r = u->region;
2005-06-12 11:48:19 +02:00
short n = (short)nmax;
if (nmax>SHRT_MAX) n = SHRT_MAX;
else if (nmax<0) n = 0;
for (u2=r->units;u2;u2=u2->next) {
if (u2->faction!=u->faction && getguard(u2)&GUARD_TAX
&& cansee(u2->faction, u->region, u, 0)
&& !alliedunit(u, u2->faction, HELP_GUARD)) {
cmistake(u, ord, 70, MSG_EVENT);
return;
}
}
if (d==NODIRECTION) {
cmistake(u, ord, 71, MSG_PRODUCE);
} else {
2005-06-12 11:48:19 +02:00
short road = rroad(r, d);
n = min(n, road);
if (n!=0) {
2005-06-12 11:48:19 +02:00
region * r2 = rconnect(r,d);
int willdo = eff_skill(u, SK_ROAD_BUILDING, r)*u->number;
willdo = min(willdo, n);
2005-06-12 11:48:19 +02:00
if (willdo==0) {
/* TODO: error message */
}
if (willdo>SHRT_MAX) road = 0;
else road = road - (short)willdo;
rsetroad(r, d, road);
ADDMSG(&u->faction->msgs, msg_message("destroy_road",
"unit from to", u, r, r2));
}
}
}
2004-06-21 18:45:27 +02:00
int
destroy_cmd(unit * u, struct order * ord)
2001-01-25 10:37:55 +01:00
{
ship *sh;
unit *u2;
2004-06-21 18:45:27 +02:00
region * r = u->region;
#if 0
const construction * con = NULL;
int size = 0;
#endif
const char *s;
int n = INT_MAX;
2001-01-25 10:37:55 +01:00
if (u->number < 1)
2004-06-21 18:45:27 +02:00
return 0;
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
init_tokens(ord);
skip_token();
s = getstrtoken();
if (findparam(s, u->faction->locale)==P_ROAD) {
2004-06-21 18:45:27 +02:00
destroy_road(u, INT_MAX, ord);
return 0;
}
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
if (s && *s) {
n = atoi(s);
if(n <= 0) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 288, MSG_PRODUCE);
return 0;
}
}
2004-06-21 18:45:27 +02:00
if (getparam(u->faction->locale) == P_ROAD) {
destroy_road(u, n, ord);
return 0;
}
if (!fval(u, UFL_OWNER)) {
cmistake(u, ord, 138, MSG_PRODUCE);
return 0;
}
if (u->building) {
building *b = u->building;
2004-06-21 18:45:27 +02:00
if (a_find(b->attribs, &at_nodestroy)) {
cmistake(u, ord, 14, MSG_EVENT);
return 0;
}
#if 0
con = b->type->construction;
size = b->size;
#endif
2001-01-25 10:37:55 +01:00
if(n >= b->size) {
/* destroy completly */
/* all units leave the building */
for (u2 = r->units; u2; u2 = u2->next)
if (u2->building == b) {
u2->building = 0;
freset(u2, UFL_OWNER);
}
ADDMSG(&u->faction->msgs, msg_message("destroy",
"building unit", b, u));
destroy_building(b);
} else {
/* partial destroy */
b->size -= n;
ADDMSG(&u->faction->msgs, msg_message("destroy_partial",
"building unit", b, u));
}
} else if (u->ship) {
sh = u->ship;
2004-06-21 18:45:27 +02:00
if (a_find(sh->attribs, &at_nodestroy)) {
cmistake(u, ord, 14, MSG_EVENT);
return 0;
}
#if 0
con = sh->type->construction;
size = (sh->size * DAMAGE_SCALE - sh->damage) / DAMAGE_SCALE;
#endif
if (rterrain(r) == T_OCEAN) {
2004-06-21 18:45:27 +02:00
cmistake(u, ord, 14, MSG_EVENT);
return 0;
}
if(n >= (sh->size*100)/sh->type->construction->maxsize) {
/* destroy completly */
/* all units leave the ship */
for (u2 = r->units; u2; u2 = u2->next)
if (u2->ship == sh) {
u2->ship = 0;
freset(u2, UFL_OWNER);
}
ADDMSG(&u->faction->msgs, msg_message("shipdestroy",
"unit region ship", u, r, sh));
destroy_ship(sh);
} else {
/* partial destroy */
sh->size -= (sh->type->construction->maxsize * n)/100;
ADDMSG(&u->faction->msgs, msg_message("shipdestroy_partial",
"unit region ship", u, r, sh));
}
} else {
log_error(("Die Einheit %s von %s war owner eines objects, war aber weder in einer Burg noch in einem Schiff.\n",
unitname(u), u->faction->name, u->faction->email));
}
#if 0
/* Achtung: Nicht an ZERST<53>RE mit Punktangabe angepa<70>t! */
if (con) {
2004-06-21 18:45:27 +02:00
/* TODO: ZERST<53>RE - Man sollte alle Materialien zur<75>ckkriegen k<>nnen: */
int c;
for (c=0;con->materials[c].number;++c) {
const requirement * rq = con->materials+c;
int recycle = (int)(rq->recycle * rq->number * size/con->reqsize);
if (recycle)
change_resource(u, rq->type, recycle);
}
}
#endif
2004-06-21 18:45:27 +02:00
return 0;
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
void
build_road(region * r, unit * u, int size, direction_t d)
{
int n, left;
if (!eff_skill(u, SK_ROAD_BUILDING, r)) {
cmistake(u, u->thisorder, 103, MSG_PRODUCE);
return;
}
if (besieged(u)) {
cmistake(u, u->thisorder, 60, MSG_PRODUCE);
return;
}
if (terrain[rterrain(r)].roadreq < 0) {
cmistake(u, u->thisorder, 94, MSG_PRODUCE);
return;
}
if (rterrain(r) == T_SWAMP) {
/* wenn kein Damm existiert */
static const struct building_type * bt_dam;
if (!bt_dam) bt_dam = bt_find("dam");
assert(bt_dam);
if (!buildingtype_exists(r, bt_dam)) {
cmistake(u, u->thisorder, 132, MSG_PRODUCE);
return;
}
}
if (rterrain(r) == T_DESERT) {
static const struct building_type * bt_caravan;
if (!bt_caravan) bt_caravan = bt_find("caravan");
assert(bt_caravan);
/* wenn keine Karawanserei existiert */
if (!buildingtype_exists(r, bt_caravan)) {
cmistake(u, u->thisorder, 133, MSG_PRODUCE);
return;
}
}
if (rterrain(r) == T_GLACIER) {
static const struct building_type * bt_tunnel;
if (!bt_tunnel) bt_tunnel = bt_find("tunnel");
assert(bt_tunnel);
/* wenn kein Tunnel existiert */
if (!buildingtype_exists(r, bt_tunnel)) {
cmistake(u, u->thisorder, 131, MSG_PRODUCE);
return;
}
}
2005-05-27 23:04:13 +02:00
if (!get_pooled(u, r, R_STONE) && u->race != new_race[RC_STONEGOLEM]) {
cmistake(u, u->thisorder, 151, MSG_PRODUCE);
return;
}
/* left kann man noch bauen */
left = terrain[rterrain(r)].roadreq - rroad(r, d);
/* hoffentlich ist r->road <= terrain[rterrain(r)].roadreq, n also >= 0 */
if (left <= 0) {
sprintf(buf, "In %s gibt es keine Br<42>cken und Stra<72>en "
"mehr zu bauen", regionname(r, u->faction));
mistake(u, u->thisorder, buf, MSG_PRODUCE);
return;
}
/* baumaximum anhand der rohstoffe */
2005-05-27 23:04:13 +02:00
if (u->race == new_race[RC_STONEGOLEM]){
n = u->number * GOLEM_STONE;
} else {
n = get_pooled(u, r, R_STONE);
}
left = min(n, left);
if (size>0) left = min(size, left);
/* n = maximum by skill. try to maximize it */
n = u->number * eff_skill(u, SK_ROAD_BUILDING, r);
if (n < left) {
item * itm = *i_find(&u->items, olditemtype[I_RING_OF_NIMBLEFINGER]);
if (itm!=NULL && itm->number>0) {
int rings = min(u->number, itm->number);
n = n * (9*rings+u->number) / u->number;
}
}
if (n < left) {
int dm = get_effect(u, oldpotiontype[P_DOMORE]);
if (dm != 0) {
int sk = eff_skill(u, SK_ROAD_BUILDING, r);
dm = (left - n + sk - 1) / sk;
dm = min(dm, u->number);
change_effect(u, oldpotiontype[P_DOMORE], -dm);
n += dm * sk;
} /* Auswirkung Schaffenstrunk */
}
/* make minimum of possible and available: */
n = min(left, n);
/* n is now modified by several special effects, so we have to
* minimize it again to make sure the road will not grow beyond
* maximum. */
rsetroad(r, d, rroad(r, d) + (short)n);
if (u->race == new_race[RC_STONEGOLEM]) {
int golemsused = n / GOLEM_STONE;
if (n%GOLEM_STONE != 0){
++golemsused;
}
scale_number(u, u->number - golemsused);
} else {
use_pooled(u, r, R_STONE, n);
/* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
produceexp(u, SK_ROAD_BUILDING, min(n, u->number));
}
ADDMSG(&u->faction->msgs, msg_message("buildroad",
"region unit size", r, u, n));
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
/* ** ** ** ** ** ** *
* new build rules *
* ** ** ** ** ** ** */
static int
required(int size, int msize, int maxneed)
/* um size von msize Punkten zu bauen,
* braucht man required von maxneed resourcen */
2001-01-25 10:37:55 +01:00
{
int used;
2001-01-25 10:37:55 +01:00
used = size * maxneed / msize;
if (size * maxneed % msize)
++used;
return used;
2001-01-25 10:37:55 +01:00
}
static int
matmod(const attrib * a, const unit * u, const resource_type * material, int value)
{
for (a=a_find((attrib*)a, &at_matmod);a;a=a->nexttype) {
mm_fun fun = (mm_fun)a->data.f;
value = fun(u, material, value);
if (value<0) return value; /* pass errors to caller */
}
return value;
2001-01-25 10:37:55 +01:00
}
/** Use up resources for building an object.
* Build up to 'size' points of 'type', where 'completed'
* of the first object have already been finished. return the
* actual size that could be built.
*/
2001-01-25 10:37:55 +01:00
int
build(unit * u, const construction * ctype, int completed, int want)
{
const construction * type = ctype;
int skills; /* number of skill points remainig */
int dm = get_effect(u, oldpotiontype[P_DOMORE]);
int made = 0;
int basesk, effsk;
if (want<=0) return 0;
if (type==NULL) return 0;
if (type->improvement==NULL && completed==type->maxsize)
return ECOMPLETE;
basesk = effskill(u, type->skill);
if (basesk==0) return ENEEDSKILL;
effsk = basesk;
if (inside_building(u)) {
effsk = skillmod(u->building->type->attribs, u, u->region, type->skill, effsk, SMF_PRODUCTION);
}
effsk = skillmod(type->attribs, u, u->region, type->skill, effsk, SMF_PRODUCTION);
if (effsk<0) return effsk; /* pass errors to caller */
if (effsk==0) return ENEEDSKILL;
skills = effsk * u->number;
/* technically, nimblefinge and domore should be in a global set of "game"-attributes,
* (as at_skillmod) but for a while, we're leaving them in here. */
if (dm != 0) {
/* Auswirkung Schaffenstrunk */
dm = min(dm, u->number);
change_effect(u, oldpotiontype[P_DOMORE], -dm);
skills += dm * effsk;
}
for (;want>0 && skills>0;) {
int c, n;
/* skip over everything that's already been done:
* type->improvement==NULL means no more improvements, but no size limits
* type->improvement==type means build another object of the same time while material lasts
* type->improvement==x means build x when type is finished
*/
while (type->improvement!=NULL &&
type->improvement!=type &&
type->maxsize>0 &&
type->maxsize<=completed)
{
completed -= type->maxsize;
type = type->improvement;
}
if (type==NULL) {
if (made==0) return ECOMPLETE;
break; /* completed */
}
/* Hier ist entweder maxsize == -1, oder completed < maxsize.
* Andernfalls ist das Datenfile oder sonstwas kaputt...
* (enno): Nein, das ist f<EFBFBD>r Dinge, bei denen die n<EFBFBD>chste Ausbaustufe
* die gleiche wie die vorherige ist. z.b. gegenst<EFBFBD>nde.
*/
if (type->maxsize>1) {
completed = completed % type->maxsize;
}
else {
completed = 0; assert(type->reqsize>=1);
}
if (basesk < type->minskill) {
if (made==0) return ELOWSKILL; /* not good enough to go on */
}
/* n = maximum buildable size */
if (type->minskill > 1) {
n = skills / type->minskill;
} else {
n = skills;
}
/* Flinkfingerring wirkt nicht auf Mengenbegrenzte (magische)
* Talente */
if (max_skill(u->faction, type->skill)==INT_MAX) {
int i = 0;
item * itm = *i_find(&u->items, olditemtype[I_RING_OF_NIMBLEFINGER]);
if (itm!=NULL) i = itm->number;
if (i>0) {
int rings = min(u->number, i);
n = n * (9*rings+u->number) / u->number;
}
}
if (want>0) {
n = min(want, n);
}
if (type->maxsize>0) {
n = min(type->maxsize-completed, n);
if (type->improvement==NULL) {
want = n;
}
}
if (type->materials) for (c=0;n>0 && type->materials[c].number;c++) {
resource_t rtype = type->materials[c].type;
int need;
int have = get_pooled(u, NULL, rtype);
int prebuilt;
int canuse = have;
if (inside_building(u)) {
canuse = matmod(u->building->type->attribs, u, oldresourcetype[rtype], canuse);
#if 0
/* exploit-check */
} else if (u->building) {
int abuse = matmod(u->building->type->attribs, u, oldresourcetype[rtype], canuse);
if (abuse>canuse) {
log_printf("ABUSE: %s saves %u %s through exploit\n",
itoa36(u->faction->no), abuse-canuse,
oldresourcetype[rtype]->_name[0]);
}
#endif
}
if (canuse<0) return canuse; /* pass errors to caller */
canuse = matmod(type->attribs, u, oldresourcetype[rtype], canuse);
if (type->reqsize>1) {
prebuilt = required(completed, type->reqsize, type->materials[c].number);
for (;n;) {
need = required(completed + n, type->reqsize, type->materials[c].number);
if (need-prebuilt<=canuse) break;
--n; /* TODO: optimieren? */
}
} else {
int maxn = canuse / type->materials[c].number;
if (maxn < n) n = maxn;
}
}
if (n<=0) {
if (made==0) return ENOMATERIALS;
else break;
}
if (type->materials) for (c=0;type->materials[c].number;c++) {
resource_t rtype = type->materials[c].type;
int prebuilt = required(completed, type->reqsize, type->materials[c].number);
int need = required(completed + n, type->reqsize, type->materials[c].number);
int multi = 1;
int canuse = 100; /* normalization */
if (inside_building(u)) canuse = matmod(u->building->type->attribs, u, oldresourcetype[rtype], canuse);
if (canuse<0) return canuse; /* pass errors to caller */
canuse = matmod(type->attribs, u, oldresourcetype[rtype], canuse);
assert(canuse % 100 == 0 || !"only constant multipliers are implemented in build()");
multi = canuse/100;
if (canuse<0) return canuse; /* pass errors to caller */
use_pooled(u, NULL, rtype, (need-prebuilt+multi-1)/multi);
}
made += n;
skills -= n * type->minskill;
want -= n;
completed = completed + n;
}
/* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
produceexp(u, ctype->skill, min(made, u->number));
return made;
2001-01-25 10:37:55 +01:00
}
int
maxbuild(const unit * u, const construction * cons)
/* calculate maximum size that can be built from available material */
/* !! ignores maximum objectsize and improvements...*/
2001-01-25 10:37:55 +01:00
{
int c;
int maximum = INT_MAX;
for (c=0;cons->materials[c].number;c++) {
resource_t rtype = cons->materials[c].type;
int have = get_pooled(u, NULL, rtype);
int need = required(1, cons->reqsize, cons->materials[c].number);
if (have<need) {
cmistake(u, u->thisorder, 88, MSG_PRODUCE);
return 0;
}
else maximum = min(maximum, have/need);
}
return maximum;
2001-01-25 10:37:55 +01:00
}
/** old build routines */
void
build_building(unit * u, const building_type * btype, int want, order * ord)
2001-01-25 10:37:55 +01:00
{
region * r = u->region;
boolean newbuilding = false;
int c, built = 0, id;
building * b = NULL;
/* einmalige Korrektur */
static char buffer[8 + IDSIZE + 1 + NAMESIZE + 1];
const char * btname;
2005-05-22 14:26:19 +02:00
order * new_order = NULL;
const struct locale * lang = u->faction->locale;
if (eff_skill(u, SK_BUILDING, r) == 0) {
cmistake(u, ord, 101, MSG_PRODUCE);
return;
}
/* Falls eine Nummer angegeben worden ist, und ein Gebaeude mit der
* betreffenden Nummer existiert, ist b nun gueltig. Wenn keine Burg
* gefunden wurde, dann wird nicht einfach eine neue erbaut. Ansonsten
* baut man an der eigenen burg weiter. */
/* Wenn die angegebene Nummer falsch ist, KEINE Burg bauen! */
id = atoi36(getstrtoken());
if (id!=0){ /* eine Nummer angegeben, keine neue Burg bauen */
b = findbuilding(id);
if (!b || b->region != u->region){ /* eine Burg mit dieser Nummer gibt es hier nicht */
/* vieleicht Tippfehler und die eigene Burg ist gemeint? */
if (u->building && u->building->type==btype) {
b = u->building;
} else {
/* keine neue Burg anfangen wenn eine Nummer angegeben war */
cmistake(u, ord, 6, MSG_PRODUCE);
return;
}
}
}
if (b) btype = b->type;
if (b && fval(btype, BTF_UNIQUE) && buildingtype_exists(r, btype)) {
/* only one of these per region */
cmistake(u, ord, 93, MSG_PRODUCE);
return;
}
if (besieged(u)) {
/* units under siege can not build */
cmistake(u, ord, 60, MSG_PRODUCE);
return;
}
if (btype->flags & BTF_NOBUILD) {
/* special building, cannot be built */
cmistake(u, ord, 221, MSG_PRODUCE);
return;
}
if (b) built = b->size;
if (want<=0 || want == INT_MAX) {
if(b == NULL) {
if(btype->maxsize > 0) {
want = btype->maxsize - built;
} else {
want = INT_MAX;
}
} else {
if(b->type->maxsize > 0) {
want = b->type->maxsize - built;
} else {
want = INT_MAX;
}
}
}
built = build(u, btype->construction, built, want);
switch (built) {
case ECOMPLETE:
/* the building is already complete */
cmistake(u, ord, 4, MSG_PRODUCE);
return;
case ENOMATERIALS: {
/* something missing from the list of materials */
const construction * cons = btype->construction;
char * ch = buf;
assert(cons);
for (c=0;cons->materials[c].number; c++) {
int n;
if (c!=0) strcat(ch++, ",");
n = cons->materials[c].number / cons->reqsize;
sprintf(ch, " %d %s", n?n:1,
2005-05-22 14:26:19 +02:00
LOC(lang, resname(cons->materials[c].type, cons->materials[c].number!=1)));
ch = ch+strlen(ch);
}
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "build_required",
"required", buf));
return;
}
case ELOWSKILL:
case ENEEDSKILL:
/* no skill, or not enough skill points to build */
cmistake(u, ord, 50, MSG_PRODUCE);
return;
}
/* at this point, the building size is increased. */
if (b==NULL) {
/* build a new building */
2005-05-22 14:26:19 +02:00
b = new_building(btype, r, lang);
b->type = btype;
fset(b, BLD_MAINTAINED);
/* Die Einheit befindet sich automatisch im Inneren der neuen Burg. */
leave(r, u);
u->building = b;
fset(u, UFL_OWNER);
2005-05-22 14:26:19 +02:00
newbuilding = true;
}
btname = LOC(lang, btype->_name);
2005-05-22 14:26:19 +02:00
if (want-built <= 0) {
/* geb<65>ude fertig */
strcpy(buffer, LOC(lang, "defaultorder"));
2005-05-22 14:26:19 +02:00
new_order = parse_order(buffer, lang);
} else if (want!=INT_MAX) {
/* reduzierte restgr<67><72>e */
sprintf(buffer, "%s %d %s %s", LOC(lang, keywords[K_MAKE]), want-built, btname, buildingid(b));
new_order = parse_order(buffer, lang);
} else if (btname) {
/* Neues Haus, Befehl mit Geb<65>udename */
sprintf(buffer, "%s %s %s", LOC(lang, keywords[K_MAKE]), btname, buildingid(b));
new_order = parse_order(buffer, u->faction->locale);
}
2005-05-22 14:26:19 +02:00
if (new_order) {
#ifdef LASTORDER
2005-05-22 14:26:19 +02:00
set_order(&u->lastorder, new_order);
#else
2005-05-22 14:26:19 +02:00
replace_order(&u->orders, ord, new_order);
free_order(new_order);
#endif
2005-05-22 14:26:19 +02:00
}
2001-01-25 10:37:55 +01:00
b->size += built;
update_lighthouse(b);
2001-01-25 10:37:55 +01:00
ADDMSG(&u->faction->msgs, msg_message("buildbuilding",
"building unit size", b, u, built));
2001-01-25 10:37:55 +01:00
}
static void
build_ship(unit * u, ship * sh, int want)
{
const construction * construction = sh->type->construction;
int size = (sh->size * DAMAGE_SCALE - sh->damage) / DAMAGE_SCALE;
int n;
2004-01-24 00:27:16 +01:00
#if 0
int can = u->number * effskill(u, SK_SHIPBUILDING) / construction->minskill;
if (want > 0) can = min(want, can);
can = min(can, construction->maxsize+sh->damage); /* 100% bauen + 100% reparieren */
2004-01-24 00:27:16 +01:00
#endif
int can = build(u, construction, size, want);
if ((n=construction->maxsize - sh->size)>0 && can>0) {
if (can>=n) {
sh->size += n;
can -= n;
}
else {
sh->size += can;
n=can;
can = 0;
}
}
if (sh->damage && can) {
int repair = min(sh->damage, can * DAMAGE_SCALE);
n += repair / DAMAGE_SCALE;
if (repair % DAMAGE_SCALE) ++n;
sh->damage = sh->damage - repair;
}
if (n) ADDMSG(&u->faction->msgs,
msg_message("buildship", "ship unit size", sh, u, n));
2001-01-25 10:37:55 +01:00
}
void
create_ship(region * r, unit * u, const struct ship_type * newtype, int want, order * ord)
2001-01-25 10:37:55 +01:00
{
static char buffer[IDSIZE + 2 * KEYWORDSIZE + 3];
ship *sh;
int msize;
const construction * cons = newtype->construction;
order * new_order;
if (!eff_skill(u, SK_SHIPBUILDING, r)) {
cmistake(u, ord, 100, MSG_PRODUCE);
return;
}
if (besieged(u)) {
cmistake(u, ord, 60, MSG_PRODUCE);
return;
}
/* check if skill and material for 1 size is available */
if (eff_skill(u, cons->skill, r) < cons->minskill) {
sprintf(buf, "Um %s zu bauen, braucht man ein Talent von "
"mindestens %d.", newtype->name[1], cons->minskill);
mistake(u, ord, buf, MSG_PRODUCE);
return;
}
msize = maxbuild(u, cons);
if (msize==0) {
cmistake(u, ord, 88, MSG_PRODUCE);
return;
}
if (want>0) want = min(want, msize);
else want = msize;
sh = new_ship(newtype, u->faction->locale, r);
leave(r, u);
u->ship = sh;
fset(u, UFL_OWNER);
sprintf(buffer, "%s %s %s",
2004-06-21 18:45:27 +02:00
locale_string(u->faction->locale, keywords[K_MAKE]), locale_string(u->faction->locale, parameters[P_SHIP]), shipid(sh));
new_order = parse_order(buffer, u->faction->locale);
#ifdef LASTORDER
set_order(&u->lastorder, new_order);
#else
2005-05-22 14:26:19 +02:00
replace_order(&u->orders, ord, new_order);
free_order(new_order);
#endif
2001-01-25 10:37:55 +01:00
build_ship(u, sh, want);
2001-01-25 10:37:55 +01:00
}
void
continue_ship(region * r, unit * u, int want)
{
const construction * cons;
ship *sh;
int msize;
if (!eff_skill(u, SK_SHIPBUILDING, r)) {
cmistake(u, u->thisorder, 100, MSG_PRODUCE);
return;
}
/* Die Schiffsnummer bzw der Schiffstyp wird eingelesen */
sh = getship(r);
if (!sh) sh = u->ship;
if (!sh) {
cmistake(u, u->thisorder, 20, MSG_PRODUCE);
return;
}
cons = sh->type->construction;
assert(cons->improvement==NULL); /* sonst ist construction::size nicht ship_type::maxsize */
if (sh->size==cons->maxsize && !sh->damage) {
cmistake(u, u->thisorder, 16, MSG_PRODUCE);
return;
}
if (eff_skill(u, cons->skill, r) < cons->minskill) {
sprintf(buf, "Um %s zu bauen, braucht man ein Talent von "
"mindestens %d.", sh->type->name[1], cons->minskill);
mistake(u, u->thisorder, buf, MSG_PRODUCE);
return;
}
msize = maxbuild(u, cons);
if (msize==0) {
cmistake(u, u->thisorder, 88, MSG_PRODUCE);
return;
}
if (want > 0) want = min(want, msize);
else want = msize;
build_ship(u, sh, want);
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
static boolean
2001-01-25 10:37:55 +01:00
mayenter(region * r, unit * u, building * b)
{
unit *u2;
if (fval(b, BLD_UNGUARDED)) return true;
u2 = buildingowner(r, b);
2001-01-25 10:37:55 +01:00
if (u2==NULL || ucontact(u2, u)
|| alliedunit(u2, u->faction, HELP_GUARD)) return true;
2001-01-25 10:37:55 +01:00
return false;
2001-01-25 10:37:55 +01:00
}
static int
mayboard(const unit * u, const ship * sh)
2001-01-25 10:37:55 +01:00
{
unit *u2 = shipowner(sh);
2001-01-25 10:37:55 +01:00
return (!u2 || ucontact(u2, u) || alliedunit(u2, u->faction, HELP_GUARD));
2001-01-25 10:37:55 +01:00
}
void
remove_contacts(void)
{
region *r;
unit *u;
attrib *a;
for (r = regions; r; r = r->next) {
for (u = r->units; u; u = u->next) {
a = (attrib *)a_find(u->attribs, &at_contact);
while(a != NULL) {
attrib * ar = a;
a = a->nexttype;
a_remove(&u->attribs, ar);
}
}
}
2001-01-25 10:37:55 +01:00
}
2004-06-21 18:45:27 +02:00
int
leave_cmd(unit * u, struct order * ord)
2001-01-25 10:37:55 +01:00
{
2004-06-21 18:45:27 +02:00
region * r = u->region;
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
if (r->terrain == T_OCEAN && u->ship) {
if(!fval(u->race, RCF_SWIM)) {
cmistake(u, ord, 11, MSG_MOVE);
return 0;
}
if(get_item(u, I_HORSE)) {
cmistake(u, ord, 231, MSG_MOVE);
return 0;
}
}
if (!slipthru(r, u, u->building)) {
sprintf(buf, "%s wird belagert.", buildingname(u->building));
mistake(u, ord, buf, MSG_MOVE);
} else {
leave(r, u);
}
return 0;
2001-01-25 10:37:55 +01:00
}
static boolean
2004-06-21 18:45:27 +02:00
entership(unit * u, ship * sh, struct order * ord, boolean lasttry)
{
/* Mu<4D> abgefangen werden, sonst k<>nnten Schwimmer an
* Bord von Schiffen an Land gelangen. */
if( !fval(u->race, RCF_WALK) &&
!fval(u->race, RCF_FLY)) {
cmistake(u, ord, 233, MSG_MOVE);
return false;
}
if (!sh) {
if (lasttry) cmistake(u, ord, 20, MSG_MOVE);
return false;
}
if (sh==u->ship) return true;
if (!mayboard(u, sh)) {
if (lasttry) cmistake(u, ord, 34, MSG_MOVE);
return false;
}
if (CheckOverload()) {
int sweight, scabins;
int mweight = shipcapacity(sh);
int mcabins = sh->type->cabins;
if (mweight>0 && mcabins>0) {
getshipweight(sh, &sweight, &scabins);
sweight += weight(u);
scabins += u->number;
sweight = ((sweight+99) / 100) * 100; /* Silberreste aufrunden */
if (sweight > mweight || scabins > mcabins) {
if (lasttry) cmistake(u, ord, 34, MSG_MOVE);
return false;
}
}
}
leave(u->region, u);
u->ship = sh;
if (shipowner(sh) == 0) {
fset(u, UFL_OWNER);
}
return true;
}
2001-01-25 10:37:55 +01:00
void
do_misc(boolean lasttry)
2001-01-25 10:37:55 +01:00
{
region *r;
ship *sh;
building *b;
2001-01-25 10:37:55 +01:00
/* lasttry: Fehler nur im zweiten Versuch melden. Sonst konfus. */
2001-01-25 10:37:55 +01:00
for (r = regions; r; r = r->next) {
unit *u;
2001-01-25 10:37:55 +01:00
for (u = r->units; u; u = u->next) {
2004-06-21 18:45:27 +02:00
order * ord;
for (ord = u->orders; ord; ord = ord->next) {
switch (get_keyword(ord)) {
case K_CONTACT:
contact_cmd(u, ord, lasttry);
break;
}
}
}
for (u = r->units; u; u = u->next) {
2004-06-21 18:45:27 +02:00
order ** ordp = &u->orders;
while (*ordp) {
order * ord = *ordp;
if (get_keyword(ord) == K_ENTER) {
init_tokens(ord);
skip_token();
switch (getparam(u->faction->locale)) {
case P_BUILDING:
case P_GEBAEUDE:
/* Schwimmer k<>nnen keine Geb<65>ude betreten, au<61>er diese sind
* auf dem Ozean */
if( !fval(u->race, RCF_WALK) && !fval(u->race, RCF_FLY)) {
if (rterrain(r) != T_OCEAN){
if (lasttry) cmistake(u, ord, 232, MSG_MOVE);
break;
}
}
b = getbuilding(r);
if (!b) {
if(lasttry) cmistake(u, ord, 6, MSG_MOVE);
break;
}
/* Geb<65>ude auf dem Ozean sollte man betreten d<>rfen
if(rterrain(r) == T_OCEAN) {
if (lasttry) cmistake(u, ord, 297, MSG_MOVE);
break;
}
*/
if (!mayenter(r, u, b)) {
if(lasttry) {
sprintf(buf, "Der Eintritt in %s wurde verwehrt",
buildingname(b));
mistake(u, ord, buf, MSG_MOVE);
}
break;
}
if (!slipthru(r, u, b)) {
if(lasttry) {
sprintf(buf, "%s wird belagert", buildingname(b));
mistake(u, ord, buf, MSG_MOVE);
}
break;
}
/* Wenn wir hier angekommen sind, war der Befehl
* erfolgreich und wir l<EFBFBD>schen ihn, damit er im
* zweiten Versuch nicht nochmal ausgef<EFBFBD>hrt wird. */
2004-06-21 18:45:27 +02:00
*ordp = ord->next;
ord->next = NULL;
2004-06-21 18:45:27 +02:00
free_order(ord);
leave(r, u);
u->building = b;
if (buildingowner(r, b) == 0) {
fset(u, UFL_OWNER);
}
break;
case P_SHIP:
sh = getship(r);
entership(u, sh, ord, lasttry);
break;
default:
if (lasttry) cmistake(u, ord, 79, MSG_MOVE);
}
}
2004-06-21 18:45:27 +02:00
if (*ordp==ord) ordp = &ord->next;
}
}
}
2001-01-25 10:37:55 +01:00
}