From c01e9b24ad93fcc017ee46578096bccc3c7f2c85 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 29 Apr 2018 15:09:07 +0200 Subject: [PATCH 01/13] unit tests for racial weapon permissions. --- src/battle.test.c | 49 ++++++++++++++++++++++++++++++++++++++++++ src/kernel/race.test.c | 13 +++++++++++ 2 files changed, 62 insertions(+) diff --git a/src/battle.test.c b/src/battle.test.c index 7d968c091..865fc08c2 100644 --- a/src/battle.test.c +++ b/src/battle.test.c @@ -68,6 +68,54 @@ static void test_make_fighter(CuTest * tc) test_teardown(); } +static void test_select_weapon(CuTest *tc) { + item_type *itype; + weapon_type *wtype; + unit *au; + fighter *af; + battle *b; + race * rc; + + test_setup(); + au = test_create_unit(test_create_faction(NULL), test_create_plain(0, 0)); + itype = test_create_itemtype("halberd"); + wtype = new_weapontype(itype, 0, frac_zero, NULL, 0, 0, 0, SK_MELEE); + i_change(&au->items, itype, 1); + rc = test_create_race("smurf"); + CuAssertIntEquals(tc, 0, rc->mask_item & au->_race->mask_item); + + b = make_battle(au->region); + af = make_fighter(b, au, make_side(b, au->faction, 0, 0, 0), false); + CuAssertPtrNotNull(tc, af->weapons); + CuAssertIntEquals(tc, 1, af->weapons[0].count); + free_battle(b); + + itype->mask_deny = rc_mask(au->_race); + b = make_battle(au->region); + af = make_fighter(b, au, make_side(b, au->faction, 0, 0, 0), false); + CuAssertPtrNotNull(tc, af->weapons); + CuAssertIntEquals(tc, 0, af->weapons[0].count); + free_battle(b); + + itype->mask_deny = 0; + itype->mask_allow = rc_mask(rc); + b = make_battle(au->region); + af = make_fighter(b, au, make_side(b, au->faction, 0, 0, 0), false); + CuAssertPtrNotNull(tc, af->weapons); + CuAssertIntEquals(tc, 0, af->weapons[0].count); + free_battle(b); + + itype->mask_deny = 0; + itype->mask_allow = rc_mask(au->_race); + b = make_battle(au->region); + af = make_fighter(b, au, make_side(b, au->faction, 0, 0, 0), false); + CuAssertPtrNotNull(tc, af->weapons); + CuAssertIntEquals(tc, 1, af->weapons[0].count); + free_battle(b); + + test_teardown(); +} + static building_type * setup_castle(void) { building_type * btype; construction *cons; @@ -583,6 +631,7 @@ CuSuite *get_battle_suite(void) { CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_make_fighter); + SUITE_ADD_TEST(suite, test_select_weapon); SUITE_ADD_TEST(suite, test_battle_skilldiff); SUITE_ADD_TEST(suite, test_battle_skilldiff_building); SUITE_ADD_TEST(suite, test_defenders_get_building_bonus); diff --git a/src/kernel/race.test.c b/src/kernel/race.test.c index d695969b7..dcbe26e0e 100644 --- a/src/kernel/race.test.c +++ b/src/kernel/race.test.c @@ -26,6 +26,18 @@ static void test_rc_name(CuTest *tc) { test_teardown(); } +static void test_rc_item_mask(CuTest *tc) { + struct race *rc; + test_setup(); + rc = rc_get_or_create("hooman"); + CuAssertIntEquals(tc, 1, rc->mask_item); + rc = rc_get_or_create("aelf"); + CuAssertIntEquals(tc, 2, rc->mask_item); + rc = rc_get_or_create("dorf"); + CuAssertIntEquals(tc, 4, rc->mask_item); + test_teardown(); +} + static void test_rc_defaults(CuTest *tc) { struct race *rc; test_setup(); @@ -193,6 +205,7 @@ CuSuite *get_race_suite(void) SUITE_ADD_TEST(suite, test_old_race); SUITE_ADD_TEST(suite, test_rc_name); SUITE_ADD_TEST(suite, test_rc_defaults); + SUITE_ADD_TEST(suite, test_rc_item_mask); SUITE_ADD_TEST(suite, test_rc_find); SUITE_ADD_TEST(suite, test_rc_mask); SUITE_ADD_TEST(suite, test_rc_set_param); From f6735049d8b14e559f172757a34169a5b82d543d Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 29 Apr 2018 15:24:36 +0200 Subject: [PATCH 02/13] add a few tests for fighters equipping the right weapons and armor. --- src/battle.h | 2 +- src/battle.test.c | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/battle.h b/src/battle.h index fd31a0284..f2dd1fea3 100644 --- a/src/battle.h +++ b/src/battle.h @@ -143,7 +143,7 @@ extern "C" { } troop; typedef struct armor { - struct armor *next; + struct armor *next; /* TODO: make this an array, not a list, like weapon */ const struct armor_type *atype; int count; } armor; diff --git a/src/battle.test.c b/src/battle.test.c index 865fc08c2..69fac2425 100644 --- a/src/battle.test.c +++ b/src/battle.test.c @@ -68,7 +68,7 @@ static void test_make_fighter(CuTest * tc) test_teardown(); } -static void test_select_weapon(CuTest *tc) { +static void test_select_weapon_restricted(CuTest *tc) { item_type *itype; weapon_type *wtype; unit *au; @@ -88,6 +88,7 @@ static void test_select_weapon(CuTest *tc) { af = make_fighter(b, au, make_side(b, au->faction, 0, 0, 0), false); CuAssertPtrNotNull(tc, af->weapons); CuAssertIntEquals(tc, 1, af->weapons[0].count); + CuAssertIntEquals(tc, 0, af->weapons[1].count); free_battle(b); itype->mask_deny = rc_mask(au->_race); @@ -111,6 +112,35 @@ static void test_select_weapon(CuTest *tc) { af = make_fighter(b, au, make_side(b, au->faction, 0, 0, 0), false); CuAssertPtrNotNull(tc, af->weapons); CuAssertIntEquals(tc, 1, af->weapons[0].count); + CuAssertIntEquals(tc, 0, af->weapons[1].count); + free_battle(b); + + test_teardown(); +} + +static void test_select_armor(CuTest *tc) { + item_type *itype, *iscale; + unit *au; + fighter *af; + battle *b; + + test_setup(); + au = test_create_unit(test_create_faction(NULL), test_create_plain(0, 0)); + itype = test_create_itemtype("plate"); + new_armortype(itype, 0.0, frac_zero, 1, 0); + i_change(&au->items, itype, 2); + iscale = test_create_itemtype("scale"); + new_armortype(iscale, 0.0, frac_zero, 2, 0); + i_change(&au->items, iscale, 1); + + b = make_battle(au->region); + af = make_fighter(b, au, make_side(b, au->faction, 0, 0, 0), false); + CuAssertPtrNotNull(tc, af->armors); + CuAssertIntEquals(tc, 1, af->armors->count); + CuAssertPtrEquals(tc, iscale->rtype->atype, (armor_type *)af->armors->atype); + CuAssertIntEquals(tc, 2, af->armors->next->count); + CuAssertPtrEquals(tc, itype->rtype->atype, (armor_type *)af->armors->next->atype); + CuAssertPtrEquals(tc, NULL, af->armors->next->next); free_battle(b); test_teardown(); @@ -631,7 +661,8 @@ CuSuite *get_battle_suite(void) { CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_make_fighter); - SUITE_ADD_TEST(suite, test_select_weapon); + SUITE_ADD_TEST(suite, test_select_weapon_restricted); + SUITE_ADD_TEST(suite, test_select_armor); SUITE_ADD_TEST(suite, test_battle_skilldiff); SUITE_ADD_TEST(suite, test_battle_skilldiff_building); SUITE_ADD_TEST(suite, test_defenders_get_building_bonus); From 6b38fdf4e6c91d5c89e2ea47337aa8d1c1e65af5 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 30 Apr 2018 20:25:58 +0200 Subject: [PATCH 03/13] fix build --- src/battle.test.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/battle.test.c b/src/battle.test.c index 69fac2425..d55ddd4a7 100644 --- a/src/battle.test.c +++ b/src/battle.test.c @@ -70,7 +70,6 @@ static void test_make_fighter(CuTest * tc) static void test_select_weapon_restricted(CuTest *tc) { item_type *itype; - weapon_type *wtype; unit *au; fighter *af; battle *b; @@ -79,7 +78,7 @@ static void test_select_weapon_restricted(CuTest *tc) { test_setup(); au = test_create_unit(test_create_faction(NULL), test_create_plain(0, 0)); itype = test_create_itemtype("halberd"); - wtype = new_weapontype(itype, 0, frac_zero, NULL, 0, 0, 0, SK_MELEE); + new_weapontype(itype, 0, frac_zero, NULL, 0, 0, 0, SK_MELEE); i_change(&au->items, itype, 1); rc = test_create_race("smurf"); CuAssertIntEquals(tc, 0, rc->mask_item & au->_race->mask_item); From 22734a4ae41c6eb894115c78e0c3ac3d76a6b352 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 30 Apr 2018 22:52:38 +0200 Subject: [PATCH 04/13] refactor building stages into build_stages(). TODO: remove them from build(). --- src/kernel/build.c | 101 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 25 deletions(-) diff --git a/src/kernel/build.c b/src/kernel/build.c index 96aee5662..c36c21a7f 100644 --- a/src/kernel/build.c +++ b/src/kernel/build.c @@ -679,7 +679,69 @@ int maxbuild(const unit * u, const construction * cons) return maximum; } -/** old build routines */ +static int build_failure(unit *u, order *ord, const building_type *btype, int want, int err) { + switch (err) { + case ECOMPLETE: + /* the building is already complete */ + cmistake(u, ord, 4, MSG_PRODUCE); + break; + case ENOMATERIALS: + ADDMSG(&u->faction->msgs, msg_materials_required(u, ord, + btype->construction, want)); + break; + case ELOWSKILL: + case ENEEDSKILL: + /* no skill, or not enough skill points to build */ + cmistake(u, ord, 50, MSG_PRODUCE); + break; + } + return err; +} + +int build_stages(unit *u, construction * con, int built, int n) { + int made = 0; + while (con) { + if (con->maxsize < 0 || con->maxsize > built) { + int err, want = INT_MAX; + if (n < INT_MAX) { + /* do not build more than n total */ + want = n - made; + } + if (con->maxsize > 0) { + /* do not build more than the rest of the stage */ + int todo = con->maxsize - built; + if (todo < want) { + want = todo; + } + } + err = build(u, con, built, want, 0); + if (err < 0) { + if (made == 0) { + /* could not make any part at all */ + return err; + } + else { + /* could not build any part of this stage (low skill, etc). */ + break; + } + } + else { + /* err is the amount we built of this stage */ + made += err; + if (err != con->maxsize && con->maxsize > 0) { + /* we did not finish the stage, can quit here */ + break; + } + } + } + /* build the next stage: */ + if (built >= con->maxsize && con->maxsize > 0) { + built -= con->maxsize; + } + con = con->improvement; + } + return made; +} int build_building(unit * u, const building_type * btype, int id, int want, order * ord) @@ -783,30 +845,19 @@ build_building(unit * u, const building_type * btype, int id, int want, order * } } } - built = build(u, btype->construction, built, n, 0); - switch (built) { - case ECOMPLETE: - /* the building is already complete */ - cmistake(u, ord, 4, MSG_PRODUCE); - break; - case ENOMATERIALS: - ADDMSG(&u->faction->msgs, msg_materials_required(u, ord, - btype->construction, want)); - break; - case ELOWSKILL: - case ENEEDSKILL: - /* no skill, or not enough skill points to build */ - cmistake(u, ord, 50, MSG_PRODUCE); - break; + built = build_stages(u, btype->construction, built, n); + + if (built < 0) { + return build_failure(u, ord, btype, want, built); } - if (built <= 0) { - return built; - } - /* at this point, the building size is increased. */ - if (b == NULL) { + + if (b) { + b->size += built; + } else { /* build a new building */ b = new_building(btype, r, lang); + b->size = built; b->type = btype; fset(b, BLD_MAINTAINED); @@ -818,7 +869,7 @@ build_building(unit * u, const building_type * btype, int id, int want, order * btname = LOC(lang, btype->_name); - if (want - built <= 0) { + if (want <= built) { /* geb�ude fertig */ new_order = default_order(lang); } @@ -850,7 +901,9 @@ build_building(unit * u, const building_type * btype, int id, int want, order * free_order(new_order); } - b->size += built; + ADDMSG(&u->faction->msgs, msg_message("buildbuilding", + "building unit size", b, u, built)); + if (b->type->maxsize > 0 && b->size > b->type->maxsize) { log_error("build: %s has size=%d, maxsize=%d", buildingname(b), b->size, b->type->maxsize); } @@ -858,8 +911,6 @@ build_building(unit * u, const building_type * btype, int id, int want, order * update_lighthouse(b); - ADDMSG(&u->faction->msgs, msg_message("buildbuilding", - "building unit size", b, u, built)); return built; } From e0e873044de40411716f51b8e8c90ef69b867350 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 30 Apr 2018 23:11:13 +0200 Subject: [PATCH 05/13] fix stone golems (assert). remove improvement logic from build(). --- scripts/tests/e2/e2features.lua | 6 +++--- src/kernel/build.c | 24 ++++-------------------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua index d9a7da4f8..0e8e3a24d 100644 --- a/scripts/tests/e2/e2features.lua +++ b/scripts/tests/e2/e2features.lua @@ -339,11 +339,11 @@ function test_stonegolems() u1:set_skill("building", 1) u2:set_skill("building", 1) --- test that no server crash occur +-- test that no server crash occurs u1:clear_orders() u1:add_order("Mache Burg") process_orders() - assert_equal(0 ,u1.number, "There shoud be no Stone Golems") + assert_equal(0, u1.number, "There should be no more stone golems") -- end test server crash -- test that Stone Golems build for four stones @@ -351,7 +351,7 @@ function test_stonegolems() u2:add_order("MACHE 4 BURG " .. itoa36(c1.id)) process_orders() assert_equal(230, c1.size, "resulting size should be 230") - assert_equal(1 ,u2.number, "There shoud be one Stone Golems") + assert_equal(1, u2.number, "There should be one stone golem") -- end test Stone Golems four stones end diff --git a/src/kernel/build.c b/src/kernel/build.c index c36c21a7f..e2f4f9503 100644 --- a/src/kernel/build.c +++ b/src/kernel/build.c @@ -512,15 +512,16 @@ int build(unit * u, const construction * ctype, int completed, int want, int ski if (want <= 0) return 0; - if (con == NULL) + if (con == NULL) { return ENOMATERIALS; - if (con->improvement == NULL && completed == con->maxsize) + } + if (completed == con->maxsize) { return ECOMPLETE; + } if (con->skill != NOSKILL) { int effsk; int dm = get_effect(u, oldpotiontype[P_DOMORE]); - assert(u->number); basesk = effskill(u, con->skill, 0); if (basesk == 0) return ENEEDSKILL; @@ -544,23 +545,6 @@ int build(unit * u, const construction * ctype, int completed, int want, int ski for (; want > 0 && skills > 0;) { int err, n; - /* skip over everything that's already been done: - * type->improvement==NULL means no more improvements, but no size limits - * type->improvement==type means build another object of the same time - * while material lasts type->improvement==x means build x when type - * is finished */ - while (con && con->improvement && - con->improvement != con && - con->maxsize > 0 && con->maxsize <= completed) { - completed -= con->maxsize; - con = con->improvement; - } - if (con == NULL) { - if (made == 0) - return ECOMPLETE; - break; /* completed */ - } - /* Hier ist entweder maxsize == -1, oder completed < maxsize. * Andernfalls ist das Datenfile oder sonstwas kaputt... * (enno): Nein, das ist f�r Dinge, bei denen die n�chste Ausbaustufe From e0cae602dd0fe032ded0e8722adbff6d019b8895 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 30 Apr 2018 23:17:48 +0200 Subject: [PATCH 06/13] add a test for building items. --- scripts/tests/e2/e2features.lua | 2 -- scripts/tests/e2/production.lua | 16 ++++++++++++++++ src/kernel/build.c | 4 +--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua index 0e8e3a24d..8fea1641a 100644 --- a/scripts/tests/e2/e2features.lua +++ b/scripts/tests/e2/e2features.lua @@ -284,8 +284,6 @@ function test_block_movement() end end - - function test_block_movement_aots() eressea.settings.set("rules.guard.base_stop_prob", "0.0") eressea.settings.set("rules.guard.skill_stop_prob", "1.0") diff --git a/scripts/tests/e2/production.lua b/scripts/tests/e2/production.lua index 5eae8ec74..5070d02f6 100644 --- a/scripts/tests/e2/production.lua +++ b/scripts/tests/e2/production.lua @@ -12,6 +12,22 @@ local function create_faction(race) return faction.create(race, race .. '@example.com', "de") end +function test_produce_multi() + local r = region.create(0, 0, 'mountain') + local f = create_faction('human') + local u = unit.create(f, r, 1) + -- sword needs skill=3, iron=1 + u:set_skill('weaponsmithing', 15) + u:add_item('iron', 5) + + turn_begin() + u:add_order("MACHE 6 Schwert") + + turn_process() + assert_equal(5, u:get_item('sword')) + assert_equal(0, u:get_item('iron')) +end + function test_greatbow_needs_elf() -- only elves can build a greatbow local r = region.create(0, 0, 'mountain') diff --git a/src/kernel/build.c b/src/kernel/build.c index e2f4f9503..ab2828873 100644 --- a/src/kernel/build.c +++ b/src/kernel/build.c @@ -589,9 +589,7 @@ int build(unit * u, const construction * ctype, int completed, int want, int ski if (con->maxsize > 0) { int req = con->maxsize - completed; if (req < n) n = req; - if (con->improvement == NULL) { - want = n; - } + want = n; } n = count_materials(u, con, n, completed); From b44e4e747d549a613c0206f042b4d30ff59b1164 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 30 Apr 2018 23:23:48 +0200 Subject: [PATCH 07/13] ships and items never have multi-stage builds. we are going to make sure of this soon. --- src/kernel/build.c | 1 - src/kernel/ship.c | 5 ----- src/move.c | 2 -- src/report.c | 1 - 4 files changed, 9 deletions(-) diff --git a/src/kernel/build.c b/src/kernel/build.c index ab2828873..bb6420717 100644 --- a/src/kernel/build.c +++ b/src/kernel/build.c @@ -642,7 +642,6 @@ message *msg_materials_required(unit * u, order * ord, int maxbuild(const unit * u, const construction * cons) /* calculate maximum size that can be built from available material */ -/* !! ignores maximum objectsize and improvements... */ { int c; int maximum = INT_MAX; diff --git a/src/kernel/ship.c b/src/kernel/ship.c index 8b38f1375..3f4c099ac 100644 --- a/src/kernel/ship.c +++ b/src/kernel/ship.c @@ -328,7 +328,6 @@ int shipspeed(const ship * sh, const unit * u) assert(u->ship == sh); assert(u == ship_owner(sh)); assert(sh->type->construction); - assert(sh->type->construction->improvement == NULL); /* sonst ist construction::size nicht ship_type::maxsize */ k = sh->type->range; if (sh->size != sh->type->construction->maxsize) @@ -396,10 +395,6 @@ int shipcapacity(const ship * sh) { int i = sh->type->cargo; - /* sonst ist construction:: size nicht ship_type::maxsize */ - assert(!sh->type->construction - || sh->type->construction->improvement == NULL); - if (sh->type->construction && sh->size != sh->type->construction->maxsize) return 0; diff --git a/src/move.c b/src/move.c index bb6b9d212..3f940c78a 100644 --- a/src/move.c +++ b/src/move.c @@ -847,7 +847,6 @@ static void drifting_ships(region * r) /* Kapitän da? Beschädigt? Genügend Matrosen? * Genügend leicht? Dann ist alles OK. */ - assert(sh->type->construction->improvement == NULL); /* sonst ist construction::size nicht ship_type::maxsize */ if (captain && sh->size == sh->type->construction->maxsize && enoughsailors(sh, crew_skill(sh)) && cansail(r, sh)) { shp = &sh->next; @@ -1669,7 +1668,6 @@ static bool ship_ready(const region * r, unit * u, order * ord) return false; } if (u->ship->type->construction) { - assert(!u->ship->type->construction->improvement); /* sonst ist construction::size nicht ship_type::maxsize */ if (u->ship->size != u->ship->type->construction->maxsize) { cmistake(u, ord, 15, MSG_MOVE); return false; diff --git a/src/report.c b/src/report.c index 319203e3b..c0b84f181 100644 --- a/src/report.c +++ b/src/report.c @@ -1803,7 +1803,6 @@ nr_ship(struct stream *out, const region *r, const ship * sh, const faction * f, if (wrptr(&bufp, &size, bytes) != 0) WARN_STATIC_BUFFER(); - assert(sh->type->construction->improvement == NULL); /* sonst ist construction::size nicht ship_type::maxsize */ if (sh->size != sh->type->construction->maxsize) { bytes = snprintf(bufp, size, ", %s (%d/%d)", LOC(f->locale, "nr_undercons"), sh->size, From c7ae070fa78f72d9f12dce4847719a81021171f5 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 1 May 2018 07:02:55 +0200 Subject: [PATCH 08/13] nobody needs the construct_t enum. --- src/kernel/build.h | 7 ------- src/xmlreader.c | 13 ++++++------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/kernel/build.h b/src/kernel/build.h index c201d900b..5f93884cd 100644 --- a/src/kernel/build.h +++ b/src/kernel/build.h @@ -37,14 +37,7 @@ extern "C" { int number; } requirement; - typedef enum construct_t { - CONS_OTHER, - CONS_ITEM, - CONS_BUILDING - } construct_t; - typedef struct construction { - construct_t type; skill_t skill; /* skill req'd per point of size */ int minskill; /* skill req'd per point of size */ diff --git a/src/xmlreader.c b/src/xmlreader.c index adddb808e..03f016986 100644 --- a/src/xmlreader.c +++ b/src/xmlreader.c @@ -214,9 +214,9 @@ xml_readrequirements(xmlNodePtr * nodeTab, int nodeNr, requirement ** reqArray) } } -void +static void xml_readconstruction(xmlXPathContextPtr xpath, xmlNodeSetPtr nodeSet, -construction ** consPtr, construct_t type) +construction ** consPtr, bool is_building) { xmlNodePtr pushNode = xpath->node; int k; @@ -243,13 +243,12 @@ construction ** consPtr, construct_t type) *consPtr = con = (construction *)calloc(sizeof(construction), 1); consPtr = &con->improvement; - con->type = type; con->skill = sk; con->maxsize = xml_ivalue(node, "maxsize", -1); con->minskill = xml_ivalue(node, "minskill", -1); con->reqsize = xml_ivalue(node, "reqsize", 1); - if (type == CONS_BUILDING) { + if (is_building) { propValue = xmlGetProp(node, BAD_CAST "name"); if (propValue != NULL) { con->name = str_strdup((const char *)propValue); @@ -342,7 +341,7 @@ static int parse_buildings(xmlDocPtr doc) /* reading eressea/buildings/building/construction */ xpath->node = node; result = xmlXPathEvalExpression(BAD_CAST "construction", xpath); - xml_readconstruction(xpath, result->nodesetval, &btype->construction, CONS_BUILDING); + xml_readconstruction(xpath, result->nodesetval, &btype->construction, true); xmlXPathFreeObject(result); /* reading eressea/buildings/building/function */ @@ -442,7 +441,7 @@ static int parse_ships(xmlDocPtr doc) /* reading eressea/ships/ship/construction */ xpath->node = node; result = xmlXPathEvalExpression(BAD_CAST "construction", xpath); - xml_readconstruction(xpath, result->nodesetval, &st->construction, CONS_OTHER); + xml_readconstruction(xpath, result->nodesetval, &st->construction, false); xmlXPathFreeObject(result); for (child = node->children; child; child = child->next) { @@ -688,7 +687,7 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype) /* reading item/construction */ xpath->node = node; result = xmlXPathEvalExpression(BAD_CAST "construction", xpath); - xml_readconstruction(xpath, result->nodesetval, &itype->construction, CONS_ITEM); + xml_readconstruction(xpath, result->nodesetval, &itype->construction, false); xmlXPathFreeObject(result); /* reading item/weapon */ From bddf4bff39512830c9670ebee941ccc6c9ba3844 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 1 May 2018 10:47:17 +0200 Subject: [PATCH 09/13] unnecessary xpath wrangling --- src/xmlreader.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/xmlreader.c b/src/xmlreader.c index 03f016986..348146d51 100644 --- a/src/xmlreader.c +++ b/src/xmlreader.c @@ -216,9 +216,8 @@ xml_readrequirements(xmlNodePtr * nodeTab, int nodeNr, requirement ** reqArray) static void xml_readconstruction(xmlXPathContextPtr xpath, xmlNodeSetPtr nodeSet, -construction ** consPtr, bool is_building) + construction **consPtr, bool is_building) { - xmlNodePtr pushNode = xpath->node; int k; for (k = 0; k != nodeSet->nodeNr; ++k) { xmlNodePtr node = nodeSet->nodeTab[k]; @@ -263,7 +262,6 @@ construction ** consPtr, bool is_building) req->nodesetval->nodeNr, &con->materials); xmlXPathFreeObject(req); } - xpath->node = pushNode; } static int From dddbf5287a2e04c284c5047dd15c1305e6f70daf Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 1 May 2018 10:53:12 +0200 Subject: [PATCH 10/13] refactor construction list. --- src/xmlreader.c | 94 +++++++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 42 deletions(-) diff --git a/src/xmlreader.c b/src/xmlreader.c index 348146d51..d98ddaa2b 100644 --- a/src/xmlreader.c +++ b/src/xmlreader.c @@ -214,53 +214,63 @@ xml_readrequirements(xmlNodePtr * nodeTab, int nodeNr, requirement ** reqArray) } } +static construction * +xml_readconstruction(xmlXPathContextPtr xpath, xmlNodePtr node, bool is_building) +{ + construction *con; + xmlChar *propValue; + xmlXPathObjectPtr req; + skill_t sk = NOSKILL; + + propValue = xmlGetProp(node, BAD_CAST "skill"); + if (propValue != NULL) { + sk = findskill((const char *)propValue); + if (sk == NOSKILL) { + log_error("construction requires skill '%s' that does not exist.\n", (const char *)propValue); + xmlFree(propValue); + return NULL; + } + xmlFree(propValue); + } + + con = (construction *)calloc(sizeof(construction), 1); + + con->skill = sk; + con->maxsize = xml_ivalue(node, "maxsize", -1); + con->minskill = xml_ivalue(node, "minskill", -1); + con->reqsize = xml_ivalue(node, "reqsize", 1); + + if (is_building) { + propValue = xmlGetProp(node, BAD_CAST "name"); + if (propValue != NULL) { + con->name = str_strdup((const char *)propValue); + xmlFree(propValue); + } + } + + /* read construction/requirement */ + xpath->node = node; + req = xmlXPathEvalExpression(BAD_CAST "requirement", xpath); + xml_readrequirements(req->nodesetval->nodeTab, + req->nodesetval->nodeNr, &con->materials); + xmlXPathFreeObject(req); + + return con; +} + static void -xml_readconstruction(xmlXPathContextPtr xpath, xmlNodeSetPtr nodeSet, +xml_readconstructions(xmlXPathContextPtr xpath, xmlNodeSetPtr nodeSet, construction **consPtr, bool is_building) { int k; for (k = 0; k != nodeSet->nodeNr; ++k) { xmlNodePtr node = nodeSet->nodeTab[k]; - xmlChar *propValue; - construction *con; - xmlXPathObjectPtr req; - skill_t sk = NOSKILL; + construction *con = xml_readconstruction(xpath, node, is_building); - propValue = xmlGetProp(node, BAD_CAST "skill"); - if (propValue != NULL) { - sk = findskill((const char *)propValue); - if (sk == NOSKILL) { - log_error("construction requires skill '%s' that does not exist.\n", (const char *)propValue); - xmlFree(propValue); - continue; - } - xmlFree(propValue); + if (con) { + *consPtr = con; + consPtr = &con->improvement; } - - assert(*consPtr == NULL); - - *consPtr = con = (construction *)calloc(sizeof(construction), 1); - consPtr = &con->improvement; - - con->skill = sk; - con->maxsize = xml_ivalue(node, "maxsize", -1); - con->minskill = xml_ivalue(node, "minskill", -1); - con->reqsize = xml_ivalue(node, "reqsize", 1); - - if (is_building) { - propValue = xmlGetProp(node, BAD_CAST "name"); - if (propValue != NULL) { - con->name = str_strdup((const char *)propValue); - xmlFree(propValue); - } - } - - /* read construction/requirement */ - xpath->node = node; - req = xmlXPathEvalExpression(BAD_CAST "requirement", xpath); - xml_readrequirements(req->nodesetval->nodeTab, - req->nodesetval->nodeNr, &con->materials); - xmlXPathFreeObject(req); } } @@ -339,7 +349,7 @@ static int parse_buildings(xmlDocPtr doc) /* reading eressea/buildings/building/construction */ xpath->node = node; result = xmlXPathEvalExpression(BAD_CAST "construction", xpath); - xml_readconstruction(xpath, result->nodesetval, &btype->construction, true); + xml_readconstructions(xpath, result->nodesetval, &btype->construction, true); xmlXPathFreeObject(result); /* reading eressea/buildings/building/function */ @@ -439,7 +449,7 @@ static int parse_ships(xmlDocPtr doc) /* reading eressea/ships/ship/construction */ xpath->node = node; result = xmlXPathEvalExpression(BAD_CAST "construction", xpath); - xml_readconstruction(xpath, result->nodesetval, &st->construction, false); + xml_readconstructions(xpath, result->nodesetval, &st->construction, false); xmlXPathFreeObject(result); for (child = node->children; child; child = child->next) { @@ -685,7 +695,7 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype) /* reading item/construction */ xpath->node = node; result = xmlXPathEvalExpression(BAD_CAST "construction", xpath); - xml_readconstruction(xpath, result->nodesetval, &itype->construction, false); + xml_readconstructions(xpath, result->nodesetval, &itype->construction, false); xmlXPathFreeObject(result); /* reading item/weapon */ From 0b097371a19ce24f438c344add27869daa1babd3 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 1 May 2018 11:18:18 +0200 Subject: [PATCH 11/13] items: less xpath, more child iteration. --- src/modules/score.c | 3 ++ src/xmlreader.c | 117 +++++++++++++++++++------------------------- 2 files changed, 53 insertions(+), 67 deletions(-) diff --git a/src/modules/score.c b/src/modules/score.c index 7b6cb07bb..0ffb73301 100644 --- a/src/modules/score.c +++ b/src/modules/score.c @@ -222,6 +222,9 @@ void score(void) int default_score(const item_type *itype) { int result = 0; + if (itype->rtype->wtype || itype->rtype->atype) { + result += 10; + } if (itype->construction) { requirement *req = itype->construction->materials; while (req->number) { diff --git a/src/xmlreader.c b/src/xmlreader.c index d98ddaa2b..fb8f8c18a 100644 --- a/src/xmlreader.c +++ b/src/xmlreader.c @@ -259,13 +259,14 @@ xml_readconstruction(xmlXPathContextPtr xpath, xmlNodePtr node, bool is_building } static void -xml_readconstructions(xmlXPathContextPtr xpath, xmlNodeSetPtr nodeSet, - construction **consPtr, bool is_building) +xml_readconstructions(xmlXPathContextPtr xpath, xmlNodeSetPtr nodeSet, building_type *btype) { + construction **consPtr = &btype->construction; int k; + for (k = 0; k != nodeSet->nodeNr; ++k) { xmlNodePtr node = nodeSet->nodeTab[k]; - construction *con = xml_readconstruction(xpath, node, is_building); + construction *con = xml_readconstruction(xpath, node, true); if (con) { *consPtr = con; @@ -349,7 +350,7 @@ static int parse_buildings(xmlDocPtr doc) /* reading eressea/buildings/building/construction */ xpath->node = node; result = xmlXPathEvalExpression(BAD_CAST "construction", xpath); - xml_readconstructions(xpath, result->nodesetval, &btype->construction, true); + xml_readconstructions(xpath, result->nodesetval, btype); xmlXPathFreeObject(result); /* reading eressea/buildings/building/function */ @@ -446,12 +447,6 @@ static int parse_ships(xmlDocPtr doc) st->range_max = xml_ivalue(node, "maxrange", st->range_max); st->storm = xml_fvalue(node, "storm", st->storm); - /* reading eressea/ships/ship/construction */ - xpath->node = node; - result = xmlXPathEvalExpression(BAD_CAST "construction", xpath); - xml_readconstructions(xpath, result->nodesetval, &st->construction, false); - xmlXPathFreeObject(result); - for (child = node->children; child; child = child->next) { if (strcmp((const char *)child->name, "modifier") == 0) { double value = xml_fvalue(child, "value", 0.0); @@ -464,6 +459,10 @@ static int parse_ships(xmlDocPtr doc) st->df_bonus = (int)value; xmlFree(propValue); } + else if (strcmp((const char *)child->name, "construction") == 0) { + assert(!st->construction); + st->construction = xml_readconstruction(xpath, child, false); + } } /* reading eressea/ships/ship/coast */ xpath->node = node; @@ -498,22 +497,25 @@ static int parse_ships(xmlDocPtr doc) return result; } -static void xml_readpotion(xmlXPathContextPtr xpath, item_type * itype) +static void xml_readpotion(xmlNodePtr node, item_type * itype) { - int level = xml_ivalue(xpath->node, "level", 0); + int level = xml_ivalue(node, "level", 0); + if ((itype->flags & ITF_CANUSE) == 0) { + log_error("potion %s has no use attribute", itype->rtype->_name); + itype->flags |= ITF_CANUSE; + } new_potiontype(itype, level); } -static luxury_type *xml_readluxury(xmlXPathContextPtr xpath, item_type * itype) +static luxury_type *xml_readluxury(xmlNodePtr node, item_type * itype) { - int price = xml_ivalue(xpath->node, "price", 0); + int price = xml_ivalue(node, "price", 0); return new_luxurytype(itype, price); } -static armor_type *xml_readarmor(xmlXPathContextPtr xpath, item_type * itype) +static armor_type *xml_readarmor(xmlNodePtr node, item_type * itype) { - xmlNodePtr node = xpath->node; armor_type *atype = NULL; unsigned int flags = ATF_NONE; int ac = xml_ivalue(node, "ac", 0); @@ -666,10 +668,9 @@ static weapon_type *xml_readweapon(xmlXPathContextPtr xpath, item_type * itype) static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype) { - xmlNodePtr node = xpath->node; + xmlNodePtr child, node = xpath->node; item_type *itype = NULL; unsigned int flags = ITF_NONE; - xmlXPathObjectPtr result; if (xml_bvalue(node, "cursed", false)) flags |= ITF_CURSED; @@ -688,63 +689,45 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype) itype = rtype->itype ? rtype->itype : it_get_or_create(rtype); itype->weight = xml_ivalue(node, "weight", 0); itype->capacity = xml_ivalue(node, "capacity", 0); + itype->score = xml_ivalue(node, "score", 0); + mask_races(node, "allow", &itype->mask_allow); mask_races(node, "deny", &itype->mask_deny); itype->flags |= flags; - /* reading item/construction */ - xpath->node = node; - result = xmlXPathEvalExpression(BAD_CAST "construction", xpath); - xml_readconstructions(xpath, result->nodesetval, &itype->construction, false); - xmlXPathFreeObject(result); - - /* reading item/weapon */ - xpath->node = node; - result = xmlXPathEvalExpression(BAD_CAST "weapon", xpath); - assert(result->nodesetval->nodeNr <= 1); - if (result->nodesetval->nodeNr != 0) { - xpath->node = result->nodesetval->nodeTab[0]; - rtype->wtype = xml_readweapon(xpath, itype); - } - xmlXPathFreeObject(result); - - /* reading item/potion */ - xpath->node = node; - result = xmlXPathEvalExpression(BAD_CAST "potion", xpath); - assert(result->nodesetval->nodeNr <= 1); - if (result->nodesetval->nodeNr != 0) { - if ((itype->flags & ITF_CANUSE) == 0) { - log_error("potion %s has no use attribute", rtype->_name); - itype->flags |= ITF_CANUSE; + for (child = node->children; child; child = child->next) { + if (strcmp((const char *)child->name, "construction") == 0) { + /* reading item/construction */ + assert(!itype->construction); + xpath->node = child; + itype->construction = xml_readconstruction(xpath, child, false); + } + else if (strcmp((const char *)child->name, "weapon") == 0) { + /* reading item/weapon */ + assert(!rtype->wtype); + xpath->node = child; + rtype->wtype = xml_readweapon(xpath, itype); + } + else if (strcmp((const char *)child->name, "armor") == 0) { + /* reading item/weapon */ + assert(!rtype->atype); + rtype->atype = xml_readarmor(child, itype); + } + else if (strcmp((const char *)child->name, "luxury") == 0) { + /* reading item/luxury */ + assert(!rtype->ltype); + rtype->ltype = xml_readluxury(child, itype); + } + else if (strcmp((const char *)child->name, "potion") == 0) { + /* reading item/potion */ + xml_readpotion(child, itype); } - xpath->node = result->nodesetval->nodeTab[0]; - xml_readpotion(xpath, itype); } - xmlXPathFreeObject(result); - /* reading item/luxury */ - xpath->node = node; - result = xmlXPathEvalExpression(BAD_CAST "luxury", xpath); - assert(result->nodesetval->nodeNr <= 1); - if (result->nodesetval->nodeNr != 0) { - xpath->node = result->nodesetval->nodeTab[0]; - rtype->ltype = xml_readluxury(xpath, itype); + if (!itype->score) { + /* do this last, because score depends on itype data */ + itype->score = default_score(itype); } - xmlXPathFreeObject(result); - - /* reading item/armor */ - xpath->node = node; - result = xmlXPathEvalExpression(BAD_CAST "armor", xpath); - assert(result->nodesetval->nodeNr <= 1); - if (result->nodesetval->nodeNr != 0) { - xpath->node = result->nodesetval->nodeTab[0]; - rtype->atype = xml_readarmor(xpath, itype); - } - xmlXPathFreeObject(result); - - itype->score = xml_ivalue(node, "score", 0); - if (!itype->score) itype->score = default_score(itype); - return itype; } From 8a21b42b65038ff444635dfed11faf5b2ad2c1cb Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 1 May 2018 11:22:47 +0200 Subject: [PATCH 12/13] start extracting building-only construction stuff. --- src/xmlreader.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/xmlreader.c b/src/xmlreader.c index fb8f8c18a..59e092d71 100644 --- a/src/xmlreader.c +++ b/src/xmlreader.c @@ -215,7 +215,7 @@ xml_readrequirements(xmlNodePtr * nodeTab, int nodeNr, requirement ** reqArray) } static construction * -xml_readconstruction(xmlXPathContextPtr xpath, xmlNodePtr node, bool is_building) +xml_readconstruction(xmlXPathContextPtr xpath, xmlNodePtr node) { construction *con; xmlChar *propValue; @@ -240,14 +240,6 @@ xml_readconstruction(xmlXPathContextPtr xpath, xmlNodePtr node, bool is_building con->minskill = xml_ivalue(node, "minskill", -1); con->reqsize = xml_ivalue(node, "reqsize", 1); - if (is_building) { - propValue = xmlGetProp(node, BAD_CAST "name"); - if (propValue != NULL) { - con->name = str_strdup((const char *)propValue); - xmlFree(propValue); - } - } - /* read construction/requirement */ xpath->node = node; req = xmlXPathEvalExpression(BAD_CAST "requirement", xpath); @@ -266,7 +258,13 @@ xml_readconstructions(xmlXPathContextPtr xpath, xmlNodeSetPtr nodeSet, building_ for (k = 0; k != nodeSet->nodeNr; ++k) { xmlNodePtr node = nodeSet->nodeTab[k]; - construction *con = xml_readconstruction(xpath, node, true); + construction *con = xml_readconstruction(xpath, node); + xmlChar *propValue = xmlGetProp(node, BAD_CAST "name"); + + if (propValue != NULL) { + con->name = str_strdup((const char *)propValue); + xmlFree(propValue); + } if (con) { *consPtr = con; @@ -461,7 +459,7 @@ static int parse_ships(xmlDocPtr doc) } else if (strcmp((const char *)child->name, "construction") == 0) { assert(!st->construction); - st->construction = xml_readconstruction(xpath, child, false); + st->construction = xml_readconstruction(xpath, child); } } /* reading eressea/ships/ship/coast */ @@ -700,7 +698,7 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype) /* reading item/construction */ assert(!itype->construction); xpath->node = child; - itype->construction = xml_readconstruction(xpath, child, false); + itype->construction = xml_readconstruction(xpath, child); } else if (strcmp((const char *)child->name, "weapon") == 0) { /* reading item/weapon */ From b5b9611a163bee130dfc39f8f403b9f4e1869b2b Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 1 May 2018 15:32:06 +0200 Subject: [PATCH 13/13] eliminate construction.improvement, create building_type.stages instead. --- src/battle.test.c | 15 +++++-- src/bindings.c | 20 --------- src/economy.c | 2 +- src/jsonconf.c | 80 +++++++++++++++++++++++++++-------- src/jsonconf.test.c | 87 +++++++++++++++++++++++--------------- src/kernel/build.c | 60 +++++++++++++------------- src/kernel/build.h | 5 --- src/kernel/build.test.c | 13 +++--- src/kernel/building.c | 51 +++++++++++++--------- src/kernel/building.h | 11 ++++- src/kernel/building.test.c | 42 ++++++++++++------ src/kernel/item.c | 4 +- src/kernel/ship.c | 4 +- src/magic.test.c | 2 +- src/move.c | 8 ---- src/tests.c | 29 ++++++++----- src/xmlreader.c | 13 +++--- 17 files changed, 264 insertions(+), 182 deletions(-) diff --git a/src/battle.test.c b/src/battle.test.c index d55ddd4a7..f293f967d 100644 --- a/src/battle.test.c +++ b/src/battle.test.c @@ -17,9 +17,11 @@ #include #include #include +#include #include +#include #include #include "tests.h" @@ -149,12 +151,17 @@ static building_type * setup_castle(void) { building_type * btype; construction *cons; - btype = bt_get_or_create("castle"); + btype = test_create_buildingtype("castle"); + assert(btype->stages); + assert(btype->stages->construction); + btype->flags |= BTF_FORTIFICATION; - cons = btype->construction = calloc(1, sizeof(construction)); + cons = btype->stages->construction; cons->maxsize = 5; - cons = cons->improvement = calloc(1, sizeof(construction)); + btype->stages->next = calloc(1, sizeof(building_stage)); + cons = calloc(1, sizeof(construction)); cons->maxsize = -1; + btype->stages->next->construction = cons; return btype; } @@ -563,9 +570,11 @@ static void test_battle_skilldiff_building(CuTest *tc) td.index = 0; ta.fighter = setup_fighter(&b, ua); ta.index = 0; + CuAssertIntEquals(tc, 0, buildingeffsize(ud->building, false)); CuAssertIntEquals(tc, 0, skilldiff(ta, td, 0)); ud->building->size = 10; + CuAssertIntEquals(tc, 1, buildingeffsize(ud->building, false)); CuAssertIntEquals(tc, -1, skilldiff(ta, td, 0)); create_curse(NULL, &ud->building->attribs, &ct_magicwalls, 1, 1, 1, 1); diff --git a/src/bindings.c b/src/bindings.c index 90f734fd9..5797f7cb5 100755 --- a/src/bindings.c +++ b/src/bindings.c @@ -770,26 +770,6 @@ static int config_get_btype(lua_State * L) } lua_settable(L, -3); } - if (btype->construction) { - lua_pushstring(L, "build_skill_min"); - lua_pushinteger(L, btype->construction->minskill); - lua_settable(L, -3); - lua_pushstring(L, "build_skill_name"); - lua_pushstring(L, skillnames[btype->construction->skill]); - lua_settable(L, -3); - if (btype->construction->materials) { - int i; - lua_pushstring(L, "materials"); - lua_newtable(L); - for (i = 0; btype->construction->materials[i].number; ++i) { - lua_pushstring(L, - btype->construction->materials[i].rtype->_name); - lua_pushinteger(L, btype->construction->materials[i].number); - lua_settable(L, -3); - } - lua_settable(L, -3); - } - } return 1; } } diff --git a/src/economy.c b/src/economy.c index 4cabb4ab5..a41b4525e 100644 --- a/src/economy.c +++ b/src/economy.c @@ -1409,7 +1409,7 @@ int make_cmd(unit * u, struct order *ord) if (pl && fval(pl, PFL_NOBUILD)) { cmistake(u, ord, 275, MSG_PRODUCE); } - else if (btype->construction) { + else if (btype->stages && btype->stages->construction) { int id = getid(); build_building(u, btype, id, m, ord); } diff --git a/src/jsonconf.c b/src/jsonconf.c index c14ac9216..b16097d51 100644 --- a/src/jsonconf.c +++ b/src/jsonconf.c @@ -151,20 +151,6 @@ static void json_construction(cJSON *json, construction **consp) { cJSON *child; construction * cons; - if (json->type == cJSON_Array) { - int size = 0; - for (child = json->child; child; child = child->next) { - construction *cons = 0; - json_construction(child, &cons); - if (cons) { - cons->maxsize -= size; - size += cons->maxsize + size; - *consp = cons; - consp = &cons->improvement; - } - } - return; - } if (json->type != cJSON_Object) { log_error("construction %s is not a json object: %d", json->string, json->type); return; @@ -309,6 +295,57 @@ static void json_terrain(cJSON *json, terrain_type *ter) { } } +static void json_stage(cJSON *json, building_stage *stage) { + cJSON *child; + + if (json->type != cJSON_Object) { + log_error("building stages is not a json object: %d", json->type); + return; + } + for (child = json->child; child; child = child->next) { + switch (child->type) { + case cJSON_Object: + if (strcmp(child->string, "construction") == 0) { + json_construction(child, &stage->construction); + } + break; + case cJSON_String: + if (strcmp(child->string, "name") == 0) { + stage->name = str_strdup(child->valuestring); + } + break; + } + } +} + +static void json_stages(cJSON *json, building_type *bt) { + cJSON *child; + building_stage *stage, **sp = &bt->stages; + int size = 0; + + if (json->type != cJSON_Array) { + log_error("building stages is not a json array: %d", json->type); + return; + } + + for (child = json->child; child; child = child->next) { + switch (child->type) { + case cJSON_Object: + stage = calloc(sizeof(building_stage), 1); + json_stage(child, stage); + if (stage->construction->maxsize > 0) { + stage->construction->maxsize -= size; + size += stage->construction->maxsize; + } + *sp = stage; + sp = &stage->next; + break; + default: + log_error("building stage contains non-object type %d", child->type); + } + } +} + static void json_building(cJSON *json, building_type *bt) { cJSON *child; const char *flags[] = { @@ -321,8 +358,10 @@ static void json_building(cJSON *json, building_type *bt) { for (child = json->child; child; child = child->next) { switch (child->type) { case cJSON_Array: - if (strcmp(child->string, "construction") == 0) { - json_construction(child, &bt->construction); + if (strcmp(child->string, "stages") == 0) { + if (!bt->stages) { + json_stages(child, bt); + } } else if (strcmp(child->string, "maintenance") == 0) { json_maintenance(child, &bt->maintenance); @@ -333,9 +372,14 @@ static void json_building(cJSON *json, building_type *bt) { break; case cJSON_Object: if (strcmp(child->string, "construction") == 0) { - json_construction(child, &bt->construction); + /* simple, single-stage building */ + if (!bt->stages) { + building_stage *stage = calloc(sizeof(building_stage), 1); + json_construction(child, &stage->construction); + bt->stages = stage; + } } - else if (strcmp(child->string, "maintenance") == 0) { + if (strcmp(child->string, "maintenance") == 0) { json_maintenance(child, &bt->maintenance); } break; diff --git a/src/jsonconf.test.c b/src/jsonconf.test.c index 88630c833..482337e40 100644 --- a/src/jsonconf.test.c +++ b/src/jsonconf.test.c @@ -110,7 +110,7 @@ static void test_prefixes(CuTest * tc) CuAssertPtrNotNull(tc, race_prefixes); CuAssertStrEquals(tc, "snow", race_prefixes[0]); CuAssertStrEquals(tc, "dark", race_prefixes[2]); - CuAssertPtrEquals(tc, 0, race_prefixes[3]); + CuAssertPtrEquals(tc, NULL, race_prefixes[3]); cJSON_Delete(json); test_teardown(); } @@ -189,7 +189,7 @@ static void test_races(CuTest * tc) test_setup(); CuAssertPtrNotNull(tc, json); - CuAssertPtrEquals(tc, 0, races); + CuAssertPtrEquals(tc, NULL, races); json_config(json); CuAssertPtrNotNull(tc, races); @@ -222,7 +222,7 @@ static void test_findrace(CuTest *tc) { CuAssertPtrNotNull(tc, json); test_setup(); lang = get_or_create_locale("de"); - CuAssertPtrEquals(tc, 0, (void *)findrace("Zwerg", lang)); + CuAssertPtrEquals(tc, NULL, (void *)findrace("Zwerg", lang)); json_config(json); init_locale(lang); @@ -245,9 +245,9 @@ static void test_items(CuTest * tc) test_setup(); CuAssertPtrNotNull(tc, json); - CuAssertPtrEquals(tc, 0, it_find("axe")); - CuAssertPtrEquals(tc, 0, rt_find("axe")); - CuAssertPtrEquals(tc, 0, (void *)get_resourcetype(R_HORSE)); + CuAssertPtrEquals(tc, NULL, it_find("axe")); + CuAssertPtrEquals(tc, NULL, rt_find("axe")); + CuAssertPtrEquals(tc, NULL, (void *)get_resourcetype(R_HORSE)); json_config(json); @@ -283,7 +283,7 @@ static void test_ships(CuTest * tc) test_setup(); CuAssertPtrNotNull(tc, json); - CuAssertPtrEquals(tc, 0, shiptypes); + CuAssertPtrEquals(tc, NULL, shiptypes); json_config(json); CuAssertPtrNotNull(tc, shiptypes); @@ -301,7 +301,7 @@ static void test_ships(CuTest * tc) CuAssertPtrNotNull(tc, st->coasts); CuAssertPtrEquals(tc, (void *)ter, (void *)st->coasts[0]); - CuAssertPtrEquals(tc, 0, (void *)st->coasts[1]); + CuAssertPtrEquals(tc, NULL, (void *)st->coasts[1]); cJSON_Delete(json); test_teardown(); @@ -309,28 +309,42 @@ static void test_ships(CuTest * tc) static void test_castles(CuTest *tc) { const char * data = "{\"buildings\": { \"castle\" : { " - "\"construction\" : [" - "{ \"maxsize\" : 2 }," - "{ \"maxsize\" : 8 }" + "\"stages\" : [" + "{ \"construction\": { \"maxsize\" : 2 }, \"name\": \"site\" }," + "{ \"construction\": { \"maxsize\" : 8 } }," + "{ \"construction\": { \"maxsize\" : -1 } }" "]}}}"; cJSON *json = cJSON_Parse(data); const building_type *bt; + const building_stage *stage; + const construction *con; test_setup(); CuAssertPtrNotNull(tc, json); - CuAssertPtrEquals(tc, 0, buildingtypes); + CuAssertPtrEquals(tc, NULL, buildingtypes); json_config(json); CuAssertPtrNotNull(tc, buildingtypes); bt = bt_find("castle"); CuAssertPtrNotNull(tc, bt); - CuAssertPtrNotNull(tc, bt->construction); - CuAssertIntEquals(tc, 2, bt->construction->maxsize); - CuAssertPtrNotNull(tc, bt->construction->improvement); - CuAssertIntEquals(tc, 6, bt->construction->improvement->maxsize); - CuAssertPtrEquals(tc, 0, bt->construction->improvement->improvement); + CuAssertPtrNotNull(tc, stage = bt->stages); + CuAssertStrEquals(tc, "site", stage->name); + CuAssertPtrNotNull(tc, con = stage->construction); + CuAssertIntEquals(tc, 2, con->maxsize); + + CuAssertPtrNotNull(tc, stage = stage->next); + CuAssertPtrEquals(tc, NULL, stage->name); + CuAssertPtrNotNull(tc, con = stage->construction); + CuAssertIntEquals(tc, 6, con->maxsize); + + CuAssertPtrNotNull(tc, stage = stage->next); + CuAssertPtrNotNull(tc, con = stage->construction); + CuAssertIntEquals(tc, -1, con->maxsize); + + CuAssertPtrEquals(tc, NULL, stage->next); + cJSON_Delete(json); test_teardown(); } @@ -344,7 +358,7 @@ static void test_spells(CuTest * tc) test_setup(); CuAssertPtrNotNull(tc, json); - CuAssertPtrEquals(tc, 0, find_spell("fireball")); + CuAssertPtrEquals(tc, NULL, find_spell("fireball")); json_config(json); sp = find_spell("fireball"); @@ -353,7 +367,7 @@ static void test_spells(CuTest * tc) cJSON_Delete(json); test_teardown(); - CuAssertPtrEquals(tc, 0, find_spell("fireball")); + CuAssertPtrEquals(tc, NULL, find_spell("fireball")); } static const char * building_data = "{\"buildings\": { " @@ -380,11 +394,12 @@ static void test_buildings(CuTest * tc) { cJSON *json = cJSON_Parse(building_data); const building_type *bt; + const construction *con; test_setup(); CuAssertPtrNotNull(tc, json); - CuAssertPtrEquals(tc, 0, buildingtypes); + CuAssertPtrEquals(tc, NULL, buildingtypes); json_config(json); CuAssertPtrNotNull(tc, buildingtypes); @@ -406,17 +421,19 @@ static void test_buildings(CuTest * tc) CuAssertIntEquals(tc, MTF_VARIABLE, bt->maintenance[0].flags); CuAssertIntEquals(tc, 0, bt->maintenance[1].number); - CuAssertPtrNotNull(tc, bt->construction); - CuAssertPtrNotNull(tc, bt->construction->materials); - CuAssertIntEquals(tc, 2, bt->construction->materials[0].number); - CuAssertPtrEquals(tc, (void *)get_resourcetype(R_STONE), (void *)bt->construction->materials[0].rtype); - CuAssertIntEquals(tc, 1, bt->construction->materials[1].number); - CuAssertPtrEquals(tc, (void *)get_resourcetype(R_IRON), (void *)bt->construction->materials[1].rtype); - CuAssertIntEquals(tc, 0, bt->construction->materials[2].number); - CuAssertIntEquals(tc, 10, bt->construction->reqsize); - CuAssertIntEquals(tc, 20, bt->construction->maxsize); - CuAssertIntEquals(tc, 1, bt->construction->minskill); - CuAssertPtrEquals(tc, 0, bt->construction->improvement); + CuAssertPtrNotNull(tc, bt->stages); + CuAssertPtrEquals(tc, NULL, bt->stages->next); + CuAssertPtrNotNull(tc, bt->stages->construction); + CuAssertPtrNotNull(tc, con = bt->stages->construction); + CuAssertPtrNotNull(tc, con->materials); + CuAssertIntEquals(tc, 2, con->materials[0].number); + CuAssertPtrEquals(tc, (void *)get_resourcetype(R_STONE), (void *)con->materials[0].rtype); + CuAssertIntEquals(tc, 1, con->materials[1].number); + CuAssertPtrEquals(tc, (void *)get_resourcetype(R_IRON), (void *)con->materials[1].rtype); + CuAssertIntEquals(tc, 0, con->materials[2].number); + CuAssertIntEquals(tc, 10, con->reqsize); + CuAssertIntEquals(tc, 20, con->maxsize); + CuAssertIntEquals(tc, 1, con->minskill); cJSON_Delete(json); test_teardown(); } @@ -483,7 +500,7 @@ static void test_configs(CuTest * tc) fwrite(building_data, 1, strlen(building_data), F); fclose(F); CuAssertPtrNotNull(tc, json); - CuAssertPtrEquals(tc, 0, buildingtypes); + CuAssertPtrEquals(tc, NULL, buildingtypes); json_config(json); CuAssertPtrNotNull(tc, buildingtypes); if (remove("test.json")!=0 && errno==ENOENT) { @@ -508,7 +525,7 @@ static void test_terrains(CuTest * tc) test_setup(); CuAssertPtrNotNull(tc, json); - CuAssertPtrEquals(tc, 0, (void *)get_terrain("plain")); + CuAssertPtrEquals(tc, NULL, (void *)get_terrain("plain")); json_config(json); ter = get_terrain("plain"); @@ -520,7 +537,7 @@ static void test_terrains(CuTest * tc) CuAssertPtrNotNull(tc, ter->herbs); CuAssertPtrEquals(tc, rt_get_or_create("h0"), ter->herbs[0]->rtype); CuAssertPtrEquals(tc, rt_get_or_create("h1"), ter->herbs[1]->rtype); - CuAssertPtrEquals(tc, 0, (void *)ter->herbs[2]); + CuAssertPtrEquals(tc, NULL, (void *)ter->herbs[2]); CuAssertPtrNotNull(tc, ter->name); /* anything named "plain" uses plain_name() */ CuAssertPtrNotNull(tc, ter->production); CuAssertPtrEquals(tc, rt_get_or_create("stone"), (resource_type *)ter->production[0].type); @@ -529,7 +546,7 @@ static void test_terrains(CuTest * tc) CuAssertStrEquals(tc, "1d5", ter->production[0].divisor); CuAssertStrEquals(tc, "1d6", ter->production[0].startlevel); CuAssertPtrEquals(tc, rt_get_or_create("iron"), (resource_type *)ter->production[1].type); - CuAssertPtrEquals(tc, 0, (void *)ter->production[2].type); + CuAssertPtrEquals(tc, NULL, (void *)ter->production[2].type); cJSON_Delete(json); test_teardown(); diff --git a/src/kernel/build.c b/src/kernel/build.c index bb6420717..45a7d0898 100644 --- a/src/kernel/build.c +++ b/src/kernel/build.c @@ -145,13 +145,25 @@ static void destroy_road(unit * u, int nmax, struct order *ord) } } +static int recycle(unit *u, construction *con, int size) { + /* TODO: Nicht an ZERST�RE mit Punktangabe angepasst! */ + int c; + for (c = 0; con->materials[c].number; ++c) { + const requirement *rq = con->materials + c; + int num = (rq->number * size / con->reqsize) / 2; + if (num) { + change_resource(u, rq->rtype, num); + } + } + return size; +} + int destroy_cmd(unit * u, struct order *ord) { char token[128]; ship *sh; unit *u2; region *r = u->region; - const construction *con = NULL; int size = 0; const char *s; int n = INT_MAX; @@ -194,6 +206,7 @@ int destroy_cmd(unit * u, struct order *ord) return 138; } if (n >= b->size) { + building_stage *stage; /* destroy completly */ /* all units leave the building */ for (u2 = r->units; u2; u2 = u2->next) { @@ -202,11 +215,13 @@ int destroy_cmd(unit * u, struct order *ord) } } ADDMSG(&u->faction->msgs, msg_message("destroy", "building unit", b, u)); - con = b->type->construction; + for (stage = b->type->stages; stage; stage = stage->next) { + size = recycle(u, stage->construction, size); + } remove_building(&r->buildings, b); } else { - /* partial destroy */ + /* TODO: partial destroy does not recycle */ b->size -= n; ADDMSG(&u->faction->msgs, msg_message("destroy_partial", "building unit", b, u)); @@ -234,7 +249,7 @@ int destroy_cmd(unit * u, struct order *ord) } ADDMSG(&u->faction->msgs, msg_message("shipdestroy", "unit region ship", u, r, sh)); - con = sh->type->construction; + size = recycle(u, sh->type->construction, size); remove_ship(&sh->region->ships, sh); } else { @@ -248,18 +263,6 @@ int destroy_cmd(unit * u, struct order *ord) cmistake(u, ord, 138, MSG_PRODUCE); return 138; } - - if (con) { - /* TODO: Nicht an ZERST�RE mit Punktangabe angepasst! */ - int c; - for (c = 0; con->materials[c].number; ++c) { - const requirement *rq = con->materials + c; - int recycle = (rq->number * size / con->reqsize) / 2; - if (recycle) { - change_resource(u, rq->rtype, recycle); - } - } - } return 0; } @@ -668,7 +671,7 @@ static int build_failure(unit *u, order *ord, const building_type *btype, int wa break; case ENOMATERIALS: ADDMSG(&u->faction->msgs, msg_materials_required(u, ord, - btype->construction, want)); + btype->stages->construction, want)); break; case ELOWSKILL: case ENEEDSKILL: @@ -679,9 +682,13 @@ static int build_failure(unit *u, order *ord, const building_type *btype, int wa return err; } -int build_stages(unit *u, construction * con, int built, int n) { +static int build_stages(unit *u, const building_type *btype, int built, int n) { + + const building_stage *stage; int made = 0; - while (con) { + + for (stage = btype->stages; stage; stage = stage->next) { + const construction * con = stage->construction; if (con->maxsize < 0 || con->maxsize > built) { int err, want = INT_MAX; if (n < INT_MAX) { @@ -719,7 +726,6 @@ int build_stages(unit *u, construction * con, int built, int n) { if (built >= con->maxsize && con->maxsize > 0) { built -= con->maxsize; } - con = con->improvement; } return made; } @@ -736,7 +742,7 @@ build_building(unit * u, const building_type * btype, int id, int want, order * const struct locale *lang = u->faction->locale; assert(u->number); - assert(btype->construction); + assert(btype->stages && btype->stages->construction); if (effskill(u, SK_BUILDING, 0) == 0) { cmistake(u, ord, 101, MSG_PRODUCE); return 0; @@ -827,7 +833,7 @@ build_building(unit * u, const building_type * btype, int id, int want, order * } } - built = build_stages(u, btype->construction, built, n); + built = build_stages(u, btype, built, n); if (built < 0) { return build_failure(u, ord, btype, want, built); @@ -1002,7 +1008,6 @@ void continue_ship(unit * u, int want) return; } cons = sh->type->construction; - assert(cons->improvement == NULL); /* sonst ist construction::size nicht ship_type::maxsize */ if (sh->size == cons->maxsize && !sh->damage) { cmistake(u, u->thisorder, 16, MSG_PRODUCE); return; @@ -1026,11 +1031,6 @@ void continue_ship(unit * u, int want) void free_construction(struct construction *cons) { - while (cons) { - construction *next = cons->improvement; - free(cons->name); - free(cons->materials); - free(cons); - cons = next; - } + free(cons->materials); + free(cons); } diff --git a/src/kernel/build.h b/src/kernel/build.h index 5f93884cd..c52cc5da1 100644 --- a/src/kernel/build.h +++ b/src/kernel/build.h @@ -44,11 +44,6 @@ extern "C" { int maxsize; /* maximum size of this type */ int reqsize; /* size of object using up 1 set of requirement. */ requirement *materials; /* material req'd to build one object */ - - /* only used by CONS_BUILDING: */ - char * name; /* building level name */ - struct construction *improvement; - /* next level, if upgradable. */ } construction; void free_construction(struct construction *cons); diff --git a/src/kernel/build.test.c b/src/kernel/build.test.c index d7a12325d..1261e5496 100644 --- a/src/kernel/build.test.c +++ b/src/kernel/build.test.c @@ -26,6 +26,7 @@ typedef struct build_fixture { region *r; race *rc; construction cons; + building_type *btype; } build_fixture; static unit * setup_build(build_fixture *bf) { @@ -34,7 +35,7 @@ static unit * setup_build(build_fixture *bf) { init_resources(); test_create_itemtype("stone"); - test_create_buildingtype("castle"); + bf->btype = test_create_buildingtype("castle"); bf->rc = test_create_race("human"); bf->r = test_create_region(0, 0, NULL); bf->f = test_create_faction(bf->rc); @@ -192,8 +193,7 @@ static void test_build_building_no_materials(CuTest *tc) { unit *u; u = setup_build(&bf); - btype = bt_find("castle"); - assert(btype); + btype = bf.btype; set_level(u, SK_BUILDING, 1); u->orders = create_order(K_MAKE, u->faction->locale, 0); CuAssertIntEquals(tc, ENOMATERIALS, build_building(u, btype, 0, 4, u->orders)); @@ -209,9 +209,7 @@ static void test_build_building_with_golem(CuTest *tc) { u = setup_build(&bf); bf.rc->ec_flags |= ECF_STONEGOLEM; - btype = bt_find("castle"); - assert(btype); - assert(btype->construction); + btype = bf.btype; set_level(bf.u, SK_BUILDING, 1); u->orders = create_order(K_MAKE, u->faction->locale, 0); @@ -231,9 +229,8 @@ static void test_build_building_success(CuTest *tc) { u = setup_build(&bf); rtype = get_resourcetype(R_STONE); - btype = bt_find("castle"); + btype = bf.btype; assert(btype && rtype && rtype->itype); - assert(btype->construction); assert(!u->region->buildings); i_change(&bf.u->items, rtype->itype, 1); diff --git a/src/kernel/building.c b/src/kernel/building.c index 7ee77ae28..c71cbc552 100644 --- a/src/kernel/building.c +++ b/src/kernel/building.c @@ -116,7 +116,13 @@ static void bt_register(building_type * btype) static void free_buildingtype(void *ptr) { building_type *btype = (building_type *)ptr; - free_construction(btype->construction); + while (btype->stages) { + building_stage *next = btype->stages->next; + free_construction(btype->stages->construction); + free(btype->stages->name); + free(btype->stages); + btype->stages = next; + } free(btype->maintenance); free(btype->_name); free(btype); @@ -189,8 +195,6 @@ static int adjust_size(const building *b, int bsize) { */ const char *buildingtype(const building_type * btype, const building * b, int bsize) { - const construction *con; - assert(btype); if (b && b->attribs) { @@ -201,14 +205,15 @@ const char *buildingtype(const building_type * btype, const building * b, int bs } } } - if (btype->construction && btype->construction->name) { + if (btype->stages && btype->stages->name) { + const building_stage *stage; if (b) { bsize = adjust_size(b, bsize); } - for (con = btype->construction; con; con = con->improvement) { - bsize -= con->maxsize; - if (!con->improvement || bsize <0) { - return con->name; + for (stage = btype->stages; stage; stage = stage->next) { + bsize -= stage->construction->maxsize; + if (!stage->next || bsize <0) { + return stage->name; } } } @@ -476,24 +481,30 @@ int buildingeffsize(const building * b, int img) int bt_effsize(const building_type * btype, const building * b, int bsize) { - int n = 0; - const construction *cons = btype->construction; - if (b) { bsize = adjust_size(b, bsize); } - if (!cons) { - return 0; + if (btype->stages) { + int n = 0; + const building_stage *stage = btype->stages; + do { + const construction *con = stage->construction; + if (con->maxsize < 0) { + break; + } + else { + if (bsize >= con->maxsize) { + bsize -= con->maxsize; + ++n; + } + stage = stage->next; + } + } while (stage && bsize > 0); + return n; } - while (cons && cons->maxsize != -1 && bsize >= cons->maxsize) { - bsize -= cons->maxsize; - cons = cons->improvement; - ++n; - } - - return n; + return 0; } const char *write_buildingname(const building * b, char *ibuf, size_t size) diff --git a/src/kernel/building.h b/src/kernel/building.h index 5a83fd0ca..915a756b7 100644 --- a/src/kernel/building.h +++ b/src/kernel/building.h @@ -54,6 +54,15 @@ extern "C" { #define BTF_NAMECHANGE 0x100 /* name and description can be changed more than once */ #define BTF_FORTIFICATION 0x200 /* building_protection, safe from monsters */ + typedef struct building_stage { + /* construction of this building stage: */ + struct construction *construction; + /* building stage name: */ + char * name; + /* next stage, if upgradable: */ + struct building_stage * next; + } building_stage; + typedef struct building_type { char *_name; @@ -67,8 +76,8 @@ extern "C" { int taxes; /* receive $1 tax per `taxes` in region */ double auraregen; /* modifier for aura regeneration inside building */ struct maintenance *maintenance; /* array of requirements */ - struct construction *construction; /* construction of 1 building-level */ struct resource_mod *modifiers; /* modify production skills */ + struct building_stage *stages; } building_type; extern struct selist *buildingtypes; diff --git a/src/kernel/building.test.c b/src/kernel/building.test.c index 1c736c75b..94dfd1b49 100644 --- a/src/kernel/building.test.c +++ b/src/kernel/building.test.c @@ -224,7 +224,7 @@ static void test_buildingowner_goes_to_same_faction_after_leave(CuTest * tc) leave_building(u3); CuAssertPtrEquals(tc, u2, building_owner(bld)); leave_building(u2); - CuAssertPtrEquals(tc, 0, building_owner(bld)); + CuAssertPtrEquals(tc, NULL, building_owner(bld)); test_teardown(); } @@ -281,7 +281,7 @@ void test_buildingowner_goes_to_empty_unit_after_leave(CuTest * tc) leave_building(u1); CuAssertPtrEquals(tc, u3, building_owner(bld)); leave_building(u3); - CuAssertPtrEquals(tc, 0, building_owner(bld)); + CuAssertPtrEquals(tc, NULL, building_owner(bld)); u2->number = 1; CuAssertPtrEquals(tc, u2, building_owner(bld)); test_teardown(); @@ -295,8 +295,8 @@ static void test_btype_defaults(CuTest *tc) { btype = bt_get_or_create("hodor"); CuAssertPtrNotNull(tc, btype); CuAssertStrEquals(tc, "hodor", btype->_name); - CuAssertPtrEquals(tc, 0, btype->maintenance); - CuAssertPtrEquals(tc, 0, btype->construction); + CuAssertPtrEquals(tc, NULL, btype->maintenance); + CuAssertPtrEquals(tc, NULL, btype->stages); CuAssertDblEquals(tc, 1.0, btype->auraregen, 0.0); CuAssertIntEquals(tc, 0, btype->taxes); CuAssertIntEquals(tc, -1, btype->maxsize); @@ -489,11 +489,11 @@ static void test_cmp_current_owner(CuTest *tc) { config_set("rules.region_owners", "1"); r = test_create_region(0, 0, NULL); btype = test_create_buildingtype("watch"); - btype->construction->maxsize = 1; + btype->stages->construction->maxsize = 1; btype->taxes = 200; b1 = test_create_building(r, btype); btype = test_create_buildingtype("castle"); - btype->construction->maxsize = 1; + btype->stages->construction->maxsize = 1; btype->taxes = 100; b2 = test_create_building(r, btype); b1->size = 1; @@ -515,16 +515,26 @@ static void test_cmp_current_owner(CuTest *tc) { static void test_building_effsize(CuTest *tc) { building *b; building_type *btype; + building_stage *stage; construction *cons; test_setup(); - btype = bt_get_or_create("castle"); - cons = btype->construction = calloc(1, sizeof(construction)); + btype = test_create_buildingtype("castle"); + stage = btype->stages; + assert(stage && stage->construction); + cons = stage->construction; cons->maxsize = 5; - cons = cons->improvement = calloc(1, sizeof(construction)); + + stage->next = calloc(1, sizeof(building_stage)); + stage = stage->next; + cons = stage->construction = calloc(1, sizeof(construction)); cons->maxsize = 5; - cons = cons->improvement = calloc(1, sizeof(construction)); + + stage->next = calloc(1, sizeof(building_stage)); + stage = stage->next; + cons = stage->construction = calloc(1, sizeof(construction)); cons->maxsize = -1; + b = test_create_building(test_create_region(0,0,0), btype); b->size = 1; CuAssertIntEquals(tc, 0, buildingeffsize(b, false)); @@ -563,14 +573,20 @@ static void test_largestbuilding(CuTest *tc) { static void test_buildingtype(CuTest *tc) { building_type *btype; test_setup(); + btype = test_create_buildingtype("hodor"); - CuAssertPtrNotNull(tc, btype->construction); + CuAssertPtrNotNull(tc, btype->stages); + CuAssertPtrEquals(tc, NULL, btype->stages->name); + CuAssertPtrNotNull(tc, btype->stages->construction); CuAssertStrEquals(tc, "hodor", buildingtype(btype, NULL, 1)); - btype->construction->name = str_strdup("castle"); + + btype->stages->name = str_strdup("castle"); CuAssertStrEquals(tc, "castle", buildingtype(btype, NULL, 1)); + btype = bt_get_or_create("portal"); - CuAssertPtrEquals(tc, NULL, btype->construction); + CuAssertPtrEquals(tc, NULL, btype->stages); CuAssertStrEquals(tc, "portal", buildingtype(btype, NULL, 1)); + test_teardown(); } diff --git a/src/kernel/item.c b/src/kernel/item.c index ad811fefd..7307c82c2 100644 --- a/src/kernel/item.c +++ b/src/kernel/item.c @@ -900,7 +900,9 @@ void write_items(struct storage *store, item * ilist) static void free_itype(item_type *itype) { assert(itype); - free_construction(itype->construction); + if (itype->construction) { + free_construction(itype->construction); + } free(itype->_appearance[0]); free(itype->_appearance[1]); free(itype); diff --git a/src/kernel/ship.c b/src/kernel/ship.c index 3f4c099ac..08eefbd12 100644 --- a/src/kernel/ship.c +++ b/src/kernel/ship.c @@ -249,7 +249,9 @@ static void free_shiptype(void *ptr) { ship_type *stype = (ship_type *)ptr; free(stype->_name); free(stype->coasts); - free_construction(stype->construction); + if (stype->construction) { + free_construction(stype->construction); + } free(stype); } diff --git a/src/magic.test.c b/src/magic.test.c index f4606b75a..e68f3ca21 100644 --- a/src/magic.test.c +++ b/src/magic.test.c @@ -477,7 +477,7 @@ static void test_illusioncastle(CuTest *tc) CuAssertPtrEquals(tc, btype, (void *)icastle_type(a)); CuAssertPtrEquals(tc, bt_icastle, (void *)b->type); CuAssertStrEquals(tc, "castle", buildingtype(btype, b, b->size)); - btype->construction->name = str_strdup("site"); + btype->stages->name = str_strdup("site"); CuAssertStrEquals(tc, "site", buildingtype(btype, b, b->size)); test_teardown(); } diff --git a/src/move.c b/src/move.c index 3f940c78a..b30717596 100644 --- a/src/move.c +++ b/src/move.c @@ -479,10 +479,6 @@ static bool cansail(const region * r, ship * sh) { UNUSED_ARG(r); - /* sonst ist construction:: size nicht ship_type::maxsize */ - assert(!sh->type->construction - || sh->type->construction->improvement == NULL); - if (sh->type->construction && sh->size != sh->type->construction->maxsize) { return false; } @@ -505,10 +501,6 @@ static double overload(const region * r, ship * sh) { UNUSED_ARG(r); - /* sonst ist construction:: size nicht ship_type::maxsize */ - assert(!sh->type->construction - || sh->type->construction->improvement == NULL); - if (sh->type->construction && sh->size != sh->type->construction->maxsize) { return DBL_MAX; } diff --git a/src/tests.c b/src/tests.c index 2affead5f..e3799c1b5 100644 --- a/src/tests.c +++ b/src/tests.c @@ -336,20 +336,27 @@ ship_type * test_create_shiptype(const char * name) building_type * test_create_buildingtype(const char * name) { + construction *con; building_type *btype = bt_get_or_create(name); btype->flags = BTF_NAMECHANGE; - if (!btype->construction) { - btype->construction = (construction *)calloc(sizeof(construction), 1); - btype->construction->skill = SK_BUILDING; - btype->construction->maxsize = -1; - btype->construction->minskill = 1; - btype->construction->reqsize = 1; + if (btype->stages) { + con = btype->stages->construction; + } else { + btype->stages = calloc(1, sizeof(building_stage)); + con = (construction *)calloc(1, sizeof(construction)); + if (con) { + con->skill = SK_BUILDING; + con->maxsize = -1; + con->minskill = 1; + con->reqsize = 1; + btype->stages->construction = con; + } } - if (!btype->construction->materials) { - btype->construction->materials = (requirement *)calloc(sizeof(requirement), 2); - btype->construction->materials[1].number = 0; - btype->construction->materials[0].number = 1; - btype->construction->materials[0].rtype = get_resourcetype(R_STONE); + if (con && !con->materials) { + con->materials = (requirement *)calloc(2, sizeof(requirement)); + con->materials[1].number = 0; + con->materials[0].number = 1; + con->materials[0].rtype = get_resourcetype(R_STONE); } if (default_locale) { locale_setstring(default_locale, name, name); diff --git a/src/xmlreader.c b/src/xmlreader.c index 59e092d71..dfb3b54ea 100644 --- a/src/xmlreader.c +++ b/src/xmlreader.c @@ -253,23 +253,24 @@ xml_readconstruction(xmlXPathContextPtr xpath, xmlNodePtr node) static void xml_readconstructions(xmlXPathContextPtr xpath, xmlNodeSetPtr nodeSet, building_type *btype) { - construction **consPtr = &btype->construction; + building_stage **stage_ptr = &btype->stages; int k; for (k = 0; k != nodeSet->nodeNr; ++k) { + building_stage *stage = calloc(1, sizeof(building_stage)); xmlNodePtr node = nodeSet->nodeTab[k]; construction *con = xml_readconstruction(xpath, node); xmlChar *propValue = xmlGetProp(node, BAD_CAST "name"); if (propValue != NULL) { - con->name = str_strdup((const char *)propValue); + stage->name = str_strdup((const char *)propValue); xmlFree(propValue); } + stage->next = NULL; + stage->construction = con; - if (con) { - *consPtr = con; - consPtr = &con->improvement; - } + *stage_ptr = stage; + stage_ptr = &stage->next; } }