diff --git a/conf/e2/config.json b/conf/e2/config.json index 0c85b316b..5d29cd1f5 100644 --- a/conf/e2/config.json +++ b/conf/e2/config.json @@ -13,7 +13,12 @@ "game.id" : 2, "orders.default": "work", "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.perlevel": 20, "taxing.perlevel": 20, diff --git a/conf/e3/config.json b/conf/e3/config.json index b70d415a7..780dc216d 100644 --- a/conf/e3/config.json +++ b/conf/e3/config.json @@ -32,8 +32,10 @@ "database.gameid": 7, "NewbieImmunity": 4, "modules.astralspace": false, - "modules.wormholes": false, - "modules.markets": true, + "modules.wormhole": false, + "modules.market": true, + "modules.iceberg": false, + "modules.volcano": true, "magic.regeneration": 0.75, "magic.power": 0.5, "resource.factor": 0.25, diff --git a/scripts/tests/common.lua b/scripts/tests/common.lua index 2f10b3e8a..0fda7168e 100644 --- a/scripts/tests/common.lua +++ b/scripts/tests/common.lua @@ -807,7 +807,6 @@ end function test_swim_and_survive() local r = region.create(0, 0, "plain") local f = create_faction('human') - f.nam = "chaos" local u = unit.create(f, r, 1) process_orders() r.terrain = "ocean" diff --git a/src/battle.c b/src/battle.c index 4451198e5..7f70eaa4a 100644 --- a/src/battle.c +++ b/src/battle.c @@ -1141,7 +1141,7 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile) const weapon_type *dwtype = NULL; const weapon_type *awtype = NULL; const weapon *weapon; - variant res = frac_make(1, 1); + variant res = frac_one; int rda, sk = 0, sd; bool magic = false; @@ -1185,6 +1185,7 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile) } if (magic) { + res = frac_sub(frac_one, res); res = frac_mul(frac_make(da, 1), res); da = res.sa[0] / res.sa[1]; } diff --git a/src/creport.c b/src/creport.c index 12eca6367..19565fcb6 100644 --- a/src/creport.c +++ b/src/creport.c @@ -855,8 +855,6 @@ void cr_output_unit(stream *out, const region * r, const faction * f, stream_printf(out, "\"%s\";Typ\n", translate(zRace, LOC(lang, zRace))); 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); stream_printf(out, "\"%s\";wahrerTyp\n", translate(zRace, LOC(lang, zRace))); diff --git a/src/economy.c b/src/economy.c index 1de4f7e4e..5629bd75d 100644 --- a/src/economy.c +++ b/src/economy.c @@ -1067,7 +1067,7 @@ static int required(int want, variant save) { int req = (int)(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; } @@ -1173,7 +1173,7 @@ attrib_allocation(const resource_type * rtype, region * r, allocation * alist) int x = avail * want / nreq; int rx = (avail * want) % nreq; /* 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; nreq -= want; 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); int max_products; unit *u; - static struct trade { + struct trade { const luxury_type *type; int number; int multi; } trades[MAXLUXURIES], *trade; - static int ntrades = 0; - int i; + int ntrades = 0; const luxury_type *ltype; - if (ntrades == 0) { - for (ntrades = 0, ltype = luxurytypes; ltype; ltype = ltype->next) { - assert(ntrades < MAXLUXURIES); - trades[ntrades++].type = ltype; - } - } - for (i = 0; i != ntrades; ++i) { - trades[i].number = 0; - trades[i].multi = 1; + for (ntrades = 0, ltype = luxurytypes; ltype; ltype = ltype->next) { + assert(ntrades < MAXLUXURIES); + trades[ntrades].number = 0; + trades[ntrades].multi = 1; + trades[ntrades++].type = ltype; } if (!buyorders) @@ -1490,7 +1485,7 @@ static void expandbuying(region * r, request * buyorders) int price, multi; ltype = g_requests[j].type.ltype; trade = trades; - while (trade->type != ltype) + while (trade->type && trade->type != ltype) ++trade; multi = trade->multi; price = ltype->price * multi; @@ -1595,17 +1590,10 @@ static void buy(unit * u, request ** buyorders, struct order *ord) return; } - if (u_race(u) == get_race(RC_INSECT)) { - /* entweder man ist insekt, oder... */ - if (r->terrain != newterrain(T_SWAMP) && r->terrain != newterrain(T_DESERT) - && !rbuildings(r)) { - cmistake(u, ord, 119, MSG_COMMERCE); - return; - } - } - else { - /* ...oder in der Region mu� es eine Burg geben. */ - building *b = 0; + /* Entweder man ist Insekt in Sumpf/Wueste, oder es muss + * einen Handelsposten in der Region geben: */ + if (u_race(u) != get_race(RC_INSECT) || (r->terrain == newterrain(T_SWAMP) || r->terrain == newterrain(T_DESERT))) { + building *b = NULL; if (r->buildings) { static int cache; 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%) */ m = region_get_morale(r); - if (m && startmoney>0) { + if (m && startmoney > 0) { 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*/ region_set_morale(r, m - 1, -1); @@ -2985,7 +2973,7 @@ void produce(struct region *r) static const struct building_type *caravan_bt; static int rc_cache; static const race *rc_insect, *rc_aquarian; - + if (bt_changed(&bt_cache)) { caravan_bt = bt_find("caravan"); } diff --git a/src/economy.test.c b/src/economy.test.c index 83c103f7f..6303b2ae8 100644 --- a/src/economy.test.c +++ b/src/economy.test.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -170,6 +171,133 @@ static void test_normals_recruit(CuTest * tc) { 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 { struct request *next; struct unit *unit; @@ -568,6 +696,8 @@ CuSuite *get_economy_suite(void) SUITE_ADD_TEST(suite, test_normals_recruit); SUITE_ADD_TEST(suite, test_heroes_dont_recruit); 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_recruit); return suite; diff --git a/src/items/weapons.c b/src/items/weapons.c index acb47f0e0..53721bca6 100644 --- a/src/items/weapons.c +++ b/src/items/weapons.c @@ -76,7 +76,7 @@ int *casualties) killed += terminate(dt, *at, AT_SPELL, damage, 1); } } while (force && killed < enemies); - if (casualties) + if (killed > 0 && casualties) *casualties = killed; return true; } diff --git a/src/kernel/alliance.test.c b/src/kernel/alliance.test.c index 885bc8413..e76a11ba2 100644 --- a/src/kernel/alliance.test.c +++ b/src/kernel/alliance.test.c @@ -216,7 +216,6 @@ static void test_alliance_cmd_transfer(CuTest *tc) { test_cleanup(); } - CuSuite *get_alliance_suite(void) { CuSuite *suite = CuSuiteNew(); diff --git a/src/kernel/build.c b/src/kernel/build.c index 2746fa406..4899163bd 100644 --- a/src/kernel/build.c +++ b/src/kernel/build.c @@ -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) { if (rtype->modifiers) { - variant save = frac_make(1, 1); + variant save = frac_one; const struct building_type *btype = NULL; const struct race *rc = u_race(u); resource_mod *mod; diff --git a/src/kernel/item.h b/src/kernel/item.h index 51ebcd2a1..6477a4923 100644 --- a/src/kernel/item.h +++ b/src/kernel/item.h @@ -224,7 +224,7 @@ extern "C" { /* convenience: */ 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 */ resource_type *rt_get_or_create(const char *name); @@ -300,7 +300,6 @@ extern "C" { void register_resources(void); void init_resources(void); - void init_itemtypes(void); void register_item_give(int(*foo) (struct unit *, struct unit *, const struct item_type *, int, struct order *), const char *name); diff --git a/src/kernel/region.c b/src/kernel/region.c index c7e3841fb..3dad8e3ab 100644 --- a/src/kernel/region.c +++ b/src/kernel/region.c @@ -711,7 +711,7 @@ const item_type *r_luxury(const region * r) { struct demand *dmd; 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) { if (dmd->value == 0) return dmd->type->itype; diff --git a/src/kernel/terrain.c b/src/kernel/terrain.c index e9f448f97..d606ff91f 100644 --- a/src/kernel/terrain.c +++ b/src/kernel/terrain.c @@ -37,9 +37,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include -#define MAXTERRAINS 14 - -const char *terraindata[MAXTERRAINS] = { +static const char *terrainnames[MAXTERRAINS] = { "ocean", "plain", "swamp", @@ -57,6 +55,17 @@ const char *terraindata[MAXTERRAINS] = { }; 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) { @@ -76,6 +85,7 @@ void free_terrains(void) } free(t); } + ++terrain_changes; } 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 *terrain = terrain_find_i(name); if (!terrain) { + ++terrain_changes; terrain = (terrain_type *)calloc(sizeof(terrain_type), 1); if (terrain) { terrain->_name = strdup(name); @@ -128,11 +139,20 @@ static const terrain_type *newterrains[MAXTERRAINS]; const struct terrain_type *newterrain(terrain_t t) { - if (t == NOTERRAIN) + const struct terrain_type *result; + if (t == NOTERRAIN) { return NULL; + } assert(t >= 0); 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) @@ -176,8 +196,8 @@ void init_terrains(void) const terrain_type *newterrain = newterrains[t]; if (newterrain != NULL) continue; - if (terraindata[t] != NULL) { - newterrain = get_terrain(terraindata[t]); + if (terrainnames[t] != NULL) { + newterrain = get_terrain(terrainnames[t]); if (newterrain != NULL) { newterrains[t] = newterrain; } diff --git a/src/kernel/terrain.h b/src/kernel/terrain.h index 3d1491867..26c509f75 100644 --- a/src/kernel/terrain.h +++ b/src/kernel/terrain.h @@ -18,6 +18,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #ifndef TERRAIN_H #define TERRAIN_H + #ifdef __cplusplus extern "C" { #endif @@ -68,12 +69,13 @@ extern "C" { struct terrain_type *next; } terrain_type; - extern terrain_type *get_or_create_terrain(const char *name); - extern const terrain_type *terrains(void); - extern const terrain_type *get_terrain(const char *name); - extern const char *terrain_name(const struct region *r); + terrain_type *get_or_create_terrain(const char *name); + const terrain_type *terrains(void); + const terrain_type *get_terrain(const char *name); + 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); #ifdef __cplusplus diff --git a/src/kernel/terrainid.h b/src/kernel/terrainid.h index 7d353d042..b6235c399 100644 --- a/src/kernel/terrainid.h +++ b/src/kernel/terrainid.h @@ -14,7 +14,7 @@ extern "C" { #endif - enum { + typedef enum { T_OCEAN = 0, T_PLAIN, T_SWAMP, @@ -29,8 +29,9 @@ extern "C" { T_VOLCANO_SMOKING, T_ICEBERG_SLEEP, T_ICEBERG, - NOTERRAIN = (terrain_t) - 1 - }; + MAXTERRAINS, + NOTERRAIN = - 1 + } terrain_t; extern const struct terrain_type *newterrain(terrain_t t); extern terrain_t oldterrain(const struct terrain_type *terrain); diff --git a/src/kernel/types.h b/src/kernel/types.h index a20e923d6..9e9ed3b66 100644 --- a/src/kernel/types.h +++ b/src/kernel/types.h @@ -22,7 +22,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include -typedef short terrain_t; typedef short item_t; struct attrib; diff --git a/src/kernel/version.c b/src/kernel/version.c index 65cbeb110..c7d757773 100644 --- a/src/kernel/version.c +++ b/src/kernel/version.c @@ -5,7 +5,7 @@ #ifndef ERESSEA_VERSION /* 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 const char *eressea_version(void) { diff --git a/src/laws.c b/src/laws.c index c98ffaaf6..63cddeeec 100644 --- a/src/laws.c +++ b/src/laws.c @@ -41,6 +41,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "teleport.h" #include "calendar.h" #include "guard.h" +#include "volcano.h" /* attributes includes */ #include @@ -72,7 +73,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include -#include /* for volcanoes in emigration (needs a flag) */ +#include #include /* util includes */ @@ -247,9 +248,19 @@ static void calculate_emigration(region * r) int rp = rpeasants(r); int max_immigrants = MAX_IMMIGRATION(maxp - rp); - if (r->terrain == newterrain(T_VOLCANO) - || r->terrain == newterrain(T_VOLCANO_SMOKING)) { - max_immigrants = max_immigrants / 10; + + if (volcano_module()) { + 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++) { @@ -4215,7 +4226,7 @@ void turn_process(void) init_processor(); process(); - if (config_get_int("modules.markets", 0)) { + if (markets_module()) { do_markets(); } } @@ -4227,7 +4238,7 @@ void turn_end(void) remove_empty_units(); /* 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(); } diff --git a/src/laws.test.c b/src/laws.test.c index 742eaa363..579514aa4 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -864,11 +865,25 @@ static void test_peasant_luck_effect(CuTest *tc) { 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) { region* r; attrib *a; test_setup(); + setup_terrains(tc); r = test_create_region(0, 0, NULL); rsetpeasants(r, 1); @@ -1149,10 +1164,12 @@ static void test_ally_cmd_errors(CuTest *tc) { static void test_name_cmd(CuTest *tc) { unit *u; faction *f; + alliance *al; order *ord; test_setup(); 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])); name_cmd(u, ord); @@ -1181,6 +1198,11 @@ static void test_name_cmd(CuTest *tc) { CuAssertStrEquals(tc, "Hodor", u->region->land->name); 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(); } diff --git a/src/main.c b/src/main.c index eedb25cf1..0c171f219 100644 --- a/src/main.c +++ b/src/main.c @@ -101,10 +101,11 @@ static dictionary *parse_config(const char *filename) if (cfgpath) { join_path(cfgpath, filename, path, sizeof(path)); log_debug("reading from configuration file %s\n", path); - d = iniparser_load(path); - } else { + d = iniparser_load(path); + } + else { log_debug("reading from configuration file %s\n", filename); - d = iniparser_load(filename); + d = iniparser_load(filename); } if (d) { 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) { int i; - int log_stderr = LOG_CPERROR; - int log_flags = LOG_CPERROR | LOG_CPWARNING | LOG_CPINFO; + int log_stderr, log_flags = 2; for (i = 1; i != argc; ++i) { char *argi = argv[i]; @@ -179,9 +179,9 @@ static int parse_args(int argc, char **argv) else if (argi[1] == '-') { /* long format */ if (strcmp(argi + 2, "version") == 0) { printf("Eressea version %s, " - "Copyright (C) 2017 Enno Rehling et al.\n", + "Copyright (C) 2017 Enno Rehling et al.\n", eressea_version()); - return 1; + return 1; #ifdef USE_CURSES } else if (strcmp(argi + 2, "color") == 0) { @@ -300,8 +300,8 @@ int main(int argc, char **argv) setup_signal_handler(); /* parse arguments again, to override ini file */ err = parse_args(argc, argv); - if (err!=0) { - return (err>0) ? 0 : err; + if (err != 0) { + return (err > 0) ? 0 : err; } d = parse_config(inifile); if (!d) { diff --git a/src/market.c b/src/market.c index 8fd4e3f0d..fd62ae353 100644 --- a/src/market.c +++ b/src/market.c @@ -71,7 +71,7 @@ attrib_type at_market = { bool markets_module(void) { - return (bool)config_get_int("modules.markets", 0); + return (bool)config_get_int("modules.market", 0); } void do_markets(void) diff --git a/src/monsters.c b/src/monsters.c index 115759d29..9079b29d6 100644 --- a/src/monsters.c +++ b/src/monsters.c @@ -874,19 +874,27 @@ void spawn_dragons(void) { region *r; 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) { unit *u; - + if (r->age < minage) { + continue; + } if (fval(r->terrain, SEA_REGION)) { - if (rng_int() % 10000 < 1) { + if (rng_int() % spawn_chance < 1) { u = spawn_seaserpent(r, monsters); } } else if ((r->terrain == newterrain(T_GLACIER) || r->terrain == newterrain(T_SWAMP) || r->terrain == newterrain(T_DESERT)) - && rng_int() % 10000 < (5 + 100 * chaosfactor(r))) { + && rng_int() % spawn_chance < (5 + 100 * chaosfactor(r))) { if (chance(0.80)) { u = create_unit(r, monsters, nrand(60, 20) + 1, get_race(RC_FIREDRAGON), 0, NULL, NULL); } diff --git a/src/move.c b/src/move.c index 8df2dd23a..904988759 100644 --- a/src/move.c +++ b/src/move.c @@ -787,22 +787,22 @@ static void msg_to_ship_inmates(ship *sh, unit **firstu, unit **lastu, message * msg_release(msg); } -region * drift_target(ship *sh) { - int d, d_offset = rng_int() % MAXDIRECTIONS; - region *rnext = NULL; +direction_t drift_target(ship *sh) { + direction_t d, dir = rng_int() % MAXDIRECTIONS; + direction_t result = NODIRECTION; for (d = 0; d != MAXDIRECTIONS; ++d) { region *rn; - direction_t dir = (direction_t)((d + d_offset) % MAXDIRECTIONS); - rn = rconnect(sh->region, dir); + direction_t dn = (direction_t)((d + dir) % MAXDIRECTIONS); + rn = rconnect(sh->region, dn); if (rn != NULL && check_ship_allowed(sh, rn) >= 0) { - rnext = rn; - if (!fval(rnext->terrain, SEA_REGION)) { + result = dn; + if (!fval(rn->terrain, SEA_REGION)) { /* prefer drifting towards non-ocean regions */ break; } } } - return rnext; + return result; } static void drifting_ships(region * r) @@ -817,7 +817,7 @@ static void drifting_ships(region * r) region *rnext = NULL; region_list *route = NULL; unit *firstu = r->units, *lastu = NULL, *captain; - direction_t dir = 0; + direction_t dir = NODIRECTION; double ovl; if (sh->type->fishing > 0) { @@ -846,13 +846,13 @@ static void drifting_ships(region * r) } ovl = overload(r, sh); - if (ovl >= overload_start()) { - rnext = NULL; - } - else { + if (ovl < overload_start()) { /* Auswahl einer Richtung: Zuerst auf Land, dann * 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) { diff --git a/src/move.h b/src/move.h index 4b9c75749..535c76bb2 100644 --- a/src/move.h +++ b/src/move.h @@ -94,7 +94,7 @@ extern "C" { #define SA_NO_COAST -2 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 } #endif diff --git a/src/move.test.c b/src/move.test.c index 908da0bb7..7095da855 100644 --- a/src/move.test.c +++ b/src/move.test.c @@ -477,19 +477,19 @@ static void test_follow_ship_msg(CuTest * tc) { static void test_drifting_ships(CuTest *tc) { ship *sh; - region *r1, *r2, *r3; + region *r; terrain_type *t_ocean, *t_plain; ship_type *st_boat; test_setup(); t_ocean = test_create_terrain("ocean", SEA_REGION); t_plain = test_create_terrain("plain", LAND_REGION); - r1 = test_create_region(0, 0, t_ocean); - r2 = test_create_region(1, 0, t_ocean); + r = test_create_region(0, 0, t_ocean); + test_create_region(1, 0, t_ocean); st_boat = test_create_shiptype("boat"); - sh = test_create_ship(r1, st_boat); - CuAssertPtrEquals(tc, r2, drift_target(sh)); - r3 = test_create_region(-1, 0, t_plain); - CuAssertPtrEquals(tc, r3, drift_target(sh)); + sh = test_create_ship(r, st_boat); + CuAssertIntEquals(tc, D_EAST, drift_target(sh)); + test_create_region(-1, 0, t_plain); + CuAssertIntEquals(tc, D_WEST, drift_target(sh)); test_cleanup(); } diff --git a/src/names.c b/src/names.c index bc740a704..ef2d58422 100644 --- a/src/names.c +++ b/src/names.c @@ -248,6 +248,8 @@ static void dragon_name(unit * u) case T_GLACIER: ter = 5; break; + default: + ter = 0; } if (num_postfix <=0) { diff --git a/src/randenc.c b/src/randenc.c index b2b282eb1..48f7a5827 100644 --- a/src/randenc.c +++ b/src/randenc.c @@ -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; unit *u; @@ -456,11 +456,7 @@ static void melt_iceberg(region * r) } /* in Ozean wandeln */ - terraform_region(r, newterrain(T_OCEAN)); - - /* Einheiten, die nicht schwimmen k�nnen oder in Schiffen sind, - * ertrinken */ - drown(r); + terraform_region(r, t_ocean); } static void move_iceberg(region * r) @@ -589,14 +585,20 @@ static void move_iceberg(region * r) static void move_icebergs(void) { 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) { - if (r->terrain == newterrain(T_ICEBERG) && !fval(r, RF_SELECT)) { + if (r->terrain == t_iceberg && !fval(r, RF_SELECT)) { int select = rng_int() % 10; if (select < 4) { /* 4% chance */ fset(r, RF_SELECT); - melt_iceberg(r); + melt_iceberg(r, t_ocean); } else if (select < 64) { /* 60% chance */ @@ -610,9 +612,14 @@ static void move_icebergs(void) void create_icebergs(void) { 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) { - if (r->terrain == newterrain(T_ICEBERG_SLEEP) && chance(0.05)) { + if (r->terrain == t_sleep && chance(0.05)) { bool has_ocean_neighbour = false; direction_t dir; region *rc; @@ -629,7 +636,7 @@ void create_icebergs(void) if (!has_ocean_neighbour) continue; - rsetterrain(r, T_ICEBERG); + r->terrain = t_iceberg; fset(r, RF_SELECT); move_iceberg(r); @@ -750,12 +757,8 @@ static void demon_skillchanges(void) */ static void icebergs(void) { - region *r; create_icebergs(); move_icebergs(); - for (r = regions; r; r = r->next) { - drown(r); - } } #define HERBS_ROT /* herbs owned by units have a chance to rot. */ @@ -801,11 +804,18 @@ void randomevents(void) region *r; faction *monsters = get_monsters(); - icebergs(); + if (config_get_int("modules.iceberg", 0)) { + icebergs(); + } + for (r = regions; r; r = r->next) { + drown(r); + } godcurse(); orc_growth(); demon_skillchanges(); - volcano_update(); + if (volcano_module()) { + volcano_update(); + } /* Monumente zerfallen, Schiffe verfaulen */ for (r = regions; r; r = r->next) { diff --git a/src/tests.c b/src/tests.c index 770d2853c..d07124af3 100644 --- a/src/tests.c +++ b/src/tests.c @@ -122,10 +122,10 @@ struct locale * test_create_locale(void) { for (i = 0; i != MAXDIRECTIONS; ++i) { locale_setstring(loc, directions[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) { - locale_setstring(loc, combatstatus[i], combatstatus[i]+7); + locale_setstring(loc, combatstatus[i], combatstatus[i] + 7); } for (i = 0; i != MAXKEYWORDS; ++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("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->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; 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) { 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) { 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) { 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; fprintf(stderr, "%s: SKIP\n", name); } diff --git a/src/util/language.c b/src/util/language.c index b3cce61af..a1cda4d2f 100644 --- a/src/util/language.c +++ b/src/util/language.c @@ -45,9 +45,6 @@ typedef struct locale { struct locale_str *strings[SMAXHASH]; } locale; -extern locale *default_locale; -extern locale *locales; - locale *default_locale; locale *locales; diff --git a/src/util/parser.c b/src/util/parser.c index fca066cb1..46a9d3a28 100644 --- a/src/util/parser.c +++ b/src/util/parser.c @@ -177,11 +177,15 @@ char *parse_token(const char **str, char *lbuf, size_t buflen) ++ctoken; } else { - *cursor++ = *ctoken++; + if (cursor - buflen < lbuf - len) { + *cursor++ = *ctoken++; + } } } else if (utf8_character == SPACE_REPLACEMENT) { - *cursor++ = ' '; + if (cursor - buflen < lbuf - len) { + *cursor++ = ' '; + } ++ctoken; } else if (utf8_character == ESCAPE_CHAR) { diff --git a/src/util/parser.test.c b/src/util/parser.test.c index 942aa1866..7690a01b4 100644 --- a/src/util/parser.test.c +++ b/src/util/parser.test.c @@ -28,6 +28,15 @@ static void test_parse_token(CuTest *tc) { 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) { char lbuf[8]; const char *tok; @@ -117,6 +126,7 @@ CuSuite *get_parser_suite(void) SUITE_ADD_TEST(suite, test_atoip); SUITE_ADD_TEST(suite, test_skip_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_utf8); SUITE_ADD_TEST(suite, test_gettoken); diff --git a/src/volcano.c b/src/volcano.c index db7166de0..fe4ce7dd3 100644 --- a/src/volcano.c +++ b/src/volcano.c @@ -106,8 +106,8 @@ damage_unit(unit * u, const char *dam, bool physical, bool magic) int damage = dice_rand(dam); if (magic) { variant magres = magic_resistance(u); - magres = frac_sub(frac_make(1, 1), magres); - damage = damage * magres.sa[0] / magres.sa[1]; + int save = magres.sa[0] / magres.sa[1]; + damage -= damage * save; } if (physical) { 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; +} diff --git a/src/volcano.h b/src/volcano.h index d06c381cc..225a76143 100644 --- a/src/volcano.h +++ b/src/volcano.h @@ -27,6 +27,7 @@ extern "C" { void volcano_outbreak(struct region * r, struct region *rn); void volcano_update(void); + bool volcano_module(void); #ifdef __cplusplus }