server/src/kernel/region.c

1521 lines
36 KiB
C
Raw Normal View History

2010-08-08 10:06:34 +02:00
/*
2015-01-30 22:10:29 +01:00
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
Katja Zedel <katze@felidae.kn-bremen.de
Christian Schlittchen <corwin@amber.kn-bremen.de>
2010-08-08 10:06:34 +02:00
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**/
2019-01-24 16:34:07 +01:00
#ifdef _MSC_VER
# include <platform.h>
#endif
2010-08-08 10:06:34 +02:00
#include "region.h"
/* kernel includes */
#include "alliance.h"
#include "building.h"
#include "calendar.h"
#include "config.h"
2010-08-08 10:06:34 +02:00
#include "connection.h"
#include "curse.h"
#include "equipment.h"
#include "faction.h"
#include "item.h"
#include "messages.h"
2010-08-08 10:06:34 +02:00
#include "plane.h"
#include "region.h"
#include "resources.h"
#include "ship.h"
#include "terrain.h"
#include "terrainid.h"
#include "unit.h"
#include <spells/regioncurse.h>
2010-08-08 10:06:34 +02:00
/* util includes */
#include <util/assert.h>
#include <kernel/attrib.h>
2018-09-29 13:21:46 +02:00
#include <kernel/gamedata.h>
#include <util/strings.h>
2010-08-08 10:06:34 +02:00
#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/strings.h>
#include <storage.h>
2010-08-08 10:06:34 +02:00
#include <modules/autoseed.h>
/* libc includes */
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
2010-08-08 10:06:34 +02:00
#include <string.h>
extern int dice_rand(const char *s);
2011-03-07 08:02:35 +01:00
region *regions;
2010-08-08 10:06:34 +02:00
int get_maxluxuries(void)
2010-08-08 10:06:34 +02:00
{
const luxury_type *ltype;
int maxluxuries = 0;
for (ltype = luxurytypes; ltype; ltype = ltype->next) {
++maxluxuries;
}
return maxluxuries;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
const int delta_x[MAXDIRECTIONS] = {
-1, 0, 1, 1, 0, -1
2010-08-08 10:06:34 +02:00
};
2011-03-07 08:02:35 +01:00
const int delta_y[MAXDIRECTIONS] = {
1, 1, 0, -1, -1, 0
2010-08-08 10:06:34 +02:00
};
2011-03-07 08:02:35 +01:00
static const direction_t back[MAXDIRECTIONS] = {
D_SOUTHEAST,
D_SOUTHWEST,
D_WEST,
D_NORTHWEST,
D_NORTHEAST,
D_EAST,
2010-08-08 10:06:34 +02:00
};
2011-03-07 08:02:35 +01:00
direction_t dir_invert(direction_t dir)
2010-08-08 10:06:34 +02:00
{
switch (dir) {
2010-08-08 10:06:34 +02:00
case D_PAUSE:
case D_SPECIAL:
return dir;
break;
2010-08-08 10:06:34 +02:00
default:
if (dir >= 0 && dir < MAXDIRECTIONS)
return back[dir];
}
assert(!"illegal direction");
return NODIRECTION;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
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) {
2017-12-30 19:49:21 +01:00
str_strlcpy(buf, "(null)", size);
}
else {
plane *pl = rplane(r);
int nx = r->x, ny = r->y;
pnormalize(&nx, &ny, pl);
adjust_coordinates(f, &nx, &ny, pl);
2017-12-11 18:35:30 +01:00
snprintf(buf, size, "%s (%d,%d)", rname(r, lang), nx, ny);
}
return buffer;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
const char *regionname(const region * r, const faction * f)
2010-08-08 10:06:34 +02:00
{
static int index = 0;
static char buf[2][NAMESIZE];
index = 1 - index;
return write_regionname(r, f, buf[index], sizeof(buf[index]));
2010-08-08 10:06:34 +02:00
}
2016-11-15 20:43:36 +01:00
int region_maxworkers(const region *r)
{
int size = max_production(r);
2019-01-24 16:34:07 +01:00
int treespace = size - (rtrees(r, 2) + rtrees(r, 1) / 2) * TREESIZE;
size /=10;
if (size > 200) size = 200;
if (treespace < size) treespace = size;
return treespace;
2016-11-15 20:43:36 +01:00
}
2011-03-07 08:02:35 +01:00
int deathcount(const region * r)
2010-08-08 10:06:34 +02:00
{
attrib *a = a_find(r->attribs, &at_deathcount);
if (!a)
return 0;
return a->data.i;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void deathcounts(region * r, int fallen)
2010-08-08 10:06:34 +02:00
{
attrib *a = NULL;
2011-03-07 08:02:35 +01:00
if (fallen == 0)
return;
if (r->attribs) {
const curse_type *ctype = &ct_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;
2010-08-08 10:06:34 +02:00
if (a->data.i <= 0) {
a_remove(&r->attribs, a);
}
2010-08-08 10:06:34 +02:00
}
/* Moveblock wird zur Zeit nicht ueber Attribute, sondern ein Bitfeld
2010-08-08 10:06:34 +02:00
r->moveblock gemacht. Sollte umgestellt werden, wenn kompliziertere
Dinge gefragt werden. */
/********************/
/* at_moveblock */
/********************/
void a_initmoveblock(variant *var)
2010-08-08 10:06:34 +02:00
{
var->v = calloc(1, sizeof(moveblock));
2010-08-08 10:06:34 +02:00
}
int a_readmoveblock(variant *var, void *owner, gamedata *data)
2010-08-08 10:06:34 +02:00
{
moveblock *m = (moveblock *)var->v;
int i;
2010-08-08 10:06:34 +02:00
READ_INT(data->store, &i);
m->dir = (direction_t)i;
return AT_READ_OK;
2010-08-08 10:06:34 +02:00
}
void
a_writemoveblock(const variant *var, const void *owner, struct storage *store)
2010-08-08 10:06:34 +02:00
{
moveblock *m = (moveblock *)var->v;
WRITE_INT(store, (int)m->dir);
2010-08-08 10:06:34 +02:00
}
attrib_type at_moveblock = {
"moveblock", a_initmoveblock, NULL, NULL, a_writemoveblock, a_readmoveblock
2010-08-08 10:06:34 +02:00
};
#define coor_hashkey(x, y) (unsigned int)((x<<16) + y)
#define RMAXHASH MAXREGIONS
2011-03-07 08:02:35 +01:00
static region *regionhash[RMAXHASH];
2010-08-08 10:06:34 +02:00
static int dummy_data;
static region *dummy_ptr = (region *)& dummy_data; /* a funny hack */
2010-08-08 10:06:34 +02:00
typedef struct uidhashentry {
int uid;
region *r;
2010-08-08 10:06:34 +02:00
} uidhashentry;
static uidhashentry uidhash[MAXREGIONS];
struct region *findregionbyid(int uid)
2010-08-08 10:06:34 +02:00
{
int key = uid % MAXREGIONS;
while (uidhash[key].uid != 0 && uidhash[key].uid != uid) {
if (++key == MAXREGIONS) key = 0;
}
return uidhash[key].r;
2010-08-08 10:06:34 +02:00
}
#define DELMARKER dummy_ptr
2011-03-07 08:02:35 +01:00
static void unhash_uid(region * r)
2010-08-08 10:06:34 +02:00
{
int key = r->uid % MAXREGIONS;
assert(r->uid);
while (uidhash[key].uid != 0 && uidhash[key].uid != r->uid) {
if (++key == MAXREGIONS) key = 0;
}
assert(uidhash[key].r == r);
uidhash[key].r = NULL;
2010-08-08 10:06:34 +02:00
}
static void rhash_uid(region * r)
2010-08-08 10:06:34 +02:00
{
int uid = r->uid;
for (;;) {
if (uid != 0) {
int key = uid % MAXREGIONS;
while (uidhash[key].uid != 0 && uidhash[key].uid != uid) {
if (++key == MAXREGIONS) key = 0;
}
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();
2010-08-08 10:06:34 +02:00
}
}
#define HASH_STATISTICS 1
#if HASH_STATISTICS
static int hash_requests;
static int hash_misses;
#endif
void pnormalize(int *x, int *y, const plane * pl)
2010-08-08 10:06:34 +02:00
{
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;
}
2010-08-08 10:06:34 +02:00
}
}
2011-03-07 08:02:35 +01:00
static region *rfindhash(int x, int y)
2010-08-08 10:06:34 +02:00
{
unsigned int rid = coor_hashkey(x, y);
int key = HASH1(rid, RMAXHASH), gk = HASH2(rid, RMAXHASH);
2010-08-08 10:06:34 +02:00
#if HASH_STATISTICS
++hash_requests;
2010-08-08 10:06:34 +02:00
#endif
while (regionhash[key] != NULL && (regionhash[key] == DELMARKER
|| regionhash[key]->x != x || regionhash[key]->y != y)) {
key = (key + gk) % RMAXHASH;
2010-08-08 10:06:34 +02:00
#if HASH_STATISTICS
++hash_misses;
2010-08-08 10:06:34 +02:00
#endif
}
return regionhash[key];
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void rhash(region * r)
2010-08-08 10:06:34 +02:00
{
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;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void runhash(region * r)
2010-08-08 10:06:34 +02:00
{
unsigned int rid = coor_hashkey(r->x, r->y);
int key = HASH1(rid, RMAXHASH), gk = HASH2(rid, RMAXHASH);
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;
}
}
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;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
region *r_connect(const region * r, direction_t dir)
2010-08-08 10:06:34 +02:00
{
region *result;
int x, y;
region *rmodify = (region *)r;
assert(dir >= 0 && dir < MAXDIRECTIONS);
if (r->connect[dir]) {
return r->connect[dir];
}
assert(dir < MAXDIRECTIONS);
x = r->x + delta_x[dir];
y = r->y + delta_y[dir];
pnormalize(&x, &y, rplane(r));
result = rfindhash(x, y);
if (result) {
rmodify->connect[dir] = result;
result->connect[back[dir]] = rmodify;
}
return result;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
region *findregion(int x, int y)
2010-08-08 10:06:34 +02:00
{
return rfindhash(x, y);
2010-08-08 10:06:34 +02:00
}
/* Contributed by Hubert Mackenberg. Thanks.
* x und y Abstand zwischen x1 und x2 berechnen
*/
2011-03-07 08:02:35 +01:00
static int koor_distance_orig(int x1, int y1, int x2, int y2)
2010-08-08 10:06:34 +02:00
{
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;
}
2010-08-08 10:06:34 +02:00
}
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;
2019-01-24 16:34:07 +01:00
int mindist = ((width > height) ? height : width) / 2;
/* 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;
2011-03-07 08:02:35 +01:00
if (result <= mindist)
return result;
dist = (width - dx) + (height - dy); /* southwest */
if (dist >= 0 && dist < result) {
result = dist;
if (result <= mindist)
return result;
}
2019-01-24 16:34:07 +01:00
dist = height - dy;
if (dist < dx) dist = dx;
if (dist >= 0 && dist < result) {
result = dist;
if (result <= mindist)
return result;
}
2019-01-24 16:34:07 +01:00
dist = width - dx;
if (dist < dy) dist = dy;
if (dist >= 0 && dist < result)
result = dist;
return result;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
int koor_distance(int x1, int y1, int x2, int y2)
2010-08-08 10:06:34 +02:00
{
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);
}
2010-08-08 10:06:34 +02:00
}
}
2011-03-07 08:02:35 +01:00
int distance(const region * r1, const region * r2)
2010-08-08 10:06:34 +02:00
{
return koor_distance(r1->x, r1->y, r2->x, r2->y);
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void free_regionlist(region_list * rl)
2010-08-08 10:06:34 +02:00
{
while (rl) {
region_list *rl2 = rl->next;
free(rl);
rl = rl2;
}
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void add_regionlist(region_list ** rl, region * r)
2010-08-08 10:06:34 +02:00
{
region_list *rl2 = (region_list *)malloc(sizeof(region_list));
2010-08-08 10:06:34 +02:00
if (!rl2) abort();
rl2->data = r;
rl2->next = *rl;
2010-08-08 10:06:34 +02:00
*rl = rl2;
2010-08-08 10:06:34 +02:00
}
/********************/
/* at_horseluck */
/********************/
attrib_type at_horseluck = {
"horseluck",
DEFAULT_INIT,
DEFAULT_FINALIZE,
DEFAULT_AGE,
NO_WRITE,
NO_READ,
NULL,
ATF_UNIQUE
2010-08-08 10:06:34 +02:00
};
/**********************/
/* at_peasantluck */
/**********************/
attrib_type at_peasantluck = {
"peasantluck",
DEFAULT_INIT,
DEFAULT_FINALIZE,
DEFAULT_AGE,
NO_WRITE,
NO_READ,
NULL,
ATF_UNIQUE
2010-08-08 10:06:34 +02:00
};
/*********************/
/* at_deathcount */
/*********************/
attrib_type at_deathcount = {
"deathcount",
DEFAULT_INIT,
DEFAULT_FINALIZE,
DEFAULT_AGE,
a_writeint,
a_readint,
NULL,
ATF_UNIQUE
2010-08-08 10:06:34 +02:00
};
/*********************/
/* at_woodcount */
/*********************/
attrib_type at_woodcount = {
"woodcount",
DEFAULT_INIT,
DEFAULT_FINALIZE,
DEFAULT_AGE,
NO_WRITE,
a_readint,
NULL,
ATF_UNIQUE
2010-08-08 10:06:34 +02:00
};
void rsetroad(region * r, direction_t d, int val)
2010-08-08 10:06:34 +02:00
{
connection *b;
region *r2 = rconnect(r, d);
assert(val>=SHRT_MIN && val<=SHRT_MAX);
if (!r2) {
return;
}
b = get_borders(r, r2);
while (b && b->type != &bt_road) {
b = b->next;
}
if (!b) {
if (!val) return;
b = new_border(&bt_road, r, r2);
}
if (r == b->from) {
b->data.sa[0] = (short)val;
}
else {
b->data.sa[1] = (short)val;
}
2010-08-08 10:06:34 +02:00
}
int rroad(const region * r, direction_t d)
2010-08-08 10:06:34 +02:00
{
connection *b;
region *r2 = rconnect(r, d);
2011-03-07 08:02:35 +01:00
if (!r2) {
return 0;
}
b = get_borders(r, r2);
while (b && b->type != &bt_road) {
b = b->next;
}
if (!b) {
return 0;
}
return (r == b->from) ? b->data.sa[0] : b->data.sa[1];
2010-08-08 10:06:34 +02:00
}
bool r_isforest(const region * r)
2010-08-08 10:06:34 +02:00
{
if (fval(r->terrain, FOREST_REGION)) {
2017-08-20 12:58:05 +02:00
/* needs to be covered with at least 48% trees */
int mincover = (int)(r->terrain->size * 0.48);
int trees = rtrees(r, 2) + rtrees(r, 1);
return (trees * TREESIZE >= mincover);
}
return false;
2010-08-08 10:06:34 +02:00
}
2012-06-24 08:04:12 +02:00
bool is_coastregion(region * r)
2010-08-08 10:06:34 +02:00
{
direction_t i;
int res = 0;
2010-08-08 10:06:34 +02:00
for (i = 0; !res && i < MAXDIRECTIONS; i++) {
region *rn = rconnect(r, i);
if (rn && fval(rn->terrain, SEA_REGION))
res++;
}
return res != 0;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
int rpeasants(const region * r)
2010-08-08 10:06:34 +02:00
{
int value = 0;
if (r->land) {
value = r->land->peasants;
assert(value >= 0);
}
return value;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void rsetpeasants(region * r, int value)
2010-08-08 10:06:34 +02:00
{
2016-01-24 14:55:43 +01:00
assert(r->land || value==0);
assert(value >= 0);
if (r->land) {
r->land->peasants = value;
}
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
int rmoney(const region * r)
2010-08-08 10:06:34 +02:00
{
return r->land ? r->land->money : 0;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void rsethorses(const region * r, int value)
2010-08-08 10:06:34 +02:00
{
assert(r->land || value==0);
assert(value >= 0);
if (r->land) {
r->land->horses = value;
}
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
int rhorses(const region * r)
2010-08-08 10:06:34 +02:00
{
return r->land ? r->land->horses : 0;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void rsetmoney(region * r, int value)
2010-08-08 10:06:34 +02:00
{
assert(r && (r->land || value==0));
assert(value >= 0);
if (r->land) {
r->land->money = value;
}
2010-08-08 10:06:34 +02:00
}
int rherbs(const region *r)
{
return r->land?r->land->herbs:0;
}
void rsetherbs(region *r, int value)
{
assert(r->land || value==0);
assert(value >= 0 && value<=SHRT_MAX);
if (r->land) {
2018-01-21 11:14:55 +01:00
r->land->herbs = value;
}
}
void rsetherbtype(region *r, const struct item_type *itype) {
assert(r->land && r->terrain);
if (itype == NULL) {
r->land->herbtype = NULL;
}
else {
if (r->terrain->herbs) {
int i;
for (i = 0; r->terrain->herbs[i]; ++i) {
if (r->terrain->herbs[i] == itype) {
r->land->herbtype = itype;
return;
}
}
}
log_debug("attempt to set herbtype=%s for terrain=%s in %s", itype->rtype->_name, r->terrain->_name, regionname(r, 0));
r->land->herbtype = itype;
}
}
2011-03-07 08:02:35 +01:00
void r_setdemand(region * r, const luxury_type * ltype, int value)
2010-08-08 10:06:34 +02:00
{
struct demand *d, **dp = &r->land->demands;
2010-08-08 10:06:34 +02:00
if (ltype == NULL)
return;
2010-08-08 10:06:34 +02:00
while (*dp && (*dp)->type != ltype)
dp = &(*dp)->next;
d = *dp;
if (!d) {
d = *dp = malloc(sizeof(struct demand));
assert_alloc(d);
d->next = NULL;
d->type = ltype;
}
d->value = value;
2010-08-08 10:06:34 +02:00
}
const item_type *r_luxury(const region * r)
2010-08-08 10:06:34 +02:00
{
struct demand *dmd;
if (r->land) {
assert(r->land->demands || !"need to call fix_demand on a region");
for (dmd = r->land->demands; dmd; dmd = dmd->next) {
if (dmd->value == 0)
return dmd->type->itype;
}
2010-08-08 10:06:34 +02:00
}
return NULL;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
int r_demand(const region * r, const luxury_type * ltype)
2010-08-08 10:06:34 +02:00
{
struct demand *d;
assert(r && r->land);
d = r->land->demands;
while (d && d->type != ltype)
d = d->next;
if (!d)
return -1;
return d->value;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
const char *rname(const region * r, const struct locale *lang)
2010-08-08 10:06:34 +02:00
{
if (r->land && r->land->name) {
return r->land->name;
}
return LOC(lang, terrain_name(r));
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
int rtrees(const region * r, int ageclass)
2010-08-08 10:06:34 +02:00
{
return ((r)->land ? (r)->land->trees[ageclass] : 0);
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
int rsettrees(const region * r, int ageclass, int value)
2010-08-08 10:06:34 +02:00
{
if (!r->land) {
assert(value == 0);
}
else {
assert(value >= 0);
if (value <= MAXTREES) {
return r->land->trees[ageclass] = value;
}
else {
r->land->trees[ageclass] = MAXTREES;
}
}
return 0;
2010-08-08 10:06:34 +02:00
}
region *region_create(int uid)
{
region *r = (region *)calloc(1, sizeof(region));
assert_alloc(r);
r->uid = uid;
rhash_uid(r);
return r;
}
static region *last;
static unsigned int max_index;
2011-03-07 08:02:35 +01:00
void add_region(region *r, int x, int y) {
r->x = x;
r->y = y;
r->_plane = findplane(x, y);
rhash(r);
if (last) {
addlist(&last, r);
}
else {
addlist(&regions, r);
}
last = r;
assert(r->next == NULL);
r->index = ++max_index;
}
region *new_region(int x, int y, struct plane *pl, int uid)
{
region *r;
r = region_create(uid);
r->age = 1;
add_region(r, x, y);
assert(pl == r->_plane);
2010-08-08 10:06:34 +02:00
return r;
}
2011-03-07 08:02:35 +01:00
static region *deleted_regions;
2010-08-08 10:06:34 +02:00
2011-03-07 08:02:35 +01:00
void remove_region(region ** rlist, region * r)
2010-08-08 10:06:34 +02:00
{
assert(r);
while (r->units) {
unit *u = r->units;
i_freeall(&u->items);
remove_unit(&r->units, u);
}
2010-08-08 10:06:34 +02:00
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;
2010-08-08 10:06:34 +02:00
}
void free_land(land_region * lr)
2010-08-08 10:06:34 +02:00
{
free(lr->ownership);
while (lr->demands) {
struct demand *d = lr->demands;
lr->demands = d->next;
free(d);
}
free(lr->name);
free(lr->display);
free(lr);
2010-08-08 10:06:34 +02:00
}
void region_setresource(region * r, const struct resource_type *rtype, int value)
2010-08-08 10:06:34 +02:00
{
rawmaterial *rm = r->resources;
while (rm) {
if (rm->rtype == rtype) {
rm->amount = value;
break;
}
rm = rm->next;
2010-08-08 10:06:34 +02:00
}
if (!rm) {
if (rtype == get_resourcetype(R_SILVER))
rsetmoney(r, value);
else if (rtype == get_resourcetype(R_PEASANT))
rsetpeasants(r, value);
else if (rtype == get_resourcetype(R_HORSE))
rsethorses(r, value);
else {
rawmaterial *rm;
if (r->terrain->production) {
int i;
for (i = 0; r->terrain->production[i].type; ++i) {
const terrain_production *production = r->terrain->production + i;
if (production->type == rtype) {
add_resource(r, 1, value, dice_rand(production->divisor), rtype);
return;
}
}
}
/* adamantium etc are not usually terraformed: */
for (rm = r->resources; rm; rm = rm->next) {
if (rm->rtype == rtype) {
rm->amount = value;
return;
}
}
if (!rm) {
add_resource(r, 1, value, 150, rtype);
}
}
}
2010-08-08 10:06:34 +02:00
}
int region_getresource_level(const region * r, const struct resource_type * rtype)
{
const rawmaterial *rm;
for (rm = r->resources; rm; rm = rm->next) {
if (rm->rtype == rtype) {
return rm->level;
}
}
return -1;
}
int region_getresource(const region * r, const struct resource_type *rtype)
2010-08-08 10:06:34 +02:00
{
const rawmaterial *rm;
for (rm = r->resources; rm; rm = rm->next) {
if (rm->rtype == rtype) {
return rm->amount;
}
2010-08-08 10:06:34 +02:00
}
if (rtype == get_resourcetype(R_SILVER))
return rmoney(r);
if (rtype == get_resourcetype(R_HORSE))
return rhorses(r);
if (rtype == get_resourcetype(R_PEASANT))
return rpeasants(r);
return 0;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void free_region(region * r)
2010-08-08 10:06:34 +02:00
{
if (last == r)
last = NULL;
if (r->land)
free_land(r->land);
if (r->msgs) {
free_messagelist(r->msgs->begin);
free(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->begin);
free(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->units) {
unit *u = r->units;
r->units = u->next;
u->region = NULL;
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);
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void free_regions(void)
2010-08-08 10:06:34 +02:00
{
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;
2010-08-08 10:06:34 +02:00
}
/** 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.
*/
2011-03-07 08:02:35 +01:00
static char *makename(void)
2010-08-08 10:06:34 +02:00
{
int s, k, e, p = 0, x = 0;
size_t nk, ne, nv, ns;
static char name[16];
const char *kons = "bcdfghklmnprstvwz",
*handle_start = "bcdgtskpvfr",
*handle_end = "nlrdst",
*vowels = "aaaaaaaaaaaeeeeeeeeeeeeiiiiiiiiiiioooooooooooouuuuuuuuuuyy";
nk = strlen(kons);
ne = strlen(handle_end);
nv = strlen(vowels);
ns = strlen(handle_start);
for (s = rng_int() % 3 + 2; s > 0; s--) {
int v;
if (x > 0) {
k = rng_int() % (int)nk;
name[p] = kons[k];
p++;
}
else {
k = rng_int() % (int)ns;
name[p] = handle_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] = handle_end[e];
p++;
x = 1;
}
else
x = 0;
}
name[p] = '\0';
name[0] = (char)toupper(name[0]);
return name;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void setluxuries(region * r, const luxury_type * sale)
2010-08-08 10:06:34 +02:00
{
const luxury_type *ltype;
2010-08-08 10:06:34 +02:00
assert(r->land);
2010-08-08 10:06:34 +02:00
if (r->land->demands) {
freelist(r->land->demands);
r->land->demands = 0;
}
2011-03-07 08:02:35 +01:00
for (ltype = luxurytypes; ltype; ltype = ltype->next) {
struct demand *dmd = malloc(sizeof(struct demand));
if (!dmd) abort();
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;
}
2010-08-08 10:06:34 +02:00
}
int fix_demand(region * rd) {
luxury_type * ltype;
int maxluxuries = get_maxluxuries();
if (maxluxuries > 0) {
int sale = rng_int() % maxluxuries;
for (ltype = luxurytypes; sale != 0 && ltype; ltype = ltype->next) {
--sale;
}
setluxuries(rd, ltype);
return 0;
}
return -1;
}
void init_region(region *r)
{
static int changed;
static const terrain_type *t_plain;
const terrain_type * terrain = r->terrain;
int horses = 0, trees = 0;
if (terrain_changed(&changed)) {
t_plain = get_terrain(terrainnames[T_PLAIN]);
}
if (terrain->size>0) {
horses = rng_int() % (terrain->size / 50);
trees = terrain->size * (30 + rng_int() % 40) / 1000;
}
if (t_plain && terrain == t_plain) {
rsethorses(r, horses);
if (chance(0.4)) {
rsettrees(r, 2, trees);
}
}
else if (trees>0 && chance(0.2)) {
rsettrees(r, 2, trees);
}
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 = (region_maxworkers(r) * (20 + dice(6, 10))) / 100;
2019-01-24 16:34:07 +01:00
if (peasants < 100) peasants = 100;
rsetpeasants(r, peasants);
rsetmoney(r, rpeasants(r) * ((wage(r, NULL, NULL,
INT_MAX) + 1) + rng_int() % 5));
}
}
2011-03-07 08:02:35 +01:00
void terraform_region(region * r, const terrain_type * terrain)
2010-08-08 10:06:34 +02:00
{
2019-02-09 13:39:28 +01:00
/* Resourcen, die nicht mehr vorkommen koennen, loeschen */
const terrain_type *oldterrain = r->terrain;
rawmaterial **lrm = &r->resources;
2010-08-08 10:06:34 +02:00
assert(terrain);
while (*lrm) {
rawmaterial *rm = *lrm;
const resource_type *rtype = NULL;
2011-03-07 08:02:35 +01:00
if (terrain->production != NULL) {
int i;
for (i = 0; terrain->production[i].type; ++i) {
if (rm->rtype == terrain->production[i].type) {
rtype = rm->rtype;
break;
}
2010-08-08 10:06:34 +02:00
}
}
if (rtype == NULL) {
*lrm = rm->next;
free(rm);
}
else {
lrm = &rm->next;
}
}
r->terrain = terrain;
terraform_resources(r);
if (!fval(terrain, LAND_REGION)) {
2017-05-09 18:49:10 +02:00
if (r->land) {
free_land(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);
2011-03-07 08:02:35 +01:00
freset(r, RF_MALLORN);
return;
2010-08-08 10:06:34 +02:00
}
if (r->land) {
int d;
for (d = 0; d != MAXDIRECTIONS; ++d) {
rsetroad(r, d, 0);
}
2010-08-08 10:06:34 +02:00
}
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));
if (!r->land) abort();
r->land->ownership = NULL;
region_set_morale(r, MORALE_DEFAULT, -1);
region_setname(r, makename());
fix_demand(r);
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));
if (!sr) abort();
}
sr->next = nb;
sr->type = sale->type;
sr->value = 1;
nb = sr;
}
else
sr->value++;
++mnr;
}
}
}
if (!nb) {
/* TODO: this is really lame */
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;
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);
2018-01-21 11:14:55 +01:00
rsetherbs(r, 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 (oldterrain == NULL || terrain->size != oldterrain->size) {
init_region(r);
}
2010-08-08 10:06:34 +02:00
}
}
/** ENNO:
* ich denke, das das hier nicht sein sollte.
* statt dessen sollte ein attribut an der region sein, dass das erledigt,
2010-08-08 10:06:34 +02:00
* egal ob durch den spell oder anderes angelegt.
**/
#include "curse.h"
int max_production(const region * r)
2010-08-08 10:06:34 +02:00
{
/* muss rterrain(r) sein, nicht rterrain() wegen rekursion */
int p = r->terrain->size;
if (curse_active(get_curse(r->attribs, &ct_drought))) {
p /= 2;
}
2010-08-08 10:06:34 +02:00
return p;
2010-08-08 10:06:34 +02:00
}
2017-09-21 16:26:53 +02:00
void resolve_region(region *r)
2010-08-08 10:06:34 +02:00
{
resolve(RESOLVE_REGION | r->uid, r);
2010-08-08 10:06:34 +02:00
}
int read_region_reference(gamedata * data, region **rp)
2010-08-08 10:06:34 +02:00
{
struct storage * store = data->store;
2017-09-21 16:26:53 +02:00
int id = 0;
READ_INT(store, &id);
*rp = findregionbyid(id);
if (*rp == NULL) {
2018-11-01 21:20:11 +01:00
*rp = region_create(id);
}
2017-09-21 16:26:53 +02:00
return id;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void write_region_reference(const region * r, struct storage *store)
2010-08-08 10:06:34 +02:00
{
if (r) {
WRITE_INT(store, r->uid);
}
else {
WRITE_INT(store, 0);
}
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
struct message_list *r_getmessages(const struct region *r,
const struct faction *viewer)
2010-08-08 10:06:34 +02:00
{
struct individual_message *imsg = r->individual_messages;
while (imsg && (imsg)->viewer != viewer)
imsg = imsg->next;
if (imsg)
return imsg->msgs;
return NULL;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
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));
if (!imsg) abort();
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);
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
struct faction *region_get_owner(const struct region *r)
2010-08-08 10:06:34 +02:00
{
if (r->land) {
if (rule_region_owners()) {
if (r->land->ownership) {
return r->land->ownership->owner;
}
}
else {
building *b = largestbuilding(r, cmp_castle_size, false);
unit * u = b ? building_owner(b) : NULL;
return u ? u->faction : NULL;
}
}
return NULL;
2010-08-08 10:06:34 +02:00
}
struct faction *region_get_last_owner(const struct region *r)
{
assert(rule_region_owners());
if (r->land && r->land->ownership) {
return r->land->ownership->last_owner;
}
return NULL;
}
2011-03-07 08:02:35 +01:00
struct alliance *region_get_alliance(const struct region *r)
2010-08-08 10:06:34 +02:00
{
assert(rule_region_owners());
if (r->land && r->land->ownership) {
region_owner *own = r->land->ownership;
return own->owner ? own->owner->alliance : (own->last_owner? own->last_owner->alliance : NULL);
}
return NULL;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void region_set_owner(struct region *r, struct faction *owner, int turn)
2010-08-08 10:06:34 +02:00
{
assert(rule_region_owners());
if (r->land) {
if (!r->land->ownership) {
region_owner *ro = malloc(sizeof(region_owner));
if (!ro) abort();
assert(region_get_morale(r) == MORALE_DEFAULT);
ro->owner = NULL;
ro->last_owner = NULL;
ro->flags = 0;
r->land->ownership = ro;
}
r->land->ownership->since_turn = turn;
r->land->ownership->morale_turn = turn;
assert(r->land->ownership->owner != owner);
r->land->ownership->last_owner = r->land->ownership->owner;
r->land->ownership->owner = owner;
2010-08-08 10:06:34 +02:00
}
}
2011-03-07 08:02:35 +01:00
faction *update_owners(region * r)
2010-08-08 10:06:34 +02:00
{
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 *new_owner = building_owner(blargest);
f = region_get_owner(r);
if (new_owner == NULL) {
if (f) {
region_set_owner(r, NULL, turn);
f = NULL;
}
}
else if (new_owner->faction != f) {
if (!r->land->ownership) {
/* there has never been a prior owner */
region_set_morale(r, MORALE_DEFAULT, turn);
}
else if (f || new_owner->faction != region_get_last_owner(r)) {
alliance *al = region_get_alliance(r);
if (al && new_owner->faction->alliance == al) {
2019-01-24 16:34:07 +01:00
int morale = region_get_morale(r) - MORALE_TRANSFER;
if (morale < 0) morale = 0;
region_set_morale(r, morale, turn);
}
else {
region_set_morale(r, MORALE_TAKEOVER, turn);
}
}
region_set_owner(r, new_owner->faction, turn);
f = new_owner->faction;
}
}
}
else if (r->land->ownership && r->land->ownership->owner) {
region_set_owner(r, NULL, turn);
2010-08-08 10:06:34 +02:00
f = NULL;
}
}
return f;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void region_setinfo(struct region *r, const char *info)
2010-08-08 10:06:34 +02:00
{
assert(r->land);
free(r->land->display);
r->land->display = (info && info[0]) ? str_strdup(info) : 0;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
const char *region_getinfo(const region * r)
{
return (r->land && r->land->display) ? r->land->display : "";
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void region_setname(struct region *r, const char *name)
2010-08-08 10:06:34 +02:00
{
if (r->land) {
free(r->land->name);
r->land->name = name ? str_strdup(name) : 0;
}
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
const char *region_getname(const region * r)
{
if (r->land && r->land->name) {
return r->land->name;
}
return "";
2010-08-08 10:06:34 +02:00
}
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;
2010-08-08 10:06:34 +02:00
}
void region_set_morale(region * r, int morale, int turn)
{
if (r->land) {
2018-01-21 11:14:55 +01:00
r->land->morale = morale;
if (turn >= 0 && r->land->ownership) {
r->land->ownership->morale_turn = turn;
}
assert(r->land->morale >= 0 && r->land->morale <= MORALE_MAX);
2010-08-08 10:06:34 +02:00
}
}
void get_neighbours(const region * r, region ** list)
{
int dir;
for (dir = 0; dir != MAXDIRECTIONS; ++dir) {
list[dir] = rconnect(r, (direction_t)dir);
}
2010-08-08 10:06:34 +02:00
}
int owner_change(const region * r)
{
if (r->land && r->land->ownership) {
return r->land->ownership->since_turn;
}
return INT_MIN;
2010-08-08 10:06:34 +02:00
}
bool is_mourning(const region * r, int in_turn)
2010-08-08 10:06:34 +02:00
{
int change = owner_change(r);
return (change == in_turn - 1 && r->land &&
r->land->ownership->last_owner && r->land->ownership->owner &&
r->land->ownership->last_owner != r->land->ownership->owner);
2010-08-08 10:06:34 +02:00
}