diff --git a/conf/e2/config.json b/conf/e2/config.json
index 946ddd1dc..6b11dceff 100644
--- a/conf/e2/config.json
+++ b/conf/e2/config.json
@@ -4,6 +4,10 @@
"prefixes.json",
"e2/terrains.json"
],
+ "disabled": [
+ "pay",
+ "jsreport"
+ ],
"settings": {
"game.id": 2,
"game.name": "Eressea",
diff --git a/conf/e2/config.xml b/conf/e2/config.xml
index 45870fded..d1eac12d8 100644
--- a/conf/e2/config.xml
+++ b/conf/e2/config.xml
@@ -52,42 +52,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
eressea-server@eressea.de
diff --git a/conf/e3/config.json b/conf/e3/config.json
index d831a0b3e..236001e1d 100644
--- a/conf/e3/config.json
+++ b/conf/e3/config.json
@@ -4,6 +4,26 @@
"prefixes.json",
"e3/terrains.json"
],
+ "disabled": [
+ "herbalism",
+ "alchemy",
+ "entertainment",
+ "espionage",
+ "perception",
+ "stealth",
+ "taxation",
+ "trade",
+ "besiege",
+ "steal",
+ "buy",
+ "teach",
+ "sabotage",
+ "spy",
+ "tax",
+ "entertain",
+ "sell",
+ "jsreport"
+ ],
"settings": {
"game.id": 3,
"game.name": "E3",
diff --git a/conf/e3/config.xml b/conf/e3/config.xml
index 663b56d26..1a1f5f26f 100644
--- a/conf/e3/config.xml
+++ b/conf/e3/config.xml
@@ -41,51 +41,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/conf/e4/config.json b/conf/e4/config.json
index a32d7aab7..ffa11443c 100644
--- a/conf/e4/config.json
+++ b/conf/e4/config.json
@@ -4,6 +4,26 @@
"prefixes.json",
"e3/terrains.json"
],
+ "disabled": [
+ "herbalism",
+ "alchemy",
+ "entertainment",
+ "espionage",
+ "perception",
+ "stealth",
+ "taxation",
+ "trade",
+ "besiege",
+ "steal",
+ "buy",
+ "teach",
+ "sabotage",
+ "spy",
+ "tax",
+ "entertain",
+ "sell",
+ "jsreport"
+ ],
"settings": {
"game.id": 4,
"game.name": "Deveron",
@@ -29,6 +49,7 @@
"recruit.allow_merge": true,
"study.expensivemigrants": true,
"study.speedup": 2,
+ "study.from_use": 0.4,
"world.era": 3,
"rules.migrants.max": 0,
"rules.reserve.twophase": true,
diff --git a/conf/e4/config.xml b/conf/e4/config.xml
index 2e68f6bf2..ffa1c5df8 100644
--- a/conf/e4/config.xml
+++ b/conf/e4/config.xml
@@ -42,49 +42,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/scripts/eressea/jsreport.lua b/scripts/eressea/jsreport.lua
index 0351efd12..b22f1acf2 100644
--- a/scripts/eressea/jsreport.lua
+++ b/scripts/eressea/jsreport.lua
@@ -1,7 +1,7 @@
local pkg = {}
function pkg.init()
- eressea.settings.set("feature.jsreport.enable", "1")
+ eressea.settings.set("jsreport.enabled", "1")
end
function pkg.update()
diff --git a/src/battle.c b/src/battle.c
index 075e2327c..3770199f6 100644
--- a/src/battle.c
+++ b/src/battle.c
@@ -2895,10 +2895,10 @@ static void aftermath(battle * b)
if (sh && fval(sh, SF_DAMAGED)) {
int n = b->turn - 2;
if (n > 0) {
- float dmg =
+ double dmg =
get_param_flt(global.parameters, "rules.ship.damage.battleround",
0.05F);
- damage_ship(sh, dmg * (float)n);
+ damage_ship(sh, dmg * n);
freset(sh, SF_DAMAGED);
}
}
diff --git a/src/chaos.c b/src/chaos.c
index df478e0f4..5b6f9c89b 100644
--- a/src/chaos.c
+++ b/src/chaos.c
@@ -191,7 +191,7 @@ static void chaos(region * r)
while (sh) {
ship *nsh = sh->next;
- float dmg =
+ double dmg =
get_param_flt(global.parameters, "rules.ship.damage.atlantis",
0.50);
damage_ship(sh, dmg);
diff --git a/src/jsreport.c b/src/jsreport.c
index 64d6d4e2d..44b9ea252 100644
--- a/src/jsreport.c
+++ b/src/jsreport.c
@@ -25,7 +25,7 @@ static void coor_from_tiled(int *x, int *y) {
static int report_json(const char *filename, report_context * ctx, const char *charset)
{
- if (get_param_int(global.parameters, "feature.jsreport.enable", 0) != 0) {
+ if (get_param_int(global.parameters, "jsreport.enabled", 0) != 0) {
FILE * F = fopen(filename, "w");
if (F) {
int x, y, minx = INT_MAX, maxx = INT_MIN, miny = INT_MAX, maxy = INT_MIN;
diff --git a/src/kernel/config.c b/src/kernel/config.c
index f31083130..1bd2e6c21 100644
--- a/src/kernel/config.c
+++ b/src/kernel/config.c
@@ -1123,10 +1123,10 @@ void set_basepath(const char *path)
g_basedir = path;
}
-float get_param_flt(const struct param *p, const char *key, float def)
+double get_param_flt(const struct param *p, const char *key, double def)
{
const char *str = get_param(p, key);
- return str ? (float)atof(str) : def;
+ return str ? atof(str) : def;
}
void set_param(struct param **p, const char *key, const char *data)
@@ -1599,16 +1599,6 @@ int maintenance_cost(const struct unit *u)
return u_race(u)->maintenance * u->number;
}
-int produceexp(struct unit *u, skill_t sk, int n)
-{
- if (global.producexpchance > 0.0F) {
- if (n == 0 || !playerrace(u_race(u)))
- return 0;
- learn_skill(u, sk, global.producexpchance);
- }
- return 0;
-}
-
int lovar(double xpct_x2)
{
int n = (int)(xpct_x2 * 500) + 1;
diff --git a/src/kernel/config.h b/src/kernel/config.h
index 23d887b99..00518c37b 100644
--- a/src/kernel/config.h
+++ b/src/kernel/config.h
@@ -261,8 +261,6 @@ extern "C" {
unsigned int data_turn;
struct param *parameters;
void *vm_state;
- float producexpchance;
- int cookie;
int data_version; /* TODO: eliminate in favor of gamedata.version */
struct _dictionary_ *inifile;
@@ -271,6 +269,10 @@ extern "C" {
const struct race * rc, int in_turn);
int(*maintenance) (const struct unit * u);
} functions;
+ /* the following are some cached values, because get_param can be slow.
+ * you should almost never need to touch them */
+ int cookie;
+ double producexpchance_;
} settings;
typedef struct helpmode {
@@ -284,7 +286,7 @@ extern "C" {
const char *get_param(const struct param *p, const char *key);
int get_param_int(const struct param *p, const char *key, int def);
int check_param(const struct param *p, const char *key, const char *searchvalue);
- float get_param_flt(const struct param *p, const char *key, float def);
+ double get_param_flt(const struct param *p, const char *key, double def);
void free_params(struct param **pp);
bool ExpensiveMigrants(void);
diff --git a/src/kernel/jsonconf.c b/src/kernel/jsonconf.c
index 5fdd82e65..64ae06477 100644
--- a/src/kernel/jsonconf.c
+++ b/src/kernel/jsonconf.c
@@ -501,6 +501,45 @@ static void json_prefixes(cJSON *json) {
}
}
+/** disable a feature.
+ * features are identified by eone of:
+ * 1. the keyword for their orders,
+ * 2. the name of the skill they use,
+ * 3. a "module.enabled" flag in the settings
+ */
+static void disable_feature(const char *str) {
+ char name[32];
+ int k;
+ skill_t sk;
+ sk = findskill(str);
+ if (sk != NOSKILL) {
+ enable_skill(sk, false);
+ return;
+ }
+ for (k = 0; k != MAXKEYWORDS; ++k) {
+ // FIXME: this loop is slow as balls.
+ if (strcmp(keywords[k], str) == 0) {
+ log_info("disable keyword %s\n", str);
+ enable_keyword(k, false);
+ return;
+ }
+ }
+ _snprintf(name, sizeof(name), "%s.enabled", str);
+ log_info("disable feature %s\n", name);
+ set_param(&global.parameters, name, "0");
+}
+
+static void json_disable_features(cJSON *json) {
+ cJSON *child;
+ if (json->type != cJSON_Array) {
+ log_error("disabled is not a json array: %d", json->type);
+ return;
+ }
+ for (child = json->child; child; child = child->next) {
+ disable_feature(child->valuestring);
+ }
+}
+
static void json_terrains(cJSON *json) {
cJSON *child;
if (json->type != cJSON_Object) {
@@ -854,6 +893,9 @@ void json_config(cJSON *json) {
else if (strcmp(child->string, "prefixes") == 0) {
json_prefixes(child);
}
+ else if (strcmp(child->string, "disabled") == 0) {
+ json_disable_features(child);
+ }
else if (strcmp(child->string, "terrains") == 0) {
json_terrains(child);
init_terrains();
diff --git a/src/kernel/jsonconf.test.c b/src/kernel/jsonconf.test.c
index 1547fde45..d7a4ad0cd 100644
--- a/src/kernel/jsonconf.test.c
+++ b/src/kernel/jsonconf.test.c
@@ -99,6 +99,31 @@ static void test_prefixes(CuTest * tc)
test_cleanup();
}
+static void test_disable(CuTest * tc)
+{
+ const char * data = "{\"disabled\": [ "
+ "\"alchemy\","
+ "\"pay\","
+ "\"besiege\","
+ "\"module\""
+ "]}";
+ cJSON *json = cJSON_Parse(data);
+
+ test_cleanup();
+ CuAssertTrue(tc, skill_enabled(SK_ALCHEMY));
+ CuAssertTrue(tc, !keyword_disabled(K_BANNER));
+ CuAssertTrue(tc, !keyword_disabled(K_PAY));
+ CuAssertTrue(tc, !keyword_disabled(K_BESIEGE));
+ CuAssertIntEquals(tc, 1, get_param_int(global.parameters, "module.enabled", 1));
+ json_config(json);
+ CuAssertTrue(tc, !skill_enabled(SK_ALCHEMY));
+ CuAssertTrue(tc, !keyword_disabled(K_BANNER));
+ CuAssertTrue(tc, keyword_disabled(K_PAY));
+ CuAssertTrue(tc, keyword_disabled(K_BESIEGE));
+ CuAssertIntEquals(tc, 0, get_param_int(global.parameters, "module.enabled", 1));
+ test_cleanup();
+}
+
static void test_races(CuTest * tc)
{
const char * data = "{\"races\": { \"orc\" : { "
@@ -576,6 +601,7 @@ CuSuite *get_jsonconf_suite(void)
SUITE_ADD_TEST(suite, test_flags);
SUITE_ADD_TEST(suite, test_settings);
SUITE_ADD_TEST(suite, test_prefixes);
+ SUITE_ADD_TEST(suite, test_disable);
SUITE_ADD_TEST(suite, test_infinitive_from_config);
return suite;
}
diff --git a/src/kernel/unit.c b/src/kernel/unit.c
index 95608e709..a12edd310 100644
--- a/src/kernel/unit.c
+++ b/src/kernel/unit.c
@@ -1933,3 +1933,27 @@ bool unit_name_equals_race(const unit *u) {
bool unit_can_study(const unit *u) {
return !((u_race(u)->flags & RCF_NOLEARN) || fval(u, UFL_WERE));
}
+
+static double produceexp_chance(void) {
+ static int update = 0;
+ if (update != global.cookie) {
+ global.producexpchance_ = get_param_flt(global.parameters, "study.from_use", 1.0 / 3);
+ update = global.cookie;
+ }
+ return global.producexpchance_;
+}
+
+void produceexp_ex(struct unit *u, skill_t sk, int n, bool (*learn)(unit *, skill_t, double))
+{
+ if (n != 0 && playerrace(u_race(u))) {
+ double chance = produceexp_chance();
+ if (chance > 0.0F) {
+ learn(u, sk, (n * chance) / u->number);
+ }
+ }
+}
+
+void produceexp(struct unit *u, skill_t sk, int n)
+{
+ produceexp_ex(u, sk, n, learn_skill);
+}
diff --git a/src/kernel/unit.h b/src/kernel/unit.h
index dcb7ad6e3..3aef7bd0f 100644
--- a/src/kernel/unit.h
+++ b/src/kernel/unit.h
@@ -161,8 +161,9 @@ extern "C" {
struct skill *unit_skill(const struct unit *u, skill_t id);
bool has_skill(const unit * u, skill_t sk);
int effskill(const struct unit *u, skill_t sk, const struct region *r);
- int produceexp(struct unit *u, skill_t sk, int n);
int SkillCap(skill_t sk);
+ void produceexp(struct unit *u, skill_t sk, int n);
+ void produceexp_ex(struct unit *u, skill_t sk, int n, bool (*learn)(unit *, skill_t, double));
void set_level(struct unit *u, skill_t id, int level);
int get_level(const struct unit *u, skill_t id);
diff --git a/src/kernel/unit.test.c b/src/kernel/unit.test.c
index 8dffbca3f..91333aa74 100644
--- a/src/kernel/unit.test.c
+++ b/src/kernel/unit.test.c
@@ -350,6 +350,32 @@ static void test_age_familiar(CuTest *tc) {
test_cleanup();
}
+static CuTest *g_tc;
+
+static bool cb_learn_one(unit *u, skill_t sk, double chance) {
+ CuAssertIntEquals(g_tc, SK_ALCHEMY, sk);
+ CuAssertDblEquals(g_tc, 0.5 / u->number, chance, 0.01);
+ return false;
+}
+
+static bool cb_learn_two(unit *u, skill_t sk, double chance) {
+ CuAssertIntEquals(g_tc, SK_ALCHEMY, sk);
+ CuAssertDblEquals(g_tc, 2 * 0.5 / u->number, chance, 0.01);
+ return false;
+}
+
+static void test_produceexp(CuTest *tc) {
+ unit *u;
+
+ g_tc = tc;
+ test_cleanup();
+ u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
+ set_param(&global.parameters, "study.from_use", "0.5");
+ produceexp_ex(u, SK_ALCHEMY, 1, cb_learn_one);
+ produceexp_ex(u, SK_ALCHEMY, 2, cb_learn_two);
+ test_cleanup();
+}
+
CuSuite *get_unit_suite(void)
{
CuSuite *suite = CuSuiteNew();
@@ -368,5 +394,6 @@ CuSuite *get_unit_suite(void)
SUITE_ADD_TEST(suite, test_skill_hunger);
SUITE_ADD_TEST(suite, test_skill_familiar);
SUITE_ADD_TEST(suite, test_age_familiar);
+ SUITE_ADD_TEST(suite, test_produceexp);
return suite;
}
diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c
index b10825dbd..4f6dbf0c0 100644
--- a/src/kernel/xmlreader.c
+++ b/src/kernel/xmlreader.c
@@ -2052,79 +2052,8 @@ static int parse_strings(xmlDocPtr doc)
return 0;
}
-static int parse_main(xmlDocPtr doc)
-{
- xmlXPathContextPtr xpath = xmlXPathNewContext(doc);
- xmlXPathObjectPtr result =
- xmlXPathEvalExpression(BAD_CAST "/eressea/game", xpath);
- xmlNodeSetPtr nodes = result->nodesetval;
- int i;
-
- xmlChar *propValue;
- if (nodes->nodeNr > 0) {
- xmlNodePtr node = nodes->nodeTab[0];
-
- global.producexpchance =
- (float)xml_fvalue(node, "learningbydoing", 1.0 / 3);
-
- propValue = xmlGetProp(node, BAD_CAST "name");
- if (propValue != NULL) {
- global.gamename = _strdup((const char *)propValue);
- xmlFree(propValue);
- }
-
- xmlXPathFreeObject(result);
-
- xpath->node = node;
- /* reading eressea/game/order */
- result = xmlXPathEvalExpression(BAD_CAST "order", xpath);
- nodes = result->nodesetval;
- for (i = 0; i != nodes->nodeNr; ++i) {
- xmlNodePtr node = nodes->nodeTab[i];
- xmlChar *propName = xmlGetProp(node, BAD_CAST "name");
- bool disable = xml_bvalue(node, "disable", false);
-
- if (disable) {
- int k;
- for (k = 0; k != MAXKEYWORDS; ++k) {
- if (strcmp(keywords[k], (const char *)propName) == 0) {
- enable_keyword(k, false);
- break;
- }
- }
- if (k == MAXKEYWORDS) {
- log_error("trying to disable unknown command %s\n", (const char *)propName);
- }
- }
- xmlFree(propName);
- }
-
- xmlXPathFreeObject(result);
-
- /* reading eressea/game/skill */
- result = xmlXPathEvalExpression(BAD_CAST "skill", xpath);
- nodes = result->nodesetval;
- for (i = 0; i != nodes->nodeNr; ++i) {
- xmlNodePtr node = nodes->nodeTab[i];
- xmlChar *propName = xmlGetProp(node, BAD_CAST "name");
- skill_t sk = findskill((const char *)propName);
- if (sk != NOSKILL) {
- bool enable = xml_bvalue(node, "enable", true);
- enable_skill(sk, enable);
- }
- xmlFree(propName);
- }
- }
- xmlXPathFreeObject(result);
-
- xmlXPathFreeContext(xpath);
- return 0;
-}
-
void register_xmlreader(void)
{
- xml_register_callback(parse_main);
-
xml_register_callback(parse_strings);
xml_register_callback(parse_messages);
xml_register_callback(parse_resources);
diff --git a/src/keyword.c b/src/keyword.c
index 683b1d2c2..0bd699836 100644
--- a/src/keyword.c
+++ b/src/keyword.c
@@ -2,8 +2,9 @@
#include
#include "keyword.h"
-#include "util/language.h"
-#include "util/umlaut.h"
+#include
+#include
+#include
#include
diff --git a/src/laws.c b/src/laws.c
index fce556ec7..41a0e72f3 100755
--- a/src/laws.c
+++ b/src/laws.c
@@ -2762,14 +2762,14 @@ void sinkships(struct region * r)
if (fval(r->terrain, SEA_REGION)) {
if (!enoughsailors(sh, crew_skill(sh))) {
// ship is at sea, but not enough people to control it
- float dmg = get_param_flt(global.parameters,
+ double dmg = get_param_flt(global.parameters,
"rules.ship.damage.nocrewocean",
0.30F);
damage_ship(sh, dmg);
}
} else if (!ship_owner(sh)) {
// any ship lying around without an owner slowly rots
- float dmg = get_param_flt(global.parameters, "rules.ship.damage.nocrew", 0.05F);
+ double dmg = get_param_flt(global.parameters, "rules.ship.damage.nocrew", 0.05F);
damage_ship(sh, dmg);
}
}
@@ -3496,7 +3496,7 @@ static int use_item(unit * u, const item_type * itype, int amount, struct order
static double heal_factor(const unit * u)
{
- static float elf_regen = -1;
+ static double elf_regen = -1;
switch (old_race(u_race(u))) {
case RC_TROLL:
case RC_DAEMON:
diff --git a/src/monsters.c b/src/monsters.c
index e010ee7ff..8de81c4c0 100644
--- a/src/monsters.c
+++ b/src/monsters.c
@@ -80,7 +80,7 @@ static void give_peasants(unit *u, const item_type *itype, int reduce) {
unit_addorder(u, parse_order(buf, u->faction->locale));
}
-static float monster_attack_chance(void) {
+static double monster_attack_chance(void) {
return get_param_flt(global.parameters, "rules.monsters.attack_chance", 0.4f);
}
diff --git a/src/move.c b/src/move.c
index e1207f0fa..f5da898cc 100644
--- a/src/move.c
+++ b/src/move.c
@@ -704,7 +704,7 @@ static float damage_drift(void)
{
static float value = -1.0F;
if (value < 0) {
- value = get_param_flt(global.parameters, "rules.ship.damage_drift", 0.02F);
+ value = (float)get_param_flt(global.parameters, "rules.ship.damage_drift", 0.02F);
}
return value;
}
@@ -1955,7 +1955,7 @@ sail(unit * u, order * ord, bool move_on_land, region_list ** routep)
ADDMSG(&f->msgs, msg_message("sailnolandingstorm", "ship region", sh, next_point));
}
else {
- float dmg =
+ double dmg =
get_param_flt(global.parameters, "rules.ship.damage.nolanding",
0.10F);
ADDMSG(&f->msgs, msg_message("sailnolanding", "ship region", sh,
diff --git a/src/randenc.c b/src/randenc.c
index cdf7b9380..801aac4c4 100644
--- a/src/randenc.c
+++ b/src/randenc.c
@@ -746,7 +746,7 @@ static void move_iceberg(region * r)
for (sh = r->ships; sh; sh = sh->next) {
/* Meldung an Kapitän */
- float dmg =
+ double dmg =
get_param_flt(global.parameters, "rules.ship.damage.intoiceberg",
0.10F);
damage_ship(sh, dmg);
@@ -759,7 +759,7 @@ static void move_iceberg(region * r)
translist(&rc->buildings, &r->buildings, rc->buildings);
}
while (rc->ships) {
- float dmg =
+ double dmg =
get_param_flt(global.parameters, "rules.ship.damage.withiceberg",
0.10F);
fset(rc->ships, SF_SELECT);
@@ -893,7 +893,7 @@ static void godcurse(void)
ship *sh;
for (sh = r->ships; sh;) {
ship *shn = sh->next;
- float dmg =
+ double dmg =
get_param_flt(global.parameters, "rules.ship.damage.godcurse",
0.10F);
damage_ship(sh, dmg);
diff --git a/src/tests.c b/src/tests.c
index e336c192a..596d7b02f 100644
--- a/src/tests.c
+++ b/src/tests.c
@@ -71,6 +71,8 @@ struct unit *test_create_unit(struct faction *f, struct region *r)
void test_cleanup(void)
{
+ int i;
+
free_terrains();
free_resources();
global.functions.maintenance = NULL;
@@ -87,6 +89,12 @@ void test_cleanup(void)
free_seen();
free_prefixes();
mt_clear();
+ for (i = 0; i != MAXSKILLS; ++i) {
+ enable_skill(i, true);
+ }
+ for (i = 0; i != MAXKEYWORDS; ++i) {
+ enable_keyword(i, true);
+ }
if (!mt_find("missing_message")) {
mt_register(mt_new_va("missing_message", "name:string", 0));
mt_register(mt_new_va("missing_feedback", "unit:unit", "region:region", "command:order", "name:string", 0));