forked from github/server
14b3ed309f
new repository structure
1608 lines
36 KiB
C
1608 lines
36 KiB
C
/*
|
||
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(®ions, 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));
|
||
}
|