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>
|
2014-12-25 22:38:01 +01:00
|
|
|
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"
|
2018-02-14 20:00:48 +01:00
|
|
|
#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"
|
2014-06-09 18:54:48 +02:00
|
|
|
#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"
|
|
|
|
|
2017-08-21 19:43:35 +02:00
|
|
|
#include <spells/regioncurse.h>
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
/* util includes */
|
2017-03-04 20:59:43 +01:00
|
|
|
#include <util/assert.h>
|
2018-09-29 11:37:17 +02:00
|
|
|
#include <kernel/attrib.h>
|
2018-09-29 13:21:46 +02:00
|
|
|
#include <kernel/gamedata.h>
|
2015-05-18 08:59:38 +02:00
|
|
|
#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>
|
2017-12-28 18:29:40 +01:00
|
|
|
#include <util/strings.h>
|
2013-12-31 10:06:28 +01:00
|
|
|
|
|
|
|
#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>
|
2014-03-15 19:29:11 +01:00
|
|
|
#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
|
|
|
|
2012-05-16 00:04:23 +02:00
|
|
|
int get_maxluxuries(void)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-12-31 20:01:01 +01:00
|
|
|
const luxury_type *ltype;
|
|
|
|
int maxluxuries = 0;
|
|
|
|
for (ltype = luxurytypes; ltype; ltype = ltype->next) {
|
|
|
|
++maxluxuries;
|
2014-12-25 22:38:01 +01:00
|
|
|
}
|
|
|
|
return maxluxuries;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
|
|
const int delta_x[MAXDIRECTIONS] = {
|
2014-12-25 22:38:01 +01:00
|
|
|
-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] = {
|
2014-12-25 22:38:01 +01:00
|
|
|
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] = {
|
2014-12-25 22:38:01 +01:00
|
|
|
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
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
switch (dir) {
|
2010-08-08 10:06:34 +02:00
|
|
|
case D_PAUSE:
|
|
|
|
case D_SPECIAL:
|
2014-12-25 22:38:01 +01:00
|
|
|
return dir;
|
|
|
|
break;
|
2010-08-08 10:06:34 +02:00
|
|
|
default:
|
2014-12-25 22:38:01 +01:00
|
|
|
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,
|
2014-12-25 22:38:01 +01:00
|
|
|
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);
|
2014-12-25 22:38:01 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
plane *pl = rplane(r);
|
|
|
|
int nx = r->x, ny = r->y;
|
|
|
|
pnormalize(&nx, &ny, pl);
|
2015-05-19 08:02:32 +02:00
|
|
|
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);
|
2014-12-25 22:38:01 +01:00
|
|
|
}
|
|
|
|
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
|
|
|
{
|
2014-12-25 22:38:01 +01: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)
|
|
|
|
{
|
2017-12-04 19:20:48 +01:00
|
|
|
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
|
|
|
{
|
2014-12-25 22:38:01 +01: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
|
|
|
{
|
2016-08-29 21:31:18 +02:00
|
|
|
attrib *a = NULL;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
2014-12-25 22:38:01 +01:00
|
|
|
if (fallen == 0)
|
|
|
|
return;
|
2016-08-29 21:31:18 +02:00
|
|
|
if (r->attribs) {
|
2017-08-21 20:18:19 +02:00
|
|
|
const curse_type *ctype = &ct_holyground;
|
2016-08-29 21:31:18 +02:00
|
|
|
if (ctype && curse_active(get_curse(r->attribs, ctype)))
|
|
|
|
return;
|
|
|
|
a = a_find(r->attribs, &at_deathcount);
|
|
|
|
}
|
|
|
|
if (!a) {
|
2014-12-25 22:38:01 +01:00
|
|
|
a = a_add(&r->attribs, a_new(&at_deathcount));
|
2016-08-29 21:31:18 +02:00
|
|
|
}
|
2014-12-25 22:38:01 +01:00
|
|
|
a->data.i += fallen;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2016-08-29 21:31:18 +02:00
|
|
|
if (a->data.i <= 0) {
|
2014-12-25 22:38:01 +01:00
|
|
|
a_remove(&r->attribs, a);
|
2016-08-29 21:31:18 +02:00
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2018-12-17 12:07:38 +01: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 */
|
|
|
|
/********************/
|
2018-02-09 21:20:43 +01:00
|
|
|
void a_initmoveblock(variant *var)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2018-02-09 21:20:43 +01:00
|
|
|
var->v = calloc(1, sizeof(moveblock));
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2018-02-09 21:20:43 +01:00
|
|
|
int a_readmoveblock(variant *var, void *owner, gamedata *data)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2018-02-09 21:20:43 +01:00
|
|
|
moveblock *m = (moveblock *)var->v;
|
2014-12-25 22:38:01 +01:00
|
|
|
int i;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2016-02-13 13:42:02 +01:00
|
|
|
READ_INT(data->store, &i);
|
2014-12-25 22:38:01 +01:00
|
|
|
m->dir = (direction_t)i;
|
|
|
|
return AT_READ_OK;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2018-02-09 21:20:43 +01:00
|
|
|
a_writemoveblock(const variant *var, const void *owner, struct storage *store)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2018-02-09 21:20:43 +01:00
|
|
|
moveblock *m = (moveblock *)var->v;
|
2014-12-25 22:38:01 +01:00
|
|
|
WRITE_INT(store, (int)m->dir);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
attrib_type at_moveblock = {
|
2014-12-25 22:38:01 +01:00
|
|
|
"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;
|
2014-12-25 22:38:01 +01:00
|
|
|
static region *dummy_ptr = (region *)& dummy_data; /* a funny hack */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
typedef struct uidhashentry {
|
2014-12-25 22:38:01 +01:00
|
|
|
int uid;
|
|
|
|
region *r;
|
2010-08-08 10:06:34 +02:00
|
|
|
} uidhashentry;
|
|
|
|
static uidhashentry uidhash[MAXREGIONS];
|
|
|
|
|
2014-08-24 21:49:55 +02:00
|
|
|
struct region *findregionbyid(int uid)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-12-25 22:38:01 +01: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
|
|
|
{
|
2014-12-25 22:38:01 +01: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
|
|
|
}
|
|
|
|
|
2018-11-01 21:08:59 +01:00
|
|
|
static void rhash_uid(region * r)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-12-25 22:38:01 +01: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
|
|
|
|
|
2018-11-03 15:48:35 +01:00
|
|
|
void pnormalize(int *x, int *y, const plane * pl)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-12-25 22:38:01 +01: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
|
|
|
{
|
2014-12-25 22:38:01 +01: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
|
2014-12-25 22:38:01 +01:00
|
|
|
++hash_requests;
|
2010-08-08 10:06:34 +02:00
|
|
|
#endif
|
2014-12-25 22:38:01 +01:00
|
|
|
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
|
2014-12-25 22:38:01 +01:00
|
|
|
++hash_misses;
|
2010-08-08 10:06:34 +02:00
|
|
|
#endif
|
2014-12-25 22:38:01 +01:00
|
|
|
}
|
|
|
|
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
|
|
|
{
|
2014-12-25 22:38:01 +01: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
|
|
|
{
|
2014-12-25 22:38:01 +01: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
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
region *result;
|
|
|
|
int x, y;
|
|
|
|
region *rmodify = (region *)r;
|
|
|
|
assert(dir >= 0 && dir < MAXDIRECTIONS);
|
2018-11-03 15:48:35 +01:00
|
|
|
if (r->connect[dir]) {
|
2014-12-25 22:38:01 +01:00
|
|
|
return r->connect[dir];
|
2018-11-03 15:48:35 +01:00
|
|
|
}
|
2014-12-25 22:38:01 +01:00
|
|
|
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
|
|
|
{
|
2014-12-25 22:38:01 +01: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
|
|
|
{
|
2014-12-25 22:38:01 +01: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)
|
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
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;
|
2014-12-25 22:38:01 +01:00
|
|
|
|
|
|
|
/* 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)
|
2014-12-25 22:38:01 +01:00
|
|
|
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;
|
2014-12-25 22:38:01 +01:00
|
|
|
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;
|
2014-12-25 22:38:01 +01:00
|
|
|
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
|
|
|
{
|
2014-12-25 22:38:01 +01: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
|
|
|
{
|
2014-12-25 22:38:01 +01: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
|
|
|
{
|
2014-12-25 22:38:01 +01: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
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
region_list *rl2 = (region_list *)malloc(sizeof(region_list));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2018-12-15 19:38:40 +01:00
|
|
|
if (!rl2) abort();
|
2014-12-25 22:38:01 +01:00
|
|
|
rl2->data = r;
|
|
|
|
rl2->next = *rl;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-12-25 22:38:01 +01:00
|
|
|
*rl = rl2;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/********************/
|
|
|
|
/* at_horseluck */
|
|
|
|
/********************/
|
|
|
|
attrib_type at_horseluck = {
|
2014-12-25 22:38:01 +01:00
|
|
|
"horseluck",
|
|
|
|
DEFAULT_INIT,
|
|
|
|
DEFAULT_FINALIZE,
|
|
|
|
DEFAULT_AGE,
|
|
|
|
NO_WRITE,
|
|
|
|
NO_READ,
|
2016-02-09 06:43:19 +01:00
|
|
|
NULL,
|
2014-12-25 22:38:01 +01:00
|
|
|
ATF_UNIQUE
|
2010-08-08 10:06:34 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/**********************/
|
|
|
|
/* at_peasantluck */
|
|
|
|
/**********************/
|
|
|
|
attrib_type at_peasantluck = {
|
2014-12-25 22:38:01 +01:00
|
|
|
"peasantluck",
|
|
|
|
DEFAULT_INIT,
|
|
|
|
DEFAULT_FINALIZE,
|
|
|
|
DEFAULT_AGE,
|
|
|
|
NO_WRITE,
|
|
|
|
NO_READ,
|
2016-02-09 06:43:19 +01:00
|
|
|
NULL,
|
2014-12-25 22:38:01 +01:00
|
|
|
ATF_UNIQUE
|
2010-08-08 10:06:34 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/*********************/
|
|
|
|
/* at_deathcount */
|
|
|
|
/*********************/
|
|
|
|
attrib_type at_deathcount = {
|
2014-12-25 22:38:01 +01:00
|
|
|
"deathcount",
|
|
|
|
DEFAULT_INIT,
|
|
|
|
DEFAULT_FINALIZE,
|
|
|
|
DEFAULT_AGE,
|
|
|
|
a_writeint,
|
|
|
|
a_readint,
|
2016-02-09 06:43:19 +01:00
|
|
|
NULL,
|
2014-12-25 22:38:01 +01:00
|
|
|
ATF_UNIQUE
|
2010-08-08 10:06:34 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/*********************/
|
|
|
|
/* at_woodcount */
|
|
|
|
/*********************/
|
|
|
|
attrib_type at_woodcount = {
|
2014-12-25 22:38:01 +01:00
|
|
|
"woodcount",
|
|
|
|
DEFAULT_INIT,
|
|
|
|
DEFAULT_FINALIZE,
|
|
|
|
DEFAULT_AGE,
|
|
|
|
NO_WRITE,
|
|
|
|
a_readint,
|
2016-02-09 06:43:19 +01:00
|
|
|
NULL,
|
2014-12-25 22:38:01 +01:00
|
|
|
ATF_UNIQUE
|
2010-08-08 10:06:34 +02:00
|
|
|
};
|
|
|
|
|
2015-05-15 19:08:44 +02:00
|
|
|
void rsetroad(region * r, direction_t d, int val)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
connection *b;
|
|
|
|
region *r2 = rconnect(r, d);
|
|
|
|
|
2015-05-15 19:08:44 +02:00
|
|
|
assert(val>=SHRT_MIN && val<=SHRT_MAX);
|
2014-12-25 22:38:01 +01:00
|
|
|
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) {
|
2015-05-15 20:35:36 +02:00
|
|
|
b->data.sa[0] = (short)val;
|
2014-12-25 22:38:01 +01:00
|
|
|
}
|
|
|
|
else {
|
2015-05-15 20:35:36 +02:00
|
|
|
b->data.sa[1] = (short)val;
|
2014-12-25 22:38:01 +01:00
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2015-05-15 19:08:44 +02:00
|
|
|
int rroad(const region * r, direction_t d)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
connection *b;
|
|
|
|
region *r2 = rconnect(r, d);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
2014-12-25 22:38:01 +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
|
|
|
}
|
|
|
|
|
2012-06-24 07:41:07 +02:00
|
|
|
bool r_isforest(const region * r)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
if (fval(r->terrain, FOREST_REGION)) {
|
2017-08-20 12:58:05 +02:00
|
|
|
/* needs to be covered with at least 48% trees */
|
2014-12-25 22:38:01 +01:00
|
|
|
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
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
direction_t i;
|
|
|
|
int res = 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-12-25 22:38:01 +01: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
|
|
|
{
|
2016-01-24 14:35:46 +01: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);
|
2016-01-24 14:35:46 +01:00
|
|
|
assert(value >= 0);
|
2016-01-24 14:45:46 +01:00
|
|
|
if (r->land) {
|
2015-12-07 17:59:50 +01:00
|
|
|
r->land->peasants = value;
|
2015-12-16 13:32:02 +01:00
|
|
|
}
|
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
|
|
|
{
|
2015-12-07 17:59:50 +01: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
|
|
|
{
|
2016-01-24 14:45:46 +01:00
|
|
|
assert(r->land || value==0);
|
2014-12-25 22:38:01 +01:00
|
|
|
assert(value >= 0);
|
2016-01-24 14:45:46 +01:00
|
|
|
if (r->land) {
|
2014-12-25 22:38:01 +01:00
|
|
|
r->land->horses = value;
|
2015-12-16 13:32:02 +01:00
|
|
|
}
|
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
|
|
|
{
|
2014-12-25 22:38:01 +01: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
|
|
|
{
|
2016-03-23 23:06:45 +01:00
|
|
|
assert(r && (r->land || value==0));
|
2016-01-24 14:57:36 +01:00
|
|
|
assert(value >= 0);
|
2015-12-07 17:59:50 +01:00
|
|
|
if (r->land) {
|
|
|
|
r->land->money = value;
|
2015-12-16 13:32:02 +01:00
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2016-08-18 19:04:48 +02:00
|
|
|
int rherbs(const region *r)
|
2015-12-07 17:59:50 +01:00
|
|
|
{
|
|
|
|
return r->land?r->land->herbs:0;
|
|
|
|
}
|
|
|
|
|
2016-08-18 19:04:48 +02:00
|
|
|
void rsetherbs(region *r, int value)
|
2015-12-07 17:59:50 +01:00
|
|
|
{
|
2016-01-30 23:09:02 +01:00
|
|
|
assert(r->land || value==0);
|
2016-08-18 19:04:48 +02:00
|
|
|
assert(value >= 0 && value<=SHRT_MAX);
|
2015-12-07 17:59:50 +01:00
|
|
|
if (r->land) {
|
2018-01-21 11:14:55 +01:00
|
|
|
r->land->herbs = value;
|
2015-12-16 13:32:02 +01:00
|
|
|
}
|
2015-12-07 17:59:50 +01:00
|
|
|
}
|
|
|
|
|
2016-08-18 19:04:48 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2015-12-07 17:59:50 +01:00
|
|
|
|
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
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
struct demand *d, **dp = &r->land->demands;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-12-25 22:38:01 +01:00
|
|
|
if (ltype == NULL)
|
|
|
|
return;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-12-25 22:38:01 +01:00
|
|
|
while (*dp && (*dp)->type != ltype)
|
|
|
|
dp = &(*dp)->next;
|
|
|
|
d = *dp;
|
|
|
|
if (!d) {
|
|
|
|
d = *dp = malloc(sizeof(struct demand));
|
2017-03-04 20:59:43 +01:00
|
|
|
assert_alloc(d);
|
2014-12-25 22:38:01 +01:00
|
|
|
d->next = NULL;
|
|
|
|
d->type = ltype;
|
|
|
|
}
|
|
|
|
d->value = value;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2016-09-16 18:03:28 +02:00
|
|
|
const item_type *r_luxury(const region * r)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
struct demand *dmd;
|
|
|
|
if (r->land) {
|
2017-11-04 20:10:05 +01:00
|
|
|
assert(r->land->demands || !"need to call fix_demand on a region");
|
2014-12-25 22:38:01 +01:00
|
|
|
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
|
|
|
}
|
2014-12-25 22:38:01 +01: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
|
|
|
{
|
2018-09-09 07:41:30 +02:00
|
|
|
struct demand *d;
|
|
|
|
|
|
|
|
assert(r && r->land);
|
|
|
|
d = r->land->demands;
|
2014-12-25 22:38:01 +01:00
|
|
|
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
|
|
|
{
|
2014-12-25 22:38:01 +01: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
|
|
|
{
|
2014-12-25 22:38:01 +01: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
|
|
|
{
|
2017-08-27 20:10:27 +02:00
|
|
|
if (!r->land) {
|
2014-12-25 22:38:01 +01:00
|
|
|
assert(value == 0);
|
2017-08-27 20:10:27 +02:00
|
|
|
}
|
2014-12-25 22:38:01 +01:00
|
|
|
else {
|
|
|
|
assert(value >= 0);
|
2017-08-27 20:10:27 +02:00
|
|
|
if (value <= MAXTREES) {
|
|
|
|
return r->land->trees[ageclass] = value;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
r->land->trees[ageclass] = MAXTREES;
|
|
|
|
}
|
2014-12-25 22:38:01 +01:00
|
|
|
}
|
|
|
|
return 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2018-11-01 21:08:59 +01:00
|
|
|
region *region_create(int uid)
|
|
|
|
{
|
|
|
|
region *r = (region *)calloc(1, sizeof(region));
|
|
|
|
assert_alloc(r);
|
|
|
|
r->uid = uid;
|
|
|
|
rhash_uid(r);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2018-11-03 15:48:35 +01:00
|
|
|
static region *last;
|
|
|
|
static unsigned int max_index;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
2018-11-03 15:48:35 +01:00
|
|
|
void add_region(region *r, int x, int y) {
|
2014-12-25 22:38:01 +01:00
|
|
|
r->x = x;
|
|
|
|
r->y = y;
|
2018-11-03 15:48:35 +01:00
|
|
|
r->_plane = findplane(x, y);
|
2014-12-25 22:38:01 +01:00
|
|
|
rhash(r);
|
2018-11-01 21:08:59 +01:00
|
|
|
if (last) {
|
2014-12-25 22:38:01 +01:00
|
|
|
addlist(&last, r);
|
2018-11-01 21:08:59 +01:00
|
|
|
}
|
|
|
|
else {
|
2014-12-25 22:38:01 +01:00
|
|
|
addlist(®ions, r);
|
2018-11-01 21:08:59 +01:00
|
|
|
}
|
2014-12-25 22:38:01 +01:00
|
|
|
last = r;
|
|
|
|
assert(r->next == NULL);
|
|
|
|
r->index = ++max_index;
|
2018-11-03 15:48:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
{
|
2018-12-15 19:38:40 +01:00
|
|
|
assert(r);
|
2014-12-25 22:38:01 +01:00
|
|
|
while (r->units) {
|
|
|
|
unit *u = r->units;
|
|
|
|
i_freeall(&u->items);
|
|
|
|
remove_unit(&r->units, u);
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-12-25 22:38:01 +01: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
|
|
|
}
|
|
|
|
|
2015-10-29 11:17:31 +01:00
|
|
|
void free_land(land_region * lr)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2015-10-14 12:22:37 +02:00
|
|
|
free(lr->ownership);
|
2014-12-25 22:38:01 +01:00
|
|
|
while (lr->demands) {
|
|
|
|
struct demand *d = lr->demands;
|
|
|
|
lr->demands = d->next;
|
|
|
|
free(d);
|
|
|
|
}
|
2017-09-16 07:58:57 +02:00
|
|
|
free(lr->name);
|
|
|
|
free(lr->display);
|
2014-12-25 22:38:01 +01:00
|
|
|
free(lr);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2018-10-30 21:01:09 +01:00
|
|
|
void region_setresource(region * r, const struct resource_type *rtype, int value)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
rawmaterial *rm = r->resources;
|
|
|
|
while (rm) {
|
2017-03-22 20:37:09 +01:00
|
|
|
if (rm->rtype == rtype) {
|
2014-12-25 22:38:01 +01:00
|
|
|
rm->amount = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rm = rm->next;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
2014-12-25 22:38:01 +01: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 {
|
2017-03-22 20:37:09 +01:00
|
|
|
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;
|
2014-12-25 22:38:01 +01:00
|
|
|
}
|
|
|
|
}
|
2017-03-22 20:37:09 +01:00
|
|
|
if (!rm) {
|
|
|
|
add_resource(r, 1, value, 150, rtype);
|
|
|
|
}
|
2012-08-02 09:06:03 +02:00
|
|
|
}
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2018-10-30 21:01:09 +01: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
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
const rawmaterial *rm;
|
|
|
|
for (rm = r->resources; rm; rm = rm->next) {
|
2017-03-22 20:37:09 +01:00
|
|
|
if (rm->rtype == rtype) {
|
2014-12-25 22:38:01 +01:00
|
|
|
return rm->amount;
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
2014-12-25 22:38:01 +01: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
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
if (last == r)
|
|
|
|
last = NULL;
|
|
|
|
if (r->land)
|
2015-10-29 11:17:31 +01:00
|
|
|
free_land(r->land);
|
2014-12-25 22:38:01 +01:00
|
|
|
|
|
|
|
if (r->msgs) {
|
2015-10-14 14:08:50 +02:00
|
|
|
free_messagelist(r->msgs->begin);
|
|
|
|
free(r->msgs);
|
2014-12-25 22:38:01 +01:00
|
|
|
r->msgs = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (r->individual_messages) {
|
|
|
|
struct individual_message *msg = r->individual_messages;
|
|
|
|
r->individual_messages = msg->next;
|
2015-10-14 14:08:50 +02:00
|
|
|
if (msg->msgs) {
|
|
|
|
free_messagelist(msg->msgs->begin);
|
|
|
|
free(msg->msgs);
|
|
|
|
}
|
2014-12-25 22:38:01 +01:00
|
|
|
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;
|
2017-06-04 14:35:15 +02:00
|
|
|
u->region = NULL;
|
2014-12-25 22:38:01 +01:00
|
|
|
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
|
|
|
{
|
2014-12-25 22:38:01 +01: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
|
|
|
{
|
2018-02-25 18:30:20 +01:00
|
|
|
int s, k, e, p = 0, x = 0;
|
2014-12-25 22:38:01 +01:00
|
|
|
size_t nk, ne, nv, ns;
|
|
|
|
static char name[16];
|
|
|
|
const char *kons = "bcdfghklmnprstvwz",
|
2018-04-28 15:58:14 +02:00
|
|
|
*handle_start = "bcdgtskpvfr",
|
|
|
|
*handle_end = "nlrdst",
|
2014-12-25 22:38:01 +01:00
|
|
|
*vowels = "aaaaaaaaaaaeeeeeeeeeeeeiiiiiiiiiiioooooooooooouuuuuuuuuuyy";
|
|
|
|
|
|
|
|
nk = strlen(kons);
|
2018-04-28 15:58:14 +02:00
|
|
|
ne = strlen(handle_end);
|
2014-12-25 22:38:01 +01:00
|
|
|
nv = strlen(vowels);
|
2018-04-28 15:58:14 +02:00
|
|
|
ns = strlen(handle_start);
|
2014-12-25 22:38:01 +01:00
|
|
|
|
|
|
|
for (s = rng_int() % 3 + 2; s > 0; s--) {
|
2018-02-25 18:30:20 +01:00
|
|
|
int v;
|
2014-12-25 22:38:01 +01:00
|
|
|
if (x > 0) {
|
|
|
|
k = rng_int() % (int)nk;
|
|
|
|
name[p] = kons[k];
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
k = rng_int() % (int)ns;
|
2018-04-28 15:58:14 +02:00
|
|
|
name[p] = handle_start[k];
|
2014-12-25 22:38:01 +01:00
|
|
|
p++;
|
|
|
|
}
|
|
|
|
v = rng_int() % (int)nv;
|
|
|
|
name[p] = vowels[v];
|
|
|
|
p++;
|
|
|
|
if (rng_int() % 3 == 2 || s == 1) {
|
|
|
|
e = rng_int() % (int)ne;
|
2018-04-28 15:58:14 +02:00
|
|
|
name[p] = handle_end[e];
|
2014-12-25 22:38:01 +01:00
|
|
|
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
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
const luxury_type *ltype;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-12-25 22:38:01 +01:00
|
|
|
assert(r->land);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2015-06-01 19:56:53 +02:00
|
|
|
if (r->land->demands) {
|
2014-12-25 22:38:01 +01:00
|
|
|
freelist(r->land->demands);
|
2015-06-01 19:56:53 +02:00
|
|
|
r->land->demands = 0;
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
2014-12-25 22:38:01 +01:00
|
|
|
for (ltype = luxurytypes; ltype; ltype = ltype->next) {
|
|
|
|
struct demand *dmd = malloc(sizeof(struct demand));
|
2018-12-15 19:38:40 +01:00
|
|
|
if (!dmd) abort();
|
2014-12-25 22:38:01 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2016-10-24 13:54:53 +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;
|
|
|
|
}
|
|
|
|
|
2018-07-13 23:37:45 +02:00
|
|
|
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);
|
2018-07-13 23:37:45 +02:00
|
|
|
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 */
|
2014-12-25 22:38:01 +01:00
|
|
|
const terrain_type *oldterrain = r->terrain;
|
|
|
|
rawmaterial **lrm = &r->resources;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-12-25 22:38:01 +01:00
|
|
|
assert(terrain);
|
|
|
|
while (*lrm) {
|
|
|
|
rawmaterial *rm = *lrm;
|
|
|
|
const resource_type *rtype = NULL;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
2014-12-25 22:38:01 +01:00
|
|
|
if (terrain->production != NULL) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; terrain->production[i].type; ++i) {
|
2017-03-22 20:37:09 +01:00
|
|
|
if (rm->rtype == terrain->production[i].type) {
|
|
|
|
rtype = rm->rtype;
|
2014-12-25 22:38:01 +01:00
|
|
|
break;
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
}
|
2014-12-25 22:38:01 +01: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) {
|
2015-10-29 11:17:31 +01:00
|
|
|
free_land(r->land);
|
2014-12-25 22:38:01 +01:00
|
|
|
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);
|
2014-12-25 22:38:01 +01:00
|
|
|
return;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2014-12-25 22:38:01 +01:00
|
|
|
if (r->land) {
|
|
|
|
int d;
|
|
|
|
for (d = 0; d != MAXDIRECTIONS; ++d) {
|
|
|
|
rsetroad(r, d, 0);
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
2014-12-25 22:38:01 +01: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));
|
2018-12-15 19:38:40 +01:00
|
|
|
if (!r->land) abort();
|
2014-12-25 22:38:01 +01:00
|
|
|
r->land->ownership = NULL;
|
|
|
|
region_set_morale(r, MORALE_DEFAULT, -1);
|
|
|
|
region_setname(r, makename());
|
2016-10-24 13:47:38 +02:00
|
|
|
fix_demand(r);
|
2014-12-25 22:38:01 +01:00
|
|
|
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));
|
2018-12-15 19:38:40 +01:00
|
|
|
if (!sr) abort();
|
2014-12-25 22:38:01 +01:00
|
|
|
}
|
|
|
|
sr->next = nb;
|
|
|
|
sr->type = sale->type;
|
|
|
|
sr->value = 1;
|
|
|
|
nb = sr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
sr->value++;
|
|
|
|
++mnr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!nb) {
|
2017-02-18 21:15:14 +01:00
|
|
|
/* TODO: this is really lame */
|
2014-12-25 22:38:01 +01:00
|
|
|
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);
|
2014-12-25 22:38:01 +01:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
2018-07-13 23:37:45 +02:00
|
|
|
if (oldterrain == NULL || terrain->size != oldterrain->size) {
|
|
|
|
init_region(r);
|
2014-12-25 22:38:01 +01:00
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** ENNO:
|
|
|
|
* ich denke, das das hier nicht sein sollte.
|
2015-09-11 22:11:59 +02:00
|
|
|
* 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"
|
2017-12-04 19:20:48 +01:00
|
|
|
int max_production(const region * r)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2018-12-17 12:07:38 +01:00
|
|
|
/* muss rterrain(r) sein, nicht rterrain() wegen rekursion */
|
2014-12-25 22:38:01 +01:00
|
|
|
int p = r->terrain->size;
|
2017-08-21 19:43:35 +02:00
|
|
|
if (curse_active(get_curse(r->attribs, &ct_drought))) {
|
2014-12-25 22:38:01 +01:00
|
|
|
p /= 2;
|
2017-08-21 19:43:35 +02:00
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-12-25 22:38:01 +01: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
|
|
|
{
|
2017-09-21 16:35:18 +02:00
|
|
|
resolve(RESOLVE_REGION | r->uid, r);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2018-11-01 21:08:59 +01:00
|
|
|
int read_region_reference(gamedata * data, region **rp)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2016-02-13 20:38:32 +01:00
|
|
|
struct storage * store = data->store;
|
2017-09-21 16:26:53 +02:00
|
|
|
int id = 0;
|
|
|
|
|
2017-09-21 16:35:18 +02:00
|
|
|
READ_INT(store, &id);
|
|
|
|
*rp = findregionbyid(id);
|
|
|
|
if (*rp == NULL) {
|
2018-11-01 21:20:11 +01:00
|
|
|
*rp = region_create(id);
|
2014-12-25 22:38:01 +01:00
|
|
|
}
|
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
|
|
|
{
|
2014-12-25 22:38:01 +01: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,
|
2014-12-25 22:38:01 +01:00
|
|
|
const struct faction *viewer)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-12-25 22:38:01 +01: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,
|
2014-12-25 22:38:01 +01:00
|
|
|
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));
|
2018-12-15 19:38:40 +01:00
|
|
|
if (!imsg) abort();
|
2014-12-25 22:38:01 +01:00
|
|
|
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
|
|
|
{
|
2017-01-25 18:17:19 +01: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;
|
|
|
|
}
|
2014-12-25 22:38:01 +01:00
|
|
|
}
|
|
|
|
return NULL;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2015-11-12 16:09:26 +01: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
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
assert(rule_region_owners());
|
|
|
|
if (r->land && r->land->ownership) {
|
|
|
|
region_owner *own = r->land->ownership;
|
2015-11-12 16:09:26 +01:00
|
|
|
return own->owner ? own->owner->alliance : (own->last_owner? own->last_owner->alliance : NULL);
|
2014-12-25 22:38:01 +01:00
|
|
|
}
|
|
|
|
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
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
assert(rule_region_owners());
|
|
|
|
if (r->land) {
|
|
|
|
if (!r->land->ownership) {
|
2018-12-15 19:38:40 +01:00
|
|
|
region_owner *ro = malloc(sizeof(region_owner));
|
|
|
|
if (!ro) abort();
|
2014-12-25 22:38:01 +01:00
|
|
|
assert(region_get_morale(r) == MORALE_DEFAULT);
|
2018-12-15 19:38:40 +01:00
|
|
|
ro->owner = NULL;
|
|
|
|
ro->last_owner = NULL;
|
|
|
|
ro->flags = 0;
|
|
|
|
r->land->ownership = ro;
|
2014-12-25 22:38:01 +01:00
|
|
|
}
|
|
|
|
r->land->ownership->since_turn = turn;
|
|
|
|
r->land->ownership->morale_turn = turn;
|
|
|
|
assert(r->land->ownership->owner != owner);
|
2015-11-12 16:09:26 +01:00
|
|
|
r->land->ownership->last_owner = r->land->ownership->owner;
|
2014-12-25 22:38:01 +01:00
|
|
|
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
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
faction *f = NULL;
|
|
|
|
assert(rule_region_owners());
|
|
|
|
if (r->land) {
|
2017-04-28 21:29:42 +02:00
|
|
|
building *bowner = largestbuilding(r, cmp_current_owner, false);
|
|
|
|
building *blargest = largestbuilding(r, cmp_taxes, false);
|
2014-12-25 22:38:01 +01:00
|
|
|
if (blargest) {
|
|
|
|
if (!bowner || bowner->size < blargest->size) {
|
|
|
|
/* region owners update? */
|
2015-11-12 16:09:26 +01:00
|
|
|
unit *new_owner = building_owner(blargest);
|
2014-12-25 22:38:01 +01:00
|
|
|
f = region_get_owner(r);
|
2015-11-12 16:09:26 +01:00
|
|
|
if (new_owner == NULL) {
|
2014-12-25 22:38:01 +01:00
|
|
|
if (f) {
|
|
|
|
region_set_owner(r, NULL, turn);
|
|
|
|
f = NULL;
|
|
|
|
}
|
|
|
|
}
|
2015-11-12 16:09:26 +01:00
|
|
|
else if (new_owner->faction != f) {
|
2014-12-25 22:38:01 +01:00
|
|
|
if (!r->land->ownership) {
|
|
|
|
/* there has never been a prior owner */
|
|
|
|
region_set_morale(r, MORALE_DEFAULT, turn);
|
|
|
|
}
|
2015-11-12 16:09:26 +01:00
|
|
|
else if (f || new_owner->faction != region_get_last_owner(r)) {
|
2014-12-25 22:38:01 +01:00
|
|
|
alliance *al = region_get_alliance(r);
|
2015-11-12 16:09:26 +01:00
|
|
|
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;
|
2014-12-25 22:38:01 +01:00
|
|
|
region_set_morale(r, morale, turn);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
region_set_morale(r, MORALE_TAKEOVER, turn);
|
|
|
|
}
|
|
|
|
}
|
2015-11-12 16:09:26 +01:00
|
|
|
region_set_owner(r, new_owner->faction, turn);
|
|
|
|
f = new_owner->faction;
|
2014-12-25 22:38:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2014-12-25 22:38:01 +01:00
|
|
|
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
|
|
|
{
|
2017-09-16 07:58:57 +02:00
|
|
|
assert(r->land);
|
|
|
|
free(r->land->display);
|
2017-12-28 18:29:40 +01:00
|
|
|
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)
|
|
|
|
{
|
2017-09-16 07:58:57 +02:00
|
|
|
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
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
if (r->land) {
|
|
|
|
free(r->land->name);
|
2017-12-28 18:29:40 +01:00
|
|
|
r->land->name = name ? str_strdup(name) : 0;
|
2014-12-25 22:38:01 +01:00
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
const char *region_getname(const region * r)
|
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
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)
|
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
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)
|
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
if (r->land) {
|
2018-01-21 11:14:55 +01:00
|
|
|
r->land->morale = morale;
|
2014-12-25 22:38:01 +01:00
|
|
|
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)
|
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
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)
|
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
if (r->land && r->land->ownership) {
|
|
|
|
return r->land->ownership->since_turn;
|
|
|
|
}
|
|
|
|
return INT_MIN;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2012-06-24 07:41:07 +02:00
|
|
|
bool is_mourning(const region * r, int in_turn)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-12-25 22:38:01 +01:00
|
|
|
int change = owner_change(r);
|
2017-04-16 15:52:34 +02:00
|
|
|
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
|
|
|
}
|