diff --git a/src/economy.c b/src/economy.c index ec6986dd6..efca9ce5c 100644 --- a/src/economy.c +++ b/src/economy.c @@ -548,7 +548,7 @@ static void recruit(unit * u, struct order *ord, econ_request ** recruitorders) return; } if (has_skill(u, SK_ALCHEMY)) { - if (count_skill(u->faction, SK_ALCHEMY) + n > skill_limit(u->faction, SK_ALCHEMY)) { + if (faction_count_skill(u->faction, SK_ALCHEMY) + n > faction_skill_limit(u->faction, SK_ALCHEMY)) { cmistake(u, ord, 156, MSG_EVENT); return; } diff --git a/src/give.c b/src/give.c index 5f9127b23..64fa39504 100644 --- a/src/give.c +++ b/src/give.c @@ -391,7 +391,7 @@ message * give_men(int n, unit * u, unit * u2, struct order *ord) } if (has_skill(u, SK_ALCHEMY) || has_skill(u2, SK_ALCHEMY)) { - int k = count_skill(u2->faction, SK_ALCHEMY); + int k = faction_count_skill(u2->faction, SK_ALCHEMY); /* Falls die Zieleinheit keine Alchemisten sind, werden sie nun * welche. */ @@ -408,7 +408,7 @@ message * give_men(int n, unit * u, unit * u2, struct order *ord) /* wird das Alchemistenmaximum ueberschritten ? */ - if (k > skill_limit(u2->faction, SK_ALCHEMY)) { + if (k > faction_skill_limit(u2->faction, SK_ALCHEMY)) { error = 156; } } @@ -592,8 +592,8 @@ void give_unit(unit * u, unit * u2, order * ord) } } if (has_skill(u, SK_MAGIC)) { - if (count_skill(u2->faction, SK_MAGIC) + u->number > - skill_limit(u2->faction, SK_MAGIC)) { + if (faction_count_skill(u2->faction, SK_MAGIC) + u->number > + faction_skill_limit(u2->faction, SK_MAGIC)) { cmistake(u, ord, 155, MSG_COMMERCE); return; } @@ -603,8 +603,8 @@ void give_unit(unit * u, unit * u2, order * ord) } } if (has_skill(u, SK_ALCHEMY) - && count_skill(u2->faction, SK_ALCHEMY) + u->number > - skill_limit(u2->faction, SK_ALCHEMY)) { + && faction_count_skill(u2->faction, SK_ALCHEMY) + u->number > + faction_skill_limit(u2->faction, SK_ALCHEMY)) { cmistake(u, ord, 156, MSG_COMMERCE); return; } diff --git a/src/kernel/build.c b/src/kernel/build.c index 0475c607e..a5eb0a6db 100644 --- a/src/kernel/build.c +++ b/src/kernel/build.c @@ -572,7 +572,7 @@ static int build_limited(unit * u, const construction * con, int completed, int } /* Flinkfingerring wirkt nicht auf Mengenbegrenzte (magische) * Talente */ - if (skill_limit(u->faction, con->skill) == INT_MAX) { + if (faction_skill_limit(u->faction, con->skill) == INT_MAX) { const resource_type *ring = get_resourcetype(R_RING_OF_NIMBLEFINGER); item *itm = ring ? *i_find(&u->items, ring->itype) : 0; int i = itm ? itm->number : 0; diff --git a/src/kernel/faction.c b/src/kernel/faction.c index 238ba834b..beec40e0a 100755 --- a/src/kernel/faction.c +++ b/src/kernel/faction.c @@ -609,7 +609,7 @@ static int allied_skillcount(const faction * f, skill_t sk) for (qi = 0; members; selist_advance(&members, &qi, 1)) { faction *m = (faction *)selist_get(members, qi); - num += count_skill(m, sk); + num += faction_count_skill(m, sk); } return num; } @@ -623,7 +623,7 @@ static int allied_skilllimit(const faction * f, skill_t sk) return value; } -int count_skill(faction * f, skill_t sk) +int faction_count_skill(faction * f, skill_t sk) { int n = 0; unit *u; @@ -638,7 +638,7 @@ int count_skill(faction * f, skill_t sk) return n; } -int skill_limit(faction * f, skill_t sk) +int faction_skill_limit(const faction * f, skill_t sk) { int m = INT_MAX; int al = allied_skilllimit(f, sk); diff --git a/src/kernel/faction.h b/src/kernel/faction.h index cbe4c7008..d22e9ee22 100644 --- a/src/kernel/faction.h +++ b/src/kernel/faction.h @@ -161,8 +161,8 @@ extern "C" { struct spellbook * faction_get_spellbook(struct faction *f); /* skills */ - int skill_limit(struct faction *f, skill_t sk); - int count_skill(struct faction *f, skill_t sk); + int faction_skill_limit(const struct faction *f, skill_t sk); + int faction_count_skill(struct faction *f, skill_t sk); bool faction_id_is_unused(int); #define COUNT_MONSTERS 0x01 diff --git a/src/kernel/faction.test.c b/src/kernel/faction.test.c index 65310ee3a..e572cf312 100644 --- a/src/kernel/faction.test.c +++ b/src/kernel/faction.test.c @@ -25,6 +25,7 @@ #include #include +#include static void test_destroyfaction_allies(CuTest *tc) { faction *f1, *f2; @@ -211,6 +212,26 @@ static void test_max_migrants(CuTest *tc) { test_teardown(); } +static void test_skill_limit(CuTest *tc) { + faction *f; + + test_setup(); + f = test_create_faction(NULL); + CuAssertIntEquals(tc, INT_MAX, faction_skill_limit(f, SK_ENTERTAINMENT)); + CuAssertIntEquals(tc, 3, faction_skill_limit(f, SK_ALCHEMY)); + config_set_int("rules.maxskills.alchemy", 4); + CuAssertIntEquals(tc, 4, faction_skill_limit(f, SK_ALCHEMY)); + CuAssertIntEquals(tc, 3, faction_skill_limit(f, SK_MAGIC)); + CuAssertIntEquals(tc, 3, max_magicians(f)); + config_set_int("rules.maxskills.magic", 4); + CuAssertIntEquals(tc, 4, faction_skill_limit(f, SK_MAGIC)); + CuAssertIntEquals(tc, 4, max_magicians(f)); + f->race = test_create_race(racenames[RC_ELF]); + CuAssertIntEquals(tc, 5, faction_skill_limit(f, SK_MAGIC)); + CuAssertIntEquals(tc, 5, max_magicians(f)); + test_teardown(); +} + static void test_valid_race(CuTest *tc) { race * rc1, *rc2; faction *f; @@ -334,6 +355,7 @@ CuSuite *get_faction_suite(void) CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_addplayer); SUITE_ADD_TEST(suite, test_max_migrants); + SUITE_ADD_TEST(suite, test_skill_limit); SUITE_ADD_TEST(suite, test_addfaction); SUITE_ADD_TEST(suite, test_remove_empty_factions); SUITE_ADD_TEST(suite, test_destroyfaction_allies); diff --git a/src/kernel/unit.c b/src/kernel/unit.c index c1e9264b9..23bcc9982 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -1812,17 +1812,27 @@ int maintenance_cost(const struct unit *u) return u_race(u)->maintenance * u->number; } -static skill_t limited_skills[] = { SK_MAGIC, SK_ALCHEMY, SK_TACTICS, SK_SPY, SK_HERBALISM, NOSKILL }; +static skill_t limited_skills[] = { SK_ALCHEMY, SK_HERBALISM, SK_MAGIC, SK_SPY, SK_TACTICS, NOSKILL }; + +bool is_limited_skill(skill_t sk) +{ + int i; + for (i = 0; limited_skills[i] != NOSKILL; ++i) { + if (sk == limited_skills[i]) { + return true; + } + } + return false; +} + bool has_limited_skills(const struct unit * u) { - int i, j; + int i; for (i = 0; i != u->skill_size; ++i) { skill *sv = u->skills + i; - for (j = 0; limited_skills[j] != NOSKILL; ++j) { - if (sv->id == limited_skills[j]) { - return true; - } + if (is_limited_skill(sv->id)) { + return true; } } return false; diff --git a/src/kernel/unit.h b/src/kernel/unit.h index 46746e3a1..fc5602694 100644 --- a/src/kernel/unit.h +++ b/src/kernel/unit.h @@ -251,6 +251,7 @@ extern "C" { bool has_horses(const struct unit *u); int maintenance_cost(const struct unit *u); bool has_limited_skills(const struct unit *u); + bool is_limited_skill(skill_t sk); #ifdef __cplusplus } diff --git a/src/laws.c b/src/laws.c index 9e9e337b1..f1e69b231 100644 --- a/src/laws.c +++ b/src/laws.c @@ -943,8 +943,42 @@ int leave_cmd(unit * u, struct order *ord) void transfer_faction(faction *fsrc, faction *fdst) { unit *u; + skill_t sk; + int skill_count[MAXSKILLS]; + int skill_limit[MAXSKILLS]; + + for (sk = 0; sk != MAXSKILLS; ++sk) { + skill_limit[sk] = faction_skill_limit(fdst, sk); + } + memset(skill_count, 0, sizeof(skill_count)); + + for (u = fdst->units; u != NULL; u = u->nextF) { + if (u->skills) { + int i; + for (i = 0; i != u->skill_size; ++i) { + const skill *sv = u->skills + i; + skill_t sk = (skill_t)sv->id; + skill_count[sk] += u->number; + } + } + } + for (u = fsrc->units; u != NULL; u = u->nextF) { if (give_unit_allowed(u) == 0) { + if (u->skills) { + int i; + + for (i = 0; i != u->skill_size; ++i) { + const skill *sv = u->skills + i; + skill_t sk = (skill_t)sv->id; + if (skill_count[sk] + u->number > skill_limit[sk]) { + break; + } + } + if (i != u->skill_size) { + continue; + } + } u_setfaction(u, fdst); } } diff --git a/src/laws.test.c b/src/laws.test.c index 573e1a486..f82ec52e5 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -1912,6 +1912,40 @@ static void test_quit_transfer(CuTest *tc) { test_teardown(); } +static void test_quit_transfer_limited(CuTest *tc) { + faction *f1, *f2; + unit *u1, *u2; + region *r; + + test_setup(); + r = test_create_plain(0, 0); + f1 = test_create_faction(NULL); + faction_setpassword(f1, "password"); + u1 = test_create_unit(f1, r); + f2 = test_create_faction(NULL); + u2 = test_create_unit(f2, r); + contact_unit(u2, u1); + u1->thisorder = create_order(K_QUIT, f1->locale, "password %s %s", + LOC(f1->locale, parameters[P_FACTION]), itoa36(f2->no)); + + set_level(u1, SK_MAGIC, 1); + set_level(u2, SK_MAGIC, 1); + CuAssertIntEquals(tc, true, has_limited_skills(u1)); + + config_set_int("rules.maxskills.magic", 1); + quit_cmd(u1, u1->thisorder); + CuAssertIntEquals(tc, FFL_QUIT, f1->flags & FFL_QUIT); + CuAssertPtrEquals(tc, f1, u1->faction); + + f1->flags -= FFL_QUIT; + config_set_int("rules.maxskills.magic", 2); + quit_cmd(u1, u1->thisorder); + CuAssertIntEquals(tc, FFL_QUIT, f1->flags & FFL_QUIT); + CuAssertPtrEquals(tc, f2, u1->faction); + + test_teardown(); +} + CuSuite *get_laws_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -1989,6 +2023,7 @@ CuSuite *get_laws_suite(void) SUITE_ADD_TEST(suite, test_long_order_on_ocean); SUITE_ADD_TEST(suite, test_quit); SUITE_ADD_TEST(suite, test_quit_transfer); + SUITE_ADD_TEST(suite, test_quit_transfer_limited); return suite; } diff --git a/src/spells.c b/src/spells.c index 83b7f2050..335bcef3c 100644 --- a/src/spells.c +++ b/src/spells.c @@ -3451,7 +3451,7 @@ static bool can_charm(const unit * u, int maxlevel) while (l < h) { int m = (l + h) / 2; if (sk == expskills[m]) { - if (skill_limit(u->faction, sk) != INT_MAX) { + if (faction_skill_limit(u->faction, sk) != INT_MAX) { return false; } else if ((int)sv->level > maxlevel) { diff --git a/src/study.c b/src/study.c index 7e5ca9812..8d39e5d5f 100644 --- a/src/study.c +++ b/src/study.c @@ -635,9 +635,9 @@ int study_cmd(unit * u, order * ord) mtype = M_GRAY; } else if (!has_skill(u, SK_MAGIC)) { - int mmax = skill_limit(u->faction, SK_MAGIC); + int mmax = faction_skill_limit(u->faction, SK_MAGIC); /* Die Einheit ist noch kein Magier */ - if (count_skill(u->faction, SK_MAGIC) + u->number > mmax) { + if (faction_count_skill(u->faction, SK_MAGIC) + u->number > mmax) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_max_magicians", "amount", mmax)); return -1; @@ -694,8 +694,8 @@ int study_cmd(unit * u, order * ord) if (sk == SK_ALCHEMY) { maxalchemy = effskill(u, SK_ALCHEMY, NULL); if (!has_skill(u, SK_ALCHEMY)) { - int amax = skill_limit(u->faction, SK_ALCHEMY); - if (count_skill(u->faction, SK_ALCHEMY) + u->number > amax) { + int amax = faction_skill_limit(u->faction, SK_ALCHEMY); + if (faction_count_skill(u->faction, SK_ALCHEMY) + u->number > amax) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_max_alchemists", "amount", amax)); return -1;