Merge branch 'develop'

This commit is contained in:
Enno Rehling 2017-11-09 18:04:41 +01:00
commit 34808a25c5
33 changed files with 348 additions and 130 deletions

View File

@ -13,7 +13,12 @@
"game.id" : 2, "game.id" : 2,
"orders.default": "work", "orders.default": "work",
"NewbieImmunity": 8, "NewbieImmunity": 8,
"modules.wormholes": true, "modules.market": false,
"modules.astralspace": true,
"modules.wormhole": true,
"modules.iceberg": true,
"modules.volcano": true,
"monsters.spawn.chance": 50,
"entertain.base": 0, "entertain.base": 0,
"entertain.perlevel": 20, "entertain.perlevel": 20,
"taxing.perlevel": 20, "taxing.perlevel": 20,

View File

@ -32,8 +32,10 @@
"database.gameid": 7, "database.gameid": 7,
"NewbieImmunity": 4, "NewbieImmunity": 4,
"modules.astralspace": false, "modules.astralspace": false,
"modules.wormholes": false, "modules.wormhole": false,
"modules.markets": true, "modules.market": true,
"modules.iceberg": false,
"modules.volcano": true,
"magic.regeneration": 0.75, "magic.regeneration": 0.75,
"magic.power": 0.5, "magic.power": 0.5,
"resource.factor": 0.25, "resource.factor": 0.25,

View File

@ -807,7 +807,6 @@ end
function test_swim_and_survive() function test_swim_and_survive()
local r = region.create(0, 0, "plain") local r = region.create(0, 0, "plain")
local f = create_faction('human') local f = create_faction('human')
f.nam = "chaos"
local u = unit.create(f, r, 1) local u = unit.create(f, r, 1)
process_orders() process_orders()
r.terrain = "ocean" r.terrain = "ocean"

View File

@ -1141,7 +1141,7 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile)
const weapon_type *dwtype = NULL; const weapon_type *dwtype = NULL;
const weapon_type *awtype = NULL; const weapon_type *awtype = NULL;
const weapon *weapon; const weapon *weapon;
variant res = frac_make(1, 1); variant res = frac_one;
int rda, sk = 0, sd; int rda, sk = 0, sd;
bool magic = false; bool magic = false;
@ -1185,6 +1185,7 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile)
} }
if (magic) { if (magic) {
res = frac_sub(frac_one, res);
res = frac_mul(frac_make(da, 1), res); res = frac_mul(frac_make(da, 1), res);
da = res.sa[0] / res.sa[1]; da = res.sa[0] / res.sa[1];
} }

View File

@ -855,8 +855,6 @@ void cr_output_unit(stream *out, const region * r, const faction * f,
stream_printf(out, "\"%s\";Typ\n", stream_printf(out, "\"%s\";Typ\n",
translate(zRace, LOC(lang, zRace))); translate(zRace, LOC(lang, zRace)));
if (u->faction == f && irace != u_race(u)) { if (u->faction == f && irace != u_race(u)) {
assert(skill_enabled(SK_STEALTH)
|| !"we're resetting this on load, so.. ircase should never be used");
zRace = rc_name_s(u_race(u), NAME_PLURAL); zRace = rc_name_s(u_race(u), NAME_PLURAL);
stream_printf(out, "\"%s\";wahrerTyp\n", stream_printf(out, "\"%s\";wahrerTyp\n",
translate(zRace, LOC(lang, zRace))); translate(zRace, LOC(lang, zRace)));

View File

@ -1067,7 +1067,7 @@ static int required(int want, variant save)
{ {
int req = (int)(want * save.sa[0] / save.sa[1]); int req = (int)(want * save.sa[0] / save.sa[1]);
int r = want * save.sa[0] % save.sa[1]; int r = want * save.sa[0] % save.sa[1];
if (r>0) ++req; if (r > 0) ++req;
return req; return req;
} }
@ -1173,7 +1173,7 @@ attrib_allocation(const resource_type * rtype, region * r, allocation * alist)
int x = avail * want / nreq; int x = avail * want / nreq;
int rx = (avail * want) % nreq; int rx = (avail * want) % nreq;
/* Wenn Rest, dann wuerfeln, ob ich was bekomme: */ /* Wenn Rest, dann wuerfeln, ob ich was bekomme: */
if (rx>0 && rng_int() % nreq < rx) ++x; if (rx > 0 && rng_int() % nreq < rx) ++x;
avail -= x; avail -= x;
nreq -= want; nreq -= want;
al->get = x * al->save.sa[1] / al->save.sa[0]; al->get = x * al->save.sa[1] / al->save.sa[0];
@ -1446,24 +1446,19 @@ static void expandbuying(region * r, request * buyorders)
const resource_type *rsilver = get_resourcetype(R_SILVER); const resource_type *rsilver = get_resourcetype(R_SILVER);
int max_products; int max_products;
unit *u; unit *u;
static struct trade { struct trade {
const luxury_type *type; const luxury_type *type;
int number; int number;
int multi; int multi;
} trades[MAXLUXURIES], *trade; } trades[MAXLUXURIES], *trade;
static int ntrades = 0; int ntrades = 0;
int i;
const luxury_type *ltype; const luxury_type *ltype;
if (ntrades == 0) { for (ntrades = 0, ltype = luxurytypes; ltype; ltype = ltype->next) {
for (ntrades = 0, ltype = luxurytypes; ltype; ltype = ltype->next) { assert(ntrades < MAXLUXURIES);
assert(ntrades < MAXLUXURIES); trades[ntrades].number = 0;
trades[ntrades++].type = ltype; trades[ntrades].multi = 1;
} trades[ntrades++].type = ltype;
}
for (i = 0; i != ntrades; ++i) {
trades[i].number = 0;
trades[i].multi = 1;
} }
if (!buyorders) if (!buyorders)
@ -1490,7 +1485,7 @@ static void expandbuying(region * r, request * buyorders)
int price, multi; int price, multi;
ltype = g_requests[j].type.ltype; ltype = g_requests[j].type.ltype;
trade = trades; trade = trades;
while (trade->type != ltype) while (trade->type && trade->type != ltype)
++trade; ++trade;
multi = trade->multi; multi = trade->multi;
price = ltype->price * multi; price = ltype->price * multi;
@ -1595,17 +1590,10 @@ static void buy(unit * u, request ** buyorders, struct order *ord)
return; return;
} }
if (u_race(u) == get_race(RC_INSECT)) { /* Entweder man ist Insekt in Sumpf/Wueste, oder es muss
/* entweder man ist insekt, oder... */ * einen Handelsposten in der Region geben: */
if (r->terrain != newterrain(T_SWAMP) && r->terrain != newterrain(T_DESERT) if (u_race(u) != get_race(RC_INSECT) || (r->terrain == newterrain(T_SWAMP) || r->terrain == newterrain(T_DESERT))) {
&& !rbuildings(r)) { building *b = NULL;
cmistake(u, ord, 119, MSG_COMMERCE);
return;
}
}
else {
/* ...oder in der Region mu<6D> es eine Burg geben. */
building *b = 0;
if (r->buildings) { if (r->buildings) {
static int cache; static int cache;
static const struct building_type *bt_castle; static const struct building_type *bt_castle;
@ -2724,7 +2712,7 @@ static void expandloot(region * r, request * lootorders)
/* Lowering morale by 1 depending on the looted money (+20%) */ /* Lowering morale by 1 depending on the looted money (+20%) */
m = region_get_morale(r); m = region_get_morale(r);
if (m && startmoney>0) { if (m && startmoney > 0) {
if (rng_int() % 100 < 20 + (looted * 80) / startmoney) { if (rng_int() % 100 < 20 + (looted * 80) / startmoney) {
/*Nur Moral -1, turns is not changed, so the first time nothing happens if the morale is good*/ /*Nur Moral -1, turns is not changed, so the first time nothing happens if the morale is good*/
region_set_morale(r, m - 1, -1); region_set_morale(r, m - 1, -1);
@ -2985,7 +2973,7 @@ void produce(struct region *r)
static const struct building_type *caravan_bt; static const struct building_type *caravan_bt;
static int rc_cache; static int rc_cache;
static const race *rc_insect, *rc_aquarian; static const race *rc_insect, *rc_aquarian;
if (bt_changed(&bt_cache)) { if (bt_changed(&bt_cache)) {
caravan_bt = bt_find("caravan"); caravan_bt = bt_find("caravan");
} }

View File

@ -14,6 +14,7 @@
#include <kernel/resources.h> #include <kernel/resources.h>
#include <kernel/ship.h> #include <kernel/ship.h>
#include <kernel/terrain.h> #include <kernel/terrain.h>
#include <kernel/terrainid.h>
#include <kernel/unit.h> #include <kernel/unit.h>
#include <util/attrib.h> #include <util/attrib.h>
@ -170,6 +171,133 @@ static void test_normals_recruit(CuTest * tc) {
test_cleanup(); test_cleanup();
} }
/**
* Create any terrain types that are used by the trade rules.
*
* This should prevent newterrain from returning NULL.
*/
static void setup_terrains(CuTest *tc) {
test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION | FLY_INTO);
test_create_terrain("ocean", SEA_REGION | SWIM_INTO | FLY_INTO);
test_create_terrain("swamp", LAND_REGION | WALK_INTO | FLY_INTO);
test_create_terrain("desert", LAND_REGION | WALK_INTO | FLY_INTO);
test_create_terrain("mountain", LAND_REGION | WALK_INTO | FLY_INTO);
init_terrains();
CuAssertPtrNotNull(tc, newterrain(T_MOUNTAIN));
CuAssertPtrNotNull(tc, newterrain(T_OCEAN));
CuAssertPtrNotNull(tc, newterrain(T_PLAIN));
CuAssertPtrNotNull(tc, newterrain(T_SWAMP));
CuAssertPtrNotNull(tc, newterrain(T_DESERT));
}
static region *setup_trade_region(CuTest *tc, const struct terrain_type *terrain) {
region *r;
item_type *it_luxury;
struct locale * lang = default_locale;
new_luxurytype(it_luxury = test_create_itemtype("jewel"), 5);
locale_setstring(lang, it_luxury->rtype->_name, it_luxury->rtype->_name);
CuAssertStrEquals(tc, it_luxury->rtype->_name, LOC(lang, resourcename(it_luxury->rtype, 0)));
new_luxurytype(it_luxury = test_create_itemtype("balm"), 5);
locale_setstring(lang, it_luxury->rtype->_name, it_luxury->rtype->_name);
CuAssertStrEquals(tc, it_luxury->rtype->_name, LOC(lang, resourcename(it_luxury->rtype, 0)));
r = test_create_region(0, 0, terrain);
return r;
}
static unit *setup_trade_unit(CuTest *tc, region *r, const struct race *rc) {
unit *u;
UNUSED_ARG(tc);
u = test_create_unit(test_create_faction(rc), r);
set_level(u, SK_TRADE, 2);
return u;
}
static void test_trade_insect(CuTest *tc) {
/* Insekten können in Wüsten und Sümpfen auch ohne Burgen handeln. */
unit *u;
region *r;
const item_type *it_luxury;
const item_type *it_silver;
test_setup();
init_resources();
test_create_locale();
setup_terrains(tc);
r = setup_trade_region(tc, get_terrain("swamp"));
init_terrains();
it_luxury = r_luxury(r);
CuAssertPtrNotNull(tc, it_luxury);
it_silver = get_resourcetype(R_SILVER)->itype;
u = setup_trade_unit(tc, r, test_create_race("insect"));
unit_addorder(u, create_order(K_BUY, u->faction->locale, "1 %s",
LOC(u->faction->locale, resourcename(it_luxury->rtype, 0))));
set_item(u, it_silver, 10);
produce(u->region);
CuAssertIntEquals(tc, 1, get_item(u, it_luxury));
CuAssertIntEquals(tc, 5, get_item(u, it_silver));
terraform_region(r, get_terrain("swamp"));
test_cleanup();
}
static void test_buy_cmd(CuTest *tc) {
region * r;
unit *u;
building *b;
const resource_type *rt_silver;
const item_type *it_luxury;
test_setup();
init_resources();
test_create_locale();
setup_terrains(tc);
r = setup_trade_region(tc, test_create_terrain("swamp", LAND_REGION));
init_terrains();
it_luxury = r_luxury(r);
CuAssertPtrNotNull(tc, it_luxury);
rt_silver = get_resourcetype(R_SILVER);
CuAssertPtrNotNull(tc, rt_silver);
CuAssertPtrNotNull(tc, rt_silver->itype);
u = test_create_unit(test_create_faction(NULL), r);
unit_addorder(u, create_order(K_BUY, u->faction->locale, "1 %s", LOC(u->faction->locale, resourcename(it_luxury->rtype, 0))));
set_item(u, rt_silver->itype, 1000);
produce(r);
CuAssertPtrNotNullMsg(tc, "trading requires a castle", test_find_messagetype(u->faction->msgs, "error119"));
test_clear_messages(u->faction);
freset(u, UFL_LONGACTION);
b = test_create_building(r, test_create_buildingtype("castle"));
produce(r);
CuAssertPtrNotNullMsg(tc, "castle must have size >=2", test_find_messagetype(u->faction->msgs, "error119"));
test_clear_messages(u->faction);
freset(u, UFL_LONGACTION);
b->size = 2;
produce(r);
CuAssertPtrEquals(tc, NULL, test_find_messagetype(u->faction->msgs, "error119"));
CuAssertPtrNotNullMsg(tc, "traders need SK_TRADE skill", test_find_messagetype(u->faction->msgs, "error102"));
test_clear_messages(u->faction);
freset(u, UFL_LONGACTION);
/* at last, the happy case: */
set_level(u, SK_TRADE, 1);
produce(r);
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "buy"));
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "buyamount"));
CuAssertIntEquals(tc, 1, get_item(u, it_luxury));
CuAssertIntEquals(tc, 995, get_item(u, rt_silver->itype));
test_cleanup();
}
typedef struct request { typedef struct request {
struct request *next; struct request *next;
struct unit *unit; struct unit *unit;
@ -568,6 +696,8 @@ CuSuite *get_economy_suite(void)
SUITE_ADD_TEST(suite, test_normals_recruit); SUITE_ADD_TEST(suite, test_normals_recruit);
SUITE_ADD_TEST(suite, test_heroes_dont_recruit); SUITE_ADD_TEST(suite, test_heroes_dont_recruit);
SUITE_ADD_TEST(suite, test_tax_cmd); SUITE_ADD_TEST(suite, test_tax_cmd);
SUITE_ADD_TEST(suite, test_buy_cmd);
SUITE_ADD_TEST(suite, test_trade_insect);
SUITE_ADD_TEST(suite, test_maintain_buildings); SUITE_ADD_TEST(suite, test_maintain_buildings);
SUITE_ADD_TEST(suite, test_recruit); SUITE_ADD_TEST(suite, test_recruit);
return suite; return suite;

View File

@ -76,7 +76,7 @@ int *casualties)
killed += terminate(dt, *at, AT_SPELL, damage, 1); killed += terminate(dt, *at, AT_SPELL, damage, 1);
} }
} while (force && killed < enemies); } while (force && killed < enemies);
if (casualties) if (killed > 0 && casualties)
*casualties = killed; *casualties = killed;
return true; return true;
} }

View File

@ -216,7 +216,6 @@ static void test_alliance_cmd_transfer(CuTest *tc) {
test_cleanup(); test_cleanup();
} }
CuSuite *get_alliance_suite(void) CuSuite *get_alliance_suite(void)
{ {
CuSuite *suite = CuSuiteNew(); CuSuite *suite = CuSuiteNew();

View File

@ -405,7 +405,7 @@ static int required(int size, int msize, int maxneed)
static int matmod(const unit * u, const resource_type * rtype, int value) static int matmod(const unit * u, const resource_type * rtype, int value)
{ {
if (rtype->modifiers) { if (rtype->modifiers) {
variant save = frac_make(1, 1); variant save = frac_one;
const struct building_type *btype = NULL; const struct building_type *btype = NULL;
const struct race *rc = u_race(u); const struct race *rc = u_race(u);
resource_mod *mod; resource_mod *mod;

View File

@ -224,7 +224,7 @@ extern "C" {
/* convenience: */ /* convenience: */
item *i_change(item ** items, const item_type * it, int delta); item *i_change(item ** items, const item_type * it, int delta);
int i_get(const item * i, const item_type * it); int i_get(const item * items, const item_type * it);
/* creation */ /* creation */
resource_type *rt_get_or_create(const char *name); resource_type *rt_get_or_create(const char *name);
@ -300,7 +300,6 @@ extern "C" {
void register_resources(void); void register_resources(void);
void init_resources(void); void init_resources(void);
void init_itemtypes(void);
void register_item_give(int(*foo) (struct unit *, struct unit *, void register_item_give(int(*foo) (struct unit *, struct unit *,
const struct item_type *, int, struct order *), const char *name); const struct item_type *, int, struct order *), const char *name);

View File

@ -711,7 +711,7 @@ const item_type *r_luxury(const region * r)
{ {
struct demand *dmd; struct demand *dmd;
if (r->land) { if (r->land) {
assert(r->land->demands || !"need to call fix_demands on a region"); assert(r->land->demands || !"need to call fix_demand on a region");
for (dmd = r->land->demands; dmd; dmd = dmd->next) { for (dmd = r->land->demands; dmd; dmd = dmd->next) {
if (dmd->value == 0) if (dmd->value == 0)
return dmd->type->itype; return dmd->type->itype;

View File

@ -37,9 +37,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#define MAXTERRAINS 14 static const char *terrainnames[MAXTERRAINS] = {
const char *terraindata[MAXTERRAINS] = {
"ocean", "ocean",
"plain", "plain",
"swamp", "swamp",
@ -57,6 +55,17 @@ const char *terraindata[MAXTERRAINS] = {
}; };
static terrain_type *registered_terrains; static terrain_type *registered_terrains;
static int terrain_changes = 1;
bool terrain_changed(int *cache) {
assert(cache);
if (*cache != terrain_changes) {
*cache = terrain_changes;
return true;
}
return false;
}
void free_terrains(void) void free_terrains(void)
{ {
@ -76,6 +85,7 @@ void free_terrains(void)
} }
free(t); free(t);
} }
++terrain_changes;
} }
const terrain_type *terrains(void) const terrain_type *terrains(void)
@ -110,6 +120,7 @@ const terrain_type *get_terrain(const char *name) {
terrain_type * get_or_create_terrain(const char *name) { terrain_type * get_or_create_terrain(const char *name) {
terrain_type *terrain = terrain_find_i(name); terrain_type *terrain = terrain_find_i(name);
if (!terrain) { if (!terrain) {
++terrain_changes;
terrain = (terrain_type *)calloc(sizeof(terrain_type), 1); terrain = (terrain_type *)calloc(sizeof(terrain_type), 1);
if (terrain) { if (terrain) {
terrain->_name = strdup(name); terrain->_name = strdup(name);
@ -128,11 +139,20 @@ static const terrain_type *newterrains[MAXTERRAINS];
const struct terrain_type *newterrain(terrain_t t) const struct terrain_type *newterrain(terrain_t t)
{ {
if (t == NOTERRAIN) const struct terrain_type *result;
if (t == NOTERRAIN) {
return NULL; return NULL;
}
assert(t >= 0); assert(t >= 0);
assert(t < MAXTERRAINS); assert(t < MAXTERRAINS);
return newterrains[t]; result = newterrains[t];
if (!result) {
result = newterrains[t] = get_terrain(terrainnames[t]);
}
if (!result) {
log_error("no such terrain: %s.", terrainnames[t]);
}
return result;
} }
terrain_t oldterrain(const struct terrain_type * terrain) terrain_t oldterrain(const struct terrain_type * terrain)
@ -176,8 +196,8 @@ void init_terrains(void)
const terrain_type *newterrain = newterrains[t]; const terrain_type *newterrain = newterrains[t];
if (newterrain != NULL) if (newterrain != NULL)
continue; continue;
if (terraindata[t] != NULL) { if (terrainnames[t] != NULL) {
newterrain = get_terrain(terraindata[t]); newterrain = get_terrain(terrainnames[t]);
if (newterrain != NULL) { if (newterrain != NULL) {
newterrains[t] = newterrain; newterrains[t] = newterrain;
} }

View File

@ -18,6 +18,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#ifndef TERRAIN_H #ifndef TERRAIN_H
#define TERRAIN_H #define TERRAIN_H
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -68,12 +69,13 @@ extern "C" {
struct terrain_type *next; struct terrain_type *next;
} terrain_type; } terrain_type;
extern terrain_type *get_or_create_terrain(const char *name); terrain_type *get_or_create_terrain(const char *name);
extern const terrain_type *terrains(void); const terrain_type *terrains(void);
extern const terrain_type *get_terrain(const char *name); const terrain_type *get_terrain(const char *name);
extern const char *terrain_name(const struct region *r); const char *terrain_name(const struct region *r);
bool terrain_changed(int *cache);
extern void init_terrains(void); void init_terrains(void);
void free_terrains(void); void free_terrains(void);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -14,7 +14,7 @@
extern "C" { extern "C" {
#endif #endif
enum { typedef enum {
T_OCEAN = 0, T_OCEAN = 0,
T_PLAIN, T_PLAIN,
T_SWAMP, T_SWAMP,
@ -29,8 +29,9 @@ extern "C" {
T_VOLCANO_SMOKING, T_VOLCANO_SMOKING,
T_ICEBERG_SLEEP, T_ICEBERG_SLEEP,
T_ICEBERG, T_ICEBERG,
NOTERRAIN = (terrain_t) - 1 MAXTERRAINS,
}; NOTERRAIN = - 1
} terrain_t;
extern const struct terrain_type *newterrain(terrain_t t); extern const struct terrain_type *newterrain(terrain_t t);
extern terrain_t oldterrain(const struct terrain_type *terrain); extern terrain_t oldterrain(const struct terrain_type *terrain);

View File

@ -22,7 +22,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <settings.h> #include <settings.h>
#include <util/variant.h> #include <util/variant.h>
typedef short terrain_t;
typedef short item_t; typedef short item_t;
struct attrib; struct attrib;

View File

@ -5,7 +5,7 @@
#ifndef ERESSEA_VERSION #ifndef ERESSEA_VERSION
/* the version number, if it was not passed to make with -D */ /* the version number, if it was not passed to make with -D */
#define ERESSEA_VERSION "3.14.0" #define ERESSEA_VERSION "3.15.0"
#endif #endif
const char *eressea_version(void) { const char *eressea_version(void) {

View File

@ -41,6 +41,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "teleport.h" #include "teleport.h"
#include "calendar.h" #include "calendar.h"
#include "guard.h" #include "guard.h"
#include "volcano.h"
/* attributes includes */ /* attributes includes */
#include <attributes/racename.h> #include <attributes/racename.h>
@ -72,7 +73,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <kernel/spell.h> #include <kernel/spell.h>
#include <kernel/spellbook.h> #include <kernel/spellbook.h>
#include <kernel/terrain.h> #include <kernel/terrain.h>
#include <kernel/terrainid.h> /* for volcanoes in emigration (needs a flag) */ #include <kernel/terrainid.h>
#include <kernel/unit.h> #include <kernel/unit.h>
/* util includes */ /* util includes */
@ -247,9 +248,19 @@ static void calculate_emigration(region * r)
int rp = rpeasants(r); int rp = rpeasants(r);
int max_immigrants = MAX_IMMIGRATION(maxp - rp); int max_immigrants = MAX_IMMIGRATION(maxp - rp);
if (r->terrain == newterrain(T_VOLCANO)
|| r->terrain == newterrain(T_VOLCANO_SMOKING)) { if (volcano_module()) {
max_immigrants = max_immigrants / 10; static int terrain_cache;
static const terrain_type *t_volcano;
static const terrain_type *t_smoking;
if (terrain_changed(&terrain_cache)) {
t_volcano = newterrain(T_VOLCANO);
t_smoking = newterrain(T_VOLCANO_SMOKING);
}
if (r->terrain == t_volcano || r->terrain == t_smoking) {
max_immigrants = max_immigrants / 10;
}
} }
for (i = 0; max_immigrants > 0 && i != MAXDIRECTIONS; i++) { for (i = 0; max_immigrants > 0 && i != MAXDIRECTIONS; i++) {
@ -4215,7 +4226,7 @@ void turn_process(void)
init_processor(); init_processor();
process(); process();
if (config_get_int("modules.markets", 0)) { if (markets_module()) {
do_markets(); do_markets();
} }
} }
@ -4227,7 +4238,7 @@ void turn_end(void)
remove_empty_units(); remove_empty_units();
/* must happen AFTER age, because that would destroy them right away */ /* must happen AFTER age, because that would destroy them right away */
if (config_get_int("modules.wormholes", 0)) { if (config_get_int("modules.wormhole", 0)) {
wormholes_update(); wormholes_update();
} }

View File

@ -16,6 +16,7 @@
#include <kernel/region.h> #include <kernel/region.h>
#include <kernel/ship.h> #include <kernel/ship.h>
#include <kernel/terrain.h> #include <kernel/terrain.h>
#include <kernel/terrainid.h>
#include <kernel/unit.h> #include <kernel/unit.h>
#include <util/attrib.h> #include <util/attrib.h>
@ -864,11 +865,25 @@ static void test_peasant_luck_effect(CuTest *tc) {
test_cleanup(); test_cleanup();
} }
/**
* Create any terrain types that are used by demographics.
*
* This should prevent newterrain from returning NULL.
*/
static void setup_terrains(CuTest *tc) {
test_create_terrain("volcano", SEA_REGION | SWIM_INTO | FLY_INTO);
test_create_terrain("activevolcano", LAND_REGION | WALK_INTO | FLY_INTO);
init_terrains();
CuAssertPtrNotNull(tc, newterrain(T_VOLCANO));
CuAssertPtrNotNull(tc, newterrain(T_VOLCANO_SMOKING));
}
static void test_luck_message(CuTest *tc) { static void test_luck_message(CuTest *tc) {
region* r; region* r;
attrib *a; attrib *a;
test_setup(); test_setup();
setup_terrains(tc);
r = test_create_region(0, 0, NULL); r = test_create_region(0, 0, NULL);
rsetpeasants(r, 1); rsetpeasants(r, 1);
@ -1149,10 +1164,12 @@ static void test_ally_cmd_errors(CuTest *tc) {
static void test_name_cmd(CuTest *tc) { static void test_name_cmd(CuTest *tc) {
unit *u; unit *u;
faction *f; faction *f;
alliance *al;
order *ord; order *ord;
test_setup(); test_setup();
u = test_create_unit(f = test_create_faction(0), test_create_region(0, 0, 0)); u = test_create_unit(f = test_create_faction(0), test_create_region(0, 0, 0));
setalliance(f, al = makealliance(42, ""));
ord = create_order(K_NAME, f->locale, "%s ' Ho\tdor '", LOC(f->locale, parameters[P_UNIT])); ord = create_order(K_NAME, f->locale, "%s ' Ho\tdor '", LOC(f->locale, parameters[P_UNIT]));
name_cmd(u, ord); name_cmd(u, ord);
@ -1181,6 +1198,11 @@ static void test_name_cmd(CuTest *tc) {
CuAssertStrEquals(tc, "Hodor", u->region->land->name); CuAssertStrEquals(tc, "Hodor", u->region->land->name);
free_order(ord); free_order(ord);
ord = create_order(K_NAME, f->locale, "%s ' Ho\tdor '", LOC(f->locale, parameters[P_ALLIANCE]));
name_cmd(u, ord);
CuAssertStrEquals(tc, "Hodor", al->name);
free_order(ord);
test_cleanup(); test_cleanup();
} }

View File

@ -101,10 +101,11 @@ static dictionary *parse_config(const char *filename)
if (cfgpath) { if (cfgpath) {
join_path(cfgpath, filename, path, sizeof(path)); join_path(cfgpath, filename, path, sizeof(path));
log_debug("reading from configuration file %s\n", path); log_debug("reading from configuration file %s\n", path);
d = iniparser_load(path); d = iniparser_load(path);
} else { }
else {
log_debug("reading from configuration file %s\n", filename); log_debug("reading from configuration file %s\n", filename);
d = iniparser_load(filename); d = iniparser_load(filename);
} }
if (d) { if (d) {
config_set_from(d, valid_keys); config_set_from(d, valid_keys);
@ -168,8 +169,7 @@ static int verbosity_to_flags(int verbosity) {
static int parse_args(int argc, char **argv) static int parse_args(int argc, char **argv)
{ {
int i; int i;
int log_stderr = LOG_CPERROR; int log_stderr, log_flags = 2;
int log_flags = LOG_CPERROR | LOG_CPWARNING | LOG_CPINFO;
for (i = 1; i != argc; ++i) { for (i = 1; i != argc; ++i) {
char *argi = argv[i]; char *argi = argv[i];
@ -179,9 +179,9 @@ static int parse_args(int argc, char **argv)
else if (argi[1] == '-') { /* long format */ else if (argi[1] == '-') { /* long format */
if (strcmp(argi + 2, "version") == 0) { if (strcmp(argi + 2, "version") == 0) {
printf("Eressea version %s, " printf("Eressea version %s, "
"Copyright (C) 2017 Enno Rehling et al.\n", "Copyright (C) 2017 Enno Rehling et al.\n",
eressea_version()); eressea_version());
return 1; return 1;
#ifdef USE_CURSES #ifdef USE_CURSES
} }
else if (strcmp(argi + 2, "color") == 0) { else if (strcmp(argi + 2, "color") == 0) {
@ -300,8 +300,8 @@ int main(int argc, char **argv)
setup_signal_handler(); setup_signal_handler();
/* parse arguments again, to override ini file */ /* parse arguments again, to override ini file */
err = parse_args(argc, argv); err = parse_args(argc, argv);
if (err!=0) { if (err != 0) {
return (err>0) ? 0 : err; return (err > 0) ? 0 : err;
} }
d = parse_config(inifile); d = parse_config(inifile);
if (!d) { if (!d) {

View File

@ -71,7 +71,7 @@ attrib_type at_market = {
bool markets_module(void) bool markets_module(void)
{ {
return (bool)config_get_int("modules.markets", 0); return (bool)config_get_int("modules.market", 0);
} }
void do_markets(void) void do_markets(void)

View File

@ -874,19 +874,27 @@ void spawn_dragons(void)
{ {
region *r; region *r;
faction *monsters = get_or_create_monsters(); faction *monsters = get_or_create_monsters();
int minage = config_get_int("monsters.spawn.min_age", 100);
int spawn_chance = 100 * config_get_int("monsters.spawn.chance", 100);
if (spawn_chance <= 0) {
/* monster spawning disabled */
return;
}
for (r = regions; r; r = r->next) { for (r = regions; r; r = r->next) {
unit *u; unit *u;
if (r->age < minage) {
continue;
}
if (fval(r->terrain, SEA_REGION)) { if (fval(r->terrain, SEA_REGION)) {
if (rng_int() % 10000 < 1) { if (rng_int() % spawn_chance < 1) {
u = spawn_seaserpent(r, monsters); u = spawn_seaserpent(r, monsters);
} }
} }
else if ((r->terrain == newterrain(T_GLACIER) else if ((r->terrain == newterrain(T_GLACIER)
|| r->terrain == newterrain(T_SWAMP) || r->terrain == newterrain(T_SWAMP)
|| r->terrain == newterrain(T_DESERT)) || r->terrain == newterrain(T_DESERT))
&& rng_int() % 10000 < (5 + 100 * chaosfactor(r))) { && rng_int() % spawn_chance < (5 + 100 * chaosfactor(r))) {
if (chance(0.80)) { if (chance(0.80)) {
u = create_unit(r, monsters, nrand(60, 20) + 1, get_race(RC_FIREDRAGON), 0, NULL, NULL); u = create_unit(r, monsters, nrand(60, 20) + 1, get_race(RC_FIREDRAGON), 0, NULL, NULL);
} }

View File

@ -787,22 +787,22 @@ static void msg_to_ship_inmates(ship *sh, unit **firstu, unit **lastu, message *
msg_release(msg); msg_release(msg);
} }
region * drift_target(ship *sh) { direction_t drift_target(ship *sh) {
int d, d_offset = rng_int() % MAXDIRECTIONS; direction_t d, dir = rng_int() % MAXDIRECTIONS;
region *rnext = NULL; direction_t result = NODIRECTION;
for (d = 0; d != MAXDIRECTIONS; ++d) { for (d = 0; d != MAXDIRECTIONS; ++d) {
region *rn; region *rn;
direction_t dir = (direction_t)((d + d_offset) % MAXDIRECTIONS); direction_t dn = (direction_t)((d + dir) % MAXDIRECTIONS);
rn = rconnect(sh->region, dir); rn = rconnect(sh->region, dn);
if (rn != NULL && check_ship_allowed(sh, rn) >= 0) { if (rn != NULL && check_ship_allowed(sh, rn) >= 0) {
rnext = rn; result = dn;
if (!fval(rnext->terrain, SEA_REGION)) { if (!fval(rn->terrain, SEA_REGION)) {
/* prefer drifting towards non-ocean regions */ /* prefer drifting towards non-ocean regions */
break; break;
} }
} }
} }
return rnext; return result;
} }
static void drifting_ships(region * r) static void drifting_ships(region * r)
@ -817,7 +817,7 @@ static void drifting_ships(region * r)
region *rnext = NULL; region *rnext = NULL;
region_list *route = NULL; region_list *route = NULL;
unit *firstu = r->units, *lastu = NULL, *captain; unit *firstu = r->units, *lastu = NULL, *captain;
direction_t dir = 0; direction_t dir = NODIRECTION;
double ovl; double ovl;
if (sh->type->fishing > 0) { if (sh->type->fishing > 0) {
@ -846,13 +846,13 @@ static void drifting_ships(region * r)
} }
ovl = overload(r, sh); ovl = overload(r, sh);
if (ovl >= overload_start()) { if (ovl < overload_start()) {
rnext = NULL;
}
else {
/* Auswahl einer Richtung: Zuerst auf Land, dann /* Auswahl einer Richtung: Zuerst auf Land, dann
* zufällig. Falls unmögliches Resultat: vergiß es. */ * zufällig. Falls unmögliches Resultat: vergiß es. */
rnext = drift_target(sh); dir = drift_target(sh);
if (dir != NODIRECTION) {
rnext = rconnect(sh->region, dir);
}
} }
if (rnext != NULL) { if (rnext != NULL) {

View File

@ -94,7 +94,7 @@ extern "C" {
#define SA_NO_COAST -2 #define SA_NO_COAST -2
int check_ship_allowed(struct ship *sh, const struct region * r); int check_ship_allowed(struct ship *sh, const struct region * r);
struct region * drift_target(struct ship *sh); direction_t drift_target(struct ship *sh);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -477,19 +477,19 @@ static void test_follow_ship_msg(CuTest * tc) {
static void test_drifting_ships(CuTest *tc) { static void test_drifting_ships(CuTest *tc) {
ship *sh; ship *sh;
region *r1, *r2, *r3; region *r;
terrain_type *t_ocean, *t_plain; terrain_type *t_ocean, *t_plain;
ship_type *st_boat; ship_type *st_boat;
test_setup(); test_setup();
t_ocean = test_create_terrain("ocean", SEA_REGION); t_ocean = test_create_terrain("ocean", SEA_REGION);
t_plain = test_create_terrain("plain", LAND_REGION); t_plain = test_create_terrain("plain", LAND_REGION);
r1 = test_create_region(0, 0, t_ocean); r = test_create_region(0, 0, t_ocean);
r2 = test_create_region(1, 0, t_ocean); test_create_region(1, 0, t_ocean);
st_boat = test_create_shiptype("boat"); st_boat = test_create_shiptype("boat");
sh = test_create_ship(r1, st_boat); sh = test_create_ship(r, st_boat);
CuAssertPtrEquals(tc, r2, drift_target(sh)); CuAssertIntEquals(tc, D_EAST, drift_target(sh));
r3 = test_create_region(-1, 0, t_plain); test_create_region(-1, 0, t_plain);
CuAssertPtrEquals(tc, r3, drift_target(sh)); CuAssertIntEquals(tc, D_WEST, drift_target(sh));
test_cleanup(); test_cleanup();
} }

View File

@ -248,6 +248,8 @@ static void dragon_name(unit * u)
case T_GLACIER: case T_GLACIER:
ter = 5; ter = 5;
break; break;
default:
ter = 0;
} }
if (num_postfix <=0) { if (num_postfix <=0) {

View File

@ -432,7 +432,7 @@ void drown(region * r)
} }
} }
static void melt_iceberg(region * r) static void melt_iceberg(region * r, const terrain_type *t_ocean)
{ {
attrib *a; attrib *a;
unit *u; unit *u;
@ -456,11 +456,7 @@ static void melt_iceberg(region * r)
} }
/* in Ozean wandeln */ /* in Ozean wandeln */
terraform_region(r, newterrain(T_OCEAN)); terraform_region(r, t_ocean);
/* Einheiten, die nicht schwimmen k<>nnen oder in Schiffen sind,
* ertrinken */
drown(r);
} }
static void move_iceberg(region * r) static void move_iceberg(region * r)
@ -589,14 +585,20 @@ static void move_iceberg(region * r)
static void move_icebergs(void) static void move_icebergs(void)
{ {
region *r; region *r;
static int terrain_cache;
static const terrain_type *t_iceberg, *t_ocean;
if (terrain_changed(&terrain_cache)) {
t_iceberg = newterrain(T_ICEBERG);
t_ocean = newterrain(T_OCEAN);
}
for (r = regions; r; r = r->next) { for (r = regions; r; r = r->next) {
if (r->terrain == newterrain(T_ICEBERG) && !fval(r, RF_SELECT)) { if (r->terrain == t_iceberg && !fval(r, RF_SELECT)) {
int select = rng_int() % 10; int select = rng_int() % 10;
if (select < 4) { if (select < 4) {
/* 4% chance */ /* 4% chance */
fset(r, RF_SELECT); fset(r, RF_SELECT);
melt_iceberg(r); melt_iceberg(r, t_ocean);
} }
else if (select < 64) { else if (select < 64) {
/* 60% chance */ /* 60% chance */
@ -610,9 +612,14 @@ static void move_icebergs(void)
void create_icebergs(void) void create_icebergs(void)
{ {
region *r; region *r;
const struct terrain_type *t_iceberg, *t_sleep;
t_iceberg = get_terrain("iceberg");
t_sleep = get_terrain("iceberg_sleep");
assert(t_iceberg && t_sleep);
for (r = regions; r; r = r->next) { for (r = regions; r; r = r->next) {
if (r->terrain == newterrain(T_ICEBERG_SLEEP) && chance(0.05)) { if (r->terrain == t_sleep && chance(0.05)) {
bool has_ocean_neighbour = false; bool has_ocean_neighbour = false;
direction_t dir; direction_t dir;
region *rc; region *rc;
@ -629,7 +636,7 @@ void create_icebergs(void)
if (!has_ocean_neighbour) if (!has_ocean_neighbour)
continue; continue;
rsetterrain(r, T_ICEBERG); r->terrain = t_iceberg;
fset(r, RF_SELECT); fset(r, RF_SELECT);
move_iceberg(r); move_iceberg(r);
@ -750,12 +757,8 @@ static void demon_skillchanges(void)
*/ */
static void icebergs(void) static void icebergs(void)
{ {
region *r;
create_icebergs(); create_icebergs();
move_icebergs(); move_icebergs();
for (r = regions; r; r = r->next) {
drown(r);
}
} }
#define HERBS_ROT /* herbs owned by units have a chance to rot. */ #define HERBS_ROT /* herbs owned by units have a chance to rot. */
@ -801,11 +804,18 @@ void randomevents(void)
region *r; region *r;
faction *monsters = get_monsters(); faction *monsters = get_monsters();
icebergs(); if (config_get_int("modules.iceberg", 0)) {
icebergs();
}
for (r = regions; r; r = r->next) {
drown(r);
}
godcurse(); godcurse();
orc_growth(); orc_growth();
demon_skillchanges(); demon_skillchanges();
volcano_update(); if (volcano_module()) {
volcano_update();
}
/* Monumente zerfallen, Schiffe verfaulen */ /* Monumente zerfallen, Schiffe verfaulen */
for (r = regions; r; r = r->next) { for (r = regions; r; r = r->next) {

View File

@ -122,10 +122,10 @@ struct locale * test_create_locale(void) {
for (i = 0; i != MAXDIRECTIONS; ++i) { for (i = 0; i != MAXDIRECTIONS; ++i) {
locale_setstring(loc, directions[i], directions[i]); locale_setstring(loc, directions[i], directions[i]);
init_direction(loc, i, directions[i]); init_direction(loc, i, directions[i]);
init_direction(loc, i, coasts[i]+7); init_direction(loc, i, coasts[i] + 7);
} }
for (i = 0; i <= ST_FLEE; ++i) { for (i = 0; i <= ST_FLEE; ++i) {
locale_setstring(loc, combatstatus[i], combatstatus[i]+7); locale_setstring(loc, combatstatus[i], combatstatus[i] + 7);
} }
for (i = 0; i != MAXKEYWORDS; ++i) { for (i = 0; i != MAXKEYWORDS; ++i) {
locale_setstring(loc, mkname("keyword", keywords[i]), keywords[i]); locale_setstring(loc, mkname("keyword", keywords[i]), keywords[i]);
@ -446,10 +446,10 @@ void test_create_world(void)
test_create_itemtype("iron"); test_create_itemtype("iron");
test_create_itemtype("stone"); test_create_itemtype("stone");
t_plain = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION | FLY_INTO); t_plain = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION | FLY_INTO);
t_plain->size = 1000; t_plain->size = 1000;
t_plain->max_road = 100; t_plain->max_road = 100;
t_ocean = test_create_terrain("ocean", SEA_REGION | SWIM_INTO | FLY_INTO); t_ocean = test_create_terrain("ocean", SEA_REGION | SWIM_INTO | FLY_INTO);
t_ocean->size = 0; t_ocean->size = 0;
island[0] = test_create_region(0, 0, t_plain); island[0] = test_create_region(0, 0, t_plain);
@ -545,20 +545,20 @@ void assert_message(CuTest * tc, message *msg, char *name, int numpar) {
void assert_pointer_parameter(CuTest * tc, message *msg, int index, void *arg) { void assert_pointer_parameter(CuTest * tc, message *msg, int index, void *arg) {
const message_type *mtype = (msg)->type; const message_type *mtype = (msg)->type;
CuAssertIntEquals((tc), VAR_VOIDPTR, mtype->types[(index)]->vtype);CuAssertPtrEquals((tc), (arg), msg->parameters[(index)].v); CuAssertIntEquals((tc), VAR_VOIDPTR, mtype->types[(index)]->vtype); CuAssertPtrEquals((tc), (arg), msg->parameters[(index)].v);
} }
void assert_int_parameter(CuTest * tc, message *msg, int index, int arg) { void assert_int_parameter(CuTest * tc, message *msg, int index, int arg) {
const message_type *mtype = (msg)->type; const message_type *mtype = (msg)->type;
CuAssertIntEquals((tc), VAR_INT, mtype->types[(index)]->vtype);CuAssertIntEquals((tc), (arg), msg->parameters[(index)].i); CuAssertIntEquals((tc), VAR_INT, mtype->types[(index)]->vtype); CuAssertIntEquals((tc), (arg), msg->parameters[(index)].i);
} }
void assert_string_parameter(CuTest * tc, message *msg, int index, const char *arg) { void assert_string_parameter(CuTest * tc, message *msg, int index, const char *arg) {
const message_type *mtype = (msg)->type; const message_type *mtype = (msg)->type;
CuAssertIntEquals((tc), VAR_VOIDPTR, mtype->types[(index)]->vtype);CuAssertStrEquals((tc), (arg), msg->parameters[(index)].v); CuAssertIntEquals((tc), VAR_VOIDPTR, mtype->types[(index)]->vtype); CuAssertStrEquals((tc), (arg), msg->parameters[(index)].v);
} }
void disabled_test(void *suite, void (*test)(CuTest *), const char *name) { void disabled_test(void *suite, void(*test)(CuTest *), const char *name) {
(void)test; (void)test;
fprintf(stderr, "%s: SKIP\n", name); fprintf(stderr, "%s: SKIP\n", name);
} }

View File

@ -45,9 +45,6 @@ typedef struct locale {
struct locale_str *strings[SMAXHASH]; struct locale_str *strings[SMAXHASH];
} locale; } locale;
extern locale *default_locale;
extern locale *locales;
locale *default_locale; locale *default_locale;
locale *locales; locale *locales;

View File

@ -177,11 +177,15 @@ char *parse_token(const char **str, char *lbuf, size_t buflen)
++ctoken; ++ctoken;
} }
else { else {
*cursor++ = *ctoken++; if (cursor - buflen < lbuf - len) {
*cursor++ = *ctoken++;
}
} }
} }
else if (utf8_character == SPACE_REPLACEMENT) { else if (utf8_character == SPACE_REPLACEMENT) {
*cursor++ = ' '; if (cursor - buflen < lbuf - len) {
*cursor++ = ' ';
}
++ctoken; ++ctoken;
} }
else if (utf8_character == ESCAPE_CHAR) { else if (utf8_character == ESCAPE_CHAR) {

View File

@ -28,6 +28,15 @@ static void test_parse_token(CuTest *tc) {
CuAssertPtrEquals(tc, NULL, (void *)tok); CuAssertPtrEquals(tc, NULL, (void *)tok);
} }
static void test_parse_token_bug_2381(CuTest *tc) {
const char *stok, *s = "Bitte~wechselt~auf~die~trireme~3im9,~sobald~eine~Crew~da~ist,~geht~es~los~:)";
char token[64];
stok = s;
stok = parse_token(&stok, token, sizeof(token));
CuAssertTrue(tc, strlen(token) < sizeof(token));
}
static void test_parse_token_limit(CuTest *tc) { static void test_parse_token_limit(CuTest *tc) {
char lbuf[8]; char lbuf[8];
const char *tok; const char *tok;
@ -117,6 +126,7 @@ CuSuite *get_parser_suite(void)
SUITE_ADD_TEST(suite, test_atoip); SUITE_ADD_TEST(suite, test_atoip);
SUITE_ADD_TEST(suite, test_skip_token); SUITE_ADD_TEST(suite, test_skip_token);
SUITE_ADD_TEST(suite, test_parse_token); SUITE_ADD_TEST(suite, test_parse_token);
SUITE_ADD_TEST(suite, test_parse_token_bug_2381);
SUITE_ADD_TEST(suite, test_parse_token_limit); SUITE_ADD_TEST(suite, test_parse_token_limit);
SUITE_ADD_TEST(suite, test_parse_token_limit_utf8); SUITE_ADD_TEST(suite, test_parse_token_limit_utf8);
SUITE_ADD_TEST(suite, test_gettoken); SUITE_ADD_TEST(suite, test_gettoken);

View File

@ -106,8 +106,8 @@ damage_unit(unit * u, const char *dam, bool physical, bool magic)
int damage = dice_rand(dam); int damage = dice_rand(dam);
if (magic) { if (magic) {
variant magres = magic_resistance(u); variant magres = magic_resistance(u);
magres = frac_sub(frac_make(1, 1), magres); int save = magres.sa[0] / magres.sa[1];
damage = damage * magres.sa[0] / magres.sa[1]; damage -= damage * save;
} }
if (physical) { if (physical) {
damage -= nb_armor(u, i); damage -= nb_armor(u, i);
@ -314,3 +314,13 @@ void volcano_update(void)
} }
} }
} }
bool volcano_module(void)
{
static int cache;
static bool active;
if (config_changed(&cache)) {
active = config_get_int("modules.volcano", 0) != 0;
}
return active;
}

View File

@ -27,6 +27,7 @@ extern "C" {
void volcano_outbreak(struct region * r, struct region *rn); void volcano_outbreak(struct region * r, struct region *rn);
void volcano_update(void); void volcano_update(void);
bool volcano_module(void);
#ifdef __cplusplus #ifdef __cplusplus
} }