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;