server/src/kernel/build.c

958 lines
26 KiB
C
Raw Normal View History

2010-08-08 10:06:34 +02:00
/*
Copyright (c) 1998-2010, Enno Rehling <enno@eressea.de>
Katja Zedel <katze@felidae.kn-bremen.de
Christian Schlittchen <corwin@amber.kn-bremen.de>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**/
#include <platform.h>
#include <kernel/config.h>
#include "build.h"
/* kernel includes */
#include "alchemy.h"
#include "alliance.h"
#include "connection.h"
#include "direction.h"
2010-08-08 10:06:34 +02:00
#include "building.h"
#include "curse.h"
#include "faction.h"
#include "group.h"
#include "item.h"
#include "magic.h"
#include "messages.h"
2010-08-08 10:06:34 +02:00
#include "move.h"
#include "order.h"
#include "pool.h"
#include "race.h"
#include "region.h"
#include "ship.h"
#include "skill.h"
#include "terrain.h"
#include "terrainid.h"
#include "unit.h"
/* from libutil */
#include <util/attrib.h>
#include <util/base36.h>
#include <util/event.h>
#include <util/goodies.h>
#include <util/language.h>
#include <util/log.h>
#include <util/parser.h>
#include <util/resolve.h>
#include <util/xml.h>
/* 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>
#include <attributes/alliance.h>
#define STONERECYCLE 50
/* Name, MaxGroesse, MinBauTalent, Kapazitaet, {Eisen, Holz, Stein, BauSilber,
* Laen, Mallorn}, UnterSilber, UnterSpezialTyp, UnterSpezial */
2011-03-07 08:02:35 +01:00
struct building *getbuilding(const struct region *r)
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
building *b = findbuilding(getid());
if (b == NULL || r != b->region)
return NULL;
2010-08-08 10:06:34 +02:00
return b;
}
2011-03-07 08:02:35 +01:00
ship *getship(const struct region * r)
2010-08-08 10:06:34 +02:00
{
ship *sh, *sx = findship(getshipid());
for (sh = r->ships; sh; sh = sh->next) {
2011-03-07 08:02:35 +01:00
if (sh == sx)
return sh;
2010-08-08 10:06:34 +02:00
}
return NULL;
}
/* ------------------------------------------------------------- */
/* ------------------------------------------------------------- */
2011-03-07 08:02:35 +01:00
static void destroy_road(unit * u, int nmax, struct order *ord)
2010-08-08 10:06:34 +02:00
{
direction_t d = get_direction(getstrtoken(), u->faction->locale);
2010-08-08 10:06:34 +02:00
unit *u2;
region *r = u->region;
short n = (short)nmax;
2011-03-07 08:02:35 +01:00
if (nmax > SHRT_MAX)
n = SHRT_MAX;
else if (nmax < 0)
n = 0;
2010-08-08 10:06:34 +02:00
2011-03-07 08:02:35 +01:00
for (u2 = r->units; u2; u2 = u2->next) {
if (u2->faction != u->faction && is_guard(u2, GUARD_TAX)
2010-08-08 10:06:34 +02:00
&& cansee(u2->faction, u->region, u, 0)
&& !alliedunit(u, u2->faction, HELP_GUARD)) {
cmistake(u, ord, 70, MSG_EVENT);
return;
}
}
2011-03-07 08:02:35 +01:00
if (d == NODIRECTION) {
2010-08-08 10:06:34 +02:00
/* Die Richtung wurde nicht erkannt */
cmistake(u, ord, 71, MSG_PRODUCE);
} else {
short road = rroad(r, d);
2014-03-16 05:03:17 +01:00
n = _min(n, road);
2011-03-07 08:02:35 +01:00
if (n != 0) {
region *r2 = rconnect(r, d);
int willdo = eff_skill(u, SK_ROAD_BUILDING, r) * u->number;
2014-03-16 05:03:17 +01:00
willdo = _min(willdo, n);
2011-03-07 08:02:35 +01:00
if (willdo == 0) {
2010-08-08 10:06:34 +02:00
/* TODO: error message */
}
2011-03-07 08:02:35 +01:00
if (willdo > SHRT_MAX)
road = 0;
else
road = road - (short)willdo;
2010-08-08 10:06:34 +02:00
rsetroad(r, d, road);
ADDMSG(&u->faction->msgs, msg_message("destroy_road",
2011-03-07 08:02:35 +01:00
"unit from to", u, r, r2));
2010-08-08 10:06:34 +02:00
}
}
}
2011-03-07 08:02:35 +01:00
int destroy_cmd(unit * u, struct order *ord)
2010-08-08 10:06:34 +02:00
{
ship *sh;
unit *u2;
2011-03-07 08:02:35 +01:00
region *r = u->region;
const construction *con = NULL;
2010-08-08 10:06:34 +02:00
int size = 0;
const char *s;
int n = INT_MAX;
if (u->number < 1)
return 0;
init_tokens(ord);
skip_token();
s = getstrtoken();
2011-03-07 08:02:35 +01:00
if (findparam(s, u->faction->locale) == P_ROAD) {
2010-08-08 10:06:34 +02:00
destroy_road(u, INT_MAX, ord);
return 0;
}
if (s && *s) {
n = atoi((const char *)s);
if (n <= 0) {
cmistake(u, ord, 288, MSG_PRODUCE);
return 0;
}
}
if (getparam(u->faction->locale) == P_ROAD) {
destroy_road(u, n, ord);
return 0;
}
if (u->building) {
building *b = u->building;
if (u!=building_owner(b)) {
cmistake(u, ord, 138, MSG_PRODUCE);
return 0;
}
if (fval(b->type, BTF_INDESTRUCTIBLE)) {
cmistake(u, ord, 138, MSG_PRODUCE);
return 0;
2011-03-07 08:02:35 +01:00
}
if (n >= b->size) {
2010-08-08 10:06:34 +02:00
/* destroy completly */
/* all units leave the building */
for (u2 = r->units; u2; u2 = u2->next) {
if (u2->building == b) {
leave_building(u2);
2010-08-08 10:06:34 +02:00
}
}
2011-03-07 08:02:35 +01:00
ADDMSG(&u->faction->msgs, msg_message("destroy", "building unit", b, u));
2010-08-08 10:06:34 +02:00
con = b->type->construction;
remove_building(&r->buildings, b);
} else {
/* partial destroy */
b->size -= n;
ADDMSG(&u->faction->msgs, msg_message("destroy_partial",
2011-03-07 08:02:35 +01:00
"building unit", b, u));
2010-08-08 10:06:34 +02:00
}
} else if (u->ship) {
sh = u->ship;
if (u!=ship_owner(sh)) {
cmistake(u, ord, 138, MSG_PRODUCE);
return 0;
}
2010-08-08 10:06:34 +02:00
if (fval(r->terrain, SEA_REGION)) {
cmistake(u, ord, 14, MSG_EVENT);
return 0;
}
2011-03-07 08:02:35 +01:00
if (n >= (sh->size * 100) / sh->type->construction->maxsize) {
2010-08-08 10:06:34 +02:00
/* destroy completly */
/* all units leave the ship */
for (u2 = r->units; u2; u2 = u2->next) {
if (u2->ship == sh) {
leave_ship(u2);
2010-08-08 10:06:34 +02:00
}
}
ADDMSG(&u->faction->msgs, msg_message("shipdestroy",
2011-03-07 08:02:35 +01:00
"unit region ship", u, r, sh));
2010-08-08 10:06:34 +02:00
con = sh->type->construction;
remove_ship(&sh->region->ships, sh);
} else {
/* partial destroy */
2011-03-07 08:02:35 +01:00
sh->size -= (sh->type->construction->maxsize * n) / 100;
2010-08-08 10:06:34 +02:00
ADDMSG(&u->faction->msgs, msg_message("shipdestroy_partial",
2011-03-07 08:02:35 +01:00
"unit region ship", u, r, sh));
2010-08-08 10:06:34 +02:00
}
} else {
cmistake(u, ord, 138, MSG_PRODUCE);
return 0;
2010-08-08 10:06:34 +02:00
}
if (con) {
/* TODO: Nicht an ZERST<53>RE mit Punktangabe angepa<70>t! */
int c;
2011-03-07 08:02:35 +01:00
for (c = 0; con->materials[c].number; ++c) {
const requirement *rq = con->materials + c;
int recycle = (int)(rq->recycle * rq->number * size / con->reqsize);
2010-08-08 10:06:34 +02:00
if (recycle) {
change_resource(u, rq->rtype, recycle);
}
}
}
return 0;
}
2011-03-07 08:02:35 +01:00
2010-08-08 10:06:34 +02:00
/* ------------------------------------------------------------- */
2011-03-07 08:02:35 +01:00
void build_road(region * r, unit * u, int size, direction_t d)
2010-08-08 10:06:34 +02:00
{
int n, left;
2011-03-07 08:02:35 +01:00
region *rn = rconnect(r, d);
2010-08-08 10:06:34 +02:00
assert(u->number);
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;
}
2011-03-07 08:02:35 +01:00
if (rn == NULL || rn->terrain->max_road < 0) {
2010-08-08 10:06:34 +02:00
cmistake(u, u->thisorder, 94, MSG_PRODUCE);
return;
}
if (r->terrain->max_road < 0) {
cmistake(u, u->thisorder, 94, MSG_PRODUCE);
return;
}
if (r->terrain == newterrain(T_SWAMP)) {
/* wenn kein Damm existiert */
const struct building_type *bt_dam = bt_find("dam");
if (!bt_dam || !buildingtype_exists(r, bt_dam, true)) {
2010-08-08 10:06:34 +02:00
cmistake(u, u->thisorder, 132, MSG_PRODUCE);
return;
}
} else if (r->terrain == newterrain(T_DESERT)) {
const struct building_type *bt_caravan = bt_find("caravan");
2010-08-08 10:06:34 +02:00
/* wenn keine Karawanserei existiert */
if (!bt_caravan || !buildingtype_exists(r, bt_caravan, true)) {
2010-08-08 10:06:34 +02:00
cmistake(u, u->thisorder, 133, MSG_PRODUCE);
return;
}
} else if (r->terrain == newterrain(T_GLACIER)) {
const struct building_type *bt_tunnel = bt_find("tunnel");
/* wenn kein Tunnel existiert */
if (!bt_tunnel || !buildingtype_exists(r, bt_tunnel, true)) {
cmistake(u, u->thisorder, 131, MSG_PRODUCE);
return;
}
2010-08-08 10:06:34 +02:00
}
/* left kann man noch bauen */
left = r->terrain->max_road - rroad(r, d);
/* hoffentlich ist r->road <= r->terrain->max_road, n also >= 0 */
if (left <= 0) {
2011-03-07 08:02:35 +01:00
ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder,
"error_roads_finished", ""));
2010-08-08 10:06:34 +02:00
return;
}
2011-03-07 08:02:35 +01:00
if (size > 0)
2014-03-16 05:03:17 +01:00
left = _min(size, left);
2010-08-08 10:06:34 +02:00
/* baumaximum anhand der rohstoffe */
if (u_race(u) == get_race(RC_STONEGOLEM)) {
2010-08-08 10:06:34 +02:00
n = u->number * GOLEM_STONE;
} else {
n = get_pooled(u, get_resourcetype(R_STONE), GET_DEFAULT, left);
2011-03-07 08:02:35 +01:00
if (n == 0) {
2010-08-08 10:06:34 +02:00
cmistake(u, u->thisorder, 151, MSG_PRODUCE);
return;
}
}
2014-03-16 05:03:17 +01:00
left = _min(n, left);
2010-08-08 10:06:34 +02:00
/* n = maximum by skill. try to maximize it */
n = u->number * eff_skill(u, SK_ROAD_BUILDING, r);
if (n < left) {
const resource_type *ring = get_resourcetype(R_RING_OF_NIMBLEFINGER);
item *itm = ring ? *i_find(&u->items, ring->itype) : 0;
2011-03-07 08:02:35 +01:00
if (itm != NULL && itm->number > 0) {
2014-03-16 05:03:17 +01:00
int rings = _min(u->number, itm->number);
2011-03-07 08:02:35 +01:00
n = n * ((roqf_factor() - 1) * rings + u->number) / u->number;
2010-08-08 10:06:34 +02:00
}
}
if (n < left) {
int dm = get_effect(u, oldpotiontype[P_DOMORE]);
if (dm != 0) {
int sk = eff_skill(u, SK_ROAD_BUILDING, r);
int todo = (left - n + sk - 1) / sk;
2014-03-16 05:03:17 +01:00
todo = _min(todo, u->number);
dm = _min(dm, todo);
2010-08-08 10:06:34 +02:00
change_effect(u, oldpotiontype[P_DOMORE], -dm);
n += dm * sk;
2011-03-07 08:02:35 +01:00
} /* Auswirkung Schaffenstrunk */
2010-08-08 10:06:34 +02:00
}
/* make minimum of possible and available: */
2014-03-16 05:03:17 +01:00
n = _min(left, n);
2010-08-08 10:06:34 +02:00
/* 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(u) == get_race(RC_STONEGOLEM)) {
2010-08-08 10:06:34 +02:00
int golemsused = n / GOLEM_STONE;
2011-03-07 08:02:35 +01:00
if (n % GOLEM_STONE != 0) {
2010-08-08 10:06:34 +02:00
++golemsused;
}
scale_number(u, u->number - golemsused);
} else {
use_pooled(u, get_resourcetype(R_STONE), GET_DEFAULT, n);
2010-08-08 10:06:34 +02:00
/* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
2014-03-16 05:03:17 +01:00
produceexp(u, SK_ROAD_BUILDING, _min(n, u->number));
2010-08-08 10:06:34 +02:00
}
ADDMSG(&u->faction->msgs, msg_message("buildroad",
2011-03-07 08:02:35 +01:00
"region unit size", r, u, n));
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
2010-08-08 10:06:34 +02:00
/* ------------------------------------------------------------- */
/* ** ** ** ** ** ** *
* new build rules *
* ** ** ** ** ** ** */
2011-03-07 08:02:35 +01:00
static int required(int size, int msize, int maxneed)
2010-08-08 10:06:34 +02:00
/* um size von msize Punkten zu bauen,
* braucht man required von maxneed resourcen */
{
int used;
used = size * maxneed / msize;
if (size * maxneed % msize)
++used;
return used;
}
static int
2011-03-07 08:02:35 +01:00
matmod(const attrib * a, const unit * u, const resource_type * material,
int value)
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
for (a = a_find((attrib *) a, &at_matmod); a && a->type == &at_matmod;
a = a->next) {
mm_fun fun = (mm_fun) a->data.f;
2010-08-08 10:06:34 +02:00
value = fun(u, material, value);
2011-03-07 08:02:35 +01:00
if (value < 0)
return value; /* pass errors to caller */
2010-08-08 10:06:34 +02:00
}
return value;
}
int roqf_factor(void)
{
int value = -1;
2011-03-07 08:02:35 +01:00
if (value < 0) {
2010-08-08 10:06:34 +02:00
value = get_param_int(global.parameters, "rules.economy.roqf", 10);
}
return value;
}
/** 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.
*/
2011-03-07 08:02:35 +01:00
int build(unit * u, const construction * ctype, int completed, int want)
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
const construction *type = ctype;
int skills = INT_MAX; /* number of skill points remainig */
2010-08-08 10:06:34 +02:00
int basesk = 0;
int made = 0;
2011-03-07 08:02:35 +01:00
if (want <= 0)
return 0;
if (type == NULL)
return ENOMATERIALS;
2011-03-07 08:02:35 +01:00
if (type->improvement == NULL && completed == type->maxsize)
2010-08-08 10:06:34 +02:00
return ECOMPLETE;
2011-03-07 08:02:35 +01:00
if (type->btype != NULL) {
building *b;
if (!u->building || u->building->type != type->btype) {
2010-08-08 10:06:34 +02:00
return EBUILDINGREQ;
}
b = inside_building(u);
2011-03-07 08:02:35 +01:00
if (b == NULL)
return EBUILDINGREQ;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
if (type->skill != NOSKILL) {
2010-08-08 10:06:34 +02:00
int effsk;
int dm = get_effect(u, oldpotiontype[P_DOMORE]);
assert(u->number);
basesk = effskill(u, type->skill);
2011-03-07 08:02:35 +01:00
if (basesk == 0)
return ENEEDSKILL;
2010-08-08 10:06:34 +02:00
effsk = basesk;
if (inside_building(u)) {
effsk = skillmod(u->building->type->attribs, u, u->region, type->skill,
2011-03-07 08:02:35 +01:00
effsk, SMF_PRODUCTION);
2010-08-08 10:06:34 +02:00
}
effsk = skillmod(type->attribs, u, u->region, type->skill,
2011-03-07 08:02:35 +01:00
effsk, SMF_PRODUCTION);
if (effsk < 0)
return effsk; /* pass errors to caller */
if (effsk == 0)
return ENEEDSKILL;
2010-08-08 10:06:34 +02:00
skills = effsk * u->number;
/* technically, nimblefinge and domore should be in a global set of
2011-03-07 08:02:35 +01:00
* "game"-attributes, (as at_skillmod) but for a while, we're leaving
* them in here. */
2010-08-08 10:06:34 +02:00
if (dm != 0) {
/* Auswirkung Schaffenstrunk */
2014-03-16 05:03:17 +01:00
dm = _min(dm, u->number);
2010-08-08 10:06:34 +02:00
change_effect(u, oldpotiontype[P_DOMORE], -dm);
skills += dm * effsk;
}
}
2011-03-07 08:02:35 +01:00
for (; want > 0 && skills > 0;) {
2010-08-08 10:06:34 +02:00
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 */
2011-03-07 08:02:35 +01:00
while (type->improvement != NULL &&
type->improvement != type &&
type->maxsize > 0 && type->maxsize <= completed) {
2010-08-08 10:06:34 +02:00
completed -= type->maxsize;
type = type->improvement;
}
2011-03-07 08:02:35 +01:00
if (type == NULL) {
if (made == 0)
return ECOMPLETE;
break; /* completed */
2010-08-08 10:06:34 +02:00
}
/* 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.
*/
2011-03-07 08:02:35 +01:00
if (type->maxsize > 1) {
2010-08-08 10:06:34 +02:00
completed = completed % type->maxsize;
2011-03-07 08:02:35 +01:00
} else {
completed = 0;
assert(type->reqsize >= 1);
2010-08-08 10:06:34 +02:00
}
if (basesk < type->minskill) {
2011-03-07 08:02:35 +01:00
if (made == 0)
return ELOWSKILL; /* not good enough to go on */
2010-08-08 10:06:34 +02:00
}
/* n = maximum buildable size */
if (type->minskill > 1) {
n = skills / type->minskill;
} else {
n = skills;
}
/* Flinkfingerring wirkt nicht auf Mengenbegrenzte (magische)
* Talente */
2011-03-07 08:02:35 +01:00
if (skill_limit(u->faction, type->skill) == INT_MAX) {
const resource_type *ring = get_resourcetype(R_RING_OF_NIMBLEFINGER);
item *itm = ring ? *i_find(&u->items, ring->itype) : 0;
int i = itm ? itm->number : 0;
2011-03-07 08:02:35 +01:00
if (i > 0) {
2014-03-16 05:03:17 +01:00
int rings = _min(u->number, i);
2011-03-07 08:02:35 +01:00
n = n * ((roqf_factor() - 1) * rings + u->number) / u->number;
2010-08-08 10:06:34 +02:00
}
}
2011-03-07 08:02:35 +01:00
if (want > 0) {
2014-03-16 05:03:17 +01:00
n = _min(want, n);
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
if (type->maxsize > 0) {
2014-03-16 05:03:17 +01:00
n = _min(type->maxsize - completed, n);
2011-03-07 08:02:35 +01:00
if (type->improvement == NULL) {
2010-08-08 10:06:34 +02:00
want = n;
}
}
2011-03-07 08:02:35 +01:00
if (type->materials)
for (c = 0; n > 0 && type->materials[c].number; c++) {
const struct resource_type *rtype = type->materials[c].rtype;
int need, prebuilt;
int canuse = get_pooled(u, rtype, GET_DEFAULT, INT_MAX);
if (inside_building(u)) {
canuse = matmod(u->building->type->attribs, u, rtype, canuse);
}
if (canuse < 0)
return canuse; /* pass errors to caller */
canuse = matmod(type->attribs, u, 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;
2010-08-08 10:06:34 +02:00
}
}
2011-03-07 08:02:35 +01:00
if (n <= 0) {
if (made == 0)
return ENOMATERIALS;
else
break;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
if (type->materials)
for (c = 0; type->materials[c].number; c++) {
const struct resource_type *rtype = type->materials[c].rtype;
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, rtype, canuse);
if (canuse < 0)
return canuse; /* pass errors to caller */
canuse = matmod(type->attribs, u, 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, rtype, GET_DEFAULT,
(need - prebuilt + multi - 1) / multi);
}
2010-08-08 10:06:34 +02:00
made += n;
skills -= n * type->minskill;
want -= n;
completed = completed + n;
}
/* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
2014-03-16 05:03:17 +01:00
produceexp(u, ctype->skill, _min(made, u->number));
2010-08-08 10:06:34 +02:00
return made;
}
2011-03-07 08:02:35 +01:00
message *msg_materials_required(unit * u, order * ord,
const construction * ctype, int multi)
2010-08-08 10:06:34 +02:00
{
int c;
/* something missing from the list of materials */
2011-03-07 08:02:35 +01:00
resource *reslist = NULL;
if (multi <= 0 || multi == INT_MAX)
multi = 1;
for (c = 0; ctype && ctype->materials[c].number; ++c) {
2011-03-07 08:02:35 +01:00
resource *res = malloc(sizeof(resource));
2010-08-08 10:06:34 +02:00
res->number = multi * ctype->materials[c].number / ctype->reqsize;
res->type = ctype->materials[c].rtype;
res->next = reslist;
reslist = res;
}
return msg_feedback(u, ord, "build_required", "required", reslist);
}
2011-03-07 08:02:35 +01:00
int maxbuild(const unit * u, const construction * cons)
2010-08-08 10:06:34 +02:00
/* calculate maximum size that can be built from available material */
2011-03-07 08:02:35 +01:00
/* !! ignores maximum objectsize and improvements... */
2010-08-08 10:06:34 +02:00
{
int c;
int maximum = INT_MAX;
2011-03-07 08:02:35 +01:00
for (c = 0; cons->materials[c].number; c++) {
const resource_type *rtype = cons->materials[c].rtype;
2010-08-08 10:06:34 +02:00
int have = get_pooled(u, rtype, GET_DEFAULT, INT_MAX);
int need = required(1, cons->reqsize, cons->materials[c].number);
2011-03-07 08:02:35 +01:00
if (have < need) {
2010-08-08 10:06:34 +02:00
return 0;
2011-03-07 08:02:35 +01:00
} else
2014-03-16 05:03:17 +01:00
maximum = _min(maximum, have / need);
2010-08-08 10:06:34 +02:00
}
return maximum;
}
/** old build routines */
int
build_building(unit * u, const building_type * btype, int id, int want, order * ord)
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
region *r = u->region;
int n = want, built = 0;
2011-03-07 08:02:35 +01:00
building *b = NULL;
2010-08-08 10:06:34 +02:00
/* einmalige Korrektur */
2011-03-07 08:02:35 +01:00
const char *btname;
order *new_order = NULL;
const struct locale *lang = u->faction->locale;
2010-08-08 10:06:34 +02:00
static int rule_other = -1;
assert(u->number);
assert(btype->construction);
2010-08-08 10:06:34 +02:00
if (eff_skill(u, SK_BUILDING, r) == 0) {
cmistake(u, ord, 101, MSG_PRODUCE);
return 0;
2010-08-08 10:06:34 +02:00
}
/* 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! */
2011-03-07 08:02:35 +01:00
if (id > 0) { /* eine Nummer angegeben, keine neue Burg bauen */
2010-08-08 10:06:34 +02:00
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? */
2011-03-07 08:02:35 +01:00
if (u->building && u->building->type == btype) {
2010-08-08 10:06:34 +02:00
b = u->building;
} else {
/* keine neue Burg anfangen wenn eine Nummer angegeben war */
cmistake(u, ord, 6, MSG_PRODUCE);
return 0;
2010-08-08 10:06:34 +02:00
}
}
2011-03-07 08:02:35 +01:00
} else if (u->building && u->building->type == btype) {
2010-08-08 10:06:34 +02:00
b = u->building;
}
2011-03-07 08:02:35 +01:00
if (b)
btype = b->type;
2010-08-08 10:06:34 +02:00
if (fval(btype, BTF_UNIQUE) && buildingtype_exists(r, btype, false)) {
2010-08-08 10:06:34 +02:00
/* only one of these per region */
cmistake(u, ord, 93, MSG_PRODUCE);
return 0;
2010-08-08 10:06:34 +02:00
}
if (besieged(u)) {
/* units under siege can not build */
cmistake(u, ord, 60, MSG_PRODUCE);
return 0;
2010-08-08 10:06:34 +02:00
}
if (btype->flags & BTF_NOBUILD) {
/* special building, cannot be built */
cmistake(u, ord, 221, MSG_PRODUCE);
return 0;
2010-08-08 10:06:34 +02:00
}
if ((r->terrain->flags & LAND_REGION) == 0) {
/* special terrain, cannot build */
cmistake(u, ord, 221, MSG_PRODUCE);
return 0;
}
2010-08-08 10:06:34 +02:00
if (btype->flags & BTF_ONEPERTURN) {
2011-03-07 08:02:35 +01:00
if (b && fval(b, BLD_EXPANDED)) {
2010-08-08 10:06:34 +02:00
cmistake(u, ord, 318, MSG_PRODUCE);
return 0;
2010-08-08 10:06:34 +02:00
}
n = 1;
}
if (b) {
2011-03-07 08:02:35 +01:00
if (rule_other < 0) {
rule_other =
get_param_int(global.parameters, "rules.build.other_buildings", 1);
2010-08-08 10:06:34 +02:00
}
if (!rule_other) {
2011-03-07 08:02:35 +01:00
unit *owner = building_owner(b);
if (!owner || owner->faction != u->faction) {
2010-08-08 10:06:34 +02:00
cmistake(u, ord, 1222, MSG_PRODUCE);
return 0;
2010-08-08 10:06:34 +02:00
}
}
}
2011-03-07 08:02:35 +01:00
if (b)
built = b->size;
if (n <= 0 || n == INT_MAX) {
if (b == NULL) {
if (btype->maxsize > 0) {
2010-08-08 10:06:34 +02:00
n = btype->maxsize - built;
} else {
n = INT_MAX;
}
} else {
if (b->type->maxsize > 0) {
n = b->type->maxsize - built;
} else {
n = INT_MAX;
}
}
}
built = build(u, btype->construction, built, n);
switch (built) {
2011-03-08 08:44:20 +01:00
case ECOMPLETE:
/* the building is already complete */
cmistake(u, ord, 4, MSG_PRODUCE);
break;
2011-03-08 08:44:20 +01:00
case ENOMATERIALS:
ADDMSG(&u->faction->msgs, msg_materials_required(u, ord,
2011-03-08 08:44:20 +01:00
btype->construction, want));
break;
2011-03-08 08:44:20 +01:00
case ELOWSKILL:
case ENEEDSKILL:
/* no skill, or not enough skill points to build */
cmistake(u, ord, 50, MSG_PRODUCE);
break;
}
if (built<=0) {
return built;
2010-08-08 10:06:34 +02:00
}
/* at this point, the building size is increased. */
2011-03-07 08:02:35 +01:00
if (b == NULL) {
2010-08-08 10:06:34 +02:00
/* build a new building */
b = new_building(btype, r, lang);
b->type = btype;
fset(b, BLD_MAINTAINED);
/* Die Einheit befindet sich automatisch im Inneren der neuen Burg. */
if (u->number && leave(u, false)) {
u_set_building(u, b);
assert(building_owner(b)==u);
2010-08-08 10:06:34 +02:00
}
#ifdef WDW_PYRAMID
2011-03-07 08:02:35 +01:00
if (b->type == bt_find("pyramid") && f_get_alliance(u->faction) != NULL) {
attrib *a = a_add(&b->attribs, a_new(&at_alliance));
2010-08-08 10:06:34 +02:00
a->data.i = u->faction->alliance->id;
}
#endif
}
btname = LOC(lang, btype->_name);
2011-03-07 08:02:35 +01:00
if (want - built <= 0) {
2010-08-08 10:06:34 +02:00
/* geb<65>ude fertig */
new_order = default_order(lang);
2011-03-07 08:02:35 +01:00
} else if (want != INT_MAX) {
2010-08-08 10:06:34 +02:00
/* reduzierte restgr<67><72>e */
2011-03-07 08:02:35 +01:00
const char *hasspace = strchr(btname, ' ');
2010-08-08 10:06:34 +02:00
if (hasspace) {
2011-03-07 08:02:35 +01:00
new_order =
create_order(K_MAKE, lang, "%d \"%s\" %i", n - built, btname, b->no);
2010-08-08 10:06:34 +02:00
} else {
2011-03-07 08:02:35 +01:00
new_order =
create_order(K_MAKE, lang, "%d %s %i", n - built, btname, b->no);
2010-08-08 10:06:34 +02:00
}
} else if (btname) {
/* Neues Haus, Befehl mit Geb<65>udename */
2011-03-07 08:02:35 +01:00
const char *hasspace = strchr(btname, ' ');
2010-08-08 10:06:34 +02:00
if (hasspace) {
new_order = create_order(K_MAKE, lang, "\"%s\" %i", btname, b->no);
} else {
new_order = create_order(K_MAKE, lang, "%s %i", btname, b->no);
}
}
if (new_order) {
replace_order(&u->orders, ord, new_order);
free_order(new_order);
}
b->size += built;
2010-08-08 10:06:34 +02:00
fset(b, BLD_EXPANDED);
update_lighthouse(b);
ADDMSG(&u->faction->msgs, msg_message("buildbuilding",
2011-03-07 08:02:35 +01:00
"building unit size", b, u, built));
return built;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static void build_ship(unit * u, ship * sh, int want)
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
const construction *construction = sh->type->construction;
2010-08-08 10:06:34 +02:00
int size = (sh->size * DAMAGE_SCALE - sh->damage) / DAMAGE_SCALE;
int n;
int can = build(u, construction, size, want);
2011-03-07 08:02:35 +01:00
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;
2010-08-08 10:06:34 +02:00
}
}
if (sh->damage && can) {
2014-03-16 05:03:17 +01:00
int repair = _min(sh->damage, can * DAMAGE_SCALE);
2010-08-08 10:06:34 +02:00
n += repair / DAMAGE_SCALE;
2011-03-07 08:02:35 +01:00
if (repair % DAMAGE_SCALE)
++n;
2010-08-08 10:06:34 +02:00
sh->damage = sh->damage - repair;
}
2011-03-07 08:02:35 +01:00
if (n)
ADDMSG(&u->faction->msgs,
msg_message("buildship", "ship unit size", sh, u, n));
2010-08-08 10:06:34 +02:00
}
void
2011-03-07 08:02:35 +01:00
create_ship(region * r, unit * u, const struct ship_type *newtype, int want,
order * ord)
2010-08-08 10:06:34 +02:00
{
ship *sh;
int msize;
2011-03-07 08:02:35 +01:00
const construction *cons = newtype->construction;
order *new_order;
2010-08-08 10:06:34 +02:00
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) {
2011-03-07 08:02:35 +01:00
ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder,
"error_build_skill_low", "value", cons->minskill));
2010-08-08 10:06:34 +02:00
return;
}
msize = maxbuild(u, cons);
2011-03-07 08:02:35 +01:00
if (msize == 0) {
2010-08-08 10:06:34 +02:00
cmistake(u, ord, 88, MSG_PRODUCE);
return;
}
2011-03-07 08:02:35 +01:00
if (want > 0)
2014-03-16 05:03:17 +01:00
want = _min(want, msize);
2011-03-07 08:02:35 +01:00
else
want = msize;
2010-08-08 10:06:34 +02:00
sh = new_ship(newtype, r, u->faction->locale);
2010-08-08 10:06:34 +02:00
if (leave(u, false)) {
if (fval(u_race(u), RCF_CANSAIL)) {
u_set_ship(u, sh);
2010-08-08 10:06:34 +02:00
}
}
2011-03-07 08:02:35 +01:00
new_order =
create_order(K_MAKE, u->faction->locale, "%s %i", LOC(u->faction->locale,
parameters[P_SHIP]), sh->no);
2010-08-08 10:06:34 +02:00
replace_order(&u->orders, ord, new_order);
free_order(new_order);
build_ship(u, sh, want);
}
2011-03-07 08:02:35 +01:00
void continue_ship(region * r, unit * u, int want)
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
const construction *cons;
2010-08-08 10:06:34 +02:00
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);
2011-03-07 08:02:35 +01:00
if (!sh)
sh = u->ship;
2010-08-08 10:06:34 +02:00
if (!sh) {
cmistake(u, u->thisorder, 20, MSG_PRODUCE);
return;
}
cons = sh->type->construction;
2011-03-07 08:02:35 +01:00
assert(cons->improvement == NULL); /* sonst ist construction::size nicht ship_type::maxsize */
if (sh->size == cons->maxsize && !sh->damage) {
2010-08-08 10:06:34 +02:00
cmistake(u, u->thisorder, 16, MSG_PRODUCE);
return;
}
if (eff_skill(u, cons->skill, r) < cons->minskill) {
2011-03-07 08:02:35 +01:00
ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder,
"error_build_skill_low", "value", cons->minskill));
2010-08-08 10:06:34 +02:00
return;
}
msize = maxbuild(u, cons);
2011-03-07 08:02:35 +01:00
if (msize == 0) {
2010-08-08 10:06:34 +02:00
cmistake(u, u->thisorder, 88, MSG_PRODUCE);
return;
}
2011-03-07 08:02:35 +01:00
if (want > 0)
2014-03-16 05:03:17 +01:00
want = _min(want, msize);
2011-03-07 08:02:35 +01:00
else
want = msize;
2010-08-08 10:06:34 +02:00
build_ship(u, sh, want);
}
2011-03-07 08:02:35 +01:00