diff --git a/res/core/common/buildings.xml b/res/core/common/buildings.xml index 540e9f085..a799ce309 100644 --- a/res/core/common/buildings.xml +++ b/res/core/common/buildings.xml @@ -93,7 +93,6 @@ - @@ -102,6 +101,8 @@ + + diff --git a/res/core/messages.xml b/res/core/messages.xml index a12252d96..5d8a60ef0 100644 --- a/res/core/messages.xml +++ b/res/core/messages.xml @@ -5539,6 +5539,15 @@ "$unit($unit) in $region($region): '$order($command)' - Diesen Gegenstand kann die Einheit nicht herstellen." "$unit($unit) in $region($region): '$order($command)' - This unit cannot produce that." + + + + + + + "$unit($unit) in $region($region): '$order($command)' - Diese Rasse kann das nicht herstellen." + "$unit($unit) in $region($region): '$order($command)' - This race cannot produce that." + diff --git a/res/core/resources/iron.xml b/res/core/resources/iron.xml index 70d5c1080..1aff826fc 100644 --- a/res/core/resources/iron.xml +++ b/res/core/resources/iron.xml @@ -3,7 +3,8 @@ + - + diff --git a/res/core/weapons/greatbow.xml b/res/core/weapons/greatbow.xml index 32f07a41e..452ac0250 100644 --- a/res/core/weapons/greatbow.xml +++ b/res/core/weapons/greatbow.xml @@ -1,8 +1,8 @@ + - diff --git a/res/e3a/armor/scale.xml b/res/e3a/armor/scale.xml index 1038f4d6c..c8c6804de 100644 --- a/res/e3a/armor/scale.xml +++ b/res/e3a/armor/scale.xml @@ -1,8 +1,8 @@ + - diff --git a/res/e3a/armor/towershield.xml b/res/e3a/armor/towershield.xml index 38e4f0928..45958f175 100644 --- a/res/e3a/armor/towershield.xml +++ b/res/e3a/armor/towershield.xml @@ -1,8 +1,8 @@ + - diff --git a/res/e3a/resources/iron.xml b/res/e3a/resources/iron.xml index 01e0d8d6d..55f63ca67 100644 --- a/res/e3a/resources/iron.xml +++ b/res/e3a/resources/iron.xml @@ -3,6 +3,7 @@ + diff --git a/res/e3a/weapons/greatbow.xml b/res/e3a/weapons/greatbow.xml index 745d1793d..358a55f7d 100644 --- a/res/e3a/weapons/greatbow.xml +++ b/res/e3a/weapons/greatbow.xml @@ -4,9 +4,9 @@ * has lower damage --> + - diff --git a/res/e3a/weapons/rep_crossbow.xml b/res/e3a/weapons/rep_crossbow.xml index 02f0d865f..ffa1c0455 100644 --- a/res/e3a/weapons/rep_crossbow.xml +++ b/res/e3a/weapons/rep_crossbow.xml @@ -1,8 +1,8 @@ + - diff --git a/scripts/tests/production.lua b/scripts/tests/production.lua index f797f3d32..b470a2652 100644 --- a/scripts/tests/production.lua +++ b/scripts/tests/production.lua @@ -124,6 +124,33 @@ function test_quarry_bonus() turn_end() end +function test_smithy_no_bonus() +-- a smithy does not give a bonus to other skills +-- five cartmakers make 5 carts, no matter what + local r = region.create(0, 0, 'mountain') + local f = create_faction('human') + local u = unit.create(f, r, 1) + + turn_begin() + u.building = building.create(u.region, 'smithy') + u.building.working = false + u.building.size = 10 + u.number = 5 + u:set_skill('cartmaking', 1) -- needs 1 min + u:add_item('log', 100) + u:add_order("MACHE Wagen") + turn_process() -- building disabled + assert_equal(5, u:get_item('cart')) + assert_equal(75, u:get_item('log')) + + u.building.working = true + turn_process() -- building active + assert_equal(10, u:get_item('cart')) + assert_equal(50, u:get_item('log')) + + turn_end() +end + function test_smithy_bonus_iron() -- a smithy adds +1 to weaponsmithing, and saves 50% iron local r = region.create(0, 0, 'mountain') diff --git a/src/attributes/CMakeLists.txt b/src/attributes/CMakeLists.txt index d0aa252ee..8be9dc7ff 100644 --- a/src/attributes/CMakeLists.txt +++ b/src/attributes/CMakeLists.txt @@ -12,7 +12,6 @@ follow.c hate.c iceberg.c key.c -matmod.c moved.c movement.c dict.c diff --git a/src/attributes/matmod.c b/src/attributes/matmod.c deleted file mode 100644 index 0cf1955d8..000000000 --- a/src/attributes/matmod.c +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright (c) 1998-2015, Enno Rehling -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 -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -**/ - -#include -#include "matmod.h" - -#include -#include - -attrib_type at_matmod = { - "matmod", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - ATF_PRESERVE -}; - -attrib *make_matmod(mm_fun function) -{ - attrib *a = a_new(&at_matmod); - a->data.f = (void(*)(void))function; - return a; -} diff --git a/src/attributes/matmod.h b/src/attributes/matmod.h deleted file mode 100644 index 99171d338..000000000 --- a/src/attributes/matmod.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright (c) 1998-2015, Enno Rehling -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 -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -**/ - -#ifndef H_ATTRIBUTE_MATMOD -#define H_ATTRIBUTE_MATMOD - -#ifdef __cplusplus -extern "C" { -#endif - - struct resource_type; - struct unit; - typedef int(*mm_fun) (const struct unit * u, - const struct resource_type * rtype, int value); - - extern struct attrib_type at_matmod; - extern struct attrib *make_matmod(mm_fun function); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/economy.c b/src/economy.c index 03ab07bcd..69efe85cb 100644 --- a/src/economy.c +++ b/src/economy.c @@ -798,7 +798,69 @@ void economics(region * r) } -/* ------------------------------------------------------------- */ +static void mod_skill(const resource_mod *mod, skill_t sk, int *skill) { + skill_t msk; + assert(mod->type == RMT_PROD_SKILL); + msk = (skill_t)mod->value.sa[0]; + if (msk == NOSKILL || msk == sk) { + *skill += mod->value.sa[1]; + } +} + +static struct message * get_modifiers(unit *u, skill_t sk, const resource_type *rtype, variant *savep, int *skillp) { + struct building *b = inside_building(u); + const struct building_type *btype = building_is_active(b) ? b->type : NULL; + int save_n = 1, save_d = 1; + int skill = 0; + int need_race = 0, need_bldg = 0; + resource_mod *mod; + + if (btype && btype->modifiers) { + for (mod = btype->modifiers; mod && mod->type != RMT_END; ++mod) { + if (mod->type == RMT_PROD_SKILL) { + mod_skill(mod, sk, &skill); + } + } + } + + for (mod = rtype->modifiers; mod && mod->type != RMT_END; ++mod) { + if (mod->btype == NULL || mod->btype == btype) { + if (mod->race == NULL || mod->race == u_race(u)) { + switch (mod->type) { + case RMT_PROD_SAVE: + if (savep) { + save_n *= mod->value.sa[0]; + save_d *= mod->value.sa[1]; + } + break; + case RMT_PROD_SKILL: + mod_skill(mod, sk, &skill); + break; + case RMT_PROD_REQUIRE: + if (mod->race) need_race |= 1; + if (mod->btype) need_bldg |= 1; + break; + default: + /* is not a production modifier, ignore it */ + break; + } + } + } + if (mod->type == RMT_PROD_REQUIRE) { + if (mod->race) need_race |= 2; + if (mod->btype) need_bldg |= 2; + } + } + if (need_race == 2) { + return msg_error(u, u->thisorder, 117); + } + if (need_bldg == 2) { + return msg_error(u, u->thisorder, 104); + } + *skillp = skill; + if (savep) *savep = frac_make(save_n, save_d); + return NULL; +} static void manufacture(unit * u, const item_type * itype, int want) { @@ -806,22 +868,20 @@ static void manufacture(unit * u, const item_type * itype, int want) int skill; int minskill = itype->construction->minskill; skill_t sk = itype->construction->skill; + message *msg; + int skill_mod; - skill = effskill(u, sk, 0); - skill = - skillmod(itype->construction->attribs, u, u->region, sk, skill, SMF_PRODUCTION); - - if (skill < 0) { - /* an error occured */ - int err = -skill; - cmistake(u, u->thisorder, err, MSG_PRODUCE); + msg = get_modifiers(u, sk, itype->rtype, NULL, &skill_mod); + if (msg) { + ADDMSG(&u->faction->msgs, msg); return; } + skill = effskill(u, sk, 0); if (want == 0) { want = maxbuild(u, itype->construction); } - n = build(u, itype->construction, 0, want); + n = build(u, itype->construction, 0, want, skill_mod); switch (n) { case ENEEDSKILL: ADDMSG(&u->faction->msgs, @@ -879,38 +939,6 @@ enum { AFL_LOWSKILL = 1 << 1 }; -struct message * get_modifiers(unit *u, const resource_type *rtype, variant *savep, int *skillp) { - struct building *b = inside_building(u); - const struct building_type *btype = building_is_active(b) ? b->type : NULL; - int save_n = 1, save_d = 1; - int skill = 0; - resource_mod *mod; - - for (mod = rtype->modifiers; mod && mod->flags != 0; ++mod) { - if (mod->btype == NULL || mod->btype == btype) { - if (mod->race == NULL || mod->race == u_race(u)) { - if (mod->flags & RMF_SAVEMATERIAL) { - save_n *= mod->value.sa[0]; - save_d *= mod->value.sa[1]; - } - if (mod->flags & RMF_SKILL) { - skill += mod->value.i; - } - } - } else if (mod->flags & RMF_REQUIREDBUILDING) { - return msg_error(u, u->thisorder, 104); - } - } - *skillp = skill; - assert(save_n < SHRT_MAX); - assert(save_n > SHRT_MIN); - assert(save_d < SHRT_MAX); - assert(save_d > SHRT_MIN); - savep->sa[0] = (short)save_n; - savep->sa[1] = (short)save_d; - return NULL; -} - static void allocate_resource(unit * u, const resource_type * rtype, int want) { const item_type *itype = resource2item(rtype); @@ -921,12 +949,14 @@ static void allocate_resource(unit * u, const resource_type * rtype, int want) const resource_type *rring; int amount, skill, skill_mod = 0; variant save_mod; + skill_t sk; /* momentan kann man keine ressourcen abbauen, wenn man daf�r * Materialverbrauch hat: */ assert(itype != NULL && (itype->construction == NULL || itype->construction->materials == NULL)); + sk = itype->construction->skill; if (!rtype->raw) { int avail = limit_resource(r, rtype); if (avail <= 0) { @@ -941,7 +971,7 @@ static void allocate_resource(unit * u, const resource_type * rtype, int want) } if (rtype->modifiers) { - message *msg = get_modifiers(u, rtype, &save_mod, &skill_mod); + message *msg = get_modifiers(u, sk, rtype, &save_mod, &skill_mod); if (msg) { ADDMSG(&u->faction->msgs, msg); return; @@ -969,17 +999,14 @@ static void allocate_resource(unit * u, const resource_type * rtype, int want) } } - assert(itype->construction->skill != 0 - || "limited resource needs a required skill for making it"); - skill = effskill(u, itype->construction->skill, 0); + assert(sk != NOSKILL || "limited resource needs a required skill for making it"); + skill = effskill(u, sk, 0); if (skill == 0) { - skill_t sk = itype->construction->skill; add_message(&u->faction->msgs, msg_feedback(u, u->thisorder, "skill_needed", "skill", sk)); return; } if (skill < itype->construction->minskill) { - skill_t sk = itype->construction->skill; add_message(&u->faction->msgs, msg_feedback(u, u->thisorder, "manufacture_skills", "skill minskill product", sk, itype->construction->minskill, @@ -1204,7 +1231,7 @@ static void create_potion(unit * u, const potion_type * ptype, int want) if (want == 0) { want = maxbuild(u, ptype->itype->construction); } - built = build(u, ptype->itype->construction, 0, want); + built = build(u, ptype->itype->construction, 0, want, 0); switch (built) { case ELOWSKILL: case ENEEDSKILL: diff --git a/src/economy.test.c b/src/economy.test.c index a0a000ccd..842db45f8 100644 --- a/src/economy.test.c +++ b/src/economy.test.c @@ -345,7 +345,114 @@ static void test_income(CuTest *tc) test_cleanup(); } -static void test_make_item(CuTest *tc) { +static void test_modify_material(CuTest *tc) { + unit *u; + struct item_type *itype; + resource_type *rtype; + resource_mod *mod; + + test_setup(); + init_resources(); + + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + set_level(u, SK_WEAPONSMITH, 1); + + /* the unit's race gets 2x savings on iron used to produce goods */ + itype = test_create_itemtype("iron"); + rtype = itype->rtype; + mod = rtype->modifiers = calloc(2, sizeof(resource_mod)); + mod[0].type = RMT_USE_SAVE; + mod[0].value = frac_make(2, 1); + mod[0].race = u_race(u); + + itype = test_create_itemtype("sword"); + make_item(u, itype, 1); + CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error_cannotmake")); + CuAssertIntEquals(tc, 0, get_item(u, itype)); + test_clear_messages(u->faction); + itype->construction = calloc(1, sizeof(construction)); + itype->construction->skill = SK_WEAPONSMITH; + itype->construction->minskill = 1; + itype->construction->maxsize = 1; + itype->construction->reqsize = 1; + itype->construction->materials = calloc(2, sizeof(requirement)); + itype->construction->materials[0].rtype = rtype; + itype->construction->materials[0].number = 2; + + set_item(u, rtype->itype, 1); /* 1 iron should get us 1 sword */ + make_item(u, itype, 1); + CuAssertIntEquals(tc, 1, get_item(u, itype)); + CuAssertIntEquals(tc, 0, get_item(u, rtype->itype)); + + u_setrace(u, test_create_race("smurf")); + set_item(u, rtype->itype, 2); /* 2 iron should be required now */ + make_item(u, itype, 1); + CuAssertIntEquals(tc, 2, get_item(u, itype)); + CuAssertIntEquals(tc, 0, get_item(u, rtype->itype)); + + test_cleanup(); +} + +static void test_modify_skill(CuTest *tc) { + unit *u; + struct item_type *itype; + /* building_type *btype; */ + resource_type *rtype; + resource_mod *mod; + + test_setup(); + init_resources(); + + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + set_level(u, SK_WEAPONSMITH, 1); + + itype = test_create_itemtype("iron"); + rtype = itype->rtype; + + itype = test_create_itemtype("sword"); + make_item(u, itype, 1); + CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error_cannotmake")); + CuAssertIntEquals(tc, 0, get_item(u, itype)); + test_clear_messages(u->faction); + itype->construction = calloc(1, sizeof(construction)); + itype->construction->skill = SK_WEAPONSMITH; + itype->construction->minskill = 1; + itype->construction->maxsize = -1; + itype->construction->reqsize = 1; + itype->construction->materials = calloc(2, sizeof(requirement)); + itype->construction->materials[0].rtype = rtype; + itype->construction->materials[0].number = 1; + + /* our race gets a +1 bonus to the item's production skill */ + mod = itype->rtype->modifiers = calloc(2, sizeof(resource_mod)); + mod[0].type = RMT_PROD_SKILL; + mod[0].value.sa[0] = SK_WEAPONSMITH; + mod[0].value.sa[1] = 1; + mod[0].race = u_race(u); + + set_item(u, rtype->itype, 2); /* 2 iron should get us 2 swords */ + make_item(u, itype, 2); + CuAssertIntEquals(tc, 2, get_item(u, itype)); + CuAssertIntEquals(tc, 0, get_item(u, rtype->itype)); + + mod[0].value.sa[0] = NOSKILL; /* match any skill */ + set_item(u, rtype->itype, 2); + make_item(u, itype, 2); + CuAssertIntEquals(tc, 4, get_item(u, itype)); + CuAssertIntEquals(tc, 0, get_item(u, rtype->itype)); + + + u_setrace(u, test_create_race("smurf")); + set_item(u, rtype->itype, 2); + make_item(u, itype, 1); /* only enough skill to make 1 now */ + CuAssertIntEquals(tc, 5, get_item(u, itype)); + CuAssertIntEquals(tc, 1, get_item(u, rtype->itype)); + + test_cleanup(); +} + + +static void test_modify_production(CuTest *tc) { unit *u; struct item_type *itype; const struct resource_type *rt_silver; @@ -393,7 +500,7 @@ static void test_make_item(CuTest *tc) { CuAssertIntEquals(tc, 290, region_getresource(u->region, rtype)); /* used 10 stones to make 10 stones */ rtype->modifiers = calloc(2, sizeof(resource_mod)); - rtype->modifiers[0].flags = RMF_SAVEMATERIAL; + rtype->modifiers[0].type = RMT_PROD_SAVE; rtype->modifiers[0].race = u->_race; rtype->modifiers[0].value.sa[0] = (short)(0.5+100*d); rtype->modifiers[0].value.sa[1] = 100; @@ -413,13 +520,34 @@ static void test_make_item(CuTest *tc) { CuAssertIntEquals(tc, 28, get_item(u, itype)); CuAssertIntEquals(tc, 280, region_getresource(u->region, rtype)); /* 50% saving = 3 stones make 6 stones */ - rtype->modifiers[0].flags = RMF_REQUIREDBUILDING; + rtype->modifiers[0].type = RMT_PROD_REQUIRE; rtype->modifiers[0].race = NULL; rtype->modifiers[0].btype = bt_get_or_create("mine"); + test_clear_messages(u->faction); make_item(u, itype, 10); + CuAssertIntEquals(tc, 28, get_item(u, itype)); CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error104")); + rtype->modifiers[0].type = RMT_PROD_REQUIRE; + rtype->modifiers[0].race = test_create_race("smurf"); + rtype->modifiers[0].btype = NULL; + + test_clear_messages(u->faction); + make_item(u, itype, 10); + CuAssertIntEquals(tc, 28, get_item(u, itype)); + CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error117")); + + rtype->modifiers[1].type = RMT_PROD_REQUIRE; + rtype->modifiers[1].race = u_race(u); + rtype->modifiers[1].btype = NULL; + + test_clear_messages(u->faction); + make_item(u, itype, 10); + CuAssertPtrEquals(tc, NULL, u->faction->msgs); + split_allocations(u->region); + CuAssertIntEquals(tc, 38, get_item(u, itype)); + test_cleanup(); } @@ -429,7 +557,9 @@ CuSuite *get_economy_suite(void) SUITE_ADD_TEST(suite, test_give_control_building); SUITE_ADD_TEST(suite, test_give_control_ship); SUITE_ADD_TEST(suite, test_income); - SUITE_ADD_TEST(suite, test_make_item); + SUITE_ADD_TEST(suite, test_modify_production); + SUITE_ADD_TEST(suite, test_modify_skill); + SUITE_ADD_TEST(suite, test_modify_material); SUITE_ADD_TEST(suite, test_steal_okay); SUITE_ADD_TEST(suite, test_steal_ocean); SUITE_ADD_TEST(suite, test_steal_nosteal); diff --git a/src/kernel/build.c b/src/kernel/build.c index 88ab35fd2..a8125d1a8 100644 --- a/src/kernel/build.c +++ b/src/kernel/build.c @@ -43,6 +43,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include #include @@ -67,9 +68,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include -/* attributes inclues */ -#include - struct building *getbuilding(const struct region *r) { building *b = findbuilding(getid()); @@ -404,16 +402,26 @@ static int required(int size, int msize, int maxneed) return used; } -static int -matmod(const attrib * a, const unit * u, const resource_type * material, -int value) +static int matmod(const unit * u, const resource_type * rtype, int value) { - for (a = a_find((attrib *)a, &at_matmod); a && a->type == &at_matmod; - a = a->next) { - mm_fun fun = (mm_fun)a->data.f; - value = fun(u, material, value); - if (value < 0) - return value; /* pass errors to caller */ + if (rtype->modifiers) { + variant save = frac_make(1, 1); + const struct building_type *btype = NULL; + const struct race *rc = u_race(u); + resource_mod *mod; + if (u->building && inside_building(u)) { + btype = u->building->type; + } + for (mod = rtype->modifiers; mod->type != RMT_END; ++mod) { + if (mod->type == RMT_USE_SAVE) { + if (!mod->btype || mod->btype == btype) { + if (!mod->race || mod->race == rc) { + save = frac_mul(save, mod->value); + } + } + } + } + return value * save.sa[0] / save.sa[1]; } return value; } @@ -439,14 +447,8 @@ static int use_materials(unit *u, const construction *type, int n, int completed required(completed + n, type->reqsize, type->materials[c].number); int multi = 1; int canuse = 100; /* normalization */ - if (building_is_active(u->building) && inside_building(u)) { - canuse = matmod(u->building->type->attribs, u, rtype, canuse); - } - if (canuse < 0) { - return canuse; /* pass errors to caller */ - } - canuse = matmod(type->attribs, u, rtype, canuse); - + canuse = matmod(u, rtype, canuse); + assert(canuse >= 0); assert(canuse % 100 == 0 || !"only constant multipliers are implemented in build()"); multi = canuse / 100; @@ -468,14 +470,9 @@ static int count_materials(unit *u, const construction *type, int n, int complet const struct resource_type *rtype = type->materials[c].rtype; int need, prebuilt; int canuse = get_pooled(u, rtype, GET_DEFAULT, INT_MAX); + canuse = matmod(u, rtype, canuse); - if (building_is_active(u->building) && inside_building(u)) { - canuse = matmod(u->building->type->attribs, u, rtype, canuse); - } - - if (canuse < 0) - return canuse; /* pass errors to caller */ - canuse = matmod(type->attribs, u, rtype, canuse); + assert(canuse >= 0); if (type->reqsize > 1) { prebuilt = required(completed, type->reqsize, type->materials[c].number); @@ -503,7 +500,7 @@ static int count_materials(unit *u, const construction *type, int n, int complet * of the first object have already been finished. return the * actual size that could be built. */ -int build(unit * u, const construction * ctype, int completed, int want) +int build(unit * u, const construction * ctype, int completed, int want, int skill_mod) { const construction *type = ctype; int skills = INT_MAX; /* number of skill points remainig */ @@ -536,17 +533,8 @@ int build(unit * u, const construction * ctype, int completed, int want) if (basesk == 0) return ENEEDSKILL; - effsk = basesk; - if (building_is_active(u->building) && inside_building(u)) { - effsk = skillmod(u->building->type->attribs, u, u->region, type->skill, - effsk, SMF_PRODUCTION); - } - effsk = skillmod(type->attribs, u, u->region, type->skill, - effsk, SMF_PRODUCTION); - if (effsk < 0) - return effsk; /* pass errors to caller */ - if (effsk == 0) - return ENEEDSKILL; + effsk = basesk + skill_mod; + assert(effsk >= 0); skills = effsk * u->number; @@ -799,7 +787,7 @@ build_building(unit * u, const building_type * btype, int id, int want, order * } } } - built = build(u, btype->construction, built, n); + built = build(u, btype->construction, built, n, 0); switch (built) { case ECOMPLETE: @@ -884,7 +872,7 @@ static void build_ship(unit * u, ship * sh, int want) const construction *construction = sh->type->construction; int size = (sh->size * DAMAGE_SCALE - sh->damage) / DAMAGE_SCALE; int n; - int can = build(u, construction, size, want); + int can = build(u, construction, size, want, 0); if ((n = construction->maxsize - sh->size) > 0 && can > 0) { if (can >= n) { diff --git a/src/kernel/build.h b/src/kernel/build.h index b09dd8332..bb1204af9 100644 --- a/src/kernel/build.h +++ b/src/kernel/build.h @@ -55,9 +55,6 @@ extern "C" { * last level of a building points to NULL, as do objects of * an unlimited size. */ - struct attrib *attribs; - /* stores skill modifiers and other attributes */ - } construction; void free_construction(struct construction *cons); @@ -76,7 +73,7 @@ extern "C" { void sunhash(struct ship *sh); int roqf_factor(void); - int build(struct unit *u, const construction * ctype, int completed, int want); + int build(struct unit *u, const construction * ctype, int completed, int want, int skill_mod); int maxbuild(const struct unit *u, const construction * cons); struct message *msg_materials_required(struct unit *u, struct order *ord, const struct construction *ctype, int multi); diff --git a/src/kernel/build.test.c b/src/kernel/build.test.c index 77efcfd73..34f645856 100644 --- a/src/kernel/build.test.c +++ b/src/kernel/build.test.c @@ -63,10 +63,10 @@ static void test_build_requires_materials(CuTest *tc) { u = setup_build(&bf); set_level(u, SK_ARMORER, 2); - CuAssertIntEquals(tc, ENOMATERIALS, build(u, &bf.cons, 0, 1)); + CuAssertIntEquals(tc, ENOMATERIALS, build(u, &bf.cons, 0, 1, 0)); itype = bf.cons.materials[0].rtype->itype; i_change(&u->items, itype, 2); - CuAssertIntEquals(tc, 1, build(u, &bf.cons, 0, 1)); + CuAssertIntEquals(tc, 1, build(u, &bf.cons, 0, 1, 0)); CuAssertIntEquals(tc, 1, i_get(u->items, itype)); teardown_build(&bf); } @@ -84,12 +84,12 @@ static void test_build_requires_building(CuTest *tc) { bf.cons.btype = btype = bt_get_or_create("hodor"); btype->maxcapacity = 1; btype->capacity = 1; - CuAssertIntEquals_Msg(tc, "must be inside a production building", EBUILDINGREQ, build(u, &bf.cons, 0, 1)); + CuAssertIntEquals_Msg(tc, "must be inside a production building", EBUILDINGREQ, build(u, &bf.cons, 0, 1, 0)); u->building = test_create_building(u->region, btype); fset(u->building, BLD_MAINTAINED); - CuAssertIntEquals(tc, 1, build(u, &bf.cons, 0, 1)); + CuAssertIntEquals(tc, 1, build(u, &bf.cons, 0, 1, 0)); btype->maxcapacity = 0; - CuAssertIntEquals_Msg(tc, "cannot build when production building capacity exceeded", EBUILDINGREQ, build(u, &bf.cons, 0, 1)); + CuAssertIntEquals_Msg(tc, "cannot build when production building capacity exceeded", EBUILDINGREQ, build(u, &bf.cons, 0, 1, 0)); teardown_build(&bf); } @@ -101,7 +101,7 @@ static void test_build_failure_missing_skill(CuTest *tc) { u = setup_build(&bf); rtype = bf.cons.materials[0].rtype; i_change(&u->items, rtype->itype, 1); - CuAssertIntEquals(tc, ENEEDSKILL, build(u, &bf.cons, 1, 1)); + CuAssertIntEquals(tc, ENEEDSKILL, build(u, &bf.cons, 1, 1, 0)); teardown_build(&bf); } @@ -114,7 +114,7 @@ static void test_build_failure_low_skill(CuTest *tc) { rtype = bf.cons.materials[0].rtype; i_change(&u->items, rtype->itype, 1); set_level(u, SK_ARMORER, bf.cons.minskill - 1); - CuAssertIntEquals(tc, ELOWSKILL, build(u, &bf.cons, 0, 10)); + CuAssertIntEquals(tc, ELOWSKILL, build(u, &bf.cons, 0, 10, 0)); teardown_build(&bf); } @@ -128,7 +128,7 @@ static void test_build_failure_completed(CuTest *tc) { i_change(&u->items, rtype->itype, 1); set_level(u, SK_ARMORER, bf.cons.minskill); bf.cons.maxsize = 1; - CuAssertIntEquals(tc, ECOMPLETE, build(u, &bf.cons, bf.cons.maxsize, 10)); + CuAssertIntEquals(tc, ECOMPLETE, build(u, &bf.cons, bf.cons.maxsize, 10, 0)); CuAssertIntEquals(tc, 1, i_get(u->items, rtype->itype)); teardown_build(&bf); } @@ -143,19 +143,19 @@ static void test_build_limits(CuTest *tc) { assert(rtype); i_change(&u->items, rtype->itype, 1); set_level(u, SK_ARMORER, bf.cons.minskill); - CuAssertIntEquals(tc, 1, build(u, &bf.cons, 0, 10)); + CuAssertIntEquals(tc, 1, build(u, &bf.cons, 0, 10, 0)); CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype)); scale_number(u, 2); set_level(u, SK_ARMORER, bf.cons.minskill); i_change(&u->items, rtype->itype, 2); - CuAssertIntEquals(tc, 2, build(u, &bf.cons, 0, 10)); + CuAssertIntEquals(tc, 2, build(u, &bf.cons, 0, 10, 0)); CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype)); scale_number(u, 2); set_level(u, SK_ARMORER, bf.cons.minskill * 2); i_change(&u->items, rtype->itype, 4); - CuAssertIntEquals(tc, 4, build(u, &bf.cons, 0, 10)); + CuAssertIntEquals(tc, 4, build(u, &bf.cons, 0, 10, 0)); CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype)); teardown_build(&bf); } @@ -174,7 +174,7 @@ static void test_build_with_ring(CuTest *tc) { set_level(u, SK_ARMORER, bf.cons.minskill); i_change(&u->items, rtype->itype, 20); i_change(&u->items, ring, 1); - CuAssertIntEquals(tc, 10, build(u, &bf.cons, 0, 20)); + CuAssertIntEquals(tc, 10, build(u, &bf.cons, 0, 20, 0)); CuAssertIntEquals(tc, 10, i_get(u->items, rtype->itype)); teardown_build(&bf); } @@ -193,16 +193,16 @@ static void test_build_with_potion(CuTest *tc) { i_change(&u->items, rtype->itype, 20); change_effect(u, ptype, 4); set_level(u, SK_ARMORER, bf.cons.minskill); - CuAssertIntEquals(tc, 2, build(u, &bf.cons, 0, 20)); + CuAssertIntEquals(tc, 2, build(u, &bf.cons, 0, 20, 0)); CuAssertIntEquals(tc, 18, i_get(u->items, rtype->itype)); CuAssertIntEquals(tc, 3, get_effect(u, ptype)); set_level(u, SK_ARMORER, bf.cons.minskill * 2); - CuAssertIntEquals(tc, 4, build(u, &bf.cons, 0, 20)); + CuAssertIntEquals(tc, 4, build(u, &bf.cons, 0, 20, 0)); CuAssertIntEquals(tc, 2, get_effect(u, ptype)); set_level(u, SK_ARMORER, bf.cons.minskill); scale_number(u, 2); /* OBS: this scales the effects, too: */ CuAssertIntEquals(tc, 4, get_effect(u, ptype)); - CuAssertIntEquals(tc, 4, build(u, &bf.cons, 0, 20)); + CuAssertIntEquals(tc, 4, build(u, &bf.cons, 0, 20, 0)); CuAssertIntEquals(tc, 2, get_effect(u, ptype)); teardown_build(&bf); } diff --git a/src/kernel/building.c b/src/kernel/building.c index ef95fd782..3eef32cc4 100644 --- a/src/kernel/building.c +++ b/src/kernel/building.c @@ -55,7 +55,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* attributes includes */ #include -#include typedef struct building_typelist { struct building_typelist *next; @@ -99,9 +98,6 @@ bool bt_changed(int *cache) void bt_register(building_type * type) { - if (type->init) { - type->init(type); - } selist_push(&buildingtypes, (void *)type); ++bt_changes; } @@ -224,31 +220,6 @@ building *findbuilding(int 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; -} - -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; -} - -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)); -} - static const char *castle_name_i(const struct building_type *btype, const struct building *b, int bsize, const char *fname[]) { @@ -915,7 +886,6 @@ int cmp_current_owner(const building * b, const building * a) void register_buildings(void) { - 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"); diff --git a/src/kernel/building.h b/src/kernel/building.h index 6d66aa9ab..f9803cdbf 100644 --- a/src/kernel/building.h +++ b/src/kernel/building.h @@ -66,10 +66,10 @@ extern "C" { 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 */ const char *(*name) (const struct building_type *, const struct building * b, int size); - void(*init) (struct building_type *); void(*age) (struct building *); double(*taxes) (const struct building *, int size); struct attrib *attribs; diff --git a/src/kernel/building.test.c b/src/kernel/building.test.c index 3f415a913..0296e3451 100644 --- a/src/kernel/building.test.c +++ b/src/kernel/building.test.c @@ -296,7 +296,6 @@ static void test_btype_defaults(CuTest *tc) { CuAssertPtrEquals(tc, 0, btype->maintenance); CuAssertPtrEquals(tc, 0, btype->construction); CuAssertTrue(tc, !btype->name); - CuAssertTrue(tc, !btype->init); CuAssertTrue(tc, !btype->age); CuAssertTrue(tc, !btype->taxes); CuAssertDblEquals(tc, 1.0, btype->auraregen, 0.0); diff --git a/src/kernel/item.h b/src/kernel/item.h index 9727e805f..51ebcd2a1 100644 --- a/src/kernel/item.h +++ b/src/kernel/item.h @@ -90,11 +90,6 @@ extern "C" { const resource_type *findresourcetype(const char *name, const struct locale *lang); - /* resource-limits for regions */ -#define RMF_SKILL 0x01 /* int, bonus on resource production skill */ -#define RMF_SAVEMATERIAL 0x02 /* fraction (sa[0]/sa[1]), multiplier on resource usage */ -#define RMF_REQUIREDBUILDING 0x04 /* building, required to build */ - /* bitfield values for item_type::flags */ #define ITF_NONE 0x0000 #define ITF_HERB 0x0001 /* this item is a herb */ diff --git a/src/kernel/resources.h b/src/kernel/resources.h index d94f043fc..1868dffc0 100644 --- a/src/kernel/resources.h +++ b/src/kernel/resources.h @@ -34,8 +34,17 @@ extern "C" { struct rawmaterial *next; } rawmaterial; + /* resource-limits for regions */ + typedef enum resource_modifier_type { + RMT_END, /* array terminator */ + RMT_PROD_SKILL, /* bonus on resource production skill */ + RMT_PROD_SAVE, /* fractional multiplier when produced */ + RMT_PROD_REQUIRE, /* building or race is required to produce this item */ + RMT_USE_SAVE, /* fractional multiplier when used to manufacture something */ + } resource_modifier_type; + typedef struct resource_mod { - int flags; + resource_modifier_type type; variant value; const struct building_type *btype; const struct race *race; diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c index 7cb871785..603231deb 100644 --- a/src/kernel/xmlreader.c +++ b/src/kernel/xmlreader.c @@ -120,6 +120,72 @@ static xmlChar *xml_cleanup_string(xmlChar * str) return str; } +static resource_mod * xml_readmodifiers(xmlXPathObjectPtr result, xmlNodePtr node) { + /* reading eressea/resources/resource/modifier */ + if (result->nodesetval != NULL && result->nodesetval->nodeNr > 0) { + int k; + resource_mod * modifiers = + calloc(result->nodesetval->nodeNr + 1, sizeof(resource_mod)); + for (k = 0; k != result->nodesetval->nodeNr; ++k) { + xmlNodePtr node = result->nodesetval->nodeTab[k]; + xmlChar *propValue; + building_type *btype = NULL; + const race *rc = NULL; + + propValue = xmlGetProp(node, BAD_CAST "race"); + if (propValue != NULL) { + rc = rc_find((const char *)propValue); + if (rc == NULL) + rc = rc_get_or_create((const char *)propValue); + xmlFree(propValue); + } + modifiers[k].race = rc; + + propValue = xmlGetProp(node, BAD_CAST "building"); + if (propValue != NULL) { + btype = bt_get_or_create((const char *)propValue); + xmlFree(propValue); + } + modifiers[k].btype = btype; + + propValue = xmlGetProp(node, BAD_CAST "type"); + assert(propValue != NULL); + if (strcmp((const char *)propValue, "skill") == 0) { + xmlChar *propSkill; + skill_t sk = NOSKILL; + + modifiers[k].type = RMT_PROD_SKILL; + propSkill = xmlGetProp(node, BAD_CAST "skill"); + if (propSkill) { + sk = findskill((const char *)propSkill); + xmlFree(propSkill); + } + modifiers[k].value.sa[0] = (short)sk; + modifiers[k].value.sa[1] = (short)xml_ivalue(node, "value", 0); + } + else if (strcmp((const char *)propValue, "material") == 0) { + modifiers[k].value = xml_fraction(node, "value"); + modifiers[k].type = RMT_PROD_SAVE; + } + else { + if (strcmp((const char *)propValue, "require") == 0) { + modifiers[k].type = RMT_PROD_REQUIRE; + } + else if (strcmp((const char *)propValue, "save") == 0) { + modifiers[k].type = RMT_USE_SAVE; + modifiers[k].value = xml_fraction(node, "value"); + } + else { + log_error("unknown type '%s' for resourcelimit-modifier", (const char *)propValue); + } + } + xmlFree(propValue); + } + return modifiers; + } + return NULL; +} + static void xml_readrequirements(xmlNodePtr * nodeTab, int nodeNr, requirement ** reqArray) { @@ -157,7 +223,6 @@ construction ** consPtr) xmlChar *propValue; construction *con; xmlXPathObjectPtr req; - int m; skill_t sk = NOSKILL; propValue = xmlGetProp(node, BAD_CAST "skill"); @@ -193,23 +258,6 @@ construction ** consPtr) xml_readrequirements(req->nodesetval->nodeTab, req->nodesetval->nodeNr, &con->materials); xmlXPathFreeObject(req); - - /* read construction/modifier */ - xpath->node = node; - req = xmlXPathEvalExpression(BAD_CAST "modifier", xpath); - for (m = 0; m != req->nodesetval->nodeNr; ++m) { - xmlNodePtr node = req->nodesetval->nodeTab[m]; - - propValue = xmlGetProp(node, BAD_CAST "function"); - if (propValue != NULL) { - pf_generic foo = get_function((const char *)propValue); - a_add(&con->attribs, make_skillmod(NOSKILL, SMF_PRODUCTION, - (skillmod_fun)foo, 1.0, 0)); - xmlFree(propValue); - } - - } - xmlXPathFreeObject(req); } xpath->node = pushNode; } @@ -280,6 +328,12 @@ static int parse_buildings(xmlDocPtr doc) if (xml_bvalue(node, "fort", false)) btype->flags |= BTF_FORTIFICATION; + /* reading eressea/buildings/building/modifier */ + xpath->node = node; + result = xmlXPathEvalExpression(BAD_CAST "modifier", xpath); + btype->modifiers = xml_readmodifiers(result, node); + xmlXPathFreeObject(result); + /* reading eressea/buildings/building/construction */ xpath->node = node; result = xmlXPathEvalExpression(BAD_CAST "construction", xpath); @@ -305,9 +359,6 @@ static int parse_buildings(xmlDocPtr doc) (const char *(*)(const struct building_type *, const struct building *, int))fun; } - else if (strcmp((const char *)propValue, "init") == 0) { - btype->init = (void(*)(struct building_type *))fun; - } else if (strcmp((const char *)propValue, "age") == 0) { btype->age = (void(*)(struct building *))fun; } @@ -344,10 +395,6 @@ static int parse_buildings(xmlDocPtr doc) mt->flags |= MTF_VARIABLE; } xmlXPathFreeObject(result); - - /* finally, initialize the new building type */ - if (btype->init) - btype->init(btype); } } xmlXPathFreeObject(buildings); @@ -951,58 +998,9 @@ static int parse_resources(xmlDocPtr doc) if (xml_bvalue(node, "limited", false)) { rtype->flags |= RTF_LIMITED; } - /* reading eressea/resources/resource/modifier */ xpath->node = node; result = xmlXPathEvalExpression(BAD_CAST "modifier", xpath); - if (result->nodesetval != NULL && result->nodesetval->nodeNr > 0) { - rtype->modifiers = - calloc(result->nodesetval->nodeNr + 1, sizeof(resource_mod)); - for (k = 0; k != result->nodesetval->nodeNr; ++k) { - xmlNodePtr node = result->nodesetval->nodeTab[k]; - building_type *btype = NULL; - const race *rc = NULL; - - propValue = xmlGetProp(node, BAD_CAST "race"); - if (propValue != NULL) { - rc = rc_find((const char *)propValue); - if (rc == NULL) - rc = rc_get_or_create((const char *)propValue); - xmlFree(propValue); - } - rtype->modifiers[k].race = rc; - - propValue = xmlGetProp(node, BAD_CAST "building"); - if (propValue != NULL) { - btype = bt_get_or_create((const char *)propValue); - xmlFree(propValue); - } - rtype->modifiers[k].btype = btype; - - propValue = xmlGetProp(node, BAD_CAST "type"); - assert(propValue != NULL); - if (strcmp((const char *)propValue, "skill") == 0) { - rtype->modifiers[k].value.i = xml_ivalue(node, "value", 0); - rtype->modifiers[k].flags = RMF_SKILL; - } - else if (strcmp((const char *)propValue, "material") == 0) { - rtype->modifiers[k].value = xml_fraction(node, "value"); - rtype->modifiers[k].flags = RMF_SAVEMATERIAL; - } - else if (strcmp((const char *)propValue, "require") == 0) { - xmlChar *propBldg = xmlGetProp(node, BAD_CAST "building"); - if (propBldg != NULL) { - btype = bt_get_or_create((const char *)propBldg); - rtype->modifiers[k].btype = btype; - rtype->modifiers[k].flags = RMF_REQUIREDBUILDING; - xmlFree(propBldg); - } - } - else { - log_error("unknown type '%s' for resourcelimit-modifier '%s'\n", (const char *)propValue, rtype->_name); - } - xmlFree(propValue); - } - } + rtype->modifiers = xml_readmodifiers(result, node); xmlXPathFreeObject(result); /* reading eressea/resources/resource/resourcelimit/function */ xpath->node = node;