server/src/kernel/build.c
Enno Rehling 898c12e99a XML construction elements never have a building.
required buildings are encoded by RMT_PROD_REQUIRE.
improved error messaging.
2017-05-06 15:53:21 +02:00

997 lines
29 KiB
C
Raw Blame History

/*
Copyright (c) 1998-2015, 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"
#include "alchemy.h"
#include "direction.h"
#include "move.h"
#include "study.h"
#include "guard.h"
#include "laws.h"
#include "skill.h"
#include "lighthouse.h"
/* kernel includes */
#include <kernel/ally.h>
#include <kernel/alliance.h>
#include <kernel/connection.h>
#include <kernel/building.h>
#include <kernel/curse.h>
#include <kernel/faction.h>
#include <kernel/group.h>
#include <kernel/item.h>
#include <kernel/messages.h>
#include <kernel/order.h>
#include <kernel/pool.h>
#include <kernel/race.h>
#include <kernel/region.h>
#include <kernel/resources.h>
#include <kernel/ship.h>
#include <kernel/terrain.h>
#include <kernel/terrainid.h>
#include <kernel/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>
struct building *getbuilding(const struct region *r)
{
building *b = findbuilding(getid());
if (b == NULL || r != b->region)
return NULL;
return b;
}
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;
}
/* ------------------------------------------------------------- */
/* ------------------------------------------------------------- */
static void destroy_road(unit * u, int nmax, struct order *ord)
{
char token[128];
const char *s = gettoken(token, sizeof(token));
direction_t d = s ? get_direction(s, u->faction->locale) : NODIRECTION;
if (d == NODIRECTION) {
/* Die Richtung wurde nicht erkannt */
cmistake(u, ord, 71, MSG_PRODUCE);
}
else {
unit *u2;
region *r = u->region;
int road, n = 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 && is_guard(u2)
&& cansee(u2->faction, u->region, u, 0)
&& !alliedunit(u, u2->faction, HELP_GUARD)) {
cmistake(u, ord, 70, MSG_EVENT);
return;
}
}
road = rroad(r, d);
n = MIN(n, road);
if (n != 0) {
region *r2 = rconnect(r, d);
int willdo = effskill(u, SK_ROAD_BUILDING, 0) * u->number;
willdo = MIN(willdo, n);
if (willdo == 0) {
/* TODO: error message */
}
else if (willdo > SHRT_MAX)
road = 0;
else
road = (short)(road - willdo);
rsetroad(r, d, road);
if (willdo > 0) {
ADDMSG(&u->faction->msgs, msg_message("destroy_road",
"unit from to", u, r, r2));
}
}
}
}
int destroy_cmd(unit * u, struct order *ord)
{
char token[128];
ship *sh;
unit *u2;
region *r = u->region;
const construction *con = NULL;
int size = 0;
const char *s;
int n = INT_MAX;
if (u->number < 1)
return 1;
if (fval(u, UFL_LONGACTION)) {
cmistake(u, ord, 52, MSG_PRODUCE);
return 52;
}
init_order(ord);
s = gettoken(token, sizeof(token));
if (s && *s) {
n = atoi((const char *)s);
if (n <= 0) {
n = INT_MAX;
}
else {
s = gettoken(token, sizeof(token));
}
}
if (s && isparam(s, 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 138;
}
if (fval(b->type, BTF_INDESTRUCTIBLE)) {
cmistake(u, ord, 138, MSG_PRODUCE);
return 138;
}
if (n >= b->size) {
/* destroy completly */
/* all units leave the building */
for (u2 = r->units; u2; u2 = u2->next) {
if (u2->building == b) {
leave_building(u2);
}
}
ADDMSG(&u->faction->msgs, msg_message("destroy", "building unit", b, u));
con = b->type->construction;
remove_building(&r->buildings, 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;
if (u != ship_owner(sh)) {
cmistake(u, ord, 138, MSG_PRODUCE);
return 138;
}
if (fval(r->terrain, SEA_REGION)) {
cmistake(u, ord, 14, MSG_EVENT);
return 14;
}
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) {
leave_ship(u2);
}
}
ADDMSG(&u->faction->msgs, msg_message("shipdestroy",
"unit region ship", u, r, sh));
con = sh->type->construction;
remove_ship(&sh->region->ships, 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 {
cmistake(u, ord, 138, MSG_PRODUCE);
return 138;
}
if (con) {
/* TODO: Nicht an ZERST<53>RE mit Punktangabe angepasst! */
int c;
for (c = 0; con->materials[c].number; ++c) {
const requirement *rq = con->materials + c;
int recycle = (rq->number * size / con->reqsize) / 2;
if (recycle) {
change_resource(u, rq->rtype, recycle);
}
}
}
return 0;
}
/* ------------------------------------------------------------- */
void build_road(unit * u, int size, direction_t d)
{
region *r = u->region;
int n, left, effsk;
region *rn = rconnect(r, d);
assert(u->number);
effsk = effskill(u, SK_ROAD_BUILDING, 0);
if (!effsk) {
cmistake(u, u->thisorder, 103, MSG_PRODUCE);
return;
}
if (besieged(u)) {
cmistake(u, u->thisorder, 60, MSG_PRODUCE);
return;
}
if (rn == NULL || rn->terrain->max_road < 0) {
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)) {
cmistake(u, u->thisorder, 132, MSG_PRODUCE);
return;
}
}
else if (r->terrain == newterrain(T_DESERT)) {
const struct building_type *bt_caravan = bt_find("caravan");
/* wenn keine Karawanserei existiert */
if (!bt_caravan || !buildingtype_exists(r, bt_caravan, true)) {
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;
}
}
/* 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) {
ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder,
"error_roads_finished", ""));
return;
}
if (size > 0)
left = MIN(size, left);
/* baumaximum anhand der rohstoffe */
if (u_race(u) == get_race(RC_STONEGOLEM)) {
n = u->number * GOLEM_STONE;
}
else {
n = get_pooled(u, get_resourcetype(R_STONE), GET_DEFAULT, left);
if (n == 0) {
cmistake(u, u->thisorder, 151, MSG_PRODUCE);
return;
}
}
left = MIN(n, left);
/* n = maximum by skill. try to maximize it */
n = u->number * effsk;
if (n < left) {
const resource_type *ring = get_resourcetype(R_RING_OF_NIMBLEFINGER);
item *itm = ring ? *i_find(&u->items, ring->itype) : 0;
if (itm != NULL && itm->number > 0) {
int rings = MIN(u->number, itm->number);
n = n * ((roqf_factor() - 1) * rings + u->number) / u->number;
}
}
if (n < left) {
int dm = get_effect(u, oldpotiontype[P_DOMORE]);
if (dm != 0) {
int todo = (left - n + effsk - 1) / effsk;
todo = MIN(todo, u->number);
dm = MIN(dm, todo);
change_effect(u, oldpotiontype[P_DOMORE], -dm);
n += dm * effsk;
} /* 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) + n);
if (u_race(u) == get_race(RC_STONEGOLEM)) {
int golemsused = n / GOLEM_STONE;
if (n % GOLEM_STONE != 0) {
++golemsused;
}
scale_number(u, u->number - golemsused);
}
else {
use_pooled(u, get_resourcetype(R_STONE), GET_DEFAULT, 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));
}
/* ------------------------------------------------------------- */
/* ** ** ** ** ** ** *
* 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 */
{
int used;
assert(msize > 0);
used = size * maxneed / msize;
if (size * maxneed % msize)
++used;
return used;
}
static int matmod(const unit * u, const resource_type * rtype, int value)
{
if (rtype->modifiers) {
variant save = frac_make(1, 1);
const struct building_type *btype = NULL;
const struct race *rc = u_race(u);
resource_mod *mod;
if (u->building && inside_building(u)) {
btype = u->building->type;
}
for (mod = rtype->modifiers; mod->type != RMT_END; ++mod) {
if (mod->type == RMT_USE_SAVE) {
if (!mod->btype || mod->btype == btype) {
if (!mod->race || mod->race == rc) {
save = frac_mul(save, mod->value);
}
}
}
}
return value * save.sa[0] / save.sa[1];
}
return value;
}
int roqf_factor(void)
{
static int config;
static int value;
if (config_changed(&config)) {
value = config_get_int("rules.economy.roqf", 10);
}
return value;
}
static int use_materials(unit *u, const construction *type, int n, int completed) {
if (type->materials) {
int c;
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 */
canuse = matmod(u, rtype, canuse);
assert(canuse >= 0);
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);
}
}
return 0;
}
static int count_materials(unit *u, const construction *type, int n, int completed)
{
if (type->materials) {
int c;
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);
canuse = matmod(u, rtype, canuse);
assert(canuse >= 0);
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;
}
}
}
}
return n;
}
/** 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.
*/
int build(unit * u, const construction * ctype, int completed, int want, int skill_mod)
{
const construction *con = ctype;
int skills = INT_MAX; /* number of skill points remainig */
int basesk = 0;
int made = 0;
if (want <= 0)
return 0;
if (con == NULL)
return ENOMATERIALS;
if (con->improvement == NULL && completed == con->maxsize)
return ECOMPLETE;
if (con->skill != NOSKILL) {
int effsk;
int dm = get_effect(u, oldpotiontype[P_DOMORE]);
assert(u->number);
basesk = effskill(u, con->skill, 0);
if (basesk == 0)
return ENEEDSKILL;
effsk = basesk + skill_mod;
assert(effsk >= 0);
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 err, 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 (con && con->improvement &&
con->improvement != con &&
con->maxsize > 0 && con->maxsize <= completed) {
completed -= con->maxsize;
con = con->improvement;
}
if (con == 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<>r Dinge, bei denen die n<>chste Ausbaustufe
* die gleiche wie die vorherige ist. z.b. gegenst<73>nde.
*/
if (con->maxsize > 0) {
completed = completed % con->maxsize;
}
else {
completed = 0;
assert(con->reqsize >= 1);
}
if (basesk < con->minskill) {
if (made == 0)
return ELOWSKILL;
else
break; /* not good enough to go on */
}
/* n = maximum buildable size */
if (con->minskill > 1) {
n = skills / con->minskill;
}
else {
n = skills;
}
/* Flinkfingerring wirkt nicht auf Mengenbegrenzte (magische)
* Talente */
if (skill_limit(u->faction, con->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;
if (i > 0) {
int rings = MIN(u->number, i);
n = n * ((roqf_factor() - 1) * rings + u->number) / u->number;
}
}
if (want < n) n = want;
if (con->maxsize > 0) {
n = MIN(con->maxsize - completed, n);
if (con->improvement == NULL) {
want = n;
}
}
n = count_materials(u, con, n, completed);
if (n <= 0) {
if (made == 0)
return ENOMATERIALS;
else
break;
}
err = use_materials(u, con, n, completed);
if (err < 0) {
return err;
}
made += n;
skills -= n * con->minskill;
want -= n;
completed = completed + n;
}
/* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
produceexp(u, ctype->skill, MIN(made, u->number));
return made;
}
message *msg_materials_required(unit * u, order * ord,
const construction * ctype, int multi)
{
int c;
message *msg;
/* something missing from the list of materials */
resource *reslist = NULL;
if (multi <= 0 || multi == INT_MAX)
multi = 1;
for (c = 0; ctype && ctype->materials[c].number; ++c) {
resource *res = malloc(sizeof(resource));
res->number = multi * ctype->materials[c].number / ctype->reqsize;
res->type = ctype->materials[c].rtype;
res->next = reslist;
reslist = res;
}
msg = msg_feedback(u, ord, "build_required", "required", reslist);
while (reslist) {
resource *res = reslist->next;
free(reslist);
reslist = res;
}
return msg;
}
int maxbuild(const unit * u, const construction * cons)
/* calculate maximum size that can be built from available material */
/* !! ignores maximum objectsize and improvements... */
{
int c;
int maximum = INT_MAX;
for (c = 0; cons->materials[c].number; c++) {
const resource_type *rtype = cons->materials[c].rtype;
int have = get_pooled(u, rtype, GET_DEFAULT, INT_MAX);
int need = required(1, cons->reqsize, cons->materials[c].number);
if (have < need) {
return 0;
}
else
maximum = MIN(maximum, have / need);
}
return maximum;
}
/** old build routines */
int
build_building(unit * u, const building_type * btype, int id, int want, order * ord)
{
region *r = u->region;
int n = want, built = 0;
building *b = NULL;
/* einmalige Korrektur */
const char *btname;
order *new_order = NULL;
const struct locale *lang = u->faction->locale;
assert(u->number);
assert(btype->construction);
if (effskill(u, SK_BUILDING, 0) == 0) {
cmistake(u, ord, 101, MSG_PRODUCE);
return 0;
}
/* 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! */
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 0;
}
}
}
else if (u->building && u->building->type == btype) {
b = u->building;
}
if (b)
btype = b->type;
if (fval(btype, BTF_UNIQUE) && buildingtype_exists(r, btype, false)) {
/* only one of these per region */
cmistake(u, ord, 93, MSG_PRODUCE);
return 0;
}
if (besieged(u)) {
/* units under siege can not build */
cmistake(u, ord, 60, MSG_PRODUCE);
return 0;
}
if (btype->flags & BTF_NOBUILD) {
/* special building, cannot be built */
cmistake(u, ord, 221, MSG_PRODUCE);
return 0;
}
if ((r->terrain->flags & LAND_REGION) == 0) {
/* special terrain, cannot build */
cmistake(u, ord, 221, MSG_PRODUCE);
return 0;
}
if (btype->flags & BTF_ONEPERTURN) {
if (b && fval(b, BLD_EXPANDED)) {
cmistake(u, ord, 318, MSG_PRODUCE);
return 0;
}
n = 1;
}
if (b) {
bool rule_other = config_get_int("rules.build.other_buildings", 1) != 0;
if (!rule_other) {
unit *owner = building_owner(b);
if (!owner || owner->faction != u->faction) {
cmistake(u, ord, 1222, MSG_PRODUCE);
return 0;
}
}
built = b->size;
}
if (n <= 0 || n == INT_MAX) {
if (b == NULL) {
if (btype->maxsize > 0) {
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, 0);
switch (built) {
case ECOMPLETE:
/* the building is already complete */
cmistake(u, ord, 4, MSG_PRODUCE);
break;
case ENOMATERIALS:
ADDMSG(&u->faction->msgs, msg_materials_required(u, ord,
btype->construction, want));
break;
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;
}
/* at this point, the building size is increased. */
if (b == NULL) {
/* 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);
}
}
btname = LOC(lang, btype->_name);
if (want - built <= 0) {
/* geb<65>ude fertig */
new_order = default_order(lang);
}
else if (want != INT_MAX && btname) {
/* reduzierte restgr<67><72>e */
const char *hasspace = strchr(btname, ' ');
if (hasspace) {
new_order =
create_order(K_MAKE, lang, "%d \"%s\" %i", n - built, btname, b->no);
}
else {
new_order =
create_order(K_MAKE, lang, "%d %s %i", n - built, btname, b->no);
}
}
else if (btname) {
/* Neues Haus, Befehl mit Geb<65>udename */
const char *hasspace = strchr(btname, ' ');
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;
if (b->type->maxsize > 0 && b->size > b->type->maxsize) {
log_error("build: %s has size=%d, maxsize=%d", buildingname(b), b->size, b->type->maxsize);
}
fset(b, BLD_EXPANDED);
update_lighthouse(b);
ADDMSG(&u->faction->msgs, msg_message("buildbuilding",
"building unit size", b, u, built));
return built;
}
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;
int can = build(u, construction, size, want, 0);
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));
}
void
create_ship(unit * u, const struct ship_type *newtype, int want,
order * ord)
{
ship *sh;
int msize;
const construction *cons = newtype->construction;
order *new_order;
region * r = u->region;
if (!effskill(u, SK_SHIPBUILDING, 0)) {
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 (effskill(u, cons->skill, 0) < cons->minskill) {
ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder,
"error_build_skill_low", "value", cons->minskill));
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, r, u->faction->locale);
if (leave(u, false)) {
if (fval(u_race(u), RCF_CANSAIL)) {
u_set_ship(u, sh);
}
}
new_order =
create_order(K_MAKE, u->faction->locale, "%s %i", LOC(u->faction->locale,
parameters[P_SHIP]), sh->no);
replace_order(&u->orders, ord, new_order);
free_order(new_order);
build_ship(u, sh, want);
}
void continue_ship(unit * u, int want)
{
const construction *cons;
ship *sh;
int msize;
region * r = u->region;
if (!effskill(u, SK_SHIPBUILDING, 0)) {
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 (effskill(u, cons->skill, 0) < cons->minskill) {
ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder,
"error_build_skill_low", "value", cons->minskill));
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);
}
void free_construction(struct construction *cons)
{
while (cons) {
construction *next = cons->improvement;
free(cons->materials);
free(cons);
cons = next;
}
}