More read/write tests for town and outdoors, including maps/dialogue

Fixes:
- For towns, the "has tavern" flag was not saved or loaded
- Outdoor roads were not saved or loaded
- For outdoor encounters, the "can't flee" and "forced" flags were not loaded, and "forced" was not saved
--> These two flags have also been separated in the code
This commit is contained in:
2015-09-30 17:00:05 -04:00
parent 16a09988f3
commit 43e82281af
26 changed files with 576 additions and 20 deletions

View File

@@ -22,6 +22,7 @@
</xs:element> </xs:element>
</xs:sequence> </xs:sequence>
<xs:attribute name="can-flee" type="bool" default="true"/> <xs:attribute name="can-flee" type="bool" default="true"/>
<xs:attribute name="force" type="bool" default="false"/>
</xs:complexType> </xs:complexType>
<xs:element name="sector"> <xs:element name="sector">
<xs:complexType> <xs:complexType>

View File

@@ -95,6 +95,7 @@
<xs:element name="strong-barriers" minOccurs="0" type="bool"/> <xs:element name="strong-barriers" minOccurs="0" type="bool"/>
<xs:element name="defy-mapping" minOccurs="0" type="bool"/> <xs:element name="defy-mapping" minOccurs="0" type="bool"/>
<xs:element name="defy-scrying" minOccurs="0" type="bool"/> <xs:element name="defy-scrying" minOccurs="0" type="bool"/>
<xs:element name="tavern" minOccurs="0" type="bool"/>
</xs:all> </xs:all>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>

View File

@@ -1498,7 +1498,7 @@ void handle_monster_actions(bool& need_redraw, bool& need_reprint) {
create_wand_monst(); create_wand_monst();
for(int i = 0; i < 10; i++) for(int i = 0; i < 10; i++)
if(univ.party.out_c[i].exists) if(univ.party.out_c[i].exists)
if((adjacent(univ.party.p_loc,univ.party.out_c[i].m_loc) || univ.party.out_c[i].what_monst.cant_flee >= 10) if((adjacent(univ.party.p_loc,univ.party.out_c[i].m_loc) || univ.party.out_c[i].what_monst.forced)
&& univ.party.in_boat < 0 && !flying()) { && univ.party.in_boat < 0 && !flying()) {
store_wandering_special = univ.party.out_c[i].what_monst; store_wandering_special = univ.party.out_c[i].what_monst;
if(handle_wandering_specials(0,0)) if(handle_wandering_specials(0,0))
@@ -1584,7 +1584,7 @@ void initiate_outdoor_combat(short i) {
// Is combat too easy? // Is combat too easy?
if((univ.party.get_level() > ((out_enc_lev_tot(i) * 5) / 3) ) && (out_enc_lev_tot(i) < 200) if((univ.party.get_level() > ((out_enc_lev_tot(i) * 5) / 3) ) && (out_enc_lev_tot(i) < 200)
&& (univ.party.out_c[i].what_monst.cant_flee % 10 != 1)) { && !univ.party.out_c[i].what_monst.cant_flee) {
add_string_to_buf("Combat: Monsters fled!"); add_string_to_buf("Combat: Monsters fled!");
univ.party.out_c[i].exists = false; univ.party.out_c[i].exists = false;
return; return;

View File

@@ -117,6 +117,7 @@ cOutdoors::cOutdoors(cScenario& scenario) : scenario(&scenario) {
for(j = 0; j < 48; j++) { for(j = 0; j < 48; j++) {
terrain[i][j] = scenario.default_ground; terrain[i][j] = scenario.default_ground;
special_spot[i][j] = false; special_spot[i][j] = false;
roads[i][j] = false;
} }
for(i = 0; i < 4; i++) { for(i = 0; i < 4; i++) {
@@ -134,7 +135,8 @@ void cOutdoors::cWandering::append(legacy::out_wandering_type old){
spec_on_meet = old.spec_on_meet; spec_on_meet = old.spec_on_meet;
spec_on_win = old.spec_on_win; spec_on_win = old.spec_on_win;
spec_on_flee = old.spec_on_flee; spec_on_flee = old.spec_on_flee;
cant_flee = old.cant_flee; cant_flee = old.cant_flee % 10 == 1;
forced = old.cant_flee >= 10;
end_spec1 = old.end_spec1; end_spec1 = old.end_spec1;
end_spec2 = old.end_spec2; end_spec2 = old.end_spec2;
} }
@@ -157,7 +159,7 @@ void cOutdoors::cWandering::writeTo(std::ostream& file, std::string prefix) cons
file << prefix << "MEET " << spec_on_meet << '\n'; file << prefix << "MEET " << spec_on_meet << '\n';
file << prefix << "WIN " << spec_on_win << '\n'; file << prefix << "WIN " << spec_on_win << '\n';
file << prefix << "FLEE " << spec_on_flee << '\n'; file << prefix << "FLEE " << spec_on_flee << '\n';
file << prefix << "FLAGS " << cant_flee << '\n'; file << prefix << "FLAGS " << cant_flee << ' ' << forced << '\n';
file << prefix << "SDF " << end_spec1 << ' ' << end_spec2 << '\n'; file << prefix << "SDF " << end_spec1 << ' ' << end_spec2 << '\n';
} }
@@ -182,7 +184,7 @@ void cOutdoors::cWandering::readFrom(std::istream& file){
else if(cur == "FLEE") else if(cur == "FLEE")
sin >> spec_on_flee; sin >> spec_on_flee;
else if(cur == "FLAGS") else if(cur == "FLAGS")
sin >> cant_flee; sin >> cant_flee >> forced;
else if(cur == "SDF") else if(cur == "SDF")
sin >> end_spec1 >> end_spec2; sin >> end_spec1 >> end_spec2;
} }

View File

@@ -40,8 +40,9 @@ public:
public: public:
std::array<mon_num_t,7> monst; std::array<mon_num_t,7> monst;
std::array<mon_num_t,3> friendly; std::array<mon_num_t,3> friendly;
short spec_on_meet,spec_on_win,spec_on_flee,cant_flee; short spec_on_meet,spec_on_win,spec_on_flee;
short end_spec1,end_spec2; short end_spec1,end_spec2;
bool cant_flee, forced;
bool isNull(); bool isNull();
void append(legacy::out_wandering_type old); void append(legacy::out_wandering_type old);

View File

@@ -38,7 +38,7 @@ protected:
public: public:
class cWandering { // formerly wandering_type class cWandering { // formerly wandering_type
public: public:
mon_num_t monst[4]; std::array<mon_num_t,4> monst;
bool isNull(); bool isNull();
void append(legacy::wandering_type old); void append(legacy::wandering_type old);

View File

@@ -92,6 +92,7 @@ template<> void ticpp::Printer::PushElement(std::string tagName, cMonster::cAtta
template<> void ticpp::Printer::PushElement(std::string tagName, cOutdoors::cWandering enc, bool) { template<> void ticpp::Printer::PushElement(std::string tagName, cOutdoors::cWandering enc, bool) {
OpenElement(tagName); OpenElement(tagName);
PushAttribute("can-flee", !enc.cant_flee); PushAttribute("can-flee", !enc.cant_flee);
PushAttribute("force", enc.forced);
for(size_t i = 0; i < enc.monst.size(); i++) { for(size_t i = 0; i < enc.monst.size(); i++) {
PushElement("monster", enc.monst[i]); PushElement("monster", enc.monst[i]);
} }
@@ -719,6 +720,8 @@ void writeTownToXml(ticpp::Printer&& data, cTown& town) {
data.PushElement("defy-scrying", true); data.PushElement("defy-scrying", true);
if(town.is_hidden) if(town.is_hidden)
data.PushElement("hidden", true); data.PushElement("hidden", true);
if(town.has_tavern)
data.PushElement("tavern", true);
data.CloseElement("flags"); data.CloseElement("flags");
for(int i = 0; i < town.wandering.size(); i++) { for(int i = 0; i < town.wandering.size(); i++) {
if(town.wandering[i].isNull()) continue; if(town.wandering[i].isNull()) continue;
@@ -849,6 +852,8 @@ map_data buildOutMapData(location which, cScenario& scenario) {
terrain.set(x, y, sector.terrain[x][y]); terrain.set(x, y, sector.terrain[x][y]);
if(sector.special_spot[x][y]) if(sector.special_spot[x][y])
terrain.addFeature(x, y, eMapFeature::FIELD, SPECIAL_SPOT); terrain.addFeature(x, y, eMapFeature::FIELD, SPECIAL_SPOT);
if(sector.roads[x][y])
terrain.addFeature(x, y, eMapFeature::FIELD, SPECIAL_ROAD);
} }
} }
for(size_t i = 0; i < sector.special_locs.size(); i++) { for(size_t i = 0; i < sector.special_locs.size(); i++) {

View File

@@ -520,8 +520,8 @@ static void put_out_wand_in_dlog(cDialog& me, short which, const cOutdoors::cWan
// TODO: Wait a second, if 0 is no monster, does that mean it's impossible to use monster 0? Should 1 be subtracted here? // TODO: Wait a second, if 0 is no monster, does that mean it's impossible to use monster 0? Should 1 be subtracted here?
else me[id].setText(scenario.scen_monsters[wand.friendly[i]].m_name); else me[id].setText(scenario.scen_monsters[wand.friendly[i]].m_name);
} }
dynamic_cast<cLed&>(me["no-flee"]).setState(wand.cant_flee % 10 == 1 ? led_red : led_off); dynamic_cast<cLed&>(me["no-flee"]).setState(wand.cant_flee ? led_red : led_off);
dynamic_cast<cLed&>(me["forced"]).setState(wand.cant_flee >= 10 ? led_red : led_off); dynamic_cast<cLed&>(me["forced"]).setState(wand.forced ? led_red : led_off);
me["onmeet"].setTextToNum(wand.spec_on_meet); me["onmeet"].setTextToNum(wand.spec_on_meet);
me["onwin"].setTextToNum(wand.spec_on_win); me["onwin"].setTextToNum(wand.spec_on_win);
me["onflee"].setTextToNum(wand.spec_on_flee); me["onflee"].setTextToNum(wand.spec_on_flee);
@@ -536,11 +536,8 @@ static void save_out_wand(cDialog& me, short which, cOutdoors::cWandering& wand,
wand.end_spec1 = me["endx"].getTextAsNum(); wand.end_spec1 = me["endx"].getTextAsNum();
wand.end_spec2 = me["endy"].getTextAsNum(); wand.end_spec2 = me["endy"].getTextAsNum();
wand.cant_flee = 0; wand.forced = dynamic_cast<cLed&>(me["forced"]).getState() != led_off;
if(dynamic_cast<cLed&>(me["forced"]).getState() != led_off) wand.cant_flee = dynamic_cast<cLed&>(me["no-flee"]).getState() != led_off;
wand.cant_flee += 10;
if(dynamic_cast<cLed&>(me["no-flee"]).getState() != led_off)
wand.cant_flee += 1;
switch(mode) { switch(mode) {
case 0: case 0:

View File

@@ -1501,6 +1501,17 @@ void readOutdoorsFromXml(ticpp::Document&& data, cOutdoors& out) {
auto& enc_list = type == "encounter" ? out.special_enc : out.wandering; auto& enc_list = type == "encounter" ? out.special_enc : out.wandering;
int num_hostile = 0, num_friendly = 0; int num_hostile = 0, num_friendly = 0;
Iterator<Attribute> attr; Iterator<Attribute> attr;
for(attr = attr.begin(elem.Get()); attr != attr.end(); attr++) {
std::string name, strval;
attr->GetName(&name);
attr->GetValue(&strval);
bool val = strval == "true";
if(name == "can-flee")
enc_list[count].cant_flee = !val;
else if(name == "force")
enc_list[count].forced = val;
else throw xBadAttr(type, name, attr->Row(), attr->Column(), fname);
}
Iterator<Element> enc; Iterator<Element> enc;
for(enc = enc.begin(elem.Get()); enc != enc.end(); enc++) { for(enc = enc.begin(elem.Get()); enc != enc.end(); enc++) {
std::string type; std::string type;
@@ -1656,6 +1667,10 @@ void readTownFromXml(ticpp::Document&& data, cTown*& town, cScenario& scen) {
flag->GetText(&val); flag->GetText(&val);
if(val == "true") if(val == "true")
town->defy_scrying = true; town->defy_scrying = true;
} else if(type == "tavern") {
flag->GetText(&val);
if(val == "true")
town->has_tavern = true;
} else throw xBadNode(type, flag->Row(), flag->Column(), fname); } else throw xBadNode(type, flag->Row(), flag->Column(), fname);
} }
} else if(type == "wandering") { } else if(type == "wandering") {
@@ -1891,6 +1906,8 @@ void loadOutMapData(map_data&& data, location which, cScenario& scen) {
case eMapFeature::FIELD: case eMapFeature::FIELD:
if(feat.second == SPECIAL_SPOT) if(feat.second == SPECIAL_SPOT)
out.special_spot[x][y] = true; out.special_spot[x][y] = true;
else if(feat.second == SPECIAL_ROAD)
out.roads[x][y] = true;
else throw xMapParseError(map_out_bad_field, feat.second, y, x, data.file); else throw xMapParseError(map_out_bad_field, feat.second, y, x, data.file);
break; break;
case eMapFeature::SIGN: case eMapFeature::SIGN:

View File

@@ -3,4 +3,5 @@
0&9,0&10,0&11,0&12 0&9,0&10,0&11,0&12
0&13,0&14,0&15,0&16 0&13,0&14,0&15,0&16
0&17,0&18,0&19,0&20 0&17,0&18,0&19,0&20
0&21,0&22,0&23,0&24 0&21,0&22,0&23,0&24
0&25

View File

@@ -0,0 +1,5 @@
1,2,3,4,5
6,7,8,9,10
11,12,13&9,14,15
16,17,18&25,19,20
21,22,23&25,24,25

View File

@@ -1,5 +1,3 @@
<sector boes="2.0.0"> <sector boes="2.0.0">
<encounter> <encounter bad='no'/>
<monster human='false'/>
</encounter>
</sector> </sector>

View File

@@ -0,0 +1,5 @@
<sector boes="2.0.0">
<encounter>
<monster human='false'/>
</encounter>
</sector>

View File

@@ -0,0 +1,26 @@
<sector boes="2.0.0">
<name>Test Sector</name>
<comment>Hello World!</comment>
<sound>birds</sound>
<encounter can-flee='false' force='true'>
<monster>12</monster>
<monster friendly='true'>15</monster>
<onmeet>90</onmeet>
<onwin>92</onwin>
<onflee>84</onflee>
<sdf x='202' y='19'/>
</encounter>
<wandering can-flee='false' force='true'>
<monster>12</monster>
<monster friendly='true'>15</monster>
<onmeet>90</onmeet>
<onwin>92</onwin>
<onflee>84</onflee>
<sdf x='202' y='19'/>
</wandering>
<sign id='7'>The best sign ever!</sign>
<area top='4' left='8' bottom='9' right='12'>
<![CDATA[Some random area Amazing!]]>
</area>
<string id='9'>A random special string</string>
</sector>

View File

@@ -0,0 +1,4 @@
<sector boes="2.0.0">
<name>Test Sector</name>
<sound>42</sound>
</sector>

37
test/files/talk/full.xml Normal file
View File

@@ -0,0 +1,37 @@
<dialogue boes="2.0.0">
<personality id='0'>
<title>Billy Kumquat</title>
<look>You see a small boy juggling fruit.</look>
<name>"I'm Billy Kumquat!"</name>
<job>"I juggle!"</job>
<unknown>"No clue!"</unknown>
</personality>
<node for='0'>
<keyword>jugg</keyword>
<type>reg</type>
<text>"It's fun!"</text>
</node>
<node for='0'>
<keyword>info</keyword>
<type>buy-sdf</type>
<param>1</param>
<param>2</param>
<param>3</param>
<text>"There's a treasure hidden in the old tree!"</text>
<text>"For a gold piece I'll tell you something interesting!"</text>
</node>
<node for='0'>
<keyword>purc</keyword>
<type>shop</type>
<param>0</param>
<param>4</param>
<text>Billy Kumquat's Oddest Fruit</text>
</node>
<node for='0'>
<keyword>kumq</keyword>
<type>quest</type>
<param>5</param>
<text>"I'll reward you if you find me some bigger kumquats!"</text>
<text>"Thanks for finding the bigger kumquats!"</text>
</node>
</dialogue>

View File

@@ -0,0 +1,6 @@
<town boes="2.0.0">
<size>32</size>
<name>Hello World</name>
<onenter condition='alive'>1</onenter>
<onenter condition='alive'>2</onenter>
</town>

56
test/files/town/full.xml Normal file
View File

@@ -0,0 +1,56 @@
<town boes="2.0.0">
<size>32</size>
<name>Test Town</name>
<comment>This is a silly little comment.</comment>
<bounds top='4' left='4' right='28' bottom='28'/>
<difficulty>1</difficulty>
<lighting>lit</lighting>
<onenter condition='alive'>12</onenter>
<onenter condition='dead'>13</onenter>
<exit dir='n' x='4' y='16'/>
<onexit dir='n'>52</onexit>
<onoffend>42</onoffend>
<timer freq='100'>15</timer>
<flags>
<chop day='18' event='4' kills='50000'/>
<hidden>true</hidden>
<strong-barriers>true</strong-barriers>
<defy-mapping>true</defy-mapping>
<defy-scrying>true</defy-scrying>
<tavern>true</tavern>
</flags>
<wandering>
<monster>40</monster>
<monster>41</monster>
<monster>42</monster>
<monster>43</monster>
</wandering>
<sign id='1'>This is a sample sign.</sign>
<string id='7'>Here is a town string.</string>
<item id='2'>
<type>120</type>
<mod>2</mod>
<charges>17</charges>
<always>true</always>
<property>true</property>
<contained>true</contained>
</item>
<creature id='12'>
<type>140</type>
<attitude>hostile-b</attitude>
<mobility>1</mobility>
<sdf x='12' y='13'/>
<encounter>50</encounter>
<time type='after-event'>
<day>17</day>
<event>14</event>
</time>
<face>142</face>
<personality>1</personality>
<onkill>80</onkill>
<ontalk>81</ontalk>
</creature>
<area top='14' left='16' bottom='20' right='22'>
<![CDATA[This is a sample area description.]]>
</area>
</town>

View File

@@ -98,6 +98,17 @@ TEST_CASE("Loading map data from file") {
CHECK(map.getFeatures(2, 5) == test); CHECK(map.getFeatures(2, 5) == test);
test[0].second = BARRIER_CAGE; test[0].second = BARRIER_CAGE;
CHECK(map.getFeatures(3, 5) == test); CHECK(map.getFeatures(3, 5) == test);
test[0].second = SPECIAL_ROAD;
CHECK(map.getFeatures(0, 6) == test);
}
SECTION("With fields outdoors") {
fin.open("files/maps/fields_out.map");
map = load_map(fin, false, "fields_out.map");
test.emplace_back(make_pair(eMapFeature::FIELD, SPECIAL_SPOT));
CHECK(map.getFeatures(2, 2) == test);
test[0].second = SPECIAL_ROAD;
CHECK(map.getFeatures(2, 3) == test);
CHECK(map.getFeatures(2, 4) == test);
} }
SECTION("With town entrance") { SECTION("With town entrance") {
fin.open("files/maps/towns_out.map"); fin.open("files/maps/towns_out.map");
@@ -215,7 +226,6 @@ TEST_CASE("Interpreting loaded map data") {
fin.open("files/maps/fields.map"); fin.open("files/maps/fields.map");
map = load_map(fin, true, "fields.map"); map = load_map(fin, true, "fields.map");
loadTownMapData(move(map), 0, scen); loadTownMapData(move(map), 0, scen);
REQUIRE(scen.towns[0]->preset_fields.size() == 24);
static const std::map<eFieldType, location> check = { static const std::map<eFieldType, location> check = {
{WALL_FORCE, {0,0}}, {WALL_FIRE, {1,0}}, {FIELD_ANTIMAGIC, {2,0}}, {CLOUD_STINK, {3,0}}, {WALL_FORCE, {0,0}}, {WALL_FIRE, {1,0}}, {FIELD_ANTIMAGIC, {2,0}}, {CLOUD_STINK, {3,0}},
{WALL_ICE, {0,1}}, {WALL_BLADES, {1,1}}, {CLOUD_SLEEP, {2,1}}, {OBJECT_BLOCK, {3,1}}, {WALL_ICE, {0,1}}, {WALL_BLADES, {1,1}}, {CLOUD_SLEEP, {2,1}}, {OBJECT_BLOCK, {3,1}},
@@ -223,7 +233,10 @@ TEST_CASE("Interpreting loaded map data") {
{BARRIER_FIRE, {0,3}}, {BARRIER_FORCE, {1,3}}, {FIELD_QUICKFIRE, {2,3}}, {SFX_SMALL_BLOOD, {3,3}}, {BARRIER_FIRE, {0,3}}, {BARRIER_FORCE, {1,3}}, {FIELD_QUICKFIRE, {2,3}}, {SFX_SMALL_BLOOD, {3,3}},
{SFX_MEDIUM_BLOOD, {0,4}}, {SFX_LARGE_BLOOD, {1,4}}, {SFX_SMALL_SLIME, {2,4}}, {SFX_LARGE_SLIME, {3,4}}, {SFX_MEDIUM_BLOOD, {0,4}}, {SFX_LARGE_BLOOD, {1,4}}, {SFX_SMALL_SLIME, {2,4}}, {SFX_LARGE_SLIME, {3,4}},
{SFX_ASH, {0,5}}, {SFX_BONES, {1,5}}, {SFX_RUBBLE, {2,5}}, {BARRIER_CAGE, {3,5}}, {SFX_ASH, {0,5}}, {SFX_BONES, {1,5}}, {SFX_RUBBLE, {2,5}}, {BARRIER_CAGE, {3,5}},
{SPECIAL_ROAD, {0,6}},
}; };
CAPTURE(check.size());
REQUIRE(scen.towns[0]->preset_fields.size() == check.size());
set<eFieldType> found; set<eFieldType> found;
for(const auto& fld : scen.towns[0]->preset_fields) { for(const auto& fld : scen.towns[0]->preset_fields) {
if(found.count(fld.type)) if(found.count(fld.type))
@@ -237,6 +250,14 @@ TEST_CASE("Interpreting loaded map data") {
if(found.size() != check.size()) if(found.size() != check.size())
FAIL("Error: A field is missing!"); FAIL("Error: A field is missing!");
} }
SECTION("With fields outdoors") {
fin.open("files/maps/fields_out.map");
map = load_map(fin, false, "fields_out.map");
loadOutMapData(move(map), loc(0,0), scen);
CHECK(scen.outdoors[0][0]->special_spot[2][2]);
CHECK(scen.outdoors[0][0]->roads[2][3]);
CHECK(scen.outdoors[0][0]->roads[2][4]);
}
SECTION("With town entrance") { SECTION("With town entrance") {
fin.open("files/maps/towns_out.map"); fin.open("files/maps/towns_out.map");
map = load_map(fin, false, "towns_out.map"); map = load_map(fin, false, "towns_out.map");

View File

@@ -106,6 +106,15 @@ TEST_CASE("Saving map data to file") {
test[0].second = OBJECT_BLOCK; test[0].second = OBJECT_BLOCK;
CHECK(map.getFeatures(3, 3) == test); CHECK(map.getFeatures(3, 3) == test);
} }
SECTION("With fields outdoors") {
map.addFeature(0, 0, eMapFeature::FIELD, SPECIAL_SPOT);
map.addFeature(1, 1, eMapFeature::FIELD, SPECIAL_ROAD);
in_and_out("fields outdoors", map, false);
test.emplace_back(make_pair(eMapFeature::FIELD, SPECIAL_SPOT));
CHECK(map.getFeatures(0, 0) == test);
test[0].second = SPECIAL_ROAD;
CHECK(map.getFeatures(1, 1) == test);
}
SECTION("With town entrance") { SECTION("With town entrance") {
map.addFeature(0, 0, eMapFeature::TOWN, 4); map.addFeature(0, 0, eMapFeature::TOWN, 4);
in_and_out("town entry loc", map, false); in_and_out("town entry loc", map, false);
@@ -249,6 +258,13 @@ TEST_CASE("Building map data") {
if(found.size() != check.size()) if(found.size() != check.size())
FAIL("Error: A field is missing!"); FAIL("Error: A field is missing!");
} }
SECTION("With fields outdoors") {
scen.outdoors[0][0]->special_spot[0][0] = true;
scen.outdoors[0][0]->roads[1][1] = true;
in_and_out(scen, false);
CHECK(scen.outdoors[0][0]->special_spot[0][0]);
CHECK(scen.outdoors[0][0]->roads[1][1]);
}
SECTION("With town entrance") { SECTION("With town entrance") {
scen.outdoors[0][0]->city_locs.emplace_back(5, 6, 7); scen.outdoors[0][0]->city_locs.emplace_back(5, 6, 7);
in_and_out(scen, false); in_and_out(scen, false);

View File

@@ -19,6 +19,9 @@ using namespace ticpp;
extern Document xmlDocFromStream(istream& stream, string name); extern Document xmlDocFromStream(istream& stream, string name);
extern void readOutdoorsFromXml(Document&& data, cOutdoors& out); extern void readOutdoorsFromXml(Document&& data, cOutdoors& out);
bool operator==(const cOutdoors::cWandering& lhs, const cOutdoors::cWandering& rhs);
ostream& operator<<(ostream& out, const cOutdoors::cWandering& enc);
TEST_CASE("Loading an outdoor section definition") { TEST_CASE("Loading an outdoor section definition") {
ifstream fin; ifstream fin;
cScenario scen; cScenario scen;
@@ -66,11 +69,16 @@ TEST_CASE("Loading an outdoor section definition") {
doc = xmlDocFromStream(fin, "encounter_too_many_monst.xml"); doc = xmlDocFromStream(fin, "encounter_too_many_monst.xml");
REQUIRE_THROWS_AS(readOutdoorsFromXml(move(doc), sector), xBadNode); REQUIRE_THROWS_AS(readOutdoorsFromXml(move(doc), sector), xBadNode);
} }
SECTION("When an encounter monster has an invalid attribute") { SECTION("When an encounter has an invalid attribute") {
fin.open("files/outdoor/encounter_bad_attr.xml"); fin.open("files/outdoor/encounter_bad_attr.xml");
doc = xmlDocFromStream(fin, "encounter_bad_attr.xml"); doc = xmlDocFromStream(fin, "encounter_bad_attr.xml");
REQUIRE_THROWS_AS(readOutdoorsFromXml(move(doc), sector), xBadAttr); REQUIRE_THROWS_AS(readOutdoorsFromXml(move(doc), sector), xBadAttr);
} }
SECTION("When an encounter monster has an invalid attribute") {
fin.open("files/outdoor/encounter_bad_monst_attr.xml");
doc = xmlDocFromStream(fin, "encounter_bad_monst_attr.xml");
REQUIRE_THROWS_AS(readOutdoorsFromXml(move(doc), sector), xBadAttr);
}
SECTION("When there are too many encounters") { SECTION("When there are too many encounters") {
fin.open("files/outdoor/encounter_too_many.xml"); fin.open("files/outdoor/encounter_too_many.xml");
doc = xmlDocFromStream(fin, "encounter_too_many.xml"); doc = xmlDocFromStream(fin, "encounter_too_many.xml");
@@ -82,4 +90,66 @@ TEST_CASE("Loading an outdoor section definition") {
REQUIRE_NOTHROW(readOutdoorsFromXml(move(doc), sector)); REQUIRE_NOTHROW(readOutdoorsFromXml(move(doc), sector));
CHECK(sector.out_name == "Test Sector"); CHECK(sector.out_name == "Test Sector");
} }
SECTION("With an arbitrary ambient sound") {
fin.open("files/outdoor/sound.xml");
doc = xmlDocFromStream(fin, "sound.xml");
REQUIRE_NOTHROW(readOutdoorsFromXml(move(doc), sector));
CHECK(sector.ambient_sound == AMBIENT_CUSTOM);
CHECK(sector.out_sound == 42);
}
SECTION("With all possible data") {
cOutdoors::cWandering refEncounter;
refEncounter.monst[0] = 12;
refEncounter.friendly[0] = 15;
refEncounter.spec_on_meet = 90;
refEncounter.spec_on_win = 92;
refEncounter.spec_on_flee = 84;
refEncounter.end_spec1 = 202;
refEncounter.end_spec2 = 19;
refEncounter.cant_flee = true;
refEncounter.forced = true;
fin.open("files/outdoor/full.xml");
doc = xmlDocFromStream(fin, "full.xml");
REQUIRE_NOTHROW(readOutdoorsFromXml(move(doc), sector));
CHECK(sector.comment == "Hello World!");
CHECK(sector.ambient_sound == AMBIENT_BIRD);
CHECK(sector.special_enc[0] == refEncounter);
CHECK(sector.wandering[0] == refEncounter);
REQUIRE(sector.sign_locs.size() >= 8);
CHECK(sector.sign_locs[7].text == "The best sign ever!");
REQUIRE(sector.info_rect.size() >= 1);
CHECK(sector.info_rect[0].descr == "Some random area Amazing!");
REQUIRE(sector.spec_strs.size() >= 10);
CHECK(sector.spec_strs[9] == "A random special string");
}
}
bool operator==(const cOutdoors::cWandering& lhs, const cOutdoors::cWandering& rhs) {
if(lhs.monst != rhs.monst) return false;
if(lhs.friendly != rhs.friendly) return false;
if(lhs.spec_on_meet != rhs.spec_on_meet) return false;
if(lhs.spec_on_win != rhs.spec_on_win) return false;
if(lhs.spec_on_flee != rhs.spec_on_flee) return false;
if(lhs.cant_flee != rhs.cant_flee) return false;
if(lhs.forced != rhs.forced) return false;
if(lhs.end_spec1 != rhs.end_spec1) return false;
if(lhs.end_spec2 != rhs.end_spec2) return false;
return true;
}
ostream& operator<<(ostream& out, const cOutdoors::cWandering& enc) {
out << "Encounter {";
out << "hostile = ";
for(auto i : enc.monst)
out << i << ' ';
out << ", friendly = ";
for(auto i : enc.friendly)
out << i << ' ';
out << ", on-meet = " << enc.spec_on_meet << " , ";
out << "on-win = " << enc.spec_on_win << " , ";
out << "on-flee = " << enc.spec_on_flee << " , ";
out << "can-flee = " << !enc.cant_flee << " , ";
out << "forced = " << enc.forced << " , ";
out << "sdf = (" << enc.end_spec1 << ',' << enc.end_spec2 << ")}";
return out;
} }

View File

@@ -19,6 +19,9 @@ extern Document xmlDocFromStream(istream& stream, string name);
extern void readOutdoorsFromXml(Document&& data, cOutdoors& out); extern void readOutdoorsFromXml(Document&& data, cOutdoors& out);
extern void writeOutdoorsToXml(ticpp::Printer&& data, cOutdoors& sector); extern void writeOutdoorsToXml(ticpp::Printer&& data, cOutdoors& sector);
extern bool operator==(const cOutdoors::cWandering& lhs, const cOutdoors::cWandering& rhs);
extern ostream& operator<<(ostream& out, const cOutdoors::cWandering& enc);
static void in_and_out(string name, cOutdoors& out, cScenario& scen) { static void in_and_out(string name, cOutdoors& out, cScenario& scen) {
string fpath = "junk/out_"; string fpath = "junk/out_";
fpath += name; fpath += name;
@@ -45,4 +48,47 @@ TEST_CASE("Saving an outdoors sector") {
in_and_out("basic", out, scen); in_and_out("basic", out, scen);
CHECK(out.out_name == "The Outdoors Test"); CHECK(out.out_name == "The Outdoors Test");
} }
SECTION("With some optional information") {
out.comment = "Let's make a comment about comments.";
out.ambient_sound = AMBIENT_DRIP;
out.sign_locs.emplace_back(0,0,"Guantanamo - 14 mi.");
out.info_rect.emplace_back(0,0,1,1,"The heart of the wilderness");
out.spec_strs.emplace_back("Something happened!");
in_and_out("optional", out, scen);
CHECK(out.comment == "Let's make a comment about comments.");
CHECK(out.ambient_sound == AMBIENT_DRIP);
REQUIRE(out.sign_locs.size() >= 1);
CHECK(out.sign_locs[0].text == "Guantanamo - 14 mi.");
REQUIRE(out.info_rect.size() >= 1);
CHECK(out.info_rect[0].descr == "The heart of the wilderness");
REQUIRE(out.spec_strs.size() >= 1);
CHECK(out.spec_strs[0] == "Something happened!");
}
SECTION("With some encounters") {
cOutdoors::cWandering spec, wand;
spec.monst[3] = 42;
spec.monst[5] = 12;
spec.spec_on_meet = 15;
spec.spec_on_win = 12;
spec.spec_on_flee = 9;
spec.cant_flee = true;
spec.forced = true;
wand.monst[2] = 80;
wand.monst[6] = 90;
wand.friendly[1] = 12;
wand.end_spec1 = 210;
wand.end_spec2 = 22;
REQUIRE(out.special_enc.size() >= 1);
out.special_enc[0] = spec;
REQUIRE(out.wandering.size() >= 1);
out.wandering[0] = wand;
in_and_out("encounters", out, scen);
REQUIRE(out.special_enc.size() >= 1);
CHECK(out.special_enc[0] == spec);
REQUIRE(out.wandering.size() >= 1);
CHECK(out.wandering[0] == wand);
}
} }

View File

@@ -121,4 +121,35 @@ TEST_CASE("Loading a town dialogue definition") {
CHECK(talk.people[0].name == "\"I'm Billy Kumquat!\""); CHECK(talk.people[0].name == "\"I'm Billy Kumquat!\"");
CHECK(talk.people[0].job == "\"I juggle!\""); CHECK(talk.people[0].job == "\"I juggle!\"");
} }
SECTION("A full personality with several nodes") {
fin.open("files/talk/full.xml");
doc = xmlDocFromStream(fin, "full.xml");
REQUIRE_NOTHROW(readDialogueFromXml(move(doc), talk, 0));
CHECK(talk.people[0].dunno == "\"No clue!\"");
REQUIRE(talk.talk_nodes.size() == 4);
CHECK(talk.talk_nodes[0].personality == 0);
CHECK(string(talk.talk_nodes[0].link1, 4) == "jugg");
CHECK(talk.talk_nodes[0].type == eTalkNode::REGULAR);
CHECK(talk.talk_nodes[0].str1 == "\"It's fun!\"");
CHECK(talk.talk_nodes[1].personality == 0);
CHECK(string(talk.talk_nodes[1].link1, 4) == "info");
CHECK(talk.talk_nodes[1].type == eTalkNode::BUY_SDF);
CHECK(talk.talk_nodes[1].extras[0] == 1);
CHECK(talk.talk_nodes[1].extras[1] == 2);
CHECK(talk.talk_nodes[1].extras[2] == 3);
CHECK(talk.talk_nodes[1].str1 == "\"There's a treasure hidden in the old tree!\"");
CHECK(talk.talk_nodes[1].str2 == "\"For a gold piece I'll tell you something interesting!\"");
CHECK(talk.talk_nodes[2].personality == 0);
CHECK(string(talk.talk_nodes[2].link1, 4) == "purc");
CHECK(talk.talk_nodes[2].type == eTalkNode::SHOP);
CHECK(talk.talk_nodes[2].extras[0] == 0);
CHECK(talk.talk_nodes[2].extras[1] == 4);
CHECK(talk.talk_nodes[2].str1 == "Billy Kumquat's Oddest Fruit");
CHECK(talk.talk_nodes[3].personality == 0);
CHECK(string(talk.talk_nodes[3].link1, 4) == "kumq");
CHECK(talk.talk_nodes[3].type == eTalkNode::RECEIVE_QUEST);
CHECK(talk.talk_nodes[3].extras[0] == 5);
CHECK(talk.talk_nodes[3].str1 == "\"I'll reward you if you find me some bigger kumquats!\"");
CHECK(talk.talk_nodes[3].str2 == "\"Thanks for finding the bigger kumquats!\"");
}
} }

View File

@@ -49,5 +49,48 @@ TEST_CASE("Saving dialogue") {
CHECK(talk.people[0].name == "\"My name is John Smith. How do you do?\""); CHECK(talk.people[0].name == "\"My name is John Smith. How do you do?\"");
CHECK(talk.people[0].job == "\"Isn't it obvious? I test things!\""); CHECK(talk.people[0].job == "\"Isn't it obvious? I test things!\"");
} }
SECTION("With some nodes") {
talk.people[0].dunno = "\"I have no idea.\"";
talk.talk_nodes.resize(4);
strncpy(talk.talk_nodes[0].link1, "sell", 4);
talk.talk_nodes[0].type = eTalkNode::SELL_ITEMS;
talk.talk_nodes[0].str1 = "\"Ah, you have unwanted items? Never fear, I can take those off your hands!\"";
strncpy(talk.talk_nodes[1].link1, "iden", 4);
talk.talk_nodes[1].type = eTalkNode::IDENTIFY;
talk.talk_nodes[1].extras[0] = 10;
talk.talk_nodes[1].str1 = "\"Yes, I can identify your items for a mere 10 gold per item!\"";
strncpy(talk.talk_nodes[2].link1, "test", 4);
talk.talk_nodes[2].type = eTalkNode::RECEIVE_QUEST;
talk.talk_nodes[2].extras[0] = 4;
talk.talk_nodes[2].str1 = "\"Yes! In fact, you can help me to test things!\"";
talk.talk_nodes[2].str2 = "\"Thanks for the help!\"";
strncpy(talk.talk_nodes[3].link1, "boat", 4);
talk.talk_nodes[3].type = eTalkNode::BUY_SHIP;
talk.talk_nodes[3].extras[0] = 0;
talk.talk_nodes[3].extras[1] = 5;
talk.talk_nodes[3].extras[2] = 1;
talk.talk_nodes[3].str1 = "\"You need a boat? Then you are in luck! I just happen to have a boat!\"";
in_and_out("full", talk);
CHECK(talk.people[0].dunno == "\"I have no idea.\"");
REQUIRE(talk.talk_nodes.size() == 4);
CHECK(string(talk.talk_nodes[0].link1, 4) == "sell");
CHECK(talk.talk_nodes[0].type == eTalkNode::SELL_ITEMS);
CHECK(talk.talk_nodes[0].str1 == "\"Ah, you have unwanted items? Never fear, I can take those off your hands!\"");
CHECK(string(talk.talk_nodes[1].link1, 4) == "iden");
CHECK(talk.talk_nodes[1].type == eTalkNode::IDENTIFY);
CHECK(talk.talk_nodes[1].extras[0] == 10);
CHECK(talk.talk_nodes[1].str1 == "\"Yes, I can identify your items for a mere 10 gold per item!\"");
CHECK(string(talk.talk_nodes[2].link1, 4) == "test");
CHECK(talk.talk_nodes[2].type == eTalkNode::RECEIVE_QUEST);
CHECK(talk.talk_nodes[2].extras[0] == 4);
CHECK(talk.talk_nodes[2].str1 == "\"Yes! In fact, you can help me to test things!\"");
CHECK(talk.talk_nodes[2].str2 == "\"Thanks for the help!\"");
CHECK(string(talk.talk_nodes[3].link1, 4) == "boat");
CHECK(talk.talk_nodes[3].type == eTalkNode::BUY_SHIP);
CHECK(talk.talk_nodes[3].extras[0] == 0);
CHECK(talk.talk_nodes[3].extras[1] == 5);
CHECK(talk.talk_nodes[3].extras[2] == 1);
CHECK(talk.talk_nodes[3].str1 == "\"You need a boat? Then you are in luck! I just happen to have a boat!\"");
}
} }

View File

@@ -51,6 +51,11 @@ TEST_CASE("Loading a town definition") {
doc = xmlDocFromStream(fin, "size_not_first.xml"); doc = xmlDocFromStream(fin, "size_not_first.xml");
REQUIRE_THROWS_AS(readTownFromXml(move(doc), town, scen), xBadNode); REQUIRE_THROWS_AS(readTownFromXml(move(doc), town, scen), xBadNode);
} }
SECTION("When the size is invalid") {
fin.open("files/town/bad_size.xml");
doc = xmlDocFromStream(fin, "bad_size.xml");
REQUIRE_THROWS_AS(readTownFromXml(move(doc), town, scen), xBadVal);
}
SECTION("When there is an invalid toplevel node") { SECTION("When there is an invalid toplevel node") {
fin.open("files/town/bad_toplevel.xml"); fin.open("files/town/bad_toplevel.xml");
doc = xmlDocFromStream(fin, "bad_toplevel.xml"); doc = xmlDocFromStream(fin, "bad_toplevel.xml");
@@ -66,6 +71,11 @@ TEST_CASE("Loading a town definition") {
doc = xmlDocFromStream(fin, "bad_onenter_condition.xml"); doc = xmlDocFromStream(fin, "bad_onenter_condition.xml");
REQUIRE_THROWS_AS(readTownFromXml(move(doc), town, scen), xBadVal); REQUIRE_THROWS_AS(readTownFromXml(move(doc), town, scen), xBadVal);
} }
SECTION("When the onenter condition is duplicated") {
fin.open("files/town/dup_onenter_condition.xml");
doc = xmlDocFromStream(fin, "dup_onenter_condition.xml");
REQUIRE_THROWS_AS(readTownFromXml(move(doc), town, scen), xBadVal);
}
SECTION("When the onexit direction is invalid") { SECTION("When the onexit direction is invalid") {
fin.open("files/town/bad_onexit_dir.xml"); fin.open("files/town/bad_onexit_dir.xml");
doc = xmlDocFromStream(fin, "bad_onexit_dir.xml"); doc = xmlDocFromStream(fin, "bad_onexit_dir.xml");
@@ -126,6 +136,61 @@ TEST_CASE("Loading a town definition") {
CHECK(town->difficulty == 1); CHECK(town->difficulty == 1);
CHECK(town->lighting_type == LIGHT_NORMAL); CHECK(town->lighting_type == LIGHT_NORMAL);
} }
SECTION("With all possible data") {
fin.open("files/town/full.xml");
doc = xmlDocFromStream(fin, "full.xml");
REQUIRE_NOTHROW(readTownFromXml(move(doc), town, scen));
CHECK(town->comment[0] == "This is a silly little comment.");
CHECK(town->spec_on_entry == 12);
CHECK(town->spec_on_entry_if_dead == 13);
CHECK(town->exit_locs[0] == loc(4,16));
CHECK(town->exit_specs[0] == 52);
CHECK(town->spec_on_hostile == 42);
CHECK(town->town_chop_time == 18);
CHECK(town->town_chop_key == 4);
CHECK(town->max_num_monst == 50000);
CHECK(town->is_hidden);
CHECK(town->strong_barriers);
CHECK(town->has_tavern);
CHECK(town->defy_mapping);
CHECK(town->defy_scrying);
REQUIRE(town->timers.size() >= 1);
CHECK(town->timers[0].node_type == 2);
CHECK(town->timers[0].node == 15);
CHECK(town->timers[0].time == 100);
REQUIRE(town->wandering.size() >= 1);
CHECK(town->wandering[0].monst[0] == 40);
CHECK(town->wandering[0].monst[1] == 41);
CHECK(town->wandering[0].monst[2] == 42);
CHECK(town->wandering[0].monst[3] == 43);
REQUIRE(town->sign_locs.size() >= 2);
CHECK(town->sign_locs[1].text == "This is a sample sign.");
REQUIRE(town->spec_strs.size() >= 8);
CHECK(town->spec_strs[7] == "Here is a town string.");
REQUIRE(town->preset_items.size() >= 3);
CHECK(town->preset_items[2].code == 120);
CHECK(town->preset_items[2].ability == 2);
CHECK(town->preset_items[2].charges == 17);
CHECK(town->preset_items[2].always_there);
CHECK(town->preset_items[2].property);
CHECK(town->preset_items[2].contained);
REQUIRE(town->creatures.size() >= 13);
CHECK(town->creatures[12].number == 140);
CHECK(town->creatures[12].start_attitude == eAttitude::HOSTILE_B);
CHECK(town->creatures[12].mobility == 1);
CHECK(town->creatures[12].spec1 == 12);
CHECK(town->creatures[12].spec2 == 13);
CHECK(town->creatures[12].spec_enc_code == 50);
CHECK(town->creatures[12].time_flag == eMonstTime::APPEAR_WHEN_EVENT);
CHECK(town->creatures[12].monster_time == 17);
CHECK(town->creatures[12].time_code == 14);
CHECK(town->creatures[12].facial_pic == 142);
CHECK(town->creatures[12].personality == 1);
CHECK(town->creatures[12].special_on_kill == 80);
CHECK(town->creatures[12].special_on_talk == 81);
REQUIRE(town->room_rect.size() >= 1);
CHECK(town->room_rect[0].descr == "This is a sample area description.");
}
delete town; delete town;
} }

View File

@@ -50,5 +50,107 @@ TEST_CASE("Saving a town") {
CHECK(town->in_town_rect == rect(2,3,30,29)); CHECK(town->in_town_rect == rect(2,3,30,29));
CHECK(town->difficulty == 1); CHECK(town->difficulty == 1);
CHECK(town->lighting_type == LIGHT_NONE); CHECK(town->lighting_type == LIGHT_NONE);
CHECK_FALSE(town->has_tavern);
CHECK_FALSE(town->defy_scrying);
}
SECTION("With lots of optional information") {
town->comment[2] = "Try a comment!";
town->spec_on_entry = 42;
town->spec_on_entry_if_dead = 19;
town->spec_on_hostile = 47;
town->exit_specs[0] = 16;
town->exit_locs[0] = {24,2};
town->town_chop_time = 25;
town->town_chop_key = 6;
town->max_num_monst = 100000;
town->is_hidden = town->has_tavern = true;
town->defy_scrying = town->defy_mapping = true;
town->strong_barriers = true;
REQUIRE(town->timers.size() >= 1);
town->timers[0].node = 12;
town->timers[0].time = 2500;
REQUIRE(town->wandering.size() >= 1);
town->wandering[0].monst = {7,8,9,10};
town->sign_locs.emplace_back(0,0,"Sign #4279816");
town->spec_strs.emplace_back("Something! With extra spaces!");
town->room_rect.emplace_back(0,0,1,1,"Unknown Area . . .");
in_and_out("optional", town, scen);
CHECK(town->comment[2] == "Try a comment!");
CHECK(town->spec_on_entry == 42);
CHECK(town->spec_on_entry_if_dead == 19);
CHECK(town->spec_on_hostile == 47);
CHECK(town->exit_specs[0] == 16);
CHECK(town->exit_locs[0] == loc(24,2));
CHECK(town->town_chop_time == 25);
CHECK(town->town_chop_key == 6);
CHECK(town->max_num_monst == 100000);
CHECK(town->is_hidden);
CHECK(town->strong_barriers);
CHECK(town->has_tavern);
CHECK(town->defy_mapping);
CHECK(town->defy_scrying);
REQUIRE(town->timers.size() >= 1);
CHECK(town->timers[0].node_type == 2);
CHECK(town->timers[0].node == 12);
CHECK(town->timers[0].time == 2500);
REQUIRE(town->wandering.size() >= 1);
CHECK(town->wandering[0].monst[0] == 7);
CHECK(town->wandering[0].monst[1] == 8);
CHECK(town->wandering[0].monst[2] == 9);
CHECK(town->wandering[0].monst[3] == 10);
REQUIRE(town->sign_locs.size() >= 1);
CHECK(town->sign_locs[0].text == "Sign #4279816");
REQUIRE(town->spec_strs.size() >= 1);
CHECK(town->spec_strs[0] == "Something! With extra spaces!");
REQUIRE(town->room_rect.size() >= 1);
CHECK(town->room_rect[0].descr == "Unknown Area . . .");
}
SECTION("With a preset item") {
town->preset_items.emplace_back();
town->preset_items.back().code = 52;
town->preset_items.back().ability = 9;
town->preset_items.back().charges = 102;
town->preset_items.back().always_there = true;
town->preset_items.back().property = true;
town->preset_items.back().contained = true;
in_and_out("item", town, scen);
REQUIRE(town->preset_items.size() >= 1);
CHECK(town->preset_items[0].code == 52);
CHECK(town->preset_items[0].ability == 9);
CHECK(town->preset_items[0].charges == 102);
CHECK(town->preset_items[0].always_there);
CHECK(town->preset_items[0].property);
CHECK(town->preset_items[0].contained);
}
SECTION("With a townsperson") {
town->creatures.emplace_back();
town->creatures.back().number = 60;
town->creatures.back().start_attitude = eAttitude::DOCILE;
town->creatures.back().mobility = 1;
town->creatures.back().spec1 = 17;
town->creatures.back().spec2 = 4;
town->creatures.back().spec_enc_code = 3;
town->creatures.back().time_flag = eMonstTime::DISAPPEAR_WHEN_EVENT;
town->creatures.back().time_code = 5;
town->creatures.back().monster_time = 16;
town->creatures.back().facial_pic = 56;
town->creatures.back().personality = 8;
town->creatures.back().special_on_kill = 9;
town->creatures.back().special_on_talk = 12;
in_and_out("townsperson", town, scen);
REQUIRE(town->creatures.size() >= 1);
CHECK(town->creatures[0].number == 60);
CHECK(town->creatures[0].start_attitude == eAttitude::DOCILE);
CHECK(town->creatures[0].mobility == 1);
CHECK(town->creatures[0].spec1 == 17);
CHECK(town->creatures[0].spec2 == 4);
CHECK(town->creatures[0].spec_enc_code == 3);
CHECK(town->creatures[0].time_flag == eMonstTime::DISAPPEAR_WHEN_EVENT);
CHECK(town->creatures[0].monster_time == 16);
CHECK(town->creatures[0].time_code == 5);
CHECK(town->creatures[0].facial_pic == 56);
CHECK(town->creatures[0].personality == 8);
CHECK(town->creatures[0].special_on_kill == 9);
CHECK(town->creatures[0].special_on_talk == 12);
} }
} }