2010-08-08 10:06:34 +02:00
|
|
|
|
/*
|
2015-01-30 22:10:29 +01:00
|
|
|
|
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
2014-12-22 16:28:17 +01:00
|
|
|
|
Katja Zedel <katze@felidae.kn-bremen.de
|
|
|
|
|
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
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"
|
2014-06-16 03:34:39 +02:00
|
|
|
|
#include "direction.h"
|
2010-08-08 10:06:34 +02:00
|
|
|
|
#include "move.h"
|
2016-03-08 14:09:22 +01:00
|
|
|
|
#include "study.h"
|
2016-11-01 22:11:10 +01:00
|
|
|
|
#include "guard.h"
|
2014-11-01 12:09:56 +01:00
|
|
|
|
#include "laws.h"
|
2010-08-08 10:06:34 +02:00
|
|
|
|
#include "skill.h"
|
2014-12-17 17:22:26 +01:00
|
|
|
|
#include "lighthouse.h"
|
2014-11-01 12:09:56 +01:00
|
|
|
|
|
|
|
|
|
/* kernel includes */
|
2015-11-24 18:52:09 +01:00
|
|
|
|
#include <kernel/ally.h>
|
2014-11-01 12:09:56 +01:00
|
|
|
|
#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/ship.h>
|
|
|
|
|
#include <kernel/terrain.h>
|
|
|
|
|
#include <kernel/terrainid.h>
|
|
|
|
|
#include <kernel/unit.h>
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
/* 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>
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
struct building *getbuilding(const struct region *r)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-12-22 16:28:17 +01:00
|
|
|
|
building *b = findbuilding(getid());
|
|
|
|
|
if (b == NULL || r != b->region)
|
|
|
|
|
return NULL;
|
|
|
|
|
return b;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
ship *getship(const struct region * r)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-12-22 16:28:17 +01:00
|
|
|
|
ship *sh, *sx = findship(getshipid());
|
|
|
|
|
for (sh = r->ships; sh; sh = sh->next) {
|
|
|
|
|
if (sh == sx)
|
|
|
|
|
return sh;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
|
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
|
|
|
|
{
|
2014-12-22 16:28:17 +01:00
|
|
|
|
char token[128];
|
|
|
|
|
const char *s = gettoken(token, sizeof(token));
|
2014-09-21 16:43:17 +02:00
|
|
|
|
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);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
2014-09-21 16:43:17 +02:00
|
|
|
|
unit *u2;
|
|
|
|
|
region *r = u->region;
|
2015-05-15 19:08:44 +02:00
|
|
|
|
int road, n = nmax;
|
2014-12-22 16:28:17 +01:00
|
|
|
|
|
2014-09-21 16:43:17 +02:00
|
|
|
|
if (nmax > SHRT_MAX) {
|
|
|
|
|
n = SHRT_MAX;
|
2014-12-22 16:28:17 +01:00
|
|
|
|
}
|
|
|
|
|
else if (nmax < 0) {
|
2014-09-21 16:43:17 +02:00
|
|
|
|
n = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (u2 = r->units; u2; u2 = u2->next) {
|
2016-10-31 07:45:06 +01:00
|
|
|
|
if (u2->faction != u->faction && is_guard(u2)
|
2014-09-21 16:43:17 +02:00
|
|
|
|
&& cansee(u2->faction, u->region, u, 0)
|
|
|
|
|
&& !alliedunit(u, u2->faction, HELP_GUARD)) {
|
|
|
|
|
cmistake(u, ord, 70, MSG_EVENT);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
road = rroad(r, d);
|
2017-01-10 16:31:05 +01:00
|
|
|
|
n = MIN(n, road);
|
2014-09-21 16:43:17 +02:00
|
|
|
|
if (n != 0) {
|
|
|
|
|
region *r2 = rconnect(r, d);
|
2015-08-27 16:16:55 +02:00
|
|
|
|
int willdo = effskill(u, SK_ROAD_BUILDING, 0) * u->number;
|
2017-01-10 16:31:05 +01:00
|
|
|
|
willdo = MIN(willdo, n);
|
2014-09-21 16:43:17 +02:00
|
|
|
|
if (willdo == 0) {
|
|
|
|
|
/* TODO: error message */
|
|
|
|
|
}
|
2016-08-28 15:31:25 +02:00
|
|
|
|
else if (willdo > SHRT_MAX)
|
2014-12-22 16:28:17 +01:00
|
|
|
|
road = 0;
|
2014-09-21 16:43:17 +02:00
|
|
|
|
else
|
2015-05-15 19:08:44 +02:00
|
|
|
|
road = (short)(road - willdo);
|
2014-09-21 16:43:17 +02:00
|
|
|
|
rsetroad(r, d, road);
|
2016-08-28 17:31:29 +02:00
|
|
|
|
if (willdo > 0) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("destroy_road",
|
|
|
|
|
"unit from to", u, r, r2));
|
|
|
|
|
}
|
2014-09-21 16:43:17 +02:00
|
|
|
|
}
|
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
|
|
|
|
{
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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)
|
2015-08-04 22:47:55 +02:00
|
|
|
|
return 1;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2015-07-03 13:04:11 +02:00
|
|
|
|
if (fval(u, UFL_LONGACTION)) {
|
|
|
|
|
cmistake(u, ord, 52, MSG_PRODUCE);
|
2015-08-04 22:47:55 +02:00
|
|
|
|
return 52;
|
2015-07-03 13:04:11 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
init_order(ord);
|
|
|
|
|
s = gettoken(token, sizeof(token));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
if (s && *s) {
|
|
|
|
|
n = atoi((const char *)s);
|
|
|
|
|
if (n <= 0) {
|
2015-07-02 11:42:18 +02:00
|
|
|
|
n = INT_MAX;
|
2014-12-22 16:28:17 +01:00
|
|
|
|
}
|
2016-08-28 17:48:06 +02:00
|
|
|
|
else {
|
|
|
|
|
s = gettoken(token, sizeof(token));
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-09-01 21:57:50 +02:00
|
|
|
|
if (s && isparam(s, u->faction->locale, P_ROAD)) {
|
2014-12-22 16:28:17 +01:00
|
|
|
|
destroy_road(u, n, ord);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
if (u->building) {
|
|
|
|
|
building *b = u->building;
|
|
|
|
|
|
|
|
|
|
if (u != building_owner(b)) {
|
|
|
|
|
cmistake(u, ord, 138, MSG_PRODUCE);
|
2015-08-04 22:47:55 +02:00
|
|
|
|
return 138;
|
2014-12-22 16:28:17 +01:00
|
|
|
|
}
|
|
|
|
|
if (fval(b->type, BTF_INDESTRUCTIBLE)) {
|
|
|
|
|
cmistake(u, ord, 138, MSG_PRODUCE);
|
2015-08-04 22:47:55 +02:00
|
|
|
|
return 138;
|
2014-12-22 16:28:17 +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) {
|
|
|
|
|
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);
|
2015-08-04 22:47:55 +02:00
|
|
|
|
return 138;
|
2014-12-22 16:28:17 +01:00
|
|
|
|
}
|
|
|
|
|
if (fval(r->terrain, SEA_REGION)) {
|
|
|
|
|
cmistake(u, ord, 14, MSG_EVENT);
|
2015-08-04 22:47:55 +02:00
|
|
|
|
return 14;
|
2014-12-22 16:28:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2015-08-04 22:47:55 +02:00
|
|
|
|
return 138;
|
2014-12-22 16:28:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (con) {
|
2016-11-23 19:19:04 +01:00
|
|
|
|
/* TODO: Nicht an ZERST<53>RE mit Punktangabe angepasst! */
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
|
2015-08-27 16:59:39 +02:00
|
|
|
|
void build_road(unit * u, int size, direction_t d)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2015-08-27 16:59:39 +02:00
|
|
|
|
region *r = u->region;
|
2015-08-27 16:16:55 +02:00
|
|
|
|
int n, left, effsk;
|
2014-12-22 16:28:17 +01:00
|
|
|
|
region *rn = rconnect(r, d);
|
|
|
|
|
|
|
|
|
|
assert(u->number);
|
2015-08-27 16:16:55 +02:00
|
|
|
|
effsk = effskill(u, SK_ROAD_BUILDING, 0);
|
|
|
|
|
if (!effsk) {
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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)
|
2017-01-10 16:31:05 +01:00
|
|
|
|
left = MIN(size, left);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-10 16:31:05 +01:00
|
|
|
|
left = MIN(n, left);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
|
|
|
|
|
/* n = maximum by skill. try to maximize it */
|
2015-08-27 16:16:55 +02:00
|
|
|
|
n = u->number * effsk;
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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) {
|
2017-01-10 16:31:05 +01:00
|
|
|
|
int rings = MIN(u->number, itm->number);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
n = n * ((roqf_factor() - 1) * rings + u->number) / u->number;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (n < left) {
|
|
|
|
|
int dm = get_effect(u, oldpotiontype[P_DOMORE]);
|
|
|
|
|
if (dm != 0) {
|
2015-08-27 16:16:55 +02:00
|
|
|
|
int todo = (left - n + effsk - 1) / effsk;
|
2017-01-10 16:31:05 +01:00
|
|
|
|
todo = MIN(todo, u->number);
|
|
|
|
|
dm = MIN(dm, todo);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
change_effect(u, oldpotiontype[P_DOMORE], -dm);
|
2015-08-27 16:16:55 +02:00
|
|
|
|
n += dm * effsk;
|
2014-12-22 16:28:17 +01:00
|
|
|
|
} /* Auswirkung Schaffenstrunk */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* make minimum of possible and available: */
|
2017-01-10 16:31:05 +01:00
|
|
|
|
n = MIN(left, n);
|
2014-12-22 16:28:17 +01: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. */
|
2015-05-15 19:08:44 +02:00
|
|
|
|
rsetroad(r, d, rroad(r, d) + n);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
|
|
|
|
|
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 */
|
2017-01-10 16:31:05 +01:00
|
|
|
|
produceexp(u, SK_ROAD_BUILDING, MIN(n, u->number));
|
2014-12-22 16:28:17 +01:00
|
|
|
|
}
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("buildroad",
|
|
|
|
|
"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)
|
2014-12-22 16:28:17 +01:00
|
|
|
|
/* um size von msize Punkten zu bauen,
|
|
|
|
|
* braucht man required von maxneed resourcen */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-12-22 16:28:17 +01:00
|
|
|
|
int used;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
used = size * maxneed / msize;
|
|
|
|
|
if (size * maxneed % msize)
|
|
|
|
|
++used;
|
|
|
|
|
return used;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2011-03-07 08:02:35 +01:00
|
|
|
|
matmod(const attrib * a, const unit * u, const resource_type * material,
|
2014-12-22 16:28:17 +01:00
|
|
|
|
int value)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-12-22 16:28:17 +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;
|
|
|
|
|
value = fun(u, material, value);
|
|
|
|
|
if (value < 0)
|
|
|
|
|
return value; /* pass errors to caller */
|
|
|
|
|
}
|
|
|
|
|
return value;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int roqf_factor(void)
|
|
|
|
|
{
|
2016-09-11 12:48:00 +02:00
|
|
|
|
static int config;
|
|
|
|
|
static int value;
|
|
|
|
|
if (config_changed(&config)) {
|
2015-11-22 10:44:46 +01:00
|
|
|
|
value = config_get_int("rules.economy.roqf", 10);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
}
|
|
|
|
|
return value;
|
2010-08-08 10:06:34 +02: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.
|
|
|
|
|
*/
|
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
|
|
|
|
{
|
2014-12-22 16:28:17 +01:00
|
|
|
|
const construction *type = ctype;
|
|
|
|
|
int skills = INT_MAX; /* number of skill points remainig */
|
|
|
|
|
int basesk = 0;
|
|
|
|
|
int made = 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
if (want <= 0)
|
|
|
|
|
return 0;
|
|
|
|
|
if (type == NULL)
|
|
|
|
|
return ENOMATERIALS;
|
|
|
|
|
if (type->improvement == NULL && completed == type->maxsize)
|
2011-03-07 08:02:35 +01:00
|
|
|
|
return ECOMPLETE;
|
2014-12-22 16:28:17 +01:00
|
|
|
|
if (type->btype != NULL) {
|
|
|
|
|
building *b;
|
|
|
|
|
if (!u->building || u->building->type != type->btype) {
|
|
|
|
|
return EBUILDINGREQ;
|
|
|
|
|
}
|
|
|
|
|
b = inside_building(u);
|
2015-11-02 14:18:50 +01:00
|
|
|
|
if (!b || !building_is_active(b)) {
|
2014-12-22 16:28:17 +01:00
|
|
|
|
return EBUILDINGREQ;
|
2015-11-02 14:18:50 +01:00
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
if (type->skill != NOSKILL) {
|
|
|
|
|
int effsk;
|
|
|
|
|
int dm = get_effect(u, oldpotiontype[P_DOMORE]);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
assert(u->number);
|
2015-08-27 16:16:55 +02:00
|
|
|
|
basesk = effskill(u, type->skill, 0);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
if (basesk == 0)
|
|
|
|
|
return ENEEDSKILL;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
effsk = basesk;
|
2015-11-02 14:18:50 +01:00
|
|
|
|
if (building_is_active(u->building) && inside_building(u)) {
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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 */
|
2017-01-10 16:31:05 +01:00
|
|
|
|
dm = MIN(dm, u->number);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
change_effect(u, oldpotiontype[P_DOMORE], -dm);
|
|
|
|
|
skills += dm * effsk;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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 */
|
2015-10-30 12:37:27 +01:00
|
|
|
|
while (type && type->improvement &&
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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...
|
2016-11-23 19:19:04 +01:00
|
|
|
|
* (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.
|
2014-12-22 16:28:17 +01:00
|
|
|
|
*/
|
2016-07-31 18:48:27 +02:00
|
|
|
|
if (type->maxsize > 0) {
|
2014-12-22 16:28:17 +01:00
|
|
|
|
completed = completed % type->maxsize;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
completed = 0;
|
|
|
|
|
assert(type->reqsize >= 1);
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
if (basesk < type->minskill) {
|
|
|
|
|
if (made == 0)
|
2015-11-06 21:04:35 +01:00
|
|
|
|
return ELOWSKILL;
|
|
|
|
|
else
|
|
|
|
|
break; /* not good enough to go on */
|
2014-12-22 16:28:17 +01:00
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
/* n = maximum buildable size */
|
|
|
|
|
if (type->minskill > 1) {
|
|
|
|
|
n = skills / type->minskill;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
n = skills;
|
|
|
|
|
}
|
|
|
|
|
/* Flinkfingerring wirkt nicht auf Mengenbegrenzte (magische)
|
|
|
|
|
* Talente */
|
|
|
|
|
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;
|
|
|
|
|
if (i > 0) {
|
2017-01-10 16:31:05 +01:00
|
|
|
|
int rings = MIN(u->number, i);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
n = n * ((roqf_factor() - 1) * rings + u->number) / u->number;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
if (want < n) n = want;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
if (type->maxsize > 0) {
|
2017-01-10 16:31:05 +01:00
|
|
|
|
n = MIN(type->maxsize - completed, n);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
if (type->improvement == NULL) {
|
|
|
|
|
want = n;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2015-11-02 14:18:50 +01:00
|
|
|
|
if (building_is_active(u->building) && inside_building(u)) {
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (n <= 0) {
|
|
|
|
|
if (made == 0)
|
|
|
|
|
return ENOMATERIALS;
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
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 */
|
2015-11-02 14:18:50 +01:00
|
|
|
|
if (building_is_active(u->building) && inside_building(u)) {
|
2014-12-22 16:28:17 +01:00
|
|
|
|
canuse = matmod(u->building->type->attribs, u, rtype, canuse);
|
2015-11-02 14:18:50 +01:00
|
|
|
|
}
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
made += n;
|
|
|
|
|
skills -= n * type->minskill;
|
|
|
|
|
want -= n;
|
|
|
|
|
completed = completed + n;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2014-12-22 16:28:17 +01:00
|
|
|
|
/* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
|
2017-01-10 16:31:05 +01:00
|
|
|
|
produceexp(u, ctype->skill, MIN(made, u->number));
|
2014-12-22 16:28:17 +01:00
|
|
|
|
|
|
|
|
|
return made;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
message *msg_materials_required(unit * u, order * ord,
|
2014-12-22 16:28:17 +01:00
|
|
|
|
const construction * ctype, int multi)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-12-22 16:28:17 +01:00
|
|
|
|
int c;
|
2015-10-14 20:41:42 +02:00
|
|
|
|
message *msg;
|
2014-12-22 16:28:17 +01:00
|
|
|
|
/* 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;
|
|
|
|
|
}
|
2015-10-14 20:41:42 +02:00
|
|
|
|
msg = msg_feedback(u, ord, "build_required", "required", reslist);
|
|
|
|
|
while (reslist) {
|
|
|
|
|
resource *res = reslist->next;
|
|
|
|
|
free(reslist);
|
|
|
|
|
reslist = res;
|
|
|
|
|
}
|
|
|
|
|
return msg;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
int maxbuild(const unit * u, const construction * cons)
|
2014-12-22 16:28:17 +01:00
|
|
|
|
/* calculate maximum size that can be built from available material */
|
|
|
|
|
/* !! ignores maximum objectsize and improvements... */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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
|
2017-01-10 16:31:05 +01:00
|
|
|
|
maximum = MIN(maximum, have / need);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
}
|
|
|
|
|
return maximum;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** old build routines */
|
|
|
|
|
|
2014-06-29 01:19:46 +02:00
|
|
|
|
int
|
|
|
|
|
build_building(unit * u, const building_type * btype, int id, int want, order * ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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);
|
2015-08-27 16:16:55 +02:00
|
|
|
|
if (effskill(u, SK_BUILDING, 0) == 0) {
|
2014-12-22 16:28:17 +01:00
|
|
|
|
cmistake(u, ord, 101, MSG_PRODUCE);
|
2014-06-29 01:19:46 +02:00
|
|
|
|
return 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-22 16:28:17 +01: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. */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
/* 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);
|
2014-06-29 01:19:46 +02:00
|
|
|
|
return 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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) {
|
2015-11-22 16:14:27 +01:00
|
|
|
|
bool rule_other = config_get_int("rules.build.other_buildings", 1) != 0;
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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;
|
2016-11-23 19:19:04 +01:00
|
|
|
|
}
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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. */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (b == NULL) {
|
2014-12-22 16:28:17 +01:00
|
|
|
|
/* build a new building */
|
|
|
|
|
b = new_building(btype, r, lang);
|
|
|
|
|
b->type = btype;
|
2016-08-21 20:12:28 +02:00
|
|
|
|
fset(b, BLD_MAINTAINED);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
|
|
|
|
|
/* Die Einheit befindet sich automatisch im Inneren der neuen Burg. */
|
|
|
|
|
if (u->number && leave(u, false)) {
|
|
|
|
|
u_set_building(u, b);
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
btname = LOC(lang, btype->_name);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
if (want - built <= 0) {
|
2016-11-23 19:19:04 +01:00
|
|
|
|
/* geb<65>ude fertig */
|
2014-12-22 16:28:17 +01:00
|
|
|
|
new_order = default_order(lang);
|
|
|
|
|
}
|
2015-11-07 23:34:51 +01:00
|
|
|
|
else if (want != INT_MAX && btname) {
|
2016-11-23 19:19:04 +01:00
|
|
|
|
/* reduzierte restgr<67><72>e */
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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) {
|
2016-11-23 19:19:04 +01:00
|
|
|
|
/* Neues Haus, Befehl mit Geb<65>udename */
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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);
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2014-12-22 16:28:17 +01:00
|
|
|
|
b->size += built;
|
2016-07-31 18:48:27 +02:00
|
|
|
|
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);
|
|
|
|
|
}
|
2014-12-22 16:28:17 +01:00
|
|
|
|
fset(b, BLD_EXPANDED);
|
|
|
|
|
|
|
|
|
|
update_lighthouse(b);
|
|
|
|
|
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("buildbuilding",
|
|
|
|
|
"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
|
|
|
|
{
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
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) {
|
2017-01-10 16:31:05 +01:00
|
|
|
|
int repair = MIN(sh->damage, can * DAMAGE_SCALE);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2015-08-27 16:59:39 +02:00
|
|
|
|
create_ship(unit * u, const struct ship_type *newtype, int want,
|
2014-12-22 16:28:17 +01:00
|
|
|
|
order * ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-12-22 16:28:17 +01:00
|
|
|
|
ship *sh;
|
|
|
|
|
int msize;
|
|
|
|
|
const construction *cons = newtype->construction;
|
|
|
|
|
order *new_order;
|
2015-08-27 16:59:39 +02:00
|
|
|
|
region * r = u->region;
|
2014-12-22 16:28:17 +01:00
|
|
|
|
|
2015-08-27 16:16:55 +02:00
|
|
|
|
if (!effskill(u, SK_SHIPBUILDING, 0)) {
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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 */
|
2015-08-27 16:16:55 +02:00
|
|
|
|
if (effskill(u, cons->skill, 0) < cons->minskill) {
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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)
|
2017-01-10 16:31:05 +01:00
|
|
|
|
want = MIN(want, msize);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-08-27 16:59:39 +02:00
|
|
|
|
void continue_ship(unit * u, int want)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2014-12-22 16:28:17 +01:00
|
|
|
|
const construction *cons;
|
|
|
|
|
ship *sh;
|
|
|
|
|
int msize;
|
2015-08-27 16:59:39 +02:00
|
|
|
|
region * r = u->region;
|
2014-12-22 16:28:17 +01:00
|
|
|
|
|
2015-08-27 16:16:55 +02:00
|
|
|
|
if (!effskill(u, SK_SHIPBUILDING, 0)) {
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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;
|
|
|
|
|
}
|
2015-08-27 16:16:55 +02:00
|
|
|
|
if (effskill(u, cons->skill, 0) < cons->minskill) {
|
2014-12-22 16:28:17 +01:00
|
|
|
|
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)
|
2017-01-10 16:31:05 +01:00
|
|
|
|
want = MIN(want, msize);
|
2014-12-22 16:28:17 +01:00
|
|
|
|
else
|
|
|
|
|
want = msize;
|
|
|
|
|
|
|
|
|
|
build_ship(u, sh, want);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2015-10-13 22:56:16 +02:00
|
|
|
|
void free_construction(struct construction *cons)
|
|
|
|
|
{
|
|
|
|
|
while (cons) {
|
|
|
|
|
construction *next = cons->improvement;
|
|
|
|
|
free(cons->materials);
|
|
|
|
|
free(cons);
|
|
|
|
|
cons = next;
|
|
|
|
|
}
|
|
|
|
|
}
|