From bbcb2113d79380916aaff8a9ddd89e5db4165eb0 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 30 Apr 2019 18:40:24 +0200 Subject: [PATCH 1/5] WIP feature: STIRB [PARTEI ] --- res/core/messages.xml | 14 ------------- res/translations/messages.de.po | 8 +------ res/translations/messages.en.po | 8 +------ src/laws.c | 37 ++++++++++++++++++++++++++++++--- src/randenc.c | 3 +-- src/spells.c | 4 ++-- 6 files changed, 39 insertions(+), 35 deletions(-) diff --git a/res/core/messages.xml b/res/core/messages.xml index 7028ab6b9..4de8b0299 100644 --- a/res/core/messages.xml +++ b/res/core/messages.xml @@ -2937,13 +2937,6 @@ - - - - - - - @@ -3244,13 +3237,6 @@ - - - - - - - diff --git a/res/translations/messages.de.po b/res/translations/messages.de.po index 57b4426ef..c24966d6a 100644 --- a/res/translations/messages.de.po +++ b/res/translations/messages.de.po @@ -587,9 +587,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - Unbekannte Opti msgid "error131" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Um in Gletschern Straßen bauen zu können, muß zuerst ein Tunnel errichtet werden.\"" -msgid "error241" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Partei muß mindestens 81 Wochen alt sein, um einen Neustart mit einer anderen Rasse zu versuchen.\"" - msgid "feedback_unit_not_found" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Einheit wurde nicht gefunden.\"" @@ -1119,7 +1116,7 @@ msgid "dissolve_units_4" msgstr "\"$unit($unit) in $region($region): $int($number) $race($race,$number) $if($eq($number,1),\"zerfiel\", \"zerfielen\") zu Staub.\"" msgid "error281" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - Gegen welche Rasse soll der Jihad ausgerufen werden?\"" +msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Zielpartei muss die selbe Rasse haben.\"" msgid "error171" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Diesen Kampfzauber gibt es nicht.\"" @@ -1784,9 +1781,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - Es konnte kein msgid "error50" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Einheit ist nicht erfahren genug dafür.\"" -msgid "error282" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - Gegen diese Rasse kann kein Jihad ausgerufen werden.\"" - msgid "nmr_warning_final" msgstr "\"Bitte sende die Befehle nächste Runde ein, wenn du weiterspielen möchtest.\"" diff --git a/res/translations/messages.en.po b/res/translations/messages.en.po index 060fe09c2..1f1db551c 100644 --- a/res/translations/messages.en.po +++ b/res/translations/messages.en.po @@ -587,9 +587,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - Unknown option. msgid "error131" msgstr "\"$unit($unit) in $region($region): '$order($command)' - You must build a tunnel before building roads through glaciers.\"" -msgid "error241" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - The faction must be at least 81 weeks old to restart with a new race.\"" - msgid "feedback_unit_not_found" msgstr "\"$unit($unit) in $region($region): '$order($command)' - The unit could not be found.\"" @@ -1119,7 +1116,7 @@ msgid "dissolve_units_4" msgstr "\"$unit($unit) in $region($region): $int($number) $race($race,$number) turned to dust.\"" msgid "error281" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - What race did you want the jihad to be against?\"" +msgstr "\"$unit($unit) in $region($region): '$order($command)' - The target faction must have the same race as yours.\"" msgid "error171" msgstr "\"$unit($unit) in $region($region): '$order($command)' - This combat spell does not exist.\"" @@ -1784,9 +1781,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - No peasant coul msgid "error50" msgstr "\"$unit($unit) in $region($region): '$order($command)' - The unit is not experienced enough to do this.\"" -msgid "error282" -msgstr "\"$unit($unit) in $region($region): '$order($command)' - You cannot start a jihad against this race.\"" - msgid "nmr_warning_final" msgstr "\"Please send in orders for the next turn if you want to continue playing.\"" diff --git a/src/laws.c b/src/laws.c index fda3f434f..d6c754f08 100644 --- a/src/laws.c +++ b/src/laws.c @@ -395,8 +395,7 @@ static void peasants(region * r, int rule) dead += (int)(0.5 + n * PEASANT_STARVATION_CHANCE); if (dead > 0) { - message *msg = add_message(&r->msgs, msg_message("phunger", "dead", dead)); - msg_release(msg); + ADDMSG(&r->msgs, msg_message("phunger", "dead", dead)); peasants -= dead; } @@ -941,6 +940,10 @@ int leave_cmd(unit * u, struct order *ord) return 0; } +int transfer_faction(faction *fsrc, faction *fdst) { + return 0; +} + int quit_cmd(unit * u, struct order *ord) { char token[128]; @@ -952,6 +955,34 @@ int quit_cmd(unit * u, struct order *ord) assert(kwd == K_QUIT); passwd = gettoken(token, sizeof(token)); if (checkpasswd(f, (const char *)passwd)) { + param_t p; + p = getparam(f->locale); + if (p == P_FACTION) { + faction *f2 = getfaction(); + if (f2 == NULL) { + cmistake(u, ord, 66, MSG_EVENT); + } + else if (f->race != f2->race) { + cmistake(u, ord, 281, MSG_EVENT); + } + else { + unit *u2; + for (u2 = u->region->units; u2; u2 = u2->next) { + if (u2->faction == f2 && ucontact(u2, u)) { + int err = transfer_faction(u->faction, u2->faction); + if (err != 0) { + /* something went wrong */ + cmistake(u, ord, err, MSG_EVENT); + } + break; + } + } + if (u2 == NULL) { + /* no target unit found */ + cmistake(u, ord, 0, MSG_EVENT); + } + } + } fset(f, FFL_QUIT); } else { @@ -2077,7 +2108,7 @@ int banner_cmd(unit * u, struct order *ord) init_order_depr(ord); s = getstrtoken(); faction_setbanner(u->faction, s); - add_message(&u->faction->msgs, msg_message("changebanner", "value", s)); + ADDMSG(&u->faction->msgs, msg_message("changebanner", "value", s)); return 0; } diff --git a/src/randenc.c b/src/randenc.c index b0e5dd13a..9c3fd4739 100644 --- a/src/randenc.c +++ b/src/randenc.c @@ -603,8 +603,7 @@ void plagues(region * r) } if (dead > 0) { - message *msg = add_message(&r->msgs, msg_message("pest", "dead", dead)); - msg_release(msg); + ADDMSG(&r->msgs, msg_message("pest", "dead", dead)); deathcounts(r, dead); rsetpeasants(r, peasants - dead); } diff --git a/src/spells.c b/src/spells.c index 5e2474861..83b7f2050 100644 --- a/src/spells.c +++ b/src/spells.c @@ -3110,8 +3110,8 @@ static int sp_chaossuction(castorder * co) create_special_direction(rt, r, 2, "vortex_desc", "vortex", false); new_border(&bt_chaosgate, r, rt); - add_message(&r->msgs, msg_message("chaosgate_effect_1", "mage", caster)); - add_message(&rt->msgs, msg_message("chaosgate_effect_2", "")); + ADDMSG(&r->msgs, msg_message("chaosgate_effect_1", "mage", caster)); + ADDMSG(&rt->msgs, msg_message("chaosgate_effect_2", "")); return cast_level; } From 6637c9485298c63fcb456eef2160bca4e953dcc3 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 1 May 2019 20:46:26 +0200 Subject: [PATCH 2/5] do not allow transfer of heros and limited units. --- src/give.c | 32 ++++++++++++++----------- src/give.h | 2 ++ src/laws.c | 62 ++++++++++++++++++++++++++++--------------------- src/laws.test.c | 46 ++++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 40 deletions(-) diff --git a/src/give.c b/src/give.c index c6e77aa68..5f9127b23 100644 --- a/src/give.c +++ b/src/give.c @@ -299,7 +299,7 @@ static bool can_give_men(const unit *u, const unit *dst, order *ord, message **m return false; } -static bool rule_transfermen(void) +bool rule_transfermen(void) { int rule = config_get_int("rules.transfermen", 1); return rule != 0; @@ -472,9 +472,23 @@ message * disband_men(int n, unit * u, struct order *ord) { return msg_message("give_person_peasants", "unit amount", u, n); } +int give_unit_allowed(const unit * u) +{ + if (unit_has_cursed_item(u)) { + return 78; + } + if (fval(u, UFL_HERO)) { + return 75; + } + if (fval(u, UFL_LOCKED) || fval(u, UFL_HUNGER)) { + return 74; + } + return 0; +} + void give_unit(unit * u, unit * u2, order * ord) { - int maxt = max_transfers(); + int err, maxt = max_transfers(); assert(u); if (!rule_transfermen() && u2 && u->faction != u2->faction) { @@ -482,17 +496,9 @@ void give_unit(unit * u, unit * u2, order * ord) return; } - if (unit_has_cursed_item(u)) { - cmistake(u, ord, 78, MSG_COMMERCE); - return; - } - - if (fval(u, UFL_HERO)) { - cmistake(u, ord, 75, MSG_COMMERCE); - return; - } - if (fval(u, UFL_LOCKED) || fval(u, UFL_HUNGER)) { - cmistake(u, ord, 74, MSG_COMMERCE); + err = give_unit_allowed(u); + if (err != 0) { + cmistake(u, ord, err, MSG_COMMERCE); return; } diff --git a/src/give.h b/src/give.h index 94eee729c..4d44790a7 100644 --- a/src/give.h +++ b/src/give.h @@ -29,10 +29,12 @@ extern "C" { struct message * disband_men(int n, struct unit * u, struct order *ord); struct message * give_men(int n, struct unit *u, struct unit *u2, struct order *ord); + int give_unit_allowed(const struct unit * u); void give_unit(struct unit *u, struct unit *u2, struct order *ord); void give_cmd(struct unit * u, struct order * ord); struct message * check_give(const struct unit * u, const struct unit * u2, struct order *ord); bool can_give_to(struct unit *u, struct unit *u2); + bool rule_transfermen(void); #ifdef __cplusplus } diff --git a/src/laws.c b/src/laws.c index d6c754f08..9e9e337b1 100644 --- a/src/laws.c +++ b/src/laws.c @@ -30,6 +30,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "battle.h" #include "contact.h" #include "economy.h" +#include "give.h" #include "market.h" #include "morale.h" #include "monsters.h" @@ -940,8 +941,13 @@ int leave_cmd(unit * u, struct order *ord) return 0; } -int transfer_faction(faction *fsrc, faction *fdst) { - return 0; +void transfer_faction(faction *fsrc, faction *fdst) { + unit *u; + for (u = fsrc->units; u != NULL; u = u->nextF) { + if (give_unit_allowed(u) == 0) { + u_setfaction(u, fdst); + } + } } int quit_cmd(unit * u, struct order *ord) @@ -955,35 +961,37 @@ int quit_cmd(unit * u, struct order *ord) assert(kwd == K_QUIT); passwd = gettoken(token, sizeof(token)); if (checkpasswd(f, (const char *)passwd)) { - param_t p; - p = getparam(f->locale); - if (p == P_FACTION) { - faction *f2 = getfaction(); - if (f2 == NULL) { - cmistake(u, ord, 66, MSG_EVENT); - } - else if (f->race != f2->race) { - cmistake(u, ord, 281, MSG_EVENT); - } - else { - unit *u2; - for (u2 = u->region->units; u2; u2 = u2->next) { - if (u2->faction == f2 && ucontact(u2, u)) { - int err = transfer_faction(u->faction, u2->faction); - if (err != 0) { - /* something went wrong */ - cmistake(u, ord, err, MSG_EVENT); - } - break; - } + int flags = FFL_QUIT; + if (rule_transfermen()) { + param_t p; + p = getparam(f->locale); + if (p == P_FACTION) { + faction *f2 = getfaction(); + if (f2 == NULL) { + cmistake(u, ord, 66, MSG_EVENT); + flags = 0; } - if (u2 == NULL) { - /* no target unit found */ - cmistake(u, ord, 0, MSG_EVENT); + else if (f->race != f2->race) { + cmistake(u, ord, 281, MSG_EVENT); + flags = 0; + } + else { + unit *u2; + for (u2 = u->region->units; u2; u2 = u2->next) { + if (u2->faction == f2 && ucontact(u2, u)) { + transfer_faction(u->faction, u2->faction); + break; + } + } + if (u2 == NULL) { + /* no target unit found */ + cmistake(u, ord, 0, MSG_EVENT); + flags = 0; + } } } } - fset(f, FFL_QUIT); + f->flags |= flags; } else { char buffer[64]; diff --git a/src/laws.test.c b/src/laws.test.c index cead8605a..573e1a486 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -1,6 +1,7 @@ #include #include "laws.h" #include "battle.h" +#include "contact.h" #include "guard.h" #include "monsters.h" @@ -1868,6 +1869,49 @@ static void test_long_order_on_ocean(CuTest *tc) { test_teardown(); } +static void test_quit(CuTest *tc) { + faction *f; + unit *u; + region *r; + + test_setup(); + r = test_create_plain(0, 0); + f = test_create_faction(NULL); + u = test_create_unit(f, r); + u->thisorder = create_order(K_QUIT, f->locale, "password"); + + faction_setpassword(f, "passwort"); + quit_cmd(u, u->thisorder); + CuAssertIntEquals(tc, 0, f->flags & FFL_QUIT); + + faction_setpassword(f, "password"); + quit_cmd(u, u->thisorder); + CuAssertIntEquals(tc, FFL_QUIT, f->flags & FFL_QUIT); + + test_teardown(); +} + +static void test_quit_transfer(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)); + 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(); @@ -1943,6 +1987,8 @@ CuSuite *get_laws_suite(void) SUITE_ADD_TEST(suite, test_nmr_timeout); SUITE_ADD_TEST(suite, test_long_orders); SUITE_ADD_TEST(suite, test_long_order_on_ocean); + SUITE_ADD_TEST(suite, test_quit); + SUITE_ADD_TEST(suite, test_quit_transfer); return suite; } From 8605409ed38db73be777b9c6185b75a8ce0f99c5 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 4 May 2019 10:45:51 +0200 Subject: [PATCH 3/5] limited-skill units are only transferred up to the limit. --- src/economy.c | 2 +- src/give.c | 12 ++++++------ src/kernel/build.c | 2 +- src/kernel/faction.c | 6 +++--- src/kernel/faction.h | 4 ++-- src/kernel/faction.test.c | 22 ++++++++++++++++++++++ src/kernel/unit.c | 22 ++++++++++++++++------ src/kernel/unit.h | 1 + src/laws.c | 34 ++++++++++++++++++++++++++++++++++ src/laws.test.c | 35 +++++++++++++++++++++++++++++++++++ src/spells.c | 2 +- src/study.c | 8 ++++---- 12 files changed, 126 insertions(+), 24 deletions(-) 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; From 2cb42872685b4812ccb34ae2b5f51af227e5c9f8 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 4 May 2019 10:55:13 +0200 Subject: [PATCH 4/5] do not transfer migrants. comments are good. --- src/laws.c | 26 ++++++++++--------- src/laws.test.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 12 deletions(-) diff --git a/src/laws.c b/src/laws.c index f1e69b231..2f86e79a6 100644 --- a/src/laws.c +++ b/src/laws.c @@ -964,22 +964,24 @@ void transfer_faction(faction *fsrc, faction *fdst) { } for (u = fsrc->units; u != NULL; u = u->nextF) { - if (give_unit_allowed(u) == 0) { - if (u->skills) { - int i; + if (u_race(u) == fdst->race) { + 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; + 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; } } - if (i != u->skill_size) { - continue; - } + u_setfaction(u, fdst); } - u_setfaction(u, fdst); } } } diff --git a/src/laws.test.c b/src/laws.test.c index f82ec52e5..c5dafd851 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -1891,6 +1891,9 @@ static void test_quit(CuTest *tc) { test_teardown(); } +/** + * Gifting units to another faction upon voluntary death (QUIT). + */ static void test_quit_transfer(CuTest *tc) { faction *f1, *f2; unit *u1, *u2; @@ -1912,6 +1915,12 @@ static void test_quit_transfer(CuTest *tc) { test_teardown(); } +/** + * Gifting units with limited skills to another faction. + * + * This is allowed only up to the limit of the target faction. + * Units that would break the limit are not transferred. + */ static void test_quit_transfer_limited(CuTest *tc) { faction *f1, *f2; unit *u1, *u2; @@ -1946,6 +1955,62 @@ static void test_quit_transfer_limited(CuTest *tc) { test_teardown(); } +/** + * Only units of the same race can be gifted to another faction. + */ +static void test_quit_transfer_migrants(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)); + + u_setrace(u1, test_create_race("smurf")); + + quit_cmd(u1, u1->thisorder); + CuAssertIntEquals(tc, FFL_QUIT, f1->flags & FFL_QUIT); + CuAssertPtrEquals(tc, f1, u1->faction); + + test_teardown(); +} + +/** + * Heroes cannot be gifted to another faction. + */ +static void test_quit_transfer_hero(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)); + + u1->flags |= UFL_HERO; + + quit_cmd(u1, u1->thisorder); + CuAssertIntEquals(tc, FFL_QUIT, f1->flags & FFL_QUIT); + CuAssertPtrEquals(tc, f1, u1->faction); + + test_teardown(); +} + CuSuite *get_laws_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -2024,6 +2089,8 @@ CuSuite *get_laws_suite(void) SUITE_ADD_TEST(suite, test_quit); SUITE_ADD_TEST(suite, test_quit_transfer); SUITE_ADD_TEST(suite, test_quit_transfer_limited); + SUITE_ADD_TEST(suite, test_quit_transfer_migrants); + SUITE_ADD_TEST(suite, test_quit_transfer_hero); return suite; } From cd28f3aee44f9630fe213dfa7723790c99c0ff46 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 4 May 2019 11:11:30 +0200 Subject: [PATCH 5/5] heroes lose their status when gifted. --- src/laws.c | 1 + src/laws.test.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/laws.c b/src/laws.c index 2f86e79a6..e8200c262 100644 --- a/src/laws.c +++ b/src/laws.c @@ -965,6 +965,7 @@ void transfer_faction(faction *fsrc, faction *fdst) { for (u = fsrc->units; u != NULL; u = u->nextF) { if (u_race(u) == fdst->race) { + u->flags &= ~UFL_HERO; if (give_unit_allowed(u) == 0) { if (u->skills) { int i; diff --git a/src/laws.test.c b/src/laws.test.c index c5dafd851..33742f427 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -1984,7 +1984,7 @@ static void test_quit_transfer_migrants(CuTest *tc) { } /** - * Heroes cannot be gifted to another faction. + * A hero that is gifted to another faction loses their status. */ static void test_quit_transfer_hero(CuTest *tc) { faction *f1, *f2; @@ -2006,7 +2006,8 @@ static void test_quit_transfer_hero(CuTest *tc) { quit_cmd(u1, u1->thisorder); CuAssertIntEquals(tc, FFL_QUIT, f1->flags & FFL_QUIT); - CuAssertPtrEquals(tc, f1, u1->faction); + CuAssertPtrEquals(tc, f2, u1->faction); + CuAssertIntEquals(tc, 0, u1->flags & UFL_HERO); test_teardown(); }