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,
"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,

View File

@ -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,

View File

@ -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"

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 *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];
}

View File

@ -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)));

View File

@ -1446,25 +1446,20 @@ 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].number = 0;
trades[ntrades].multi = 1;
trades[ntrades++].type = ltype;
}
}
for (i = 0; i != ntrades; ++i) {
trades[i].number = 0;
trades[i].multi = 1;
}
if (!buyorders)
return;
@ -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<6D> 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;

View File

@ -14,6 +14,7 @@
#include <kernel/resources.h>
#include <kernel/ship.h>
#include <kernel/terrain.h>
#include <kernel/terrainid.h>
#include <kernel/unit.h>
#include <util/attrib.h>
@ -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;

View File

@ -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;
}

View File

@ -216,7 +216,6 @@ static void test_alliance_cmd_transfer(CuTest *tc) {
test_cleanup();
}
CuSuite *get_alliance_suite(void)
{
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)
{
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;

View File

@ -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);

View File

@ -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;

View File

@ -37,9 +37,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <stdlib.h>
#include <string.h>
#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;
}

View File

@ -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

View File

@ -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);

View File

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

View File

@ -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) {

View File

@ -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 <attributes/racename.h>
@ -72,7 +73,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <kernel/spell.h>
#include <kernel/spellbook.h>
#include <kernel/terrain.h>
#include <kernel/terrainid.h> /* for volcanoes in emigration (needs a flag) */
#include <kernel/terrainid.h>
#include <kernel/unit.h>
/* util includes */
@ -247,10 +248,20 @@ 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)) {
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++) {
int dir = (turn + 1 + i) % MAXDIRECTIONS;
@ -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();
}

View File

@ -16,6 +16,7 @@
#include <kernel/region.h>
#include <kernel/ship.h>
#include <kernel/terrain.h>
#include <kernel/terrainid.h>
#include <kernel/unit.h>
#include <util/attrib.h>
@ -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();
}

View File

@ -102,7 +102,8 @@ static dictionary *parse_config(const char *filename)
join_path(cfgpath, filename, path, sizeof(path));
log_debug("reading from configuration file %s\n", path);
d = iniparser_load(path);
} else {
}
else {
log_debug("reading from configuration file %s\n", filename);
d = iniparser_load(filename);
}
@ -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];

View File

@ -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)

View File

@ -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);
}

View File

@ -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) {

View File

@ -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

View File

@ -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();
}

View File

@ -248,6 +248,8 @@ static void dragon_name(unit * u)
case T_GLACIER:
ter = 5;
break;
default:
ter = 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;
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();
if (config_get_int("modules.iceberg", 0)) {
icebergs();
}
for (r = regions; r; r = r->next) {
drown(r);
}
godcurse();
orc_growth();
demon_skillchanges();
if (volcano_module()) {
volcano_update();
}
/* Monumente zerfallen, Schiffe verfaulen */
for (r = regions; r; r = r->next) {

View File

@ -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;

View File

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

View File

@ -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);

View File

@ -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;
}

View File

@ -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
}