WIP: reimplemented production modifiers.

The system itself seems to work, but some Lua tests are still failing.
This commit is contained in:
Enno Rehling 2017-04-02 14:43:53 +02:00
parent 4aa26343f6
commit e72155a563
24 changed files with 385 additions and 312 deletions

View File

@ -93,7 +93,6 @@
</building>
<building name="smithy" capacity="1">
<function name="init" value="init_smithy"/>
<maintenance type="money" amount="300"/>
<maintenance type="log" amount="1"/>
<construction skill="building" minskill="3">
@ -102,6 +101,8 @@
<requirement type="iron" quantity="2"/>
<requirement type="money" quantity="200"/>
</construction>
<modifier type="skill" skill="weaponsmithing" value="1"/>
<modifier type="skill" skill="armorer" value="1"/>
</building>
<building name="magictower" maxcapacity="2" maxsize="50" magic="yes" magres="40" fumblebonus="10" auraregen="1.75">

View File

@ -5539,6 +5539,15 @@
<text locale="de">"$unit($unit) in $region($region): '$order($command)' - Diesen Gegenstand kann die Einheit nicht herstellen."</text>
<text locale="en">"$unit($unit) in $region($region): '$order($command)' - This unit cannot produce that."</text>
</message>
<message name="error117" section="errors">
<type>
<arg name="unit" type="unit"/>
<arg name="region" type="region"/>
<arg name="command" type="order"/>
</type>
<text locale="de">"$unit($unit) in $region($region): '$order($command)' - Diese Rasse kann das nicht herstellen."</text>
<text locale="en">"$unit($unit) in $region($region): '$order($command)' - This race cannot produce that."</text>
</message>
<message name="error116" section="errors">
<type>
<arg name="unit" type="unit"/>

View File

@ -3,7 +3,8 @@
<item weight="500" score="10">
<construction skill="mining" minskill="1"/>
</item>
<modifier building="smithy" type="save" value="2"/>
<modifier building="mine" type="skill" value="1"/>
<modifier building="mine" type="material" value="0.5"/>
<modifier race="dwarf" type="material" value="0.60"/>
<modifier race="dwarf" type="material" value="0.6"/>
</resource>

View File

@ -1,8 +1,8 @@
<?xml version="1.0"?>
<resource name="greatbow">
<modifier type="require" race="elf"/>
<item weight="100">
<construction skill="weaponsmithing" minskill="5">
<modifier function="mod_elves_only"/>
<requirement type="mallorn" quantity="2"/>
</construction>
<weapon pierce="true" missile="true" skill="bow" offmod="0" defmod="0" reload="0" magres="0.0">

View File

@ -1,8 +1,8 @@
<?xml version="1.0"?>
<resource name="scale">
<modifier type="require" race="dwarf"/>
<item weight="300" score="150" allow="dwarf halfling">
<construction skill="armorer" minskill="5">
<modifier function="mod_dwarves_only"/>
<requirement type="iron" quantity="2"/>
</construction>
<armor ac="3" penalty="0.10" projectile="0.05" magres="0.0"/>

View File

@ -1,8 +1,8 @@
<?xml version="1.0"?>
<resource name="towershield">
<modifier type="require" race="dwarf"/>
<item weight="200" score="60" allow="dwarf">
<construction skill="armorer" minskill="4">
<modifier function="mod_dwarves_only"/>
<requirement type="iron" quantity="1"/>
</construction>
<armor ac="0" penalty="-0.15" projectile="0.25" magres="0.0" shield="yes">

View File

@ -3,6 +3,7 @@
<item weight="500" score="10">
<construction skill="mining" minskill="1"/>
</item>
<modifier building="smithy" type="save" value="2"/>
<modifier building="mine" type="skill" value="1"/>
<modifier building="mine" type="material" value="0.5"/>
</resource>

View File

@ -4,9 +4,9 @@
* has lower damage
-->
<resource name="greatbow">
<modifier type="require" race="elf"/>
<item weight="100" allow="elf">
<construction skill="weaponsmithing" minskill="5">
<modifier function="mod_elves_only"/>
<requirement type="mallorn" quantity="2"/>
</construction>
<weapon pierce="true" missile="true" skill="bow" offmod="0" defmod="0" reload="0" magres="0.0">

View File

@ -1,8 +1,8 @@
<?xml version="1.0"?>
<resource name="rep_crossbow">
<modifier type="require" race="dwarf"/>
<item weight="100" allow="dwarf halfling">
<construction skill="weaponsmithing" minskill="5">
<modifier function="mod_dwarves_only"/>
<requirement type="log" quantity="1"/>
<requirement type="iron" quantity="1"/>
</construction>

View File

@ -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')

View File

@ -12,7 +12,6 @@ follow.c
hate.c
iceberg.c
key.c
matmod.c
moved.c
movement.c
dict.c

View File

@ -1,41 +0,0 @@
/*
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
Katja Zedel <katze@felidae.kn-bremen.de
Christian Schlittchen <corwin@amber.kn-bremen.de>
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 <platform.h>
#include "matmod.h"
#include <util/attrib.h>
#include <stddef.h>
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;
}

View File

@ -1,37 +0,0 @@
/*
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
Katja Zedel <katze@felidae.kn-bremen.de
Christian Schlittchen <corwin@amber.kn-bremen.de>
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

View File

@ -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<61>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:

View File

@ -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);

View File

@ -43,6 +43,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <kernel/pool.h>
#include <kernel/race.h>
#include <kernel/region.h>
#include <kernel/resources.h>
#include <kernel/ship.h>
#include <kernel/terrain.h>
#include <kernel/terrainid.h>
@ -67,9 +68,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <stdlib.h>
#include <string.h>
/* attributes inclues */
#include <attributes/matmod.h>
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) {

View File

@ -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);

View File

@ -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);
}

View File

@ -55,7 +55,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/* attributes includes */
#include <attributes/reduceproduction.h>
#include <attributes/matmod.h>
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");

View File

@ -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;

View File

@ -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);

View File

@ -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 */

View File

@ -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;

View File

@ -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;