From 484e52d491368d47492cf09d1dbeab7e84159816 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 17 Dec 2014 21:31:02 +0100 Subject: [PATCH] added a test: region owners can set PAY NOT for empty buildings if they are in region_owner_pay_building. --- src/kernel/building.c | 558 +++++++++++++++++++++--------------------- src/laws.test.c | 37 ++- 2 files changed, 314 insertions(+), 281 deletions(-) diff --git a/src/kernel/building.c b/src/kernel/building.c index 0fcce549e..ca45d85a9 100644 --- a/src/kernel/building.c +++ b/src/kernel/building.c @@ -1,7 +1,7 @@ /* Copyright (c) 1998-2010, Enno Rehling - Katja Zedel +Katja Zedel Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -57,8 +57,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include typedef struct building_typelist { - struct building_typelist *next; - building_type *type; + struct building_typelist *next; + building_type *type; } building_typelist; quicklist *buildingtypes = NULL; @@ -66,17 +66,17 @@ quicklist *buildingtypes = NULL; /* Returns a building type for the (internal) name */ static building_type *bt_find_i(const char *name) { - quicklist *ql; - int qi; + quicklist *ql; + int qi; - assert(name); + assert(name); - for (qi = 0, ql = buildingtypes; ql; ql_advance(&ql, &qi, 1)) { - building_type *btype = (building_type *) ql_get(ql, qi); - if (strcmp(btype->_name, name) == 0) - return btype; - } - return NULL; + for (qi = 0, ql = buildingtypes; ql; ql_advance(&ql, &qi, 1)) { + building_type *btype = (building_type *)ql_get(ql, qi); + if (strcmp(btype->_name, name) == 0) + return btype; + } + return NULL; } const building_type *bt_find(const char *name) @@ -86,10 +86,10 @@ const building_type *bt_find(const char *name) void bt_register(building_type * type) { - if (type->init) { - type->init(type); - } - ql_push(&buildingtypes, (void *)type); + if (type->init) { + type->init(type); + } + ql_push(&buildingtypes, (void *)type); } void free_buildingtypes(void) { @@ -100,36 +100,36 @@ void free_buildingtypes(void) { building_type *bt_get_or_create(const char *name) { - if (name != NULL) { - building_type *btype = bt_find_i(name); - if (btype == NULL) { - btype = calloc(sizeof(building_type), 1); - btype->_name = _strdup(name); - bt_register(btype); + if (name != NULL) { + building_type *btype = bt_find_i(name); + if (btype == NULL) { + btype = calloc(sizeof(building_type), 1); + btype->_name = _strdup(name); + bt_register(btype); + } + return btype; } - return btype; - } - return NULL; + return NULL; } int buildingcapacity(const building * b) { - if (b->type->capacity >= 0) { - if (b->type->maxcapacity >= 0) { - return _min(b->type->maxcapacity, b->size * b->type->capacity); + if (b->type->capacity >= 0) { + if (b->type->maxcapacity >= 0) { + return _min(b->type->maxcapacity, b->size * b->type->capacity); + } + return b->size * b->type->capacity; } - return b->size * b->type->capacity; - } - if (b->size >= b->type->maxsize) { - if (b->type->maxcapacity >= 0) { - return b->type->maxcapacity; + if (b->size >= b->type->maxsize) { + if (b->type->maxcapacity >= 0) { + return b->type->maxcapacity; + } } - } - return 0; + return 0; } attrib_type at_building_generic_type = { - "building_generic_type", NULL, NULL, NULL, a_writestring, a_readstring, + "building_generic_type", NULL, NULL, NULL, a_writestring, a_readstring, ATF_UNIQUE }; @@ -162,113 +162,113 @@ const char *buildingtype(const building_type * btype, const building * b, int bs static building *buildhash[BMAXHASH]; void bhash(building * b) { - building *old = buildhash[b->no % BMAXHASH]; + building *old = buildhash[b->no % BMAXHASH]; - buildhash[b->no % BMAXHASH] = b; - b->nexthash = old; + buildhash[b->no % BMAXHASH] = b; + b->nexthash = old; } void bunhash(building * b) { - building **show; + building **show; - for (show = &buildhash[b->no % BMAXHASH]; *show; show = &(*show)->nexthash) { - if ((*show)->no == b->no) - break; - } - if (*show) { - assert(*show == b); - *show = (*show)->nexthash; - b->nexthash = 0; - } + for (show = &buildhash[b->no % BMAXHASH]; *show; show = &(*show)->nexthash) { + if ((*show)->no == b->no) + break; + } + if (*show) { + assert(*show == b); + *show = (*show)->nexthash; + b->nexthash = 0; + } } static building *bfindhash(int i) { - building *old; + building *old; - for (old = buildhash[i % BMAXHASH]; old; old = old->nexthash) - if (old->no == i) - return old; - return 0; + for (old = buildhash[i % BMAXHASH]; old; old = old->nexthash) + if (old->no == i) + return old; + return 0; } building *findbuilding(int i) { - return bfindhash(i); + return bfindhash(i); } /* ** old building types ** */ static int sm_smithy(const unit * u, const region * r, skill_t sk, int value) { /* skillmod */ - if (sk == SK_WEAPONSMITH || sk == SK_ARMORER) { - if (u->region == r) - return value + 1; - } - return value; + if (sk == SK_WEAPONSMITH || sk == SK_ARMORER) { + if (u->region == r) + return value + 1; + } + return value; } static int mm_smithy(const unit * u, const resource_type * rtype, int value) { /* material-mod */ - if (rtype == get_resourcetype(R_IRON)) - return value * 2; - return value; + if (rtype == get_resourcetype(R_IRON)) + return value * 2; + return value; } static void init_smithy(struct building_type *bt) { - a_add(&bt->attribs, make_skillmod(NOSKILL, SMF_PRODUCTION, sm_smithy, 1.0, - 0)); - a_add(&bt->attribs, make_matmod(mm_smithy)); + a_add(&bt->attribs, make_skillmod(NOSKILL, SMF_PRODUCTION, sm_smithy, 1.0, + 0)); + a_add(&bt->attribs, make_matmod(mm_smithy)); } static const char *castle_name_i(const struct building_type *btype, - const struct building *b, int bsize, const char *fname[]) + const struct building *b, int bsize, const char *fname[]) { - int i = bt_effsize(btype, b, bsize); + int i = bt_effsize(btype, b, bsize); - return fname[i]; + return fname[i]; } static const char *castle_name_2(const struct building_type *btype, - const struct building *b, int bsize) + const struct building *b, int bsize) { - const char *fname[] = { - "site", - "fortification", - "tower", - "castle", - "fortress", - "citadel" - }; - return castle_name_i(btype, b, bsize, fname); + const char *fname[] = { + "site", + "fortification", + "tower", + "castle", + "fortress", + "citadel" + }; + return castle_name_i(btype, b, bsize, fname); } static const char *castle_name(const struct building_type *btype, - const struct building *b, int bsize) + const struct building *b, int bsize) { - const char *fname[] = { - "site", - "tradepost", - "fortification", - "tower", - "castle", - "fortress", - "citadel" - }; - return castle_name_i(btype, b, bsize, fname); + const char *fname[] = { + "site", + "tradepost", + "fortification", + "tower", + "castle", + "fortress", + "citadel" + }; + return castle_name_i(btype, b, bsize, fname); } static const char *fort_name(const struct building_type *btype, - const struct building *b, int bsize) + const struct building *b, int bsize) { - const char *fname[] = { - "scaffolding", - "guardhouse", - "guardtower", - }; - return castle_name_i(btype, b, bsize, fname); + const char *fname[] = { + "scaffolding", + "guardhouse", + "guardtower", + }; + return castle_name_i(btype, b, bsize, fname); } /* for finding out what was meant by a particular building string */ @@ -278,132 +278,132 @@ static local_names *bnames; /* Find the building type for a given localized name (as seen by the user). Useful for parsing * orders. The inverse of locale_string(lang, btype->_name), sort of. */ const building_type *findbuildingtype(const char *name, - const struct locale *lang) + const struct locale *lang) { - variant type; - local_names *bn = bnames; + variant type; + local_names *bn = bnames; - while (bn) { - if (bn->lang == lang) - break; - bn = bn->next; - } - if (!bn) { - quicklist *ql = buildingtypes; - int qi; - - bn = (local_names *)calloc(sizeof(local_names), 1); - bn->next = bnames; - bn->lang = lang; - - for (qi = 0, ql = buildingtypes; ql; ql_advance(&ql, &qi, 1)) { - building_type *btype = (building_type *) ql_get(ql, qi); - - const char *n = locale_string(lang, btype->_name); - type.v = (void *)btype; - addtoken(&bn->names, n, type); + while (bn) { + if (bn->lang == lang) + break; + bn = bn->next; } - bnames = bn; - } - if (findtoken(bn->names, name, &type) == E_TOK_NOMATCH) - return NULL; - return (const building_type *)type.v; + if (!bn) { + quicklist *ql = buildingtypes; + int qi; + + bn = (local_names *)calloc(sizeof(local_names), 1); + bn->next = bnames; + bn->lang = lang; + + for (qi = 0, ql = buildingtypes; ql; ql_advance(&ql, &qi, 1)) { + building_type *btype = (building_type *)ql_get(ql, qi); + + const char *n = locale_string(lang, btype->_name); + type.v = (void *)btype; + addtoken(&bn->names, n, type); + } + bnames = bn; + } + if (findtoken(bn->names, name, &type) == E_TOK_NOMATCH) + return NULL; + return (const building_type *)type.v; } static int eressea_building_protection(building * b, unit * u) { - int beff = buildingeffsize(b, false) - 1; - /* -1 because the tradepost has no protection value */ + int beff = buildingeffsize(b, false) - 1; + /* -1 because the tradepost has no protection value */ - return beff; + return beff; } static int meropis_building_protection(building * b, unit * u) { - return 2; + return 2; } void register_buildings(void) { - register_function((pf_generic) & eressea_building_protection, - "eressea_building_protection"); - register_function((pf_generic) & meropis_building_protection, - "meropis_building_protection"); - register_function((pf_generic) & init_smithy, "init_smithy"); - register_function((pf_generic) & castle_name, "castle_name"); - register_function((pf_generic) & castle_name_2, "castle_name_2"); - register_function((pf_generic) & fort_name, "fort_name"); + register_function((pf_generic)& eressea_building_protection, + "eressea_building_protection"); + register_function((pf_generic)& meropis_building_protection, + "meropis_building_protection"); + register_function((pf_generic)& init_smithy, "init_smithy"); + register_function((pf_generic)& castle_name, "castle_name"); + register_function((pf_generic)& castle_name_2, "castle_name_2"); + register_function((pf_generic)& fort_name, "fort_name"); } void write_building_reference(const struct building *b, struct storage *store) { - WRITE_INT(store, (b && b->region) ? b->no : 0); + WRITE_INT(store, (b && b->region) ? b->no : 0); } int resolve_building(variant id, void *address) { - int result = 0; - building *b = NULL; - if (id.i != 0) { - b = findbuilding(id.i); - if (b == NULL) { - result = -1; + int result = 0; + building *b = NULL; + if (id.i != 0) { + b = findbuilding(id.i); + if (b == NULL) { + result = -1; + } } - } - *(building **) address = b; - return result; + *(building **)address = b; + return result; } variant read_building_reference(struct storage * store) { - variant result; - READ_INT(store, &result.i); - return result; + variant result; + READ_INT(store, &result.i); + return result; } building *new_building(const struct building_type * btype, region * r, - const struct locale * lang) + const struct locale * lang) { - building **bptr = &r->buildings; - building *b = (building *) calloc(1, sizeof(building)); - static bool init_lighthouse = false; - static const struct building_type *bt_lighthouse = 0; - const char *bname = 0; - char buffer[32]; + building **bptr = &r->buildings; + building *b = (building *)calloc(1, sizeof(building)); + static bool init_lighthouse = false; + static const struct building_type *bt_lighthouse = 0; + const char *bname = 0; + char buffer[32]; - if (!init_lighthouse) { - bt_lighthouse = bt_find("lighthouse"); - init_lighthouse = true; - } + if (!init_lighthouse) { + bt_lighthouse = bt_find("lighthouse"); + init_lighthouse = true; + } - b->no = newcontainerid(); - bhash(b); + b->no = newcontainerid(); + bhash(b); - b->type = btype; - b->region = r; - while (*bptr) - bptr = &(*bptr)->next; - *bptr = b; + b->type = btype; + b->region = r; + while (*bptr) + bptr = &(*bptr)->next; + *bptr = b; - if (b->type == bt_lighthouse) { - r->flags |= RF_LIGHTHOUSE; - } - if (b->type->name) { - bname = LOC(lang, buildingtype(btype, b, 0)); - } - if (!bname) { - bname = LOC(lang, btype->_name); - } - if (!bname) { - bname = LOC(lang, parameters[P_GEBAEUDE]); - } - if (!bname) { - bname = parameters[P_GEBAEUDE]; - } - assert(bname); - slprintf(buffer, sizeof(buffer), "%s %s", bname, buildingid(b)); - b->name = _strdup(bname); - return b; + if (b->type == bt_lighthouse) { + r->flags |= RF_LIGHTHOUSE; + } + if (b->type->name) { + bname = LOC(lang, buildingtype(btype, b, 0)); + } + if (!bname) { + bname = LOC(lang, btype->_name); + } + if (!bname) { + bname = LOC(lang, parameters[P_GEBAEUDE]); + } + if (!bname) { + bname = parameters[P_GEBAEUDE]; + } + assert(bname); + slprintf(buffer, sizeof(buffer), "%s %s", bname, buildingid(b)); + b->name = _strdup(bname); + return b; } static building *deleted_buildings; @@ -431,8 +431,8 @@ void remove_building(building ** blist, building * b) update_lighthouse(b); bunhash(b); - /* Falls Karawanserei, Damm oder Tunnel einstürzen, wird die schon - * gebaute Straße zur Hälfte vernichtet */ + /* Falls Karawanserei, Damm oder Tunnel einstürzen, wird die schon + * gebaute Straße zur Hälfte vernichtet */ if (b->type == bt_caravan || b->type == bt_dam || b->type == bt_tunnel) { region *r = b->region; int d; @@ -443,7 +443,7 @@ void remove_building(building ** blist, building * b) } } } - + /* Stattdessen nur aus Liste entfernen, aber im Speicher halten. */ while (*blist && *blist != b) { blist = &(*blist)->next; @@ -456,19 +456,19 @@ void remove_building(building ** blist, building * b) void free_building(building * b) { - while (b->attribs) - a_remove(&b->attribs, b->attribs); - free(b->name); - free(b->display); - free(b); + while (b->attribs) + a_remove(&b->attribs, b->attribs); + free(b->name); + free(b->display); + free(b); } void free_buildings(void) { - while (deleted_buildings) { - building *b = deleted_buildings; - deleted_buildings = b->next; - } + while (deleted_buildings) { + building *b = deleted_buildings; + deleted_buildings = b->next; + } } extern struct attrib_type at_icastle; @@ -478,87 +478,87 @@ extern struct attrib_type at_icastle; * stages */ int buildingeffsize(const building * b, int img) { - const struct building_type *btype = NULL; + const struct building_type *btype = NULL; - if (b == NULL) - return 0; + if (b == NULL) + return 0; - btype = b->type; - if (img) { - const attrib *a = a_find(b->attribs, &at_icastle); - if (a) { - btype = (const struct building_type *)a->data.v; + btype = b->type; + if (img) { + const attrib *a = a_find(b->attribs, &at_icastle); + if (a) { + btype = (const struct building_type *)a->data.v; + } } - } - return bt_effsize(btype, b, b->size); + return bt_effsize(btype, b, b->size); } int bt_effsize(const building_type * btype, const building * b, int bsize) { - int i = bsize, n = 0; - const construction *cons = btype->construction; + int i = bsize, n = 0; + const construction *cons = btype->construction; - /* TECH DEBT: simplest thing that works for E3 dwarf/halfling faction rules */ - if (b && get_param_int(global.parameters, "rules.dwarf_castles", 0) - && strcmp(btype->_name, "castle") == 0) { - unit *u = building_owner(b); - if (u && u->faction->race == get_race(RC_HALFLING)) { - i = bsize * 10 / 8; + /* TECH DEBT: simplest thing that works for E3 dwarf/halfling faction rules */ + if (b && get_param_int(global.parameters, "rules.dwarf_castles", 0) + && strcmp(btype->_name, "castle") == 0) { + unit *u = building_owner(b); + if (u && u->faction->race == get_race(RC_HALFLING)) { + i = bsize * 10 / 8; + } } - } - if (!cons || !cons->improvement) { - return 0; - } + if (!cons || !cons->improvement) { + return 0; + } - while (cons && cons->maxsize != -1 && i >= cons->maxsize) { - i -= cons->maxsize; - cons = cons->improvement; - ++n; - } + while (cons && cons->maxsize != -1 && i >= cons->maxsize) { + i -= cons->maxsize; + cons = cons->improvement; + ++n; + } - return n; + return n; } const char *write_buildingname(const building * b, char *ibuf, size_t size) { - slprintf(ibuf, size, "%s (%s)", b->name, itoa36(b->no)); - return ibuf; + slprintf(ibuf, size, "%s (%s)", b->name, itoa36(b->no)); + return ibuf; } const char *buildingname(const building * b) { - typedef char name[OBJECTIDSIZE + 1]; - static name idbuf[8]; - static int nextbuf = 0; - char *ibuf = idbuf[(++nextbuf) % 8]; - return write_buildingname(b, ibuf, sizeof(name)); + typedef char name[OBJECTIDSIZE + 1]; + static name idbuf[8]; + static int nextbuf = 0; + char *ibuf = idbuf[(++nextbuf) % 8]; + return write_buildingname(b, ibuf, sizeof(name)); } void building_set_owner(struct unit * owner) { - assert(owner && owner->building); - owner->building->_owner = owner; + assert(owner && owner->building); + owner->building->_owner = owner; } static unit *building_owner_ex(const building * bld, const struct faction * last_owner) { - unit *u, *heir = 0; - /* Eigentümer tot oder kein Eigentümer vorhanden. Erste lebende Einheit - * nehmen. */ - for (u = bld->region->units; u; u = u->next) { - if (u->building == bld) { - if (u->number > 0) { - if (heir && last_owner && heir->faction!=last_owner && u->faction==last_owner) { - heir = u; - break; /* we found someone from the same faction who is not dead. let's take this guy */ + unit *u, *heir = 0; + /* Eigentümer tot oder kein Eigentümer vorhanden. Erste lebende Einheit + * nehmen. */ + for (u = bld->region->units; u; u = u->next) { + if (u->building == bld) { + if (u->number > 0) { + if (heir && last_owner && heir->faction != last_owner && u->faction == last_owner) { + heir = u; + break; /* we found someone from the same faction who is not dead. let's take this guy */ + } + else if (!heir) { + heir = u; /* you'll do in an emergency */ + } + } } - else if (!heir) { - heir = u; /* you'll do in an emergency */ - } - } } - } if (!heir && check_param(global.parameters, "rules.region_owner_pay_building", bld->type->_name)) { if (rule_region_owners()) { u = building_owner(largestbuilding(bld->region, &cmp_taxes, false)); @@ -575,54 +575,54 @@ static unit *building_owner_ex(const building * bld, const struct faction * last unit *building_owner(const building * bld) { - if (!bld) { - return NULL; - } - unit *owner = bld->_owner; - if (!owner || (owner->building!=bld || owner->number<=0)) { - unit * heir = building_owner_ex(bld, owner?owner->faction:0); - return (heir && heir->number>0) ? heir : 0; - } - return owner; + if (!bld) { + return NULL; + } + unit *owner = bld->_owner; + if (!owner || (owner->building != bld || owner->number <= 0)) { + unit * heir = building_owner_ex(bld, owner ? owner->faction : 0); + return (heir && heir->number > 0) ? heir : 0; + } + return owner; } void building_update_owner(building * bld) { - unit * owner = bld->_owner; - bld->_owner = building_owner_ex(bld, owner?owner->faction:0); + unit * owner = bld->_owner; + bld->_owner = building_owner_ex(bld, owner ? owner->faction : 0); } const char *building_getname(const building * self) { - return self->name; + return self->name; } void building_setname(building * self, const char *name) { - free(self->name); - if (name) - self->name = _strdup(name); - else - self->name = NULL; + free(self->name); + if (name) + self->name = _strdup(name); + else + self->name = NULL; } region *building_getregion(const building * b) { - return b->region; + return b->region; } void building_setregion(building * b, region * r) { - building **blist = &b->region->buildings; - while (*blist && *blist != b) { - blist = &(*blist)->next; - } - *blist = b->next; - b->next = NULL; + building **blist = &b->region->buildings; + while (*blist && *blist != b) { + blist = &(*blist)->next; + } + *blist = b->next; + b->next = NULL; - blist = &r->buildings; - while (*blist && *blist != b) - blist = &(*blist)->next; - *blist = b; + blist = &r->buildings; + while (*blist && *blist != b) + blist = &(*blist)->next; + *blist = b; - b->region = r; + b->region = r; } diff --git a/src/laws.test.c b/src/laws.test.c index 5275d459e..f4ae84a16 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -13,12 +13,14 @@ #include #include +#include #include #include #include #include +#include static void test_new_building_can_be_renamed(CuTest * tc) { @@ -356,16 +358,23 @@ struct pay_fixture { unit *u2; }; +static double level_taxes(const building * b, int level) { + return b->size*level*2.0; +} + static void setup_pay_cmd(struct pay_fixture *fix) { faction *f; region *r; building *b; + building_type *btcastle; test_create_world(); f = test_create_faction(NULL); r = findregion(0, 0); assert(r && f); - b = test_create_building(r, bt_get_or_create("lighthouse")); + btcastle = bt_get_or_create("castle"); + btcastle->taxes = level_taxes; + b = test_create_building(r, btcastle); assert(b); fix->u1 = test_create_unit(f, r); fix->u2 = test_create_unit(f, r); @@ -394,6 +403,30 @@ static void test_pay_cmd(CuTest *tc) { test_cleanup(); } +static void test_pay_cmd_other_building(CuTest *tc) { + struct pay_fixture fix; + order *ord; + faction *f; + building *b; + char cmd[32]; + + test_cleanup(); + setup_pay_cmd(&fix); + f = fix.u1->faction; + b = test_create_building(fix.u1->region, bt_get_or_create("lighthouse")); + set_param(&global.parameters, "rules.region_owners", "1"); + set_param(&global.parameters, "rules.region_owner_pay_building", "lighthouse"); + update_owners(b->region); + + _snprintf(cmd, sizeof(cmd), "NOT %s", itoa36(b->no)); + ord = create_order(K_PAY, f->locale, cmd); + assert(ord); + CuAssertPtrEquals(tc, fix.u1, building_owner(b)); + CuAssertIntEquals(tc, 0, pay_cmd(fix.u1, ord)); + CuAssertIntEquals(tc, BLD_DONTPAY, b->flags&BLD_DONTPAY); + test_cleanup(); +} + static void test_pay_cmd_must_be_owner(CuTest *tc) { struct pay_fixture fix; order *ord; @@ -412,7 +445,6 @@ static void test_pay_cmd_must_be_owner(CuTest *tc) { test_cleanup(); } - static void test_new_units(CuTest *tc) { unit *u; faction *f; @@ -581,6 +613,7 @@ CuSuite *get_laws_suite(void) SUITE_ADD_TEST(suite, test_reserve_self); SUITE_ADD_TEST(suite, test_reserve_cmd); SUITE_ADD_TEST(suite, test_pay_cmd); + SUITE_ADD_TEST(suite, test_pay_cmd_other_building); SUITE_ADD_TEST(suite, test_pay_cmd_must_be_owner); SUITE_ADD_TEST(suite, test_new_units); SUITE_ADD_TEST(suite, test_cannot_create_unit_above_limit);