diff --git a/scripts/tests/economy.lua b/scripts/tests/economy.lua index 9c72e7e70..0c140e418 100644 --- a/scripts/tests/economy.lua +++ b/scripts/tests/economy.lua @@ -28,8 +28,6 @@ function test_work() end function test_bug_2361_forget_magic() - -- https://bugs.eressea.de/view.php?id=2361 - -- familiars cannot forget magic local r = region.create(0, 0, "plain") local f = faction.create("human") local u = unit.create(f, r, 1) @@ -38,12 +36,30 @@ function test_bug_2361_forget_magic() u:add_order("VERGESSE Magie") u:set_skill('magic', 5) uf.race = 'unicorn' + u.familiar = uf + process_orders() + assert_equal(0, u:get_skill('magic')) + assert_nil(u.familiar) + -- without a mage, familiars become ghosts: + assert_equal('ghost', uf.race_name) + assert_equal(0, uf:get_skill('magic')) +end + +function test_bug_2361_familiar_cannot_forget_magic_() + -- https://bugs.eressea.de/view.php?id=2361 + local r = region.create(0, 0, "plain") + local f = faction.create("human") + local u = unit.create(f, r, 1) + local uf = unit.create(f, r, 1) + u:clear_orders() + u:set_skill('magic', 5) + uf.race = 'unicorn' uf:clear_orders() uf:add_order("VERGESSE Magie") uf:set_skill('magic', 5) u.familiar = uf process_orders() - assert_equal(0, u:get_skill('magic')) + -- familiars cannot forget magic: assert_equal(5, uf:get_skill('magic')) end diff --git a/src/bind_tolua.c b/src/bind_tolua.c index d681b9088..3812e7689 100644 --- a/src/bind_tolua.c +++ b/src/bind_tolua.c @@ -14,7 +14,8 @@ #include -void tolua_bind_open(lua_State * L) { +void tolua_bind_open(lua_State * L) +{ tolua_eressea_open(L); tolua_process_open(L); tolua_settings_open(L); diff --git a/src/bindings.c b/src/bindings.c index 5cb758175..f2beb6d06 100755 --- a/src/bindings.c +++ b/src/bindings.c @@ -3,6 +3,7 @@ #endif #include "bindings.h" +#include "bind_tolua.h" #include "console.h" #include "gamedb.h" @@ -865,8 +866,6 @@ static int lua_rng_default(lua_State *L) { return 0; } -void tolua_bind_open(lua_State * L); - int tolua_bindings_open(lua_State * L, const dictionary *inifile) { tolua_open(L); diff --git a/src/bindings.h b/src/bindings.h index 144f65ca6..f0259bc6e 100755 --- a/src/bindings.h +++ b/src/bindings.h @@ -12,6 +12,8 @@ extern "C" { int tolua_selist_push(struct lua_State *L, const char *list_type, const char *elem_type, struct selist *list); + void bind_monsters(struct lua_State *L); + int log_lua_error(struct lua_State *L); void lua_done(struct lua_State *L); diff --git a/src/chaos.c b/src/chaos.c index ef1dffca6..2639592f0 100644 --- a/src/chaos.c +++ b/src/chaos.c @@ -86,6 +86,7 @@ static void chaos(region * r) if (u && playerrace(u_race(u))) { ADDMSG(&u->faction->msgs, msg_message("chaos_disease", "unit", u)); u_setfaction(u, get_monsters()); + u_freeorders(u); u_setrace(u, get_race(RC_GHOUL)); } } diff --git a/src/economy.c b/src/economy.c index 7cd28f6b9..35e08ade6 100644 --- a/src/economy.c +++ b/src/economy.c @@ -292,6 +292,7 @@ static int forget_cmd(unit * u, order * ord) if (ufam) { a_removeall(&ufam->attribs, NULL); u_setfaction(ufam, get_monsters()); + u_freeorders(ufam); unit_convert_race(ufam, NULL, "ghost"); } a_removeall(&u->attribs, &at_mage); diff --git a/src/give.c b/src/give.c index 47f472ec1..ac792da9a 100644 --- a/src/give.c +++ b/src/give.c @@ -709,6 +709,7 @@ void give_unit(unit * u, unit * u2, order * ord) } add_give_person(u, u2, u->number, ord, 0); u_setfaction(u, u2->faction); + u_freeorders(u); u2->faction->newbies += u->number; } diff --git a/src/kernel/unit.c b/src/kernel/unit.c index 06463820c..e9ec8e657 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -886,6 +886,12 @@ struct building *inside_building(const struct unit *u) return NULL; } +void u_freeorders(unit *u) +{ + free_orders(&u->orders); + set_order(&u->thisorder, NULL); +} + void u_setfaction(unit * u, faction * f) { if (u->faction == f) @@ -896,9 +902,6 @@ void u_setfaction(unit * u, faction * f) u->faction->num_people -= u->number; } set_group(u, NULL); - free_orders(&u->orders); - set_order(&u->thisorder, NULL); - if (u->nextF) { u->nextF->prevF = u->prevF; } diff --git a/src/kernel/unit.h b/src/kernel/unit.h index c77cfdb7a..7fc6fa730 100644 --- a/src/kernel/unit.h +++ b/src/kernel/unit.h @@ -179,6 +179,7 @@ extern "C" { /* cleanup code for this module */ void free_units(void); void u_setfaction(struct unit *u, struct faction *f); + void u_freeorders(struct unit *u); void set_number(struct unit *u, int count); int invisible(const struct unit *target, const struct unit *viewer); diff --git a/src/laws.c b/src/laws.c index 61262b75d..10975c6a7 100644 --- a/src/laws.c +++ b/src/laws.c @@ -924,6 +924,7 @@ int leave_cmd(unit * u, struct order *ord) void transfer_faction(faction *fsrc, faction *fdst) { unit *u; skill_t sk; + int hmax, hnow; int skill_count[MAXSKILLS]; int skill_limit[MAXSKILLS]; @@ -945,19 +946,28 @@ void transfer_faction(faction *fsrc, faction *fdst) { } } + hnow = countheroes(fdst); + hmax = maxheroes(fdst); u = fsrc->units; while (u) { unit *unext = u->nextF; if (u_race(u) == fdst->race) { - u->flags &= ~UFL_HERO; - if (give_unit_allowed(u) == 0) { + if (u->flags & UFL_HERO) { + if (u->number + hnow > hmax) { + u->flags &= ~UFL_HERO; + } + else { + hnow += u->number; + } + } + if (give_unit_allowed(u) == 0 && !get_mage(u)) { 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; } @@ -3764,6 +3774,10 @@ void process(void) } if (ord) { porder->data.per_order.process(u, ord); + if (!u->orders) { + /* GIVE UNIT or QUIT delete all orders of the unit, stop */ + break; + } } } if (!ord || *ordp == ord) diff --git a/src/laws.test.c b/src/laws.test.c index 8b92629b2..45a5f3023 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -2106,6 +2106,41 @@ static void test_quit_transfer_limited(CuTest *tc) { test_teardown(); } +/** + * Mages cannot be transfered. At all. + */ +static void test_quit_transfer_mages(CuTest *tc) { + faction *f1, *f2; + unit *u1, *u2; + region *r; + + test_setup(); + config_set_int("rules.maxskills.magic", 2); + 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)); + + f1->magiegebiet = M_GWYRRD; + set_level(u1, SK_MAGIC, 1); + create_mage(u1, M_GWYRRD); + + f2->magiegebiet = M_GWYRRD; + set_level(u2, SK_MAGIC, 1); + create_mage(u2, M_GWYRRD); + + quit_cmd(u1, u1->thisorder); + CuAssertPtrEquals(tc, f1, u1->faction); + CuAssertIntEquals(tc, FFL_QUIT, f1->flags & FFL_QUIT); + + test_teardown(); +} + /** * Only units of the same race can be gifted to another faction. */ @@ -2271,6 +2306,7 @@ CuSuite *get_laws_suite(void) 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_mages); SUITE_ADD_TEST(suite, test_quit_transfer_hero); SUITE_ADD_TEST(suite, test_transfer_faction); #endif diff --git a/src/main.c b/src/main.c index cfc2f9c22..130a2f6f7 100644 --- a/src/main.c +++ b/src/main.c @@ -290,8 +290,6 @@ void locale_init(void) } } -extern void bind_monsters(lua_State *L); - int main(int argc, char **argv) { int err = 0; diff --git a/src/monsters.c b/src/monsters.c index 7cdebfd6c..6f20a8c8b 100644 --- a/src/monsters.c +++ b/src/monsters.c @@ -174,6 +174,7 @@ void monsters_desert(struct faction *monsters) ADDMSG(&u->faction->msgs, msg_message("desertion", "unit region", u, r)); u_setfaction(u, monsters); + u_freeorders(u); } } } @@ -1157,6 +1158,7 @@ void monster_kills_peasants(unit * u) void make_zombie(unit * u) { u_setfaction(u, get_monsters()); + u_freeorders(u); scale_number(u, 1); u->hp = unit_max_hp(u) * u->number; u_setrace(u, get_race(RC_ZOMBIE)); diff --git a/src/spells.c b/src/spells.c index 200e87f62..6cff4bb9b 100644 --- a/src/spells.c +++ b/src/spells.c @@ -3553,7 +3553,7 @@ static int sp_charmingsong(castorder * co) /* setze Partei um und loesche langen Befehl aus Sicherheitsgruenden */ u_setfaction(target, mage->faction); - set_order(&target->thisorder, NULL); + u_freeorders(target); /* setze Parteitarnung, damit nicht sofort klar ist, wer dahinter * steckt */ @@ -3796,7 +3796,7 @@ static int sp_migranten(castorder * co) return 0; } u_setfaction(target, mage->faction); - set_order(&target->thisorder, NULL); + u_freeorders(target); /* Erfolg melden */ ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "sp_migranten", diff --git a/src/triggers/changefaction.c b/src/triggers/changefaction.c index 36e40ed46..85403083b 100644 --- a/src/triggers/changefaction.c +++ b/src/triggers/changefaction.c @@ -48,7 +48,9 @@ static int changefaction_handle(trigger * t, void *data) */ changefaction_data *td = (changefaction_data *)t->data.v; if (td->unit && td->faction) { - u_setfaction(td->unit, td->faction); + unit * u = td->unit; + u_setfaction(u, td->faction); + u_freeorders(u); } else { log_error("could not perform changefaction::handle()\n");