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