merge from develop. luxuty price conflict resolved.

This commit is contained in:
Enno Rehling 2018-05-01 17:22:36 +02:00
commit 3db9d5d878
23 changed files with 545 additions and 349 deletions

View file

@ -284,8 +284,6 @@ function test_block_movement()
end end
end end
function test_block_movement_aots() function test_block_movement_aots()
eressea.settings.set("rules.guard.base_stop_prob", "0.0") eressea.settings.set("rules.guard.base_stop_prob", "0.0")
eressea.settings.set("rules.guard.skill_stop_prob", "1.0") eressea.settings.set("rules.guard.skill_stop_prob", "1.0")
@ -339,11 +337,11 @@ function test_stonegolems()
u1:set_skill("building", 1) u1:set_skill("building", 1)
u2:set_skill("building", 1) u2:set_skill("building", 1)
-- test that no server crash occur -- test that no server crash occurs
u1:clear_orders() u1:clear_orders()
u1:add_order("Mache Burg") u1:add_order("Mache Burg")
process_orders() process_orders()
assert_equal(0 ,u1.number, "There shoud be no Stone Golems") assert_equal(0, u1.number, "There should be no more stone golems")
-- end test server crash -- end test server crash
-- test that Stone Golems build for four stones -- test that Stone Golems build for four stones
@ -351,7 +349,7 @@ function test_stonegolems()
u2:add_order("MACHE 4 BURG " .. itoa36(c1.id)) u2:add_order("MACHE 4 BURG " .. itoa36(c1.id))
process_orders() process_orders()
assert_equal(230, c1.size, "resulting size should be 230") assert_equal(230, c1.size, "resulting size should be 230")
assert_equal(1 ,u2.number, "There shoud be one Stone Golems") assert_equal(1, u2.number, "There should be one stone golem")
-- end test Stone Golems four stones -- end test Stone Golems four stones
end end

View file

@ -12,6 +12,22 @@ local function create_faction(race)
return faction.create(race, race .. '@example.com', "de") return faction.create(race, race .. '@example.com', "de")
end end
function test_produce_multi()
local r = region.create(0, 0, 'mountain')
local f = create_faction('human')
local u = unit.create(f, r, 1)
-- sword needs skill=3, iron=1
u:set_skill('weaponsmithing', 15)
u:add_item('iron', 5)
turn_begin()
u:add_order("MACHE 6 Schwert")
turn_process()
assert_equal(5, u:get_item('sword'))
assert_equal(0, u:get_item('iron'))
end
function test_greatbow_needs_elf() function test_greatbow_needs_elf()
-- only elves can build a greatbow -- only elves can build a greatbow
local r = region.create(0, 0, 'mountain') local r = region.create(0, 0, 'mountain')

View file

@ -143,7 +143,7 @@ extern "C" {
} troop; } troop;
typedef struct armor { typedef struct armor {
struct armor *next; struct armor *next; /* TODO: make this an array, not a list, like weapon */
const struct armor_type *atype; const struct armor_type *atype;
int count; int count;
} armor; } armor;

View file

@ -17,9 +17,11 @@
#include <util/functions.h> #include <util/functions.h>
#include <util/rand.h> #include <util/rand.h>
#include <util/rng.h> #include <util/rng.h>
#include <util/strings.h>
#include <CuTest.h> #include <CuTest.h>
#include <assert.h>
#include <stdio.h> #include <stdio.h>
#include "tests.h" #include "tests.h"
@ -68,16 +70,98 @@ static void test_make_fighter(CuTest * tc)
test_teardown(); test_teardown();
} }
static void test_select_weapon_restricted(CuTest *tc) {
item_type *itype;
unit *au;
fighter *af;
battle *b;
race * rc;
test_setup();
au = test_create_unit(test_create_faction(NULL), test_create_plain(0, 0));
itype = test_create_itemtype("halberd");
new_weapontype(itype, 0, frac_zero, NULL, 0, 0, 0, SK_MELEE);
i_change(&au->items, itype, 1);
rc = test_create_race("smurf");
CuAssertIntEquals(tc, 0, rc->mask_item & au->_race->mask_item);
b = make_battle(au->region);
af = make_fighter(b, au, make_side(b, au->faction, 0, 0, 0), false);
CuAssertPtrNotNull(tc, af->weapons);
CuAssertIntEquals(tc, 1, af->weapons[0].count);
CuAssertIntEquals(tc, 0, af->weapons[1].count);
free_battle(b);
itype->mask_deny = rc_mask(au->_race);
b = make_battle(au->region);
af = make_fighter(b, au, make_side(b, au->faction, 0, 0, 0), false);
CuAssertPtrNotNull(tc, af->weapons);
CuAssertIntEquals(tc, 0, af->weapons[0].count);
free_battle(b);
itype->mask_deny = 0;
itype->mask_allow = rc_mask(rc);
b = make_battle(au->region);
af = make_fighter(b, au, make_side(b, au->faction, 0, 0, 0), false);
CuAssertPtrNotNull(tc, af->weapons);
CuAssertIntEquals(tc, 0, af->weapons[0].count);
free_battle(b);
itype->mask_deny = 0;
itype->mask_allow = rc_mask(au->_race);
b = make_battle(au->region);
af = make_fighter(b, au, make_side(b, au->faction, 0, 0, 0), false);
CuAssertPtrNotNull(tc, af->weapons);
CuAssertIntEquals(tc, 1, af->weapons[0].count);
CuAssertIntEquals(tc, 0, af->weapons[1].count);
free_battle(b);
test_teardown();
}
static void test_select_armor(CuTest *tc) {
item_type *itype, *iscale;
unit *au;
fighter *af;
battle *b;
test_setup();
au = test_create_unit(test_create_faction(NULL), test_create_plain(0, 0));
itype = test_create_itemtype("plate");
new_armortype(itype, 0.0, frac_zero, 1, 0);
i_change(&au->items, itype, 2);
iscale = test_create_itemtype("scale");
new_armortype(iscale, 0.0, frac_zero, 2, 0);
i_change(&au->items, iscale, 1);
b = make_battle(au->region);
af = make_fighter(b, au, make_side(b, au->faction, 0, 0, 0), false);
CuAssertPtrNotNull(tc, af->armors);
CuAssertIntEquals(tc, 1, af->armors->count);
CuAssertPtrEquals(tc, iscale->rtype->atype, (armor_type *)af->armors->atype);
CuAssertIntEquals(tc, 2, af->armors->next->count);
CuAssertPtrEquals(tc, itype->rtype->atype, (armor_type *)af->armors->next->atype);
CuAssertPtrEquals(tc, NULL, af->armors->next->next);
free_battle(b);
test_teardown();
}
static building_type * setup_castle(void) { static building_type * setup_castle(void) {
building_type * btype; building_type * btype;
construction *cons; construction *cons;
btype = bt_get_or_create("castle"); btype = test_create_buildingtype("castle");
assert(btype->stages);
assert(btype->stages->construction);
btype->flags |= BTF_FORTIFICATION; btype->flags |= BTF_FORTIFICATION;
cons = btype->construction = calloc(1, sizeof(construction)); cons = btype->stages->construction;
cons->maxsize = 5; cons->maxsize = 5;
cons = cons->improvement = calloc(1, sizeof(construction)); btype->stages->next = calloc(1, sizeof(building_stage));
cons = calloc(1, sizeof(construction));
cons->maxsize = -1; cons->maxsize = -1;
btype->stages->next->construction = cons;
return btype; return btype;
} }
@ -486,9 +570,11 @@ static void test_battle_skilldiff_building(CuTest *tc)
td.index = 0; td.index = 0;
ta.fighter = setup_fighter(&b, ua); ta.fighter = setup_fighter(&b, ua);
ta.index = 0; ta.index = 0;
CuAssertIntEquals(tc, 0, buildingeffsize(ud->building, false));
CuAssertIntEquals(tc, 0, skilldiff(ta, td, 0)); CuAssertIntEquals(tc, 0, skilldiff(ta, td, 0));
ud->building->size = 10; ud->building->size = 10;
CuAssertIntEquals(tc, 1, buildingeffsize(ud->building, false));
CuAssertIntEquals(tc, -1, skilldiff(ta, td, 0)); CuAssertIntEquals(tc, -1, skilldiff(ta, td, 0));
create_curse(NULL, &ud->building->attribs, &ct_magicwalls, 1, 1, 1, 1); create_curse(NULL, &ud->building->attribs, &ct_magicwalls, 1, 1, 1, 1);
@ -583,6 +669,8 @@ CuSuite *get_battle_suite(void)
{ {
CuSuite *suite = CuSuiteNew(); CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_make_fighter); SUITE_ADD_TEST(suite, test_make_fighter);
SUITE_ADD_TEST(suite, test_select_weapon_restricted);
SUITE_ADD_TEST(suite, test_select_armor);
SUITE_ADD_TEST(suite, test_battle_skilldiff); SUITE_ADD_TEST(suite, test_battle_skilldiff);
SUITE_ADD_TEST(suite, test_battle_skilldiff_building); SUITE_ADD_TEST(suite, test_battle_skilldiff_building);
SUITE_ADD_TEST(suite, test_defenders_get_building_bonus); SUITE_ADD_TEST(suite, test_defenders_get_building_bonus);

View file

@ -770,26 +770,6 @@ static int config_get_btype(lua_State * L)
} }
lua_settable(L, -3); lua_settable(L, -3);
} }
if (btype->construction) {
lua_pushstring(L, "build_skill_min");
lua_pushinteger(L, btype->construction->minskill);
lua_settable(L, -3);
lua_pushstring(L, "build_skill_name");
lua_pushstring(L, skillnames[btype->construction->skill]);
lua_settable(L, -3);
if (btype->construction->materials) {
int i;
lua_pushstring(L, "materials");
lua_newtable(L);
for (i = 0; btype->construction->materials[i].number; ++i) {
lua_pushstring(L,
btype->construction->materials[i].rtype->_name);
lua_pushinteger(L, btype->construction->materials[i].number);
lua_settable(L, -3);
}
lua_settable(L, -3);
}
}
return 1; return 1;
} }
} }

View file

@ -1409,7 +1409,7 @@ int make_cmd(unit * u, struct order *ord)
if (pl && fval(pl, PFL_NOBUILD)) { if (pl && fval(pl, PFL_NOBUILD)) {
cmistake(u, ord, 275, MSG_PRODUCE); cmistake(u, ord, 275, MSG_PRODUCE);
} }
else if (btype->construction) { else if (btype->stages && btype->stages->construction) {
int id = getid(); int id = getid();
build_building(u, btype, id, m, ord); build_building(u, btype, id, m, ord);
} }

View file

@ -154,20 +154,6 @@ static void json_construction(cJSON *json, construction **consp) {
cJSON *child; cJSON *child;
construction * cons; construction * cons;
if (json->type == cJSON_Array) {
int size = 0;
for (child = json->child; child; child = child->next) {
construction *cons = 0;
json_construction(child, &cons);
if (cons) {
cons->maxsize -= size;
size += cons->maxsize + size;
*consp = cons;
consp = &cons->improvement;
}
}
return;
}
if (json->type != cJSON_Object) { if (json->type != cJSON_Object) {
log_error("construction %s is not a json object: %d", json->string, json->type); log_error("construction %s is not a json object: %d", json->string, json->type);
return; return;
@ -312,6 +298,57 @@ static void json_terrain(cJSON *json, terrain_type *ter) {
} }
} }
static void json_stage(cJSON *json, building_stage *stage) {
cJSON *child;
if (json->type != cJSON_Object) {
log_error("building stages is not a json object: %d", json->type);
return;
}
for (child = json->child; child; child = child->next) {
switch (child->type) {
case cJSON_Object:
if (strcmp(child->string, "construction") == 0) {
json_construction(child, &stage->construction);
}
break;
case cJSON_String:
if (strcmp(child->string, "name") == 0) {
stage->name = str_strdup(child->valuestring);
}
break;
}
}
}
static void json_stages(cJSON *json, building_type *bt) {
cJSON *child;
building_stage *stage, **sp = &bt->stages;
int size = 0;
if (json->type != cJSON_Array) {
log_error("building stages is not a json array: %d", json->type);
return;
}
for (child = json->child; child; child = child->next) {
switch (child->type) {
case cJSON_Object:
stage = calloc(sizeof(building_stage), 1);
json_stage(child, stage);
if (stage->construction->maxsize > 0) {
stage->construction->maxsize -= size;
size += stage->construction->maxsize;
}
*sp = stage;
sp = &stage->next;
break;
default:
log_error("building stage contains non-object type %d", child->type);
}
}
}
static void json_building(cJSON *json, building_type *bt) { static void json_building(cJSON *json, building_type *bt) {
cJSON *child; cJSON *child;
const char *flags[] = { const char *flags[] = {
@ -324,8 +361,10 @@ static void json_building(cJSON *json, building_type *bt) {
for (child = json->child; child; child = child->next) { for (child = json->child; child; child = child->next) {
switch (child->type) { switch (child->type) {
case cJSON_Array: case cJSON_Array:
if (strcmp(child->string, "construction") == 0) { if (strcmp(child->string, "stages") == 0) {
json_construction(child, &bt->construction); if (!bt->stages) {
json_stages(child, bt);
}
} }
else if (strcmp(child->string, "maintenance") == 0) { else if (strcmp(child->string, "maintenance") == 0) {
json_maintenance(child, &bt->maintenance); json_maintenance(child, &bt->maintenance);
@ -336,9 +375,14 @@ static void json_building(cJSON *json, building_type *bt) {
break; break;
case cJSON_Object: case cJSON_Object:
if (strcmp(child->string, "construction") == 0) { if (strcmp(child->string, "construction") == 0) {
json_construction(child, &bt->construction); /* simple, single-stage building */
if (!bt->stages) {
building_stage *stage = calloc(sizeof(building_stage), 1);
json_construction(child, &stage->construction);
bt->stages = stage;
}
} }
else if (strcmp(child->string, "maintenance") == 0) { if (strcmp(child->string, "maintenance") == 0) {
json_maintenance(child, &bt->maintenance); json_maintenance(child, &bt->maintenance);
} }
break; break;

View file

@ -110,7 +110,7 @@ static void test_prefixes(CuTest * tc)
CuAssertPtrNotNull(tc, race_prefixes); CuAssertPtrNotNull(tc, race_prefixes);
CuAssertStrEquals(tc, "snow", race_prefixes[0]); CuAssertStrEquals(tc, "snow", race_prefixes[0]);
CuAssertStrEquals(tc, "dark", race_prefixes[2]); CuAssertStrEquals(tc, "dark", race_prefixes[2]);
CuAssertPtrEquals(tc, 0, race_prefixes[3]); CuAssertPtrEquals(tc, NULL, race_prefixes[3]);
cJSON_Delete(json); cJSON_Delete(json);
test_teardown(); test_teardown();
} }
@ -189,7 +189,7 @@ static void test_races(CuTest * tc)
test_setup(); test_setup();
CuAssertPtrNotNull(tc, json); CuAssertPtrNotNull(tc, json);
CuAssertPtrEquals(tc, 0, races); CuAssertPtrEquals(tc, NULL, races);
json_config(json); json_config(json);
CuAssertPtrNotNull(tc, races); CuAssertPtrNotNull(tc, races);
@ -222,7 +222,7 @@ static void test_findrace(CuTest *tc) {
CuAssertPtrNotNull(tc, json); CuAssertPtrNotNull(tc, json);
test_setup(); test_setup();
lang = get_or_create_locale("de"); lang = get_or_create_locale("de");
CuAssertPtrEquals(tc, 0, (void *)findrace("Zwerg", lang)); CuAssertPtrEquals(tc, NULL, (void *)findrace("Zwerg", lang));
json_config(json); json_config(json);
init_locale(lang); init_locale(lang);
@ -245,9 +245,9 @@ static void test_items(CuTest * tc)
test_setup(); test_setup();
CuAssertPtrNotNull(tc, json); CuAssertPtrNotNull(tc, json);
CuAssertPtrEquals(tc, 0, it_find("axe")); CuAssertPtrEquals(tc, NULL, it_find("axe"));
CuAssertPtrEquals(tc, 0, rt_find("axe")); CuAssertPtrEquals(tc, NULL, rt_find("axe"));
CuAssertPtrEquals(tc, 0, (void *)get_resourcetype(R_HORSE)); CuAssertPtrEquals(tc, NULL, (void *)get_resourcetype(R_HORSE));
json_config(json); json_config(json);
@ -283,7 +283,7 @@ static void test_ships(CuTest * tc)
test_setup(); test_setup();
CuAssertPtrNotNull(tc, json); CuAssertPtrNotNull(tc, json);
CuAssertPtrEquals(tc, 0, shiptypes); CuAssertPtrEquals(tc, NULL, shiptypes);
json_config(json); json_config(json);
CuAssertPtrNotNull(tc, shiptypes); CuAssertPtrNotNull(tc, shiptypes);
@ -301,7 +301,7 @@ static void test_ships(CuTest * tc)
CuAssertPtrNotNull(tc, st->coasts); CuAssertPtrNotNull(tc, st->coasts);
CuAssertPtrEquals(tc, (void *)ter, (void *)st->coasts[0]); CuAssertPtrEquals(tc, (void *)ter, (void *)st->coasts[0]);
CuAssertPtrEquals(tc, 0, (void *)st->coasts[1]); CuAssertPtrEquals(tc, NULL, (void *)st->coasts[1]);
cJSON_Delete(json); cJSON_Delete(json);
test_teardown(); test_teardown();
@ -309,28 +309,42 @@ static void test_ships(CuTest * tc)
static void test_castles(CuTest *tc) { static void test_castles(CuTest *tc) {
const char * data = "{\"buildings\": { \"castle\" : { " const char * data = "{\"buildings\": { \"castle\" : { "
"\"construction\" : [" "\"stages\" : ["
"{ \"maxsize\" : 2 }," "{ \"construction\": { \"maxsize\" : 2 }, \"name\": \"site\" },"
"{ \"maxsize\" : 8 }" "{ \"construction\": { \"maxsize\" : 8 } },"
"{ \"construction\": { \"maxsize\" : -1 } }"
"]}}}"; "]}}}";
cJSON *json = cJSON_Parse(data); cJSON *json = cJSON_Parse(data);
const building_type *bt; const building_type *bt;
const building_stage *stage;
const construction *con;
test_setup(); test_setup();
CuAssertPtrNotNull(tc, json); CuAssertPtrNotNull(tc, json);
CuAssertPtrEquals(tc, 0, buildingtypes); CuAssertPtrEquals(tc, NULL, buildingtypes);
json_config(json); json_config(json);
CuAssertPtrNotNull(tc, buildingtypes); CuAssertPtrNotNull(tc, buildingtypes);
bt = bt_find("castle"); bt = bt_find("castle");
CuAssertPtrNotNull(tc, bt); CuAssertPtrNotNull(tc, bt);
CuAssertPtrNotNull(tc, bt->construction); CuAssertPtrNotNull(tc, stage = bt->stages);
CuAssertIntEquals(tc, 2, bt->construction->maxsize); CuAssertStrEquals(tc, "site", stage->name);
CuAssertPtrNotNull(tc, bt->construction->improvement); CuAssertPtrNotNull(tc, con = stage->construction);
CuAssertIntEquals(tc, 6, bt->construction->improvement->maxsize); CuAssertIntEquals(tc, 2, con->maxsize);
CuAssertPtrEquals(tc, 0, bt->construction->improvement->improvement);
CuAssertPtrNotNull(tc, stage = stage->next);
CuAssertPtrEquals(tc, NULL, stage->name);
CuAssertPtrNotNull(tc, con = stage->construction);
CuAssertIntEquals(tc, 6, con->maxsize);
CuAssertPtrNotNull(tc, stage = stage->next);
CuAssertPtrNotNull(tc, con = stage->construction);
CuAssertIntEquals(tc, -1, con->maxsize);
CuAssertPtrEquals(tc, NULL, stage->next);
cJSON_Delete(json); cJSON_Delete(json);
test_teardown(); test_teardown();
} }
@ -344,7 +358,7 @@ static void test_spells(CuTest * tc)
test_setup(); test_setup();
CuAssertPtrNotNull(tc, json); CuAssertPtrNotNull(tc, json);
CuAssertPtrEquals(tc, 0, find_spell("fireball")); CuAssertPtrEquals(tc, NULL, find_spell("fireball"));
json_config(json); json_config(json);
sp = find_spell("fireball"); sp = find_spell("fireball");
@ -353,7 +367,7 @@ static void test_spells(CuTest * tc)
cJSON_Delete(json); cJSON_Delete(json);
test_teardown(); test_teardown();
CuAssertPtrEquals(tc, 0, find_spell("fireball")); CuAssertPtrEquals(tc, NULL, find_spell("fireball"));
} }
static const char * building_data = "{\"buildings\": { " static const char * building_data = "{\"buildings\": { "
@ -380,11 +394,12 @@ static void test_buildings(CuTest * tc)
{ {
cJSON *json = cJSON_Parse(building_data); cJSON *json = cJSON_Parse(building_data);
const building_type *bt; const building_type *bt;
const construction *con;
test_setup(); test_setup();
CuAssertPtrNotNull(tc, json); CuAssertPtrNotNull(tc, json);
CuAssertPtrEquals(tc, 0, buildingtypes); CuAssertPtrEquals(tc, NULL, buildingtypes);
json_config(json); json_config(json);
CuAssertPtrNotNull(tc, buildingtypes); CuAssertPtrNotNull(tc, buildingtypes);
@ -406,17 +421,19 @@ static void test_buildings(CuTest * tc)
CuAssertIntEquals(tc, MTF_VARIABLE, bt->maintenance[0].flags); CuAssertIntEquals(tc, MTF_VARIABLE, bt->maintenance[0].flags);
CuAssertIntEquals(tc, 0, bt->maintenance[1].number); CuAssertIntEquals(tc, 0, bt->maintenance[1].number);
CuAssertPtrNotNull(tc, bt->construction); CuAssertPtrNotNull(tc, bt->stages);
CuAssertPtrNotNull(tc, bt->construction->materials); CuAssertPtrEquals(tc, NULL, bt->stages->next);
CuAssertIntEquals(tc, 2, bt->construction->materials[0].number); CuAssertPtrNotNull(tc, bt->stages->construction);
CuAssertPtrEquals(tc, (void *)get_resourcetype(R_STONE), (void *)bt->construction->materials[0].rtype); CuAssertPtrNotNull(tc, con = bt->stages->construction);
CuAssertIntEquals(tc, 1, bt->construction->materials[1].number); CuAssertPtrNotNull(tc, con->materials);
CuAssertPtrEquals(tc, (void *)get_resourcetype(R_IRON), (void *)bt->construction->materials[1].rtype); CuAssertIntEquals(tc, 2, con->materials[0].number);
CuAssertIntEquals(tc, 0, bt->construction->materials[2].number); CuAssertPtrEquals(tc, (void *)get_resourcetype(R_STONE), (void *)con->materials[0].rtype);
CuAssertIntEquals(tc, 10, bt->construction->reqsize); CuAssertIntEquals(tc, 1, con->materials[1].number);
CuAssertIntEquals(tc, 20, bt->construction->maxsize); CuAssertPtrEquals(tc, (void *)get_resourcetype(R_IRON), (void *)con->materials[1].rtype);
CuAssertIntEquals(tc, 1, bt->construction->minskill); CuAssertIntEquals(tc, 0, con->materials[2].number);
CuAssertPtrEquals(tc, 0, bt->construction->improvement); CuAssertIntEquals(tc, 10, con->reqsize);
CuAssertIntEquals(tc, 20, con->maxsize);
CuAssertIntEquals(tc, 1, con->minskill);
cJSON_Delete(json); cJSON_Delete(json);
test_teardown(); test_teardown();
} }
@ -483,7 +500,7 @@ static void test_configs(CuTest * tc)
fwrite(building_data, 1, strlen(building_data), F); fwrite(building_data, 1, strlen(building_data), F);
fclose(F); fclose(F);
CuAssertPtrNotNull(tc, json); CuAssertPtrNotNull(tc, json);
CuAssertPtrEquals(tc, 0, buildingtypes); CuAssertPtrEquals(tc, NULL, buildingtypes);
json_config(json); json_config(json);
CuAssertPtrNotNull(tc, buildingtypes); CuAssertPtrNotNull(tc, buildingtypes);
if (remove("test.json")!=0 && errno==ENOENT) { if (remove("test.json")!=0 && errno==ENOENT) {
@ -508,7 +525,7 @@ static void test_terrains(CuTest * tc)
test_setup(); test_setup();
CuAssertPtrNotNull(tc, json); CuAssertPtrNotNull(tc, json);
CuAssertPtrEquals(tc, 0, (void *)get_terrain("plain")); CuAssertPtrEquals(tc, NULL, (void *)get_terrain("plain"));
json_config(json); json_config(json);
ter = get_terrain("plain"); ter = get_terrain("plain");
@ -520,7 +537,7 @@ static void test_terrains(CuTest * tc)
CuAssertPtrNotNull(tc, ter->herbs); CuAssertPtrNotNull(tc, ter->herbs);
CuAssertPtrEquals(tc, rt_get_or_create("h0"), ter->herbs[0]->rtype); CuAssertPtrEquals(tc, rt_get_or_create("h0"), ter->herbs[0]->rtype);
CuAssertPtrEquals(tc, rt_get_or_create("h1"), ter->herbs[1]->rtype); CuAssertPtrEquals(tc, rt_get_or_create("h1"), ter->herbs[1]->rtype);
CuAssertPtrEquals(tc, 0, (void *)ter->herbs[2]); CuAssertPtrEquals(tc, NULL, (void *)ter->herbs[2]);
CuAssertPtrNotNull(tc, ter->name); /* anything named "plain" uses plain_name() */ CuAssertPtrNotNull(tc, ter->name); /* anything named "plain" uses plain_name() */
CuAssertPtrNotNull(tc, ter->production); CuAssertPtrNotNull(tc, ter->production);
CuAssertPtrEquals(tc, rt_get_or_create("stone"), (resource_type *)ter->production[0].type); CuAssertPtrEquals(tc, rt_get_or_create("stone"), (resource_type *)ter->production[0].type);
@ -529,7 +546,7 @@ static void test_terrains(CuTest * tc)
CuAssertStrEquals(tc, "1d5", ter->production[0].divisor); CuAssertStrEquals(tc, "1d5", ter->production[0].divisor);
CuAssertStrEquals(tc, "1d6", ter->production[0].startlevel); CuAssertStrEquals(tc, "1d6", ter->production[0].startlevel);
CuAssertPtrEquals(tc, rt_get_or_create("iron"), (resource_type *)ter->production[1].type); CuAssertPtrEquals(tc, rt_get_or_create("iron"), (resource_type *)ter->production[1].type);
CuAssertPtrEquals(tc, 0, (void *)ter->production[2].type); CuAssertPtrEquals(tc, NULL, (void *)ter->production[2].type);
cJSON_Delete(json); cJSON_Delete(json);
test_teardown(); test_teardown();

View file

@ -145,13 +145,25 @@ static void destroy_road(unit * u, int nmax, struct order *ord)
} }
} }
static int recycle(unit *u, construction *con, int size) {
/* TODO: Nicht an ZERST<53>RE mit Punktangabe angepasst! */
int c;
for (c = 0; con->materials[c].number; ++c) {
const requirement *rq = con->materials + c;
int num = (rq->number * size / con->reqsize) / 2;
if (num) {
change_resource(u, rq->rtype, num);
}
}
return size;
}
int destroy_cmd(unit * u, struct order *ord) int destroy_cmd(unit * u, struct order *ord)
{ {
char token[128]; char token[128];
ship *sh; ship *sh;
unit *u2; unit *u2;
region *r = u->region; region *r = u->region;
const construction *con = NULL;
int size = 0; int size = 0;
const char *s; const char *s;
int n = INT_MAX; int n = INT_MAX;
@ -194,6 +206,7 @@ int destroy_cmd(unit * u, struct order *ord)
return 138; return 138;
} }
if (n >= b->size) { if (n >= b->size) {
building_stage *stage;
/* destroy completly */ /* destroy completly */
/* all units leave the building */ /* all units leave the building */
for (u2 = r->units; u2; u2 = u2->next) { for (u2 = r->units; u2; u2 = u2->next) {
@ -202,11 +215,13 @@ int destroy_cmd(unit * u, struct order *ord)
} }
} }
ADDMSG(&u->faction->msgs, msg_message("destroy", "building unit", b, u)); ADDMSG(&u->faction->msgs, msg_message("destroy", "building unit", b, u));
con = b->type->construction; for (stage = b->type->stages; stage; stage = stage->next) {
size = recycle(u, stage->construction, size);
}
remove_building(&r->buildings, b); remove_building(&r->buildings, b);
} }
else { else {
/* partial destroy */ /* TODO: partial destroy does not recycle */
b->size -= n; b->size -= n;
ADDMSG(&u->faction->msgs, msg_message("destroy_partial", ADDMSG(&u->faction->msgs, msg_message("destroy_partial",
"building unit", b, u)); "building unit", b, u));
@ -234,7 +249,7 @@ int destroy_cmd(unit * u, struct order *ord)
} }
ADDMSG(&u->faction->msgs, msg_message("shipdestroy", ADDMSG(&u->faction->msgs, msg_message("shipdestroy",
"unit region ship", u, r, sh)); "unit region ship", u, r, sh));
con = sh->type->construction; size = recycle(u, sh->type->construction, size);
remove_ship(&sh->region->ships, sh); remove_ship(&sh->region->ships, sh);
} }
else { else {
@ -248,18 +263,6 @@ int destroy_cmd(unit * u, struct order *ord)
cmistake(u, ord, 138, MSG_PRODUCE); cmistake(u, ord, 138, MSG_PRODUCE);
return 138; return 138;
} }
if (con) {
/* TODO: Nicht an ZERST<53>RE mit Punktangabe angepasst! */
int c;
for (c = 0; con->materials[c].number; ++c) {
const requirement *rq = con->materials + c;
int recycle = (rq->number * size / con->reqsize) / 2;
if (recycle) {
change_resource(u, rq->rtype, recycle);
}
}
}
return 0; return 0;
} }
@ -512,15 +515,16 @@ int build(unit * u, const construction * ctype, int completed, int want, int ski
if (want <= 0) if (want <= 0)
return 0; return 0;
if (con == NULL) if (con == NULL) {
return ENOMATERIALS; return ENOMATERIALS;
if (con->improvement == NULL && completed == con->maxsize) }
if (completed == con->maxsize) {
return ECOMPLETE; return ECOMPLETE;
}
if (con->skill != NOSKILL) { if (con->skill != NOSKILL) {
int effsk; int effsk;
int dm = get_effect(u, oldpotiontype[P_DOMORE]); int dm = get_effect(u, oldpotiontype[P_DOMORE]);
assert(u->number);
basesk = effskill(u, con->skill, 0); basesk = effskill(u, con->skill, 0);
if (basesk == 0) if (basesk == 0)
return ENEEDSKILL; return ENEEDSKILL;
@ -544,23 +548,6 @@ int build(unit * u, const construction * ctype, int completed, int want, int ski
for (; want > 0 && skills > 0;) { for (; want > 0 && skills > 0;) {
int err, n; int err, n;
/* skip over everything that's already been done:
* type->improvement==NULL means no more improvements, but no size limits
* 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 (con && con->improvement &&
con->improvement != con &&
con->maxsize > 0 && con->maxsize <= completed) {
completed -= con->maxsize;
con = con->improvement;
}
if (con == NULL) {
if (made == 0)
return ECOMPLETE;
break; /* completed */
}
/* Hier ist entweder maxsize == -1, oder completed < maxsize. /* Hier ist entweder maxsize == -1, oder completed < maxsize.
* Andernfalls ist das Datenfile oder sonstwas kaputt... * Andernfalls ist das Datenfile oder sonstwas kaputt...
* (enno): Nein, das ist f<EFBFBD>r Dinge, bei denen die n<EFBFBD>chste Ausbaustufe * (enno): Nein, das ist f<EFBFBD>r Dinge, bei denen die n<EFBFBD>chste Ausbaustufe
@ -605,9 +592,7 @@ int build(unit * u, const construction * ctype, int completed, int want, int ski
if (con->maxsize > 0) { if (con->maxsize > 0) {
int req = con->maxsize - completed; int req = con->maxsize - completed;
if (req < n) n = req; if (req < n) n = req;
if (con->improvement == NULL) { want = n;
want = n;
}
} }
n = count_materials(u, con, n, completed); n = count_materials(u, con, n, completed);
@ -660,7 +645,6 @@ message *msg_materials_required(unit * u, order * ord,
int maxbuild(const unit * u, const construction * cons) int maxbuild(const unit * u, const construction * cons)
/* calculate maximum size that can be built from available material */ /* calculate maximum size that can be built from available material */
/* !! ignores maximum objectsize and improvements... */
{ {
int c; int c;
int maximum = INT_MAX; int maximum = INT_MAX;
@ -679,7 +663,72 @@ int maxbuild(const unit * u, const construction * cons)
return maximum; return maximum;
} }
/** old build routines */ static int build_failure(unit *u, order *ord, const building_type *btype, int want, int err) {
switch (err) {
case ECOMPLETE:
/* the building is already complete */
cmistake(u, ord, 4, MSG_PRODUCE);
break;
case ENOMATERIALS:
ADDMSG(&u->faction->msgs, msg_materials_required(u, ord,
btype->stages->construction, want));
break;
case ELOWSKILL:
case ENEEDSKILL:
/* no skill, or not enough skill points to build */
cmistake(u, ord, 50, MSG_PRODUCE);
break;
}
return err;
}
static int build_stages(unit *u, const building_type *btype, int built, int n) {
const building_stage *stage;
int made = 0;
for (stage = btype->stages; stage; stage = stage->next) {
const construction * con = stage->construction;
if (con->maxsize < 0 || con->maxsize > built) {
int err, want = INT_MAX;
if (n < INT_MAX) {
/* do not build more than n total */
want = n - made;
}
if (con->maxsize > 0) {
/* do not build more than the rest of the stage */
int todo = con->maxsize - built;
if (todo < want) {
want = todo;
}
}
err = build(u, con, built, want, 0);
if (err < 0) {
if (made == 0) {
/* could not make any part at all */
return err;
}
else {
/* could not build any part of this stage (low skill, etc). */
break;
}
}
else {
/* err is the amount we built of this stage */
made += err;
if (err != con->maxsize && con->maxsize > 0) {
/* we did not finish the stage, can quit here */
break;
}
}
}
/* build the next stage: */
if (built >= con->maxsize && con->maxsize > 0) {
built -= con->maxsize;
}
}
return made;
}
int int
build_building(unit * u, const building_type * btype, int id, int want, order * ord) build_building(unit * u, const building_type * btype, int id, int want, order * ord)
@ -693,7 +742,7 @@ build_building(unit * u, const building_type * btype, int id, int want, order *
const struct locale *lang = u->faction->locale; const struct locale *lang = u->faction->locale;
assert(u->number); assert(u->number);
assert(btype->construction); assert(btype->stages && btype->stages->construction);
if (effskill(u, SK_BUILDING, 0) == 0) { if (effskill(u, SK_BUILDING, 0) == 0) {
cmistake(u, ord, 101, MSG_PRODUCE); cmistake(u, ord, 101, MSG_PRODUCE);
return 0; return 0;
@ -783,30 +832,19 @@ build_building(unit * u, const building_type * btype, int id, int want, order *
} }
} }
} }
built = build(u, btype->construction, built, n, 0);
switch (built) { built = build_stages(u, btype, built, n);
case ECOMPLETE:
/* the building is already complete */ if (built < 0) {
cmistake(u, ord, 4, MSG_PRODUCE); return build_failure(u, ord, btype, want, built);
break;
case ENOMATERIALS:
ADDMSG(&u->faction->msgs, msg_materials_required(u, ord,
btype->construction, want));
break;
case ELOWSKILL:
case ENEEDSKILL:
/* no skill, or not enough skill points to build */
cmistake(u, ord, 50, MSG_PRODUCE);
break;
} }
if (built <= 0) {
return built; if (b) {
} b->size += built;
/* at this point, the building size is increased. */ } else {
if (b == NULL) {
/* build a new building */ /* build a new building */
b = new_building(btype, r, lang); b = new_building(btype, r, lang);
b->size = built;
b->type = btype; b->type = btype;
fset(b, BLD_MAINTAINED); fset(b, BLD_MAINTAINED);
@ -818,7 +856,7 @@ build_building(unit * u, const building_type * btype, int id, int want, order *
btname = LOC(lang, btype->_name); btname = LOC(lang, btype->_name);
if (want - built <= 0) { if (want <= built) {
/* geb<65>ude fertig */ /* geb<65>ude fertig */
new_order = default_order(lang); new_order = default_order(lang);
} }
@ -850,7 +888,9 @@ build_building(unit * u, const building_type * btype, int id, int want, order *
free_order(new_order); free_order(new_order);
} }
b->size += built; ADDMSG(&u->faction->msgs, msg_message("buildbuilding",
"building unit size", b, u, built));
if (b->type->maxsize > 0 && b->size > b->type->maxsize) { if (b->type->maxsize > 0 && b->size > b->type->maxsize) {
log_error("build: %s has size=%d, maxsize=%d", buildingname(b), b->size, b->type->maxsize); log_error("build: %s has size=%d, maxsize=%d", buildingname(b), b->size, b->type->maxsize);
} }
@ -858,8 +898,6 @@ build_building(unit * u, const building_type * btype, int id, int want, order *
update_lighthouse(b); update_lighthouse(b);
ADDMSG(&u->faction->msgs, msg_message("buildbuilding",
"building unit size", b, u, built));
return built; return built;
} }
@ -970,7 +1008,6 @@ void continue_ship(unit * u, int want)
return; return;
} }
cons = sh->type->construction; cons = sh->type->construction;
assert(cons->improvement == NULL); /* sonst ist construction::size nicht ship_type::maxsize */
if (sh->size == cons->maxsize && !sh->damage) { if (sh->size == cons->maxsize && !sh->damage) {
cmistake(u, u->thisorder, 16, MSG_PRODUCE); cmistake(u, u->thisorder, 16, MSG_PRODUCE);
return; return;
@ -994,11 +1031,6 @@ void continue_ship(unit * u, int want)
void free_construction(struct construction *cons) void free_construction(struct construction *cons)
{ {
while (cons) { free(cons->materials);
construction *next = cons->improvement; free(cons);
free(cons->name);
free(cons->materials);
free(cons);
cons = next;
}
} }

View file

@ -37,25 +37,13 @@ extern "C" {
int number; int number;
} requirement; } requirement;
typedef enum construct_t {
CONS_OTHER,
CONS_ITEM,
CONS_BUILDING
} construct_t;
typedef struct construction { typedef struct construction {
construct_t type;
skill_t skill; /* skill req'd per point of size */ skill_t skill; /* skill req'd per point of size */
int minskill; /* skill req'd per point of size */ int minskill; /* skill req'd per point of size */
int maxsize; /* maximum size of this type */ int maxsize; /* maximum size of this type */
int reqsize; /* size of object using up 1 set of requirement. */ int reqsize; /* size of object using up 1 set of requirement. */
requirement *materials; /* material req'd to build one object */ requirement *materials; /* material req'd to build one object */
/* only used by CONS_BUILDING: */
char * name; /* building level name */
struct construction *improvement;
/* next level, if upgradable. */
} construction; } construction;
void free_construction(struct construction *cons); void free_construction(struct construction *cons);

View file

@ -26,6 +26,7 @@ typedef struct build_fixture {
region *r; region *r;
race *rc; race *rc;
construction cons; construction cons;
building_type *btype;
} build_fixture; } build_fixture;
static unit * setup_build(build_fixture *bf) { static unit * setup_build(build_fixture *bf) {
@ -34,7 +35,7 @@ static unit * setup_build(build_fixture *bf) {
init_resources(); init_resources();
test_create_itemtype("stone"); test_create_itemtype("stone");
test_create_buildingtype("castle"); bf->btype = test_create_buildingtype("castle");
bf->rc = test_create_race("human"); bf->rc = test_create_race("human");
bf->r = test_create_region(0, 0, NULL); bf->r = test_create_region(0, 0, NULL);
bf->f = test_create_faction(bf->rc); bf->f = test_create_faction(bf->rc);
@ -192,8 +193,7 @@ static void test_build_building_no_materials(CuTest *tc) {
unit *u; unit *u;
u = setup_build(&bf); u = setup_build(&bf);
btype = bt_find("castle"); btype = bf.btype;
assert(btype);
set_level(u, SK_BUILDING, 1); set_level(u, SK_BUILDING, 1);
u->orders = create_order(K_MAKE, u->faction->locale, 0); u->orders = create_order(K_MAKE, u->faction->locale, 0);
CuAssertIntEquals(tc, ENOMATERIALS, build_building(u, btype, 0, 4, u->orders)); CuAssertIntEquals(tc, ENOMATERIALS, build_building(u, btype, 0, 4, u->orders));
@ -209,9 +209,7 @@ static void test_build_building_with_golem(CuTest *tc) {
u = setup_build(&bf); u = setup_build(&bf);
bf.rc->ec_flags |= ECF_STONEGOLEM; bf.rc->ec_flags |= ECF_STONEGOLEM;
btype = bt_find("castle"); btype = bf.btype;
assert(btype);
assert(btype->construction);
set_level(bf.u, SK_BUILDING, 1); set_level(bf.u, SK_BUILDING, 1);
u->orders = create_order(K_MAKE, u->faction->locale, 0); u->orders = create_order(K_MAKE, u->faction->locale, 0);
@ -231,9 +229,8 @@ static void test_build_building_success(CuTest *tc) {
u = setup_build(&bf); u = setup_build(&bf);
rtype = get_resourcetype(R_STONE); rtype = get_resourcetype(R_STONE);
btype = bt_find("castle"); btype = bf.btype;
assert(btype && rtype && rtype->itype); assert(btype && rtype && rtype->itype);
assert(btype->construction);
assert(!u->region->buildings); assert(!u->region->buildings);
i_change(&bf.u->items, rtype->itype, 1); i_change(&bf.u->items, rtype->itype, 1);

View file

@ -116,7 +116,13 @@ static void bt_register(building_type * btype)
static void free_buildingtype(void *ptr) { static void free_buildingtype(void *ptr) {
building_type *btype = (building_type *)ptr; building_type *btype = (building_type *)ptr;
free_construction(btype->construction); while (btype->stages) {
building_stage *next = btype->stages->next;
free_construction(btype->stages->construction);
free(btype->stages->name);
free(btype->stages);
btype->stages = next;
}
free(btype->maintenance); free(btype->maintenance);
free(btype->_name); free(btype->_name);
free(btype); free(btype);
@ -189,8 +195,6 @@ static int adjust_size(const building *b, int bsize) {
*/ */
const char *buildingtype(const building_type * btype, const building * b, int bsize) const char *buildingtype(const building_type * btype, const building * b, int bsize)
{ {
const construction *con;
assert(btype); assert(btype);
if (b && b->attribs) { if (b && b->attribs) {
@ -201,14 +205,15 @@ const char *buildingtype(const building_type * btype, const building * b, int bs
} }
} }
} }
if (btype->construction && btype->construction->name) { if (btype->stages && btype->stages->name) {
const building_stage *stage;
if (b) { if (b) {
bsize = adjust_size(b, bsize); bsize = adjust_size(b, bsize);
} }
for (con = btype->construction; con; con = con->improvement) { for (stage = btype->stages; stage; stage = stage->next) {
bsize -= con->maxsize; bsize -= stage->construction->maxsize;
if (!con->improvement || bsize <0) { if (!stage->next || bsize <0) {
return con->name; return stage->name;
} }
} }
} }
@ -476,24 +481,30 @@ int buildingeffsize(const building * b, int img)
int bt_effsize(const building_type * btype, const building * b, int bsize) int bt_effsize(const building_type * btype, const building * b, int bsize)
{ {
int n = 0;
const construction *cons = btype->construction;
if (b) { if (b) {
bsize = adjust_size(b, bsize); bsize = adjust_size(b, bsize);
} }
if (!cons) { if (btype->stages) {
return 0; int n = 0;
const building_stage *stage = btype->stages;
do {
const construction *con = stage->construction;
if (con->maxsize < 0) {
break;
}
else {
if (bsize >= con->maxsize) {
bsize -= con->maxsize;
++n;
}
stage = stage->next;
}
} while (stage && bsize > 0);
return n;
} }
while (cons && cons->maxsize != -1 && bsize >= cons->maxsize) { return 0;
bsize -= cons->maxsize;
cons = cons->improvement;
++n;
}
return n;
} }
const char *write_buildingname(const building * b, char *ibuf, size_t size) const char *write_buildingname(const building * b, char *ibuf, size_t size)

View file

@ -54,6 +54,15 @@ extern "C" {
#define BTF_NAMECHANGE 0x100 /* name and description can be changed more than once */ #define BTF_NAMECHANGE 0x100 /* name and description can be changed more than once */
#define BTF_FORTIFICATION 0x200 /* building_protection, safe from monsters */ #define BTF_FORTIFICATION 0x200 /* building_protection, safe from monsters */
typedef struct building_stage {
/* construction of this building stage: */
struct construction *construction;
/* building stage name: */
char * name;
/* next stage, if upgradable: */
struct building_stage * next;
} building_stage;
typedef struct building_type { typedef struct building_type {
char *_name; char *_name;
@ -67,8 +76,8 @@ extern "C" {
int taxes; /* receive $1 tax per `taxes` in region */ int taxes; /* receive $1 tax per `taxes` in region */
double auraregen; /* modifier for aura regeneration inside building */ double auraregen; /* modifier for aura regeneration inside building */
struct maintenance *maintenance; /* array of requirements */ struct maintenance *maintenance; /* array of requirements */
struct construction *construction; /* construction of 1 building-level */
struct resource_mod *modifiers; /* modify production skills */ struct resource_mod *modifiers; /* modify production skills */
struct building_stage *stages;
} building_type; } building_type;
extern struct selist *buildingtypes; extern struct selist *buildingtypes;

View file

@ -224,7 +224,7 @@ static void test_buildingowner_goes_to_same_faction_after_leave(CuTest * tc)
leave_building(u3); leave_building(u3);
CuAssertPtrEquals(tc, u2, building_owner(bld)); CuAssertPtrEquals(tc, u2, building_owner(bld));
leave_building(u2); leave_building(u2);
CuAssertPtrEquals(tc, 0, building_owner(bld)); CuAssertPtrEquals(tc, NULL, building_owner(bld));
test_teardown(); test_teardown();
} }
@ -281,7 +281,7 @@ void test_buildingowner_goes_to_empty_unit_after_leave(CuTest * tc)
leave_building(u1); leave_building(u1);
CuAssertPtrEquals(tc, u3, building_owner(bld)); CuAssertPtrEquals(tc, u3, building_owner(bld));
leave_building(u3); leave_building(u3);
CuAssertPtrEquals(tc, 0, building_owner(bld)); CuAssertPtrEquals(tc, NULL, building_owner(bld));
u2->number = 1; u2->number = 1;
CuAssertPtrEquals(tc, u2, building_owner(bld)); CuAssertPtrEquals(tc, u2, building_owner(bld));
test_teardown(); test_teardown();
@ -295,8 +295,8 @@ static void test_btype_defaults(CuTest *tc) {
btype = bt_get_or_create("hodor"); btype = bt_get_or_create("hodor");
CuAssertPtrNotNull(tc, btype); CuAssertPtrNotNull(tc, btype);
CuAssertStrEquals(tc, "hodor", btype->_name); CuAssertStrEquals(tc, "hodor", btype->_name);
CuAssertPtrEquals(tc, 0, btype->maintenance); CuAssertPtrEquals(tc, NULL, btype->maintenance);
CuAssertPtrEquals(tc, 0, btype->construction); CuAssertPtrEquals(tc, NULL, btype->stages);
CuAssertDblEquals(tc, 1.0, btype->auraregen, 0.0); CuAssertDblEquals(tc, 1.0, btype->auraregen, 0.0);
CuAssertIntEquals(tc, 0, btype->taxes); CuAssertIntEquals(tc, 0, btype->taxes);
CuAssertIntEquals(tc, -1, btype->maxsize); CuAssertIntEquals(tc, -1, btype->maxsize);
@ -489,11 +489,11 @@ static void test_cmp_current_owner(CuTest *tc) {
config_set("rules.region_owners", "1"); config_set("rules.region_owners", "1");
r = test_create_region(0, 0, NULL); r = test_create_region(0, 0, NULL);
btype = test_create_buildingtype("watch"); btype = test_create_buildingtype("watch");
btype->construction->maxsize = 1; btype->stages->construction->maxsize = 1;
btype->taxes = 200; btype->taxes = 200;
b1 = test_create_building(r, btype); b1 = test_create_building(r, btype);
btype = test_create_buildingtype("castle"); btype = test_create_buildingtype("castle");
btype->construction->maxsize = 1; btype->stages->construction->maxsize = 1;
btype->taxes = 100; btype->taxes = 100;
b2 = test_create_building(r, btype); b2 = test_create_building(r, btype);
b1->size = 1; b1->size = 1;
@ -515,16 +515,26 @@ static void test_cmp_current_owner(CuTest *tc) {
static void test_building_effsize(CuTest *tc) { static void test_building_effsize(CuTest *tc) {
building *b; building *b;
building_type *btype; building_type *btype;
building_stage *stage;
construction *cons; construction *cons;
test_setup(); test_setup();
btype = bt_get_or_create("castle"); btype = test_create_buildingtype("castle");
cons = btype->construction = calloc(1, sizeof(construction)); stage = btype->stages;
assert(stage && stage->construction);
cons = stage->construction;
cons->maxsize = 5; cons->maxsize = 5;
cons = cons->improvement = calloc(1, sizeof(construction));
stage->next = calloc(1, sizeof(building_stage));
stage = stage->next;
cons = stage->construction = calloc(1, sizeof(construction));
cons->maxsize = 5; cons->maxsize = 5;
cons = cons->improvement = calloc(1, sizeof(construction));
stage->next = calloc(1, sizeof(building_stage));
stage = stage->next;
cons = stage->construction = calloc(1, sizeof(construction));
cons->maxsize = -1; cons->maxsize = -1;
b = test_create_building(test_create_region(0,0,0), btype); b = test_create_building(test_create_region(0,0,0), btype);
b->size = 1; b->size = 1;
CuAssertIntEquals(tc, 0, buildingeffsize(b, false)); CuAssertIntEquals(tc, 0, buildingeffsize(b, false));
@ -563,14 +573,20 @@ static void test_largestbuilding(CuTest *tc) {
static void test_buildingtype(CuTest *tc) { static void test_buildingtype(CuTest *tc) {
building_type *btype; building_type *btype;
test_setup(); test_setup();
btype = test_create_buildingtype("hodor"); btype = test_create_buildingtype("hodor");
CuAssertPtrNotNull(tc, btype->construction); CuAssertPtrNotNull(tc, btype->stages);
CuAssertPtrEquals(tc, NULL, btype->stages->name);
CuAssertPtrNotNull(tc, btype->stages->construction);
CuAssertStrEquals(tc, "hodor", buildingtype(btype, NULL, 1)); CuAssertStrEquals(tc, "hodor", buildingtype(btype, NULL, 1));
btype->construction->name = str_strdup("castle");
btype->stages->name = str_strdup("castle");
CuAssertStrEquals(tc, "castle", buildingtype(btype, NULL, 1)); CuAssertStrEquals(tc, "castle", buildingtype(btype, NULL, 1));
btype = bt_get_or_create("portal"); btype = bt_get_or_create("portal");
CuAssertPtrEquals(tc, NULL, btype->construction); CuAssertPtrEquals(tc, NULL, btype->stages);
CuAssertStrEquals(tc, "portal", buildingtype(btype, NULL, 1)); CuAssertStrEquals(tc, "portal", buildingtype(btype, NULL, 1));
test_teardown(); test_teardown();
} }

View file

@ -900,7 +900,9 @@ void write_items(struct storage *store, item * ilist)
static void free_itype(item_type *itype) { static void free_itype(item_type *itype) {
assert(itype); assert(itype);
free_construction(itype->construction); if (itype->construction) {
free_construction(itype->construction);
}
free(itype->_appearance[0]); free(itype->_appearance[0]);
free(itype->_appearance[1]); free(itype->_appearance[1]);
free(itype); free(itype);

View file

@ -26,6 +26,18 @@ static void test_rc_name(CuTest *tc) {
test_teardown(); test_teardown();
} }
static void test_rc_item_mask(CuTest *tc) {
struct race *rc;
test_setup();
rc = rc_get_or_create("hooman");
CuAssertIntEquals(tc, 1, rc->mask_item);
rc = rc_get_or_create("aelf");
CuAssertIntEquals(tc, 2, rc->mask_item);
rc = rc_get_or_create("dorf");
CuAssertIntEquals(tc, 4, rc->mask_item);
test_teardown();
}
static void test_rc_defaults(CuTest *tc) { static void test_rc_defaults(CuTest *tc) {
struct race *rc; struct race *rc;
test_setup(); test_setup();
@ -193,6 +205,7 @@ CuSuite *get_race_suite(void)
SUITE_ADD_TEST(suite, test_old_race); SUITE_ADD_TEST(suite, test_old_race);
SUITE_ADD_TEST(suite, test_rc_name); SUITE_ADD_TEST(suite, test_rc_name);
SUITE_ADD_TEST(suite, test_rc_defaults); SUITE_ADD_TEST(suite, test_rc_defaults);
SUITE_ADD_TEST(suite, test_rc_item_mask);
SUITE_ADD_TEST(suite, test_rc_find); SUITE_ADD_TEST(suite, test_rc_find);
SUITE_ADD_TEST(suite, test_rc_mask); SUITE_ADD_TEST(suite, test_rc_mask);
SUITE_ADD_TEST(suite, test_rc_set_param); SUITE_ADD_TEST(suite, test_rc_set_param);

View file

@ -249,7 +249,9 @@ static void free_shiptype(void *ptr) {
ship_type *stype = (ship_type *)ptr; ship_type *stype = (ship_type *)ptr;
free(stype->_name); free(stype->_name);
free(stype->coasts); free(stype->coasts);
free_construction(stype->construction); if (stype->construction) {
free_construction(stype->construction);
}
free(stype); free(stype);
} }
@ -328,7 +330,6 @@ int shipspeed(const ship * sh, const unit * u)
assert(u->ship == sh); assert(u->ship == sh);
assert(u == ship_owner(sh)); assert(u == ship_owner(sh));
assert(sh->type->construction); assert(sh->type->construction);
assert(sh->type->construction->improvement == NULL); /* sonst ist construction::size nicht ship_type::maxsize */
k = sh->type->range; k = sh->type->range;
if (sh->size != sh->type->construction->maxsize) if (sh->size != sh->type->construction->maxsize)
@ -396,10 +397,6 @@ int shipcapacity(const ship * sh)
{ {
int i = sh->type->cargo; int i = sh->type->cargo;
/* sonst ist construction:: size nicht ship_type::maxsize */
assert(!sh->type->construction
|| sh->type->construction->improvement == NULL);
if (sh->type->construction && sh->size != sh->type->construction->maxsize) if (sh->type->construction && sh->size != sh->type->construction->maxsize)
return 0; return 0;

View file

@ -477,7 +477,7 @@ static void test_illusioncastle(CuTest *tc)
CuAssertPtrEquals(tc, btype, (void *)icastle_type(a)); CuAssertPtrEquals(tc, btype, (void *)icastle_type(a));
CuAssertPtrEquals(tc, bt_icastle, (void *)b->type); CuAssertPtrEquals(tc, bt_icastle, (void *)b->type);
CuAssertStrEquals(tc, "castle", buildingtype(btype, b, b->size)); CuAssertStrEquals(tc, "castle", buildingtype(btype, b, b->size));
btype->construction->name = str_strdup("site"); btype->stages->name = str_strdup("site");
CuAssertStrEquals(tc, "site", buildingtype(btype, b, b->size)); CuAssertStrEquals(tc, "site", buildingtype(btype, b, b->size));
test_teardown(); test_teardown();
} }

View file

@ -222,6 +222,9 @@ void score(void)
int default_score(const item_type *itype) { int default_score(const item_type *itype) {
int result = 0; int result = 0;
if (itype->rtype->wtype || itype->rtype->atype) {
result += 10;
}
if (itype->construction) { if (itype->construction) {
requirement *req = itype->construction->materials; requirement *req = itype->construction->materials;
while (req->number) { while (req->number) {

View file

@ -479,10 +479,6 @@ static bool cansail(const region * r, ship * sh)
{ {
UNUSED_ARG(r); UNUSED_ARG(r);
/* sonst ist construction:: size nicht ship_type::maxsize */
assert(!sh->type->construction
|| sh->type->construction->improvement == NULL);
if (sh->type->construction && sh->size != sh->type->construction->maxsize) { if (sh->type->construction && sh->size != sh->type->construction->maxsize) {
return false; return false;
} }
@ -505,10 +501,6 @@ static double overload(const region * r, ship * sh)
{ {
UNUSED_ARG(r); UNUSED_ARG(r);
/* sonst ist construction:: size nicht ship_type::maxsize */
assert(!sh->type->construction
|| sh->type->construction->improvement == NULL);
if (sh->type->construction && sh->size != sh->type->construction->maxsize) { if (sh->type->construction && sh->size != sh->type->construction->maxsize) {
return DBL_MAX; return DBL_MAX;
} }
@ -847,7 +839,6 @@ static void drifting_ships(region * r)
/* Kapitän da? Beschädigt? Genügend Matrosen? /* Kapitän da? Beschädigt? Genügend Matrosen?
* Genügend leicht? Dann ist alles OK. */ * Genügend leicht? Dann ist alles OK. */
assert(sh->type->construction->improvement == NULL); /* sonst ist construction::size nicht ship_type::maxsize */
if (captain && sh->size == sh->type->construction->maxsize if (captain && sh->size == sh->type->construction->maxsize
&& enoughsailors(sh, crew_skill(sh)) && cansail(r, sh)) { && enoughsailors(sh, crew_skill(sh)) && cansail(r, sh)) {
shp = &sh->next; shp = &sh->next;
@ -1669,7 +1660,6 @@ static bool ship_ready(const region * r, unit * u, order * ord)
return false; return false;
} }
if (u->ship->type->construction) { if (u->ship->type->construction) {
assert(!u->ship->type->construction->improvement); /* sonst ist construction::size nicht ship_type::maxsize */
if (u->ship->size != u->ship->type->construction->maxsize) { if (u->ship->size != u->ship->type->construction->maxsize) {
cmistake(u, ord, 15, MSG_MOVE); cmistake(u, ord, 15, MSG_MOVE);
return false; return false;

View file

@ -1803,7 +1803,6 @@ nr_ship(struct stream *out, const region *r, const ship * sh, const faction * f,
if (wrptr(&bufp, &size, bytes) != 0) if (wrptr(&bufp, &size, bytes) != 0)
WARN_STATIC_BUFFER(); WARN_STATIC_BUFFER();
assert(sh->type->construction->improvement == NULL); /* sonst ist construction::size nicht ship_type::maxsize */
if (sh->size != sh->type->construction->maxsize) { if (sh->size != sh->type->construction->maxsize) {
bytes = snprintf(bufp, size, ", %s (%d/%d)", bytes = snprintf(bufp, size, ", %s (%d/%d)",
LOC(f->locale, "nr_undercons"), sh->size, LOC(f->locale, "nr_undercons"), sh->size,

View file

@ -336,20 +336,27 @@ ship_type * test_create_shiptype(const char * name)
building_type * test_create_buildingtype(const char * name) building_type * test_create_buildingtype(const char * name)
{ {
construction *con;
building_type *btype = bt_get_or_create(name); building_type *btype = bt_get_or_create(name);
btype->flags = BTF_NAMECHANGE; btype->flags = BTF_NAMECHANGE;
if (!btype->construction) { if (btype->stages) {
btype->construction = (construction *)calloc(sizeof(construction), 1); con = btype->stages->construction;
btype->construction->skill = SK_BUILDING; } else {
btype->construction->maxsize = -1; btype->stages = calloc(1, sizeof(building_stage));
btype->construction->minskill = 1; con = (construction *)calloc(1, sizeof(construction));
btype->construction->reqsize = 1; if (con) {
con->skill = SK_BUILDING;
con->maxsize = -1;
con->minskill = 1;
con->reqsize = 1;
btype->stages->construction = con;
}
} }
if (!btype->construction->materials) { if (con && !con->materials) {
btype->construction->materials = (requirement *)calloc(sizeof(requirement), 2); con->materials = (requirement *)calloc(2, sizeof(requirement));
btype->construction->materials[1].number = 0; con->materials[1].number = 0;
btype->construction->materials[0].number = 1; con->materials[0].number = 1;
btype->construction->materials[0].rtype = get_resourcetype(R_STONE); con->materials[0].rtype = get_resourcetype(R_STONE);
} }
if (default_locale) { if (default_locale) {
locale_setstring(default_locale, name, name); locale_setstring(default_locale, name, name);

View file

@ -214,57 +214,64 @@ xml_readrequirements(xmlNodePtr * nodeTab, int nodeNr, requirement ** reqArray)
} }
} }
void static construction *
xml_readconstruction(xmlXPathContextPtr xpath, xmlNodeSetPtr nodeSet, xml_readconstruction(xmlXPathContextPtr xpath, xmlNodePtr node)
construction ** consPtr, construct_t type)
{ {
xmlNodePtr pushNode = xpath->node; construction *con;
int k; xmlChar *propValue;
for (k = 0; k != nodeSet->nodeNr; ++k) { xmlXPathObjectPtr req;
xmlNodePtr node = nodeSet->nodeTab[k]; skill_t sk = NOSKILL;
xmlChar *propValue;
construction *con; propValue = xmlGetProp(node, BAD_CAST "skill");
xmlXPathObjectPtr req; if (propValue != NULL) {
skill_t sk = NOSKILL; sk = findskill((const char *)propValue);
if (sk == NOSKILL) {
log_error("construction requires skill '%s' that does not exist.\n", (const char *)propValue);
xmlFree(propValue);
return NULL;
}
xmlFree(propValue);
}
con = (construction *)calloc(sizeof(construction), 1);
con->skill = sk;
con->maxsize = xml_ivalue(node, "maxsize", -1);
con->minskill = xml_ivalue(node, "minskill", -1);
con->reqsize = xml_ivalue(node, "reqsize", 1);
/* read construction/requirement */
xpath->node = node;
req = xmlXPathEvalExpression(BAD_CAST "requirement", xpath);
xml_readrequirements(req->nodesetval->nodeTab,
req->nodesetval->nodeNr, &con->materials);
xmlXPathFreeObject(req);
return con;
}
static void
xml_readconstructions(xmlXPathContextPtr xpath, xmlNodeSetPtr nodeSet, building_type *btype)
{
building_stage **stage_ptr = &btype->stages;
int k;
for (k = 0; k != nodeSet->nodeNr; ++k) {
building_stage *stage = calloc(1, sizeof(building_stage));
xmlNodePtr node = nodeSet->nodeTab[k];
construction *con = xml_readconstruction(xpath, node);
xmlChar *propValue = xmlGetProp(node, BAD_CAST "name");
propValue = xmlGetProp(node, BAD_CAST "skill");
if (propValue != NULL) { if (propValue != NULL) {
sk = findskill((const char *)propValue); stage->name = str_strdup((const char *)propValue);
if (sk == NOSKILL) {
log_error("construction requires skill '%s' that does not exist.\n", (const char *)propValue);
xmlFree(propValue);
continue;
}
xmlFree(propValue); xmlFree(propValue);
} }
stage->next = NULL;
stage->construction = con;
assert(*consPtr == NULL); *stage_ptr = stage;
stage_ptr = &stage->next;
*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);
if (type == CONS_BUILDING) {
propValue = xmlGetProp(node, BAD_CAST "name");
if (propValue != NULL) {
con->name = str_strdup((const char *)propValue);
xmlFree(propValue);
}
}
/* read construction/requirement */
xpath->node = node;
req = xmlXPathEvalExpression(BAD_CAST "requirement", xpath);
xml_readrequirements(req->nodesetval->nodeTab,
req->nodesetval->nodeNr, &con->materials);
xmlXPathFreeObject(req);
} }
xpath->node = pushNode;
} }
static int static int
@ -342,7 +349,7 @@ static int parse_buildings(xmlDocPtr doc)
/* reading eressea/buildings/building/construction */ /* reading eressea/buildings/building/construction */
xpath->node = node; xpath->node = node;
result = xmlXPathEvalExpression(BAD_CAST "construction", xpath); result = xmlXPathEvalExpression(BAD_CAST "construction", xpath);
xml_readconstruction(xpath, result->nodesetval, &btype->construction, CONS_BUILDING); xml_readconstructions(xpath, result->nodesetval, btype);
xmlXPathFreeObject(result); xmlXPathFreeObject(result);
/* reading eressea/buildings/building/function */ /* reading eressea/buildings/building/function */
@ -439,12 +446,6 @@ static int parse_ships(xmlDocPtr doc)
st->range_max = xml_ivalue(node, "maxrange", st->range_max); st->range_max = xml_ivalue(node, "maxrange", st->range_max);
st->storm = xml_fvalue(node, "storm", st->storm); st->storm = xml_fvalue(node, "storm", st->storm);
/* reading eressea/ships/ship/construction */
xpath->node = node;
result = xmlXPathEvalExpression(BAD_CAST "construction", xpath);
xml_readconstruction(xpath, result->nodesetval, &st->construction, CONS_OTHER);
xmlXPathFreeObject(result);
for (child = node->children; child; child = child->next) { for (child = node->children; child; child = child->next) {
if (strcmp((const char *)child->name, "modifier") == 0) { if (strcmp((const char *)child->name, "modifier") == 0) {
double value = xml_fvalue(child, "value", 0.0); double value = xml_fvalue(child, "value", 0.0);
@ -457,6 +458,10 @@ static int parse_ships(xmlDocPtr doc)
st->df_bonus = (int)value; st->df_bonus = (int)value;
xmlFree(propValue); xmlFree(propValue);
} }
else if (strcmp((const char *)child->name, "construction") == 0) {
assert(!st->construction);
st->construction = xml_readconstruction(xpath, child);
}
} }
/* reading eressea/ships/ship/coast */ /* reading eressea/ships/ship/coast */
xpath->node = node; xpath->node = node;
@ -491,16 +496,20 @@ static int parse_ships(xmlDocPtr doc)
return result; return result;
} }
static void xml_readpotion(xmlXPathContextPtr xpath, item_type * itype) static void xml_readpotion(xmlNodePtr node, item_type * itype)
{ {
int level = xml_ivalue(xpath->node, "level", 0); int level = xml_ivalue(node, "level", 0);
if ((itype->flags & ITF_CANUSE) == 0) {
log_error("potion %s has no use attribute", itype->rtype->_name);
itype->flags |= ITF_CANUSE;
}
new_potiontype(itype, level); new_potiontype(itype, level);
} }
static luxury_type *xml_readluxury(xmlXPathContextPtr xpath, item_type * itype) static luxury_type *xml_readluxury(xmlNodePtr node, item_type * itype)
{ {
int price = xml_ivalue(xpath->node, "price", 0); int price = xml_ivalue(node, "price", 0);
if (itype->rtype->ltype) { if (itype->rtype->ltype) {
itype->rtype->ltype->price = price; itype->rtype->ltype->price = price;
return itype->rtype->ltype; return itype->rtype->ltype;
@ -508,9 +517,8 @@ static luxury_type *xml_readluxury(xmlXPathContextPtr xpath, item_type * itype)
return new_luxurytype(itype, price); return new_luxurytype(itype, price);
} }
static armor_type *xml_readarmor(xmlXPathContextPtr xpath, item_type * itype) static armor_type *xml_readarmor(xmlNodePtr node, item_type * itype)
{ {
xmlNodePtr node = xpath->node;
armor_type *atype = NULL; armor_type *atype = NULL;
unsigned int flags = ATF_NONE; unsigned int flags = ATF_NONE;
int ac = xml_ivalue(node, "ac", 0); int ac = xml_ivalue(node, "ac", 0);
@ -658,10 +666,9 @@ static weapon_type *xml_readweapon(xmlXPathContextPtr xpath, item_type * itype)
static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype) static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype)
{ {
xmlNodePtr node = xpath->node; xmlNodePtr child, node = xpath->node;
item_type *itype = NULL; item_type *itype = NULL;
unsigned int flags = ITF_NONE; unsigned int flags = ITF_NONE;
xmlXPathObjectPtr result;
if (xml_bvalue(node, "cursed", false)) if (xml_bvalue(node, "cursed", false))
flags |= ITF_CURSED; flags |= ITF_CURSED;
@ -687,63 +694,45 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype)
itype->weight = xml_ivalue(node, "weight", 0); itype->weight = xml_ivalue(node, "weight", 0);
itype->capacity = xml_ivalue(node, "capacity", 0); itype->capacity = xml_ivalue(node, "capacity", 0);
itype->score = xml_ivalue(node, "score", 0);
mask_races(node, "allow", &itype->mask_allow); mask_races(node, "allow", &itype->mask_allow);
mask_races(node, "deny", &itype->mask_deny); mask_races(node, "deny", &itype->mask_deny);
itype->flags |= flags; itype->flags |= flags;
/* reading item/construction */ for (child = node->children; child; child = child->next) {
xpath->node = node; if (strcmp((const char *)child->name, "construction") == 0) {
result = xmlXPathEvalExpression(BAD_CAST "construction", xpath); /* reading item/construction */
xml_readconstruction(xpath, result->nodesetval, &itype->construction, CONS_ITEM); assert(!itype->construction);
xmlXPathFreeObject(result); xpath->node = child;
itype->construction = xml_readconstruction(xpath, child);
/* reading item/weapon */ }
xpath->node = node; else if (strcmp((const char *)child->name, "weapon") == 0) {
result = xmlXPathEvalExpression(BAD_CAST "weapon", xpath); /* reading item/weapon */
assert(result->nodesetval->nodeNr <= 1); assert(!rtype->wtype);
if (result->nodesetval->nodeNr != 0) { xpath->node = child;
xpath->node = result->nodesetval->nodeTab[0]; rtype->wtype = xml_readweapon(xpath, itype);
rtype->wtype = xml_readweapon(xpath, itype); }
} else if (strcmp((const char *)child->name, "armor") == 0) {
xmlXPathFreeObject(result); /* reading item/weapon */
assert(!rtype->atype);
/* reading item/potion */ rtype->atype = xml_readarmor(child, itype);
xpath->node = node; }
result = xmlXPathEvalExpression(BAD_CAST "potion", xpath); else if (strcmp((const char *)child->name, "luxury") == 0) {
assert(result->nodesetval->nodeNr <= 1); /* reading item/luxury */
if (result->nodesetval->nodeNr != 0) { assert(!rtype->ltype);
if ((itype->flags & ITF_CANUSE) == 0) { rtype->ltype = xml_readluxury(child, itype);
log_error("potion %s has no use attribute", rtype->_name); }
itype->flags |= ITF_CANUSE; else if (strcmp((const char *)child->name, "potion") == 0) {
/* reading item/potion */
xml_readpotion(child, itype);
} }
xpath->node = result->nodesetval->nodeTab[0];
xml_readpotion(xpath, itype);
} }
xmlXPathFreeObject(result);
/* reading item/luxury */ if (!itype->score) {
xpath->node = node; /* do this last, because score depends on itype data */
result = xmlXPathEvalExpression(BAD_CAST "luxury", xpath); itype->score = default_score(itype);
assert(result->nodesetval->nodeNr <= 1);
if (result->nodesetval->nodeNr != 0) {
xpath->node = result->nodesetval->nodeTab[0];
rtype->ltype = xml_readluxury(xpath, itype);
} }
xmlXPathFreeObject(result);
/* reading item/armor */
xpath->node = node;
result = xmlXPathEvalExpression(BAD_CAST "armor", xpath);
assert(result->nodesetval->nodeNr <= 1);
if (result->nodesetval->nodeNr != 0) {
xpath->node = result->nodesetval->nodeTab[0];
rtype->atype = xml_readarmor(xpath, itype);
}
xmlXPathFreeObject(result);
itype->score = xml_ivalue(node, "score", 0);
if (!itype->score) itype->score = default_score(itype);
return itype; return itype;
} }