diff --git a/clibs b/clibs index 66a891b38..d86c85254 160000 --- a/clibs +++ b/clibs @@ -1 +1 @@ -Subproject commit 66a891b383f1a51bb0d4e5cf002530f7f70bf7f4 +Subproject commit d86c8525489d7f11b7ba13e101bb59ecf160b871 diff --git a/res/translations/strings.de.po b/res/translations/strings.de.po index 1731fa10e..f4b166221 100644 --- a/res/translations/strings.de.po +++ b/res/translations/strings.de.po @@ -2864,6 +2864,9 @@ msgstr "der Schatten" msgid "ALLES" msgstr "ALLES" +msgid "AUTO" +msgstr "AUTO" + msgid "undead_postfix_2" msgstr "der Finsternis" @@ -5958,6 +5961,10 @@ msgctxt "keyword" msgid "maketemp" msgstr "MACHE TEMP" +msgctxt "keyword" +msgid "autostudy" +msgstr "LERNE AUTO" + msgctxt "spell" msgid "reanimate" msgstr "Wiederbelebung" diff --git a/res/translations/strings.en.po b/res/translations/strings.en.po index 0d64e9f99..32924310c 100644 --- a/res/translations/strings.en.po +++ b/res/translations/strings.en.po @@ -2510,6 +2510,9 @@ msgstr "halfling foot" msgid "ALLES" msgstr "ALL" +msgid "AUTO" +msgstr "AUTO" + msgctxt "race" msgid "songdragon_d" msgstr "song dragons" @@ -5268,7 +5271,11 @@ msgstr "berserkers blood potions" msgctxt "keyword" msgid "maketemp" -msgstr "MAKETEMP" +msgstr "MAKE TEMP" + +msgctxt "keyword" +msgid "autostudy" +msgstr "LEARN AUTO" msgctxt "spell" msgid "reanimate" diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua index bf90ab6a0..54033d496 100644 --- a/scripts/tests/e2/e2features.lua +++ b/scripts/tests/e2/e2features.lua @@ -12,6 +12,30 @@ function setup() eressea.settings.set("rules.peasants.growth.factor", "0") end +function test_study_auto() + local r = region.create(0, 0, "plain") + local f = faction.create("human") + local u = unit.create(f, r, 1) + u:add_order("LERN AUT Waffenbau") + assert_equal("LERNE AUTO Waffenbau", u:get_order(0)) + process_orders() + assert_equal(1, u:get_skill("weaponsmithing")) +end + +function test_study_auto_expensive() + local r = region.create(0, 0, "plain") + local f = faction.create("human") + local u = unit.create(f, r, 1) + u:add_order("LERNE AUTO Magie") + assert_equal("LERNE Magie", u:get_order(0)) + u:clear_orders() + u:add_order("LERN AUT Taktik") + assert_equal("LERNE Taktik", u:get_order(0)) + u:clear_orders() + u:add_order("LERN AUT Waffenbau") + assert_equal("LERNE AUTO Waffenbau", u:get_order(0)) +end + function test_calendar() assert_equal("winter", get_season(1011)) assert_equal("spring", get_season(1012)) diff --git a/scripts/tests/e3/buildings.lua b/scripts/tests/e3/buildings.lua index e642c1aa7..9899d138e 100644 --- a/scripts/tests/e3/buildings.lua +++ b/scripts/tests/e3/buildings.lua @@ -42,7 +42,8 @@ function test_build_watch() process_orders() assert_not_nil(u.building) if 5 ~= u.building.size then - for k,v in f.messages do + -- debug logging to find intermittent errors + for k,v in ipairs(f.messages) do print(v) end end diff --git a/scripts/tests/study.lua b/scripts/tests/study.lua index 14d4ce1d6..32638ba90 100644 --- a/scripts/tests/study.lua +++ b/scripts/tests/study.lua @@ -24,7 +24,7 @@ end function test_study() local r = region.create(0, 0, "plain") - local f = faction.create("human", "test@example.com", "de") + local f = faction.create("human") local u = unit.create(f, r, 1) u:add_order("LERNEN Armbrust") process_orders() @@ -33,7 +33,7 @@ end function test_study_expensive() local r = region.create(0, 0, "plain") - local f = faction.create("human", "test@example.com", "de") + local f = faction.create("human") local u = unit.create(f, r, 1) eressea.settings.set("skills.cost.alchemy", "50") u:add_order("LERNEN Alchemie") @@ -45,7 +45,7 @@ end function test_unit_spells() local r = region.create(0, 0, "plain") - local f = faction.create("human", "test@example.com", "de") + local f = faction.create("human") local u = unit.create(f, r, 1) u.magic = "gray" u:set_skill("magic", 1) @@ -75,7 +75,7 @@ end function test_study_no_teacher() local r = region.create(0, 0, "plain") - local f = faction.create("human", "test@example.com", "de") + local f = faction.create("human") local u1 = make_student(f, r, 1) u1:set_skill("crossbow", 1) process_orders() @@ -84,7 +84,7 @@ end function test_study_with_teacher() local r = region.create(0, 0, "plain") - local f = faction.create("human", "test@example.com", "de") + local f = faction.create("human") local u1 = make_student(f, r, 1) make_teacher(u1) @@ -95,7 +95,7 @@ end function test_study_too_many_students() local r = region.create(0, 0, "plain") - local f = faction.create("human", "test@example.com", "de") + local f = faction.create("human") local u1 = make_student(f, r, 20, "Taktik") u1.name = "Student" u1:add_item("money", 201*u1.number) @@ -106,7 +106,7 @@ end function test_study_multiple_teachers() local r = region.create(0, 0, "plain") - local f = faction.create("human", "test@example.com", "de") + local f = faction.create("human") local u1 = make_student(f, r, 20, "Taktik") u1.name = "Student" u1:add_item("money", 201*u1.number) diff --git a/src/automate.c b/src/automate.c index a74cf770b..6af40d649 100644 --- a/src/automate.c +++ b/src/automate.c @@ -38,14 +38,14 @@ int autostudy_init(scholar scholars[], int max_scholars, region *r) if (kwd == K_AUTOSTUDY) { if (long_order_allowed(u) && unit_can_study(u)) { scholar * st = scholars + nscholars; - if (++nscholars == max_scholars) { - log_fatal("you must increase MAXSCHOLARS"); - } - st->u = u; init_order(u->thisorder, u->faction->locale); st->sk = getskill(u->faction->locale); st->level = effskill_study(u, st->sk); st->learn = 0; + st->u = u; + if (++nscholars == max_scholars) { + log_fatal("you must increase MAXSCHOLARS"); + } } else { ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder, "error_race_nolearn", "race", diff --git a/src/kernel/order.c b/src/kernel/order.c index 342f45577..c14a2b30b 100644 --- a/src/kernel/order.c +++ b/src/kernel/order.c @@ -141,7 +141,8 @@ int stream_order(struct stream *out, const struct order *ord, const struct local if (ord->id < 0) { skill_t sk = (skill_t)(100 + ord->id); - assert(kwd == K_STUDY && sk != SK_MAGIC && sk < MAXSKILLS); + assert(kwd == K_AUTOSTUDY || kwd == K_STUDY); + assert(sk != SK_MAGIC && sk < MAXSKILLS); text = skillname(sk, lang); if (strchr(text, ' ') != NULL) { swrite(" '", 1, 2, out); @@ -310,12 +311,13 @@ order *parse_order(const char *s, const struct locale * lang) assert(lang); assert(s); if (*s != 0) { + char token[32]; keyword_t kwd = NOKEYWORD; const char *sptr = s; bool persistent = false, noerror = false; - const char * p; + char * p; - p = *sptr ? parse_token_depr(&sptr) : 0; + p = parse_token(&sptr, token, sizeof(token)); if (p) { while (*p == '!' || *p == '@') { if (*p == '!') noerror = true; @@ -326,7 +328,7 @@ order *parse_order(const char *s, const struct locale * lang) } if (kwd == K_MAKE) { const char *sp = sptr; - p = parse_token_depr(&sp); + p = parse_token(&sp, token, sizeof(token)); if (p && isparam(p, lang, P_TEMP)) { kwd = K_MAKETEMP; sptr = sp; @@ -334,10 +336,15 @@ order *parse_order(const char *s, const struct locale * lang) } else if (kwd == K_STUDY) { const char *sp = sptr; - p = parse_token_depr(&sp); + p = parse_token(&sp, token, sizeof(token)); if (p && isparam(p, lang, P_AUTO)) { - kwd = K_AUTOSTUDY; + skill_t sk; sptr = sp; + p = parse_token(&sp, token, sizeof(token)); + sk = get_skill(p, lang); + if (!expensive_skill(sk)) { + kwd = K_AUTOSTUDY; + } } } if (kwd != NOKEYWORD) { diff --git a/src/kernel/order.test.c b/src/kernel/order.test.c index d1f8b4176..c4a55177b 100644 --- a/src/kernel/order.test.c +++ b/src/kernel/order.test.c @@ -121,6 +121,8 @@ static void test_parse_autostudy(CuTest *tc) { test_setup(); lang = get_or_create_locale("en"); locale_setstring(lang, mkname("skill", skillnames[SK_ENTERTAINMENT]), "Entertainment"); + locale_setstring(lang, mkname("skill", skillnames[SK_MAGIC]), "Magic"); + locale_setstring(lang, mkname("skill", skillnames[SK_TACTICS]), "Tactics"); locale_setstring(lang, keyword(K_STUDY), "STUDY"); locale_setstring(lang, keyword(K_AUTOSTUDY), "AUTOSTUDY"); locale_setstring(lang, parameters[P_AUTO], "AUTO"); @@ -134,6 +136,17 @@ static void test_parse_autostudy(CuTest *tc) { CuAssertIntEquals(tc, K_AUTOSTUDY, init_order(ord, lang)); CuAssertStrEquals(tc, "Entertainment", getstrtoken()); free_order(ord); + + ord = parse_order("STUDY AUTO Magic", lang); + CuAssertIntEquals(tc, K_STUDY, getkeyword(ord)); + CuAssertStrEquals(tc, "STUDY Magic", get_command(ord, lang, cmd, sizeof(cmd))); + free_order(ord); + + ord = parse_order("STUDY AUTO Tactics", lang); + CuAssertIntEquals(tc, K_STUDY, getkeyword(ord)); + CuAssertStrEquals(tc, "STUDY Tactics", get_command(ord, lang, cmd, sizeof(cmd))); + free_order(ord); + test_teardown(); } diff --git a/src/skill.c b/src/skill.c index 3d5b77a96..b279ab488 100644 --- a/src/skill.c +++ b/src/skill.c @@ -113,3 +113,38 @@ skill_t get_skill(const char *s, const struct locale * lang) return result; } +int skill_cost(skill_t sk) { + static int config; + static int costs[MAXSKILLS]; + int cost; + switch (sk) { + case SK_SPY: + cost = 100; + break; + case SK_TACTICS: + case SK_HERBALISM: + case SK_ALCHEMY: + cost = 200; + break; + default: + cost = -1; + } + + if (config_changed(&config)) { + memset(costs, 0, sizeof(costs)); + } + + if (costs[sk] == 0) { + char buffer[256]; + sprintf(buffer, "skills.cost.%s", skillnames[sk]); + costs[sk] = config_get_int(buffer, cost); + } + if (costs[sk] >= 0) { + return costs[sk]; + } + return (cost > 0) ? cost : 0; +} + +bool expensive_skill(skill_t sk) { + return (sk == SK_MAGIC) || skill_cost(sk) > 0; +} diff --git a/src/skill.h b/src/skill.h index 3c88be725..ad6c7c2cb 100644 --- a/src/skill.h +++ b/src/skill.h @@ -49,5 +49,7 @@ void init_skills(const struct locale *lang); void init_skill(const struct locale *lang, skill_t kwd, const char *str); void enable_skill(skill_t sk, bool enabled); bool skill_enabled(skill_t sk); +int skill_cost(skill_t sk); +bool expensive_skill(skill_t sk); #endif diff --git a/src/skill.test.c b/src/skill.test.c index abfe529f9..09e9c8fe9 100644 --- a/src/skill.test.c +++ b/src/skill.test.c @@ -1,6 +1,8 @@ #include -#include "skill.h" + +#include "kernel/config.h" #include "util/language.h" +#include "skill.h" #include "tests.h" #include @@ -38,12 +40,34 @@ static void test_get_skill(CuTest *tc) { test_teardown(); } +static void test_skill_cost(CuTest *tc) { + test_setup(); + CuAssertTrue(tc, expensive_skill(SK_MAGIC)); + CuAssertTrue(tc, expensive_skill(SK_TACTICS)); + CuAssertTrue(tc, expensive_skill(SK_SPY)); + CuAssertTrue(tc, expensive_skill(SK_ALCHEMY)); + CuAssertTrue(tc, expensive_skill(SK_HERBALISM)); + CuAssertTrue(tc, !expensive_skill(SK_CROSSBOW)); + + CuAssertIntEquals(tc, 100, skill_cost(SK_SPY)); + CuAssertIntEquals(tc, 200, skill_cost(SK_TACTICS)); + CuAssertIntEquals(tc, 200, skill_cost(SK_ALCHEMY)); + CuAssertIntEquals(tc, 200, skill_cost(SK_HERBALISM)); + CuAssertIntEquals(tc, 0, skill_cost(SK_CROSSBOW)); + + config_set_int("skills.cost.crossbow", 300); + CuAssertIntEquals(tc, 300, skill_cost(SK_CROSSBOW)); + CuAssertTrue(tc, expensive_skill(SK_CROSSBOW)); + test_teardown(); +} + CuSuite *get_skill_suite(void) { CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_init_skill); SUITE_ADD_TEST(suite, test_init_skills); SUITE_ADD_TEST(suite, test_get_skill); + SUITE_ADD_TEST(suite, test_skill_cost); return suite; } diff --git a/src/study.c b/src/study.c index bd29fc2a8..c978e69ad 100644 --- a/src/study.c +++ b/src/study.c @@ -130,47 +130,23 @@ bool magic_lowskill(unit * u) return u_race(u) == toad_rc; } -/* ------------------------------------------------------------- */ - int study_cost(struct unit *u, skill_t sk) { - static int config; - static int costs[MAXSKILLS]; - int cost = -1; - if (sk == SK_MAGIC) { - int next_level = 1 + (u ? get_level(u, sk) : 0); + static int config; + static int cost; /* Die Magiekosten betragen 50+Summe(50*Stufe) */ /* 'Stufe' ist dabei die naechste zu erreichende Stufe */ - cost = config_get_int("skills.cost.magic", 50); - return cost * (1 + ((next_level + next_level * next_level) / 2)); + if (config_changed(&config)) { + cost = config_get_int("skills.cost.magic", 50); + } + if (cost > 0) { + int next_level = 1 + (u ? get_level(u, sk) : 0); + return cost * (1 + ((next_level + next_level * next_level) / 2)); + } + return cost; } - else switch (sk) { - case SK_SPY: - cost = 100; - break; - case SK_TACTICS: - case SK_HERBALISM: - case SK_ALCHEMY: - cost = 200; - break; - default: - cost = -1; - } - - if (config_changed(&config)) { - memset(costs, 0, sizeof(costs)); - } - - if (costs[sk] == 0) { - char buffer[256]; - sprintf(buffer, "skills.cost.%s", skillnames[sk]); - costs[sk] = config_get_int(buffer, cost); - } - if (costs[sk] >= 0) { - return costs[sk]; - } - return (cost > 0) ? cost : 0; + return skill_cost(sk); } /* ------------------------------------------------------------- */ diff --git a/src/study.h b/src/study.h index e99fb4808..cca0428b7 100644 --- a/src/study.h +++ b/src/study.h @@ -29,26 +29,9 @@ extern "C" { struct unit; struct selist; - int teach_cmd(struct unit *u, struct order *ord); - int study_cmd(struct unit *u, struct order *ord); - - magic_t getmagicskill(const struct locale *lang); - skill_t getskill(const struct locale *lang); - bool is_migrant(struct unit *u); - int study_cost(struct unit *u, skill_t talent); - - typedef void(*learn_fun)(struct unit *u, skill_t sk, int days); - #define STUDYDAYS 30 - void learn_skill(struct unit *u, skill_t sk, int days); - void reduce_skill_days(struct unit *u, skill_t sk, int days); - - void produceexp(struct unit *u, skill_t sk, int n); - void produceexp_ex(struct unit *u, skill_t sk, int n, learn_fun learn); - - void demon_skillchange(struct unit *u); - #define TEACHNUMBER 10 + typedef struct teaching_info { struct selist *teachers; int students; @@ -57,6 +40,24 @@ extern "C" { extern const struct attrib_type at_learning; + int teach_cmd(struct unit *u, struct order *ord); + int study_cmd(struct unit *u, struct order *ord); + + magic_t getmagicskill(const struct locale *lang); + skill_t getskill(const struct locale *lang); + bool is_migrant(struct unit *u); + int study_cost(struct unit *u, skill_t sk); + + typedef void(*learn_fun)(struct unit *u, skill_t sk, int days); + + void learn_skill(struct unit *u, skill_t sk, int days); + void reduce_skill_days(struct unit *u, skill_t sk, int days); + + void produceexp(struct unit *u, skill_t sk, int n); + void produceexp_ex(struct unit *u, skill_t sk, int n, learn_fun learn); + + void demon_skillchange(struct unit *u); + void inject_learn(learn_fun fun); #ifdef __cplusplus