Merge pull request #687 from ennorehling/develop

refactor buildings: taxes, names, tests
This commit is contained in:
Enno Rehling 2017-04-29 21:58:32 +02:00 committed by GitHub
commit 3d13cb8968
26 changed files with 351 additions and 315 deletions

View File

@ -1,23 +1,21 @@
<?xml version="1.0"?>
<building name="castle" capacity="1" fort="yes">
<function name="name" value="castle_name_2"/>
<function name="taxes" value="lua_building_taxes"/>
<construction skill="building" minskill="1" maxsize="10">
<building name="castle" capacity="1" fort="yes" taxes="100">
<construction skill="building" minskill="1" maxsize="10" name="site">
<requirement type="stone" quantity="1"/>
</construction>
<construction skill="building" minskill="2" maxsize="40">
<construction skill="building" minskill="2" maxsize="40" name="fortification">
<requirement type="stone" quantity="1"/>
</construction>
<construction skill="building" minskill="3" maxsize="200">
<construction skill="building" minskill="3" maxsize="200" name="tower">
<requirement type="stone" quantity="1"/>
</construction>
<construction skill="building" minskill="4" maxsize="1000">
<construction skill="building" minskill="4" maxsize="1000" name="castle">
<requirement type="stone" quantity="1"/>
</construction>
<construction skill="building" minskill="5" maxsize="5000">
<construction skill="building" minskill="5" maxsize="5000" name="fortress">
<requirement type="stone" quantity="1"/>
</construction>
<construction skill="building" minskill="6">
<construction skill="building" minskill="6" name="citadel">
<requirement type="stone" quantity="1"/>
</construction>
</building>

View File

@ -1,25 +1,24 @@
<?xml version="1.0"?>
<building name="castle" capacity="1" fort="yes">
<function name="name" value="castle_name"/>
<construction skill="building" minskill="1" maxsize="2">
<construction skill="building" minskill="1" maxsize="2" name="site">
<requirement type="stone" quantity="1"/>
</construction>
<construction skill="building" minskill="1" maxsize="8">
<construction skill="building" minskill="1" maxsize="8" name="tradepost">
<requirement type="stone" quantity="1"/>
</construction>
<construction skill="building" minskill="2" maxsize="40">
<construction skill="building" minskill="2" maxsize="40" name="fortification">
<requirement type="stone" quantity="1"/>
</construction>
<construction skill="building" minskill="3" maxsize="200">
<construction skill="building" minskill="3" maxsize="200" name="tower">
<requirement type="stone" quantity="1"/>
</construction>
<construction skill="building" minskill="4" maxsize="1000">
<construction skill="building" minskill="4" maxsize="1000" name="castle">
<requirement type="stone" quantity="1"/>
</construction>
<construction skill="building" minskill="5" maxsize="5000">
<construction skill="building" minskill="5" maxsize="5000" name="fortress">
<requirement type="stone" quantity="1"/>
</construction>
<construction skill="building" minskill="6">
<construction skill="building" minskill="6" name="citadel">
<requirement type="stone" quantity="1"/>
</construction>
</building>

View File

@ -3,16 +3,14 @@
<xi:include href="config://default/buildings/castle-2.xml" />
<building name="watch" maxsize="10" capacity="1" fort="yes">
<function name="name" value="fort_name"/>
<function name="taxes" value="lua_building_taxes"/>
<construction skill="building" minskill="1" maxsize="5">
<building name="watch" maxsize="10" capacity="1" fort="yes" taxes="200">
<construction skill="building" minskill="1" maxsize="5" name="scaffolding">
<requirement type="log" quantity="1"/>
</construction>
<construction skill="building" minskill="2" maxsize="5">
<construction skill="building" minskill="2" maxsize="5" name="guardhouse">
<requirement type="log" quantity="1"/>
</construction>
<construction skill="building" minskill="2">
<construction skill="building" minskill="2" name="guardtower">
<requirement type="log" quantity="1"/>
</construction>
</building>

View File

@ -1,13 +1,3 @@
function building_taxes(b, blevel)
btype = b.type
if btype=="castle" then
return blevel * 0.01
elseif btype=="watch" then
return blevel * 0.005
end
return 0.0
end
-- the "raindance" spell
function raindance(r, mage, level, force)
if (create_curse(mage, r, "blessedharvest", force, 1+force*2, 100 * force)) then

View File

@ -0,0 +1,53 @@
require "lunit"
module("tests.e2.buildings", package.seeall, lunit.testcase )
function setup()
eressea.game.reset()
eressea.settings.set("rules.food.flags", "4")
end
function teardown()
eressea.settings.set("rules.food.flags", "0")
end
function test_castle_names()
local r = region.create(0, 0, "plain")
local b = building.create(r, "castle")
assert_equal("site", b:get_typename(1))
assert_equal("tradepost", b:get_typename(2))
assert_equal("tradepost", b:get_typename(9))
assert_equal("fortification", b:get_typename(10))
assert_equal("fortification", b:get_typename(49))
assert_equal("tower", b:get_typename(50))
assert_equal("tower", b:get_typename(249))
assert_equal("castle", b:get_typename(250))
assert_equal("castle", b:get_typename(1249))
assert_equal("fortress", b:get_typename(1250))
assert_equal("fortress", b:get_typename(6249))
assert_equal("citadel", b:get_typename(6250))
end
function test_build_castle_stages()
local r = region.create(0,0, "plain")
local f = faction.create("human")
local u = unit.create(f, r, 1000)
local b = building.create(r, "castle")
u:add_item("stone", 1000)
u:set_skill("building", 1)
u:clear_orders()
u:add_order("MACHE BURG " .. itoa36(b.id))
process_orders()
assert_equal(10, b.size)
u:set_skill("building", 3)
u:clear_orders()
u:add_order("MACHE BURG " .. itoa36(b.id))
process_orders()
assert_equal(250, b.size)
end

View File

@ -1,3 +1,4 @@
require 'tests.e2.buildings'
require 'tests.e2.production'
require 'tests.e2.adamantium'
require 'tests.e2.undead'

View File

@ -1,6 +1,6 @@
require "lunit"
module("tests.e3.castles", package.seeall, lunit.testcase )
module("tests.e3.buildings", package.seeall, lunit.testcase )
function setup()
eressea.game.reset()
@ -11,6 +11,23 @@ function teardown()
eressea.settings.set("rules.food.flags", "0")
end
function test_castle_names()
local r = region.create(0, 0, "plain")
local b = building.create(r, "castle")
assert_equal("site", b:get_typename(1))
assert_equal("site", b:get_typename(9))
assert_equal("fortification", b:get_typename(10))
assert_equal("fortification", b:get_typename(49))
assert_equal("tower", b:get_typename(50))
assert_equal("tower", b:get_typename(249))
assert_equal("castle", b:get_typename(250))
assert_equal("castle", b:get_typename(1249))
assert_equal("fortress", b:get_typename(1250))
assert_equal("fortress", b:get_typename(6249))
assert_equal("citadel", b:get_typename(6250))
end
function test_build_watch()
local r = region.create(0, 0, "plain")
local f = faction.create("human", "e3build@eressea.de", "de")
@ -33,7 +50,7 @@ function test_build_watch()
assert_equal(10, u.building.size)
end
function test_watch()
function test_watch_names()
local r = region.create(0, 0, "plain")
local b = building.create(r, "watch")

View File

@ -1,15 +1,15 @@
-- require 'tests.e3.castles'
-- require 'tests.e3.stealth'
-- require 'tests.e3.spells'
require 'tests.e3.buildings'
require 'tests.e3.stealth'
require 'tests.e3.spells'
require 'tests.e3.rules'
-- require 'tests.e3.parser'
-- require 'tests.e3.morale'
-- require 'tests.e3.items'
-- require 'tests.economy'
-- require 'tests.orders'
-- require 'tests.common'
-- require 'tests.items'
-- require 'tests.magicbag'
-- require 'tests.process'
-- require 'tests.e3.production'
-- require 'tests.production'
require 'tests.e3.parser'
require 'tests.e3.morale'
require 'tests.e3.items'
require 'tests.economy'
require 'tests.orders'
require 'tests.common'
require 'tests.items'
require 'tests.magicbag'
require 'tests.process'
require 'tests.e3.production'
require 'tests.production'

View File

@ -575,7 +575,7 @@ void give_control(unit * u, unit * u2)
assert(u->building == u2->building);
if (f == u->faction) {
building *b = largestbuilding(r, &cmp_current_owner, false);
building *b = largestbuilding(r, cmp_current_owner, false);
if (b == u->building) {
friendly_takeover(r, u2->faction);
}
@ -888,7 +888,7 @@ static void manufacture(unit * u, const item_type * itype, int want)
case EBUILDINGREQ:
ADDMSG(&u->faction->msgs,
msg_feedback(u, u->thisorder, "building_needed", "building",
itype->construction->btype->_name));
itype->construction->extra.btype->_name));
return;
case ELOWSKILL:
ADDMSG(&u->faction->msgs,
@ -1239,7 +1239,7 @@ static void create_potion(unit * u, const potion_type * ptype, int want)
case EBUILDINGREQ:
ADDMSG(&u->faction->msgs,
msg_feedback(u, u->thisorder, "building_needed", "building",
ptype->itype->construction->btype->_name));
ptype->itype->construction->extra.btype->_name));
break;
case ECOMPLETE:
assert(0);
@ -2929,7 +2929,7 @@ static void peasant_taxes(region * r)
unit *u;
building *b;
int money;
int maxsize;
int level;
f = region_get_owner(r);
if (f == NULL || is_mourning(r, turn)) {
@ -2947,10 +2947,10 @@ static void peasant_taxes(region * r)
if (u == NULL || u->faction != f)
return;
maxsize = buildingeffsize(b, false);
if (maxsize > 0) {
double taxfactor = money * b->type->taxes(b, maxsize);
double morale = MORALE_TAX_FACTOR * money * region_get_morale(r);
level = buildingeffsize(b, false);
if (level > 0) {
double taxfactor = money * level / building_taxes(b);
double morale = money * region_get_morale(r) / MORALE_TAX_FACTOR;
if (taxfactor > morale) {
taxfactor = morale;
}

View File

@ -70,7 +70,6 @@ void game_init(void)
register_spells();
register_names();
register_resources();
register_buildings();
register_itemfunctions();
#if MUSEUM_MODULE
register_museum();

View File

@ -112,7 +112,7 @@ static bool is_guardian_r(const unit * guard)
if (guard->building && rule_region_owners() && guard == building_owner(guard->building)) {
faction *owner = region_get_owner(guard->region);
if (owner == guard->faction) {
building *bowner = largestbuilding(guard->region, &cmp_taxes, false);
building *bowner = largestbuilding(guard->region, cmp_taxes, false);
if (bowner == guard->building) {
return true;
}

View File

@ -352,58 +352,6 @@ lua_wage(const region * r, const faction * f, const race * rc, int in_turn)
return result;
}
static void lua_agebuilding(building * b)
{
lua_State *L = (lua_State *)global.vm_state;
char fname[64];
strlcpy(fname, "age_", sizeof(fname));
strlcat(fname, b->type->_name, sizeof(fname));
lua_getglobal(L, fname);
if (lua_isfunction(L, -1)) {
tolua_pushusertype(L, (void *)b, TOLUA_CAST "building");
if (lua_pcall(L, 1, 0, 0) != 0) {
const char *error = lua_tostring(L, -1);
log_error("agebuilding(%s) calling '%s': %s.\n", buildingname(b), fname, error);
lua_pop(L, 1);
}
}
else {
log_error("agebuilding(%s) calling '%s': not a function.\n", buildingname(b), fname);
lua_pop(L, 1);
}
}
static double lua_building_taxes(building * b, int level)
{
lua_State *L = (lua_State *)global.vm_state;
const char *fname = "building_taxes";
double result = 0.0F;
lua_getglobal(L, fname);
if (lua_isfunction(L, -1)) {
tolua_pushusertype(L, (void *)b, TOLUA_CAST "building");
lua_pushinteger(L, level);
if (lua_pcall(L, 2, 1, 0) != 0) {
const char *error = lua_tostring(L, -1);
log_error("building_taxes(%s) calling '%s': %s.\n", buildingname(b), fname, error);
lua_pop(L, 1);
}
else {
result = (double)lua_tonumber(L, -1);
lua_pop(L, 1);
}
}
else {
log_error("building_taxes(%s) calling '%s': not a function.\n", buildingname(b), fname);
lua_pop(L, 1);
}
return result;
}
static int lua_maintenance(const unit * u)
{
lua_State *L = (lua_State *)global.vm_state;
@ -526,10 +474,6 @@ void register_tolua_helpers(void)
at_register(&at_direction);
at_register(&at_building_action);
register_function((pf_generic)lua_building_taxes,
TOLUA_CAST "lua_building_taxes");
register_function((pf_generic)lua_agebuilding,
TOLUA_CAST "lua_agebuilding");
register_function((pf_generic)lua_callspell, TOLUA_CAST "lua_castspell");
register_function((pf_generic)lua_initfamiliar,
TOLUA_CAST "lua_initfamiliar");

View File

@ -502,20 +502,20 @@ static int count_materials(unit *u, const construction *type, int n, int complet
*/
int build(unit * u, const construction * ctype, int completed, int want, int skill_mod)
{
const construction *type = ctype;
const construction *con = ctype;
int skills = INT_MAX; /* number of skill points remainig */
int basesk = 0;
int made = 0;
if (want <= 0)
return 0;
if (type == NULL)
if (con == NULL)
return ENOMATERIALS;
if (type->improvement == NULL && completed == type->maxsize)
if (con->improvement == NULL && completed == con->maxsize)
return ECOMPLETE;
if (type->btype != NULL) {
if (con->type==CONS_ITEM && con->extra.btype) {
building *b;
if (!u->building || u->building->type != type->btype) {
if (!u->building || u->building->type != con->extra.btype) {
return EBUILDINGREQ;
}
b = inside_building(u);
@ -524,12 +524,12 @@ int build(unit * u, const construction * ctype, int completed, int want, int ski
}
}
if (type->skill != NOSKILL) {
if (con->skill != NOSKILL) {
int effsk;
int dm = get_effect(u, oldpotiontype[P_DOMORE]);
assert(u->number);
basesk = effskill(u, type->skill, 0);
basesk = effskill(u, con->skill, 0);
if (basesk == 0)
return ENEEDSKILL;
@ -557,13 +557,13 @@ int build(unit * u, const construction * ctype, int completed, int want, int ski
* type->improvement==type means build another object of the same time
* while material lasts type->improvement==x means build x when type
* is finished */
while (type && type->improvement &&
type->improvement != type &&
type->maxsize > 0 && type->maxsize <= completed) {
completed -= type->maxsize;
type = type->improvement;
while (con && con->improvement &&
con->improvement != con &&
con->maxsize > 0 && con->maxsize <= completed) {
completed -= con->maxsize;
con = con->improvement;
}
if (type == NULL) {
if (con == NULL) {
if (made == 0)
return ECOMPLETE;
break; /* completed */
@ -574,15 +574,15 @@ int build(unit * u, const construction * ctype, int completed, int want, int ski
* (enno): Nein, das ist f<EFBFBD>r Dinge, bei denen die n<EFBFBD>chste Ausbaustufe
* die gleiche wie die vorherige ist. z.b. gegenst<EFBFBD>nde.
*/
if (type->maxsize > 0) {
completed = completed % type->maxsize;
if (con->maxsize > 0) {
completed = completed % con->maxsize;
}
else {
completed = 0;
assert(type->reqsize >= 1);
assert(con->reqsize >= 1);
}
if (basesk < type->minskill) {
if (basesk < con->minskill) {
if (made == 0)
return ELOWSKILL;
else
@ -590,15 +590,15 @@ int build(unit * u, const construction * ctype, int completed, int want, int ski
}
/* n = maximum buildable size */
if (type->minskill > 1) {
n = skills / type->minskill;
if (con->minskill > 1) {
n = skills / con->minskill;
}
else {
n = skills;
}
/* Flinkfingerring wirkt nicht auf Mengenbegrenzte (magische)
* Talente */
if (skill_limit(u->faction, type->skill) == INT_MAX) {
if (skill_limit(u->faction, con->skill) == INT_MAX) {
const resource_type *ring = get_resourcetype(R_RING_OF_NIMBLEFINGER);
item *itm = ring ? *i_find(&u->items, ring->itype) : 0;
int i = itm ? itm->number : 0;
@ -610,26 +610,26 @@ int build(unit * u, const construction * ctype, int completed, int want, int ski
if (want < n) n = want;
if (type->maxsize > 0) {
n = MIN(type->maxsize - completed, n);
if (type->improvement == NULL) {
if (con->maxsize > 0) {
n = MIN(con->maxsize - completed, n);
if (con->improvement == NULL) {
want = n;
}
}
n = count_materials(u, type, n, completed);
n = count_materials(u, con, n, completed);
if (n <= 0) {
if (made == 0)
return ENOMATERIALS;
else
break;
}
err = use_materials(u, type, n, completed);
err = use_materials(u, con, n, completed);
if (err < 0) {
return err;
}
made += n;
skills -= n * type->minskill;
skills -= n * con->minskill;
want -= n;
completed = completed + n;
}

View File

@ -37,15 +37,27 @@ extern "C" {
int number;
} requirement;
typedef enum construct_t {
CONS_OTHER,
CONS_ITEM,
CONS_BUILDING
} construct_t;
typedef struct construction {
construct_t type;
skill_t skill; /* skill req'd per point of size */
int minskill; /* skill req'd per point of size */
int maxsize; /* maximum size of this type */
int reqsize; /* size of object using up 1 set of requirement. */
requirement *materials; /* material req'd to build one object */
const struct building_type *btype;
/* building type required to make this thing */
union {
/* CONS_BUILDING: */
char * name; /* building level name */
/* CONS_ITEM: */
const struct building_type *btype; /* building required to build item */
} extra;
struct construction *improvement;
/* next level, if upgradable. if more than one of these items

View File

@ -81,7 +81,8 @@ static void test_build_requires_building(CuTest *tc) {
rtype = bf.cons.materials[0].rtype;
i_change(&u->items, rtype->itype, 1);
set_level(u, SK_ARMORER, 2);
bf.cons.btype = btype = bt_get_or_create("hodor");
bf.cons.type = CONS_ITEM;
bf.cons.extra.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, 0));

View File

@ -157,27 +157,52 @@ attrib_type at_building_generic_type = {
ATF_UNIQUE
};
/* TECH DEBT: simplest thing that works for E3 dwarf/halfling faction rules */
static int adjust_size(const building *b, int bsize) {
assert(b);
if (config_get_int("rules.dwarf_castles", 0)
&& strcmp(b->type->_name, "castle") == 0) {
unit *u = building_owner(b);
if (u && u->faction->race == get_race(RC_HALFLING)) {
return bsize * 5 / 4;
}
}
return bsize;
}
/* Returns the (internal) name for a building of given size and type. Especially, returns the correct
* name if it depends on the size (as for Eressea castles).
*/
const char *buildingtype(const building_type * btype, const building * b, int bsize)
{
const char *s;
const construction *con;
assert(btype);
s = btype->_name;
if (btype->name) {
s = btype->name(btype, b, bsize);
}
if (b && b->attribs) {
if (is_building_type(btype, "generic")) {
const attrib *a = a_find(b->attribs, &at_building_generic_type);
if (a) {
s = (const char *)a->data.v;
return (const char *)a->data.v;
}
}
}
return s;
if (btype->name) {
return btype->name(btype, b, bsize);
}
if (btype->construction->extra.name) {
if (b) {
assert(b->type == btype);
bsize = adjust_size(b, bsize);
}
for (con = btype->construction; con; con = con->improvement) {
bsize -= con->maxsize;
if (!con->improvement || bsize <0) {
return con->extra.name;
}
}
}
return btype->_name;
}
#define BMAXHASH 7919
@ -219,55 +244,6 @@ building *findbuilding(int i)
{
return bfindhash(i);
}
static const char *castle_name_i(const struct building_type *btype,
const struct building *b, int bsize, const char *fname[])
{
int i = bt_effsize(btype, b, bsize);
return fname[i];
}
static const char *castle_name_2(const struct building_type *btype,
const struct building *b, int bsize)
{
const char *fname[] = {
"site",
"fortification",
"tower",
"castle",
"fortress",
"citadel"
};
return castle_name_i(btype, b, bsize, fname);
}
static const char *castle_name(const struct building_type *btype,
const struct building *b, int bsize)
{
const char *fname[] = {
"site",
"tradepost",
"fortification",
"tower",
"castle",
"fortress",
"citadel"
};
return castle_name_i(btype, b, bsize, fname);
}
static const char *fort_name(const struct building_type *btype,
const struct building *b, int bsize)
{
const char *fname[] = {
"scaffolding",
"guardhouse",
"guardtower",
};
return castle_name_i(btype, b, bsize, fname);
}
/* for finding out what was meant by a particular building string */
static local_names *bnames;
@ -486,24 +462,19 @@ int buildingeffsize(const building * b, int img)
int bt_effsize(const building_type * btype, const building * b, int bsize)
{
int i = bsize, n = 0;
int n = 0;
const construction *cons = btype->construction;
/* TECH DEBT: simplest thing that works for E3 dwarf/halfling faction rules */
if (b && config_get_int("rules.dwarf_castles", 0)
&& strcmp(btype->_name, "castle") == 0) {
unit *u = building_owner(b);
if (u && u->faction->race == get_race(RC_HALFLING)) {
i = bsize * 10 / 8;
}
if (b) {
bsize = adjust_size(b, bsize);
}
if (!cons || !cons->improvement) {
if (!cons) {
return 0;
}
while (cons && cons->maxsize != -1 && i >= cons->maxsize) {
i -= cons->maxsize;
while (cons && cons->maxsize != -1 && bsize >= cons->maxsize) {
bsize -= cons->maxsize;
cons = cons->improvement;
++n;
}
@ -552,10 +523,10 @@ static unit *building_owner_ex(const building * bld, const struct faction * last
}
if (!heir && config_token("rules.region_owner_pay_building", bld->type->_name)) {
if (rule_region_owners()) {
u = building_owner(largestbuilding(bld->region, &cmp_taxes, false));
u = building_owner(largestbuilding(bld->region, cmp_taxes, false));
}
else {
u = building_owner(largestbuilding(bld->region, &cmp_wage, false));
u = building_owner(largestbuilding(bld->region, cmp_wage, false));
}
if (u) {
heir = u;
@ -674,7 +645,7 @@ building *largestbuilding(const region * r, cmp_building_cb cmp_gt,
{
building *b, *best = NULL;
for (b = rbuildings(r); b; b = b->next) {
for (b = r->buildings; b; b = b->next) {
if (cmp_gt(b, best) <= 0)
continue;
if (!imaginary) {
@ -702,7 +673,7 @@ static const int wagetable[7][4] = {
static int
default_wage(const region * r, const faction * f, const race * rc, int in_turn)
{
building *b = largestbuilding(r, &cmp_wage, false);
building *b = largestbuilding(r, cmp_wage, false);
int esize = 0;
double wage;
static int ct_cache;
@ -813,6 +784,12 @@ bool is_owner_building(const struct building * b)
return false;
}
int building_taxes(const building *b) {
assert(b);
return b->type->taxes;
}
int cmp_taxes(const building * b, const building * a)
{
faction *f = region_get_owner(b->region);
@ -822,14 +799,12 @@ int cmp_taxes(const building * b, const building * a)
return -1;
}
else if (a) {
int newsize = buildingeffsize(b, false);
double newtaxes = b->type->taxes(b, newsize);
int oldsize = buildingeffsize(a, false);
double oldtaxes = a->type->taxes(a, oldsize);
int newtaxes = building_taxes(b);
int oldtaxes = building_taxes(a);
if (newtaxes < oldtaxes)
if (newtaxes > oldtaxes)
return -1;
else if (newtaxes > oldtaxes)
else if (newtaxes < oldtaxes)
return 1;
else if (b->size < a->size)
return -1;
@ -838,8 +813,9 @@ int cmp_taxes(const building * b, const building * a)
else {
if (u && u->faction == f) {
u = building_owner(a);
if (u && u->faction == f)
return -1;
if (u && u->faction == f) {
return 0;
}
return 1;
}
}
@ -848,7 +824,7 @@ int cmp_taxes(const building * b, const building * a)
return 1;
}
}
return -1;
return 0;
}
int cmp_current_owner(const building * b, const building * a)
@ -861,10 +837,8 @@ int cmp_current_owner(const building * b, const building * a)
if (!u || u->faction != f)
return -1;
if (a) {
int newsize = buildingeffsize(b, false);
double newtaxes = b->type->taxes(b, newsize);
int oldsize = buildingeffsize(a, false);
double oldtaxes = a->type->taxes(a, oldsize);
int newtaxes = building_taxes(b);
int oldtaxes = building_taxes(a);
if (newtaxes > oldtaxes) {
return 1;
@ -872,21 +846,11 @@ int cmp_current_owner(const building * b, const building * a)
if (newtaxes < oldtaxes) {
return -1;
}
if (newsize != oldsize) {
return newsize - oldsize;
}
return (b->size - a->size);
}
else {
return 1;
}
}
return -1;
}
void register_buildings(void)
{
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");
return 0;
}

View File

@ -63,6 +63,7 @@ extern "C" {
variant magres; /* how well it resists against spells */
int magresbonus; /* bonus it gives the target against spells */
int fumblebonus; /* bonus that reduces fumbling */
int taxes; /* receive $1 tax per `taxes` in region */
double auraregen; /* modifier for aura regeneration inside building */
struct maintenance *maintenance; /* array of requirements */
struct construction *construction; /* construction of 1 building-level */
@ -70,8 +71,6 @@ extern "C" {
const char *(*name) (const struct building_type *,
const struct building * b, int size);
void(*age) (struct building *);
double(*taxes) (const struct building *, int size);
struct attrib *attribs;
} building_type;
@ -84,7 +83,6 @@ extern "C" {
bool bt_changed(int *cache);
const building_type *bt_find(const char *name);
void free_buildingtypes(void);
void register_buildings(void);
void bt_register(struct building_type *type);
int bt_effsize(const struct building_type *btype,
const struct building *b, int bsize);
@ -140,6 +138,7 @@ extern "C" {
int cmp_taxes(const struct building *b, const struct building *bother);
int cmp_current_owner(const struct building *b,
const struct building *bother);
int building_taxes(const building *b);
/* old functions, still in build.c: */
int buildingeffsize(const building * b, int imaginary);

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->age);
CuAssertTrue(tc, !btype->taxes);
CuAssertDblEquals(tc, 1.0, btype->auraregen, 0.0);
CuAssertIntEquals(tc, -1, btype->maxsize);
@ -427,10 +426,87 @@ static void test_cmp_castle_size(CuTest *tc) {
u_set_building(u2, b2);
b1->size = 5;
b2->size = 10;
CuAssertTrue(tc, cmp_castle_size(b1, b2)<0);
CuAssertTrue(tc, cmp_castle_size(b2, b1)>0);
CuAssertTrue(tc, cmp_castle_size(b1, NULL)>0);
CuAssertTrue(tc, cmp_castle_size(NULL, b1)<0);
CuAssertTrue(tc, cmp_castle_size(b1, b2) < 0);
CuAssertTrue(tc, cmp_castle_size(b2, b1) > 0);
CuAssertTrue(tc, cmp_castle_size(b1, b1) == 0);
test_cleanup();
}
static void test_cmp_wage(CuTest *tc) {
region *r;
building *b1, *b2;
building_type *btype;
test_setup();
btype = test_create_buildingtype("castle");
btype->taxes = 100;
r = test_create_region(0, 0, 0);
b1 = test_create_building(r, btype);
b2 = test_create_building(r, btype);
b1->size = 5;
b2->size = 10;
CuAssertPtrEquals(tc, b2, largestbuilding(r, cmp_wage, false));
CuAssertTrue(tc, cmp_wage(b1, b2) < 0);
CuAssertTrue(tc, cmp_wage(b2, b1) > 0);
CuAssertTrue(tc, cmp_wage(b1, b1) == 0);
test_cleanup();
}
static void test_cmp_taxes(CuTest *tc) {
region *r;
building *b1, *b2;
building_type *btype;
unit *u1, *u2;
test_setup();
btype = test_create_buildingtype("castle");
btype->taxes = 100;
r = test_create_region(0, 0, 0);
b1 = test_create_building(r, btype);
b2 = test_create_building(r, btype);
b1->size = 5;
b2->size = 10;
u1 = test_create_unit(test_create_faction(0), r);
u_set_building(u1, b1);
u2 = test_create_unit(test_create_faction(0), r);
u_set_building(u2, b2);
CuAssertPtrEquals(tc, b2, largestbuilding(r, cmp_taxes, false));
CuAssertTrue(tc, cmp_taxes(b1, b2) < 0);
CuAssertTrue(tc, cmp_taxes(b2, b1) > 0);
CuAssertTrue(tc, cmp_taxes(b1, b1) == 0);
test_cleanup();
}
static void test_cmp_current_owner(CuTest *tc) {
region *r;
building *b1, *b2;
building_type *btype;
unit *u1, *u2;
test_setup();
config_set("rules.region_owners", "1");
r = test_create_region(0, 0, 0);
btype = test_create_buildingtype("watch");
btype->construction->maxsize = 1;
btype->taxes = 200;
b1 = test_create_building(r, btype);
btype = test_create_buildingtype("castle");
btype->construction->maxsize = 1;
btype->taxes = 100;
b2 = test_create_building(r, btype);
b1->size = 1;
CuAssertIntEquals(tc, 1, buildingeffsize(b1, false));
b2->size = 1;
CuAssertIntEquals(tc, 1, buildingeffsize(b2, false));
u1 = test_create_unit(test_create_faction(0), r);
u_set_building(u1, b1);
u2 = test_create_unit(test_create_faction(0), r);
u_set_building(u2, b2);
region_set_owner(r, u1->faction, turn);
CuAssertPtrEquals(tc, b1, largestbuilding(r, cmp_current_owner, false));
CuAssertTrue(tc, cmp_current_owner(b2, b1) < 0);
CuAssertTrue(tc, cmp_current_owner(b1, b2) > 0);
CuAssertTrue(tc, cmp_current_owner(b1, b1) == 0);
test_cleanup();
}
@ -459,10 +535,37 @@ static void test_building_effsize(CuTest *tc) {
test_cleanup();
}
static int cmp_size(const building *lhs, const building *rhs) {
assert(lhs);
return rhs ? lhs->size - rhs->size : 1;
}
static void test_largestbuilding(CuTest *tc) {
region *r;
building *b1, *b2;
test_setup();
r = test_create_region(0, 0, NULL);
CuAssertPtrEquals(tc, NULL, largestbuilding(r, cmp_size, false));
b1 = test_create_building(r, NULL);
b2 = test_create_building(r, NULL);
b1->size = 1;
b2->size = 1;
CuAssertPtrEquals(tc, b1, largestbuilding(r, cmp_size, false));
b1->size = 2;
CuAssertPtrEquals(tc, b1, largestbuilding(r, cmp_size, false));
b2->size = 3;
CuAssertPtrEquals(tc, b2, largestbuilding(r, cmp_size, false));
test_cleanup();
}
CuSuite *get_building_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_largestbuilding);
SUITE_ADD_TEST(suite, test_cmp_castle_size);
SUITE_ADD_TEST(suite, test_cmp_taxes);
SUITE_ADD_TEST(suite, test_cmp_wage);
SUITE_ADD_TEST(suite, test_cmp_current_owner);
SUITE_ADD_TEST(suite, test_register_building);
SUITE_ADD_TEST(suite, test_btype_defaults);
SUITE_ADD_TEST(suite, test_building_set_owner);

View File

@ -1398,8 +1398,8 @@ faction *update_owners(region * r)
faction *f = NULL;
assert(rule_region_owners());
if (r->land) {
building *bowner = largestbuilding(r, &cmp_current_owner, false);
building *blargest = largestbuilding(r, &cmp_taxes, false);
building *bowner = largestbuilding(r, cmp_current_owner, false);
building *blargest = largestbuilding(r, cmp_taxes, false);
if (blargest) {
if (!bowner || bowner->size < blargest->size) {
/* region owners update? */

View File

@ -66,7 +66,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
struct faction;
struct gamedata;
#define MORALE_TAX_FACTOR 0.005 /* 0.5% tax per point of morale */
#define MORALE_TAX_FACTOR 200 /* 0.5% tax per point of morale, 1 silver per 200 */
#define MORALE_MAX 10 /* Maximum morale allowed */
#define MORALE_DEFAULT 1 /* Morale of peasants when they are conquered for the first time */
#define MORALE_TAKEOVER 0 /* Morale of peasants after they lose their lord */

View File

@ -214,7 +214,7 @@ xml_readrequirements(xmlNodePtr * nodeTab, int nodeNr, requirement ** reqArray)
void
xml_readconstruction(xmlXPathContextPtr xpath, xmlNodeSetPtr nodeSet,
construction ** consPtr)
construction ** consPtr, construct_t type)
{
xmlNodePtr pushNode = xpath->node;
int k;
@ -241,15 +241,25 @@ construction ** consPtr)
*consPtr = con = (construction *)calloc(sizeof(construction), 1);
consPtr = &con->improvement;
con->type = type;
con->skill = sk;
con->maxsize = xml_ivalue(node, "maxsize", -1);
con->minskill = xml_ivalue(node, "minskill", -1);
con->reqsize = xml_ivalue(node, "reqsize", 1);
propValue = xmlGetProp(node, BAD_CAST "building");
if (propValue != NULL) {
con->btype = bt_get_or_create((const char *)propValue);
xmlFree(propValue);
if (type == CONS_ITEM) {
propValue = xmlGetProp(node, BAD_CAST "building");
if (propValue != NULL) {
con->extra.btype = bt_get_or_create((const char *)propValue);
xmlFree(propValue);
}
}
else if (type == CONS_BUILDING) {
propValue = xmlGetProp(node, BAD_CAST "name");
if (propValue != NULL) {
con->extra.name = strdup((const char *)propValue);
xmlFree(propValue);
}
}
/* read construction/requirement */
@ -310,6 +320,7 @@ static int parse_buildings(xmlDocPtr doc)
btype->magresbonus = xml_ivalue(node, "magresbonus", btype->magresbonus);
btype->fumblebonus = xml_ivalue(node, "fumblebonus", btype->fumblebonus);
btype->auraregen = xml_fvalue(node, "auraregen", btype->auraregen);
btype->taxes = xml_ivalue(node, "taxes", btype->taxes);
if (xml_bvalue(node, "nodestroy", false))
btype->flags |= BTF_INDESTRUCTIBLE;
@ -337,7 +348,7 @@ static int parse_buildings(xmlDocPtr doc)
/* reading eressea/buildings/building/construction */
xpath->node = node;
result = xmlXPathEvalExpression(BAD_CAST "construction", xpath);
xml_readconstruction(xpath, result->nodesetval, &btype->construction);
xml_readconstruction(xpath, result->nodesetval, &btype->construction, CONS_BUILDING);
xmlXPathFreeObject(result);
/* reading eressea/buildings/building/function */
@ -359,12 +370,6 @@ static int parse_buildings(xmlDocPtr doc)
(const char *(*)(const struct building_type *,
const struct building *, int))fun;
}
else if (strcmp((const char *)propValue, "age") == 0) {
btype->age = (void(*)(struct building *))fun;
}
else if (strcmp((const char *)propValue, "taxes") == 0) {
btype->taxes = (double(*)(const struct building *, int))fun;
}
else {
log_error("unknown function type '%s' for building %s\n", (const char *)propValue, btype->_name);
}
@ -572,7 +577,7 @@ static int parse_ships(xmlDocPtr doc)
/* reading eressea/ships/ship/construction */
xpath->node = node;
result = xmlXPathEvalExpression(BAD_CAST "construction", xpath);
xml_readconstruction(xpath, result->nodesetval, &st->construction);
xml_readconstruction(xpath, result->nodesetval, &st->construction, CONS_OTHER);
xmlXPathFreeObject(result);
for (child = node->children; child; child = child->next) {
@ -858,7 +863,7 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype)
/* reading item/construction */
xpath->node = node;
result = xmlXPathEvalExpression(BAD_CAST "construction", xpath);
xml_readconstruction(xpath, result->nodesetval, &itype->construction);
xml_readconstruction(xpath, result->nodesetval, &itype->construction, CONS_ITEM);
xmlXPathFreeObject(result);
/* reading item/weapon */

View File

@ -2883,10 +2883,6 @@ static building *age_building(building * b)
a_age(&b->attribs, b);
handle_event(b->attribs, "timer", b);
if (b->type->age) {
b->type->age(b);
}
return b;
}

View File

@ -525,10 +525,6 @@ struct pay_fixture {
unit *u2;
};
static double level_taxes(const building * b, int level) {
return b->size * level * 2.0;
}
static void setup_pay_cmd(struct pay_fixture *fix) {
faction *f;
region *r;
@ -540,7 +536,7 @@ static void setup_pay_cmd(struct pay_fixture *fix) {
r = findregion(0, 0);
assert(r && f);
btcastle = test_create_buildingtype("castle");
btcastle->taxes = level_taxes;
btcastle->taxes = 100;
b = test_create_building(r, btcastle);
assert(b);
fix->u1 = test_create_unit(f, r);

View File

@ -231,26 +231,6 @@ bool FactionSpells(void)
return rule != 0;
}
void read_spells(struct selist **slistp, magic_t mtype,
struct storage *store)
{
for (;;) {
spell *sp;
char spname[64];
READ_TOK(store, spname, sizeof(spname));
if (strcmp(spname, "end") == 0)
break;
sp = find_spell(spname);
if (!sp) {
log_error("read_spells: could not find spell '%s' in school '%s'\n", spname, magic_school[mtype]);
}
if (sp) {
add_spell(slistp, sp);
}
}
}
int get_spell_level_mage(const spell * sp, void * cbdata)
{
sc_mage *mage = (sc_mage *)cbdata;
@ -305,18 +285,6 @@ static int read_mage(attrib * a, void *owner, struct gamedata *data)
return AT_READ_OK;
}
void write_spells(struct selist *slist, struct storage *store)
{
selist *ql;
int qi;
for (ql = slist, qi = 0; ql; selist_advance(&ql, &qi, 1)) {
spell *sp = (spell *)selist_get(ql, qi);
WRITE_TOK(store, sp->sname);
}
WRITE_TOK(store, "end");
}
static void
write_mage(const attrib * a, const void *owner, struct storage *store)
{

View File

@ -244,10 +244,6 @@ extern "C" {
/* prüft, ob der Spruch in der Spruchliste der Einheit steht. */
void pick_random_spells(struct faction *f, int level, struct spellbook * book, int num_spells);
void show_new_spells(struct faction * f, int level, const struct spellbook *book);
void updatespelllist(struct unit *u);
/* fügt alle Zauber des Magiegebietes der Einheit, deren Stufe kleiner
* als das aktuelle Magietalent ist, in die Spruchliste der Einheit
* ein */
bool knowsspell(const struct region *r, const struct unit *u,
const struct spell * sp);
/* prüft, ob die Einheit diesen Spruch gerade beherrscht, dh
@ -349,10 +345,6 @@ extern "C" {
struct order *ord, const struct spllprm *spobj);
bool FactionSpells(void);
void write_spells(struct selist *slist, struct storage *store);
void read_spells(struct selist **slistp, magic_t mtype,
struct storage *store);
struct spellbook * get_spellbook(const char * name);
void free_spellbooks(void);
void free_spellbook(struct spellbook *sb);

View File

@ -43,10 +43,11 @@ void morale_update(region *r) {
if (r->land->ownership && r->land->ownership->owner) {
int stability = turn - r->land->ownership->morale_turn;
int maxmorale = MORALE_DEFAULT;
building *b = largestbuilding(r, &cmp_taxes, false);
building *b = largestbuilding(r, cmp_taxes, false);
if (b) {
int bsize = buildingeffsize(b, false);
maxmorale = (int)(0.5 + b->type->taxes(b, bsize + 1) / MORALE_TAX_FACTOR);
assert(b->type->taxes>0);
maxmorale = (bsize + 1) * MORALE_TAX_FACTOR / b->type->taxes;
}
if (morale < maxmorale) {
if (stability > MORALE_COOLDOWN && r->land->ownership->owner