Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion res/e3a/races.xml
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,7 @@
<skill name="building" modifier="-99"/>
<skill name="forestry" modifier="-99"/>
<skill name="catapult" modifier="-99"/>
<skill name="magic" modifier="-99"/>
<skill name="magic" modifier="-99"/>
<skill name="training" modifier="-99"/>
<skill name="riding" modifier="-99"/>
<skill name="armorer" modifier="-99"/>
Expand Down
3 changes: 2 additions & 1 deletion src/battle.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 */
Expand Down
1 change: 1 addition & 0 deletions src/battle.test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/crimport.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion src/exparse.c
Original file line number Diff line number Diff line change
Expand Up @@ -1090,7 +1090,7 @@ 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;
rc->bonus[sk] = (signed char)mod;
if (speed != INT_MAX) {
set_study_speed(rc, sk, speed);
}
Expand Down
2 changes: 1 addition & 1 deletion src/kernel/race.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions src/kernel/region.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/kernel/save.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
83 changes: 56 additions & 27 deletions src/kernel/skills.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "unit.h"

#include <kernel/attrib.h>
#include <kernel/race.h>
#include <kernel/skills.h>

#include <util/goodies.h>
Expand Down Expand Up @@ -93,49 +94,71 @@ static int progress_weeks(unsigned int level)

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)
void sk_set_level(const struct unit *u, skill *sv, int level)
{
int weeks = rule_random_progress() ? progress_weeks(level + 1) : (level + 1);
skill_set(sv, level, weeks * SKILL_DAYS_PER_WEEK);
const struct race *rc = u ? u_race(u) : NULL;
int speed = rc ? study_speed(rc, sv->id) : SKILL_DAYS_PER_WEEK;
int days = SKILL_DAYS_PER_WEEK + (weeks - 1) * speed;
ASSERT_VALID_SKILL(sv, rc);
skill_set(sv, level, days);
}

void increase_skill_weeks(unit * u, enum skill_t sk, const unsigned int weeks)
{
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);
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) {
unsigned int max_days = MAX_DAYS_TO_NEXT_LEVEL(sv->level);
while (sv->days + days > max_days) {
// maximum number of days before we must step down a level:
unsigned int days_lost = max_days - sv->days;
// subtract those days of un-learning, step down a level:
days -= days_lost;
sk_set_level(u, sv, sv->level - 1);
max_days = MAX_DAYS_TO_NEXT_LEVEL(sv->level);
}
sv->days += days;
}
}

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);
}
if (sv->level == 0) {
/* reroll */
sk_set_level(sv, sv->level + 1);
else {
increase_skill_days(u, sv, days);
}
}

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)
Expand Down Expand Up @@ -232,3 +255,9 @@ int skill_days(unit *u, enum skill_t sk)
const skill *sv = unit_skill(u, sk);
return sv ? sv->days : 1;
}

int study_speed(const struct race *rc, skill_t sk)
{
int mod = (rc && rc->study_speed) ? rc->study_speed[sk] : 0;
return SKILL_DAYS_PER_WEEK - mod;
}
12 changes: 8 additions & 4 deletions src/kernel/skills.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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, 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))
53 changes: 31 additions & 22 deletions src/kernel/skills.test.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ 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);
Expand All @@ -31,54 +31,63 @@ 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);
set_number(u, 2); // number should have no effect on skill values
change_skill_days(u, SK_CROSSBOW, SKILL_DAYS_PER_WEEK);
sv = unit_skill(u, SK_CROSSBOW);
CuAssertIntEquals(tc, 1, skill_level(u, SK_CROSSBOW));
CuAssertIntEquals(tc, 1, sv->level);
CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW));
/* no random progress, so it will take 2 weeks of learning to next level: */
CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK, sv->days);
increase_skill_weeks(u, SK_CROSSBOW, 1);
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, 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);
CuAssertIntEquals(tc, 1 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW));
change_skill_days(u, SK_CROSSBOW, SKILL_DAYS_PER_WEEK);
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);
CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW));
change_skill_days(u, SK_CROSSBOW, 4 * SKILL_DAYS_PER_WEEK);
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);
CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW));

reduce_skill_weeks(u, sv, 1);
change_skill_days(u, SK_CROSSBOW, -SKILL_DAYS_PER_WEEK);
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);
CuAssertIntEquals(tc, 4 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW));
change_skill_days(u, SK_CROSSBOW, -SKILL_DAYS_PER_WEEK);
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_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);
CuAssertIntEquals(tc, 7 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW));
change_skill_days(u, SK_CROSSBOW, -SKILL_DAYS_PER_WEEK);
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, 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);
// to reach level 11: 21 weeks = 630 days
// of which only 4 weeks left
// => we can lose 17 weeks without losing a level
// losing 8 more, though!
// so the level drop sets us to expected time to get to level 10 (10 weeks)
// plus those 8 should make 18 weeks
// 18 weeks is less or equal to what level 10 needs (19)
change_skill_days(u, SK_CROSSBOW, -SKILL_DAYS_PER_WEEK * 25);
CuAssertIntEquals(tc, 9, skill_level(u, SK_CROSSBOW));
CuAssertIntEquals(tc, 9, sv->level);
CuAssertIntEquals(tc, 18 * SKILL_DAYS_PER_WEEK, sv->days);
CuAssertIntEquals(tc, 18 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW));
}

static void test_set_level(CuTest * tc)
Expand Down
6 changes: 3 additions & 3 deletions src/kernel/unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/laws.c
Original file line number Diff line number Diff line change
Expand Up @@ -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... */
Expand Down
2 changes: 1 addition & 1 deletion src/spells.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Loading
Loading