diff --git a/process/CMakeLists.txt b/process/CMakeLists.txt index 106c4c864..7039bf127 100644 --- a/process/CMakeLists.txt +++ b/process/CMakeLists.txt @@ -1,5 +1,5 @@ install(PROGRAMS create-orders backup-box backup-onedrive - run-turn send-zip-report + run-turn send-zip-report updatelist.sh send-bz2-report compress.py compress.sh epasswd.py accept-orders.py getemail.py checkpasswd.py sendreport.sh sendreports.sh orders-accept DESTINATION bin) diff --git a/process/cron/run-eressea.cron b/process/cron/run-eressea.cron index f72d37482..11993132d 100755 --- a/process/cron/run-eressea.cron +++ b/process/cron/run-eressea.cron @@ -86,6 +86,7 @@ backup BACKUP "$GAME" "$TURN" upload "$BACKUP" rm -f execute.lock "$BIN/run-turn" "$GAME" "$TURN" +"$BIN/updatelist.sh" "$TURN" touch execute.lock (( NEXTTURN=TURN+1 )) || true diff --git a/process/updatelist.sh b/process/updatelist.sh new file mode 100755 index 000000000..cf63df8e1 --- /dev/null +++ b/process/updatelist.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +TURN=$1 +LIST=eressea-announce@kn-bremen.de +awk '{ if ($2 =="'$TURN'") print $5 }' deadlog.txt |\ + mailman delmembers -f- -l$LIST +tail reports/reports.txt | cut -d: -f2 | sed -e 's/email=//' | \ + mailman addmembers - $LIST + diff --git a/res/e3a/races.xml b/res/e3a/races.xml index ad2e6c1a9..95c46852a 100644 --- a/res/e3a/races.xml +++ b/res/e3a/races.xml @@ -714,7 +714,7 @@ - + diff --git a/src/battle.c b/src/battle.c index 26cad87e7..d37d846cc 100644 --- a/src/battle.c +++ b/src/battle.c @@ -40,6 +40,7 @@ #include "kernel/region.h" #include "kernel/ship.h" #include "kernel/skill.h" +#include "kernel/skills.h" #include "kernel/terrain.h" #include "kernel/unit.h" #include "kernel/spell.h" @@ -938,7 +939,7 @@ void kill_troop(troop dt) */ void drain_exp(struct unit *u, int n) { - skill_t sk = (skill_t)(rng_int() % MAXSKILLS); + skill_t sk = (skill_t)(rng_uint() % MAXSKILLS); skill_t ssk; /* TODO (enno): we can use u->skill_size to find a random skill */ diff --git a/src/battle.test.c b/src/battle.test.c index 95b014ceb..9ba14b10e 100644 --- a/src/battle.test.c +++ b/src/battle.test.c @@ -1520,6 +1520,7 @@ static void test_combat_rosthauch(CuTest *tc) { const resource_type *rtype; test_setup(); + random_source_inject_constants(1.0, 0xdeadbeef); init_resources(); rtype = rt_get_or_create("iron"); it_rust1 = create_weapon("sword", rtype); diff --git a/src/bind_region.c b/src/bind_region.c index 782900075..c80eb7efd 100644 --- a/src/bind_region.c +++ b/src/bind_region.c @@ -273,15 +273,17 @@ static int tolua_region_has_border(lua_State *L) if (btype) { direction_t dir = (direction_t)tolua_tonumber(L, 3, 0); region *r2 = rconnect(r, dir); - connection *b; - for (b = get_borders(r, r2); b; b = b->next) { - if (b->type == btype) { - lua_pushboolean(L, true); - return 1; + if (r2) { + connection *b; + for (b = get_borders(r, r2); b; b = b->next) { + if (b->type == btype) { + lua_pushboolean(L, true); + return 1; + } } + lua_pushboolean(L, false); + return 1; } - lua_pushboolean(L, false); - return 1; } return 0; } diff --git a/src/crimport.c b/src/crimport.c index 13c45d13a..e5f82a268 100644 --- a/src/crimport.c +++ b/src/crimport.c @@ -898,7 +898,7 @@ static enum CR_Error handle_skill(context *ctx, const char *value, const char *n int level = atoi(val + 1) - rc_skillmod(rc, sk) - terrain_mod(rc, sk, u->region); if (level > 0) { struct skill *sv = add_skill(u, sk); - sk_set_level(sv, level); + sk_set_level(u, sv, level); sv->old = sv->level; } if (sk == SK_STAMINA) { diff --git a/src/exparse.c b/src/exparse.c index a80067628..2a8b17b3d 100644 --- a/src/exparse.c +++ b/src/exparse.c @@ -28,6 +28,7 @@ #include #include +#include #include #ifdef XML_LARGE_SIZE @@ -1070,7 +1071,7 @@ static void start_races(parseinfo *pi, const XML_Char *el, const XML_Char **attr } else if (xml_strequal(el, "skill")) { const XML_Char *name = NULL; - int i, speed = 0, mod = 0; + int i, speed = INT_MAX, mod = 0; for (i = 0; attr[i]; i += 2) { const XML_Char *key = attr[i], *val = attr[i + 1]; @@ -1090,8 +1091,8 @@ static void start_races(parseinfo *pi, const XML_Char *el, const XML_Char **attr if (name) { skill_t sk = find_skill(name); if (sk != NOSKILL) { - rc->bonus[sk] = (char)mod; - if (speed != 0) { + rc->bonus[sk] = (signed char)mod; + if (speed != INT_MAX) { set_study_speed(rc, sk, speed); } } diff --git a/src/kernel/build.test.c b/src/kernel/build.test.c index 05366fa66..4c4765c1f 100644 --- a/src/kernel/build.test.c +++ b/src/kernel/build.test.c @@ -307,6 +307,7 @@ static void test_build_with_potion_and_ring(CuTest *tc) set_level(u, bf.cons.skill, bf.cons.minskill); CuAssertIntEquals(tc, 1, build(u, 1, &bf.cons, 0, 200, 0)); + set_level(u, bf.cons.skill, bf.cons.minskill); i_change(&u->items, ring, 1); change_effect(u, ptype, 4); CuAssertIntEquals(tc, 11, build(u, 1, &bf.cons, 0, 200, 0)); diff --git a/src/kernel/connection.c b/src/kernel/connection.c index f5dfd43d1..8a6740c78 100644 --- a/src/kernel/connection.c +++ b/src/kernel/connection.c @@ -100,8 +100,11 @@ static connection **get_borders_i(const region * r1, const region * r2) connection *get_borders(const region * r1, const region * r2) { - connection **bp = get_borders_i(r1, r2); - return *bp; + if (r1 && r2) { + connection **bp = get_borders_i(r1, r2); + return *bp; + } + return NULL; } connection *border_create(region *from) diff --git a/src/kernel/messages.test.c b/src/kernel/messages.test.c index 967545e83..b1e304f03 100644 --- a/src/kernel/messages.test.c +++ b/src/kernel/messages.test.c @@ -6,7 +6,6 @@ #include "util/message.h" #include "util/keyword.h" // for K_ENTERTAIN, K_MOVE -#include "util/variant.h" // for variant #include #include @@ -88,7 +87,7 @@ void test_add_message(CuTest *tc) { CuAssertPtrEquals(tc, msg, mlist->begin->msg); CuAssertPtrEquals(tc, NULL, mlist->begin->next); CuAssertIntEquals(tc, 2, msg->refcount); - msg->is_silent = 1; + msg->is_silent = -1; add_message(&mlist, msg); CuAssertIntEquals(tc, 2, msg->refcount); CuAssertPtrEquals(tc, NULL, mlist->begin->next); diff --git a/src/kernel/race.c b/src/kernel/race.c index bda9bceff..9c9f011a5 100644 --- a/src/kernel/race.c +++ b/src/kernel/race.c @@ -455,7 +455,7 @@ void set_study_speed(race *rc, skill_t sk, int modifier) { rc->study_speed = calloc(1, MAXSKILLS); if (!rc->study_speed) abort(); } - rc->study_speed[sk] = (char)modifier; + rc->study_speed[sk] = (signed char)modifier; } const race *rc_otherrace(const race *rc) diff --git a/src/kernel/race.test.c b/src/kernel/race.test.c index 56ecb8b51..f37d5b69a 100644 --- a/src/kernel/race.test.c +++ b/src/kernel/race.test.c @@ -8,7 +8,6 @@ #include #include -#include #include #include diff --git a/src/kernel/region.c b/src/kernel/region.c index 022c7e3e4..5b5ceee01 100644 --- a/src/kernel/region.c +++ b/src/kernel/region.c @@ -1097,8 +1097,8 @@ void init_region(region *r) t_plain = get_terrain(terrainnames[T_PLAIN]); } if (terrain->size>0) { - horses = rng_int() % (terrain->size / 50); - trees = terrain->size * (30 + rng_int() % 40) / 1000; + horses = rng_uint() % (terrain->size / 50); + trees = terrain->size * (30 + rng_uint() % 40) / 1000; } if (t_plain && terrain == t_plain) { rsethorses(r, horses); diff --git a/src/kernel/save.c b/src/kernel/save.c index 2b54afaba..958ac4039 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -383,7 +383,7 @@ static void write_skills(gamedata *data, const unit *u) { #ifndef NDEBUG assert(SK_SKILL(sv) > sk); sk = SK_SKILL(sv); - assert(sv->days <= MAX_DAYS_TO_NEXT_LEVEL(sv->level)); + ASSERT_VALID_SKILL(sv, u_race(u)); #endif WRITE_INT(data->store, sv->id); WRITE_INT(data->store, sv->level); diff --git a/src/kernel/skills.c b/src/kernel/skills.c index 43e75d809..9b9d1dd92 100644 --- a/src/kernel/skills.c +++ b/src/kernel/skills.c @@ -5,6 +5,7 @@ #include "unit.h" #include +#include #include #include @@ -80,62 +81,96 @@ static bool rule_random_progress(void) return rule != 0; } -static int progress_weeks(unsigned int level) +static unsigned int progress_weeks(unsigned int level) /* how many weeks must i study to get from level-1 to level */ { unsigned int coins = MAX_WEEKS_TO_NEXT_LEVEL(level - 1) - 1; - int heads = 1; + unsigned int heads = 1; while (coins--) { - heads += rng_int() % 2; + heads += rng_uint() % 2; } return heads; } static void skill_set(skill *sv, unsigned int level, unsigned int days) { - assert(days <= MAX_DAYS_TO_NEXT_LEVEL(level)); sv->level = level; sv->days = days; assert(sv->days == days && sv->level == level); } -void sk_set_level(skill *sv, int level) +int study_speed(const struct race *rc, enum skill_t sk) { - int weeks = rule_random_progress() ? progress_weeks(level + 1) : (level + 1); - skill_set(sv, level, weeks * SKILL_DAYS_PER_WEEK); + int mod = (rc && rc->study_speed) ? rc->study_speed[sk] : 0; + return SKILL_DAYS_PER_WEEK - mod; } -void increase_skill_weeks(unit * u, enum skill_t sk, const unsigned int weeks) +void sk_set_level(const struct unit *u, skill *sv, unsigned int level) { - skill *sv = unit_skill(u, sk); - unsigned int days = weeks * SKILL_DAYS_PER_WEEK; - if (!sv) { - sv = add_skill(u, sk); - } - while (sv->days <= days) { - days -= sv->days; - sk_set_level(sv, sv->level + 1); + unsigned int weeks = rule_random_progress() ? progress_weeks(level + 1) : (level + 1); + const struct race *rc = u ? u_race(u) : NULL; + int speed = rc ? study_speed(rc, sv->id) : SKILL_DAYS_PER_WEEK; + unsigned int days = SKILL_DAYS_PER_WEEK + (weeks - 1) * speed; + ASSERT_VALID_SKILL(sv, rc); + skill_set(sv, level, days); +} + +static void increase_skill_days(unit *u, skill *sv, unsigned int days) { + if (days > 0) { + unsigned int leveldays = sv->days; + while (leveldays <= days) { + sk_set_level(u, sv, sv->level + 1); + days -= leveldays; + leveldays = sv->days; + } + sv->days = leveldays - days; + ASSERT_VALID_SKILL(sv, u_race(u)); } - sv->days -= days; - assert(sv->days <= MAX_DAYS_TO_NEXT_LEVEL(sv->level)); } -void reduce_skill_weeks(unit * u, skill * sv, const unsigned int weeks) +static void reduce_skill_days(unit *u, skill *sv, unsigned int days) { - unsigned int days = weeks * SKILL_DAYS_PER_WEEK; - unsigned int max_days = MAX_DAYS_TO_NEXT_LEVEL(sv->level); + if (sv) { + // first, strip full levels off the skill: + // max_days = maximum days I can have "to do" at current level + unsigned int max_days = MAX_DAYS_TO_NEXT_LEVEL(sv->level); + days += sv->days; + while (sv->level > 0 && max_days < days) { + // level_days = expected time to complete current level. + unsigned int level_days = SKILL_DAYS_PER_WEEK * (1 + sv->level); + days -= level_days; + --sv->level; + max_days = MAX_DAYS_TO_NEXT_LEVEL(sv->level); + } + // store the remaining days + sv->days = days; + if (sv->level == 0 && sv->days >= SKILL_DAYS_PER_WEEK) { + remove_skill(u, (skill_t)sv->id); + } + } +} - sv->days += days; - while (sv->level > 0 && sv->days > max_days) { - sv->days -= sv->level * SKILL_DAYS_PER_WEEK; - --sv->level; - max_days -= 2 * SKILL_DAYS_PER_WEEK; +void change_skill(unit *u, skill *sv, int days) +{ + assert(sv); + if (days < 0) { + reduce_skill_days(u, sv, -days); + } + else { + increase_skill_days(u, sv, days); } - if (sv->level == 0) { - /* reroll */ - sk_set_level(sv, sv->level + 1); +} + +void change_skill_days(struct unit *u, enum skill_t sk, int days) +{ + assert(sk >= 0 && sk < MAXSKILLS); + if (days != 0) { + skill *sv = unit_skill(u, sk); + if (!sv && days > 0) { + sv = add_skill(u, sk); + } + change_skill(u, sv, days); } - assert(sv->days <= MAX_DAYS_TO_NEXT_LEVEL(sv->level)); } int skill_compare(const skill * sk, const skill * sc) @@ -232,3 +267,4 @@ int skill_days(unit *u, enum skill_t sk) const skill *sv = unit_skill(u, sk); return sv ? sv->days : 1; } + diff --git a/src/kernel/skills.h b/src/kernel/skills.h index a8c26581e..29ad2c356 100644 --- a/src/kernel/skills.h +++ b/src/kernel/skills.h @@ -4,7 +4,9 @@ #define MAX_WEEKS_TO_NEXT_LEVEL(level) ((level) * 2 + 1) #define MAX_DAYS_TO_NEXT_LEVEL(level) (SKILL_DAYS_PER_WEEK * MAX_WEEKS_TO_NEXT_LEVEL(level)) - +#define MAX_DAYS_TO_NEXT_LEVEL_EX(level, speed) (SKILL_DAYS_PER_WEEK + (speed) * (MAX_WEEKS_TO_NEXT_LEVEL(level) - 1)) +#define ASSERT_VALID_SKILL(sv, rc) \ + assert((sv)->days <= MAX_DAYS_TO_NEXT_LEVEL_EX((sv)->level, study_speed((rc), (sv)->id))) typedef struct skill { unsigned int id : 5; unsigned int level : 7; @@ -35,14 +37,16 @@ int skillmod(const struct unit *u, const struct region *r, struct attrib *make_skillmod(enum skill_t sk, skillmod_fun special, double multiplier, int bonus); -void increase_skill_weeks(struct unit * u, enum skill_t sk, const unsigned int weeks); -void reduce_skill_weeks(struct unit *u, skill * sv, const unsigned int weeks); +void change_skill_days(struct unit *u, enum skill_t sk, int days); +void change_skill(struct unit *u, skill *sv, int days); int merge_skill(const skill* sv, const skill* sn, skill* result, int n, int add); -void sk_set_level(skill * sv, int level); +void sk_set_level(const struct unit *u, skill * sv, unsigned int level); int skill_compare(const skill* sk, const skill* sc); int skill_level(struct unit *u, enum skill_t sk); /** number of days-equivalent the unit must STUDY to reach the next level: */ int skill_days(struct unit *u, enum skill_t sk); +/** number of days in a week for this learner */ +int study_speed(const struct race *rc, enum skill_t sk); #define SK_SKILL(sv) ((skill_t) (sv->id)) diff --git a/src/kernel/skills.test.c b/src/kernel/skills.test.c index d7752c304..654706851 100644 --- a/src/kernel/skills.test.c +++ b/src/kernel/skills.test.c @@ -17,12 +17,19 @@ static void test_skill_set(CuTest *tc) test_setup(); config_set_int("study.random_progress", 0); - sk_set_level(&value, 2); + sk_set_level(NULL, &value, 2); CuAssertIntEquals(tc, 1, value.old); CuAssertIntEquals(tc, 2, value.level); CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, value.days); } +static int days_effort(const skill *sv) +{ + int n = sv->level + 1; + int weeks = (n + 1) * n / 2; + return weeks * SKILL_DAYS_PER_WEEK - sv->days; +} + static void test_skill_change(CuTest *tc) { unit *u; @@ -31,54 +38,88 @@ static void test_skill_change(CuTest *tc) test_setup(); config_set_int("study.random_progress", 0); u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); - increase_skill_weeks(u, SK_CROSSBOW, 1); - sv = unit_skill(u, SK_CROSSBOW); + set_number(u, 2); // number should have no effect on skill values + change_skill_days(u, SK_CROSSBOW, SKILL_DAYS_PER_WEEK); + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1 * SKILL_DAYS_PER_WEEK, days_effort(sv)); CuAssertIntEquals(tc, 1, skill_level(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 1, sv->level); + /* no random progress, so it will take 2 weeks of learning to next level: */ CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK, sv->days); - increase_skill_weeks(u, SK_CROSSBOW, 1); + change_skill_days(u, SK_CROSSBOW, SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK, days_effort(sv)); CuAssertIntEquals(tc, 1, skill_level(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 1, sv->level); CuAssertIntEquals(tc, 1 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 1 * SKILL_DAYS_PER_WEEK, sv->days); - increase_skill_weeks(u, SK_CROSSBOW, 1); + change_skill_days(u, SK_CROSSBOW, SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, days_effort(sv)); CuAssertIntEquals(tc, 2, skill_level(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 2, sv->level); CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, sv->days); - increase_skill_weeks(u, SK_CROSSBOW, 4); + change_skill_days(u, SK_CROSSBOW, 4 * SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 7 * SKILL_DAYS_PER_WEEK, days_effort(sv)); CuAssertIntEquals(tc, 3, skill_level(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 3, sv->level); CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, sv->days); - - reduce_skill_weeks(u, sv, 1); + change_skill_days(u, SK_CROSSBOW, -SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 6 * SKILL_DAYS_PER_WEEK, days_effort(sv)); CuAssertIntEquals(tc, 3, skill_level(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 3, sv->level); CuAssertIntEquals(tc, 4 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 4 * SKILL_DAYS_PER_WEEK, sv->days); - reduce_skill_weeks(u, sv, 1); + change_skill_days(u, SK_CROSSBOW, -SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 5 * SKILL_DAYS_PER_WEEK, days_effort(sv)); CuAssertIntEquals(tc, 3, skill_level(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 3, sv->level); CuAssertIntEquals(tc, 5 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 5 * SKILL_DAYS_PER_WEEK, sv->days); - reduce_skill_weeks(u, sv, 2); + change_skill_days(u, SK_CROSSBOW, -SKILL_DAYS_PER_WEEK * 2); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, days_effort(sv)); CuAssertIntEquals(tc, 3, skill_level(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 3, sv->level); CuAssertIntEquals(tc, 7 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 7 * SKILL_DAYS_PER_WEEK, sv->days); - reduce_skill_weeks(u, sv, 1); + change_skill_days(u, SK_CROSSBOW, -SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK, days_effort(sv)); CuAssertIntEquals(tc, 2, skill_level(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 2, sv->level); - CuAssertIntEquals(tc, 5 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 5 * SKILL_DAYS_PER_WEEK, sv->days); + CuAssertIntEquals(tc, 4 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); + sv->level = 10; - reduce_skill_weeks(u, sv, 25); - CuAssertIntEquals(tc, 8, skill_level(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 8, sv->level); - CuAssertIntEquals(tc, 11 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 11 * SKILL_DAYS_PER_WEEK, sv->days); + sv->days = SKILL_DAYS_PER_WEEK; + CuAssertIntEquals(tc, 65 * SKILL_DAYS_PER_WEEK, days_effort(sv)); + + change_skill_days(u, SK_CROSSBOW, -25 * SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 40 * SKILL_DAYS_PER_WEEK, days_effort(sv)); + // 40 = (8 * 9) / 2 + 4 + CuAssertIntEquals(tc, 9, skill_level(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 15 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); +} + +static void test_reduce_skill(CuTest *tc) +{ + unit *u; + + test_setup(); + config_set_int("study.random_progress", 0); + u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); + set_number(u, 2); // number should have no effect on skill values + change_skill_days(u, SK_CROSSBOW, SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 1, skill_level(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); + change_skill_days(u, SK_CROSSBOW, -SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 1, skill_level(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); + change_skill_days(u, SK_CROSSBOW, -1); + CuAssertPtrEquals(tc, NULL, u->skills); + + change_skill_days(u, SK_CROSSBOW, 3 * SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 2, skill_level(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); + change_skill_days(u, SK_CROSSBOW, - 2 * SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, days_effort(u->skills)); + CuAssertIntEquals(tc, 2, skill_level(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 5 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); + change_skill_days(u, SK_CROSSBOW, -1); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK - 1, days_effort(u->skills)); + CuAssertIntEquals(tc, 1, skill_level(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK + 1, skill_days(u, SK_CROSSBOW)); + change_skill_days(u, SK_CROSSBOW, 2 - SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 1, days_effort(u->skills)); + change_skill_days(u, SK_CROSSBOW, -1); + CuAssertIntEquals(tc, 1, skill_level(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); + change_skill_days(u, SK_CROSSBOW, -1); + CuAssertPtrEquals(tc, NULL, u->skills); } static void test_set_level(CuTest * tc) @@ -211,6 +252,7 @@ static void test_skills_merge(CuTest* tc) CuSuite *get_skills_suite(void) { CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_reduce_skill); SUITE_ADD_TEST(suite, test_skill_set); SUITE_ADD_TEST(suite, test_skill_change); SUITE_ADD_TEST(suite, test_set_level); diff --git a/src/kernel/unit.c b/src/kernel/unit.c index 2288dffb3..33f6ab24d 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -581,13 +581,13 @@ void set_level(struct unit * u, enum skill_t sk, unsigned int value) for (len = arrlen(u->skills), s = 0; s != len; ++s) { skill* sv = u->skills + s; if (sv->id == sk) { - sk_set_level(sv, value); + sk_set_level(u, sv, value); sv->old = sv->level; return; } ++sv; } - sk_set_level(add_skill(u, sk), value); + sk_set_level(u, add_skill(u, sk), value); } static int leftship_age(struct attrib *a, void *owner) @@ -1231,7 +1231,7 @@ static int newunitid(void) { int random_unit_no; int start_random_no; - random_unit_no = 1 + (rng_int() % MAX_UNIT_NR); + random_unit_no = 1 + (rng_uint() % MAX_UNIT_NR); start_random_no = random_unit_no; while (ufindhash(random_unit_no) || dfindhash(random_unit_no) diff --git a/src/kernel/version.c b/src/kernel/version.c index 56acac4fb..b017fdb12 100644 --- a/src/kernel/version.c +++ b/src/kernel/version.c @@ -8,7 +8,7 @@ #ifndef ERESSEA_VERSION /* the version number, if it was not passed to make with -D */ -#define ERESSEA_VERSION "30.4.0" +#define ERESSEA_VERSION "31.1.0" #endif const char *eressea_version(void) { diff --git a/src/laws.c b/src/laws.c index f92063faa..af6fecab7 100644 --- a/src/laws.c +++ b/src/laws.c @@ -189,7 +189,7 @@ static void potion_effects(unit *u) { } /* bestes Talent raussuchen */ if (sb != NULL) { - reduce_skill_weeks(u, sb, weeks); + change_skill(u, sb, -SKILL_DAYS_PER_WEEK * weeks); ADDMSG(&u->faction->msgs, msg_message("dumbeffect", "unit weeks skill", u, weeks, (skill_t)sb->id)); } /* sonst Glueck gehabt: wer nix weiss, kann nix vergessen... */ diff --git a/src/main.c b/src/main.c index 9d8973225..a3de84ae9 100644 --- a/src/main.c +++ b/src/main.c @@ -239,7 +239,7 @@ static int parse_args(int argc, char **argv) case 'l': i = get_arg(argc, argv, 2, i, &arg, NULL); if (arg) { - log_flags = arg ? atoi(arg) : 0xff; + log_flags = atoi(arg); } else { return usage(argv[0], NULL); } @@ -263,7 +263,7 @@ static int parse_args(int argc, char **argv) case 'w': i = get_arg(argc, argv, 2, i, &arg, NULL); if (arg) { - bcrypt_workfactor = arg ? atoi(arg) : 0xff; + bcrypt_workfactor = atoi(arg); } else { return usage(argv[0], NULL); } @@ -274,7 +274,7 @@ static int parse_args(int argc, char **argv) case 'v': i = get_arg(argc, argv, 2, i, &arg, NULL); if (arg) { - verbosity = arg ? atoi(arg) : 0xff; + verbosity = atoi(arg); } break; case 'h': diff --git a/src/spells.c b/src/spells.c index b468a3b77..756d89c84 100644 --- a/src/spells.c +++ b/src/spells.c @@ -4051,7 +4051,7 @@ static int sp_headache(castorder * co) int change = target->number; if (change > 10) change = 10; change *= (rng_uint() % 2 + 1) / target->number; - reduce_skill_weeks(target, smax, change); + change_skill(target, smax, -SKILL_DAYS_PER_WEEK * change); } set_order(&target->thisorder, NULL); diff --git a/src/study.c b/src/study.c index 6560e7add..c9ff33751 100644 --- a/src/study.c +++ b/src/study.c @@ -164,21 +164,6 @@ static int produceexp_days(void) { return rule; } -static int study_days(unit * u, skill_t sk) -{ - int speed = SKILL_DAYS_PER_WEEK; - if (u_race(u)->study_speed) { - speed += u_race(u)->study_speed[sk]; - if (speed < SKILL_DAYS_PER_WEEK) { - skill *sv = unit_skill(u, sk); - if (sv == NULL) { - speed = SKILL_DAYS_PER_WEEK; - } - } - } - return u->number * speed; -} - static int teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk, bool report, int *academy_students) @@ -215,7 +200,7 @@ teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk, const struct building_type *btype = bt_find("academy"); if (active_building(student, btype)) { /* Jeder Schueler zusaetzlich +10 Tage wenn in Uni. */ - teach->days += students * produceexp_days(); /* learning erhoehen */ + teach->days += students * SKILL_DAYS_PER_WEEK/3; /* learning erhoehen */ /* Lehrer zusaetzlich +1 Tag pro Schueler. */ if (academy_students) { *academy_students += students; @@ -429,7 +414,8 @@ int teach_cmd(unit * teacher, struct order *ord) free_order(new_order); /* parse_order & set_order have each increased the refcount */ } if (academy_students > 0 && sk_academy != NOSKILL) { - change_skill_days(teacher, sk_academy, academy_students); + // TODO: rounding errors here. + change_skill_days(teacher, sk_academy, academy_students / teacher->number); } reset_order(); return 0; @@ -636,7 +622,7 @@ int study_cmd(unit * u, order * ord) } days = teach ? teach->days : 0; - days += study_days(u, sk); + days += SKILL_DAYS_PER_WEEK * u->number; if (studycost) { int cost = studycost * u->number; @@ -687,64 +673,18 @@ bool can_teach(const unit* u) return !(fval(u, UFL_WERE) || fval(u_race(u), RCF_NOTEACH)); } -static learn_fun inject_learn_fun = 0; - -void inject_learn(learn_fun fun) { - inject_learn_fun = fun; -} - -static void increase_skill_days(unit *u, skill_t sk, int days) { - assert(sk >= 0 && sk < MAXSKILLS && days >= 0); - if (days > 0) { - int leveldays = SKILL_DAYS_PER_WEEK * u->number; - int weeks = 0; - if (inject_learn_fun) { - inject_learn_fun(u, sk, days); - } - while (days >= leveldays) { - ++weeks; - days -= leveldays; - } - if (days > 0 && rng_int() % leveldays >= leveldays - days) { - ++weeks; - } - if (weeks > 0) { - increase_skill_weeks(u, sk, weeks); - } - } -} - void produceexp(struct unit *u, enum skill_t sk) { assert(u); if (u->number > 0) { const struct race *rc = u_race(u); if ((rc->flags & RCF_NOLEARN) == 0 && rc_can_learn(rc, sk)) { - increase_skill_days(u, sk, produceexp_days() * u->number); - } - } -} - -static void reduce_skill_days(unit *u, skill_t sk, int days) { - if (days > 0) { - skill *sv = unit_skill(u, sk); - if (sv) { - while (days > 0) { - if (days >= SKILL_DAYS_PER_WEEK * u->number) { - reduce_skill_weeks(u, sv, 1); - days -= SKILL_DAYS_PER_WEEK; - } - else { - if (chance(days / ((double)SKILL_DAYS_PER_WEEK * u->number))) /* (rng_int() % (30 * u->number) < days)*/ - reduce_skill_weeks(u, sv, 1); - days = 0; - } - } + change_skill_days(u, sk, produceexp_days()); } } } -/** +/** * days should be scaled by u->number; SKILL_DAYS_PER_WEEK * u->number is one week worth of learning * @return int * The additional spend, i.e. from an academy. @@ -774,7 +714,7 @@ int learn_skill(unit *u, enum skill_t sk, int days, int studycost) { n = n * avail / cost; cost = n * studycost; } - days += produceexp_days() * n; + days += SKILL_DAYS_PER_WEEK * n / 3; } /* the artacademy currently improves the learning of entertainment @@ -809,19 +749,10 @@ int learn_skill(unit *u, enum skill_t sk, int days, int studycost) { if (fval(u, UFL_HUNGER)) { days /= 2; } - change_skill_days(u, sk, days); + change_skill_days(u, sk, days / u->number); return cost; } -void change_skill_days(unit *u, enum skill_t sk, int days) { - if (days < 0) { - reduce_skill_days(u, sk, -days); - } - else { - increase_skill_days(u, sk, days); - } -} - /** * Talente von Daemonen verschieben sich. */ @@ -864,14 +795,14 @@ void demon_skillchange(unit *u) } if (roll < downchance) { - reduce_skill_weeks(u, sv, weeks); + change_skill(u, sv, -SKILL_DAYS_PER_WEEK * weeks); if (sv->level < 1) { /* demons should never forget below 1 */ set_level(u, sv->id, 1); } } else { - change_skill_days(u, sv->id, SKILL_DAYS_PER_WEEK * u->number * weeks); + change_skill_days(u, sv->id, SKILL_DAYS_PER_WEEK * weeks); } } ++sv; diff --git a/src/study.h b/src/study.h index e0ff4d93a..06619e320 100644 --- a/src/study.h +++ b/src/study.h @@ -36,13 +36,9 @@ bool check_student(const struct unit *u, struct order *ord, int learn_skill(struct unit *u, enum skill_t sk, int days, int studycost); -void change_skill_days(struct unit *u, enum skill_t sk, int days); void produceexp(struct unit *u, enum skill_t sk); void demon_skillchange(struct unit *u); -typedef void(*learn_fun)(struct unit *u, enum skill_t sk, int days); -void inject_learn(learn_fun fun); - #endif diff --git a/src/study.test.c b/src/study.test.c index 3d06bb248..9725c5adc 100644 --- a/src/study.test.c +++ b/src/study.test.c @@ -33,34 +33,6 @@ struct locale; -#define MAXLOG 4 -typedef struct log_entry { - unit *u; - skill_t sk; - int days; -} log_entry; - -static log_entry log_learners[MAXLOG]; -static int log_size; - -static void log_learn(unit *u, skill_t sk, int days) { - if (log_size < MAXLOG) { - log_entry * entry = &log_learners[log_size++]; - entry->u = u; - entry->sk = sk; - entry->days = days; - } -} - -void learn_inject(void) { - log_size = 0; - inject_learn(log_learn); -} - -void learn_reset(void) { - inject_learn(0); -} - typedef struct { unit *u; unit *teachers[2]; @@ -68,6 +40,7 @@ typedef struct { static void setup_study(void) { test_setup(); + config_set("study.random_progress", "0"); mt_create_error(77); mt_create_error(771); mt_create_error(178); @@ -99,7 +72,6 @@ static void setup_teacher(study_fixture *fix, skill_t sk) { assert(fix); setup_study(); - config_set("study.random_progress", "0"); r = test_create_plain(0, 0); f = test_create_faction(); f->locale = lang = test_create_locale(); @@ -185,42 +157,39 @@ static void test_study_speed(CuTest *tc) { race *rc; skill *sv; - test_setup(); - learn_inject(); + setup_study(); rc = test_create_race("orc"); - u = test_create_unit(test_create_faction_ex(rc, NULL), test_create_plain(0, 0)); - set_level(u, SK_BUILDING, 1); - set_level(u, SK_CATAPULT, 1); set_study_speed(rc, SK_BUILDING, -5); - sv = unit_skill(u, SK_BUILDING); - sv->days = 1 * SKILL_DAYS_PER_WEEK; CuAssertIntEquals(tc, -5, rc->study_speed[SK_BUILDING]); - u->thisorder = create_order(K_STUDY, u->faction->locale, skillnames[SK_BUILDING]); - random_source_inject_constants(0.0, 0); - study_cmd(u, u->thisorder); - CuAssertPtrEquals(tc, u, log_learners[0].u); - CuAssertIntEquals(tc, SK_BUILDING, log_learners[0].sk); - CuAssertIntEquals(tc, 25, log_learners[0].days); + u = test_create_unit(test_create_faction_ex(rc, NULL), test_create_plain(0, 0)); + + sv = add_skill(u, SK_CATAPULT); + set_level(u, SK_CATAPULT, 3); + CuAssertIntEquals(tc, 3, sv->level); + CuAssertIntEquals(tc, 4 * SKILL_DAYS_PER_WEEK, sv->days); + + sv = add_skill(u, SK_BUILDING); + set_level(u, SK_BUILDING, 1); CuAssertIntEquals(tc, 1, sv->level); - CuAssertIntEquals(tc, 1 * SKILL_DAYS_PER_WEEK, sv->days); + CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK + sv->level * 5, sv->days); + set_level(u, SK_BUILDING, 3); + CuAssertIntEquals(tc, 3, sv->level); + CuAssertIntEquals(tc, 4 * SKILL_DAYS_PER_WEEK + sv->level * 5, sv->days); - random_source_inject_constants(0.0, 5); - u->flags &= ~UFL_LONGACTION; + // learning at 30 days/week, no study_cost effect here: + u->thisorder = create_order(K_STUDY, u->faction->locale, skillnames[SK_BUILDING]); study_cmd(u, u->thisorder); - CuAssertPtrEquals(tc, u, log_learners[1].u); - CuAssertIntEquals(tc, SK_BUILDING, log_learners[1].sk); - CuAssertIntEquals(tc, 25, log_learners[1].days); - CuAssertIntEquals(tc, 2, sv->level); + CuAssertIntEquals(tc, 3, sv->level); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK + sv->level * 5, sv->days); + // learning at 30 days/week, no study_cost effect here: free_order(u->thisorder); u->thisorder = create_order(K_STUDY, u->faction->locale, skillnames[SK_CATAPULT]); u->flags &= ~UFL_LONGACTION; study_cmd(u, u->thisorder); - CuAssertPtrEquals(tc, u, log_learners[2].u); - CuAssertIntEquals(tc, SK_CATAPULT, log_learners[2].sk); - CuAssertIntEquals(tc, 30, log_learners[2].days); + CuAssertIntEquals(tc, 3, sv->level); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK + sv->level * 5, sv->days); - learn_reset(); test_teardown(); } @@ -254,6 +223,7 @@ static void test_study_bug_2194(CuTest *tc) { unit *u, *u1, *u2; struct locale * loc; building * b; + skill *sv; setup_study(); random_source_inject_constant(0.0); @@ -278,20 +248,16 @@ static void test_study_bug_2194(CuTest *tc) { u_set_building(u2, b); i_change(&u1->items, get_resourcetype(R_SILVER)->itype, 50); i_change(&u2->items, get_resourcetype(R_SILVER)->itype, 50); - learn_inject(); teach_cmd(u, u->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, u, log_learners[0].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertIntEquals(tc, 1, log_size); - CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "teach_asgood")); + CuAssertPtrEquals(tc, NULL, unit_skill(u, SK_MAGIC)); + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, TEACHDIFFERENCE, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * 3 - (u1->number / u->number), sv->days); free_order(u->thisorder); u->thisorder = create_order(K_TEACH, loc, itoa36(u2->no)); - learn_inject(); teach_cmd(u, u->thisorder); - learn_reset(); - CuAssertIntEquals(tc, 0, log_size); + CuAssertPtrEquals(tc, NULL, unit_skill(u, SK_MAGIC)); test_teardown(); } @@ -300,13 +266,17 @@ static void test_academy_building(CuTest *tc) { struct locale * loc; building * b; message * msg; + skill *sv; + const attrib *a; + const teaching_info *ti; + const struct item_type *it_silver; setup_study(); mt_create_va(mt_new("teach_asgood", NULL), "unit:unit", "region:region", "command:order", "student:unit", MT_NEW_END); - random_source_inject_constant(0.0); init_resources(); + it_silver = test_create_silver(); loc = test_create_locale(); setup_locale(loc); u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); @@ -326,18 +296,42 @@ static void test_academy_building(CuTest *tc) { u_set_building(u, b); u_set_building(u1, b); u_set_building(u2, b); - i_change(&u1->items, get_resourcetype(R_SILVER)->itype, 50); - i_change(&u2->items, get_resourcetype(R_SILVER)->itype, 50); - learn_inject(); + i_change(&u1->items, it_silver, 50 * u1->number + 10); + i_change(&u2->items, it_silver, 50 * u2->number + 10); + + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, sv ? sv->days : 0); teach_cmd(u, u->thisorder); - learn_reset(); + // cannot teach u2, because skill too high: CuAssertPtrNotNull(tc, msg = test_find_messagetype(u->faction->msgs, "teach_asgood")); CuAssertPtrEquals(tc, u, (unit *)msg->parameters[0].v); CuAssertPtrEquals(tc, u2, (unit *)msg->parameters[3].v); - CuAssertPtrEquals(tc, u, log_learners[0].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertIntEquals(tc, u1->number, log_learners[0].days); + // teacher teaches 15 students, gets 15/2 XP: + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK - (u1->number / u->number), sv->days); + + // u1 gets taught, gets teacher-in-academy bonus: + CuAssertPtrNotNull(tc, a = a_find(u1->attribs, &at_learning)); + ti = (const teaching_info *)a->data.v; + CuAssertIntEquals(tc, u1->number, ti->students); + CuAssertIntEquals(tc, u1->number * 20 * 2, ti->days); + + // u1 is learning with a teacher, gains 60*4/3 = 80 XP: + study_cmd(u1, u1->thisorder); + CuAssertIntEquals(tc, 10, i_get(u1->items, it_silver)); + CuAssertPtrNotNull(tc, sv = unit_skill(u1, SK_CROSSBOW)); + // uses 30 to reach L1, 50 towards L2 (10 remain): + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, 10, sv->days); + + // u2 is learning without a teacher, gets to level 1, +10 XP for academy: + study_cmd(u2, u2->thisorder); + CuAssertIntEquals(tc, 10, i_get(u2->items, it_silver)); + CuAssertPtrNotNull(tc, sv = unit_skill(u2, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, 20, sv->days); + test_teardown(); } @@ -350,10 +344,9 @@ static void test_academy_bonus(CuTest *tc) { unit *u, *u0, *u1, *u3; struct locale * loc; building * b; + skill *sv; setup_study(); - - random_source_inject_constant(0.0); init_resources(); loc = test_create_locale(); setup_locale(loc); @@ -380,26 +373,35 @@ static void test_academy_bonus(CuTest *tc) { scale_number(u, 2); scale_number(u1, 9); - scale_number(u3, 2); + scale_number(u3, 3); i_change(&u1->items, get_resourcetype(R_SILVER)->itype, 5000); - learn_inject(); + // u0 teaches 10 people, gains +10 XP + sv = unit_skill(u0, SK_CROSSBOW); + CuAssertIntEquals(tc, TEACHDIFFERENCE, sv->level); + CuAssertIntEquals(tc, (sv->level + 1) * SKILL_DAYS_PER_WEEK, sv->days); teach_cmd(u0, u0->thisorder); + CuAssertIntEquals(tc, TEACHDIFFERENCE, sv->level); + CuAssertIntEquals(tc, (sv->level + 1) * SKILL_DAYS_PER_WEEK - SKILL_DAYS_PER_WEEK / 3, sv->days); + + // u teaches remaining 2 person, gains +1 XP + sv = unit_skill(u, SK_CROSSBOW); + CuAssertIntEquals(tc, TEACHDIFFERENCE, sv->level); + CuAssertIntEquals(tc, (sv->level + 1) * SKILL_DAYS_PER_WEEK, sv->days); teach_cmd(u, u->thisorder); + CuAssertIntEquals(tc, TEACHDIFFERENCE, sv->level); + CuAssertIntEquals(tc, (sv->level + 1) * SKILL_DAYS_PER_WEEK - SKILL_DAYS_PER_WEEK / 30, sv->days); + + // u1 lernt in einer Akademie (+40 XP), mit Lehrer (+20): + sv = add_skill(u1, SK_CROSSBOW); study_cmd(u1, u1->thisorder); - study_cmd(u3, u3->thisorder); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK / 3, sv->days); - CuAssertIntEquals(tc, 4, log_size); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertPtrEquals(tc, u0, log_learners[0].u); - CuAssertIntEquals(tc, 10, log_learners[0].days); - CuAssertPtrEquals(tc, u, log_learners[1].u); - CuAssertIntEquals(tc, 1, log_learners[1].days); - CuAssertPtrEquals(tc, u1, log_learners[2].u); - CuAssertIntEquals(tc, 720, log_learners[2].days); - CuAssertPtrEquals(tc, u3, log_learners[3].u); - CuAssertIntEquals(tc, 160, log_learners[3].days); - learn_reset(); + sv = add_skill(u3, SK_CROSSBOW); + study_cmd(u3, u3->thisorder); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK / 3, sv->days); test_teardown(); } @@ -408,7 +410,6 @@ void test_learn_skill_single(CuTest *tc) { skill *sv; setup_study(); - config_set("study.random_progress", "0"); u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); CuAssertIntEquals(tc, 0, learn_skill(u, SK_ALCHEMY, SKILL_DAYS_PER_WEEK, 0)); CuAssertPtrNotNull(tc, sv = u->skills); @@ -428,7 +429,6 @@ void test_learn_skill_multi(CuTest *tc) { skill *sv; setup_study(); - config_set("study.random_progress", "0"); u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); scale_number(u, 10); CuAssertIntEquals(tc, 0, learn_skill(u, SK_ALCHEMY, SKILL_DAYS_PER_WEEK * u->number, 0)); @@ -530,19 +530,33 @@ static void test_demon_skillchange_hungry(CuTest *tc) { test_teardown(); } +static void test_produceexp(CuTest *tc) { + unit *u; + skill *sv; + + test_setup(); + + u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); + test_set_skill(u, SK_CROSSBOW, 1, 1); + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_CROSSBOW)); + produceexp(u, SK_CROSSBOW); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * 2 / 3, sv->days); + test_teardown(); +} + static void test_study_cmd(CuTest *tc) { unit *u; + skill *sv; setup_study(); init_resources(); u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); u->thisorder = create_order(K_STUDY, u->faction->locale, "CROSSBOW"); - learn_inject(); study_cmd(u, u->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, u, log_learners[0].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, log_learners[0].days); + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK, sv->days); test_teardown(); } @@ -606,6 +620,7 @@ static void test_study_cost_magic(CuTest *tc) { static void test_study_cost(CuTest *tc) { unit *u; const struct item_type *itype; + skill *sv; setup_study(); @@ -619,13 +634,11 @@ static void test_study_cost(CuTest *tc) { CuAssertIntEquals(tc, 50, study_cost(u, SK_ALCHEMY)); i_change(&u->items, itype, u->number * study_cost(u, SK_ALCHEMY)); - learn_inject(); study_cmd(u, u->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, u, log_learners[0].u); - CuAssertIntEquals(tc, SK_ALCHEMY, log_learners[0].sk); - CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * u->number, log_learners[0].days); CuAssertIntEquals(tc, 0, i_get(u->items, itype)); + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_ALCHEMY)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK, sv->days); test_teardown(); } @@ -633,6 +646,7 @@ static void test_teach_magic(CuTest *tc) { unit *u, *ut; faction *f; const struct item_type *itype; + skill *sv; setup_study(); init_resources(); @@ -646,20 +660,18 @@ static void test_teach_magic(CuTest *tc) { set_level(ut, SK_MAGIC, TEACHDIFFERENCE); create_mage(ut, M_GWYRRD); ut->thisorder = create_order(K_TEACH, f->locale, itoa36(u->no)); - learn_inject(); teach_cmd(ut, ut->thisorder); study_cmd(u, u->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, u, log_learners[0].u); - CuAssertIntEquals(tc, SK_MAGIC, log_learners[0].sk); - CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * 2, log_learners[0].days); CuAssertIntEquals(tc, 0, i_get(u->items, itype)); + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_MAGIC)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, sv->days); test_teardown(); } static void test_teach_cmd(CuTest *tc) { unit *u, *ut; - + setup_study(); init_resources(); u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); @@ -668,13 +680,11 @@ static void test_teach_cmd(CuTest *tc) { ut = test_create_unit(u->faction, u->region); set_level(ut, SK_CROSSBOW, TEACHDIFFERENCE); ut->thisorder = create_order(K_TEACH, u->faction->locale, itoa36(u->no)); - learn_inject(); + teach_cmd(ut, ut->thisorder); study_cmd(u, u->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, u, log_learners[0].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * 2 * u->number, log_learners[0].days); + CuAssertIntEquals(tc, 1, skill_level(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); test_teardown(); } @@ -700,7 +710,8 @@ static void test_teach_not_found(CuTest *tc) { static void test_teach_two(CuTest *tc) { unit *u1, *u2, *ut; - + skill *sv; + setup_study(); init_resources(); u1 = test_create_unit(test_create_faction(), test_create_plain(0, 0)); @@ -712,17 +723,17 @@ static void test_teach_two(CuTest *tc) { ut = test_create_unit(u1->faction, u1->region); set_level(ut, SK_CROSSBOW, TEACHDIFFERENCE); ut->thisorder = create_order(K_TEACH, ut->faction->locale, "%s %s", itoa36(u1->no), itoa36(u2->no)); - learn_inject(); teach_cmd(ut, ut->thisorder); study_cmd(u1, u1->thisorder); study_cmd(u2, u2->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, u1, log_learners[0].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * 2 * u1->number, log_learners[0].days); - CuAssertPtrEquals(tc, u2, log_learners[1].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[1].sk); - CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * 2 * u2->number, log_learners[1].days); + CuAssertPtrNotNull(tc, sv = unit_skill(u1, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, sv->days); + + CuAssertPtrNotNull(tc, sv = unit_skill(u2, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, sv->days); + test_teardown(); } @@ -730,6 +741,7 @@ static void test_teach_two_skills(CuTest *tc) { unit *u1, *u2, *ut; faction *f; region *r; + skill *sv; setup_study(); init_resources(); @@ -745,22 +757,22 @@ static void test_teach_two_skills(CuTest *tc) { set_level(ut, SK_ENTERTAINMENT, TEACHDIFFERENCE); set_level(ut, SK_CROSSBOW, TEACHDIFFERENCE); ut->thisorder = create_order(K_TEACH, f->locale, "%s %s", itoa36(u1->no), itoa36(u2->no)); - learn_inject(); teach_cmd(ut, ut->thisorder); study_cmd(u1, u1->thisorder); study_cmd(u2, u2->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, u1, log_learners[0].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * 2 * u1->number, log_learners[0].days); - CuAssertPtrEquals(tc, u2, log_learners[1].u); - CuAssertIntEquals(tc, SK_ENTERTAINMENT, log_learners[1].sk); - CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * 2 * u2->number, log_learners[1].days); + CuAssertPtrNotNull(tc, sv = unit_skill(u1, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, sv->days); + + CuAssertPtrNotNull(tc, sv = unit_skill(u2, SK_ENTERTAINMENT)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, sv->days); test_teardown(); } static void test_teach_one_to_many(CuTest *tc) { unit *u, *ut; + const skill *sv; setup_study(); init_resources(); @@ -770,18 +782,17 @@ static void test_teach_one_to_many(CuTest *tc) { ut = test_create_unit(u->faction, u->region); set_level(ut, SK_CROSSBOW, TEACHDIFFERENCE); ut->thisorder = create_order(K_TEACH, u->faction->locale, itoa36(u->no)); - learn_inject(); teach_cmd(ut, ut->thisorder); study_cmd(u, u->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, u, log_learners[0].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * 10 + SKILL_DAYS_PER_WEEK * u->number, log_learners[0].days); + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK / 2, sv->days); test_teardown(); } static void test_teach_many_to_one(CuTest *tc) { unit *u, *u1, *u2; + const skill *sv; setup_study(); init_resources(); @@ -794,14 +805,14 @@ static void test_teach_many_to_one(CuTest *tc) { u2 = test_create_unit(u->faction, u->region); set_level(u2, SK_CROSSBOW, TEACHDIFFERENCE); u2->thisorder = create_order(K_TEACH, u->faction->locale, itoa36(u->no)); - learn_inject(); + teach_cmd(u1, u1->thisorder); teach_cmd(u2, u2->thisorder); study_cmd(u, u->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, u, log_learners[0].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK * u->number, log_learners[0].days); + + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, sv->days); test_teardown(); } @@ -867,24 +878,24 @@ static void test_teach_many_to_many(CuTest *tc) { scale_number(t2, 2); set_level(t2, SK_CROSSBOW, TEACHDIFFERENCE); t2->thisorder = create_order(K_TEACH, f->locale, "%s %s", itoa36(s1->no), itoa36(s2->no)); - learn_inject(); + teach_cmd(t1, t1->thisorder); teach_cmd(t2, t2->thisorder); study_cmd(s1, s1->thisorder); study_cmd(s2, s2->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, s1, log_learners[0].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK * s1->number, log_learners[0].days); - CuAssertPtrEquals(tc, s2, log_learners[1].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[1].sk); - CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK * s2->number, log_learners[1].days); + + CuAssertIntEquals(tc, 1, skill_level(s1, SK_CROSSBOW)); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, skill_days(s1, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, skill_level(s2, SK_CROSSBOW)); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, skill_days(s2, SK_CROSSBOW)); + test_teardown(); } CuSuite *get_study_suite(void) { CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_produceexp); SUITE_ADD_TEST(suite, test_study_cmd); SUITE_ADD_TEST(suite, test_study_cost); SUITE_ADD_TEST(suite, test_study_cost_magic); diff --git a/src/summary.c b/src/summary.c index b39d0df79..a98f74a5e 100644 --- a/src/summary.c +++ b/src/summary.c @@ -248,7 +248,6 @@ static int cmp_nmr_faction(const void *a, const void *b) static void report_nmrs(FILE *F, int timeout) { nmr_faction *nmrs = NULL; - size_t len, i; faction *f; for (f = factions; f; f = f->next) { if (turn - 1 - f->lastorders > 0) { @@ -257,13 +256,16 @@ static void report_nmrs(FILE *F, int timeout) nmr->nmr = turn - 1 - f->lastorders; } } - len = arrlen(nmrs); - qsort(nmrs, len, sizeof(struct nmr_faction), cmp_nmr_faction); - fprintf(F, "\n\nFactions with NMRs:\n"); - for (i = 0; i != len; ++i) { - out_faction(F, nmrs[i].f); + if (nmrs) { + size_t len, i; + len = arrlen(nmrs); + qsort(nmrs, len, sizeof(struct nmr_faction), cmp_nmr_faction); + fprintf(F, "\n\nFactions with NMRs:\n"); + for (i = 0; i != len; ++i) { + out_faction(F, nmrs[i].f); + } + arrfree(nmrs); } - arrfree(nmrs); } void report_summary(const summary * s, bool full) diff --git a/src/tests.c b/src/tests.c index fe613943a..0ba65a832 100644 --- a/src/tests.c +++ b/src/tests.c @@ -270,6 +270,7 @@ void test_reset(void) if (month_season == test_months) { month_season = NULL; } + random_source_reset(); free_gamedata(); } diff --git a/src/triggers/shock.c b/src/triggers/shock.c index 3af2122b7..b41bf29e4 100644 --- a/src/triggers/shock.c +++ b/src/triggers/shock.c @@ -64,7 +64,7 @@ static void do_shock(unit * u, const char *reason) skill* sv = u->skills + s; int weeks = (sv->level * sv->level - sv->level) / 2; int change = (weeks + 9) / 10; - reduce_skill_weeks(u, sv, change); + change_skill(u, sv, -SKILL_DAYS_PER_WEEK * change); } } /* Dies ist ein Hack, um das skillmod und familiar-Attribut beim Mage diff --git a/src/util/rand.c b/src/util/rand.c index cac84bc2a..8a70566fe 100644 --- a/src/util/rand.c +++ b/src/util/rand.c @@ -13,7 +13,7 @@ int lovar(double xpct_x2) int n = (int)(xpct_x2 * 500) + 1; if (n == 0) return 0; - return (rng_int() % n + rng_int() % n) / 1000; + return (rng_uint() % n + rng_uint() % n) / 1000; } /* gaussian distribution