Merge pull request #296 from ennorehling/feature/config-producexpchance

Bug 2137: calculating produceexp correctly
This commit is contained in:
Enno Rehling 2015-09-12 16:52:55 +02:00
commit 32ff7c89bc
16 changed files with 77 additions and 43 deletions

View File

@ -53,7 +53,7 @@
<xi:include href="config://default/names-ghouls.xml"/> <xi:include href="config://default/names-ghouls.xml"/>
<xi:include href="config://default/names-dragons.xml"/> <xi:include href="config://default/names-dragons.xml"/>
<game name="Eressea"> <game>
<!-- Game specific settings --> <!-- Game specific settings -->
<order name="pay" disable="yes"/> <order name="pay" disable="yes"/>

View File

@ -42,7 +42,7 @@
<xi:include href="config://default/names-ghouls.xml"/> <xi:include href="config://default/names-ghouls.xml"/>
<xi:include href="config://default/names-dragons.xml"/> <xi:include href="config://default/names-dragons.xml"/>
<game name="E3"> <game>
<!-- Game specific settings --> <!-- Game specific settings -->
<order name="besiege" disable="yes"/> <order name="besiege" disable="yes"/>
<order name="steal" disable="yes"/> <order name="steal" disable="yes"/>

View File

@ -29,6 +29,7 @@
"recruit.allow_merge": true, "recruit.allow_merge": true,
"study.expensivemigrants": true, "study.expensivemigrants": true,
"study.speedup": 2, "study.speedup": 2,
"study.from_use": 0.4,
"world.era": 3, "world.era": 3,
"rules.migrants.max": 0, "rules.migrants.max": 0,
"rules.reserve.twophase": true, "rules.reserve.twophase": true,

View File

@ -42,7 +42,7 @@
<xi:include href="config://default/names-ghouls.xml"/> <xi:include href="config://default/names-ghouls.xml"/>
<xi:include href="config://default/names-dragons.xml"/> <xi:include href="config://default/names-dragons.xml"/>
<game name="Deveron"> <game>
<order name="besiege" disable="yes"/> <order name="besiege" disable="yes"/>
<order name="steal" disable="yes"/> <order name="steal" disable="yes"/>
<order name="buy" disable="yes"/> <order name="buy" disable="yes"/>

View File

@ -2895,10 +2895,10 @@ static void aftermath(battle * b)
if (sh && fval(sh, SF_DAMAGED)) { if (sh && fval(sh, SF_DAMAGED)) {
int n = b->turn - 2; int n = b->turn - 2;
if (n > 0) { if (n > 0) {
float dmg = double dmg =
get_param_flt(global.parameters, "rules.ship.damage.battleround", get_param_flt(global.parameters, "rules.ship.damage.battleround",
0.05F); 0.05F);
damage_ship(sh, dmg * (float)n); damage_ship(sh, dmg * n);
freset(sh, SF_DAMAGED); freset(sh, SF_DAMAGED);
} }
} }

View File

@ -191,7 +191,7 @@ static void chaos(region * r)
while (sh) { while (sh) {
ship *nsh = sh->next; ship *nsh = sh->next;
float dmg = double dmg =
get_param_flt(global.parameters, "rules.ship.damage.atlantis", get_param_flt(global.parameters, "rules.ship.damage.atlantis",
0.50); 0.50);
damage_ship(sh, dmg); damage_ship(sh, dmg);

View File

@ -1123,10 +1123,10 @@ void set_basepath(const char *path)
g_basedir = 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); 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) 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; 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 lovar(double xpct_x2)
{ {
int n = (int)(xpct_x2 * 500) + 1; int n = (int)(xpct_x2 * 500) + 1;

View File

@ -261,8 +261,6 @@ extern "C" {
unsigned int data_turn; unsigned int data_turn;
struct param *parameters; struct param *parameters;
void *vm_state; void *vm_state;
float producexpchance;
int cookie;
int data_version; /* TODO: eliminate in favor of gamedata.version */ int data_version; /* TODO: eliminate in favor of gamedata.version */
struct _dictionary_ *inifile; struct _dictionary_ *inifile;
@ -271,6 +269,10 @@ extern "C" {
const struct race * rc, int in_turn); const struct race * rc, int in_turn);
int(*maintenance) (const struct unit * u); int(*maintenance) (const struct unit * u);
} functions; } 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; } settings;
typedef struct helpmode { typedef struct helpmode {
@ -284,7 +286,7 @@ extern "C" {
const char *get_param(const struct param *p, const char *key); 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 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); 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); void free_params(struct param **pp);
bool ExpensiveMigrants(void); bool ExpensiveMigrants(void);

View File

@ -1933,3 +1933,27 @@ bool unit_name_equals_race(const unit *u) {
bool unit_can_study(const unit *u) { bool unit_can_study(const unit *u) {
return !((u_race(u)->flags & RCF_NOLEARN) || fval(u, UFL_WERE)); 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);
}

View File

@ -161,8 +161,9 @@ extern "C" {
struct skill *unit_skill(const struct unit *u, skill_t id); struct skill *unit_skill(const struct unit *u, skill_t id);
bool has_skill(const unit * u, skill_t sk); bool has_skill(const unit * u, skill_t sk);
int effskill(const struct unit *u, skill_t sk, const struct region *r); 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); 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); void set_level(struct unit *u, skill_t id, int level);
int get_level(const struct unit *u, skill_t id); int get_level(const struct unit *u, skill_t id);

View File

@ -350,6 +350,32 @@ static void test_age_familiar(CuTest *tc) {
test_cleanup(); 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 *get_unit_suite(void)
{ {
CuSuite *suite = CuSuiteNew(); 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_hunger);
SUITE_ADD_TEST(suite, test_skill_familiar); SUITE_ADD_TEST(suite, test_skill_familiar);
SUITE_ADD_TEST(suite, test_age_familiar); SUITE_ADD_TEST(suite, test_age_familiar);
SUITE_ADD_TEST(suite, test_produceexp);
return suite; return suite;
} }

View File

@ -2060,19 +2060,9 @@ static int parse_main(xmlDocPtr doc)
xmlNodeSetPtr nodes = result->nodesetval; xmlNodeSetPtr nodes = result->nodesetval;
int i; int i;
xmlChar *propValue;
if (nodes->nodeNr > 0) { if (nodes->nodeNr > 0) {
xmlNodePtr node = nodes->nodeTab[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); xmlXPathFreeObject(result);
xpath->node = node; xpath->node = node;
@ -2082,9 +2072,8 @@ static int parse_main(xmlDocPtr doc)
for (i = 0; i != nodes->nodeNr; ++i) { for (i = 0; i != nodes->nodeNr; ++i) {
xmlNodePtr node = nodes->nodeTab[i]; xmlNodePtr node = nodes->nodeTab[i];
xmlChar *propName = xmlGetProp(node, BAD_CAST "name"); xmlChar *propName = xmlGetProp(node, BAD_CAST "name");
bool disable = xml_bvalue(node, "disable", false);
if (disable) { if (xml_bvalue(node, "disable", false)) {
int k; int k;
for (k = 0; k != MAXKEYWORDS; ++k) { for (k = 0; k != MAXKEYWORDS; ++k) {
if (strcmp(keywords[k], (const char *)propName) == 0) { if (strcmp(keywords[k], (const char *)propName) == 0) {

View File

@ -2762,14 +2762,14 @@ void sinkships(struct region * r)
if (fval(r->terrain, SEA_REGION)) { if (fval(r->terrain, SEA_REGION)) {
if (!enoughsailors(sh, crew_skill(sh))) { if (!enoughsailors(sh, crew_skill(sh))) {
// ship is at sea, but not enough people to control it // 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", "rules.ship.damage.nocrewocean",
0.30F); 0.30F);
damage_ship(sh, dmg); damage_ship(sh, dmg);
} }
} else if (!ship_owner(sh)) { } else if (!ship_owner(sh)) {
// any ship lying around without an owner slowly rots // 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); 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 double heal_factor(const unit * u)
{ {
static float elf_regen = -1; static double elf_regen = -1;
switch (old_race(u_race(u))) { switch (old_race(u_race(u))) {
case RC_TROLL: case RC_TROLL:
case RC_DAEMON: case RC_DAEMON:

View File

@ -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)); 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); return get_param_flt(global.parameters, "rules.monsters.attack_chance", 0.4f);
} }

View File

@ -704,7 +704,7 @@ static float damage_drift(void)
{ {
static float value = -1.0F; static float value = -1.0F;
if (value < 0) { 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; 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)); ADDMSG(&f->msgs, msg_message("sailnolandingstorm", "ship region", sh, next_point));
} }
else { else {
float dmg = double dmg =
get_param_flt(global.parameters, "rules.ship.damage.nolanding", get_param_flt(global.parameters, "rules.ship.damage.nolanding",
0.10F); 0.10F);
ADDMSG(&f->msgs, msg_message("sailnolanding", "ship region", sh, ADDMSG(&f->msgs, msg_message("sailnolanding", "ship region", sh,

View File

@ -746,7 +746,7 @@ static void move_iceberg(region * r)
for (sh = r->ships; sh; sh = sh->next) { for (sh = r->ships; sh; sh = sh->next) {
/* Meldung an Kapitän */ /* Meldung an Kapitän */
float dmg = double dmg =
get_param_flt(global.parameters, "rules.ship.damage.intoiceberg", get_param_flt(global.parameters, "rules.ship.damage.intoiceberg",
0.10F); 0.10F);
damage_ship(sh, dmg); damage_ship(sh, dmg);
@ -759,7 +759,7 @@ static void move_iceberg(region * r)
translist(&rc->buildings, &r->buildings, rc->buildings); translist(&rc->buildings, &r->buildings, rc->buildings);
} }
while (rc->ships) { while (rc->ships) {
float dmg = double dmg =
get_param_flt(global.parameters, "rules.ship.damage.withiceberg", get_param_flt(global.parameters, "rules.ship.damage.withiceberg",
0.10F); 0.10F);
fset(rc->ships, SF_SELECT); fset(rc->ships, SF_SELECT);
@ -893,7 +893,7 @@ static void godcurse(void)
ship *sh; ship *sh;
for (sh = r->ships; sh;) { for (sh = r->ships; sh;) {
ship *shn = sh->next; ship *shn = sh->next;
float dmg = double dmg =
get_param_flt(global.parameters, "rules.ship.damage.godcurse", get_param_flt(global.parameters, "rules.ship.damage.godcurse",
0.10F); 0.10F);
damage_ship(sh, dmg); damage_ship(sh, dmg);