server/src/common/kernel/building.c

601 lines
12 KiB
C
Raw Normal View History

2001-01-25 10:37:55 +01:00
/* vi: set ts=2:
*
*
* Eressea PB(E)M host Copyright (C) 1998-2003
2001-01-25 10:37:55 +01:00
* Christian Schlittchen (corwin@amber.kn-bremen.de)
* Katja Zedel (katze@felidae.kn-bremen.de)
* Henning Peters (faroul@beyond.kn-bremen.de)
* Enno Rehling (enno@eressea.de)
2001-01-25 10:37:55 +01:00
* Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
*
* This program may not be used, modified or distributed without
* prior permission by the authors of Eressea.
*/
#include <config.h>
2007-06-20 02:34:02 +02:00
#include <kernel/eressea.h>
2001-01-25 10:37:55 +01:00
#include "building.h"
/* kernel includes */
#include "item.h"
#include "curse.h" /* f<>r C_NOCOST */
#include "unit.h"
#include "region.h"
#include "skill.h"
#include "magic.h"
2001-01-25 10:37:55 +01:00
#include "save.h"
2007-06-20 02:34:02 +02:00
#include "version.h"
2001-01-25 10:37:55 +01:00
/* util includes */
#include <util/attrib.h>
#include <util/base36.h>
#include <util/event.h>
#include <util/functions.h>
#include <util/language.h>
2007-06-20 02:34:02 +02:00
#include <util/lists.h>
#include <util/log.h>
#include <util/resolve.h>
#include <util/storage.h>
#include <util/umlaut.h>
2001-01-25 10:37:55 +01:00
/* libc includes */
#include <assert.h>
#include <stdlib.h>
#include <string.h>
2005-10-23 13:27:55 +02:00
#include <limits.h>
2001-01-25 10:37:55 +01:00
/* attributes includes */
#include <attributes/matmod.h>
static const char * NULLSTRING = "(null)";
static void
lc_init(struct attrib *a)
{
a->data.v = calloc(1, sizeof(building_action));
}
static void
lc_done(struct attrib *a)
{
building_action * data = (building_action*)a->data.v;
if (data->fname) free(data->fname);
if (data->param) free(data->param);
free(data);
}
static void
lc_write(const struct attrib * a, struct storage * store)
{
building_action * data = (building_action*)a->data.v;
const char * fname = data->fname;
const char * fparam = data->param;
building * b = data->b;
write_building_reference(b, store);
store->w_tok(store, fname);
store->w_tok(store, fparam?fparam:NULLSTRING);
}
static int
lc_read(struct attrib * a, struct storage * store)
{
building_action * data = (building_action*)a->data.v;
int result = read_reference(&data->b, store, read_building_reference, resolve_building);
if (store->version<UNICODE_VERSION) {
data->fname = store->r_str(store);
} else {
data->fname = store->r_tok(store);
}
if (store->version>=BACTION_VERSION) {
char lbuf[256];
if (store->version<UNICODE_VERSION) {
store->r_str_buf(store, lbuf, sizeof(lbuf));
} else {
store->r_tok_buf(store, lbuf, sizeof(lbuf));
}
if (strcmp(lbuf, NULLSTRING)==0) data->param = NULL;
else data->param = strdup(lbuf);
} else {
data->param = strdup(NULLSTRING);
}
if (result==0 && !data->b) {
return AT_READ_FAIL;
}
return AT_READ_OK;
}
attrib_type at_building_action = {
"lcbuilding",
lc_init, lc_done,
NULL,
lc_write, lc_read
};
typedef struct building_typelist {
struct building_typelist * next;
building_type * type;
} building_typelist;
2001-01-25 10:37:55 +01:00
static building_typelist *buildingtypes;
building_type *
2001-01-25 10:37:55 +01:00
bt_find(const char* name)
{
const struct building_typelist * btl = buildingtypes;
while (btl && strcmp(btl->type->_name, name)) btl = btl->next;
2007-08-12 15:02:24 +02:00
if (btl==NULL) {
return NULL;
}
return btl->type;
2001-01-25 10:37:55 +01:00
}
2004-02-21 23:25:00 +01:00
void
bt_register(building_type * type)
2001-01-25 10:37:55 +01:00
{
struct building_typelist * btl = malloc(sizeof(building_type));
if (type->init) type->init(type);
2001-01-25 10:37:55 +01:00
btl->type = type;
btl->next = buildingtypes;
buildingtypes = btl;
}
int
buildingcapacity(const building * b)
{
if (b->type->capacity>=0) {
if (b->type->maxcapacity>=0) {
return min(b->type->maxcapacity, b->size * b->type->capacity);
}
2001-01-25 10:37:55 +01:00
return b->size * b->type->capacity;
}
if (b->size>=b->type->maxsize) {
if (b->type->maxcapacity>=0) {
return b->type->maxcapacity;
}
}
2001-01-25 10:37:55 +01:00
return 0;
}
attrib_type at_building_generic_type = {
"building_generic_type", NULL, NULL, NULL, a_writestring, a_readstring, ATF_UNIQUE
};
const char *
buildingtype(const building_type * btype, const building * b, int bsize)
2001-01-25 10:37:55 +01:00
{
const char * s = NULL;
static boolean init_generic = false;
static const struct building_type * bt_generic;
2001-01-25 10:37:55 +01:00
if (!init_generic) {
init_generic = true;
bt_generic = bt_find("generic");
}
if (btype == bt_generic) {
2001-01-25 10:37:55 +01:00
const attrib *a = a_find(b->attribs, &at_building_generic_type);
if (a) s = (const char*)a->data.v;
}
if (btype->name) s = btype->name(btype, bsize);
2001-01-25 10:37:55 +01:00
if (s==NULL) s = btype->_name;
return s;
}
#define BMAXHASH 7919
2001-01-25 10:37:55 +01:00
static building *buildhash[BMAXHASH];
void
bhash(building * b)
{
building *old = buildhash[b->no % BMAXHASH];
2001-01-25 10:37:55 +01:00
buildhash[b->no % BMAXHASH] = b;
b->nexthash = old;
2001-01-25 10:37:55 +01:00
}
void
bunhash(building * b)
{
building **show;
2001-01-25 10:37:55 +01:00
for (show = &buildhash[b->no % BMAXHASH]; *show; show = &(*show)->nexthash) {
if ((*show)->no == b->no)
break;
}
if (*show) {
assert(*show == b);
*show = (*show)->nexthash;
b->nexthash = 0;
}
2001-01-25 10:37:55 +01:00
}
static building *
bfindhash(int i)
{
building *old;
for (old = buildhash[i % BMAXHASH]; old; old = old->nexthash)
if (old->no == i)
return old;
return 0;
}
building *
findbuilding(int i)
{
return bfindhash(i);
}
/* ** old building types ** */
/** Building: Fortification */
enum {
B_SITE,
B_TRADEPOST,
2001-01-25 10:37:55 +01:00
B_FORTIFICATION,
B_TOWER,
B_CASTLE,
B_FORTRESS,
B_CITADEL,
MAXBUILDINGS
};
static int
sm_smithy(const unit * u, const region * r, skill_t sk, int value) /* skillmod */
{
if (sk==SK_WEAPONSMITH || sk==SK_ARMORER) {
if (u->region == r) return value + 1;
}
return value;
}
static int
mm_smithy(const unit * u, const resource_type * rtype, int value) /* material-mod */
{
if (rtype == oldresourcetype[R_IRON]) return value * 2;
return value;
}
static void
init_smithy(struct building_type * bt)
{
2004-01-24 12:04:59 +01:00
a_add(&bt->attribs, make_skillmod(NOSKILL, SMF_PRODUCTION, sm_smithy, 1.0, 0));
a_add(&bt->attribs, make_matmod(mm_smithy));
}
2002-03-31 14:04:46 +02:00
2001-01-25 10:37:55 +01:00
static const char *
castle_name(const struct building_type* btype, int bsize)
2001-01-25 10:37:55 +01:00
{
const char * fname[MAXBUILDINGS] = {
"site",
"tradepost",
2001-01-25 10:37:55 +01:00
"fortification",
"tower",
"castle",
"fortress",
"citadel" };
const construction * ctype;
2001-01-25 10:37:55 +01:00
int i = 0;
ctype = btype->construction;
while (ctype && ctype->maxsize != -1 && ctype->maxsize<=bsize) {
2001-01-25 10:37:55 +01:00
bsize-=ctype->maxsize;
ctype=ctype->improvement;
++i;
}
return fname[i];
2001-01-25 10:37:55 +01:00
}
2005-10-23 13:27:55 +02:00
#ifdef WDW_PYRAMID
2005-10-23 13:27:55 +02:00
static const char *
pyramid_name(const struct building_type* btype, int bsize)
{
2005-10-23 13:27:55 +02:00
static char p_name_buf[32];
int level=0;
const construction * ctype;
ctype = btype->construction;
2005-10-23 13:27:55 +02:00
while (ctype && ctype->maxsize != -1 && ctype->maxsize<=bsize) {
bsize-=ctype->maxsize;
ctype=ctype->improvement;
++level;
}
2005-10-23 13:27:55 +02:00
sprintf(p_name_buf, "pyramid%d", level);
return p_name_buf;
}
int
2005-10-23 13:27:55 +02:00
wdw_pyramid_level(const struct building *b)
{
2005-10-23 13:27:55 +02:00
const construction *ctype = b->type->construction;
int completed = b->size;
int level = 0;
while(ctype->improvement != NULL &&
ctype->improvement != ctype &&
ctype->maxsize > 0 &&
ctype->maxsize <= completed)
{
++level;
completed-=ctype->maxsize;
ctype = ctype->improvement;
}
2005-10-23 13:27:55 +02:00
return level;
}
#endif
2001-01-25 10:37:55 +01:00
/* for finding out what was meant by a particular building string */
static local_names * bnames;
const building_type *
findbuildingtype(const char * name, const struct locale * lang)
2001-01-25 10:37:55 +01:00
{
variant type;
2001-01-25 10:37:55 +01:00
local_names * bn = bnames;
while (bn) {
if (bn->lang==lang) break;
bn=bn->next;
}
if (!bn) {
struct building_typelist * btl = buildingtypes;
bn = calloc(sizeof(local_names), 1);
bn->next = bnames;
bn->lang = lang;
while (btl) {
const char * n = locale_string(lang, btl->type->_name);
type.v = (void*)btl->type;
addtoken(&bn->names, n, type);
2001-01-25 10:37:55 +01:00
btl=btl->next;
}
bnames = bn;
}
if (findtoken(&bn->names, name, &type)==E_TOK_NOMATCH) return NULL;
return (const building_type*)type.v;
2001-01-25 10:37:55 +01:00
}
2001-01-25 10:37:55 +01:00
void
register_buildings(void)
2001-01-25 10:37:55 +01:00
{
register_function((pf_generic)init_smithy, "init_smithy");
register_function((pf_generic)castle_name, "castle_name");
2005-10-23 13:27:55 +02:00
#ifdef WDW_PYRAMID
register_function((pf_generic)pyramid_name, "pyramid_name");
#endif
2001-01-25 10:37:55 +01:00
}
void
write_building_reference(const struct building * b, struct storage * store)
2001-01-25 10:37:55 +01:00
{
store->w_id(store, (b && b->region)?b->no:0);
2001-01-25 10:37:55 +01:00
}
2001-01-25 10:37:55 +01:00
int
resolve_building(variant id, void * address)
2001-01-25 10:37:55 +01:00
{
building * b = NULL;
if (id.i!=0) {
b = findbuilding(id.i);
if (b==NULL) {
return -1;
}
}
*(building**)address = b;
return 0;
}
variant
read_building_reference(struct storage * store)
{
variant result;
result.i = store->r_id(store);
return result;
2001-01-25 10:37:55 +01:00
}
void
free_buildinglist(building_list *blist)
{
while (blist) {
building_list * rl2 = blist->next;
free(blist);
blist = rl2;
}
}
void
add_buildinglist(building_list **blist, building *b)
{
building_list *rl2 = (building_list*)malloc(sizeof(building_list));
rl2->data = b;
rl2->next = *blist;
*blist = rl2;
}
2001-01-25 10:37:55 +01:00
building *
new_building(const struct building_type * btype, region * r, const struct locale * lang)
{
2007-06-27 03:03:46 +02:00
building *b = (building *) calloc(1, sizeof(building));
static boolean init_lighthouse = false;
static const struct building_type * bt_lighthouse = 0;
if (!init_lighthouse) {
bt_lighthouse = bt_find("lighthouse");
init_lighthouse = true;
}
2007-06-27 03:03:46 +02:00
b->no = newcontainerid();
bhash(b);
b->type = btype;
b->region = r;
addlist(&r->buildings, b);
if (b->type==bt_lighthouse) {
r->flags |= RF_LIGHTHOUSE;
}
2007-06-27 03:03:46 +02:00
{
const char * bname;
2007-06-27 03:03:46 +02:00
if (b->type->name==NULL) {
bname = LOC(lang, btype->_name);
} else {
bname = LOC(lang, buildingtype(btype, b, 0));
}
b->name = strdup(bname);
2007-06-27 03:03:46 +02:00
}
return b;
2001-01-25 10:37:55 +01:00
}
static building * deleted_buildings;
/** remove a building from the region.
* remove_building lets units leave the building
*/
2001-01-25 10:37:55 +01:00
void
remove_building(building ** blist, building * b)
2001-01-25 10:37:55 +01:00
{
2006-01-29 02:55:36 +01:00
unit *u;
direction_t d;
static const struct building_type * bt_caravan, * bt_dam, * bt_tunnel;
boolean init = false;
if (!init) {
init = true;
bt_caravan = bt_find("caravan");
bt_dam = bt_find("dam");
bt_tunnel = bt_find("tunnel");
}
2001-01-25 10:37:55 +01:00
assert(bfindhash(b->no));
handle_event(b->attribs, "destroy", b);
2006-01-29 02:55:36 +01:00
for (u=b->region->units; u; u=u->next) {
if (u->building == b) leave(b->region, u);
}
b->size = 0;
update_lighthouse(b);
bunhash(b);
/* Falls Karawanserei, Damm oder Tunnel einst<73>rzen, wird die schon
* gebaute Stra<EFBFBD>e zur H<EFBFBD>lfte vernichtet */
if (b->type == bt_caravan || b->type == bt_dam || b->type == bt_tunnel) {
region * r = b->region;
for (d=0;d!=MAXDIRECTIONS;++d) if (rroad(r, d) > 0) {
rsetroad(r, d, rroad(r, d) / 2);
}
}
2006-01-29 02:55:36 +01:00
/* Stattdessen nur aus Liste entfernen, aber im Speicher halten. */
while (*blist && *blist!=b) blist = &(*blist)->next;
*blist = b->next;
b->region = NULL;
b->next = deleted_buildings;
deleted_buildings = b;
}
void
free_building(building * b)
{
while (b->attribs) a_remove (&b->attribs, b->attribs);
free(b->name);
free(b->display);
free(b);
}
void
free_buildings(void)
{
while (deleted_buildings) {
building * b = deleted_buildings;
deleted_buildings = b->next;
}
2001-01-25 10:37:55 +01:00
}
2007-06-20 02:34:02 +02:00
extern struct attrib_type at_icastle;
2001-01-25 10:37:55 +01:00
int
buildingeffsize(const building * b, boolean img)
{
int i = b->size, n = 0;
const construction * cons;
static const struct building_type * bt_castle;
if (!bt_castle) bt_castle = bt_find("castle");
assert(bt_castle);
2001-01-25 10:37:55 +01:00
if (b==NULL) return 0;
if (b->type!=bt_castle) {
2001-01-25 10:37:55 +01:00
if (img) {
const attrib * a = a_find(b->attribs, &at_icastle);
if (!a || a->data.v != bt_castle) return 0;
2001-01-25 10:37:55 +01:00
} else return 0;
}
cons = bt_castle->construction;
2001-01-25 10:37:55 +01:00
assert(cons);
while (cons && cons->maxsize != -1 && i>=cons->maxsize) {
i-=cons->maxsize;
cons = cons->improvement;
++n;
}
return n;
2001-01-25 10:37:55 +01:00
}
const char *
write_buildingname(const building * b, char * ibuf, size_t size)
{
snprintf((char*)ibuf, size, "%s (%s)", b->name, itoa36(b->no));
ibuf[size-1] = 0;
return ibuf;
}
const char *
buildingname(const building * b)
{
typedef char name[OBJECTIDSIZE + 1];
static name idbuf[8];
static int nextbuf = 0;
char *ibuf = idbuf[(++nextbuf) % 8];
return write_buildingname(b, ibuf, sizeof(name));
}
2001-01-25 10:37:55 +01:00
unit *
buildingowner(const region * r, const building * b)
{
unit *u = NULL;
unit *first = NULL;
#ifndef BROKEN_OWNERS
2001-01-25 10:37:55 +01:00
assert(r == b->region);
#endif
2001-01-25 10:37:55 +01:00
/* Pr<50>fen ob Eigent<6E>mer am leben. */
for (u = r->units; u; u = u->next) {
if (u->building == b) {
if (!first && u->number > 0)
first = u;
if (fval(u, UFL_OWNER) && u->number > 0)
2001-01-25 10:37:55 +01:00
return u;
if (u->number == 0)
freset(u, UFL_OWNER);
2001-01-25 10:37:55 +01:00
}
}
/* Eigent<6E>mer tot oder kein Eigent<6E>mer vorhanden. Erste lebende Einheit
* nehmen. */
if (first)
fset(first, UFL_OWNER);
2001-01-25 10:37:55 +01:00
return first;
}