2010-08-08 08:05:54 +02:00
|
|
|
|
/*
|
|
|
|
|
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));
|
|
|
|
|
}
|