From 26e06804d677fe77e94ede1174d61652dc48fe13 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 5 Jul 2018 20:06:32 +0200 Subject: [PATCH 01/30] autostudy framework --- src/CMakeLists.txt | 1 + src/automate.c | 71 +++++++++++++++++++++++++++++++++++++++++++++ src/automate.h | 28 ++++++++++++++++++ src/kernel/config.c | 3 +- src/kernel/order.c | 19 +++++++++--- src/kernel/types.h | 1 + src/kernel/unit.c | 5 ++-- src/kernel/unit.h | 2 +- src/keyword.c | 1 + src/keyword.h | 1 + src/laws.c | 2 ++ src/study.c | 8 ++--- src/study.h | 1 + 13 files changed, 130 insertions(+), 13 deletions(-) create mode 100644 src/automate.c create mode 100644 src/automate.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5b267c0d5..0ff92bf2d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -86,6 +86,7 @@ ENDIF() set (ERESSEA_SRC vortex.c + automate.c move.c piracy.c spells.c diff --git a/src/automate.c b/src/automate.c new file mode 100644 index 000000000..0855ff0e3 --- /dev/null +++ b/src/automate.c @@ -0,0 +1,71 @@ +#include + +#include "kernel/faction.h" +#include "kernel/order.h" +#include "kernel/region.h" +#include "kernel/unit.h" + +#include "util/log.h" + +#include "automate.h" +#include "keyword.h" +#include "study.h" + +#include + +typedef struct student { + unit *u; + skill_t sk; + int level; +} student; + +#define MAXSTUDENTS 128 + +int cmp_students(const void *lhs, const void *rhs) { + const student *a = (const student *)lhs; + const student *b = (const student *)rhs; + if (a->sk == b->sk) { + /* sort by level, descending: */ + return b->level - a->level; + } + /* order by skill */ + return (int)a->sk - (int)b->sk; +} + +void do_autostudy(region *r) { + unit *u; + int nstudents = 0; + student students[MAXSTUDENTS]; + + for (u = r->units; u; u = u->next) { + keyword_t kwd = getkeyword(u->thisorder); + if (kwd == K_AUTOSTUDY) { + student * st = students + nstudents; + if (++nstudents == MAXSTUDENTS) { + log_fatal("you must increase MAXSTUDENTS"); + } + st->u = u; + init_order(u->thisorder, u->faction->locale); + st->sk = getskill(u->faction->locale); + st->level = effskill_study(u, st->sk); + } + } + if (nstudents > 0) { + int i, taught = 0; + skill_t sk = NOSKILL; + student *teacher = NULL, *student = NULL; + + qsort(students, nstudents, sizeof(student), cmp_students); + for (i = 0; i != nstudents; ++i) { + if (students[i].u) { + if (sk == NOSKILL) { + sk = students[i].sk; + } + else if (sk != students[i].sk) { + continue; + } + students[i].u = NULL; + } + } + } +} diff --git a/src/automate.h b/src/automate.h new file mode 100644 index 000000000..ded8f938b --- /dev/null +++ b/src/automate.h @@ -0,0 +1,28 @@ +/* +Copyright (c) 1998-2018, Enno Rehling +Katja Zedel + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +**/ + +#pragma once + +#ifndef H_GC_AUTOMATE +#define H_GC_AUTOMATE + +struct region; + +void do_autostudy(struct region *r); + +#endif diff --git a/src/kernel/config.c b/src/kernel/config.c index ec45ddd0d..750d406f6 100644 --- a/src/kernel/config.c +++ b/src/kernel/config.c @@ -135,7 +135,8 @@ const char *parameters[MAXPARAMS] = { "GRUPPE", "PARTEITARNUNG", "BAEUME", - "ALLIANZ" + "ALLIANZ", + "AUTO" }; int findoption(const char *s, const struct locale *lang) diff --git a/src/kernel/order.c b/src/kernel/order.c index e660edf81..ac2573758 100644 --- a/src/kernel/order.c +++ b/src/kernel/order.c @@ -214,12 +214,12 @@ static int create_data(keyword_t kwd, const char *s, order_data *data; int id; - assert(kwd!=NOKEYWORD); + assert(kwd != NOKEYWORD); if (!s || *s == 0) { return 0; } - if (kwd==K_STUDY) { + if (kwd == K_STUDY || kwd == K_AUTOSTUDY) { const char * sptr = s; skill_t sk = get_skill(parse_token_depr(&sptr), lang); if (sk != SK_MAGIC && sk != NOSKILL) { @@ -332,6 +332,14 @@ order *parse_order(const char *s, const struct locale * lang) sptr = sp; } } + else if (kwd == K_STUDY) { + const char *sp = sptr; + p = parse_token_depr(&sp); + if (p && isparam(p, lang, P_AUTO)) { + kwd = K_AUTOSTUDY; + sptr = sp; + } + } if (kwd != NOKEYWORD) { order *ord = (order *)malloc(sizeof(order)); create_order_i(ord, kwd, sptr, persistent, noerror, lang); @@ -366,6 +374,7 @@ bool is_repeated(keyword_t kwd) case K_STEAL: case K_SABOTAGE: case K_STUDY: + case K_AUTOSTUDY: case K_TEACH: case K_GROW: case K_PLANT: @@ -406,6 +415,7 @@ bool is_exclusive(const order * ord) case K_STEAL: case K_SABOTAGE: case K_STUDY: + case K_AUTOSTUDY: case K_TEACH: case K_GROW: case K_PLANT: @@ -447,6 +457,7 @@ bool is_long(keyword_t kwd) case K_STEAL: case K_SABOTAGE: case K_STUDY: + case K_AUTOSTUDY: case K_TEACH: case K_GROW: case K_PLANT: @@ -541,7 +552,7 @@ keyword_t init_order(const struct order *ord, const struct locale *lang) assert(sk < MAXSKILLS); assert(lang); - assert(kwd == K_STUDY); + assert(kwd == K_STUDY || kwd == K_AUTOSTUDY); str = skillname(sk, lang); if (strchr(str, ' ') == NULL) { init_tokens_str(str); @@ -575,7 +586,7 @@ keyword_t init_order_depr(const struct order *ord) { if (ord) { keyword_t kwd = ORD_KEYWORD(ord); - assert(kwd != K_STUDY); + assert(kwd != K_STUDY && kwd != K_AUTOSTUDY); } return init_order(ord, NULL); } diff --git a/src/kernel/types.h b/src/kernel/types.h index 61f1268cc..48a4326fa 100644 --- a/src/kernel/types.h +++ b/src/kernel/types.h @@ -130,6 +130,7 @@ typedef enum { P_FACTIONSTEALTH, P_TREES, P_ALLIANCE, + P_AUTO, MAXPARAMS, NOPARAM } param_t; diff --git a/src/kernel/unit.c b/src/kernel/unit.c index 07c6c0856..770723bea 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -1310,13 +1310,12 @@ int eff_skill(const unit * u, const skill *sv, const region *r) return 0; } -int effskill_study(const unit * u, skill_t sk, const region * r) +int effskill_study(const unit * u, skill_t sk) { skill *sv = unit_skill(u, sk); if (sv && sv->level > 0) { int mlevel = sv->level; - if (!r) r = u->region; - mlevel += get_modifier(u, sv->id, sv->level, r, true); + mlevel += get_modifier(u, sv->id, sv->level, u->region, true); if (mlevel > 0) return mlevel; } diff --git a/src/kernel/unit.h b/src/kernel/unit.h index ac61affde..a6135715c 100644 --- a/src/kernel/unit.h +++ b/src/kernel/unit.h @@ -163,7 +163,7 @@ extern "C" { void clone_men(const struct unit *src, struct unit *dst, int n); /* like transfer, but do not subtract from src */ int eff_skill(const struct unit *u, const struct skill *sv, const struct region *r); - int effskill_study(const struct unit *u, skill_t sk, const struct region *r); + int effskill_study(const struct unit *u, skill_t sk); int get_modifier(const struct unit *u, skill_t sk, int level, const struct region *r, bool noitem); diff --git a/src/keyword.c b/src/keyword.c index 7519b6449..48726bcbc 100644 --- a/src/keyword.c +++ b/src/keyword.c @@ -149,5 +149,6 @@ const char *keywords[MAXKEYWORDS] = { "promote", "pay", "loot", + "autostudy", }; diff --git a/src/keyword.h b/src/keyword.h index a9273c3f5..6e4832708 100644 --- a/src/keyword.h +++ b/src/keyword.h @@ -72,6 +72,7 @@ extern "C" K_PROMOTION, K_PAY, K_LOOT, + K_AUTOSTUDY, MAXKEYWORDS, NOKEYWORD } keyword_t; diff --git a/src/laws.c b/src/laws.c index aa6e237e2..3902e9a8e 100644 --- a/src/laws.c +++ b/src/laws.c @@ -26,6 +26,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include "alchemy.h" +#include "automate.h" #include "battle.h" #include "economy.h" #include "keyword.h" @@ -4011,6 +4012,7 @@ void init_processor(void) } p += 10; + add_proc_region(p, do_autostudy, "study automation"); add_proc_order(p, K_TEACH, teach_cmd, PROC_THISORDER | PROC_LONGORDER, "Lehren"); p += 10; diff --git a/src/study.c b/src/study.c index 46da90b74..6df5b5eaf 100644 --- a/src/study.c +++ b/src/study.c @@ -69,7 +69,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #define TEACH_ALL 1 #define TEACH_FRIENDS -static skill_t getskill(const struct locale *lang) +skill_t getskill(const struct locale *lang) { char token[128]; const char * s = gettoken(token, sizeof(token)); @@ -331,7 +331,7 @@ int teach_cmd(unit * teacher, struct order *ord) sk = teachskill[t]; } if (sk != NOSKILL - && effskill_study(teacher, sk, 0) - TEACHDIFFERENCE > effskill_study(student, sk, 0)) { + && effskill_study(teacher, sk) - TEACHDIFFERENCE > effskill_study(student, sk)) { teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); } } @@ -343,7 +343,7 @@ int teach_cmd(unit * teacher, struct order *ord) init_order(student->thisorder, student->faction->locale); sk = getskill(student->faction->locale); if (sk != NOSKILL - && effskill_study(teacher, sk, 0) - TEACHDIFFERENCE >= effskill(student, sk, 0)) { + && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(student, sk)) { teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); } } @@ -432,7 +432,7 @@ int teach_cmd(unit * teacher, struct order *ord) continue; } - if (effskill_study(student, sk, 0) > effskill_study(teacher, sk, 0) + if (effskill_study(student, sk, NULL) > effskill_study(teacher, sk) - TEACHDIFFERENCE) { if (feedback) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "teach_asgood", diff --git a/src/study.h b/src/study.h index b4b76bb8c..e99fb4808 100644 --- a/src/study.h +++ b/src/study.h @@ -33,6 +33,7 @@ extern "C" { 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); From a93d8fd56b08787376ace7df8904ad36b3f295ab Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 7 Jul 2018 17:29:22 +0200 Subject: [PATCH 02/30] finish parser for K_AUTOSTUDY and P_AUTO. --- src/CMakeLists.txt | 1 + src/automate.c | 26 +++++++++++++------------- src/automate.h | 12 ++++++++++++ src/automate.test.c | 38 ++++++++++++++++++++++++++++++++++++++ src/kernel/order.c | 2 +- src/kernel/order.test.c | 31 ++++++++++++++++++++++++++++--- src/study.c | 4 ++-- src/test_eressea.c | 3 ++- 8 files changed, 97 insertions(+), 20 deletions(-) create mode 100644 src/automate.test.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0ff92bf2d..cf96cfbc2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -188,6 +188,7 @@ set(TESTS_SRC tests.c academy.test.c alchemy.test.c + automate.test.c battle.test.c creport.test.c direction.test.c diff --git a/src/automate.c b/src/automate.c index 0855ff0e3..c1d4b384b 100644 --- a/src/automate.c +++ b/src/automate.c @@ -13,14 +13,6 @@ #include -typedef struct student { - unit *u; - skill_t sk; - int level; -} student; - -#define MAXSTUDENTS 128 - int cmp_students(const void *lhs, const void *rhs) { const student *a = (const student *)lhs; const student *b = (const student *)rhs; @@ -32,16 +24,16 @@ int cmp_students(const void *lhs, const void *rhs) { return (int)a->sk - (int)b->sk; } -void do_autostudy(region *r) { +int autostudy_init(student students[], int max_students, region *r) +{ unit *u; int nstudents = 0; - student students[MAXSTUDENTS]; for (u = r->units; u; u = u->next) { keyword_t kwd = getkeyword(u->thisorder); if (kwd == K_AUTOSTUDY) { student * st = students + nstudents; - if (++nstudents == MAXSTUDENTS) { + if (++nstudents == max_students) { log_fatal("you must increase MAXSTUDENTS"); } st->u = u; @@ -50,10 +42,18 @@ void do_autostudy(region *r) { st->level = effskill_study(u, st->sk); } } + return nstudents; +} + +#define MAXSTUDENTS 128 + +void do_autostudy(region *r) { + student students[MAXSTUDENTS]; + int nstudents = autostudy_init(students, MAXSTUDENTS, r); + if (nstudents > 0) { - int i, taught = 0; + int i; skill_t sk = NOSKILL; - student *teacher = NULL, *student = NULL; qsort(students, nstudents, sizeof(student), cmp_students); for (i = 0; i != nstudents; ++i) { diff --git a/src/automate.h b/src/automate.h index ded8f938b..49b052f56 100644 --- a/src/automate.h +++ b/src/automate.h @@ -21,8 +21,20 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #ifndef H_GC_AUTOMATE #define H_GC_AUTOMATE +#include "skill.h" + struct region; +struct unit; + +typedef struct student { + struct unit *u; + skill_t sk; + int level; + int learn; +} student; void do_autostudy(struct region *r); +int autostudy_init(student students[], int max_students, struct region *r); + #endif diff --git a/src/automate.test.c b/src/automate.test.c new file mode 100644 index 000000000..d8dc8ed74 --- /dev/null +++ b/src/automate.test.c @@ -0,0 +1,38 @@ +#ifdef _MSC_VER +#include +#endif + +#include "automate.h" + +#include "kernel/faction.h" +#include "kernel/order.h" +#include "kernel/region.h" +#include "kernel/unit.h" + +#include "tests.h" + +#include + +static void test_autostudy(CuTest *tc) { + student students[4]; + unit *u1, *u2; + faction *f; + region *r; + + test_setup(); + r = test_create_plain(0, 0); + f = test_create_faction(NULL); + u1 = test_create_unit(f, r); + u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + u2 = test_create_unit(f, r); + u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + CuAssertIntEquals(tc, 2, autostudy_init(students, 4, r)); + test_teardown(); +} + +CuSuite *get_automate_suite(void) +{ + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_autostudy); + return suite; +} diff --git a/src/kernel/order.c b/src/kernel/order.c index ac2573758..342f45577 100644 --- a/src/kernel/order.c +++ b/src/kernel/order.c @@ -84,7 +84,7 @@ char* get_command(const order *ord, const struct locale *lang, char *sbuffer, si sbs_strcat(&sbs, str); if (ord->id < 0) { skill_t sk = (skill_t)(100+ord->id); - assert(kwd == K_STUDY && sk != SK_MAGIC && sk < MAXSKILLS); + assert((kwd == K_STUDY || kwd == K_AUTOSTUDY) && sk != SK_MAGIC && sk < MAXSKILLS); str = skillname(sk, lang); if (str) { if (strchr(str, ' ') == NULL) { diff --git a/src/kernel/order.test.c b/src/kernel/order.test.c index 3a2f44c56..d1f8b4176 100644 --- a/src/kernel/order.test.c +++ b/src/kernel/order.test.c @@ -113,6 +113,30 @@ static void test_parse_make(CuTest *tc) { test_teardown(); } +static void test_parse_autostudy(CuTest *tc) { + char cmd[32]; + order *ord; + struct locale * lang; + + test_setup(); + lang = get_or_create_locale("en"); + locale_setstring(lang, mkname("skill", skillnames[SK_ENTERTAINMENT]), "Entertainment"); + locale_setstring(lang, keyword(K_STUDY), "STUDY"); + locale_setstring(lang, keyword(K_AUTOSTUDY), "AUTOSTUDY"); + locale_setstring(lang, parameters[P_AUTO], "AUTO"); + init_locale(lang); + + ord = parse_order("STUDY AUTO Entertainment", lang); + CuAssertPtrNotNull(tc, ord); + CuAssertIntEquals(tc, K_AUTOSTUDY, getkeyword(ord)); + CuAssertStrEquals(tc, "AUTOSTUDY Entertainment", get_command(ord, lang, cmd, sizeof(cmd))); + + CuAssertIntEquals(tc, K_AUTOSTUDY, init_order(ord, lang)); + CuAssertStrEquals(tc, "Entertainment", getstrtoken()); + free_order(ord); + test_teardown(); +} + static void test_parse_make_temp(CuTest *tc) { char cmd[32]; order *ord; @@ -130,7 +154,7 @@ static void test_parse_make_temp(CuTest *tc) { CuAssertIntEquals(tc, K_MAKETEMP, getkeyword(ord)); CuAssertStrEquals(tc, "MAKETEMP herp", get_command(ord, lang, cmd, sizeof(cmd))); - CuAssertIntEquals(tc, K_MAKETEMP, init_order_depr(ord)); + CuAssertIntEquals(tc, K_MAKETEMP, init_order(ord, lang)); CuAssertStrEquals(tc, "herp", getstrtoken()); free_order(ord); test_teardown(); @@ -153,7 +177,7 @@ static void test_parse_maketemp(CuTest *tc) { CuAssertPtrNotNull(tc, ord); CuAssertStrEquals(tc, "MAKETEMP herp", get_command(ord, lang, cmd, sizeof(cmd))); CuAssertIntEquals(tc, K_MAKETEMP, getkeyword(ord)); - CuAssertIntEquals(tc, K_MAKETEMP, init_order_depr(ord)); + CuAssertIntEquals(tc, K_MAKETEMP, init_order(ord, lang)); CuAssertStrEquals(tc, "herp", getstrtoken()); free_order(ord); test_teardown(); @@ -167,7 +191,7 @@ static void test_init_order(CuTest *tc) { lang = get_or_create_locale("en"); ord = create_order(K_MAKETEMP, lang, "hurr durr"); - CuAssertIntEquals(tc, K_MAKETEMP, init_order_depr(ord)); + CuAssertIntEquals(tc, K_MAKETEMP, init_order(ord, lang)); CuAssertStrEquals(tc, "hurr", getstrtoken()); CuAssertStrEquals(tc, "durr", getstrtoken()); free_order(ord); @@ -548,6 +572,7 @@ CuSuite *get_order_suite(void) SUITE_ADD_TEST(suite, test_study_order_quoted); SUITE_ADD_TEST(suite, test_parse_order); SUITE_ADD_TEST(suite, test_parse_make); + SUITE_ADD_TEST(suite, test_parse_autostudy); SUITE_ADD_TEST(suite, test_parse_make_temp); SUITE_ADD_TEST(suite, test_parse_maketemp); SUITE_ADD_TEST(suite, test_init_order); diff --git a/src/study.c b/src/study.c index 6df5b5eaf..217fe9505 100644 --- a/src/study.c +++ b/src/study.c @@ -343,7 +343,7 @@ int teach_cmd(unit * teacher, struct order *ord) init_order(student->thisorder, student->faction->locale); sk = getskill(student->faction->locale); if (sk != NOSKILL - && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(student, sk)) { + && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(student, sk, NULL)) { teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); } } @@ -432,7 +432,7 @@ int teach_cmd(unit * teacher, struct order *ord) continue; } - if (effskill_study(student, sk, NULL) > effskill_study(teacher, sk) + if (effskill_study(student, sk) > effskill_study(teacher, sk) - TEACHDIFFERENCE) { if (feedback) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "teach_asgood", diff --git a/src/test_eressea.c b/src/test_eressea.c index c299fc09c..5684a8ad4 100644 --- a/src/test_eressea.c +++ b/src/test_eressea.c @@ -93,7 +93,6 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(xerewards); /* kernel */ ADD_SUITE(academy); - ADD_SUITE(alchemy); ADD_SUITE(alliance); ADD_SUITE(ally); ADD_SUITE(building); @@ -121,6 +120,8 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(spells); ADD_SUITE(unit); /* gamecode */ + ADD_SUITE(alchemy); + ADD_SUITE(automate); ADD_SUITE(battle); ADD_SUITE(calendar); ADD_SUITE(creport); From 9bd439e2e192fbeb97611eaa35a68abece3990b1 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 7 Jul 2018 20:56:35 +0200 Subject: [PATCH 03/30] Test, sorting units by skill and level --- src/automate.c | 5 ++++- src/automate.test.c | 22 ++++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/automate.c b/src/automate.c index c1d4b384b..f195bb6db 100644 --- a/src/automate.c +++ b/src/automate.c @@ -40,8 +40,12 @@ int autostudy_init(student students[], int max_students, region *r) init_order(u->thisorder, u->faction->locale); st->sk = getskill(u->faction->locale); st->level = effskill_study(u, st->sk); + st->learn = 0; } } + if (nstudents > 0) { + qsort(students, nstudents, sizeof(student), cmp_students); + } return nstudents; } @@ -55,7 +59,6 @@ void do_autostudy(region *r) { int i; skill_t sk = NOSKILL; - qsort(students, nstudents, sizeof(student), cmp_students); for (i = 0; i != nstudents; ++i) { if (students[i].u) { if (sk == NOSKILL) { diff --git a/src/automate.test.c b/src/automate.test.c index d8dc8ed74..3ea640cdf 100644 --- a/src/automate.test.c +++ b/src/automate.test.c @@ -15,7 +15,7 @@ static void test_autostudy(CuTest *tc) { student students[4]; - unit *u1, *u2; + unit *u1, *u2, *u3; faction *f; region *r; @@ -24,9 +24,27 @@ static void test_autostudy(CuTest *tc) { f = test_create_faction(NULL); u1 = test_create_unit(f, r); u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + test_create_unit(f, r); u2 = test_create_unit(f, r); u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); - CuAssertIntEquals(tc, 2, autostudy_init(students, 4, r)); + set_level(u2, SK_ENTERTAINMENT, 2); + u3 = test_create_unit(f, r); + u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); + students[3].u = NULL; + CuAssertIntEquals(tc, 3, autostudy_init(students, 4, r)); + CuAssertPtrEquals(tc, u2, students[0].u); + CuAssertIntEquals(tc, 2, students[0].level); + CuAssertIntEquals(tc, 0, students[0].learn); + CuAssertIntEquals(tc, SK_ENTERTAINMENT, students[0].sk); + CuAssertPtrEquals(tc, u1, students[1].u); + CuAssertIntEquals(tc, 0, students[1].level); + CuAssertIntEquals(tc, 0, students[1].learn); + CuAssertIntEquals(tc, SK_ENTERTAINMENT, students[1].sk); + CuAssertPtrEquals(tc, u3, students[2].u); + CuAssertIntEquals(tc, 0, students[2].level); + CuAssertIntEquals(tc, 0, students[2].learn); + CuAssertIntEquals(tc, SK_PERCEPTION, students[2].sk); + CuAssertPtrEquals(tc, NULL, students[3].u); test_teardown(); } From 7affbd6c74a9ebebf8d949a2b881e607a423a4ea Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 9 Jul 2018 03:31:13 +0200 Subject: [PATCH 04/30] autostudy continued (WIP) --- src/academy.c | 8 ++-- src/academy.h | 2 +- src/automate.c | 101 +++++++++++++++++++++++++++++++------------- src/automate.h | 7 +-- src/automate.test.c | 64 ++++++++++++++++++++-------- src/study.c | 86 ++++++++++++++++++------------------- 6 files changed, 170 insertions(+), 98 deletions(-) diff --git a/src/academy.c b/src/academy.c index 5df13efa4..1298e9b57 100644 --- a/src/academy.c +++ b/src/academy.c @@ -33,13 +33,13 @@ void academy_teaching_bonus(struct unit *u, skill_t sk, int students) { } } -bool academy_can_teach(unit *teacher, unit *student, skill_t sk) { +bool academy_can_teach(unit *teacher, unit *scholar, skill_t sk) { const struct building_type *btype = bt_find("academy"); - if (active_building(teacher, btype) && active_building(student, btype)) { - int j = study_cost(student, sk) * 2; + if (active_building(teacher, btype) && active_building(scholar, btype)) { + int j = study_cost(scholar, sk) * 2; if (j < 50) j = 50; /* kann Einheit das zahlen? */ - return get_pooled(student, get_resourcetype(R_SILVER), GET_DEFAULT, j) >= j; + return get_pooled(scholar, get_resourcetype(R_SILVER), GET_DEFAULT, j) >= j; /* sonst nehmen sie nicht am Unterricht teil */ } return false; diff --git a/src/academy.h b/src/academy.h index f6af93748..3c496d8fa 100644 --- a/src/academy.h +++ b/src/academy.h @@ -9,7 +9,7 @@ extern "C" { struct unit; void academy_teaching_bonus(struct unit *u, skill_t sk, int academy); - bool academy_can_teach(struct unit *teacher, struct unit *student, skill_t sk); + bool academy_can_teach(struct unit *teacher, struct unit *scholar, skill_t sk); #ifdef __cplusplus } #endif diff --git a/src/automate.c b/src/automate.c index f195bb6db..997f67bd8 100644 --- a/src/automate.c +++ b/src/automate.c @@ -13,9 +13,10 @@ #include -int cmp_students(const void *lhs, const void *rhs) { - const student *a = (const student *)lhs; - const student *b = (const student *)rhs; +int cmp_scholars(const void *lhs, const void *rhs) +{ + const scholar *a = (const scholar *)lhs; + const scholar *b = (const scholar *)rhs; if (a->sk == b->sk) { /* sort by level, descending: */ return b->level - a->level; @@ -24,17 +25,17 @@ int cmp_students(const void *lhs, const void *rhs) { return (int)a->sk - (int)b->sk; } -int autostudy_init(student students[], int max_students, region *r) +int autostudy_init(scholar scholars[], int max_scholars, region *r) { unit *u; - int nstudents = 0; + int nscholars = 0; for (u = r->units; u; u = u->next) { keyword_t kwd = getkeyword(u->thisorder); if (kwd == K_AUTOSTUDY) { - student * st = students + nstudents; - if (++nstudents == max_students) { - log_fatal("you must increase MAXSTUDENTS"); + scholar * st = scholars + nscholars; + if (++nscholars == max_scholars) { + log_fatal("you must increase MAXSCHOLARS"); } st->u = u; init_order(u->thisorder, u->faction->locale); @@ -43,31 +44,73 @@ int autostudy_init(student students[], int max_students, region *r) st->learn = 0; } } - if (nstudents > 0) { - qsort(students, nstudents, sizeof(student), cmp_students); + if (nscholars > 0) { + qsort(scholars, nscholars, sizeof(scholar), cmp_scholars); } - return nstudents; + return nscholars; } -#define MAXSTUDENTS 128 - -void do_autostudy(region *r) { - student students[MAXSTUDENTS]; - int nstudents = autostudy_init(students, MAXSTUDENTS, r); - - if (nstudents > 0) { - int i; - skill_t sk = NOSKILL; - - for (i = 0; i != nstudents; ++i) { - if (students[i].u) { - if (sk == NOSKILL) { - sk = students[i].sk; +void autostudy_run(scholar scholars[], int nscholars) +{ + int i, t, s, ti = 0, si = 0, ts = 0, tt = 0; + skill_t sk = scholars[0].sk; + for (i = ti; i != nscholars && scholars[i].sk == sk; ++i) { + int mint; + ts += scholars[i].u->number; /* count total scholars */ + mint = (ts + 10) / 11; /* need a minimum of ceil(ts/11) teachers */ + while (mint>tt) { + tt += scholars[si++].u->number; + } + } + /* now si splits the teachers and students 1:10 */ + /* first student must be 2 levels below first teacher: */ + while (scholars[ti].level - TEACHDIFFERENCE > scholars[si].level) { + tt += scholars[si++].u->number; + } + /* invariant: unit ti can still teach i students */ + i = scholars[ti].u->number * 10; + for (t = ti, s = si; t != si && s != nscholars; ++t) { + /* TODO: is there no constant for students per teacher? */ + while (s != nscholars) { + int n = scholars[s].u->number; + scholars[s].learn += n; + if (i >= n) { + i -= n; + scholars[s].learn += n; + /* next student */ + } + else { + scholars[s].learn += i; + /* go to next suitable teacher */ + do { + ++t; } - else if (sk != students[i].sk) { - continue; - } - students[i].u = NULL; + while (scholars[t].level - TEACHDIFFERENCE < scholars[s].level); + } + } + } +} + +#define MAXSCHOLARS 128 + +void do_autostudy(region *r) +{ + scholar scholars[MAXSCHOLARS]; + int nscholars = autostudy_init(scholars, MAXSCHOLARS, r); + + if (nscholars > 0) { + int i; + skill_t sk = NOSKILL; + + for (i = 0; i != nscholars; ++i) { + if (scholars[i].u) { + if (sk == NOSKILL) { + sk = scholars[i].sk; + } + else if (sk != scholars[i].sk) { + continue; + } + scholars[i].u = NULL; } } } diff --git a/src/automate.h b/src/automate.h index 49b052f56..285fc6638 100644 --- a/src/automate.h +++ b/src/automate.h @@ -26,15 +26,16 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. struct region; struct unit; -typedef struct student { +typedef struct scholar { struct unit *u; skill_t sk; int level; int learn; -} student; +} scholar; void do_autostudy(struct region *r); -int autostudy_init(student students[], int max_students, struct region *r); +int autostudy_init(scholar scholars[], int max_scholars, struct region *r); +void autostudy_run(scholar scholars[], int nscholars); #endif diff --git a/src/automate.test.c b/src/automate.test.c index 3ea640cdf..7404a2d9c 100644 --- a/src/automate.test.c +++ b/src/automate.test.c @@ -13,8 +13,8 @@ #include -static void test_autostudy(CuTest *tc) { - student students[4]; +static void test_autostudy_init(CuTest *tc) { + scholar scholars[4]; unit *u1, *u2, *u3; faction *f; region *r; @@ -30,27 +30,55 @@ static void test_autostudy(CuTest *tc) { set_level(u2, SK_ENTERTAINMENT, 2); u3 = test_create_unit(f, r); u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); - students[3].u = NULL; - CuAssertIntEquals(tc, 3, autostudy_init(students, 4, r)); - CuAssertPtrEquals(tc, u2, students[0].u); - CuAssertIntEquals(tc, 2, students[0].level); - CuAssertIntEquals(tc, 0, students[0].learn); - CuAssertIntEquals(tc, SK_ENTERTAINMENT, students[0].sk); - CuAssertPtrEquals(tc, u1, students[1].u); - CuAssertIntEquals(tc, 0, students[1].level); - CuAssertIntEquals(tc, 0, students[1].learn); - CuAssertIntEquals(tc, SK_ENTERTAINMENT, students[1].sk); - CuAssertPtrEquals(tc, u3, students[2].u); - CuAssertIntEquals(tc, 0, students[2].level); - CuAssertIntEquals(tc, 0, students[2].learn); - CuAssertIntEquals(tc, SK_PERCEPTION, students[2].sk); - CuAssertPtrEquals(tc, NULL, students[3].u); + scholars[3].u = NULL; + CuAssertIntEquals(tc, 3, autostudy_init(scholars, 4, r)); + CuAssertPtrEquals(tc, u2, scholars[0].u); + CuAssertIntEquals(tc, 2, scholars[0].level); + CuAssertIntEquals(tc, 0, scholars[0].learn); + CuAssertIntEquals(tc, SK_ENTERTAINMENT, scholars[0].sk); + CuAssertPtrEquals(tc, u1, scholars[1].u); + CuAssertIntEquals(tc, 0, scholars[1].level); + CuAssertIntEquals(tc, 0, scholars[1].learn); + CuAssertIntEquals(tc, SK_ENTERTAINMENT, scholars[1].sk); + CuAssertPtrEquals(tc, u3, scholars[2].u); + CuAssertIntEquals(tc, 0, scholars[2].level); + CuAssertIntEquals(tc, 0, scholars[2].learn); + CuAssertIntEquals(tc, SK_PERCEPTION, scholars[2].sk); + CuAssertPtrEquals(tc, NULL, scholars[3].u); test_teardown(); } +static void test_autostudy_run(CuTest *tc) { + scholar scholars[4]; + unit *u1, *u2, *u3; + faction *f; + region *r; + + test_setup(); + r = test_create_plain(0, 0); + f = test_create_faction(NULL); + u1 = test_create_unit(f, r); + u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + set_number(u1, 2); + u2 = test_create_unit(f, r); + u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + set_number(u2, 10); + set_level(u2, SK_ENTERTAINMENT, 2); + u3 = test_create_unit(f, r); + u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); + set_number(u3, 20); + scholars[3].u = NULL; + CuAssertIntEquals(tc, 3, autostudy_init(scholars, 4, r)); + autostudy_run(scholars, 3); + CuAssertIntEquals(tc, 0, scholars[0].learn); + CuAssertIntEquals(tc, 20, scholars[1].learn); + CuAssertIntEquals(tc, 30, scholars[2].learn); +} + CuSuite *get_automate_suite(void) { CuSuite *suite = CuSuiteNew(); - SUITE_ADD_TEST(suite, test_autostudy); + SUITE_ADD_TEST(suite, test_autostudy_init); + SUITE_ADD_TEST(suite, test_autostudy_run); return suite; } diff --git a/src/study.c b/src/study.c index 217fe9505..0dc4386bc 100644 --- a/src/study.c +++ b/src/study.c @@ -195,37 +195,37 @@ const attrib_type at_learning = { #define EXPERIENCEDAYS 10 -static int study_days(unit * student, skill_t sk) +static int study_days(unit * scholar, skill_t sk) { int speed = STUDYDAYS; - if (u_race(student)->study_speed) { - speed += u_race(student)->study_speed[sk]; + if (u_race(scholar)->study_speed) { + speed += u_race(scholar)->study_speed[sk]; if (speed < STUDYDAYS) { - skill *sv = unit_skill(student, sk); + skill *sv = unit_skill(scholar, sk); if (sv == 0) { speed = STUDYDAYS; } } } - return student->number * speed; + return scholar->number * speed; } static int -teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk, +teach_unit(unit * teacher, unit * scholar, int nteaching, skill_t sk, bool report, int *academy_students) { teaching_info *teach = NULL; attrib *a; int students; - if (magic_lowskill(student)) { + if (magic_lowskill(scholar)) { cmistake(teacher, teacher->thisorder, 292, MSG_EVENT); return 0; } - students = student->number; + students = scholar->number; /* subtract already taught students */ - a = a_find(student->attribs, &at_learning); + a = a_find(scholar->attribs, &at_learning); if (a != NULL) { teach = (teaching_info *)a->data.v; students -= teach->students; @@ -235,18 +235,18 @@ teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk, if (students > 0) { if (teach == NULL) { - a = a_add(&student->attribs, a_new(&at_learning)); + a = a_add(&scholar->attribs, a_new(&at_learning)); teach = (teaching_info *)a->data.v; } selist_push(&teach->teachers, teacher); teach->days += students * STUDYDAYS; teach->students += students; - if (student->building && teacher->building == student->building) { + if (scholar->building && teacher->building == scholar->building) { /* Solange Akademien groessenbeschraenkt sind, sollte Lehrer und * Student auch in unterschiedlichen Gebaeuden stehen duerfen */ /* FIXME comment contradicts implementation */ - if (academy_can_teach(teacher, student, sk)) { + if (academy_can_teach(teacher, scholar, sk)) { /* Jeder Schueler zusaetzlich +10 Tage wenn in Uni. */ teach->days += students * EXPERIENCEDAYS; /* learning erhoehen */ /* Lehrer zusaetzlich +1 Tag pro Schueler. */ @@ -304,7 +304,7 @@ int teach_cmd(unit * teacher, struct order *ord) #if TEACH_ALL if (getparam(teacher->faction->locale) == P_ANY) { skill_t sk; - unit *student; + unit *scholar; skill_t teachskill[MAXSKILLS]; int t = 0; @@ -313,15 +313,15 @@ int teach_cmd(unit * teacher, struct order *ord) teachskill[t] = getskill(teacher->faction->locale); } while (sk != NOSKILL); - for (student = r->units; teaching > 0 && student; student = student->next) { - if (LongHunger(student)) { + for (scholar = r->units; teaching > 0 && scholar; scholar = scholar->next) { + if (LongHunger(scholar)) { continue; } - else if (student->faction == teacher->faction) { - if (getkeyword(student->thisorder) == K_STUDY) { + else if (scholar->faction == teacher->faction) { + if (getkeyword(scholar->thisorder) == K_STUDY) { /* Input ist nun von student->thisorder !! */ - init_order(student->thisorder, student->faction->locale); - sk = getskill(student->faction->locale); + init_order(scholar->thisorder, scholar->faction->locale); + sk = getskill(scholar->faction->locale); if (sk != NOSKILL && teachskill[0] != NOSKILL) { for (t = 0; teachskill[t] != NOSKILL; ++t) { if (sk == teachskill[t]) { @@ -331,20 +331,20 @@ int teach_cmd(unit * teacher, struct order *ord) sk = teachskill[t]; } if (sk != NOSKILL - && effskill_study(teacher, sk) - TEACHDIFFERENCE > effskill_study(student, sk)) { - teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); + && effskill_study(teacher, sk) - TEACHDIFFERENCE > effskill_study(scholar, sk)) { + teaching -= teach_unit(teacher, scholar, teaching, sk, true, &academy_students); } } } #ifdef TEACH_FRIENDS - else if (alliedunit(teacher, student->faction, HELP_GUARD)) { - if (getkeyword(student->thisorder) == K_STUDY) { + else if (alliedunit(teacher, scholar->faction, HELP_GUARD)) { + if (getkeyword(scholar->thisorder) == K_STUDY) { /* Input ist nun von student->thisorder !! */ - init_order(student->thisorder, student->faction->locale); - sk = getskill(student->faction->locale); + init_order(scholar->thisorder, scholar->faction->locale); + sk = getskill(scholar->faction->locale); if (sk != NOSKILL - && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(student, sk, NULL)) { - teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); + && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(scholar, sk, NULL)) { + teaching -= teach_unit(teacher, scholar, teaching, sk, true, &academy_students); } } } @@ -363,15 +363,15 @@ int teach_cmd(unit * teacher, struct order *ord) while (!parser_end()) { skill_t sk; - unit *student; + unit *scholar; bool feedback; - getunit(r, teacher->faction, &student); + getunit(r, teacher->faction, &scholar); ++count; /* Falls die Unit nicht gefunden wird, Fehler melden */ - if (!student) { + if (!scholar) { char tbuf[20]; const char *uid; const char *token; @@ -403,8 +403,8 @@ int teach_cmd(unit * teacher, struct order *ord) continue; } - feedback = teacher->faction == student->faction - || alliedunit(student, teacher->faction, HELP_GUARD); + feedback = teacher->faction == scholar->faction + || alliedunit(scholar, teacher->faction, HELP_GUARD); /* Neuen Befehl zusammenbauen. TEMP-Einheiten werden automatisch in * ihre neuen Nummern uebersetzt. */ @@ -412,31 +412,31 @@ int teach_cmd(unit * teacher, struct order *ord) strncat(zOrder, " ", sz - 1); --sz; } - sz -= str_strlcpy(zOrder + 4096 - sz, itoa36(student->no), sz); + sz -= str_strlcpy(zOrder + 4096 - sz, itoa36(scholar->no), sz); - if (getkeyword(student->thisorder) != K_STUDY) { + if (getkeyword(scholar->thisorder) != K_STUDY) { ADDMSG(&teacher->faction->msgs, - msg_feedback(teacher, ord, "teach_nolearn", "student", student)); + msg_feedback(teacher, ord, "teach_nolearn", "student", scholar)); continue; } /* Input ist nun von student->thisorder !! */ parser_pushstate(); - init_order(student->thisorder, student->faction->locale); - sk = getskill(student->faction->locale); + init_order(scholar->thisorder, scholar->faction->locale); + sk = getskill(scholar->faction->locale); parser_popstate(); if (sk == NOSKILL) { ADDMSG(&teacher->faction->msgs, - msg_feedback(teacher, ord, "teach_nolearn", "student", student)); + msg_feedback(teacher, ord, "teach_nolearn", "student", scholar)); continue; } - if (effskill_study(student, sk) > effskill_study(teacher, sk) + if (effskill_study(scholar, sk) > effskill_study(teacher, sk) - TEACHDIFFERENCE) { if (feedback) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "teach_asgood", - "student", student)); + "student", scholar)); } continue; } @@ -444,18 +444,18 @@ int teach_cmd(unit * teacher, struct order *ord) /* ist der Magier schon spezialisiert, so versteht er nur noch * Lehrer seines Gebietes */ sc_mage *mage1 = get_mage_depr(teacher); - sc_mage *mage2 = get_mage_depr(student); + sc_mage *mage2 = get_mage_depr(scholar); if (mage2 && mage1 && mage2->magietyp != M_GRAY && mage1->magietyp != mage2->magietyp) { if (feedback) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, - "error_different_magic", "target", student)); + "error_different_magic", "target", scholar)); } continue; } } sk_academy = sk; - teaching -= teach_unit(teacher, student, teaching, sk, false, &academy_students); + teaching -= teach_unit(teacher, scholar, teaching, sk, false, &academy_students); } new_order = create_order(K_TEACH, teacher->faction->locale, "%s", zOrder); replace_order(&teacher->orders, ord, new_order); From 51740476baa501fcc17803bfe15f7350ce522571 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 11 Jul 2018 21:03:00 +0200 Subject: [PATCH 05/30] finished autostudy --- src/automate.c | 110 +++++++++++++++++++++++++++++++------------- src/automate.h | 2 + src/automate.test.c | 34 ++++++++++++-- 3 files changed, 111 insertions(+), 35 deletions(-) diff --git a/src/automate.c b/src/automate.c index 997f67bd8..bf130acf3 100644 --- a/src/automate.c +++ b/src/automate.c @@ -12,6 +12,7 @@ #include "study.h" #include +#include int cmp_scholars(const void *lhs, const void *rhs) { @@ -50,44 +51,89 @@ int autostudy_init(scholar scholars[], int max_scholars, region *r) return nscholars; } +static void teaching(scholar *s, int n) { + assert(n <= s->u->number); + s->learn += n; +} + +static void learning(scholar *s, int n) { + assert(n <= s->u->number); + s->learn += n; +} + void autostudy_run(scholar scholars[], int nscholars) { - int i, t, s, ti = 0, si = 0, ts = 0, tt = 0; - skill_t sk = scholars[0].sk; - for (i = ti; i != nscholars && scholars[i].sk == sk; ++i) { - int mint; - ts += scholars[i].u->number; /* count total scholars */ - mint = (ts + 10) / 11; /* need a minimum of ceil(ts/11) teachers */ - while (mint>tt) { - tt += scholars[si++].u->number; - } - } - /* now si splits the teachers and students 1:10 */ - /* first student must be 2 levels below first teacher: */ - while (scholars[ti].level - TEACHDIFFERENCE > scholars[si].level) { - tt += scholars[si++].u->number; - } - /* invariant: unit ti can still teach i students */ - i = scholars[ti].u->number * 10; - for (t = ti, s = si; t != si && s != nscholars; ++t) { - /* TODO: is there no constant for students per teacher? */ - while (s != nscholars) { - int n = scholars[s].u->number; - scholars[s].learn += n; - if (i >= n) { - i -= n; - scholars[s].learn += n; - /* next student */ + int ti = 0; + while (ti != nscholars) { + skill_t sk = scholars[ti].sk; + int t, s, se, ts = 0, tt = 0, si = ti; + for (se = ti; se != nscholars && scholars[se].sk == sk; ++se) { + int mint; + ts += scholars[se].u->number; /* count total scholars */ + mint = (ts + 10) / 11; /* need a minimum of ceil(ts/11) teachers */ + for (; mint > tt && si != nscholars; ++si) { + tt += scholars[si].u->number; } - else { - scholars[s].learn += i; - /* go to next suitable teacher */ - do { - ++t; + } + /* now si splits the teachers and students 1:10 */ + /* first student must be 2 levels below first teacher: */ + for (; si != se && scholars[ti].level - TEACHDIFFERENCE > scholars[si].level; ++si) { + tt += scholars[si].u->number; + } + if (si == se) { + /* there are no students, so standard learning only */ + for (t = ti; t != se; ++t) { + learning(scholars + t, scholars[t].u->number); + } + } + else { + /* invariant: unit ti can still teach i students */ + int i = scholars[ti].u->number * STUDENTS_PER_TEACHER; + /* invariant: unit si has n students that can still be taught */ + int n = scholars[si].u->number; + for (t = ti, s = si; t != si && s != se; ) { + if (i > n) { + /* t has more than enough teaching capacity for s */ + i -= n; + teaching(scholars + s, n); + learning(scholars + s, scholars[s].u->number); + /* next student, please: */ + if (++s == se) { + continue; + } + n = scholars[s].u->number; + } + else { + /* s gets partial credit and we need a new teacher */ + teaching(scholars + s, i); + + /* we are done with this teacher. any remaining people are regular learners: */ + if (scholars[t].u->number > 1) { + /* remain = number - ceil(taught/10); */ + int remain = (STUDENTS_PER_TEACHER * scholars[t].u->number - i + STUDENTS_PER_TEACHER - 1) / STUDENTS_PER_TEACHER; + learning(scholars + t, remain); + } + + /* we want a new teacher for s. if any exists, it's next in the sequence. */ + if (++t == si) { + continue; + } + if (scholars[t].level - TEACHDIFFERENCE < scholars[s].level) { + /* next teacher cannot teach, we must skip students. */ + do { + learning(scholars + s, (n - i)); + i = 0; + if (++s == se) { + continue; + } + n = scholars[s].u->number; + } while (scholars[t].level - TEACHDIFFERENCE < scholars[s].level); + } + i = scholars[t].u->number * STUDENTS_PER_TEACHER; } - while (scholars[t].level - TEACHDIFFERENCE < scholars[s].level); } } + ti = se; } } diff --git a/src/automate.h b/src/automate.h index 285fc6638..61fed6866 100644 --- a/src/automate.h +++ b/src/automate.h @@ -33,6 +33,8 @@ typedef struct scholar { int learn; } scholar; +#define STUDENTS_PER_TEACHER 10 + void do_autostudy(struct region *r); int autostudy_init(scholar scholars[], int max_scholars, struct region *r); diff --git a/src/automate.test.c b/src/automate.test.c index 7404a2d9c..999cb25d4 100644 --- a/src/automate.test.c +++ b/src/automate.test.c @@ -60,19 +60,46 @@ static void test_autostudy_run(CuTest *tc) { u1 = test_create_unit(f, r); u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); set_number(u1, 2); + set_level(u1, SK_ENTERTAINMENT, 2); u2 = test_create_unit(f, r); u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); set_number(u2, 10); - set_level(u2, SK_ENTERTAINMENT, 2); u3 = test_create_unit(f, r); u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); - set_number(u3, 20); + set_number(u3, 15); scholars[3].u = NULL; CuAssertIntEquals(tc, 3, autostudy_init(scholars, 4, r)); autostudy_run(scholars, 3); CuAssertIntEquals(tc, 0, scholars[0].learn); CuAssertIntEquals(tc, 20, scholars[1].learn); - CuAssertIntEquals(tc, 30, scholars[2].learn); + CuAssertIntEquals(tc, 15, scholars[2].learn); +} + +static void test_autostudy_run_noteachers(CuTest *tc) { + scholar scholars[4]; + unit *u1, *u2, *u3; + faction *f; + region *r; + + test_setup(); + r = test_create_plain(0, 0); + f = test_create_faction(NULL); + u1 = test_create_unit(f, r); + u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_LUMBERJACK]); + set_number(u1, 2); + set_level(u1, SK_ENTERTAINMENT, 2); + u2 = test_create_unit(f, r); + u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + set_number(u2, 10); + u3 = test_create_unit(f, r); + u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); + set_number(u3, 15); + scholars[3].u = NULL; + CuAssertIntEquals(tc, 3, autostudy_init(scholars, 4, r)); + autostudy_run(scholars, 3); + CuAssertIntEquals(tc, 2, scholars[0].learn); + CuAssertIntEquals(tc, 10, scholars[1].learn); + CuAssertIntEquals(tc, 15, scholars[2].learn); } CuSuite *get_automate_suite(void) @@ -80,5 +107,6 @@ CuSuite *get_automate_suite(void) CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_autostudy_init); SUITE_ADD_TEST(suite, test_autostudy_run); + SUITE_ADD_TEST(suite, test_autostudy_run_noteachers); return suite; } From c4424f9899376c0b6a4d3e844ff830cc0fc48a54 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 11 Jul 2018 21:07:31 +0200 Subject: [PATCH 06/30] actually do the learning. --- src/automate.c | 22 +++++----------------- src/study.c | 6 +++--- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/automate.c b/src/automate.c index bf130acf3..e08d7a625 100644 --- a/src/automate.c +++ b/src/automate.c @@ -142,22 +142,10 @@ void autostudy_run(scholar scholars[], int nscholars) void do_autostudy(region *r) { scholar scholars[MAXSCHOLARS]; - int nscholars = autostudy_init(scholars, MAXSCHOLARS, r); - - if (nscholars > 0) { - int i; - skill_t sk = NOSKILL; - - for (i = 0; i != nscholars; ++i) { - if (scholars[i].u) { - if (sk == NOSKILL) { - sk = scholars[i].sk; - } - else if (sk != scholars[i].sk) { - continue; - } - scholars[i].u = NULL; - } - } + int i, nscholars = autostudy_init(scholars, MAXSCHOLARS, r); + autostudy_run(scholars, nscholars); + for (i = 0; i != nscholars; ++i) { + int days = STUDYDAYS * scholars[i].learn; + learn_skill(scholars[i].u, scholars[i].sk, days); } } diff --git a/src/study.c b/src/study.c index 0dc4386bc..bd29fc2a8 100644 --- a/src/study.c +++ b/src/study.c @@ -766,9 +766,6 @@ int study_cmd(unit * u, order * ord) days *= 2; } - if (fval(u, UFL_HUNGER)) - days /= 2; - learn_skill(u, sk, days); if (a != NULL) { if (teach->teachers) { @@ -832,6 +829,9 @@ void learn_skill(unit *u, skill_t sk, int days) { int leveldays = STUDYDAYS * u->number; int weeks = 0; + if (fval(u, UFL_HUNGER)) { + days /= 2; + } assert(sk >= 0 && sk < MAXSKILLS); if (inject_learn_fun) { inject_learn_fun(u, sk, days); From af0a359acf03250653bdfe5f4d321e3b68e3aedf Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 11 Jul 2018 21:39:33 +0200 Subject: [PATCH 07/30] make local comparison function static --- clibs | 2 +- src/automate.c | 2 +- storage | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clibs b/clibs index 9b6e34959..f9842e07a 160000 --- a/clibs +++ b/clibs @@ -1 +1 @@ -Subproject commit 9b6e34959f77d7ca3a4ce3826cb487487f557441 +Subproject commit f9842e07a442c5453c270badf25ab72633b4edf5 diff --git a/src/automate.c b/src/automate.c index e08d7a625..2f188f352 100644 --- a/src/automate.c +++ b/src/automate.c @@ -14,7 +14,7 @@ #include #include -int cmp_scholars(const void *lhs, const void *rhs) +static int cmp_scholars(const void *lhs, const void *rhs) { const scholar *a = (const scholar *)lhs; const scholar *b = (const scholar *)rhs; diff --git a/storage b/storage index 6eea76952..5623ee652 160000 --- a/storage +++ b/storage @@ -1 +1 @@ -Subproject commit 6eea7695285f9c9f1d25421897ca55f466ef7566 +Subproject commit 5623ee6527e97af20c7d8efc03800b6fe1579744 From 25d93f3a182ca592445cb29da1386216237694e1 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 12 Jul 2018 20:52:09 +0200 Subject: [PATCH 08/30] update storage library with needed bugfix --- storage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage b/storage index 5623ee652..0ef4b39e3 160000 --- a/storage +++ b/storage @@ -1 +1 @@ -Subproject commit 5623ee6527e97af20c7d8efc03800b6fe1579744 +Subproject commit 0ef4b39e39d8146d6ca323500a285c53843db988 From 902ce9bf69ff1d8751a243738fb1876788cdfd9b Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 14 Jul 2018 15:56:44 +0200 Subject: [PATCH 09/30] extract long_order_allowed for use in autostudy. --- clibs | 2 +- src/automate.c | 26 ++++++++++++++++++-------- src/automate.test.c | 2 ++ src/laws.c | 31 ++++++++++++++++++++----------- src/laws.h | 1 + src/laws.test.c | 30 ++++++++++++++++++++++++++++++ src/study.test.c | 20 ++++++++++---------- 7 files changed, 82 insertions(+), 30 deletions(-) diff --git a/clibs b/clibs index f9842e07a..9b6e34959 160000 --- a/clibs +++ b/clibs @@ -1 +1 @@ -Subproject commit f9842e07a442c5453c270badf25ab72633b4edf5 +Subproject commit 9b6e34959f77d7ca3a4ce3826cb487487f557441 diff --git a/src/automate.c b/src/automate.c index 2f188f352..ec4ea83d8 100644 --- a/src/automate.c +++ b/src/automate.c @@ -1,6 +1,7 @@ #include #include "kernel/faction.h" +#include "kernel/messages.h" #include "kernel/order.h" #include "kernel/region.h" #include "kernel/unit.h" @@ -9,6 +10,7 @@ #include "automate.h" #include "keyword.h" +#include "laws.h" #include "study.h" #include @@ -34,15 +36,21 @@ int autostudy_init(scholar scholars[], int max_scholars, region *r) for (u = r->units; u; u = u->next) { keyword_t kwd = getkeyword(u->thisorder); if (kwd == K_AUTOSTUDY) { - scholar * st = scholars + nscholars; - if (++nscholars == max_scholars) { - log_fatal("you must increase MAXSCHOLARS"); + 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; + } + else { + ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder, "error_race_nolearn", "race", + u_race(u))); } - 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; } } if (nscholars > 0) { @@ -54,11 +62,13 @@ int autostudy_init(scholar scholars[], int max_scholars, region *r) static void teaching(scholar *s, int n) { assert(n <= s->u->number); s->learn += n; + fset(s->u, UFL_LONGACTION); } static void learning(scholar *s, int n) { assert(n <= s->u->number); s->learn += n; + fset(s->u, UFL_LONGACTION); } void autostudy_run(scholar scholars[], int nscholars) diff --git a/src/automate.test.c b/src/automate.test.c index 999cb25d4..a77376668 100644 --- a/src/automate.test.c +++ b/src/automate.test.c @@ -73,6 +73,7 @@ static void test_autostudy_run(CuTest *tc) { CuAssertIntEquals(tc, 0, scholars[0].learn); CuAssertIntEquals(tc, 20, scholars[1].learn); CuAssertIntEquals(tc, 15, scholars[2].learn); + test_teardown(); } static void test_autostudy_run_noteachers(CuTest *tc) { @@ -100,6 +101,7 @@ static void test_autostudy_run_noteachers(CuTest *tc) { CuAssertIntEquals(tc, 2, scholars[0].learn); CuAssertIntEquals(tc, 10, scholars[1].learn); CuAssertIntEquals(tc, 15, scholars[2].learn); + test_teardown(); } CuSuite *get_automate_suite(void) diff --git a/src/laws.c b/src/laws.c index 3902e9a8e..3280d91bf 100644 --- a/src/laws.c +++ b/src/laws.c @@ -3647,6 +3647,24 @@ void add_proc_unit(int priority, void(*process) (unit *), const char *name) } } +bool long_order_allowed(const unit *u) +{ + const region *r = u->region; + if (fval(u, UFL_LONGACTION)) { + /* this message was already given in laws.update_long_order + cmistake(u, ord, 52, MSG_PRODUCE); + */ + return false; + } + else if (fval(r->terrain, SEA_REGION) + && u_race(u) != get_race(RC_AQUARIAN) + && !(u_race(u)->flags & RCF_SWIM)) { + /* error message disabled by popular demand */ + return false; + } + return true; +} + /* per priority, execute processors in order from PR_GLOBAL down to PR_ORDER */ void process(void) { @@ -3716,16 +3734,7 @@ void process(void) cmistake(u, ord, 224, MSG_MAGIC); ord = NULL; } - else if (fval(u, UFL_LONGACTION)) { - /* this message was already given in laws.update_long_order - cmistake(u, ord, 52, MSG_PRODUCE); - */ - ord = NULL; - } - else if (fval(r->terrain, SEA_REGION) - && u_race(u) != get_race(RC_AQUARIAN) - && !(u_race(u)->flags & RCF_SWIM)) { - /* error message disabled by popular demand */ + else if (!long_order_allowed(u)) { ord = NULL; } } @@ -4167,7 +4176,7 @@ void update_subscriptions(void) /** determine if unit can be seen by faction * @param f -- the observiong faction * @param u -- the unit that is observed - * @param r -- the region that u is obesrved in (see below) + * @param r -- the region that u is obesrved from (see below) * @param m -- terrain modifier to stealth * * r kann != u->region sein, wenn es um Durchreisen geht, diff --git a/src/laws.h b/src/laws.h index ae2c712ca..0b5db17c7 100755 --- a/src/laws.h +++ b/src/laws.h @@ -66,6 +66,7 @@ extern "C" { void update_long_order(struct unit *u); void sinkships(struct region * r); void do_enter(struct region *r, bool is_final_attempt); + bool long_order_allowed(const struct unit *u); int password_cmd(struct unit *u, struct order *ord); int banner_cmd(struct unit *u, struct order *ord); diff --git a/src/laws.test.c b/src/laws.test.c index 8d13b7c81..c7c7a6682 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -1759,6 +1759,34 @@ static void test_nmr_timeout(CuTest *tc) { test_teardown(); } +static void test_long_orders(CuTest *tc) { + unit *u; + + test_setup(); + u = test_create_unit(test_create_faction(NULL), test_create_plain(0, 0)); + CuAssertTrue(tc, long_order_allowed(u)); + u->flags |= UFL_LONGACTION; + CuAssertTrue(tc, !long_order_allowed(u)); + test_teardown(); +} + +static void test_long_order_on_ocean(CuTest *tc) { + unit *u; + race * rc; + + test_setup(); + rc = test_create_race("pikachu"); + u = test_create_unit(test_create_faction(rc), test_create_ocean(0, 0)); + CuAssertTrue(tc, !long_order_allowed(u)); + rc->flags |= RCF_SWIM; + CuAssertTrue(tc, long_order_allowed(u)); + + rc = test_create_race("aquarian"); + u = test_create_unit(test_create_faction(rc), u->region); + CuAssertTrue(tc, long_order_allowed(u)); + test_teardown(); +} + CuSuite *get_laws_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -1831,6 +1859,8 @@ CuSuite *get_laws_suite(void) SUITE_ADD_TEST(suite, test_cansee_ring); SUITE_ADD_TEST(suite, test_cansee_sphere); SUITE_ADD_TEST(suite, test_nmr_timeout); + SUITE_ADD_TEST(suite, test_long_orders); + SUITE_ADD_TEST(suite, test_long_order_on_ocean); return suite; } diff --git a/src/study.test.c b/src/study.test.c index c1d0b0841..a63be7cf1 100644 --- a/src/study.test.c +++ b/src/study.test.c @@ -89,15 +89,15 @@ static void setup_teacher(study_fixture *fix, skill_t sk) { setup_locale(lang); fix->u = test_create_unit(f, r); assert(fix->u); - fix->u->thisorder = create_order(K_STUDY, f->locale, "%s", skillnames[sk]); + fix->u->thisorder = create_order(K_STUDY, f->locale, skillnames[sk]); fix->teachers[0] = test_create_unit(f, r); assert(fix->teachers[0]); - fix->teachers[0]->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(fix->u->no)); + fix->teachers[0]->thisorder = create_order(K_TEACH, f->locale, itoa36(fix->u->no)); fix->teachers[1] = test_create_unit(f, r); assert(fix->teachers[1]); - fix->teachers[1]->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(fix->u->no)); + fix->teachers[1]->thisorder = create_order(K_TEACH, f->locale, itoa36(fix->u->no)); test_clear_messages(f); } @@ -110,7 +110,7 @@ static void test_study_no_teacher(CuTest *tc) { CuAssertPtrNotNull(tc, sv = unit_skill(fix.u, SK_CROSSBOW)); CuAssertIntEquals(tc, 1, sv->level); CuAssertIntEquals(tc, 2, sv->weeks); - CuAssertPtrEquals(tc, 0, test_get_last_message(fix.u->faction->msgs)); + CuAssertPtrEquals(tc, NULL, test_get_last_message(fix.u->faction->msgs)); test_teardown(); } @@ -121,7 +121,7 @@ static void test_study_with_teacher(CuTest *tc) { setup_teacher(&fix, SK_CROSSBOW); set_level(fix.teachers[0], SK_CROSSBOW, TEACHDIFFERENCE); teach_cmd(fix.teachers[0], fix.teachers[0]->thisorder); - CuAssertPtrEquals(tc, 0, test_get_last_message(fix.u->faction->msgs)); + CuAssertPtrEquals(tc, NULL, test_get_last_message(fix.u->faction->msgs)); study_cmd(fix.u, fix.u->thisorder); CuAssertPtrNotNull(tc, sv = unit_skill(fix.u, SK_CROSSBOW)); CuAssertIntEquals(tc, 1, sv->level); @@ -288,7 +288,7 @@ static void test_academy_bonus(CuTest *tc) { u1 = test_create_unit(u->faction, u->region); u3 = test_create_unit(u->faction, u->region); u0->thisorder = create_order(K_TEACH, loc, "%s %s", itoa36(u3->no), itoa36(u1->no)); - u->thisorder = create_order(K_TEACH, loc, "%s", itoa36(u1->no)); + u->thisorder = create_order(K_TEACH, loc, itoa36(u1->no)); u1->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]); u3->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]); @@ -405,7 +405,7 @@ static void test_study_magic(CuTest *tc) { f = test_create_faction(NULL); lang = f->locale; u = test_create_unit(f, test_create_region(0, 0, NULL)); - u->thisorder = create_order(K_STUDY, lang, "%s", skillnames[SK_MAGIC]); + u->thisorder = create_order(K_STUDY, lang, skillnames[SK_MAGIC]); itype = test_create_silver(); CuAssertIntEquals(tc, -1, study_cmd(u, u->thisorder)); @@ -423,7 +423,7 @@ static void test_study_magic(CuTest *tc) { CuAssertIntEquals(tc, M_GWYRRD, f->magiegebiet); CuAssertIntEquals(tc, 0, i_get(u->items, itype)); CuAssertPtrNotNull(tc, get_mage_depr(u)); - CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "error65")); + CuAssertPtrEquals(tc, NULL, test_find_messagetype(f->msgs, "error65")); CuAssertIntEquals(tc, M_GWYRRD, get_mage_depr(u)->magietyp); test_teardown(); @@ -491,12 +491,12 @@ static void test_teach_magic(CuTest *tc) { f = test_create_faction(NULL); f->magiegebiet = M_GWYRRD; u = test_create_unit(f, test_create_region(0, 0, NULL)); - u->thisorder = create_order(K_STUDY, f->locale, "%s", skillnames[SK_MAGIC]); + u->thisorder = create_order(K_STUDY, f->locale, skillnames[SK_MAGIC]); i_change(&u->items, itype, study_cost(u, SK_MAGIC)); ut = test_create_unit(f, u->region); set_level(ut, SK_MAGIC, TEACHDIFFERENCE); create_mage(ut, M_GWYRRD); - ut->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(u->no)); + ut->thisorder = create_order(K_TEACH, f->locale, itoa36(u->no)); learn_inject(); teach_cmd(ut, ut->thisorder); study_cmd(u, u->thisorder); From 8a503445f61f5c568b7c4bfa0cb1a59c5892b849 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 16 Jul 2018 10:53:12 +0200 Subject: [PATCH 10/30] missing include, do not use fset --- src/.DS_Store | Bin 0 -> 8196 bytes src/automate.c | 4 ++-- src/util/.DS_Store | Bin 0 -> 6148 bytes 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 src/.DS_Store create mode 100644 src/util/.DS_Store diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f331cdebb3c73099a3bcd9d817441024e2973339 GIT binary patch literal 8196 zcmeHMTWl0n82-QQg&8{1=~`i1ob1-67O{kuAOumny>V+v8`{z>8p`g@2&2=TvNO9y zBxW_yXbe%`Pz?Iui>a3v1$|UsjQU_?UudEx>We<83BHgR<9}vmNlWR2i7`axB&!R^U%O8r^O&Jsl`%c4U= z$Uw+I$Uw+I$Uw-z&A zFY&c1E*KL5rUESc$R1Acn=<-M5gak#H)neyh)V@n_R()n2o4_zMn-T#fj2t$pYXy7 zDIdc!WFTZ`8_*pnU34lwSY)XZC(8;NnYKc+@|dx8a|+mOsmn7 zY0Qnxx~83X4jBc9aawJ3j@xtla&~^SU8zGbfTVaG1G{a$C&s#Um@T`5y7NuKO9eMn5Gp~CVZJV-PQgwO4%47}8H|j@*7+UN@;S=EV0h@4Dy1qO*U+DZd7Oh>6ePtqAWOY`&rU7(Bf8GS_+ zTA-ikH~O8f&>wUa5lD!l6-l(?Zgijv526Pt^kP2_VG?O%kcAEdkHLY966SCk&*C{e zj~DO~&f-;^!|Qkp@8UhYkB{&PzQ#AWgbKdJclaJZ;}=}PpOL!A>PYZAHWF+RG+Mn4 zA0y3-i?u!*e5*QlW39(`@7cRg8M=A3etC`1((TQ6tX-FA-?U}h&WHN0$>6%DzgUBw z=zLXqYOAWrd)P~4T=7?e5}#mE8Yfp;5b7$ic%;humeyGOuJ{ItwWbEuj`%$iuLd>Z z^>uG&Tv2OS__9~+>Q>@v9cx_js`qVI6t$KYf$zP47klej?!LE2V=u8!45M_!8)WI< zlI|CDiGHA8S+rN_FVv#}F|@H2JAaIA)%d01VJ&3fA21;Heois*-W*){`@a_y79j&61OHD3P&+U-(8u?-(yk)T z+6m4_Ig2FX%07BcxLEQbfa}-(Fy!k5hl)0-0LwnIh0}lkAz=AW*Kq&$9z|H(LJa&3 DfC<08 literal 0 HcmV?d00001 diff --git a/src/automate.c b/src/automate.c index ec4ea83d8..a74cf770b 100644 --- a/src/automate.c +++ b/src/automate.c @@ -62,13 +62,13 @@ int autostudy_init(scholar scholars[], int max_scholars, region *r) static void teaching(scholar *s, int n) { assert(n <= s->u->number); s->learn += n; - fset(s->u, UFL_LONGACTION); + s->u->flags |= UFL_LONGACTION; } static void learning(scholar *s, int n) { assert(n <= s->u->number); s->learn += n; - fset(s->u, UFL_LONGACTION); + s->u->flags |= UFL_LONGACTION; } void autostudy_run(scholar scholars[], int nscholars) diff --git a/src/util/.DS_Store b/src/util/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Sat, 21 Jul 2018 21:41:13 +0200 Subject: [PATCH 11/30] backup the reports, because sometimes I am an idiot --- process/backup-eressea | 3 +++ 1 file changed, 3 insertions(+) diff --git a/process/backup-eressea b/process/backup-eressea index 4914f5af0..4e5f69002 100755 --- a/process/backup-eressea +++ b/process/backup-eressea @@ -28,6 +28,9 @@ if [ -e orders.$TURN ]; then files="$files orders.$TURN" fi echo "backup turn $TURN, game $GAME, files: $files" +if [ -d reports ] ; then + tar cjf backup/$TURN-reports.tar.bz2 reports +fi tar cjf backup/$TURN.tar.bz2 $files echo "uploading game-$GAME/$TURN.tar.bz2" curl -s -n -T backup/$TURN.tar.bz2 https://dav.box.com/dav/Eressea/game-$GAME/$TURN.tar.bz2 From 11eb4980f84b9b26e60f7ec5530ab6ef0133b229 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 24 Jul 2018 13:57:46 +0200 Subject: [PATCH 12/30] use system-wide muttrc --- process/send-bz2-report | 2 +- process/send-zip-report | 2 +- s/preview | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/process/send-bz2-report b/process/send-bz2-report index c9fb87840..51a8113bf 100755 --- a/process/send-bz2-report +++ b/process/send-bz2-report @@ -25,6 +25,6 @@ addr=$1 subj=$2 shift 2 -mutt -F "$ERESSEA/etc/muttrc" -s "$subj" -a "$@" -- "$addr" \ +mutt -s "$subj" -a "$@" -- "$addr" \ < "$ERESSEA/server/etc/$TEMPLATE" diff --git a/process/send-zip-report b/process/send-zip-report index 89f742b82..a6bd85246 100755 --- a/process/send-zip-report +++ b/process/send-zip-report @@ -44,6 +44,6 @@ addr=$1 subject=$2 shift 2 -mutt -F "$ERESSEA/etc/muttrc" -s "$subject" -a "$@" -- "$addr" \ +mutt -s "$subject" -a "$@" -- "$addr" \ < "$TEMPLATE" || echo "Sending failed for email/report: $2/$3" diff --git a/s/preview b/s/preview index 68fe6e39f..39d5466d0 100755 --- a/s/preview +++ b/s/preview @@ -93,7 +93,7 @@ email=$(grep "faction=$1:" reports.txt | cut -d: -f2 | sed 's/email=//') echo "sending reports to $1 / $email" info=/dev/null [ -e ../email.txt ] && info=../email.txt -cat $info | mutt -F $ERESSEA/etc/muttrc -s "Testauswertung Spiel $game Partei $1" -a $zip -- $email +cat $info | mutt -s "Testauswertung Spiel $game Partei $1" -a $zip -- $email } game=0 From e7184add00e0d1e1de1629489bddd5537e88f023 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 24 Jul 2018 13:57:46 +0200 Subject: [PATCH 13/30] use system-wide muttrc --- process/send-bz2-report | 2 +- process/send-zip-report | 2 +- s/preview | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/process/send-bz2-report b/process/send-bz2-report index c9fb87840..51a8113bf 100755 --- a/process/send-bz2-report +++ b/process/send-bz2-report @@ -25,6 +25,6 @@ addr=$1 subj=$2 shift 2 -mutt -F "$ERESSEA/etc/muttrc" -s "$subj" -a "$@" -- "$addr" \ +mutt -s "$subj" -a "$@" -- "$addr" \ < "$ERESSEA/server/etc/$TEMPLATE" diff --git a/process/send-zip-report b/process/send-zip-report index 89f742b82..a6bd85246 100755 --- a/process/send-zip-report +++ b/process/send-zip-report @@ -44,6 +44,6 @@ addr=$1 subject=$2 shift 2 -mutt -F "$ERESSEA/etc/muttrc" -s "$subject" -a "$@" -- "$addr" \ +mutt -s "$subject" -a "$@" -- "$addr" \ < "$TEMPLATE" || echo "Sending failed for email/report: $2/$3" diff --git a/s/preview b/s/preview index 68fe6e39f..39d5466d0 100755 --- a/s/preview +++ b/s/preview @@ -93,7 +93,7 @@ email=$(grep "faction=$1:" reports.txt | cut -d: -f2 | sed 's/email=//') echo "sending reports to $1 / $email" info=/dev/null [ -e ../email.txt ] && info=../email.txt -cat $info | mutt -F $ERESSEA/etc/muttrc -s "Testauswertung Spiel $game Partei $1" -a $zip -- $email +cat $info | mutt -s "Testauswertung Spiel $game Partei $1" -a $zip -- $email } game=0 From ec571924037cc06b23eff622b828f865cd65a57b Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 30 Jul 2018 22:19:40 +0200 Subject: [PATCH 14/30] equip_newunits got called twice. --- scripts/eressea/equipment.lua | 1 - scripts/tests/config.lua | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/scripts/eressea/equipment.lua b/scripts/eressea/equipment.lua index 8852dfb27..c7bfe7dad 100644 --- a/scripts/eressea/equipment.lua +++ b/scripts/eressea/equipment.lua @@ -2,7 +2,6 @@ local self = {} local function equip_first(u) - equip_newunits(u) name = 'seed_' .. u.race equip_unit(u, name, 255) end diff --git a/scripts/tests/config.lua b/scripts/tests/config.lua index 316a8ccca..733a8ee07 100644 --- a/scripts/tests/config.lua +++ b/scripts/tests/config.lua @@ -30,6 +30,26 @@ function test_first_troll() assert_equal(2, u:eff_skill('perception')) end +function test_first_human() + local f = faction.create('human') + local r = region.create(0, 0, "plain") + local u = unit.create(f, r, 1) + u:equip('first_unit') + assert_not_nil(u.building) + assert_equal('castle', u.building.type) + assert_equal(10, u.building.size) +end + +function test_first_aquarian() + local f = faction.create('aquarian') + local r = region.create(0, 0, "plain") + local u = unit.create(f, r, 1) + u:equip('first_unit') + assert_not_nil(u.ship) + assert_equal('boat', u.ship.type) + assert_equal(1, u:get_skill('sailing')) +end + function test_seed_unit() local r = region.create(0, 0, "plain") local f = faction.create('human') @@ -53,3 +73,4 @@ function test_seed_elf() assert_equal('castle', u.building.type) assert_equal(10, u.building.size) end + From ee8a02c42526d0614b84cb098ff913c2abab0ba0 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 5 Jul 2018 20:06:32 +0200 Subject: [PATCH 15/30] autostudy framework --- src/CMakeLists.txt | 1 + src/automate.c | 71 +++++++++++++++++++++++++++++++++++++++++++++ src/automate.h | 28 ++++++++++++++++++ src/kernel/config.c | 3 +- src/kernel/order.c | 19 +++++++++--- src/kernel/types.h | 1 + src/kernel/unit.c | 5 ++-- src/kernel/unit.h | 2 +- src/keyword.c | 1 + src/keyword.h | 1 + src/laws.c | 2 ++ src/study.c | 8 ++--- src/study.h | 1 + 13 files changed, 130 insertions(+), 13 deletions(-) create mode 100644 src/automate.c create mode 100644 src/automate.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5b267c0d5..0ff92bf2d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -86,6 +86,7 @@ ENDIF() set (ERESSEA_SRC vortex.c + automate.c move.c piracy.c spells.c diff --git a/src/automate.c b/src/automate.c new file mode 100644 index 000000000..0855ff0e3 --- /dev/null +++ b/src/automate.c @@ -0,0 +1,71 @@ +#include + +#include "kernel/faction.h" +#include "kernel/order.h" +#include "kernel/region.h" +#include "kernel/unit.h" + +#include "util/log.h" + +#include "automate.h" +#include "keyword.h" +#include "study.h" + +#include + +typedef struct student { + unit *u; + skill_t sk; + int level; +} student; + +#define MAXSTUDENTS 128 + +int cmp_students(const void *lhs, const void *rhs) { + const student *a = (const student *)lhs; + const student *b = (const student *)rhs; + if (a->sk == b->sk) { + /* sort by level, descending: */ + return b->level - a->level; + } + /* order by skill */ + return (int)a->sk - (int)b->sk; +} + +void do_autostudy(region *r) { + unit *u; + int nstudents = 0; + student students[MAXSTUDENTS]; + + for (u = r->units; u; u = u->next) { + keyword_t kwd = getkeyword(u->thisorder); + if (kwd == K_AUTOSTUDY) { + student * st = students + nstudents; + if (++nstudents == MAXSTUDENTS) { + log_fatal("you must increase MAXSTUDENTS"); + } + st->u = u; + init_order(u->thisorder, u->faction->locale); + st->sk = getskill(u->faction->locale); + st->level = effskill_study(u, st->sk); + } + } + if (nstudents > 0) { + int i, taught = 0; + skill_t sk = NOSKILL; + student *teacher = NULL, *student = NULL; + + qsort(students, nstudents, sizeof(student), cmp_students); + for (i = 0; i != nstudents; ++i) { + if (students[i].u) { + if (sk == NOSKILL) { + sk = students[i].sk; + } + else if (sk != students[i].sk) { + continue; + } + students[i].u = NULL; + } + } + } +} diff --git a/src/automate.h b/src/automate.h new file mode 100644 index 000000000..ded8f938b --- /dev/null +++ b/src/automate.h @@ -0,0 +1,28 @@ +/* +Copyright (c) 1998-2018, Enno Rehling +Katja Zedel + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +**/ + +#pragma once + +#ifndef H_GC_AUTOMATE +#define H_GC_AUTOMATE + +struct region; + +void do_autostudy(struct region *r); + +#endif diff --git a/src/kernel/config.c b/src/kernel/config.c index ec45ddd0d..750d406f6 100644 --- a/src/kernel/config.c +++ b/src/kernel/config.c @@ -135,7 +135,8 @@ const char *parameters[MAXPARAMS] = { "GRUPPE", "PARTEITARNUNG", "BAEUME", - "ALLIANZ" + "ALLIANZ", + "AUTO" }; int findoption(const char *s, const struct locale *lang) diff --git a/src/kernel/order.c b/src/kernel/order.c index e660edf81..ac2573758 100644 --- a/src/kernel/order.c +++ b/src/kernel/order.c @@ -214,12 +214,12 @@ static int create_data(keyword_t kwd, const char *s, order_data *data; int id; - assert(kwd!=NOKEYWORD); + assert(kwd != NOKEYWORD); if (!s || *s == 0) { return 0; } - if (kwd==K_STUDY) { + if (kwd == K_STUDY || kwd == K_AUTOSTUDY) { const char * sptr = s; skill_t sk = get_skill(parse_token_depr(&sptr), lang); if (sk != SK_MAGIC && sk != NOSKILL) { @@ -332,6 +332,14 @@ order *parse_order(const char *s, const struct locale * lang) sptr = sp; } } + else if (kwd == K_STUDY) { + const char *sp = sptr; + p = parse_token_depr(&sp); + if (p && isparam(p, lang, P_AUTO)) { + kwd = K_AUTOSTUDY; + sptr = sp; + } + } if (kwd != NOKEYWORD) { order *ord = (order *)malloc(sizeof(order)); create_order_i(ord, kwd, sptr, persistent, noerror, lang); @@ -366,6 +374,7 @@ bool is_repeated(keyword_t kwd) case K_STEAL: case K_SABOTAGE: case K_STUDY: + case K_AUTOSTUDY: case K_TEACH: case K_GROW: case K_PLANT: @@ -406,6 +415,7 @@ bool is_exclusive(const order * ord) case K_STEAL: case K_SABOTAGE: case K_STUDY: + case K_AUTOSTUDY: case K_TEACH: case K_GROW: case K_PLANT: @@ -447,6 +457,7 @@ bool is_long(keyword_t kwd) case K_STEAL: case K_SABOTAGE: case K_STUDY: + case K_AUTOSTUDY: case K_TEACH: case K_GROW: case K_PLANT: @@ -541,7 +552,7 @@ keyword_t init_order(const struct order *ord, const struct locale *lang) assert(sk < MAXSKILLS); assert(lang); - assert(kwd == K_STUDY); + assert(kwd == K_STUDY || kwd == K_AUTOSTUDY); str = skillname(sk, lang); if (strchr(str, ' ') == NULL) { init_tokens_str(str); @@ -575,7 +586,7 @@ keyword_t init_order_depr(const struct order *ord) { if (ord) { keyword_t kwd = ORD_KEYWORD(ord); - assert(kwd != K_STUDY); + assert(kwd != K_STUDY && kwd != K_AUTOSTUDY); } return init_order(ord, NULL); } diff --git a/src/kernel/types.h b/src/kernel/types.h index 61f1268cc..48a4326fa 100644 --- a/src/kernel/types.h +++ b/src/kernel/types.h @@ -130,6 +130,7 @@ typedef enum { P_FACTIONSTEALTH, P_TREES, P_ALLIANCE, + P_AUTO, MAXPARAMS, NOPARAM } param_t; diff --git a/src/kernel/unit.c b/src/kernel/unit.c index 07c6c0856..770723bea 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -1310,13 +1310,12 @@ int eff_skill(const unit * u, const skill *sv, const region *r) return 0; } -int effskill_study(const unit * u, skill_t sk, const region * r) +int effskill_study(const unit * u, skill_t sk) { skill *sv = unit_skill(u, sk); if (sv && sv->level > 0) { int mlevel = sv->level; - if (!r) r = u->region; - mlevel += get_modifier(u, sv->id, sv->level, r, true); + mlevel += get_modifier(u, sv->id, sv->level, u->region, true); if (mlevel > 0) return mlevel; } diff --git a/src/kernel/unit.h b/src/kernel/unit.h index ac61affde..a6135715c 100644 --- a/src/kernel/unit.h +++ b/src/kernel/unit.h @@ -163,7 +163,7 @@ extern "C" { void clone_men(const struct unit *src, struct unit *dst, int n); /* like transfer, but do not subtract from src */ int eff_skill(const struct unit *u, const struct skill *sv, const struct region *r); - int effskill_study(const struct unit *u, skill_t sk, const struct region *r); + int effskill_study(const struct unit *u, skill_t sk); int get_modifier(const struct unit *u, skill_t sk, int level, const struct region *r, bool noitem); diff --git a/src/keyword.c b/src/keyword.c index 7519b6449..48726bcbc 100644 --- a/src/keyword.c +++ b/src/keyword.c @@ -149,5 +149,6 @@ const char *keywords[MAXKEYWORDS] = { "promote", "pay", "loot", + "autostudy", }; diff --git a/src/keyword.h b/src/keyword.h index a9273c3f5..6e4832708 100644 --- a/src/keyword.h +++ b/src/keyword.h @@ -72,6 +72,7 @@ extern "C" K_PROMOTION, K_PAY, K_LOOT, + K_AUTOSTUDY, MAXKEYWORDS, NOKEYWORD } keyword_t; diff --git a/src/laws.c b/src/laws.c index 8d75b0897..80c730c8f 100644 --- a/src/laws.c +++ b/src/laws.c @@ -26,6 +26,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include "alchemy.h" +#include "automate.h" #include "battle.h" #include "economy.h" #include "keyword.h" @@ -4001,6 +4002,7 @@ void init_processor(void) } p += 10; + add_proc_region(p, do_autostudy, "study automation"); add_proc_order(p, K_TEACH, teach_cmd, PROC_THISORDER | PROC_LONGORDER, "Lehren"); p += 10; diff --git a/src/study.c b/src/study.c index 46da90b74..6df5b5eaf 100644 --- a/src/study.c +++ b/src/study.c @@ -69,7 +69,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #define TEACH_ALL 1 #define TEACH_FRIENDS -static skill_t getskill(const struct locale *lang) +skill_t getskill(const struct locale *lang) { char token[128]; const char * s = gettoken(token, sizeof(token)); @@ -331,7 +331,7 @@ int teach_cmd(unit * teacher, struct order *ord) sk = teachskill[t]; } if (sk != NOSKILL - && effskill_study(teacher, sk, 0) - TEACHDIFFERENCE > effskill_study(student, sk, 0)) { + && effskill_study(teacher, sk) - TEACHDIFFERENCE > effskill_study(student, sk)) { teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); } } @@ -343,7 +343,7 @@ int teach_cmd(unit * teacher, struct order *ord) init_order(student->thisorder, student->faction->locale); sk = getskill(student->faction->locale); if (sk != NOSKILL - && effskill_study(teacher, sk, 0) - TEACHDIFFERENCE >= effskill(student, sk, 0)) { + && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(student, sk)) { teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); } } @@ -432,7 +432,7 @@ int teach_cmd(unit * teacher, struct order *ord) continue; } - if (effskill_study(student, sk, 0) > effskill_study(teacher, sk, 0) + if (effskill_study(student, sk, NULL) > effskill_study(teacher, sk) - TEACHDIFFERENCE) { if (feedback) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "teach_asgood", diff --git a/src/study.h b/src/study.h index b4b76bb8c..e99fb4808 100644 --- a/src/study.h +++ b/src/study.h @@ -33,6 +33,7 @@ extern "C" { 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); From f6b6904ced6fdf3460138ceef59a0f7db320d74e Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 7 Jul 2018 17:29:22 +0200 Subject: [PATCH 16/30] finish parser for K_AUTOSTUDY and P_AUTO. --- src/CMakeLists.txt | 1 + src/automate.c | 26 +++++++++++++------------- src/automate.h | 12 ++++++++++++ src/automate.test.c | 38 ++++++++++++++++++++++++++++++++++++++ src/kernel/order.c | 2 +- src/kernel/order.test.c | 31 ++++++++++++++++++++++++++++--- src/study.c | 4 ++-- src/test_eressea.c | 3 ++- 8 files changed, 97 insertions(+), 20 deletions(-) create mode 100644 src/automate.test.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0ff92bf2d..cf96cfbc2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -188,6 +188,7 @@ set(TESTS_SRC tests.c academy.test.c alchemy.test.c + automate.test.c battle.test.c creport.test.c direction.test.c diff --git a/src/automate.c b/src/automate.c index 0855ff0e3..c1d4b384b 100644 --- a/src/automate.c +++ b/src/automate.c @@ -13,14 +13,6 @@ #include -typedef struct student { - unit *u; - skill_t sk; - int level; -} student; - -#define MAXSTUDENTS 128 - int cmp_students(const void *lhs, const void *rhs) { const student *a = (const student *)lhs; const student *b = (const student *)rhs; @@ -32,16 +24,16 @@ int cmp_students(const void *lhs, const void *rhs) { return (int)a->sk - (int)b->sk; } -void do_autostudy(region *r) { +int autostudy_init(student students[], int max_students, region *r) +{ unit *u; int nstudents = 0; - student students[MAXSTUDENTS]; for (u = r->units; u; u = u->next) { keyword_t kwd = getkeyword(u->thisorder); if (kwd == K_AUTOSTUDY) { student * st = students + nstudents; - if (++nstudents == MAXSTUDENTS) { + if (++nstudents == max_students) { log_fatal("you must increase MAXSTUDENTS"); } st->u = u; @@ -50,10 +42,18 @@ void do_autostudy(region *r) { st->level = effskill_study(u, st->sk); } } + return nstudents; +} + +#define MAXSTUDENTS 128 + +void do_autostudy(region *r) { + student students[MAXSTUDENTS]; + int nstudents = autostudy_init(students, MAXSTUDENTS, r); + if (nstudents > 0) { - int i, taught = 0; + int i; skill_t sk = NOSKILL; - student *teacher = NULL, *student = NULL; qsort(students, nstudents, sizeof(student), cmp_students); for (i = 0; i != nstudents; ++i) { diff --git a/src/automate.h b/src/automate.h index ded8f938b..49b052f56 100644 --- a/src/automate.h +++ b/src/automate.h @@ -21,8 +21,20 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #ifndef H_GC_AUTOMATE #define H_GC_AUTOMATE +#include "skill.h" + struct region; +struct unit; + +typedef struct student { + struct unit *u; + skill_t sk; + int level; + int learn; +} student; void do_autostudy(struct region *r); +int autostudy_init(student students[], int max_students, struct region *r); + #endif diff --git a/src/automate.test.c b/src/automate.test.c new file mode 100644 index 000000000..d8dc8ed74 --- /dev/null +++ b/src/automate.test.c @@ -0,0 +1,38 @@ +#ifdef _MSC_VER +#include +#endif + +#include "automate.h" + +#include "kernel/faction.h" +#include "kernel/order.h" +#include "kernel/region.h" +#include "kernel/unit.h" + +#include "tests.h" + +#include + +static void test_autostudy(CuTest *tc) { + student students[4]; + unit *u1, *u2; + faction *f; + region *r; + + test_setup(); + r = test_create_plain(0, 0); + f = test_create_faction(NULL); + u1 = test_create_unit(f, r); + u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + u2 = test_create_unit(f, r); + u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + CuAssertIntEquals(tc, 2, autostudy_init(students, 4, r)); + test_teardown(); +} + +CuSuite *get_automate_suite(void) +{ + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_autostudy); + return suite; +} diff --git a/src/kernel/order.c b/src/kernel/order.c index ac2573758..342f45577 100644 --- a/src/kernel/order.c +++ b/src/kernel/order.c @@ -84,7 +84,7 @@ char* get_command(const order *ord, const struct locale *lang, char *sbuffer, si sbs_strcat(&sbs, str); if (ord->id < 0) { skill_t sk = (skill_t)(100+ord->id); - assert(kwd == K_STUDY && sk != SK_MAGIC && sk < MAXSKILLS); + assert((kwd == K_STUDY || kwd == K_AUTOSTUDY) && sk != SK_MAGIC && sk < MAXSKILLS); str = skillname(sk, lang); if (str) { if (strchr(str, ' ') == NULL) { diff --git a/src/kernel/order.test.c b/src/kernel/order.test.c index 3a2f44c56..d1f8b4176 100644 --- a/src/kernel/order.test.c +++ b/src/kernel/order.test.c @@ -113,6 +113,30 @@ static void test_parse_make(CuTest *tc) { test_teardown(); } +static void test_parse_autostudy(CuTest *tc) { + char cmd[32]; + order *ord; + struct locale * lang; + + test_setup(); + lang = get_or_create_locale("en"); + locale_setstring(lang, mkname("skill", skillnames[SK_ENTERTAINMENT]), "Entertainment"); + locale_setstring(lang, keyword(K_STUDY), "STUDY"); + locale_setstring(lang, keyword(K_AUTOSTUDY), "AUTOSTUDY"); + locale_setstring(lang, parameters[P_AUTO], "AUTO"); + init_locale(lang); + + ord = parse_order("STUDY AUTO Entertainment", lang); + CuAssertPtrNotNull(tc, ord); + CuAssertIntEquals(tc, K_AUTOSTUDY, getkeyword(ord)); + CuAssertStrEquals(tc, "AUTOSTUDY Entertainment", get_command(ord, lang, cmd, sizeof(cmd))); + + CuAssertIntEquals(tc, K_AUTOSTUDY, init_order(ord, lang)); + CuAssertStrEquals(tc, "Entertainment", getstrtoken()); + free_order(ord); + test_teardown(); +} + static void test_parse_make_temp(CuTest *tc) { char cmd[32]; order *ord; @@ -130,7 +154,7 @@ static void test_parse_make_temp(CuTest *tc) { CuAssertIntEquals(tc, K_MAKETEMP, getkeyword(ord)); CuAssertStrEquals(tc, "MAKETEMP herp", get_command(ord, lang, cmd, sizeof(cmd))); - CuAssertIntEquals(tc, K_MAKETEMP, init_order_depr(ord)); + CuAssertIntEquals(tc, K_MAKETEMP, init_order(ord, lang)); CuAssertStrEquals(tc, "herp", getstrtoken()); free_order(ord); test_teardown(); @@ -153,7 +177,7 @@ static void test_parse_maketemp(CuTest *tc) { CuAssertPtrNotNull(tc, ord); CuAssertStrEquals(tc, "MAKETEMP herp", get_command(ord, lang, cmd, sizeof(cmd))); CuAssertIntEquals(tc, K_MAKETEMP, getkeyword(ord)); - CuAssertIntEquals(tc, K_MAKETEMP, init_order_depr(ord)); + CuAssertIntEquals(tc, K_MAKETEMP, init_order(ord, lang)); CuAssertStrEquals(tc, "herp", getstrtoken()); free_order(ord); test_teardown(); @@ -167,7 +191,7 @@ static void test_init_order(CuTest *tc) { lang = get_or_create_locale("en"); ord = create_order(K_MAKETEMP, lang, "hurr durr"); - CuAssertIntEquals(tc, K_MAKETEMP, init_order_depr(ord)); + CuAssertIntEquals(tc, K_MAKETEMP, init_order(ord, lang)); CuAssertStrEquals(tc, "hurr", getstrtoken()); CuAssertStrEquals(tc, "durr", getstrtoken()); free_order(ord); @@ -548,6 +572,7 @@ CuSuite *get_order_suite(void) SUITE_ADD_TEST(suite, test_study_order_quoted); SUITE_ADD_TEST(suite, test_parse_order); SUITE_ADD_TEST(suite, test_parse_make); + SUITE_ADD_TEST(suite, test_parse_autostudy); SUITE_ADD_TEST(suite, test_parse_make_temp); SUITE_ADD_TEST(suite, test_parse_maketemp); SUITE_ADD_TEST(suite, test_init_order); diff --git a/src/study.c b/src/study.c index 6df5b5eaf..217fe9505 100644 --- a/src/study.c +++ b/src/study.c @@ -343,7 +343,7 @@ int teach_cmd(unit * teacher, struct order *ord) init_order(student->thisorder, student->faction->locale); sk = getskill(student->faction->locale); if (sk != NOSKILL - && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(student, sk)) { + && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(student, sk, NULL)) { teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); } } @@ -432,7 +432,7 @@ int teach_cmd(unit * teacher, struct order *ord) continue; } - if (effskill_study(student, sk, NULL) > effskill_study(teacher, sk) + if (effskill_study(student, sk) > effskill_study(teacher, sk) - TEACHDIFFERENCE) { if (feedback) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "teach_asgood", diff --git a/src/test_eressea.c b/src/test_eressea.c index c299fc09c..5684a8ad4 100644 --- a/src/test_eressea.c +++ b/src/test_eressea.c @@ -93,7 +93,6 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(xerewards); /* kernel */ ADD_SUITE(academy); - ADD_SUITE(alchemy); ADD_SUITE(alliance); ADD_SUITE(ally); ADD_SUITE(building); @@ -121,6 +120,8 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(spells); ADD_SUITE(unit); /* gamecode */ + ADD_SUITE(alchemy); + ADD_SUITE(automate); ADD_SUITE(battle); ADD_SUITE(calendar); ADD_SUITE(creport); From 0e754a31ac7e6ee19e801dd248445a274166f574 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 7 Jul 2018 20:56:35 +0200 Subject: [PATCH 17/30] Test, sorting units by skill and level --- src/automate.c | 5 ++++- src/automate.test.c | 22 ++++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/automate.c b/src/automate.c index c1d4b384b..f195bb6db 100644 --- a/src/automate.c +++ b/src/automate.c @@ -40,8 +40,12 @@ int autostudy_init(student students[], int max_students, region *r) init_order(u->thisorder, u->faction->locale); st->sk = getskill(u->faction->locale); st->level = effskill_study(u, st->sk); + st->learn = 0; } } + if (nstudents > 0) { + qsort(students, nstudents, sizeof(student), cmp_students); + } return nstudents; } @@ -55,7 +59,6 @@ void do_autostudy(region *r) { int i; skill_t sk = NOSKILL; - qsort(students, nstudents, sizeof(student), cmp_students); for (i = 0; i != nstudents; ++i) { if (students[i].u) { if (sk == NOSKILL) { diff --git a/src/automate.test.c b/src/automate.test.c index d8dc8ed74..3ea640cdf 100644 --- a/src/automate.test.c +++ b/src/automate.test.c @@ -15,7 +15,7 @@ static void test_autostudy(CuTest *tc) { student students[4]; - unit *u1, *u2; + unit *u1, *u2, *u3; faction *f; region *r; @@ -24,9 +24,27 @@ static void test_autostudy(CuTest *tc) { f = test_create_faction(NULL); u1 = test_create_unit(f, r); u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + test_create_unit(f, r); u2 = test_create_unit(f, r); u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); - CuAssertIntEquals(tc, 2, autostudy_init(students, 4, r)); + set_level(u2, SK_ENTERTAINMENT, 2); + u3 = test_create_unit(f, r); + u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); + students[3].u = NULL; + CuAssertIntEquals(tc, 3, autostudy_init(students, 4, r)); + CuAssertPtrEquals(tc, u2, students[0].u); + CuAssertIntEquals(tc, 2, students[0].level); + CuAssertIntEquals(tc, 0, students[0].learn); + CuAssertIntEquals(tc, SK_ENTERTAINMENT, students[0].sk); + CuAssertPtrEquals(tc, u1, students[1].u); + CuAssertIntEquals(tc, 0, students[1].level); + CuAssertIntEquals(tc, 0, students[1].learn); + CuAssertIntEquals(tc, SK_ENTERTAINMENT, students[1].sk); + CuAssertPtrEquals(tc, u3, students[2].u); + CuAssertIntEquals(tc, 0, students[2].level); + CuAssertIntEquals(tc, 0, students[2].learn); + CuAssertIntEquals(tc, SK_PERCEPTION, students[2].sk); + CuAssertPtrEquals(tc, NULL, students[3].u); test_teardown(); } From c8ebde3990b50537c8148f8dd02a2213efeb6ae0 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 9 Jul 2018 03:31:13 +0200 Subject: [PATCH 18/30] autostudy continued (WIP) --- src/academy.c | 8 ++-- src/academy.h | 2 +- src/automate.c | 101 +++++++++++++++++++++++++++++++------------- src/automate.h | 7 +-- src/automate.test.c | 64 ++++++++++++++++++++-------- src/study.c | 86 ++++++++++++++++++------------------- 6 files changed, 170 insertions(+), 98 deletions(-) diff --git a/src/academy.c b/src/academy.c index 5df13efa4..1298e9b57 100644 --- a/src/academy.c +++ b/src/academy.c @@ -33,13 +33,13 @@ void academy_teaching_bonus(struct unit *u, skill_t sk, int students) { } } -bool academy_can_teach(unit *teacher, unit *student, skill_t sk) { +bool academy_can_teach(unit *teacher, unit *scholar, skill_t sk) { const struct building_type *btype = bt_find("academy"); - if (active_building(teacher, btype) && active_building(student, btype)) { - int j = study_cost(student, sk) * 2; + if (active_building(teacher, btype) && active_building(scholar, btype)) { + int j = study_cost(scholar, sk) * 2; if (j < 50) j = 50; /* kann Einheit das zahlen? */ - return get_pooled(student, get_resourcetype(R_SILVER), GET_DEFAULT, j) >= j; + return get_pooled(scholar, get_resourcetype(R_SILVER), GET_DEFAULT, j) >= j; /* sonst nehmen sie nicht am Unterricht teil */ } return false; diff --git a/src/academy.h b/src/academy.h index f6af93748..3c496d8fa 100644 --- a/src/academy.h +++ b/src/academy.h @@ -9,7 +9,7 @@ extern "C" { struct unit; void academy_teaching_bonus(struct unit *u, skill_t sk, int academy); - bool academy_can_teach(struct unit *teacher, struct unit *student, skill_t sk); + bool academy_can_teach(struct unit *teacher, struct unit *scholar, skill_t sk); #ifdef __cplusplus } #endif diff --git a/src/automate.c b/src/automate.c index f195bb6db..997f67bd8 100644 --- a/src/automate.c +++ b/src/automate.c @@ -13,9 +13,10 @@ #include -int cmp_students(const void *lhs, const void *rhs) { - const student *a = (const student *)lhs; - const student *b = (const student *)rhs; +int cmp_scholars(const void *lhs, const void *rhs) +{ + const scholar *a = (const scholar *)lhs; + const scholar *b = (const scholar *)rhs; if (a->sk == b->sk) { /* sort by level, descending: */ return b->level - a->level; @@ -24,17 +25,17 @@ int cmp_students(const void *lhs, const void *rhs) { return (int)a->sk - (int)b->sk; } -int autostudy_init(student students[], int max_students, region *r) +int autostudy_init(scholar scholars[], int max_scholars, region *r) { unit *u; - int nstudents = 0; + int nscholars = 0; for (u = r->units; u; u = u->next) { keyword_t kwd = getkeyword(u->thisorder); if (kwd == K_AUTOSTUDY) { - student * st = students + nstudents; - if (++nstudents == max_students) { - log_fatal("you must increase MAXSTUDENTS"); + scholar * st = scholars + nscholars; + if (++nscholars == max_scholars) { + log_fatal("you must increase MAXSCHOLARS"); } st->u = u; init_order(u->thisorder, u->faction->locale); @@ -43,31 +44,73 @@ int autostudy_init(student students[], int max_students, region *r) st->learn = 0; } } - if (nstudents > 0) { - qsort(students, nstudents, sizeof(student), cmp_students); + if (nscholars > 0) { + qsort(scholars, nscholars, sizeof(scholar), cmp_scholars); } - return nstudents; + return nscholars; } -#define MAXSTUDENTS 128 - -void do_autostudy(region *r) { - student students[MAXSTUDENTS]; - int nstudents = autostudy_init(students, MAXSTUDENTS, r); - - if (nstudents > 0) { - int i; - skill_t sk = NOSKILL; - - for (i = 0; i != nstudents; ++i) { - if (students[i].u) { - if (sk == NOSKILL) { - sk = students[i].sk; +void autostudy_run(scholar scholars[], int nscholars) +{ + int i, t, s, ti = 0, si = 0, ts = 0, tt = 0; + skill_t sk = scholars[0].sk; + for (i = ti; i != nscholars && scholars[i].sk == sk; ++i) { + int mint; + ts += scholars[i].u->number; /* count total scholars */ + mint = (ts + 10) / 11; /* need a minimum of ceil(ts/11) teachers */ + while (mint>tt) { + tt += scholars[si++].u->number; + } + } + /* now si splits the teachers and students 1:10 */ + /* first student must be 2 levels below first teacher: */ + while (scholars[ti].level - TEACHDIFFERENCE > scholars[si].level) { + tt += scholars[si++].u->number; + } + /* invariant: unit ti can still teach i students */ + i = scholars[ti].u->number * 10; + for (t = ti, s = si; t != si && s != nscholars; ++t) { + /* TODO: is there no constant for students per teacher? */ + while (s != nscholars) { + int n = scholars[s].u->number; + scholars[s].learn += n; + if (i >= n) { + i -= n; + scholars[s].learn += n; + /* next student */ + } + else { + scholars[s].learn += i; + /* go to next suitable teacher */ + do { + ++t; } - else if (sk != students[i].sk) { - continue; - } - students[i].u = NULL; + while (scholars[t].level - TEACHDIFFERENCE < scholars[s].level); + } + } + } +} + +#define MAXSCHOLARS 128 + +void do_autostudy(region *r) +{ + scholar scholars[MAXSCHOLARS]; + int nscholars = autostudy_init(scholars, MAXSCHOLARS, r); + + if (nscholars > 0) { + int i; + skill_t sk = NOSKILL; + + for (i = 0; i != nscholars; ++i) { + if (scholars[i].u) { + if (sk == NOSKILL) { + sk = scholars[i].sk; + } + else if (sk != scholars[i].sk) { + continue; + } + scholars[i].u = NULL; } } } diff --git a/src/automate.h b/src/automate.h index 49b052f56..285fc6638 100644 --- a/src/automate.h +++ b/src/automate.h @@ -26,15 +26,16 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. struct region; struct unit; -typedef struct student { +typedef struct scholar { struct unit *u; skill_t sk; int level; int learn; -} student; +} scholar; void do_autostudy(struct region *r); -int autostudy_init(student students[], int max_students, struct region *r); +int autostudy_init(scholar scholars[], int max_scholars, struct region *r); +void autostudy_run(scholar scholars[], int nscholars); #endif diff --git a/src/automate.test.c b/src/automate.test.c index 3ea640cdf..7404a2d9c 100644 --- a/src/automate.test.c +++ b/src/automate.test.c @@ -13,8 +13,8 @@ #include -static void test_autostudy(CuTest *tc) { - student students[4]; +static void test_autostudy_init(CuTest *tc) { + scholar scholars[4]; unit *u1, *u2, *u3; faction *f; region *r; @@ -30,27 +30,55 @@ static void test_autostudy(CuTest *tc) { set_level(u2, SK_ENTERTAINMENT, 2); u3 = test_create_unit(f, r); u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); - students[3].u = NULL; - CuAssertIntEquals(tc, 3, autostudy_init(students, 4, r)); - CuAssertPtrEquals(tc, u2, students[0].u); - CuAssertIntEquals(tc, 2, students[0].level); - CuAssertIntEquals(tc, 0, students[0].learn); - CuAssertIntEquals(tc, SK_ENTERTAINMENT, students[0].sk); - CuAssertPtrEquals(tc, u1, students[1].u); - CuAssertIntEquals(tc, 0, students[1].level); - CuAssertIntEquals(tc, 0, students[1].learn); - CuAssertIntEquals(tc, SK_ENTERTAINMENT, students[1].sk); - CuAssertPtrEquals(tc, u3, students[2].u); - CuAssertIntEquals(tc, 0, students[2].level); - CuAssertIntEquals(tc, 0, students[2].learn); - CuAssertIntEquals(tc, SK_PERCEPTION, students[2].sk); - CuAssertPtrEquals(tc, NULL, students[3].u); + scholars[3].u = NULL; + CuAssertIntEquals(tc, 3, autostudy_init(scholars, 4, r)); + CuAssertPtrEquals(tc, u2, scholars[0].u); + CuAssertIntEquals(tc, 2, scholars[0].level); + CuAssertIntEquals(tc, 0, scholars[0].learn); + CuAssertIntEquals(tc, SK_ENTERTAINMENT, scholars[0].sk); + CuAssertPtrEquals(tc, u1, scholars[1].u); + CuAssertIntEquals(tc, 0, scholars[1].level); + CuAssertIntEquals(tc, 0, scholars[1].learn); + CuAssertIntEquals(tc, SK_ENTERTAINMENT, scholars[1].sk); + CuAssertPtrEquals(tc, u3, scholars[2].u); + CuAssertIntEquals(tc, 0, scholars[2].level); + CuAssertIntEquals(tc, 0, scholars[2].learn); + CuAssertIntEquals(tc, SK_PERCEPTION, scholars[2].sk); + CuAssertPtrEquals(tc, NULL, scholars[3].u); test_teardown(); } +static void test_autostudy_run(CuTest *tc) { + scholar scholars[4]; + unit *u1, *u2, *u3; + faction *f; + region *r; + + test_setup(); + r = test_create_plain(0, 0); + f = test_create_faction(NULL); + u1 = test_create_unit(f, r); + u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + set_number(u1, 2); + u2 = test_create_unit(f, r); + u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + set_number(u2, 10); + set_level(u2, SK_ENTERTAINMENT, 2); + u3 = test_create_unit(f, r); + u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); + set_number(u3, 20); + scholars[3].u = NULL; + CuAssertIntEquals(tc, 3, autostudy_init(scholars, 4, r)); + autostudy_run(scholars, 3); + CuAssertIntEquals(tc, 0, scholars[0].learn); + CuAssertIntEquals(tc, 20, scholars[1].learn); + CuAssertIntEquals(tc, 30, scholars[2].learn); +} + CuSuite *get_automate_suite(void) { CuSuite *suite = CuSuiteNew(); - SUITE_ADD_TEST(suite, test_autostudy); + SUITE_ADD_TEST(suite, test_autostudy_init); + SUITE_ADD_TEST(suite, test_autostudy_run); return suite; } diff --git a/src/study.c b/src/study.c index 217fe9505..0dc4386bc 100644 --- a/src/study.c +++ b/src/study.c @@ -195,37 +195,37 @@ const attrib_type at_learning = { #define EXPERIENCEDAYS 10 -static int study_days(unit * student, skill_t sk) +static int study_days(unit * scholar, skill_t sk) { int speed = STUDYDAYS; - if (u_race(student)->study_speed) { - speed += u_race(student)->study_speed[sk]; + if (u_race(scholar)->study_speed) { + speed += u_race(scholar)->study_speed[sk]; if (speed < STUDYDAYS) { - skill *sv = unit_skill(student, sk); + skill *sv = unit_skill(scholar, sk); if (sv == 0) { speed = STUDYDAYS; } } } - return student->number * speed; + return scholar->number * speed; } static int -teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk, +teach_unit(unit * teacher, unit * scholar, int nteaching, skill_t sk, bool report, int *academy_students) { teaching_info *teach = NULL; attrib *a; int students; - if (magic_lowskill(student)) { + if (magic_lowskill(scholar)) { cmistake(teacher, teacher->thisorder, 292, MSG_EVENT); return 0; } - students = student->number; + students = scholar->number; /* subtract already taught students */ - a = a_find(student->attribs, &at_learning); + a = a_find(scholar->attribs, &at_learning); if (a != NULL) { teach = (teaching_info *)a->data.v; students -= teach->students; @@ -235,18 +235,18 @@ teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk, if (students > 0) { if (teach == NULL) { - a = a_add(&student->attribs, a_new(&at_learning)); + a = a_add(&scholar->attribs, a_new(&at_learning)); teach = (teaching_info *)a->data.v; } selist_push(&teach->teachers, teacher); teach->days += students * STUDYDAYS; teach->students += students; - if (student->building && teacher->building == student->building) { + if (scholar->building && teacher->building == scholar->building) { /* Solange Akademien groessenbeschraenkt sind, sollte Lehrer und * Student auch in unterschiedlichen Gebaeuden stehen duerfen */ /* FIXME comment contradicts implementation */ - if (academy_can_teach(teacher, student, sk)) { + if (academy_can_teach(teacher, scholar, sk)) { /* Jeder Schueler zusaetzlich +10 Tage wenn in Uni. */ teach->days += students * EXPERIENCEDAYS; /* learning erhoehen */ /* Lehrer zusaetzlich +1 Tag pro Schueler. */ @@ -304,7 +304,7 @@ int teach_cmd(unit * teacher, struct order *ord) #if TEACH_ALL if (getparam(teacher->faction->locale) == P_ANY) { skill_t sk; - unit *student; + unit *scholar; skill_t teachskill[MAXSKILLS]; int t = 0; @@ -313,15 +313,15 @@ int teach_cmd(unit * teacher, struct order *ord) teachskill[t] = getskill(teacher->faction->locale); } while (sk != NOSKILL); - for (student = r->units; teaching > 0 && student; student = student->next) { - if (LongHunger(student)) { + for (scholar = r->units; teaching > 0 && scholar; scholar = scholar->next) { + if (LongHunger(scholar)) { continue; } - else if (student->faction == teacher->faction) { - if (getkeyword(student->thisorder) == K_STUDY) { + else if (scholar->faction == teacher->faction) { + if (getkeyword(scholar->thisorder) == K_STUDY) { /* Input ist nun von student->thisorder !! */ - init_order(student->thisorder, student->faction->locale); - sk = getskill(student->faction->locale); + init_order(scholar->thisorder, scholar->faction->locale); + sk = getskill(scholar->faction->locale); if (sk != NOSKILL && teachskill[0] != NOSKILL) { for (t = 0; teachskill[t] != NOSKILL; ++t) { if (sk == teachskill[t]) { @@ -331,20 +331,20 @@ int teach_cmd(unit * teacher, struct order *ord) sk = teachskill[t]; } if (sk != NOSKILL - && effskill_study(teacher, sk) - TEACHDIFFERENCE > effskill_study(student, sk)) { - teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); + && effskill_study(teacher, sk) - TEACHDIFFERENCE > effskill_study(scholar, sk)) { + teaching -= teach_unit(teacher, scholar, teaching, sk, true, &academy_students); } } } #ifdef TEACH_FRIENDS - else if (alliedunit(teacher, student->faction, HELP_GUARD)) { - if (getkeyword(student->thisorder) == K_STUDY) { + else if (alliedunit(teacher, scholar->faction, HELP_GUARD)) { + if (getkeyword(scholar->thisorder) == K_STUDY) { /* Input ist nun von student->thisorder !! */ - init_order(student->thisorder, student->faction->locale); - sk = getskill(student->faction->locale); + init_order(scholar->thisorder, scholar->faction->locale); + sk = getskill(scholar->faction->locale); if (sk != NOSKILL - && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(student, sk, NULL)) { - teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); + && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(scholar, sk, NULL)) { + teaching -= teach_unit(teacher, scholar, teaching, sk, true, &academy_students); } } } @@ -363,15 +363,15 @@ int teach_cmd(unit * teacher, struct order *ord) while (!parser_end()) { skill_t sk; - unit *student; + unit *scholar; bool feedback; - getunit(r, teacher->faction, &student); + getunit(r, teacher->faction, &scholar); ++count; /* Falls die Unit nicht gefunden wird, Fehler melden */ - if (!student) { + if (!scholar) { char tbuf[20]; const char *uid; const char *token; @@ -403,8 +403,8 @@ int teach_cmd(unit * teacher, struct order *ord) continue; } - feedback = teacher->faction == student->faction - || alliedunit(student, teacher->faction, HELP_GUARD); + feedback = teacher->faction == scholar->faction + || alliedunit(scholar, teacher->faction, HELP_GUARD); /* Neuen Befehl zusammenbauen. TEMP-Einheiten werden automatisch in * ihre neuen Nummern uebersetzt. */ @@ -412,31 +412,31 @@ int teach_cmd(unit * teacher, struct order *ord) strncat(zOrder, " ", sz - 1); --sz; } - sz -= str_strlcpy(zOrder + 4096 - sz, itoa36(student->no), sz); + sz -= str_strlcpy(zOrder + 4096 - sz, itoa36(scholar->no), sz); - if (getkeyword(student->thisorder) != K_STUDY) { + if (getkeyword(scholar->thisorder) != K_STUDY) { ADDMSG(&teacher->faction->msgs, - msg_feedback(teacher, ord, "teach_nolearn", "student", student)); + msg_feedback(teacher, ord, "teach_nolearn", "student", scholar)); continue; } /* Input ist nun von student->thisorder !! */ parser_pushstate(); - init_order(student->thisorder, student->faction->locale); - sk = getskill(student->faction->locale); + init_order(scholar->thisorder, scholar->faction->locale); + sk = getskill(scholar->faction->locale); parser_popstate(); if (sk == NOSKILL) { ADDMSG(&teacher->faction->msgs, - msg_feedback(teacher, ord, "teach_nolearn", "student", student)); + msg_feedback(teacher, ord, "teach_nolearn", "student", scholar)); continue; } - if (effskill_study(student, sk) > effskill_study(teacher, sk) + if (effskill_study(scholar, sk) > effskill_study(teacher, sk) - TEACHDIFFERENCE) { if (feedback) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "teach_asgood", - "student", student)); + "student", scholar)); } continue; } @@ -444,18 +444,18 @@ int teach_cmd(unit * teacher, struct order *ord) /* ist der Magier schon spezialisiert, so versteht er nur noch * Lehrer seines Gebietes */ sc_mage *mage1 = get_mage_depr(teacher); - sc_mage *mage2 = get_mage_depr(student); + sc_mage *mage2 = get_mage_depr(scholar); if (mage2 && mage1 && mage2->magietyp != M_GRAY && mage1->magietyp != mage2->magietyp) { if (feedback) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, - "error_different_magic", "target", student)); + "error_different_magic", "target", scholar)); } continue; } } sk_academy = sk; - teaching -= teach_unit(teacher, student, teaching, sk, false, &academy_students); + teaching -= teach_unit(teacher, scholar, teaching, sk, false, &academy_students); } new_order = create_order(K_TEACH, teacher->faction->locale, "%s", zOrder); replace_order(&teacher->orders, ord, new_order); From b4cb1dfe8d48ed3a60586350017f15ac4280b2a6 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 11 Jul 2018 21:03:00 +0200 Subject: [PATCH 19/30] finished autostudy --- src/automate.c | 110 +++++++++++++++++++++++++++++++------------- src/automate.h | 2 + src/automate.test.c | 34 ++++++++++++-- 3 files changed, 111 insertions(+), 35 deletions(-) diff --git a/src/automate.c b/src/automate.c index 997f67bd8..bf130acf3 100644 --- a/src/automate.c +++ b/src/automate.c @@ -12,6 +12,7 @@ #include "study.h" #include +#include int cmp_scholars(const void *lhs, const void *rhs) { @@ -50,44 +51,89 @@ int autostudy_init(scholar scholars[], int max_scholars, region *r) return nscholars; } +static void teaching(scholar *s, int n) { + assert(n <= s->u->number); + s->learn += n; +} + +static void learning(scholar *s, int n) { + assert(n <= s->u->number); + s->learn += n; +} + void autostudy_run(scholar scholars[], int nscholars) { - int i, t, s, ti = 0, si = 0, ts = 0, tt = 0; - skill_t sk = scholars[0].sk; - for (i = ti; i != nscholars && scholars[i].sk == sk; ++i) { - int mint; - ts += scholars[i].u->number; /* count total scholars */ - mint = (ts + 10) / 11; /* need a minimum of ceil(ts/11) teachers */ - while (mint>tt) { - tt += scholars[si++].u->number; - } - } - /* now si splits the teachers and students 1:10 */ - /* first student must be 2 levels below first teacher: */ - while (scholars[ti].level - TEACHDIFFERENCE > scholars[si].level) { - tt += scholars[si++].u->number; - } - /* invariant: unit ti can still teach i students */ - i = scholars[ti].u->number * 10; - for (t = ti, s = si; t != si && s != nscholars; ++t) { - /* TODO: is there no constant for students per teacher? */ - while (s != nscholars) { - int n = scholars[s].u->number; - scholars[s].learn += n; - if (i >= n) { - i -= n; - scholars[s].learn += n; - /* next student */ + int ti = 0; + while (ti != nscholars) { + skill_t sk = scholars[ti].sk; + int t, s, se, ts = 0, tt = 0, si = ti; + for (se = ti; se != nscholars && scholars[se].sk == sk; ++se) { + int mint; + ts += scholars[se].u->number; /* count total scholars */ + mint = (ts + 10) / 11; /* need a minimum of ceil(ts/11) teachers */ + for (; mint > tt && si != nscholars; ++si) { + tt += scholars[si].u->number; } - else { - scholars[s].learn += i; - /* go to next suitable teacher */ - do { - ++t; + } + /* now si splits the teachers and students 1:10 */ + /* first student must be 2 levels below first teacher: */ + for (; si != se && scholars[ti].level - TEACHDIFFERENCE > scholars[si].level; ++si) { + tt += scholars[si].u->number; + } + if (si == se) { + /* there are no students, so standard learning only */ + for (t = ti; t != se; ++t) { + learning(scholars + t, scholars[t].u->number); + } + } + else { + /* invariant: unit ti can still teach i students */ + int i = scholars[ti].u->number * STUDENTS_PER_TEACHER; + /* invariant: unit si has n students that can still be taught */ + int n = scholars[si].u->number; + for (t = ti, s = si; t != si && s != se; ) { + if (i > n) { + /* t has more than enough teaching capacity for s */ + i -= n; + teaching(scholars + s, n); + learning(scholars + s, scholars[s].u->number); + /* next student, please: */ + if (++s == se) { + continue; + } + n = scholars[s].u->number; + } + else { + /* s gets partial credit and we need a new teacher */ + teaching(scholars + s, i); + + /* we are done with this teacher. any remaining people are regular learners: */ + if (scholars[t].u->number > 1) { + /* remain = number - ceil(taught/10); */ + int remain = (STUDENTS_PER_TEACHER * scholars[t].u->number - i + STUDENTS_PER_TEACHER - 1) / STUDENTS_PER_TEACHER; + learning(scholars + t, remain); + } + + /* we want a new teacher for s. if any exists, it's next in the sequence. */ + if (++t == si) { + continue; + } + if (scholars[t].level - TEACHDIFFERENCE < scholars[s].level) { + /* next teacher cannot teach, we must skip students. */ + do { + learning(scholars + s, (n - i)); + i = 0; + if (++s == se) { + continue; + } + n = scholars[s].u->number; + } while (scholars[t].level - TEACHDIFFERENCE < scholars[s].level); + } + i = scholars[t].u->number * STUDENTS_PER_TEACHER; } - while (scholars[t].level - TEACHDIFFERENCE < scholars[s].level); } } + ti = se; } } diff --git a/src/automate.h b/src/automate.h index 285fc6638..61fed6866 100644 --- a/src/automate.h +++ b/src/automate.h @@ -33,6 +33,8 @@ typedef struct scholar { int learn; } scholar; +#define STUDENTS_PER_TEACHER 10 + void do_autostudy(struct region *r); int autostudy_init(scholar scholars[], int max_scholars, struct region *r); diff --git a/src/automate.test.c b/src/automate.test.c index 7404a2d9c..999cb25d4 100644 --- a/src/automate.test.c +++ b/src/automate.test.c @@ -60,19 +60,46 @@ static void test_autostudy_run(CuTest *tc) { u1 = test_create_unit(f, r); u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); set_number(u1, 2); + set_level(u1, SK_ENTERTAINMENT, 2); u2 = test_create_unit(f, r); u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); set_number(u2, 10); - set_level(u2, SK_ENTERTAINMENT, 2); u3 = test_create_unit(f, r); u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); - set_number(u3, 20); + set_number(u3, 15); scholars[3].u = NULL; CuAssertIntEquals(tc, 3, autostudy_init(scholars, 4, r)); autostudy_run(scholars, 3); CuAssertIntEquals(tc, 0, scholars[0].learn); CuAssertIntEquals(tc, 20, scholars[1].learn); - CuAssertIntEquals(tc, 30, scholars[2].learn); + CuAssertIntEquals(tc, 15, scholars[2].learn); +} + +static void test_autostudy_run_noteachers(CuTest *tc) { + scholar scholars[4]; + unit *u1, *u2, *u3; + faction *f; + region *r; + + test_setup(); + r = test_create_plain(0, 0); + f = test_create_faction(NULL); + u1 = test_create_unit(f, r); + u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_LUMBERJACK]); + set_number(u1, 2); + set_level(u1, SK_ENTERTAINMENT, 2); + u2 = test_create_unit(f, r); + u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + set_number(u2, 10); + u3 = test_create_unit(f, r); + u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); + set_number(u3, 15); + scholars[3].u = NULL; + CuAssertIntEquals(tc, 3, autostudy_init(scholars, 4, r)); + autostudy_run(scholars, 3); + CuAssertIntEquals(tc, 2, scholars[0].learn); + CuAssertIntEquals(tc, 10, scholars[1].learn); + CuAssertIntEquals(tc, 15, scholars[2].learn); } CuSuite *get_automate_suite(void) @@ -80,5 +107,6 @@ CuSuite *get_automate_suite(void) CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_autostudy_init); SUITE_ADD_TEST(suite, test_autostudy_run); + SUITE_ADD_TEST(suite, test_autostudy_run_noteachers); return suite; } From b0fb1e503224eb55968360c02b60bb3f63ae80cb Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 11 Jul 2018 21:07:31 +0200 Subject: [PATCH 20/30] actually do the learning. --- src/automate.c | 22 +++++----------------- src/study.c | 6 +++--- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/automate.c b/src/automate.c index bf130acf3..e08d7a625 100644 --- a/src/automate.c +++ b/src/automate.c @@ -142,22 +142,10 @@ void autostudy_run(scholar scholars[], int nscholars) void do_autostudy(region *r) { scholar scholars[MAXSCHOLARS]; - int nscholars = autostudy_init(scholars, MAXSCHOLARS, r); - - if (nscholars > 0) { - int i; - skill_t sk = NOSKILL; - - for (i = 0; i != nscholars; ++i) { - if (scholars[i].u) { - if (sk == NOSKILL) { - sk = scholars[i].sk; - } - else if (sk != scholars[i].sk) { - continue; - } - scholars[i].u = NULL; - } - } + int i, nscholars = autostudy_init(scholars, MAXSCHOLARS, r); + autostudy_run(scholars, nscholars); + for (i = 0; i != nscholars; ++i) { + int days = STUDYDAYS * scholars[i].learn; + learn_skill(scholars[i].u, scholars[i].sk, days); } } diff --git a/src/study.c b/src/study.c index 0dc4386bc..bd29fc2a8 100644 --- a/src/study.c +++ b/src/study.c @@ -766,9 +766,6 @@ int study_cmd(unit * u, order * ord) days *= 2; } - if (fval(u, UFL_HUNGER)) - days /= 2; - learn_skill(u, sk, days); if (a != NULL) { if (teach->teachers) { @@ -832,6 +829,9 @@ void learn_skill(unit *u, skill_t sk, int days) { int leveldays = STUDYDAYS * u->number; int weeks = 0; + if (fval(u, UFL_HUNGER)) { + days /= 2; + } assert(sk >= 0 && sk < MAXSKILLS); if (inject_learn_fun) { inject_learn_fun(u, sk, days); From 2bb3e7601d96ae4703e53816332f6e99f199c9b1 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 5 Aug 2018 06:42:31 +0200 Subject: [PATCH 21/30] merge conflict --- clibs | 2 +- src/automate.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clibs b/clibs index d86c85254..66a891b38 160000 --- a/clibs +++ b/clibs @@ -1 +1 @@ -Subproject commit d86c8525489d7f11b7ba13e101bb59ecf160b871 +Subproject commit 66a891b383f1a51bb0d4e5cf002530f7f70bf7f4 diff --git a/src/automate.c b/src/automate.c index e08d7a625..2f188f352 100644 --- a/src/automate.c +++ b/src/automate.c @@ -14,7 +14,7 @@ #include #include -int cmp_scholars(const void *lhs, const void *rhs) +static int cmp_scholars(const void *lhs, const void *rhs) { const scholar *a = (const scholar *)lhs; const scholar *b = (const scholar *)rhs; From 36b7104ce37b92aa6e455e7ee6ebda667590661e Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 14 Jul 2018 15:56:44 +0200 Subject: [PATCH 22/30] extract long_order_allowed for use in autostudy. --- src/automate.c | 26 ++++++++++++++++++-------- src/automate.test.c | 2 ++ src/laws.c | 31 ++++++++++++++++++++----------- src/laws.h | 1 + src/laws.test.c | 30 ++++++++++++++++++++++++++++++ src/study.test.c | 20 ++++++++++---------- 6 files changed, 81 insertions(+), 29 deletions(-) diff --git a/src/automate.c b/src/automate.c index 2f188f352..ec4ea83d8 100644 --- a/src/automate.c +++ b/src/automate.c @@ -1,6 +1,7 @@ #include #include "kernel/faction.h" +#include "kernel/messages.h" #include "kernel/order.h" #include "kernel/region.h" #include "kernel/unit.h" @@ -9,6 +10,7 @@ #include "automate.h" #include "keyword.h" +#include "laws.h" #include "study.h" #include @@ -34,15 +36,21 @@ int autostudy_init(scholar scholars[], int max_scholars, region *r) for (u = r->units; u; u = u->next) { keyword_t kwd = getkeyword(u->thisorder); if (kwd == K_AUTOSTUDY) { - scholar * st = scholars + nscholars; - if (++nscholars == max_scholars) { - log_fatal("you must increase MAXSCHOLARS"); + 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; + } + else { + ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder, "error_race_nolearn", "race", + u_race(u))); } - 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; } } if (nscholars > 0) { @@ -54,11 +62,13 @@ int autostudy_init(scholar scholars[], int max_scholars, region *r) static void teaching(scholar *s, int n) { assert(n <= s->u->number); s->learn += n; + fset(s->u, UFL_LONGACTION); } static void learning(scholar *s, int n) { assert(n <= s->u->number); s->learn += n; + fset(s->u, UFL_LONGACTION); } void autostudy_run(scholar scholars[], int nscholars) diff --git a/src/automate.test.c b/src/automate.test.c index 999cb25d4..a77376668 100644 --- a/src/automate.test.c +++ b/src/automate.test.c @@ -73,6 +73,7 @@ static void test_autostudy_run(CuTest *tc) { CuAssertIntEquals(tc, 0, scholars[0].learn); CuAssertIntEquals(tc, 20, scholars[1].learn); CuAssertIntEquals(tc, 15, scholars[2].learn); + test_teardown(); } static void test_autostudy_run_noteachers(CuTest *tc) { @@ -100,6 +101,7 @@ static void test_autostudy_run_noteachers(CuTest *tc) { CuAssertIntEquals(tc, 2, scholars[0].learn); CuAssertIntEquals(tc, 10, scholars[1].learn); CuAssertIntEquals(tc, 15, scholars[2].learn); + test_teardown(); } CuSuite *get_automate_suite(void) diff --git a/src/laws.c b/src/laws.c index 80c730c8f..68f2894b3 100644 --- a/src/laws.c +++ b/src/laws.c @@ -3637,6 +3637,24 @@ void add_proc_unit(int priority, void(*process) (unit *), const char *name) } } +bool long_order_allowed(const unit *u) +{ + const region *r = u->region; + if (fval(u, UFL_LONGACTION)) { + /* this message was already given in laws.update_long_order + cmistake(u, ord, 52, MSG_PRODUCE); + */ + return false; + } + else if (fval(r->terrain, SEA_REGION) + && u_race(u) != get_race(RC_AQUARIAN) + && !(u_race(u)->flags & RCF_SWIM)) { + /* error message disabled by popular demand */ + return false; + } + return true; +} + /* per priority, execute processors in order from PR_GLOBAL down to PR_ORDER */ void process(void) { @@ -3706,16 +3724,7 @@ void process(void) cmistake(u, ord, 224, MSG_MAGIC); ord = NULL; } - else if (fval(u, UFL_LONGACTION)) { - /* this message was already given in laws.update_long_order - cmistake(u, ord, 52, MSG_PRODUCE); - */ - ord = NULL; - } - else if (fval(r->terrain, SEA_REGION) - && u_race(u) != get_race(RC_AQUARIAN) - && !(u_race(u)->flags & RCF_SWIM)) { - /* error message disabled by popular demand */ + else if (!long_order_allowed(u)) { ord = NULL; } } @@ -4157,7 +4166,7 @@ void update_subscriptions(void) /** determine if unit can be seen by faction * @param f -- the observiong faction * @param u -- the unit that is observed - * @param r -- the region that u is obesrved in (see below) + * @param r -- the region that u is obesrved from (see below) * @param m -- terrain modifier to stealth * * r kann != u->region sein, wenn es um Durchreisen geht, diff --git a/src/laws.h b/src/laws.h index ae2c712ca..0b5db17c7 100755 --- a/src/laws.h +++ b/src/laws.h @@ -66,6 +66,7 @@ extern "C" { void update_long_order(struct unit *u); void sinkships(struct region * r); void do_enter(struct region *r, bool is_final_attempt); + bool long_order_allowed(const struct unit *u); int password_cmd(struct unit *u, struct order *ord); int banner_cmd(struct unit *u, struct order *ord); diff --git a/src/laws.test.c b/src/laws.test.c index 8d13b7c81..c7c7a6682 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -1759,6 +1759,34 @@ static void test_nmr_timeout(CuTest *tc) { test_teardown(); } +static void test_long_orders(CuTest *tc) { + unit *u; + + test_setup(); + u = test_create_unit(test_create_faction(NULL), test_create_plain(0, 0)); + CuAssertTrue(tc, long_order_allowed(u)); + u->flags |= UFL_LONGACTION; + CuAssertTrue(tc, !long_order_allowed(u)); + test_teardown(); +} + +static void test_long_order_on_ocean(CuTest *tc) { + unit *u; + race * rc; + + test_setup(); + rc = test_create_race("pikachu"); + u = test_create_unit(test_create_faction(rc), test_create_ocean(0, 0)); + CuAssertTrue(tc, !long_order_allowed(u)); + rc->flags |= RCF_SWIM; + CuAssertTrue(tc, long_order_allowed(u)); + + rc = test_create_race("aquarian"); + u = test_create_unit(test_create_faction(rc), u->region); + CuAssertTrue(tc, long_order_allowed(u)); + test_teardown(); +} + CuSuite *get_laws_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -1831,6 +1859,8 @@ CuSuite *get_laws_suite(void) SUITE_ADD_TEST(suite, test_cansee_ring); SUITE_ADD_TEST(suite, test_cansee_sphere); SUITE_ADD_TEST(suite, test_nmr_timeout); + SUITE_ADD_TEST(suite, test_long_orders); + SUITE_ADD_TEST(suite, test_long_order_on_ocean); return suite; } diff --git a/src/study.test.c b/src/study.test.c index c1d0b0841..a63be7cf1 100644 --- a/src/study.test.c +++ b/src/study.test.c @@ -89,15 +89,15 @@ static void setup_teacher(study_fixture *fix, skill_t sk) { setup_locale(lang); fix->u = test_create_unit(f, r); assert(fix->u); - fix->u->thisorder = create_order(K_STUDY, f->locale, "%s", skillnames[sk]); + fix->u->thisorder = create_order(K_STUDY, f->locale, skillnames[sk]); fix->teachers[0] = test_create_unit(f, r); assert(fix->teachers[0]); - fix->teachers[0]->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(fix->u->no)); + fix->teachers[0]->thisorder = create_order(K_TEACH, f->locale, itoa36(fix->u->no)); fix->teachers[1] = test_create_unit(f, r); assert(fix->teachers[1]); - fix->teachers[1]->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(fix->u->no)); + fix->teachers[1]->thisorder = create_order(K_TEACH, f->locale, itoa36(fix->u->no)); test_clear_messages(f); } @@ -110,7 +110,7 @@ static void test_study_no_teacher(CuTest *tc) { CuAssertPtrNotNull(tc, sv = unit_skill(fix.u, SK_CROSSBOW)); CuAssertIntEquals(tc, 1, sv->level); CuAssertIntEquals(tc, 2, sv->weeks); - CuAssertPtrEquals(tc, 0, test_get_last_message(fix.u->faction->msgs)); + CuAssertPtrEquals(tc, NULL, test_get_last_message(fix.u->faction->msgs)); test_teardown(); } @@ -121,7 +121,7 @@ static void test_study_with_teacher(CuTest *tc) { setup_teacher(&fix, SK_CROSSBOW); set_level(fix.teachers[0], SK_CROSSBOW, TEACHDIFFERENCE); teach_cmd(fix.teachers[0], fix.teachers[0]->thisorder); - CuAssertPtrEquals(tc, 0, test_get_last_message(fix.u->faction->msgs)); + CuAssertPtrEquals(tc, NULL, test_get_last_message(fix.u->faction->msgs)); study_cmd(fix.u, fix.u->thisorder); CuAssertPtrNotNull(tc, sv = unit_skill(fix.u, SK_CROSSBOW)); CuAssertIntEquals(tc, 1, sv->level); @@ -288,7 +288,7 @@ static void test_academy_bonus(CuTest *tc) { u1 = test_create_unit(u->faction, u->region); u3 = test_create_unit(u->faction, u->region); u0->thisorder = create_order(K_TEACH, loc, "%s %s", itoa36(u3->no), itoa36(u1->no)); - u->thisorder = create_order(K_TEACH, loc, "%s", itoa36(u1->no)); + u->thisorder = create_order(K_TEACH, loc, itoa36(u1->no)); u1->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]); u3->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]); @@ -405,7 +405,7 @@ static void test_study_magic(CuTest *tc) { f = test_create_faction(NULL); lang = f->locale; u = test_create_unit(f, test_create_region(0, 0, NULL)); - u->thisorder = create_order(K_STUDY, lang, "%s", skillnames[SK_MAGIC]); + u->thisorder = create_order(K_STUDY, lang, skillnames[SK_MAGIC]); itype = test_create_silver(); CuAssertIntEquals(tc, -1, study_cmd(u, u->thisorder)); @@ -423,7 +423,7 @@ static void test_study_magic(CuTest *tc) { CuAssertIntEquals(tc, M_GWYRRD, f->magiegebiet); CuAssertIntEquals(tc, 0, i_get(u->items, itype)); CuAssertPtrNotNull(tc, get_mage_depr(u)); - CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "error65")); + CuAssertPtrEquals(tc, NULL, test_find_messagetype(f->msgs, "error65")); CuAssertIntEquals(tc, M_GWYRRD, get_mage_depr(u)->magietyp); test_teardown(); @@ -491,12 +491,12 @@ static void test_teach_magic(CuTest *tc) { f = test_create_faction(NULL); f->magiegebiet = M_GWYRRD; u = test_create_unit(f, test_create_region(0, 0, NULL)); - u->thisorder = create_order(K_STUDY, f->locale, "%s", skillnames[SK_MAGIC]); + u->thisorder = create_order(K_STUDY, f->locale, skillnames[SK_MAGIC]); i_change(&u->items, itype, study_cost(u, SK_MAGIC)); ut = test_create_unit(f, u->region); set_level(ut, SK_MAGIC, TEACHDIFFERENCE); create_mage(ut, M_GWYRRD); - ut->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(u->no)); + ut->thisorder = create_order(K_TEACH, f->locale, itoa36(u->no)); learn_inject(); teach_cmd(ut, ut->thisorder); study_cmd(u, u->thisorder); From 22d0fe5693bd943aa5d8c911dba328f3a8fb75a1 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 16 Jul 2018 10:53:12 +0200 Subject: [PATCH 23/30] missing include, do not use fset --- src/.DS_Store | Bin 0 -> 8196 bytes src/automate.c | 4 ++-- src/util/.DS_Store | Bin 0 -> 6148 bytes 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 src/.DS_Store create mode 100644 src/util/.DS_Store diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f331cdebb3c73099a3bcd9d817441024e2973339 GIT binary patch literal 8196 zcmeHMTWl0n82-QQg&8{1=~`i1ob1-67O{kuAOumny>V+v8`{z>8p`g@2&2=TvNO9y zBxW_yXbe%`Pz?Iui>a3v1$|UsjQU_?UudEx>We<83BHgR<9}vmNlWR2i7`axB&!R^U%O8r^O&Jsl`%c4U= z$Uw+I$Uw+I$Uw-z&A zFY&c1E*KL5rUESc$R1Acn=<-M5gak#H)neyh)V@n_R()n2o4_zMn-T#fj2t$pYXy7 zDIdc!WFTZ`8_*pnU34lwSY)XZC(8;NnYKc+@|dx8a|+mOsmn7 zY0Qnxx~83X4jBc9aawJ3j@xtla&~^SU8zGbfTVaG1G{a$C&s#Um@T`5y7NuKO9eMn5Gp~CVZJV-PQgwO4%47}8H|j@*7+UN@;S=EV0h@4Dy1qO*U+DZd7Oh>6ePtqAWOY`&rU7(Bf8GS_+ zTA-ikH~O8f&>wUa5lD!l6-l(?Zgijv526Pt^kP2_VG?O%kcAEdkHLY966SCk&*C{e zj~DO~&f-;^!|Qkp@8UhYkB{&PzQ#AWgbKdJclaJZ;}=}PpOL!A>PYZAHWF+RG+Mn4 zA0y3-i?u!*e5*QlW39(`@7cRg8M=A3etC`1((TQ6tX-FA-?U}h&WHN0$>6%DzgUBw z=zLXqYOAWrd)P~4T=7?e5}#mE8Yfp;5b7$ic%;humeyGOuJ{ItwWbEuj`%$iuLd>Z z^>uG&Tv2OS__9~+>Q>@v9cx_js`qVI6t$KYf$zP47klej?!LE2V=u8!45M_!8)WI< zlI|CDiGHA8S+rN_FVv#}F|@H2JAaIA)%d01VJ&3fA21;Heois*-W*){`@a_y79j&61OHD3P&+U-(8u?-(yk)T z+6m4_Ig2FX%07BcxLEQbfa}-(Fy!k5hl)0-0LwnIh0}lkAz=AW*Kq&$9z|H(LJa&3 DfC<08 literal 0 HcmV?d00001 diff --git a/src/automate.c b/src/automate.c index ec4ea83d8..a74cf770b 100644 --- a/src/automate.c +++ b/src/automate.c @@ -62,13 +62,13 @@ int autostudy_init(scholar scholars[], int max_scholars, region *r) static void teaching(scholar *s, int n) { assert(n <= s->u->number); s->learn += n; - fset(s->u, UFL_LONGACTION); + s->u->flags |= UFL_LONGACTION; } static void learning(scholar *s, int n) { assert(n <= s->u->number); s->learn += n; - fset(s->u, UFL_LONGACTION); + s->u->flags |= UFL_LONGACTION; } void autostudy_run(scholar scholars[], int nscholars) diff --git a/src/util/.DS_Store b/src/util/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Tue, 21 Aug 2018 22:25:04 +0200 Subject: [PATCH 24/30] =?UTF-8?q?BUG=202477=20Null-Personen=20Meldungen=20?= =?UTF-8?q?sind=20unn=C3=B6tig.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/monsters.c | 67 ++++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/src/monsters.c b/src/monsters.c index bbeb483b8..a2d2b2121 100644 --- a/src/monsters.c +++ b/src/monsters.c @@ -1016,37 +1016,42 @@ static void eaten_by_monster(unit * u) { /* adjustment for smaller worlds */ double multi = RESOURCE_QUANTITY * newterrain(T_PLAIN)->size / 10000.0; - int n = 0; - int horse = -1; const resource_type *rhorse = get_resourcetype(R_HORSE); const race *rc = u_race(u); - int scare; + int p = rpeasants(u->region); - scare = rc_scare(rc); - if (scare>0) { - n = rng_int() % scare * u->number; - } else { - n = rng_int() % (u->number / 20 + 1); - horse = 0; - } - horse = horse ? i_get(u->items, rhorse->itype) : 0; + if (p > 0) { + int horse = -1; + int scare = rc_scare(rc); + int n = 0; - n = (int)(n * multi); - if (n > 0) { - - n = lovar(n); - - if (n > 0) { - int p = rpeasants(u->region); - if (p < n) n = p; - deathcounts(u->region, n); - rsetpeasants(u->region, rpeasants(u->region) - n); - ADDMSG(&u->region->msgs, msg_message("eatpeasants", "unit amount", u, n)); + if (scare > 0) { + n = rng_int() % scare * u->number; + } + else { + n = rng_int() % (u->number / 20 + 1); + horse = 0; + } + + horse = horse ? i_get(u->items, rhorse->itype) : 0; + if (horse > 0) { + i_change(&u->items, rhorse->itype, -horse); + ADDMSG(&u->region->msgs, msg_message("eathorse", "unit amount", u, horse)); + } + + n = (int)(n * multi); + if (n > 0) { + n = lovar(n); + + if (p < n) n = p; + if (n > 0) { + if (n > 0) { + deathcounts(u->region, n); + rsetpeasants(u->region, rpeasants(u->region) - n); + ADDMSG(&u->region->msgs, msg_message("eatpeasants", "unit amount", u, n)); + } + } } - } - if (horse > 0) { - i_change(&u->items, rhorse->itype, -horse); - ADDMSG(&u->region->msgs, msg_message("eathorse", "unit amount", u, horse)); } } @@ -1059,10 +1064,12 @@ static void absorbed_by_monster(unit * u) if (n > 0) { int p = rpeasants(u->region); if (p < n) n = p; - rsetpeasants(u->region, rpeasants(u->region) - n); - scale_number(u, u->number + n); - ADDMSG(&u->region->msgs, msg_message("absorbpeasants", - "unit race amount", u, u_race(u), n)); + if (n > 0) { + rsetpeasants(u->region, rpeasants(u->region) - n); + scale_number(u, u->number + n); + ADDMSG(&u->region->msgs, msg_message("absorbpeasants", + "unit race amount", u, u_race(u), n)); + } } } } From 876113d991ea14be521c9f09eccf6e90a503bcf4 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 3 Sep 2018 20:43:17 +0200 Subject: [PATCH 25/30] fix multiple coverity defects in parsers. --- src/exparse.c | 442 ++++++++++++++++++++++++---------------------- src/util/pofile.c | 9 +- 2 files changed, 238 insertions(+), 213 deletions(-) diff --git a/src/exparse.c b/src/exparse.c index 9f49459a9..71efe0667 100644 --- a/src/exparse.c +++ b/src/exparse.c @@ -65,14 +65,17 @@ typedef struct parseinfo { void *object; } parseinfo; -static int xml_strcmp(const XML_Char *xs, const char *cs) { - return strcmp(xs, cs); +static bool xml_strequal(const XML_Char *xs, const char *cs) { + if (xs && cs) { + return strcmp(xs, cs) == 0; + } + return false; } static bool xml_bool(const XML_Char *val) { - if (xml_strcmp(val, "yes") == 0) return true; - if (xml_strcmp(val, "true") == 0) return true; - if (xml_strcmp(val, "1") == 0) return true; + if (xml_strequal(val, "yes")) return true; + if (xml_strequal(val, "true")) return true; + if (xml_strequal(val, "1")) return true; return false; } @@ -94,7 +97,7 @@ static variant xml_fraction(const XML_Char *val) { const XML_Char *attr_get(const XML_Char **attr, const char *key) { int i; for (i = 0; attr[i]; i += 2) { - if (xml_strcmp(attr[i], key) == 0) { + if (xml_strequal(attr[i], key)) { return attr[i + 1]; } } @@ -134,7 +137,7 @@ static bool handle_flag(int *flags, const XML_Char **pair, const char *names[]) for (i = 0; names[i]; ++i) { const char * name = names[i]; if (name[0] == '!') { - if (xml_strcmp(pair[0], name+1) == 0) { + if (xml_strequal(pair[0], name+1)) { if (xml_bool(pair[1])) { *flags &= ~(1 << i); } @@ -144,7 +147,7 @@ static bool handle_flag(int *flags, const XML_Char **pair, const char *names[]) return true; } } - else if (xml_strcmp(pair[0], name) == 0) { + else if (xml_strequal(pair[0], name)) { if (xml_bool(pair[1])) { *flags |= (1 << i); } @@ -165,15 +168,15 @@ static void handle_resource(parseinfo *pi, const XML_Char *el, const XML_Char ** bool material = false; (void)el; for (i = 0; attr[i]; i += 2) { - if (xml_strcmp(attr[i], "name") == 0) { + if (xml_strequal(attr[i], "name")) { name = attr[i + 1]; } - else if (xml_strcmp(attr[i], "appearance") == 0) { + else if (xml_strequal(attr[i], "appearance")) { /* TODO: appearance should be a property of item, not resource */ appear = attr[i + 1]; flags |= RTF_ITEM; } - else if (xml_strcmp(attr[i], "material") == 0) { + else if (xml_strequal(attr[i], "material")) { material = xml_bool(attr[i + 1]); } else if (!handle_flag(&flags, attr + i, flag_names)) { @@ -206,22 +209,22 @@ static void handle_item(parseinfo *pi, const XML_Char *el, const XML_Char **attr } for (i = 0; attr[i]; i += 2) { char buffer[64]; - if (xml_strcmp(attr[i], "weight") == 0) { + if (xml_strequal(attr[i], "weight")) { itype->weight = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "capacity") == 0) { + else if (xml_strequal(attr[i], "capacity")) { itype->capacity = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "score") == 0) { + else if (xml_strequal(attr[i], "score")) { itype->score = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "allow") == 0) { + else if (xml_strequal(attr[i], "allow")) { size_t len = strlen(attr[i + 1]); assert(len < sizeof(buffer)); memcpy(buffer, attr[i + 1], len + 1); itype->mask_allow = rc_get_mask(buffer); } - else if (xml_strcmp(attr[i], "deny") == 0) { + else if (xml_strequal(attr[i], "deny")) { size_t len = strlen(attr[i + 1]); assert(len < sizeof(buffer)); memcpy(buffer, attr[i + 1], len + 1); @@ -244,16 +247,16 @@ static void handle_armor(parseinfo *pi, const XML_Char *el, const XML_Char **att armor_type *atype = new_armortype(itype, 0.0, frac_zero, 0, 0); int i, flags = 0; for (i = 0; attr[i]; i += 2) { - if (xml_strcmp(attr[i], "penalty") == 0) { + if (xml_strequal(attr[i], "penalty")) { atype->penalty = xml_float(attr[i + 1]); } - else if (xml_strcmp(attr[i], "projectile") == 0) { + else if (xml_strequal(attr[i], "projectile")) { atype->projectile = xml_float(attr[i + 1]); } - else if (xml_strcmp(attr[i], "ac") == 0) { + else if (xml_strequal(attr[i], "ac")) { atype->prot = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "magres") == 0) { + else if (xml_strequal(attr[i], "magres")) { atype->magres = xml_fraction(attr[i + 1]); } else if (!handle_flag(&flags, attr + i, flag_names)) { @@ -270,19 +273,19 @@ static void handle_weapon(parseinfo *pi, const XML_Char *el, const XML_Char **at weapon_type *wtype = new_weapontype(itype, 0, frac_zero, NULL, 0, 0, 0, NOSKILL); int i, flags = 0; for (i = 0; attr[i]; i += 2) { - if (xml_strcmp(attr[i], "offmod") == 0) { + if (xml_strequal(attr[i], "offmod")) { wtype->offmod = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "defmod") == 0) { + else if (xml_strequal(attr[i], "defmod")) { wtype->defmod = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "reload") == 0) { + else if (xml_strequal(attr[i], "reload")) { wtype->reload = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "skill") == 0) { + else if (xml_strequal(attr[i], "skill")) { wtype->skill = findskill(attr[i + 1]); } - else if (xml_strcmp(attr[i], "magres") == 0) { + else if (xml_strequal(attr[i], "magres")) { wtype->magres = xml_fraction(attr[i + 1]);; } else if (!handle_flag(&flags, attr + i, flag_names)) { @@ -296,7 +299,7 @@ static int msg_nargs; static char * msg_args[MSG_MAXARGS]; static void end_messages(parseinfo *pi, const XML_Char *el) { - if (xml_strcmp(el, "message") == 0) { + if (xml_strequal(el, "message")) { int i; struct message_type *mtype = (struct message_type *)pi->object; assert(mtype); @@ -312,23 +315,23 @@ static void end_messages(parseinfo *pi, const XML_Char *el) { } msg_nargs = 0; } - else if (xml_strcmp(el, "messages") == 0) { + else if (xml_strequal(el, "messages")) { pi->type = EXP_UNKNOWN; } } static void start_messages(parseinfo *pi, const XML_Char *el, const XML_Char **attr) { - if (xml_strcmp(el, "arg") == 0) { + if (xml_strequal(el, "arg")) { int i; const XML_Char *name = NULL, *type = NULL; assert(msg_nargs < MSG_MAXARGS); for (i = 0; attr[i]; i += 2) { const XML_Char *key = attr[i], *val = attr[i + 1]; - if (xml_strcmp(key, "name") == 0) { + if (xml_strequal(key, "name")) { name = val; } - else if (xml_strcmp(key, "type") == 0) { + else if (xml_strequal(key, "type")) { type = val; } else { @@ -341,15 +344,15 @@ static void start_messages(parseinfo *pi, const XML_Char *el, const XML_Char **a msg_args[msg_nargs++] = str_strdup(zBuffer); } } - else if (xml_strcmp(el, "message") == 0) { + else if (xml_strequal(el, "message")) { const XML_Char *name = NULL, *section = NULL; int i; for (i = 0; attr[i]; i += 2) { const XML_Char *key = attr[i], *val = attr[i + 1]; - if (xml_strcmp(key, "name") == 0) { + if (xml_strequal(key, "name")) { name = val; } - else if (xml_strcmp(key, "section") == 0) { + else if (xml_strequal(key, "section")) { section = val; } else { @@ -360,7 +363,7 @@ static void start_messages(parseinfo *pi, const XML_Char *el, const XML_Char **a pi->object = mt_new(name, section); } } - else if (xml_strcmp(el, "type") != 0) { + else if (!xml_strequal(el, "type")) { handle_bad_input(pi, el, NULL); } } @@ -374,7 +377,7 @@ static void start_spells(parseinfo *pi, const XML_Char *el, const XML_Char **att "far", "variable", "ocean", "ship", "los", "unittarget", "shiptarget", "buildingtarget", "regiontarget", "globaltarget", NULL }; - if (xml_strcmp(el, "resource") == 0) { + if (xml_strequal(el, "resource")) { spell_component *spc; int i; @@ -385,20 +388,20 @@ static void start_spells(parseinfo *pi, const XML_Char *el, const XML_Char **att memset(spc, 0, sizeof(spell_component)); for (i = 0; attr[i]; i += 2) { const XML_Char *key = attr[i], *val = attr[i + 1]; - if (xml_strcmp(key, "name") == 0) { + if (xml_strequal(key, "name")) { spc->type = rt_get_or_create(val); } - else if (xml_strcmp(key, "amount") == 0) { + else if (xml_strequal(key, "amount")) { spc->amount = xml_int(val); } - else if (xml_strcmp(key, "cost") == 0) { - if (xml_strcmp(val, "level") == 0) { + else if (xml_strequal(key, "cost")) { + if (xml_strequal(val, "level")) { spc->cost = SPC_LEVEL; } - else if (xml_strcmp(val, "linear") == 0) { + else if (xml_strequal(val, "linear")) { spc->cost = SPC_LINEAR; } - else if (xml_strcmp(val, "fixed") == 0) { + else if (xml_strequal(val, "fixed")) { spc->cost = SPC_FIX; } else { @@ -410,25 +413,25 @@ static void start_spells(parseinfo *pi, const XML_Char *el, const XML_Char **att } } } - else if (xml_strcmp(el, "spell") == 0) { + else if (xml_strequal(el, "spell")) { spell *sp; const XML_Char *name = NULL, *syntax = NULL, *parameter = NULL; int i, rank = 0, flags = 0; for (i = 0; attr[i]; i += 2) { const XML_Char *key = attr[i], *val = attr[i + 1]; - if (xml_strcmp(key, "name") == 0) { + if (xml_strequal(key, "name")) { name = val; } - else if (xml_strcmp(key, "syntax") == 0) { + else if (xml_strequal(key, "syntax")) { syntax = val; } - else if (xml_strcmp(key, "parameters") == 0) { + else if (xml_strequal(key, "parameters")) { parameter = val; } - else if (xml_strcmp(key, "rank") == 0) { + else if (xml_strequal(key, "rank")) { rank = xml_int(val); } - else if (xml_strcmp(key, "combat") == 0) { + else if (xml_strequal(key, "combat")) { int mode = PRECOMBATSPELL; int k = xml_int(val); if (k > 1 && k <= 3) { @@ -453,7 +456,7 @@ static void start_spells(parseinfo *pi, const XML_Char *el, const XML_Char **att static void start_spellbooks(parseinfo *pi, const XML_Char *el, const XML_Char **attr) { spellbook * sb = (spellbook *)pi->object; - if (xml_strcmp(el, "spellbook") == 0) { + if (xml_strequal(el, "spellbook")) { const XML_Char *name = attr_get(attr, "name"); if (name) { @@ -463,16 +466,16 @@ static void start_spellbooks(parseinfo *pi, const XML_Char *el, const XML_Char * handle_bad_input(pi, el, NULL); } } - else if (xml_strcmp(el, "entry") == 0) { + else if (xml_strequal(el, "entry")) { int i, level = 0; const XML_Char *name = NULL; assert(sb); for (i = 0; attr[i]; i += 2) { - if (xml_strcmp(attr[i], "spell") == 0) { + if (xml_strequal(attr[i], "spell")) { name = attr[i + 1]; } - else if (xml_strcmp(attr[i], "level") == 0) { + else if (xml_strequal(attr[i], "level")) { level = xml_int(attr[i + 1]); } else { @@ -495,22 +498,22 @@ static void start_weapon(parseinfo *pi, const XML_Char *el, const XML_Char **att resource_type *rtype = (resource_type *)pi->object; assert(rtype && rtype->wtype); - if (xml_strcmp(el, "function") == 0) { + if (xml_strequal(el, "function")) { const XML_Char *name = NULL, *type = NULL; int i; for (i = 0; attr[i]; i += 2) { - if (xml_strcmp(attr[i], "name") == 0) { + if (xml_strequal(attr[i], "name")) { type = attr[i + 1]; } - else if (xml_strcmp(attr[i], "value") == 0) { + else if (xml_strequal(attr[i], "value")) { name = attr[i + 1]; } else { handle_bad_input(pi, el, attr[i]); } } - if (type && xml_strcmp(type, "attack") == 0) { + if (name && type && xml_strequal(type, "attack")) { pf_generic fun = get_function(name); rtype->wtype->attack = (wtype_attack)fun; } @@ -518,49 +521,49 @@ static void start_weapon(parseinfo *pi, const XML_Char *el, const XML_Char **att handle_bad_input(pi, el, attr[i]); } } - else if (xml_strcmp(el, "modifier") == 0) { + else if (xml_strequal(el, "modifier")) { const XML_Char *type = NULL; int i, flags = 0, race_mask = 0; int value = 0; for (i = 0; attr[i]; i += 2) { - if (xml_strcmp(attr[i], "type") == 0) { + if (xml_strequal(attr[i], "type")) { type = attr[i + 1]; } - else if (xml_strcmp(attr[i], "value") == 0) { + else if (xml_strequal(attr[i], "value")) { value = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "races") == 0) { + else if (xml_strequal(attr[i], "races")) { char list[64]; - strcpy(list, attr[i + 1]); + str_strlcpy(list, attr[i + 1], sizeof(list)); race_mask = rc_get_mask(list); } - else if (xml_strcmp(attr[i], "offensive") == 0) { + else if (xml_strequal(attr[i], "offensive")) { if (xml_bool(attr[i + 1])) { flags |= WMF_OFFENSIVE; } } - else if (xml_strcmp(attr[i], "defensive") == 0) { + else if (xml_strequal(attr[i], "defensive")) { if (xml_bool(attr[i + 1])) { flags |= WMF_DEFENSIVE; } } - else if (xml_strcmp(attr[i], "walking") == 0) { + else if (xml_strequal(attr[i], "walking")) { if (xml_bool(attr[i + 1])) { flags |= WMF_WALKING; } } - else if (xml_strcmp(attr[i], "riding") == 0) { + else if (xml_strequal(attr[i], "riding")) { if (xml_bool(attr[i + 1])) { flags |= WMF_RIDING; } } - else if (xml_strcmp(attr[i], "against_riding") == 0) { + else if (xml_strequal(attr[i], "against_riding")) { if (xml_bool(attr[i + 1])) { flags |= WMF_AGAINST_RIDING; } } - else if (xml_strcmp(attr[i], "against_walking") == 0) { + else if (xml_strequal(attr[i], "against_walking")) { if (xml_bool(attr[i + 1])) { flags |= WMF_AGAINST_WALKING; } @@ -576,13 +579,13 @@ static void start_weapon(parseinfo *pi, const XML_Char *el, const XML_Char **att ++nwmods; /* weapon modifiers */ - if (xml_strcmp(type, "missile_target") == 0) { + if (xml_strequal(type, "missile_target")) { flags |= WMF_MISSILE_TARGET; } - else if (xml_strcmp(type, "damage") == 0) { + else if (xml_strequal(type, "damage")) { flags |= WMF_DAMAGE; } - else if (xml_strcmp(type, "skill") == 0) { + else if (xml_strequal(type, "skill")) { flags |= WMF_SKILL; } else { @@ -596,17 +599,17 @@ static void start_weapon(parseinfo *pi, const XML_Char *el, const XML_Char **att handle_bad_input(pi, el, NULL); } } - else if (xml_strcmp(el, "damage") == 0) { + else if (xml_strequal(el, "damage")) { weapon_type *wtype = rtype->wtype; int i, pos = 0; for (i = 0; attr[i]; i += 2) { - if (xml_strcmp(attr[i], "type") == 0) { + if (xml_strequal(attr[i], "type")) { /* damage vs. rider(1) or not(0)? */ - if (xml_strcmp(attr[i + 1], "rider") == 0) { + if (xml_strequal(attr[i + 1], "rider")) { pos = 1; } } - else if (xml_strcmp(attr[i], "value") == 0) { + else if (xml_strequal(attr[i], "value")) { wtype->damage[pos] = str_strdup(attr[i + 1]); } else { @@ -627,10 +630,10 @@ static void handle_requirement(parseinfo *pi, const XML_Char *el, const XML_Char req = reqs + nreqs; req->number = 1; for (i = 0; attr[i]; i += 2) { - if (xml_strcmp(attr[i], "type") == 0) { + if (xml_strequal(attr[i], "type")) { req->rtype = rt_get_or_create(attr[i + 1]); } - else if (xml_strcmp(attr[i], "quantity") == 0) { + else if (xml_strequal(attr[i], "quantity")) { req->number = xml_int(attr[i + 1]); } else { @@ -648,13 +651,13 @@ static void handle_maintenance(parseinfo *pi, const XML_Char *el, const XML_Char up = upkeep + nupkeep; memset(up, 0, sizeof(maintenance)); for (i = 0; attr[i]; i += 2) { - if (xml_strcmp(attr[i], "type") == 0) { + if (xml_strequal(attr[i], "type")) { up->rtype = rt_get_or_create(attr[i + 1]); } - else if (xml_strcmp(attr[i], "amount") == 0) { + else if (xml_strequal(attr[i], "amount")) { up->number = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "variable") == 0) { + else if (xml_strequal(attr[i], "variable")) { if (xml_bool(attr[i + 1])) { up->flags |= MTF_VARIABLE; } @@ -692,42 +695,57 @@ static void handle_modifier(parseinfo *pi, const XML_Char *el, const XML_Char ** assert(nrmods < RMOD_MAX); ++nrmods; for (i = 0; attr[i]; i += 2) { - if (xml_strcmp(attr[i], "type") == 0) { + if (xml_strequal(attr[i], "type")) { type = attr[i + 1]; } - else if (xml_strcmp(attr[i], "building") == 0) { + else if (xml_strequal(attr[i], "building")) { mod->btype = bt_get_or_create(attr[i + 1]); } - else if (xml_strcmp(attr[i], "skill") == 0) { + else if (xml_strequal(attr[i], "skill")) { sk = findskill(attr[i + 1]); } - else if (xml_strcmp(attr[i], "races") == 0) { + else if (xml_strequal(attr[i], "races")) { char list[64]; - strcpy(list, attr[i + 1]); + str_strlcpy(list, attr[i + 1], sizeof(list)); mod->race_mask = rc_get_mask(list); } - else if (xml_strcmp(attr[i], "value") == 0) { + else if (xml_strequal(attr[i], "value")) { value = attr[i + 1]; } else { handle_bad_input(pi, el, attr[i]); } } - if (xml_strcmp(type, "skill") == 0) { + if (xml_strequal(type, "skill")) { mod->type = RMT_PROD_SKILL; - mod->value.sa[0] = (short)sk; - mod->value.sa[1] = (short)xml_int(value); + if (value) { + mod->value.sa[0] = (short)sk; + mod->value.sa[1] = (short)xml_int(value); + } + else { + handle_bad_input(pi, el, type); + } } - else if (xml_strcmp(type, "require") == 0) { + else if (xml_strequal(type, "require")) { mod->type = RMT_PROD_REQUIRE; } - else if (xml_strcmp(type, "material") == 0) { + else if (xml_strequal(type, "material")) { mod->type = RMT_PROD_SAVE; - mod->value = xml_fraction(value); + if (value) { + mod->value = xml_fraction(value); + } + else { + handle_bad_input(pi, el, type); + } } - else if (xml_strcmp(type, "save") == 0) { + else if (xml_strequal(type, "save")) { mod->type = RMT_USE_SAVE; - mod->value = xml_fraction(value); + if (value) { + mod->value = xml_fraction(value); + } + else { + handle_bad_input(pi, el, type); + } } else { handle_bad_input(pi, el, type); @@ -741,19 +759,19 @@ static construction *parse_construction(parseinfo *pi, const XML_Char *el, const con->minskill = -1; con->reqsize = 1; for (i = 0; attr[i]; i += 2) { - if (xml_strcmp(attr[i], "skill") == 0) { + if (xml_strequal(attr[i], "skill")) { con->skill = findskill(attr[i + 1]); } - else if (xml_strcmp(attr[i], "maxsize") == 0) { + else if (xml_strequal(attr[i], "maxsize")) { con->maxsize = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "reqsize") == 0) { + else if (xml_strequal(attr[i], "reqsize")) { con->reqsize = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "minskill") == 0) { + else if (xml_strequal(attr[i], "minskill")) { con->minskill = xml_int(attr[i + 1]); } - else if (stage != NULL && xml_strcmp(attr[i], "name") == 0) { + else if (stage != NULL && xml_strequal(attr[i], "name")) { /* only building stages have names */ stage->name = str_strdup(attr[i + 1]); } @@ -767,24 +785,24 @@ static construction *parse_construction(parseinfo *pi, const XML_Char *el, const static void start_resources(parseinfo *pi, const XML_Char *el, const XML_Char **attr) { resource_type *rtype = (resource_type *)pi->object; - if (xml_strcmp(el, "resource") == 0) { + if (xml_strequal(el, "resource")) { handle_resource(pi, el, attr); } else if (rtype) { - if (xml_strcmp(el, "item") == 0) { + if (xml_strequal(el, "item")) { assert(rtype); handle_item(pi, el, attr); } - else if (xml_strcmp(el, "function") == 0) { + else if (xml_strequal(el, "function")) { const XML_Char *name = NULL; pf_generic fun = NULL; int i; for (i = 0; attr[i]; i += 2) { - if (xml_strcmp(attr[i], "name") == 0) { + if (xml_strequal(attr[i], "name")) { name = attr[i + 1]; } - else if (xml_strcmp(attr[i], "value") == 0) { + else if (xml_strequal(attr[i], "value")) { fun = get_function(attr[i + 1]); } else { @@ -794,39 +812,39 @@ static void start_resources(parseinfo *pi, const XML_Char *el, const XML_Char ** assert(rtype); if (name && fun) { - if (xml_strcmp(name, "change") == 0) { + if (xml_strequal(name, "change")) { rtype->uchange = (rtype_uchange)fun; } - else if (xml_strcmp(name, "name") == 0) { + else if (xml_strequal(name, "name")) { rtype->name = (rtype_name)fun; } - else if (xml_strcmp(name, "attack") == 0) { + else if (xml_strequal(name, "attack")) { assert(rtype->wtype); rtype->wtype->attack = (wtype_attack)fun; } } } - else if (xml_strcmp(el, "modifier") == 0) { + else if (xml_strequal(el, "modifier")) { handle_modifier(pi, el, attr); } else if (rtype->itype) { item_type *itype = rtype->itype; - if (xml_strcmp(el, "construction") == 0) { + if (xml_strequal(el, "construction")) { itype->construction = parse_construction(pi, el, attr); } - else if (xml_strcmp(el, "requirement") == 0) { + else if (xml_strequal(el, "requirement")) { assert(itype->construction); handle_requirement(pi, el, attr); } - else if (xml_strcmp(el, "luxury") == 0) { + else if (xml_strequal(el, "luxury")) { int price = atoi(attr_get(attr, "price")); assert(price > 0); rtype->ltype = new_luxurytype(itype, price); } - else if (xml_strcmp(el, "potion") == 0) { + else if (xml_strequal(el, "potion")) { int i, level = 0; for (i = 0; attr[i]; i += 2) { - if (xml_strcmp(attr[i], "level") == 0) { + if (xml_strequal(attr[i], "level")) { level = xml_int(attr[i + 1]); } else { @@ -835,10 +853,10 @@ static void start_resources(parseinfo *pi, const XML_Char *el, const XML_Char ** } new_potiontype(itype, level); } - else if (xml_strcmp(el, "armor") == 0) { + else if (xml_strequal(el, "armor")) { handle_armor(pi, el, attr); } - else if (xml_strcmp(el, "weapon") == 0) { + else if (xml_strequal(el, "weapon")) { pi->type = EXP_WEAPON; handle_weapon(pi, el, attr); } @@ -857,7 +875,7 @@ static void start_resources(parseinfo *pi, const XML_Char *el, const XML_Char ** static void start_ships(parseinfo *pi, const XML_Char *el, const XML_Char **attr) { const char *flag_names[] = { "opensea", "fly", "nocoast", "speedy", NULL }; - if (xml_strcmp(el, "ship") == 0) { + if (xml_strequal(el, "ship")) { const XML_Char *name; name = attr_get(attr, "name"); @@ -866,42 +884,42 @@ static void start_ships(parseinfo *pi, const XML_Char *el, const XML_Char **attr int i, flags = SFL_DEFAULT; for (i = 0; attr[i]; i += 2) { - if (xml_strcmp(attr[i], "range") == 0) { + if (xml_strequal(attr[i], "range")) { stype->range = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "maxrange") == 0) { + else if (xml_strequal(attr[i], "maxrange")) { stype->range_max = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "cabins") == 0) { + else if (xml_strequal(attr[i], "cabins")) { stype->cabins = PERSON_WEIGHT * xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "cargo") == 0) { + else if (xml_strequal(attr[i], "cargo")) { stype->cargo = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "combat") == 0) { + else if (xml_strequal(attr[i], "combat")) { stype->combat = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "fishing") == 0) { + else if (xml_strequal(attr[i], "fishing")) { stype->fishing = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "cptskill") == 0) { + else if (xml_strequal(attr[i], "cptskill")) { stype->cptskill = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "minskill") == 0) { + else if (xml_strequal(attr[i], "minskill")) { stype->minskill = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "sumskill") == 0) { + else if (xml_strequal(attr[i], "sumskill")) { stype->sumskill = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "damage") == 0) { + else if (xml_strequal(attr[i], "damage")) { stype->damage = xml_float(attr[i + 1]); } - else if (xml_strcmp(attr[i], "storm") == 0) { + else if (xml_strequal(attr[i], "storm")) { stype->storm = xml_float(attr[i + 1]); } else if (!handle_flag(&flags, attr + i, flag_names)) { /* we already handled the name earlier */ - if (xml_strcmp(attr[i], "name") != 0) { + if (!xml_strequal(attr[i], "name")) { handle_bad_input(pi, el, attr[i]); } } @@ -913,18 +931,18 @@ static void start_ships(parseinfo *pi, const XML_Char *el, const XML_Char **attr else { ship_type *stype = (ship_type *)pi->object; assert(stype); - if (xml_strcmp(el, "modifier") == 0) { + if (xml_strequal(el, "modifier")) { /* these modifiers are not like buildings */ int i; const XML_Char *type = NULL, *value = NULL; for (i = 0; attr[i]; i += 2) { - if (xml_strcmp(attr[i], "type") == 0) { + if (xml_strequal(attr[i], "type")) { type = attr[i + 1]; } - else if (xml_strcmp(attr[i], "value") == 0) { + else if (xml_strequal(attr[i], "value")) { value = attr[i + 1]; } - else if (xml_strcmp(attr[i], "factor") == 0) { + else if (xml_strequal(attr[i], "factor")) { value = attr[i + 1]; } else { @@ -932,26 +950,32 @@ static void start_ships(parseinfo *pi, const XML_Char *el, const XML_Char **attr } } if (type) { - if (xml_strcmp(type, "tactics") == 0) { + if (!value) { + handle_bad_input(pi, el, attr[i]); + } + else if (xml_strequal(type, "tactics")) { stype->tac_bonus = xml_float(value); } - else if (xml_strcmp(type, "attack") == 0) { + else if (xml_strequal(type, "attack")) { stype->at_bonus = xml_int(value); } - else if (xml_strcmp(type, "defense") == 0) { + else if (xml_strequal(type, "defense")) { stype->df_bonus = xml_int(value); } + else { + handle_bad_input(pi, el, attr[i]); + } } } - else if (xml_strcmp(el, "requirement") == 0) { + else if (xml_strequal(el, "requirement")) { assert(stype->construction); handle_requirement(pi, el, attr); } - else if (xml_strcmp(el, "construction") == 0) { + else if (xml_strequal(el, "construction")) { assert(!stype->construction); stype->construction = parse_construction(pi, el, attr); } - else if (xml_strcmp(el, "coast") == 0) { + else if (xml_strequal(el, "coast")) { handle_coast(pi, el, attr); } else { @@ -979,7 +1003,7 @@ static void start_races(parseinfo *pi, const XML_Char *el, const XML_Char **attr "giveperson", "giveunit", "getitem", "recruitethereal", "recruitunlimited", "stonegolem", "irongolem", NULL }; - if (xml_strcmp(el, "attack") == 0) { + if (xml_strequal(el, "attack")) { int i; struct att * at; assert(rc); @@ -991,19 +1015,19 @@ static void start_races(parseinfo *pi, const XML_Char *el, const XML_Char **attr } for (i = 0; attr[i]; i += 2) { const XML_Char *key = attr[i], *val = attr[i + 1]; - if (xml_strcmp(key, "type") == 0) { + if (xml_strequal(key, "type")) { at->type = xml_int(val); } - else if (xml_strcmp(key, "flags") == 0) { + else if (xml_strequal(key, "flags")) { at->flags = xml_int(val); } - else if (xml_strcmp(key, "level") == 0) { + else if (xml_strequal(key, "level")) { at->level = xml_int(val); } - else if (xml_strcmp(key, "damage") == 0) { + else if (xml_strequal(key, "damage")) { at->data.dice = str_strdup(val); } - else if (xml_strcmp(key, "spell") == 0) { + else if (xml_strequal(key, "spell")) { at->data.sp = spellref_create(NULL, val); } else { @@ -1011,14 +1035,14 @@ static void start_races(parseinfo *pi, const XML_Char *el, const XML_Char **attr } } } - else if (xml_strcmp(el, "familiar") == 0) { + else if (xml_strequal(el, "familiar")) { race *frc = NULL; int i; assert(rc); for (i = 0; attr[i]; i += 2) { const XML_Char *key = attr[i], *val = attr[i + 1]; - if (xml_strcmp(key, "race") == 0) { + if (xml_strequal(key, "race")) { frc = rc_get_or_create(val); frc->flags |= RCF_FAMILIAR; } @@ -1032,19 +1056,19 @@ static void start_races(parseinfo *pi, const XML_Char *el, const XML_Char **attr } } } - else if (xml_strcmp(el, "skill") == 0) { + else if (xml_strequal(el, "skill")) { const XML_Char *name = NULL; int i, speed = 0, mod = 0; for (i = 0; attr[i]; i += 2) { const XML_Char *key = attr[i], *val = attr[i + 1]; - if (xml_strcmp(key, "name") == 0) { + if (xml_strequal(key, "name")) { name = val; } - else if (xml_strcmp(key, "modifier") == 0) { + else if (xml_strequal(key, "modifier")) { mod = xml_int(val); } - else if (xml_strcmp(key, "speed") == 0) { + else if (xml_strequal(key, "speed")) { speed = xml_int(val); } else { @@ -1061,22 +1085,22 @@ static void start_races(parseinfo *pi, const XML_Char *el, const XML_Char **attr } } } - else if (xml_strcmp(el, "param") == 0) { + else if (xml_strequal(el, "param")) { const XML_Char *key = attr_get(attr, "name"), *val = attr_get(attr, "value"); if (key && val) { rc_set_param(rc, key, val); } } - else if (xml_strcmp(el, "ai") == 0) { + else if (xml_strequal(el, "ai")) { /* AI flags are cumulative to race flags. XML format is dumb */ int i, flags = 0; assert(rc); for (i = 0; attr[i]; i += 2) { const XML_Char *key = attr[i], *val = attr[i + 1]; - if (xml_strcmp(key, "splitsize") == 0) { + if (xml_strequal(key, "splitsize")) { rc->splitsize = xml_int(val); } - else if (xml_strcmp(key, "scare") == 0) { + else if (xml_strequal(key, "scare")) { rc_set_param(rc, "scare", val); } else if (!handle_flag(&flags, attr + i, flag_names)) { @@ -1085,7 +1109,7 @@ static void start_races(parseinfo *pi, const XML_Char *el, const XML_Char **attr } rc->flags |= flags; } - else if (xml_strcmp(el, "race") == 0) { + else if (xml_strequal(el, "race")) { const XML_Char *name; nfamiliars = 0; @@ -1103,59 +1127,59 @@ static void start_races(parseinfo *pi, const XML_Char *el, const XML_Char **attr for (i = 0; attr[i]; i += 2) { const XML_Char *key = attr[i], *val = attr[i + 1]; - if (xml_strcmp(key, "maxaura") == 0) { + if (xml_strequal(key, "maxaura")) { rc->maxaura = (int)(100 * xml_float(val)); } - else if (xml_strcmp(key, "magres") == 0) { + else if (xml_strequal(key, "magres")) { /* specified in percent: */ rc->magres = frac_make(xml_int(val), 100); } - else if (xml_strcmp(key, "healing") == 0) { + else if (xml_strequal(key, "healing")) { rc->healing = (int)(xml_float(val) * 100); } - else if (xml_strcmp(key, "regaura") == 0) { + else if (xml_strequal(key, "regaura")) { rc->regaura = xml_float(val); } - else if (xml_strcmp(key, "recruitcost") == 0) { + else if (xml_strequal(key, "recruitcost")) { rc->recruitcost = xml_int(val); } - else if (xml_strcmp(key, "maintenance") == 0) { + else if (xml_strequal(key, "maintenance")) { rc->maintenance = xml_int(val); } - else if (xml_strcmp(key, "income") == 0) { + else if (xml_strequal(key, "income")) { rc->income = xml_int(val); } - else if (xml_strcmp(key, "weight") == 0) { + else if (xml_strequal(key, "weight")) { rc->weight = xml_int(val); } - else if (xml_strcmp(key, "capacity") == 0) { + else if (xml_strequal(key, "capacity")) { rc->capacity = xml_int(val); } - else if (xml_strcmp(key, "speed") == 0) { + else if (xml_strequal(key, "speed")) { rc->speed = xml_float(val); } - else if (xml_strcmp(key, "hp") == 0) { + else if (xml_strequal(key, "hp")) { rc->hitpoints = xml_int(val); } - else if (xml_strcmp(key, "ac") == 0) { + else if (xml_strequal(key, "ac")) { rc->armor = xml_int(val); } - else if (xml_strcmp(key, "damage") == 0) { + else if (xml_strequal(key, "damage")) { rc->def_damage = str_strdup(val); } - else if (xml_strcmp(key, "unarmedattack") == 0) { + else if (xml_strequal(key, "unarmedattack")) { rc->at_default = xml_int(val); } - else if (xml_strcmp(key, "unarmeddefense") == 0) { + else if (xml_strequal(key, "unarmeddefense")) { rc->df_default = xml_int(val); } - else if (xml_strcmp(key, "attackmodifier") == 0) { + else if (xml_strequal(key, "attackmodifier")) { rc->at_bonus = xml_int(val); } - else if (xml_strcmp(key, "defensemodifier") == 0) { + else if (xml_strequal(key, "defensemodifier")) { rc->df_bonus = xml_int(val); } - else if (xml_strcmp(key, "studyspeed") == 0) { + else if (xml_strequal(key, "studyspeed")) { int study_speed = xml_int(val); if (study_speed != 0) { skill_t sk; @@ -1169,7 +1193,7 @@ static void start_races(parseinfo *pi, const XML_Char *el, const XML_Char **attr if (!handle_flag(&rc->battle_flags, attr + i, bflag_names)) { if (!handle_flag(&rc->ec_flags, attr + i, eflag_names)) { /* we already handled the name earlier: */ - if (xml_strcmp(key, "name") != 0) { + if (!xml_strequal(key, "name")) { handle_bad_input(pi, el, attr[i]); } } @@ -1186,7 +1210,7 @@ static void start_races(parseinfo *pi, const XML_Char *el, const XML_Char **attr static void start_buildings(parseinfo *pi, const XML_Char *el, const XML_Char **attr) { const char *flag_names[] = { "nodestroy", "nobuild", "unique", "decay", "magic", "namechange", "fort", "oneperturn", NULL }; - if (xml_strcmp(el, "building") == 0) { + if (xml_strequal(el, "building")) { const XML_Char *name; assert(stage == NULL); @@ -1196,34 +1220,34 @@ static void start_buildings(parseinfo *pi, const XML_Char *el, const XML_Char ** int i, flags = BTF_DEFAULT; for (i = 0; attr[i]; i += 2) { - if (xml_strcmp(attr[i], "maxsize") == 0) { + if (xml_strequal(attr[i], "maxsize")) { btype->maxsize = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "capacity") == 0) { + else if (xml_strequal(attr[i], "capacity")) { btype->capacity = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "maxcapacity") == 0) { + else if (xml_strequal(attr[i], "maxcapacity")) { btype->maxcapacity = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "magresbonus") == 0) { + else if (xml_strequal(attr[i], "magresbonus")) { btype->magresbonus = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "fumblebonus") == 0) { + else if (xml_strequal(attr[i], "fumblebonus")) { btype->fumblebonus = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "taxes") == 0) { + else if (xml_strequal(attr[i], "taxes")) { btype->taxes = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "auraregen") == 0) { + else if (xml_strequal(attr[i], "auraregen")) { btype->auraregen = xml_int(attr[i + 1]); } - else if (xml_strcmp(attr[i], "magres") == 0) { + else if (xml_strequal(attr[i], "magres")) { /* magres is specified in percent! */ btype->magres = frac_make(xml_int(attr[i + 1]), 100); } else if (!handle_flag(&flags, attr + i, flag_names)) { /* we already handled the name earlier */ - if (xml_strcmp(attr[i], "name") != 0) { + if (!xml_strequal(attr[i], "name")) { handle_bad_input(pi, el, attr[i]); } } @@ -1235,20 +1259,20 @@ static void start_buildings(parseinfo *pi, const XML_Char *el, const XML_Char ** else { building_type *btype = (building_type *)pi->object; assert(btype); - if (xml_strcmp(el, "modifier") == 0) { + if (xml_strequal(el, "modifier")) { handle_modifier(pi, el, attr); } - else if (xml_strcmp(el, "requirement") == 0) { + else if (xml_strequal(el, "requirement")) { assert(stage); assert(stage->construction); handle_requirement(pi, el, attr); } - else if (xml_strcmp(el, "construction") == 0) { + else if (xml_strequal(el, "construction")) { assert(stage == NULL); stage = calloc(1, sizeof(building_stage)); stage->construction = parse_construction(pi, el, attr); } - else if (xml_strcmp(el, "maintenance") == 0) { + else if (xml_strequal(el, "maintenance")) { assert(!btype->maintenance); handle_maintenance(pi, el, attr); } @@ -1262,31 +1286,31 @@ static void XMLCALL handle_start(void *data, const XML_Char *el, const XML_Char parseinfo *pi = (parseinfo *)data; if (pi->depth == 0) { pi->type = EXP_UNKNOWN; - if (xml_strcmp(el, "eressea") != 0) { + if (!xml_strequal(el, "eressea")) { handle_bad_input(pi, el, NULL); } } else if (pi->depth == 1) { - if (xml_strcmp(el, "resources") == 0) { + if (xml_strequal(el, "resources")) { pi->type = EXP_RESOURCES; } - else if (xml_strcmp(el, "buildings") == 0) { + else if (xml_strequal(el, "buildings")) { pi->type = EXP_BUILDINGS; } - else if (xml_strcmp(el, "ships") == 0) { + else if (xml_strequal(el, "ships")) { pi->type = EXP_SHIPS; } - else if (xml_strcmp(el, "messages") == 0) { + else if (xml_strequal(el, "messages")) { pi->type = EXP_MESSAGES; } - else if (xml_strcmp(el, "spells") == 0) { + else if (xml_strequal(el, "spells")) { pi->type = EXP_SPELLS; } - else if (xml_strcmp(el, "spellbook") == 0) { + else if (xml_strequal(el, "spellbook")) { pi->type = EXP_SPELLBOOKS; start_spellbooks(pi, el, attr); } - else if (xml_strcmp(el, "races") == 0) { + else if (xml_strequal(el, "races")) { pi->type = EXP_RACES; } else { @@ -1332,10 +1356,10 @@ static void XMLCALL handle_start(void *data, const XML_Char *el, const XML_Char } static void end_spells(parseinfo *pi, const XML_Char *el) { - if (xml_strcmp(el, "spells") == 0) { + if (xml_strequal(el, "spells")) { pi->type = EXP_UNKNOWN; } - else if (xml_strcmp(el, "spell") == 0) { + else if (xml_strequal(el, "spell")) { spell *sp = (spell *)pi->object; if (ncomponents > 0) { sp->components = calloc(sizeof(spell_component), ncomponents + 1); @@ -1350,10 +1374,10 @@ static void end_weapon(parseinfo *pi, const XML_Char *el) { resource_type *rtype = (resource_type *)pi->object; assert(rtype && rtype->wtype); - if (xml_strcmp(el, "weapon") == 0) { + if (xml_strequal(el, "weapon")) { pi->type = EXP_RESOURCES; } - else if (xml_strcmp(el, "modifier") == 0) { + else if (xml_strequal(el, "modifier")) { if (nwmods > 0) { weapon_type *wtype = rtype->wtype; wtype->modifiers = calloc(sizeof(weapon_mod), nwmods + 1); @@ -1365,14 +1389,14 @@ static void end_weapon(parseinfo *pi, const XML_Char *el) { static void end_resources(parseinfo *pi, const XML_Char *el) { resource_type *rtype = (resource_type *)pi->object; - if (xml_strcmp(el, "resource") == 0) { + if (xml_strequal(el, "resource")) { if (nrmods > 0) { rtype->modifiers = calloc(sizeof(resource_mod), nrmods + 1); memcpy(rtype->modifiers, rmods, sizeof(resource_mod) * nrmods); nrmods = 0; } } - else if (xml_strcmp(el, "construction") == 0) { + else if (xml_strequal(el, "construction")) { if (nreqs > 0) { construction *con = rtype->itype->construction; con->materials = calloc(sizeof(requirement), nreqs + 1); @@ -1380,14 +1404,14 @@ static void end_resources(parseinfo *pi, const XML_Char *el) { nreqs = 0; } } - else if (xml_strcmp(el, "resources") == 0) { + else if (xml_strequal(el, "resources")) { pi->type = EXP_UNKNOWN; } } static void end_races(parseinfo *pi, const XML_Char *el) { race *rc = (race *)pi->object; - if (xml_strcmp(el, "race") == 0) { + if (xml_strequal(el, "race")) { assert(rc); rc->attack[nattacks].type = AT_NONE; nattacks = 0; @@ -1400,14 +1424,14 @@ static void end_races(parseinfo *pi, const XML_Char *el) { nfamiliars = 0; pi->object = NULL; } - else if (xml_strcmp(el, "races") == 0) { + else if (xml_strequal(el, "races")) { pi->type = EXP_UNKNOWN; } } static void end_ships(parseinfo *pi, const XML_Char *el) { ship_type *stype = (ship_type *)pi->object; - if (xml_strcmp(el, "construction") == 0) { + if (xml_strequal(el, "construction")) { assert(stype); assert(stype->construction); if (nreqs > 0) { @@ -1417,7 +1441,7 @@ static void end_ships(parseinfo *pi, const XML_Char *el) { nreqs = 0; } } - else if (xml_strcmp(el, "ship") == 0) { + else if (xml_strequal(el, "ship")) { if (ncoasts > 0) { stype->coasts = calloc(sizeof(const terrain_type *), ncoasts + 1); memcpy(stype->coasts, coasts, sizeof(const terrain_type *) * ncoasts); @@ -1425,7 +1449,7 @@ static void end_ships(parseinfo *pi, const XML_Char *el) { } pi->object = NULL; } - else if (xml_strcmp(el, "ships") == 0) { + else if (xml_strequal(el, "ships")) { pi->type = EXP_UNKNOWN; } } @@ -1435,7 +1459,7 @@ static void end_buildings(parseinfo *pi, const XML_Char *el) { static building_stage **stage_ptr; building_type *btype = (building_type *)pi->object; - if (xml_strcmp(el, "construction") == 0) { + if (xml_strequal(el, "construction")) { assert(btype); if (stage) { if (nreqs > 0) { @@ -1454,7 +1478,7 @@ static void end_buildings(parseinfo *pi, const XML_Char *el) { stage = NULL; } } - else if (xml_strcmp(el, "building") == 0) { + else if (xml_strequal(el, "building")) { stage_ptr = NULL; if (nupkeep > 0) { btype->maintenance = calloc(sizeof(maintenance), nupkeep + 1); @@ -1468,7 +1492,7 @@ static void end_buildings(parseinfo *pi, const XML_Char *el) { } pi->object = NULL; } - else if (xml_strcmp(el, "buildings") == 0) { + else if (xml_strequal(el, "buildings")) { pi->type = EXP_UNKNOWN; } } diff --git a/src/util/pofile.c b/src/util/pofile.c index 63ee6dcc8..8de04364d 100644 --- a/src/util/pofile.c +++ b/src/util/pofile.c @@ -66,6 +66,7 @@ int pofile_read(const char *filename, int (*callback)(const char *msgid, const c if (!F) { log_error("could not open %s", filename); + return -1; } msgctxt[0] = 0; @@ -106,10 +107,10 @@ int pofile_read(const char *filename, int (*callback)(const char *msgid, const c line = read_line(F); } } - if (ferror(F)) { - log_error("read error in %s:%d.", filename, po_lineno); - return -1; - } + err = ferror(F); fclose(F); + if (err) { + log_error("read error %d in %s:%d.", err, filename, po_lineno); + } return err; } From 754456523745fd6e69b6341fdb813db339a72b6f Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 4 Sep 2018 15:28:05 +0200 Subject: [PATCH 26/30] start work on 3.18 release --- src/kernel/version.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kernel/version.c b/src/kernel/version.c index 994291ec2..0cfad7b73 100644 --- a/src/kernel/version.c +++ b/src/kernel/version.c @@ -7,7 +7,7 @@ #ifndef ERESSEA_VERSION /* the version number, if it was not passed to make with -D */ -#define ERESSEA_VERSION "3.17.0" +#define ERESSEA_VERSION "3.18.0" #endif const char *eressea_version(void) { From 6f5f1651c7a1f86358929c1ebe404f310f5e6e17 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 9 Sep 2018 07:41:30 +0200 Subject: [PATCH 27/30] fix crashes when trying to trade on ocean. fix missing message sections. --- res/core/messages.xml | 6 +++--- src/economy.c | 5 +++-- src/kernel/region.c | 5 ++++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/res/core/messages.xml b/res/core/messages.xml index d566c82e6..7e55e05f9 100644 --- a/res/core/messages.xml +++ b/res/core/messages.xml @@ -30,7 +30,7 @@ - + @@ -38,7 +38,7 @@ - + @@ -46,7 +46,7 @@ - + diff --git a/src/economy.c b/src/economy.c index 3bb7b1066..4a6aa5672 100644 --- a/src/economy.c +++ b/src/economy.c @@ -1660,7 +1660,7 @@ static void buy(unit * u, econ_request ** buyorders, struct order *ord) return; } } - if (r_demand(r, ltype)) { + if (!r->land || r_demand(r, ltype)) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "luxury_notsold", "")); return; } @@ -1702,6 +1702,7 @@ static void expandselling(region * r, econ_request * sellorders, int limit) static int bt_cache; static const struct building_type *castle_bt, *harbour_bt, *caravan_bt; + assert(r->land); if (bt_changed(&bt_cache)) { castle_bt = bt_find("castle"); harbour_bt = bt_find("harbour"); @@ -1971,7 +1972,7 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord) econ_request *o; int k, available; - if (!r_demand(r, ltype)) { + if (!r->land || !r_demand(r, ltype)) { cmistake(u, ord, 263, MSG_COMMERCE); return false; } diff --git a/src/kernel/region.c b/src/kernel/region.c index 7cb52c201..29f7222e1 100644 --- a/src/kernel/region.c +++ b/src/kernel/region.c @@ -716,7 +716,10 @@ const item_type *r_luxury(const region * r) int r_demand(const region * r, const luxury_type * ltype) { - struct demand *d = r->land->demands; + struct demand *d; + + assert(r && r->land); + d = r->land->demands; while (d && d->type != ltype) d = d->next; if (!d) From 67089aeb8c528412303c416631e94f9541d50a4b Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 9 Sep 2018 15:13:20 +0200 Subject: [PATCH 28/30] Bug 2487: Fix LERNE AUTO translations Also disallow autolearning expensive skills. --- clibs | 2 +- res/translations/strings.de.po | 7 +++++ res/translations/strings.en.po | 9 ++++++- scripts/tests/e2/e2features.lua | 24 +++++++++++++++++ scripts/tests/study.lua | 14 +++++----- src/automate.c | 8 +++--- src/kernel/order.c | 19 +++++++++----- src/kernel/order.test.c | 13 ++++++++++ src/skill.c | 35 +++++++++++++++++++++++++ src/skill.h | 2 ++ src/skill.test.c | 26 ++++++++++++++++++- src/study.c | 46 ++++++++------------------------- src/study.h | 37 +++++++++++++------------- 13 files changed, 169 insertions(+), 73 deletions(-) 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/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 From 9456c4bdc7306f8be246764de07ef04aa04ce35f Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 9 Sep 2018 15:16:11 +0200 Subject: [PATCH 29/30] fix debug logging. --- scripts/tests/e3/buildings.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 From 062abe81026a6afd03950980764dda45184eb023 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 9 Sep 2018 17:10:18 +0200 Subject: [PATCH 30/30] refactor siege property w. getter/setter --- src/battle.c | 6 ++---- src/creport.c | 8 ++++++-- src/economy.c | 2 ++ src/kernel/building.c | 11 +++++++++++ src/kernel/building.h | 5 ++++- src/kernel/unit.c | 8 +++++--- src/laws.c | 9 +++++++-- src/laws.test.c | 3 ++- src/report.c | 16 +++++++++------- src/settings.h | 12 ------------ src/study.h | 1 + src/wormhole.c | 1 - 12 files changed, 49 insertions(+), 33 deletions(-) diff --git a/src/battle.c b/src/battle.c index 9e2f841c0..3f61269a0 100644 --- a/src/battle.c +++ b/src/battle.c @@ -100,9 +100,7 @@ typedef enum combatmagic { #define MINSPELLRANGE 1 #define MAXSPELLRANGE 7 -#ifndef ROW_FACTOR -# define ROW_FACTOR 10 -#endif +#define ROW_FACTOR 3 /* factor for combat row advancement rule */ #define EFFECT_PANIC_SPELL 0.25 #define TROLL_REGENERATION 0.10 @@ -161,7 +159,7 @@ static void init_rules(void) skill_formula = config_get_int("rules.combat.skill_formula", FORMULA_ORIG); /* maximum number of combat turns */ - max_turns = config_get_int("rules.combat.turns", COMBAT_TURNS); + max_turns = config_get_int("rules.combat.turns", 5); /* damage calculation */ if (config_get_int("rules.combat.critical", 1)) { rule_damage |= DAMAGE_CRITICAL; diff --git a/src/creport.c b/src/creport.c index 91d56ca3b..137259083 100644 --- a/src/creport.c +++ b/src/creport.c @@ -645,6 +645,7 @@ static void cr_output_building(struct stream *out, building *b, const unit *owner, int fno, faction *f) { const char *bname, *billusion; + int i; stream_printf(out, "BURG %d\n", b->no); @@ -673,9 +674,12 @@ static void cr_output_building(struct stream *out, building *b, if (fno >= 0) { stream_printf(out, "%d;Partei\n", fno); } - if (b->besieged) { - stream_printf(out, "%d;Belagerer\n", b->besieged); + + i = building_get_siege(b); + if (i) { + stream_printf(out, "%d;Belagerer\n", i); } + cr_output_curses(out, f, b, TYP_BUILDING); } diff --git a/src/economy.c b/src/economy.c index baba6c1ac..cc611d49d 100644 --- a/src/economy.c +++ b/src/economy.c @@ -103,6 +103,8 @@ static void recruit_init(void) } } +#define ENTERTAINFRACTION 20 + int entertainmoney(const region * r) { double n; diff --git a/src/kernel/building.c b/src/kernel/building.c index 75cad30f7..9dfc7db35 100644 --- a/src/kernel/building.c +++ b/src/kernel/building.c @@ -870,3 +870,14 @@ int cmp_current_owner(const building * b, const building * a) } return 0; } + +int building_get_siege(const struct building *b) +{ + return b->_besieged; +} + +int building_add_siege(struct building *b, int delta) +{ + b->_besieged += delta; + return b->_besieged; +} diff --git a/src/kernel/building.h b/src/kernel/building.h index eacf42942..319304d79 100644 --- a/src/kernel/building.h +++ b/src/kernel/building.h @@ -118,7 +118,7 @@ extern "C" { int no; int size; int sizeleft; /* is only used during battle. should be a temporary attribute */ - int besieged; /* should be an attribute */ + int _besieged; /* should be an attribute */ int flags; } building; @@ -134,6 +134,9 @@ extern "C" { int id, int size, struct order *ord); bool building_finished(const struct building *b); + int building_get_siege(const struct building *b); + int building_add_siege(struct building *b, int delta); + int wage(const struct region *r, const struct faction *f, const struct race *rc, int in_turn); diff --git a/src/kernel/unit.c b/src/kernel/unit.c index 770723bea..146761f05 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -1917,9 +1917,11 @@ int getunit(const region * r, const faction * f, unit **uresult) int besieged(const unit * u) { /* belagert kann man in schiffen und burgen werden */ - return (u && !keyword_disabled(K_BESIEGE) - && u->building && u->building->besieged - && u->building->besieged >= u->building->size * SIEGEFACTOR); + if (u && !keyword_disabled(K_BESIEGE) && u->building) { + building * b = u->building; + return building_get_siege(b) >= b->size * SIEGEFACTOR; + } + return false; } bool has_horses(const unit * u) diff --git a/src/laws.c b/src/laws.c index 68f2894b3..67a44c673 100644 --- a/src/laws.c +++ b/src/laws.c @@ -306,6 +306,11 @@ static void calculate_emigration(region * r) } } +/* Vermehrungsrate Bauern in 1/10000. +* TODO: Evt. Berechnungsfehler, reale Vermehrungsraten scheinen hoeher. */ +#define PEASANTGROWTH 10 +#define PEASANTLUCK 10 +#define PEASANTFORCE 0.75 /* Chance einer Vermehrung trotz 90% Auslastung */ static double peasant_growth_factor(void) { @@ -908,7 +913,7 @@ static int slipthru(const region * r, const unit * u, const building * b) int n, o; /* b ist die burg, in die man hinein oder aus der man heraus will. */ - if (b == NULL || b->besieged < b->size * SIEGEFACTOR) { + if (b == NULL || building_get_siege(b) < b->size * SIEGEFACTOR) { return 1; } @@ -3857,7 +3862,7 @@ int siege_cmd(unit * u, order * ord) usetsiege(u, b); if (katapultiere < bewaffnete) katapultiere = bewaffnete; - b->besieged += katapultiere; + building_add_siege(b, katapultiere); /* definitiver schaden eingeschraenkt */ if (d > b->size - 1) d = b->size - 1; diff --git a/src/laws.test.c b/src/laws.test.c index c7c7a6682..fb644dc90 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -113,7 +113,8 @@ static void test_contact(CuTest * tc) u3 = test_create_unit(test_create_faction(NULL), r); set_level(u3, SK_PERCEPTION, 2); usetsiege(u3, b); - b->besieged = 1; + building_add_siege(b, 1); + CuAssertIntEquals(tc, 1, building_get_siege(b)); CuAssertIntEquals(tc, 1, can_contact(r, u1, u2)); u_set_building(u1, b); diff --git a/src/report.c b/src/report.c index 9f1bfa4bd..7de2550a4 100644 --- a/src/report.c +++ b/src/report.c @@ -1853,14 +1853,16 @@ nr_building(struct stream *out, const region *r, const building *b, const factio sbs_strcat(&sbs, LOC(lang, "nr_building_inprogress")); } - if (b->besieged > 0 && r->seen.mode >= seen_lighthouse) { - msg = msg_message("nr_building_besieged", "soldiers diff", b->besieged, - b->besieged - b->size * SIEGEFACTOR); + if (!keyword_disabled(K_BESIEGE) && r->seen.mode >= seen_lighthouse) { + int s = building_get_siege(b); + if (s > 0) { + msg = msg_message("nr_building_besieged", "soldiers diff", s, + s - b->size * SIEGEFACTOR); + size = nr_render(msg, lang, sbs.end, sbs.size - (sbs.end - sbs.begin), f); + sbs.end += size; - size = nr_render(msg, lang, sbs.end, sbs.size - (sbs.end - sbs.begin), f); - sbs.end += size; - - msg_release(msg); + msg_release(msg); + } } i = 0; if (b->display && b->display[0]) { diff --git a/src/settings.h b/src/settings.h index 57edb1566..498786be9 100644 --- a/src/settings.h +++ b/src/settings.h @@ -10,23 +10,11 @@ without prior permission by the authors of Eressea. */ -#define ENTERTAINFRACTION 20 -#define TEACHDIFFERENCE 2 #define RESOURCE_QUANTITY 0.5 #define RECRUITFRACTION 40 /* 100/RECRUITFRACTION% */ -#define COMBAT_TURNS 5 -#undef NEWATSROI - -/* Vermehrungsrate Bauern in 1/10000. -* TODO: Evt. Berechnungsfehler, reale Vermehrungsraten scheinen hoeher. */ -#define PEASANTGROWTH 10 -#define PEASANTLUCK 10 - -#define ROW_FACTOR 3 /* factor for combat row advancement rule */ /* TODO: move these settings to settings.h or into configuration files */ #define TREESIZE (8) /* space used by trees (in #peasants) */ -#define PEASANTFORCE 0.75 /* Chance einer Vermehrung trotz 90% Auslastung */ /* Gebaeudegroesse = Minimalbelagerer */ #define SIEGEFACTOR 2 diff --git a/src/study.h b/src/study.h index cca0428b7..686677064 100644 --- a/src/study.h +++ b/src/study.h @@ -31,6 +31,7 @@ extern "C" { #define STUDYDAYS 30 #define TEACHNUMBER 10 +#define TEACHDIFFERENCE 2 typedef struct teaching_info { struct selist *teachers; diff --git a/src/wormhole.c b/src/wormhole.c index 197fc346d..2000018ab 100644 --- a/src/wormhole.c +++ b/src/wormhole.c @@ -12,7 +12,6 @@ #include #include -#include "settings.h" #include "wormhole.h"