From a8bfa5e0954790113b2e84c8d9ed700d88c363ae Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 31 Aug 2017 21:19:25 +0200 Subject: [PATCH 1/4] try fixing familars without a mage. --- src/economy.c | 4 +-- src/kernel/save.c | 61 ++++++++++++++++++++++++++++++++++++++++++++- src/magic.c | 13 +--------- src/magic.h | 28 ++++++++++----------- src/util/gamedata.h | 3 ++- 5 files changed, 78 insertions(+), 31 deletions(-) diff --git a/src/economy.c b/src/economy.c index 546f7876e..74595a335 100644 --- a/src/economy.c +++ b/src/economy.c @@ -663,8 +663,8 @@ static int forget_cmd(unit * u, order * ord) sk = get_skill(s, u->faction->locale); if (sk != NOSKILL) { - if (sk == SK_MAGIC && (u_race(u)->flags & RCF_FAMILIAR)) { - /* some races cannot forget their innate magical abilities */ + if (sk == SK_MAGIC && is_familiar(u)) { + /* some units cannot forget their innate magical abilities */ return 0; } ADDMSG(&u->faction->msgs, msg_message("forget", "unit skill", u, sk)); diff --git a/src/kernel/save.c b/src/kernel/save.c index a3fc7e5af..39fab86af 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -29,6 +29,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "faction.h" #include "group.h" #include "item.h" +#include "magic.h" #include "messages.h" #include "move.h" #include "objtypes.h" @@ -51,6 +52,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include /* util includes */ #include @@ -1607,7 +1609,59 @@ ship *read_ship(struct gamedata *data) } -int read_game(gamedata *data) { +static void fix_familiars(void) { + region *r; + for (r = regions; r; r = r->next) { + unit * u; + for (u = r->units; u; u = u->next) { + if (u->_race != u->faction->race && (u->_race->flags & RCF_FAMILIAR)) { + /* unit is potentially a familiar */ + attrib * a = a_find(u->attribs, &at_mage); + if (a) { + /* unit is magical */ + attrib * am = a_find(u->attribs, &at_familiarmage); + if (!am) { + /* but it is not a familiar? */ + attrib * ae = a_find(u->attribs, &at_eventhandler); + if (ae) { + trigger **tlist; + tlist = get_triggers(ae, "destroy"); + if (tlist) { + trigger *t; + unit *um = NULL; + for (t = *tlist; t; t = t->next) { + if (t->type == &tt_shock) { + um = (unit *)t->data.v; + break; + } + } + if (um) { + attrib *af = a_find(um->attribs, &at_familiar); + log_error("%s seems to be a broken familiar of %s.", + unitname(u), unitname(um)); + if (af) { + unit * uf = (unit *)af->data.v; + log_error("%s already has a familiar: %s.", + unitname(um), unitname(uf)); + } + else { + set_familiar(um, u); + } + } + else { + log_error("%s seems to be a broken familiar with no trigger.", unitname(u)); + } + } + } + } + } + } + } + } +} + +int read_game(gamedata *data) +{ int p, nread; faction *f, **fp; region *r; @@ -1783,6 +1837,11 @@ int read_game(gamedata *data) { } } } + + if (data->version < FAMILIAR_FIX_VERSION) { + fix_familiars(); + } + if (loadplane || maxregions >= 0) { remove_empty_factions(); } diff --git a/src/magic.c b/src/magic.c index 7d33a28e1..f5e7810b4 100644 --- a/src/magic.c +++ b/src/magic.c @@ -2140,16 +2140,6 @@ void free_castorders(castorder * co) return; } -/* ------------------------------------------------------------- */ -/*** - ** at_familiarmage - **/ - -typedef struct familiar_data { - unit *mage; - unit *familiar; -} famililar_data; - bool is_familiar(const unit * u) { attrib *a = a_find(u->attribs, &at_familiarmage); @@ -2182,7 +2172,7 @@ static int sm_familiar(const unit * u, const region * r, skill_t sk, int value) } } -static void set_familiar(unit * mage, unit * familiar) +void set_familiar(unit * mage, unit * familiar) { /* if the skill modifier for the mage does not yet exist, add it */ attrib *a = a_find(mage->attribs, &at_skillmod); @@ -2384,7 +2374,6 @@ static int read_clone(attrib * a, void *owner, struct gamedata *data) } /* mages */ - static int resolve_mage(variant data, void *addr) { unit *mage; diff --git a/src/magic.h b/src/magic.h index 6c905ea68..f5d2ff0f4 100644 --- a/src/magic.h +++ b/src/magic.h @@ -38,6 +38,10 @@ extern "C" { #define IRONGOLEM_CRUMBLE 15 /* monatlich Chance zu zerfallen */ #define STONEGOLEM_CRUMBLE 10 /* monatlich Chance zu zerfallen */ + extern const char *magic_school[MAXMAGIETYP]; + extern struct attrib_type at_familiar; + extern struct attrib_type at_familiarmage; + /* ------------------------------------------------------------- */ /* Spruchparameter * Wir suchen beim Parsen des Befehls erstmal nach lokalen Objekten, @@ -82,11 +86,6 @@ extern "C" { #define TARGET_RESISTS (1<<0) #define TARGET_NOTFOUND (1<<1) - /* ------------------------------------------------------------- */ - /* Magierichtungen */ - - extern const char *magic_school[MAXMAGIETYP]; - /* ------------------------------------------------------------- */ /* Magier: * - Magierichtung @@ -324,16 +323,15 @@ extern "C" { /* Sprüche in der struct region */ /* (sind in curse) */ - extern struct unit *get_familiar(const struct unit *u); - extern struct unit *get_familiar_mage(const struct unit *u); - extern struct unit *get_clone(const struct unit *u); - extern struct unit *get_clone_mage(const struct unit *u); - extern struct attrib_type at_familiar; - extern struct attrib_type at_familiarmage; - extern void remove_familiar(struct unit *mage); - extern bool create_newfamiliar(struct unit *mage, struct unit *familiar); - extern void create_newclone(struct unit *mage, struct unit *familiar); - extern struct unit *has_clone(struct unit *mage); + void set_familiar(struct unit * mage, struct unit * familiar); + struct unit *get_familiar(const struct unit *u); + struct unit *get_familiar_mage(const struct unit *u); + struct unit *get_clone(const struct unit *u); + struct unit *get_clone_mage(const struct unit *u); + void remove_familiar(struct unit *mage); + bool create_newfamiliar(struct unit *mage, struct unit *familiar); + void create_newclone(struct unit *mage, struct unit *familiar); + struct unit *has_clone(struct unit *mage); const char *spell_info(const struct spell *sp, const struct locale *lang); diff --git a/src/util/gamedata.h b/src/util/gamedata.h index 97f8416b3..058bc3f07 100644 --- a/src/util/gamedata.h +++ b/src/util/gamedata.h @@ -36,10 +36,11 @@ #define NOLANDITEM_VERSION 356 /* land_region has no items */ #define NORCSPELL_VERSION 357 /* data contains no RC_SPELL units */ #define SORTKEYS_VERSION 358 /* at_keys is sorted */ +#define FAMILIAR_FIX_VERSION 359 /* at_keys is sorted */ /* unfinished: */ #define CRYPT_VERSION 400 /* passwords are encrypted */ -#define RELEASE_VERSION SORTKEYS_VERSION /* current datafile */ +#define RELEASE_VERSION FAMILIAR_FIX_VERSION /* current datafile */ #define MIN_VERSION INTPAK_VERSION /* minimal datafile we support */ #define MAX_VERSION RELEASE_VERSION /* change this if we can need to read the future datafile, and we can do so */ From eabf72e60e0e360cddaff12aee87ea04327bb86b Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Fri, 1 Sep 2017 22:04:36 +0200 Subject: [PATCH 2/4] BUG 2362: clone_men hitpoint calculation was wrong. --- src/kernel/unit.c | 2 +- src/kernel/unit.test.c | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/kernel/unit.c b/src/kernel/unit.c index 9c08f9dc1..44e9445b7 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -1060,7 +1060,7 @@ void clone_men(const unit * u, unit * dst, int n) transfer_curse(u, dst, n); } set_number(dst, dst->number + n); - dst->hp += u->hp * dst->number / u->number; + dst->hp += u->hp * n / u->number; assert(dst->hp >= dst->number); /* TODO: Das ist schnarchlahm! und gehoert nicht hierhin */ a = a_find(dst->attribs, &at_effect); diff --git a/src/kernel/unit.test.c b/src/kernel/unit.test.c index 6ab8ee21a..2abb8c85c 100644 --- a/src/kernel/unit.test.c +++ b/src/kernel/unit.test.c @@ -548,6 +548,27 @@ static void test_clone_men(CuTest *tc) { test_cleanup(); } +static void test_transfermen(CuTest *tc) { + unit *u1, *u2; + region *r; + faction *f; + test_setup(); + r = test_create_region(0, 0, NULL); + f = test_create_faction(NULL); + u1 = test_create_unit(f, r); + scale_number(u1, 3500); + u2 = test_create_unit(f, r); + scale_number(u2, 3500); + CuAssertIntEquals(tc, 70000, u1->hp); + CuAssertIntEquals(tc, 70000, u2->hp); + transfermen(u1, u2, u1->number); + CuAssertIntEquals(tc, 7000, u2->number); + CuAssertIntEquals(tc, 140000, u2->hp); + CuAssertIntEquals(tc, 0, u1->number); + CuAssertIntEquals(tc, 0, u1->hp); + test_cleanup(); +} + CuSuite *get_unit_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -557,6 +578,7 @@ CuSuite *get_unit_suite(void) SUITE_ADD_TEST(suite, test_unit_name_from_race); SUITE_ADD_TEST(suite, test_update_monster_name); SUITE_ADD_TEST(suite, test_clone_men); + SUITE_ADD_TEST(suite, test_transfermen); SUITE_ADD_TEST(suite, test_remove_unit); SUITE_ADD_TEST(suite, test_remove_empty_units); SUITE_ADD_TEST(suite, test_remove_units_without_faction); From 108268fa17f1ad40bde9dac1053c04b655b9d9f1 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Fri, 1 Sep 2017 22:10:38 +0200 Subject: [PATCH 3/4] BUG 2269: Vertraute vergessen ihre Magie nicht. --- scripts/tests/economy.lua | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/tests/economy.lua b/scripts/tests/economy.lua index 9289efa2b..719b1f764 100644 --- a/scripts/tests/economy.lua +++ b/scripts/tests/economy.lua @@ -17,15 +17,18 @@ function test_bug_2361_forget_magic() 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:add_order("VERGESSE Magie") u:set_skill('magic', 5) - u.race = 'unicorn' - process_orders() - assert_equal(5, u:get_skill('magic')) - u.race = 'human' + 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')) + assert_equal(5, uf:get_skill('magic')) end function test_mine_bonus() From 30b0b2ad810a2e0840672c3ad73f2b3aa7fc7c4f Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 2 Sep 2017 17:36:09 +0200 Subject: [PATCH 4/4] fix a lot of memory leaks that were exposed by unit tests. --- src/kernel/build.c | 1 + src/kernel/item.c | 3 ++- src/tests.test.c | 3 ++- src/util/message.test.c | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/kernel/build.c b/src/kernel/build.c index a6e8edc77..1f4e143df 100644 --- a/src/kernel/build.c +++ b/src/kernel/build.c @@ -991,6 +991,7 @@ void free_construction(struct construction *cons) { while (cons) { construction *next = cons->improvement; + free(cons->name); free(cons->materials); free(cons); cons = next; diff --git a/src/kernel/item.c b/src/kernel/item.c index 04274d633..8639ac5d1 100644 --- a/src/kernel/item.c +++ b/src/kernel/item.c @@ -517,6 +517,7 @@ static int icache_size; #define ICACHE_MAX 100 void item_done(void) { + icache_size = ICACHE_MAX; i_freeall(&icache); icache_size = 0; } @@ -951,7 +952,7 @@ void write_items(struct storage *store, item * ilist) static void free_itype(item_type *itype) { assert(itype); - free(itype->construction); + free_construction(itype->construction); free(itype->_appearance[0]); free(itype->_appearance[1]); free(itype); diff --git a/src/tests.test.c b/src/tests.test.c index 880956e8b..b2a90526d 100644 --- a/src/tests.test.c +++ b/src/tests.test.c @@ -27,11 +27,12 @@ static void test_resources(CuTest *tc) { rtype = rt_get_or_create("stone"); CuAssertPtrEquals(tc, (void *)rtype, (void *)rt_find("stone")); CuAssertPtrEquals(tc, (void *)rtype, (void *)get_resourcetype(R_STONE)); - test_cleanup(); + free_resources(); CuAssertPtrEquals(tc, 0, rt_find("stone")); CuAssertPtrEquals(tc, 0, rt_find("peasant")); rtype = rt_get_or_create("stone"); CuAssertPtrEquals(tc, (void *)rtype, (void *)get_resourcetype(R_STONE)); + test_cleanup(); } CuSuite *get_tests_suite(void) diff --git a/src/util/message.test.c b/src/util/message.test.c index d114e33f2..7d5dc03e9 100644 --- a/src/util/message.test.c +++ b/src/util/message.test.c @@ -9,6 +9,7 @@ static void test_mt_new(CuTest *tc) message_type *mt; test_setup(); mt = mt_new_va("test", "name:string", "number:int", NULL); + mt_register(mt); CuAssertPtrNotNull(tc, mt); CuAssertStrEquals(tc, "test", mt->name); CuAssertIntEquals(tc, 2, mt->nparameters);