server/src/kernel/region.c

1609 lines
34 KiB
C
Raw Normal View History

/*
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 "region.h"
/* kernel includes */
#include "alliance.h"
#include "building.h"
#include "connection.h"
#include "curse.h"
#include "equipment.h"
#include "faction.h"
#include "item.h"
#include "message.h"
#include "plane.h"
#include "region.h"
#include "resources.h"
#include "save.h"
#include "ship.h"
#include "terrain.h"
#include "terrainid.h"
#include "unit.h"
#include "version.h"
/* util includes */
#include <util/attrib.h>
#include <util/goodies.h>
#include <util/lists.h>
#include <util/log.h>
#include <util/resolve.h>
#include <util/umlaut.h>
#include <util/language.h>
#include <util/rand.h>
#include <util/rng.h>
#include <util/storage.h>
#include <modules/autoseed.h>
/* libc includes */
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
extern int dice_rand(const char *s);
region *regions;
int
get_maxluxuries()
{
static int maxluxuries = -1;
if (maxluxuries==-1) {
const luxury_type * ltype;
maxluxuries = 0;
for (ltype = luxurytypes;ltype;ltype=ltype->next) ++maxluxuries;
}
return maxluxuries;
}
const int delta_x[MAXDIRECTIONS] =
{
-1, 0, 1, 1, 0, -1
};
const int delta_y[MAXDIRECTIONS] =
{
1, 1, 0, -1, -1, 0
};
static const direction_t back[MAXDIRECTIONS] =
{
D_SOUTHEAST,
D_SOUTHWEST,
D_WEST,
D_NORTHWEST,
D_NORTHEAST,
D_EAST,
};
direction_t
dir_invert(direction_t dir)
{
switch (dir) {
case D_PAUSE:
case D_SPECIAL:
return dir;
break;
default:
if (dir>=0 && dir<MAXDIRECTIONS) return back[dir];
}
assert(!"illegal direction");
return NODIRECTION;
}
const char *
write_regionname(const region * r, const faction * f, char * buffer, size_t size)
{
char * buf = (char *)buffer;
const struct locale * lang = f ? f->locale : 0;
if (r==NULL) {
strcpy(buf, "(null)");
} else {
plane * pl = rplane(r);
int nx = r->x, ny = r->y;
pnormalize(&nx, &ny, pl);
adjust_coordinates(f, &nx, &ny, pl, r);
snprintf(buf, size, "%s (%d,%d)", rname(r, lang), nx, ny);
}
buf[size-1] = 0;
return buffer;
}
const char *
regionname(const region * r, const faction * f)
{
static char buf[NAMESIZE];
return write_regionname(r, f, buf, sizeof(buf));
}
int
deathcount(const region * r)
{
attrib * a = a_find(r->attribs, &at_deathcount);
if (!a) return 0;
return a->data.i;
}
int
chaoscount(const region * r)
{
attrib * a = a_find(r->attribs, &at_chaoscount);
if (!a) return 0;
return a->data.i;
}
void
deathcounts (region * r, int fallen)
{
attrib * a;
static const curse_type * ctype = NULL;
if (fallen==0) return;
if (!ctype) ctype = ct_find("holyground");
if (ctype && curse_active(get_curse(r->attribs, ctype))) return;
a = a_find(r->attribs, &at_deathcount);
if (!a) a = a_add(&r->attribs, a_new(&at_deathcount));
a->data.i += fallen;
if (a->data.i<=0) a_remove(&r->attribs, a);
}
void
chaoscounts(region * r, int fallen)
{
attrib * a;
if (fallen==0) return;
a = a_find(r->attribs, &at_chaoscount);
if (!a) a = a_add(&r->attribs, a_new(&at_chaoscount));
a->data.i += fallen;
if (a->data.i<=0) a_remove(&r->attribs, a);
}
/********************/
/* at_direction */
/********************/
static void
a_initdirection(attrib *a)
{
a->data.v = calloc(1, sizeof(spec_direction));
}
static void
a_freedirection(attrib *a)
{
free(a->data.v);
}
static int
a_agedirection(attrib *a)
{
spec_direction *d = (spec_direction *)(a->data.v);
--d->duration;
return (d->duration>0)?AT_AGE_KEEP:AT_AGE_REMOVE;
}
typedef struct dir_lookup {
char * name;
const char * oldname;
struct dir_lookup * next;
} dir_lookup;
static dir_lookup * dir_name_lookup;
void
register_special_direction(const char * name)
{
struct locale * lang;
char * str = strdup(name);
for (lang=locales;lang;lang=nextlocale(lang)) {
tnode * tokens = get_translations(lang, UT_SPECDIR);
const char * token = LOC(lang, name);
if (token) {
variant var;
var.v = str;
addtoken(tokens, token, var);
if (lang==default_locale) {
dir_lookup * dl = malloc(sizeof(dir_lookup));
dl->name = str;
dl->oldname = token;
dl->next = dir_name_lookup;
dir_name_lookup = dl;
}
} else {
log_error(("no translation for spec_direction '%s' in locale '%s'\n",
name, locale_name(lang)));
}
}
}
static int
a_readdirection(attrib *a, void * owner, struct storage * store)
{
spec_direction *d = (spec_direction *)(a->data.v);
d->x = (short)store->r_int(store);
d->y = (short)store->r_int(store);
d->duration = store->r_int(store);
if (store->version<UNICODE_VERSION) {
char lbuf[16];
dir_lookup * dl = dir_name_lookup;
store->r_tok_buf(store, NULL, 0);
store->r_tok_buf(store, lbuf, sizeof(lbuf));
cstring_i(lbuf);
for (;dl;dl=dl->next) {
if (strcmp(lbuf, dl->oldname)==0) {
d->keyword=strdup(dl->name);
sprintf(lbuf, "%s_desc", d->keyword);
d->desc=strdup(dl->name);
break;
}
}
if (dl==NULL) {
log_error(("unknown spec_direction '%s'\n", lbuf));
assert(!"not implemented");
}
}
else {
d->desc = store->r_tok(store);
d->keyword = store->r_tok(store);
}
d->active = true;
return AT_READ_OK;
}
static void
a_writedirection(const attrib * a, const void * owner, struct storage * store)
{
spec_direction *d = (spec_direction *)(a->data.v);
store->w_int(store, d->x);
store->w_int(store, d->y);
store->w_int(store, d->duration);
store->w_tok(store, d->desc);
store->w_tok(store, d->keyword);
}
attrib_type at_direction = {
"direction",
a_initdirection,
a_freedirection,
a_agedirection,
a_writedirection,
a_readdirection
};
region *
find_special_direction(const region *r, const char *token, const struct locale * lang)
{
attrib *a;
spec_direction *d;
if (strlen(token)==0) return NULL;
for (a = a_find(r->attribs, &at_direction);a && a->type==&at_direction;a=a->next) {
d = (spec_direction *)(a->data.v);
if (d->active) {
tnode * tokens = get_translations(lang, UT_SPECDIR);
variant var;
if (findtoken(tokens, token, &var)==E_TOK_SUCCESS) {
if (strcmp((const char *)var.v, d->keyword)==0) {
return findregion(d->x, d->y);
}
}
}
}
return NULL;
}
attrib *
create_special_direction(region *r, region * rt, int duration,
const char *desc, const char *keyword)
{
attrib *a = a_add(&r->attribs, a_new(&at_direction));
spec_direction *d = (spec_direction *)(a->data.v);
d->active = false;
d->x = rt->x;
d->y = rt->y;
d->duration = duration;
d->desc = strdup(desc);
d->keyword = strdup(keyword);
return a;
}
/* Moveblock wird zur Zeit nicht <20>ber Attribute, sondern ein Bitfeld
r->moveblock gemacht. Sollte umgestellt werden, wenn kompliziertere
Dinge gefragt werden. */
/********************/
/* at_moveblock */
/********************/
void
a_initmoveblock(attrib *a)
{
a->data.v = calloc(1, sizeof(moveblock));
}
int
a_readmoveblock(attrib *a, void * owner, struct storage * store)
{
moveblock *m = (moveblock *)(a->data.v);
int i;
i = store->r_int(store);
m->dir = (direction_t)i;
return AT_READ_OK;
}
void
a_writemoveblock(const attrib * a, const void * owner, struct storage * store)
{
moveblock *m = (moveblock *)(a->data.v);
store->w_int(store, (int)m->dir);
}
attrib_type at_moveblock = {
"moveblock", a_initmoveblock, NULL, NULL, a_writemoveblock, a_readmoveblock
};
#define coor_hashkey(x, y) (unsigned int)((x<<16) + y)
#define RMAXHASH MAXREGIONS
static region * regionhash[RMAXHASH];
static int dummy_data;
static region * dummy_ptr = (region*)&dummy_data; /* a funny hack */
typedef struct uidhashentry {
unsigned int uid;
region * r;
} uidhashentry;
static uidhashentry uidhash[MAXREGIONS];
struct region *
findregionbyid(unsigned int uid)
{
int key = uid % MAXREGIONS;
while (uidhash[key].uid!=0 && uidhash[key].uid!=uid) ++key;
return uidhash[key].r;
}
#define DELMARKER dummy_ptr
static void
unhash_uid(region * r)
{
int key = r->uid % MAXREGIONS;
assert(r->uid);
while (uidhash[key].uid!=0 && uidhash[key].uid!=r->uid) ++key;
assert(uidhash[key].r==r);
uidhash[key].r = NULL;
}
static void
hash_uid(region * r)
{
unsigned int uid = r->uid;
for (;;) {
if (uid!=0) {
int key = uid % MAXREGIONS;
while (uidhash[key].uid!=0 && uidhash[key].uid!=uid) ++key;
if (uidhash[key].uid==0) {
uidhash[key].uid = uid;
uidhash[key].r = r;
break;
}
assert(uidhash[key].r!=r || !"duplicate registration");
}
r->uid = uid = rng_int();
}
}
#define HASH_STATISTICS 1
#if HASH_STATISTICS
static int hash_requests;
static int hash_misses;
#endif
boolean pnormalize(int * x, int * y, const plane * pl)
{
if (pl) {
if (x) {
int width = pl->maxx - pl->minx + 1;
int nx = *x - pl->minx;
nx = (nx>0)?nx:(width-(-nx)%width);
*x = nx % width+ pl->minx;
}
if (y) {
int height = pl->maxy - pl->miny + 1;
int ny = *y - pl->miny;
ny = (ny>0)?ny:(height-(-ny)%height);
*y = ny % height + pl->miny;
}
}
return false; /* TBD */
}
static region *
rfindhash(int x, int y)
{
unsigned int rid;
rid = coor_hashkey(x, y);
#if HASH_STATISTICS
++hash_requests;
#endif
if (rid>=0) {
int key = HASH1(rid, RMAXHASH), gk = HASH2(rid, RMAXHASH);
while (regionhash[key]!=NULL && (regionhash[key]==DELMARKER || regionhash[key]->x!=x || regionhash[key]->y!=y)) {
key = (key + gk) % RMAXHASH;
#if HASH_STATISTICS
++hash_misses;
#endif
}
return regionhash[key];
}
return NULL;
}
void
rhash(region * r)
{
unsigned int rid = coor_hashkey(r->x, r->y);
int key = HASH1(rid, RMAXHASH), gk = HASH2(rid, RMAXHASH);
while (regionhash[key]!=NULL && regionhash[key]!=DELMARKER && regionhash[key]!=r) {
key = (key + gk) % RMAXHASH;
}
assert(regionhash[key]!=r || !"trying to add the same region twice");
regionhash[key] = r;
}
void
runhash(region * r)
{
unsigned int rid = coor_hashkey(r->x, r->y);
int key = HASH1(rid, RMAXHASH), gk = HASH2(rid, RMAXHASH);
#ifdef FAST_CONNECT
int d, di;
for (d=0,di=MAXDIRECTIONS/2;d!=MAXDIRECTIONS;++d,++di) {
region * rc = r->connect[d];
if (rc!=NULL) {
if (di>=MAXDIRECTIONS) di-=MAXDIRECTIONS;
rc->connect[di] = NULL;
r->connect[d] = NULL;
}
}
#endif
while (regionhash[key]!=NULL && regionhash[key]!=r) {
key = (key + gk) % RMAXHASH;
}
assert(regionhash[key]==r || !"trying to remove a unit that is not hashed");
regionhash[key] = DELMARKER;
}
region *
r_connect(const region * r, direction_t dir)
{
region * result;
int x, y;
#ifdef FAST_CONNECT
region * rmodify = (region*)r;
assert (dir>=0 && dir<MAXDIRECTIONS);
if (r->connect[dir]) return r->connect[dir];
#endif
assert(dir<MAXDIRECTIONS);
x = r->x + delta_x[dir];
y = r->y + delta_y[dir];
pnormalize(&x, &y, rplane(r));
result = rfindhash(x, y);
#ifdef FAST_CONNECT
if (result) {
rmodify->connect[dir] = result;
result->connect[back[dir]] = rmodify;
}
#endif
return result;
}
region *
findregion(int x, int y)
{
return rfindhash(x, y);
}
/* Contributed by Hubert Mackenberg. Thanks.
* x und y Abstand zwischen x1 und x2 berechnen
*/
static int
koor_distance_orig(int x1, int y1, int x2, int y2)
{
int dx = x1 - x2;
int dy = y1 - y2;
/* Bei negativem dy am Ursprung spiegeln, das veraendert
* den Abstand nicht
*/
if ( dy < 0 ) {
dy = -dy;
dx = -dx;
}
/*
* dy ist jetzt >=0, fuer dx sind 3 Faelle zu untescheiden
*/
if ( dx >= 0 ) {
int result = dx + dy;
return result;
}
else if (-dx >= dy) {
int result = -dx;
return result;
}
else {
return dy;
}
}
static int
koor_distance_wrap_xy(int x1, int y1, int x2, int y2, int width, int height)
{
int dx = x1 - x2;
int dy = y1 - y2;
int result, dist;
int mindist = MIN(width, height) >> 1;
/* Bei negativem dy am Ursprung spiegeln, das veraendert
* den Abstand nicht
*/
if ( dy < 0 ) {
dy = -dy;
dx = -dx;
}
if (dx<0) {
dx = width + dx;
}
/* dx,dy is now pointing northeast */
result = dx + dy;
if (result<=mindist) return result;
dist = (width-dx) + (height-dy); /* southwest */
if (dist>=0 && dist<result) {
result = dist;
if (result<=mindist) return result;
}
dist = MAX(dx, height-dy);
if (dist>=0 && dist<result) {
result = dist;
if (result<=mindist) return result;
}
dist = MAX(width-dx, dy);
if (dist>=0 && dist<result) result = dist;
return result;
}
int
koor_distance(int x1, int y1, int x2, int y2)
{
const plane * p1 = findplane(x1, y1);
const plane * p2 = findplane(x2, y2);
if (p1!=p2) return INT_MAX;
else {
int width = plane_width(p1);
int height = plane_height(p1);
if (width && height) {
return koor_distance_wrap_xy(x1, y1, x2, y2, width, height);
} else {
return koor_distance_orig(x1, y1, x2, y2);
}
}
}
int
distance(const region * r1, const region * r2)
{
return koor_distance(r1->x, r1->y, r2->x, r2->y);
}
static direction_t
koor_reldirection(int ax, int ay, int bx, int by, const struct plane * pl)
{
direction_t dir;
for (dir=0;dir!=MAXDIRECTIONS;++dir) {
int x = ax + delta_x[dir];
int y = ay + delta_y[dir];
pnormalize(&x, &y, pl);
if (bx == x && by == y) return dir;
}
return NODIRECTION;
}
spec_direction *
special_direction(const region * from, const region * to)
{
const attrib *a = a_findc(from->attribs, &at_direction);
while (a!=NULL && a->type==&at_direction) {
spec_direction * sd = (spec_direction *)a->data.v;
if (sd->x==to->x && sd->y==to->y) return sd;
a = a->next;
}
return NULL;
}
direction_t
reldirection(const region * from, const region * to)
{
plane * pl = rplane(from);
if (pl == rplane(to)) {
direction_t dir = koor_reldirection(from->x, from->y, to->x, to->y, pl);
if (dir==NODIRECTION) {
spec_direction *sd = special_direction(from, to);
if (sd!=NULL && sd->active) return D_SPECIAL;
}
return dir;
}
return NODIRECTION;
}
void
free_regionlist(region_list *rl)
{
while (rl) {
region_list * rl2 = rl->next;
free(rl);
rl = rl2;
}
}
void
add_regionlist(region_list **rl, region *r)
{
region_list *rl2 = (region_list*)malloc(sizeof(region_list));
rl2->data = r;
rl2->next = *rl;
*rl = rl2;
}
/********************/
/* at_horseluck */
/********************/
attrib_type at_horseluck = {
"horseluck",
DEFAULT_INIT,
DEFAULT_FINALIZE,
DEFAULT_AGE,
NO_WRITE,
NO_READ,
ATF_UNIQUE
};
/**********************/
/* at_peasantluck */
/**********************/
attrib_type at_peasantluck = {
"peasantluck",
DEFAULT_INIT,
DEFAULT_FINALIZE,
DEFAULT_AGE,
NO_WRITE,
NO_READ,
ATF_UNIQUE
};
/*********************/
/* at_chaoscount */
/*********************/
attrib_type at_chaoscount = {
"chaoscount",
DEFAULT_INIT,
DEFAULT_FINALIZE,
DEFAULT_AGE,
a_writeint,
a_readint,
ATF_UNIQUE
};
/*********************/
/* at_deathcount */
/*********************/
attrib_type at_deathcount = {
"deathcount",
DEFAULT_INIT,
DEFAULT_FINALIZE,
DEFAULT_AGE,
a_writeint,
a_readint,
ATF_UNIQUE
};
/*********************/
/* at_woodcount */
/*********************/
attrib_type at_woodcount = {
"woodcount",
DEFAULT_INIT,
DEFAULT_FINALIZE,
DEFAULT_AGE,
NO_WRITE,
a_readint,
ATF_UNIQUE
};
/*********************/
/* at_travelunit */
/*********************/
attrib_type at_travelunit = {
"travelunit",
DEFAULT_INIT,
DEFAULT_FINALIZE,
DEFAULT_AGE,
NO_WRITE,
NO_READ
};
void
rsetroad(region * r, direction_t d, short val)
{
connection * b;
region * r2 = rconnect(r, d);
if (!r2) return;
b = get_borders(r, r2);
while (b && b->type!=&bt_road) b = b->next;
if (!b) b = new_border(&bt_road, r, r2);
if (r==b->from) b->data.sa[0] = val;
else b->data.sa[1] = val;
}
short
rroad(const region * r, direction_t d)
{
int rval;
connection * b;
region * r2 = rconnect(r, d);
if (!r2) return 0;
b = get_borders(r, r2);
while (b && b->type!=&bt_road) b = b->next;
if (!b) return 0;
rval = b->data.i;
if (r==b->from) return b->data.sa[0];
return b->data.sa[1];
}
boolean
r_isforest(const region * r)
{
if (fval(r->terrain, FOREST_REGION)) {
/* needs to be covered with at leas 48% trees */
int mincover = (int)(r->terrain->size * 0.48);
int trees = rtrees(r, 2) + rtrees(r, 1);
return (trees*TREESIZE >= mincover);
}
return false;
}
int
is_coastregion(region *r)
{
direction_t i;
int res = 0;
for(i=0;i<MAXDIRECTIONS;i++) {
region * rn = rconnect(r,i);
if (rn && fval(rn->terrain, SEA_REGION)) res++;
}
return res;
}
int
rpeasants(const region * r)
{
return ((r)->land?(r)->land->peasants:0);
}
void
rsetpeasants(region * r, int value)
{
((r)->land?((r)->land->peasants=(value)):(assert((value)>=0), (value)),0);
}
int
rmoney(const region * r)
{
return ((r)->land?(r)->land->money:0);
}
void
rsethorses(const region *r, int value)
{
assert(value >= 0);
if(r->land)
r->land->horses = value;
}
int
rhorses(const region *r)
{
return r->land?r->land->horses:0;
}
void
rsetmoney(region * r, int value)
{
((r)->land?((r)->land->money=(value)):(assert((value)>=0), (value)),0);
}
void
r_setdemand(region * r, const luxury_type * ltype, int value)
{
struct demand * d, ** dp = &r->land->demands;
if (ltype==NULL) return;
while (*dp && (*dp)->type != ltype) dp = &(*dp)->next;
d = *dp;
if (!d) {
d = *dp = malloc(sizeof(struct demand));
d->next = NULL;
d->type = ltype;
}
d->value = value;
}
const item_type *
r_luxury(region * r)
{
struct demand * dmd;
if (r->land) {
if (!r->land->demands) {
fix_demand(r);
}
for (dmd=r->land->demands;dmd;dmd=dmd->next) {
if (dmd->value==0) return dmd->type->itype;
}
}
return NULL;
}
int
r_demand(const region * r, const luxury_type * ltype)
{
struct demand * d = r->land->demands;
while (d && d->type != ltype) d = d->next;
if (!d) return -1;
return d->value;
}
const char *
rname(const region * r, const struct locale * lang)
{
if (r->land) {
return r->land->name;
}
return LOC(lang, terrain_name(r));
}
int
rtrees(const region *r, int ageclass)
{
return ((r)->land?(r)->land->trees[ageclass]:0);
}
int
rsettrees(const region *r, int ageclass, int value)
{
if (!r->land) assert(value==0);
else {
assert(value>=0);
return r->land->trees[ageclass]=value;
}
return 0;
}
static region *last;
static unsigned int max_index = 0;
region *
new_region(int x, int y, struct plane * pl, unsigned int uid)
{
region *r;
pnormalize(&x, &y, pl);
r = rfindhash(x, y);
if (r) {
log_error(("duplicate region discovered: %s(%d,%d)\n", regionname(r, NULL), x, y));
if (r->units)
log_error(("duplicate region contains units\n"));
return r;
}
r = calloc(1, sizeof(region));
r->x = x;
r->y = y;
r->uid = uid;
r->age = 1;
r->_plane = pl;
rhash(r);
hash_uid(r);
if (last)
addlist(&last, r);
else
addlist(&regions, r);
last = r;
assert(r->next==NULL);
r->index = ++max_index;
return r;
}
static region * deleted_regions;
void
remove_region(region ** rlist, region * r)
{
while (r->units) {
unit * u = r->units;
i_freeall(&u->items);
remove_unit(&r->units, u);
}
runhash(r);
unhash_uid(r);
while (*rlist && *rlist!=r) rlist=&(*rlist)->next;
assert(*rlist==r);
*rlist = r->next;
r->next = deleted_regions;
deleted_regions = r;
}
static void
freeland(land_region * lr)
{
while (lr->demands) {
struct demand * d = lr->demands;
lr->demands = d->next;
free(d);
}
if (lr->name) free(lr->name);
free(lr);
}
void
region_setresource(region * r, const resource_type * rtype, int value)
{
rawmaterial * rm = r->resources;
while (rm) {
if (rm->type->rtype==rtype) {
rm->amount = value;
break;
}
rm=rm->next;
}
if (!rm) {
if (rtype==rt_find("money")) rsetmoney(r, value);
else if (rtype==rt_find("peasant")) rsetpeasants(r, value);
else if (rtype==rt_find("horse")) rsethorses(r, value);
}
}
int
region_getresource(const region * r, const resource_type * rtype)
{
const rawmaterial * rm;
for (rm=r->resources;rm;rm=rm->next) {
if (rm->type->rtype==rtype) {
return rm->amount;
}
}
if (rtype==rt_find("money")) return rmoney(r);
if (rtype==rt_find("horse")) return rhorses(r);
if (rtype==rt_find("peasant")) return rpeasants(r);
return 0;
}
void
free_region(region * r)
{
if (last == r) last = NULL;
free(r->display);
if (r->land) freeland(r->land);
if (r->msgs) {
free_messagelist(r->msgs);
r->msgs = 0;
}
while (r->individual_messages) {
struct individual_message * msg = r->individual_messages;
r->individual_messages = msg->next;
if (msg->msgs) free_messagelist(msg->msgs);
free(msg);
}
while (r->attribs) a_remove (&r->attribs, r->attribs);
while (r->resources) {
rawmaterial * res = r->resources;
r->resources = res->next;
free(res);
}
while (r->donations) {
donation * don = r->donations;
r->donations = don->next;
free(don);
}
while (r->units) {
unit * u = r->units;
r->units = u->next;
uunhash(u);
free_unit(u);
free(u);
}
while (r->buildings) {
building * b = r->buildings;
assert(b->region==r);
r->buildings = b->next;
bunhash(b); /* must be done here, because remove_building does it, and wasn't called */
free_building(b);
}
while (r->ships) {
ship * s = r->ships;
assert(s->region==r);
r->ships = s->next;
sunhash(s);
free_ship(s);
}
free(r);
}
void
free_regions(void)
{
memset(uidhash, 0, sizeof(uidhash));
while (deleted_regions) {
region * r = deleted_regions;
deleted_regions = r->next;
free_region(r);
}
while (regions) {
region * r = regions;
regions = r->next;
runhash(r);
free_region(r);
}
max_index = 0;
last = NULL;
}
/** creates a name for a region
* TODO: Make vowels XML-configurable and allow non-ascii characters again.
* - that will probably require a wchar_t * string to pick from.
*/
static char *
makename(void)
{
int s, v, k, e, p = 0, x = 0;
size_t nk, ne, nv, ns;
static char name[16];
const char *kons = "bcdfghklmnprstvwz",
*start = "bcdgtskpvfr",
*end = "nlrdst",
*vowels = "aaaaaaaaaaaeeeeeeeeeeeeiiiiiiiiiiioooooooooooouuuuuuuuuuyy";
/* const char * vowels_latin1 = "aaaaaaaaa<61><61>eeeeeeeee<65><65><EFBFBD>iiiiiiiii<69><69>ooooooooo<6F><6F><EFBFBD>uuuuuuuuu<75>yy"; */
nk = strlen(kons);
ne = strlen(end);
nv = strlen(vowels);
ns = strlen(start);
for (s = rng_int() % 3 + 2; s > 0; s--) {
if (x > 0) {
k = rng_int() % (int)nk;
name[p] = kons[k];
p++;
} else {
k = rng_int() % (int)ns;
name[p] = start[k];
p++;
}
v = rng_int() % (int)nv;
name[p] = vowels[v];
p++;
if (rng_int() % 3 == 2 || s == 1) {
e = rng_int() % (int)ne;
name[p] = end[e];
p++;
x = 1;
} else
x = 0;
}
name[p] = '\0';
name[0] = (char) toupper(name[0]);
return name;
}
void
setluxuries(region * r, const luxury_type * sale)
{
const luxury_type * ltype;
assert(r->land);
if(r->land->demands) freelist(r->land->demands);
for (ltype=luxurytypes; ltype; ltype=ltype->next) {
struct demand * dmd = malloc(sizeof(struct demand));
dmd->type = ltype;
if (ltype!=sale) dmd->value = 1 + rng_int() % 5;
else dmd->value = 0;
dmd->next = r->land->demands;
r->land->demands = dmd;
}
}
void
terraform_region(region * r, const terrain_type * terrain)
{
/* Resourcen, die nicht mehr vorkommen k<>nnen, l<>schen */
const terrain_type * oldterrain = r->terrain;
rawmaterial **lrm = &r->resources;
assert(terrain);
while (*lrm) {
rawmaterial *rm = *lrm;
const resource_type * rtype = NULL;
if (terrain->production!=NULL) {
int i;
for (i=0;terrain->production[i].type;++i) {
if (rm->type->rtype == terrain->production[i].type) {
rtype = rm->type->rtype;
break;
}
}
}
if (rtype==NULL) {
*lrm = rm->next;
free(rm);
} else {
lrm = &rm->next;
}
}
r->terrain = terrain;
terraform_resources(r);
if (!fval(terrain, LAND_REGION)) {
region_setinfo(r, NULL);
if (r->land!=NULL) {
i_freeall(&r->land->items);
freeland(r->land);
r->land = NULL;
}
rsettrees(r, 0, 0);
rsettrees(r, 1, 0);
rsettrees(r, 2, 0);
rsethorses(r, 0);
rsetpeasants(r, 0);
rsetmoney(r, 0);
freset(r, RF_ENCOUNTER);
freset(r, RF_MALLORN);
/* Beschreibung und Namen l<>schen */
return;
}
if (r->land) {
i_freeall(&r->land->items);
} else {
static struct surround {
struct surround * next;
const luxury_type * type;
int value;
} *trash =NULL, *nb = NULL;
const luxury_type * ltype = NULL;
direction_t d;
int mnr = 0;
r->land = calloc(1, sizeof(land_region));
r->land->ownership = NULL;
region_set_morale(r, MORALE_DEFAULT, -1);
region_setname(r, makename());
for (d=0;d!=MAXDIRECTIONS;++d) {
region * nr = rconnect(r, d);
if (nr && nr->land) {
struct demand * sale = r->land->demands;
while (sale && sale->value!=0) sale=sale->next;
if (sale) {
struct surround * sr = nb;
while (sr && sr->type!=sale->type) sr=sr->next;
if (!sr) {
if (trash) {
sr = trash;
trash = trash->next;
} else {
sr = calloc(1, sizeof(struct surround));
}
sr->next = nb;
sr->type = sale->type;
sr->value = 1;
nb = sr;
} else sr->value++;
++mnr;
}
}
}
if (!nb) {
int i = get_maxluxuries();
if (i>0) {
i = rng_int() % i;
ltype = luxurytypes;
while (i--) ltype=ltype->next;
}
} else {
int i = rng_int() % mnr;
struct surround * srd = nb;
while (i>srd->value) {
i-=srd->value;
srd=srd->next;
}
if (srd->type) setluxuries(r, srd->type);
while (srd->next!=NULL) srd=srd->next;
srd->next=trash;
trash = nb;
nb = NULL;
}
}
if (fval(terrain, LAND_REGION)) {
const item_type * itype = NULL;
char equip_hash[64];
/* TODO: put the equipment in struct terrain, faster */
sprintf(equip_hash, "terrain_%s", terrain->_name);
equip_items(&r->land->items, get_equipment(equip_hash));
if (r->terrain->herbs) {
int len=0;
while (r->terrain->herbs[len]) ++len;
if (len) itype = r->terrain->herbs[rng_int()%len];
}
if (itype!=NULL) {
rsetherbtype(r, itype);
rsetherbs(r, (short)(50+rng_int()%31));
}
else {
rsetherbtype(r, NULL);
}
if (oldterrain==NULL || !fval(oldterrain, LAND_REGION)) {
if (rng_int() % 100 < 3) fset(r, RF_MALLORN);
else freset(r, RF_MALLORN);
if (rng_int() % 100 < ENCCHANCE) {
fset(r, RF_ENCOUNTER);
}
}
}
if (oldterrain==NULL || terrain->size!=oldterrain->size) {
if (terrain==newterrain(T_PLAIN)) {
rsethorses(r, rng_int() % (terrain->size / 50));
if(rng_int()%100 < 40) {
rsettrees(r, 2, terrain->size * (30+rng_int()%40)/1000);
}
} else if (chance(0.2)) {
rsettrees(r, 2, terrain->size * (30 + rng_int() % 40) / 1000);
} else {
rsettrees(r, 2, 0);
}
rsettrees(r, 1, rtrees(r, 2)/4);
rsettrees(r, 0, rtrees(r, 2)/8);
if (!fval(r, RF_CHAOTIC)) {
int peasants;
peasants = (maxworkingpeasants(r) * (20+dice_rand("6d10")))/100;
rsetpeasants(r, MAX(100, peasants));
rsetmoney(r, rpeasants(r) * ((wage(r, NULL, NULL, INT_MAX)+1) + rng_int() % 5));
}
}
}
/** ENNO:
* ich denke, das das hier nicht sein sollte.
* statt dessen sollte ein attribut an der region sein, das das erledigt,
* egal ob durch den spell oder anderes angelegt.
**/
#include "curse.h"
int
production(const region *r)
{
/* mu<6D> rterrain(r) sein, nicht rterrain() wegen rekursion */
int p = r->terrain->size / MAXPEASANTS_PER_AREA;
if (curse_active(get_curse(r->attribs, ct_find("drought")))) p /= 2;
return p;
}
int
resolve_region_coor(variant id, void * address) {
region * r = findregion(id.sa[0], id.sa[1]);
if (r) {
*(region**)address = r;
return 0;
}
*(region**)address = NULL;
return -1;
}
int
resolve_region_id(variant id, void * address)
{
region * r = NULL;
if (id.i!=0) {
r = findregionbyid((unsigned int)id.i);
if (r==NULL) {
*(region**)address = NULL;
return -1;
}
}
*(region**)address = r;
return 0;
}
variant
read_region_reference(struct storage * store)
{
variant result;
if (store->version<UIDHASH_VERSION) {
result.sa[0] = (short)store->r_int(store);
result.sa[1] = (short)store->r_int(store);
} else {
result.i = store->r_int(store);
}
return result;
}
void
write_region_reference(const region * r, struct storage * store)
{
if (r) {
store->w_int(store, r->uid);
} else {
store->w_int(store, 0);
}
}
struct message_list *
r_getmessages(const struct region * r, const struct faction * viewer)
{
struct individual_message * imsg = r->individual_messages;
while (imsg && (imsg)->viewer!=viewer) imsg = imsg->next;
if (imsg) return imsg->msgs;
return NULL;
}
struct message *
r_addmessage(struct region * r, const struct faction * viewer, struct message * msg)
{
assert(r);
if (viewer) {
struct individual_message * imsg;
imsg = r->individual_messages;
while (imsg && imsg->viewer!=viewer) imsg = imsg->next;
if (imsg==NULL) {
imsg = malloc(sizeof(struct individual_message));
imsg->next = r->individual_messages;
imsg->msgs = NULL;
r->individual_messages = imsg;
imsg->viewer = viewer;
}
return add_message(&imsg->msgs, msg);
}
return add_message(&r->msgs, msg);
}
struct faction *
region_get_owner(const struct region * r)
{
assert(rule_region_owners());
if (r->land && r->land->ownership) {
return r->land->ownership->owner;
}
return NULL;
}
struct alliance *
region_get_alliance(const struct region * r)
{
assert(rule_region_owners());
if (r->land && r->land->ownership) {
region_owner * own = r->land->ownership;
return own->owner?own->owner->alliance:own->alliance;
}
return NULL;
}
void
region_set_owner(struct region * r, struct faction * owner, int turn)
{
assert(rule_region_owners());
if (r->land) {
if (!r->land->ownership) {
r->land->ownership = malloc(sizeof(region_owner));
assert(region_get_morale(r)==MORALE_DEFAULT);
r->land->ownership->owner = NULL;
r->land->ownership->alliance = NULL;
r->land->ownership->flags = 0;
}
r->land->ownership->since_turn = turn;
r->land->ownership->morale_turn = turn;
assert(r->land->ownership->owner != owner);
r->land->ownership->owner = owner;
if (owner) {
r->land->ownership->alliance = owner->alliance;
}
}
}
faction * update_owners(region * r)
{
faction * f = NULL;
assert(rule_region_owners());
if (r->land) {
building * bowner = largestbuilding(r, &cmp_current_owner, false);
building * blargest = largestbuilding(r, &cmp_taxes, false);
if (blargest) {
if (!bowner || bowner->size<blargest->size) {
/* region owners update? */
unit * u = building_owner(blargest);
f = region_get_owner(r);
if (u==NULL) {
if (f) {
region_set_owner(r, NULL, turn);
r->land->ownership->flags |= OWNER_MOURNING;
f = NULL;
}
} else if (u->faction!=f) {
if (!r->land->ownership) {
/* there has never been a prior owner */
region_set_morale(r, MORALE_DEFAULT, turn);
} else {
alliance * al = region_get_alliance(r);
if (al && u->faction->alliance==al) {
int morale = MAX(0, r->land->morale-MORALE_TRANSFER);
region_set_morale(r, morale, turn);
} else {
region_set_morale(r, MORALE_TAKEOVER, turn);
if (f) {
r->land->ownership->flags |= OWNER_MOURNING;
}
}
}
region_set_owner(r, u->faction, turn);
f = u->faction;
}
}
} else if (r->land->ownership && r->land->ownership->owner) {
r->land->ownership->flags |= OWNER_MOURNING;
region_set_owner(r, NULL, turn);
f = NULL;
}
}
return f;
}
void
region_setinfo(struct region * r, const char * info)
{
free(r->display);
r->display = info?strdup(info):0;
}
const char *
region_getinfo(const region * r) {
return r->display?r->display:"";
}
void
region_setname(struct region * r, const char * name)
{
if (r->land) {
free(r->land->name);
r->land->name = name?strdup(name):0;
}
}
const char *
region_getname(const region * r) {
if (r->land && r->land->name) {
return r->land->name;
}
return "";
}
int region_get_morale(const region * r)
{
if (r->land) {
assert(r->land->morale>=0 && r->land->morale<=MORALE_MAX);
return r->land->morale;
}
return -1;
}
void region_set_morale(region * r, int morale, int turn)
{
if (r->land) {
r->land->morale = (short)morale;
if (turn>=0 && r->land->ownership) {
r->land->ownership->morale_turn = turn;
}
assert(r->land->morale>=0 && r->land->morale<=MORALE_MAX);
}
}
void get_neighbours(const region * r, region ** list)
{
direction_t dir;
for (dir=0;dir!=MAXDIRECTIONS;++dir) {
list[dir] = rconnect(r, dir);
}
}
int owner_change(const region * r)
{
if (r->land && r->land->ownership) {
return r->land->ownership->since_turn;
}
return -1;
}
boolean is_mourning(const region * r, int in_turn)
{
int change = owner_change(r);
return (change==in_turn-1 && (r->land->ownership->flags&OWNER_MOURNING));
}