Some test cases for saving scenarios, and fix some bugs they revealed
- Fix variable town entry and saved item rects not being saved if they referenced an invalid town - Fix "blank" scenario timers being saved - Fix junk data appearing in timers - Output operators for location and rectangle types - Set timer node_type to 2 when loading from town record
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -66,5 +66,10 @@ src/doxy/doxy_warnings.txt
|
||||
# Windows junk files
|
||||
Thumbs.db
|
||||
|
||||
# Test case junk files
|
||||
test/junk/*.xml
|
||||
test/junk/*.spec
|
||||
test/junk/*.map
|
||||
|
||||
# Misc
|
||||
oldstructs.txt
|
||||
|
||||
@@ -237,11 +237,13 @@
|
||||
91BC33901B4388E80008882C /* libboost_filesystem.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 919DDBFA19006CC9003E7FED /* libboost_filesystem.dylib */; };
|
||||
91BC33911B4388E80008882C /* libboost_system.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 919DDBFB19006CC9003E7FED /* libboost_system.dylib */; };
|
||||
91BC33921B4388E80008882C /* libboost_thread.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 910D9CA31B36439100414B17 /* libboost_thread.dylib */; };
|
||||
91BC33981B4481EF0008882C /* scen.fileio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91B3EEF20F969BA700BF5B67 /* scen.fileio.cpp */; };
|
||||
91BFA3D71901B18F001686E4 /* mask.vert in Copy Shaders */ = {isa = PBXBuildFile; fileRef = 91BFA3D61901B024001686E4 /* mask.vert */; };
|
||||
91C6864A0FD5EEFD000F6D01 /* pc.graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91B3EF0A0F969BD300BF5B67 /* pc.graphics.cpp */; };
|
||||
91C749B81A2D6670008E0E10 /* strings in Copy Strings */ = {isa = PBXBuildFile; fileRef = 91C749B71A2D6432008E0E10 /* strings */; };
|
||||
91C749BA1A2D670D008E0E10 /* dialogs in Copy Dialog Definitions */ = {isa = PBXBuildFile; fileRef = 91C749B91A2D66F7008E0E10 /* dialogs */; };
|
||||
91CC173C1B421CA0003D9A69 /* catch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91CC17391B421CA0003D9A69 /* catch.cpp */; };
|
||||
91CC173E1B421CA0003D9A69 /* scen_write.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91CC173B1B421CA0003D9A69 /* scen_write.cpp */; };
|
||||
91CC17491B422D5C003D9A69 /* scen_read.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91CC173A1B421CA0003D9A69 /* scen_read.cpp */; };
|
||||
91D634560F8FD77800674AB3 /* BoE.icns in Resources */ = {isa = PBXBuildFile; fileRef = 2B8F435C0C0973680012E4A8 /* BoE.icns */; };
|
||||
91EF052C1904D099001BEF85 /* bold.ttf in Copy Fonts */ = {isa = PBXBuildFile; fileRef = 91EF05291904D082001BEF85 /* bold.ttf */; };
|
||||
@@ -732,6 +734,7 @@
|
||||
91CC172D1B421C0A003D9A69 /* boe_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = boe_test; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
91CC17391B421CA0003D9A69 /* catch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = catch.cpp; sourceTree = "<group>"; };
|
||||
91CC173A1B421CA0003D9A69 /* scen_read.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = scen_read.cpp; sourceTree = "<group>"; };
|
||||
91CC173B1B421CA0003D9A69 /* scen_write.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = scen_write.cpp; sourceTree = "<group>"; };
|
||||
91CC173F1B421CAA003D9A69 /* catch.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch.hpp; sourceTree = "<group>"; };
|
||||
91D635AA0F90E7B500674AB3 /* stealth.exs */ = {isa = PBXFileReference; lastKnownFileType = file; path = stealth.exs; sourceTree = "<group>"; };
|
||||
91D635AB0F90E7B500674AB3 /* stealth.meg */ = {isa = PBXFileReference; lastKnownFileType = file; path = stealth.meg; sourceTree = "<group>"; };
|
||||
@@ -1293,6 +1296,7 @@
|
||||
children = (
|
||||
91CC17391B421CA0003D9A69 /* catch.cpp */,
|
||||
91CC173A1B421CA0003D9A69 /* scen_read.cpp */,
|
||||
91CC173B1B421CA0003D9A69 /* scen_write.cpp */,
|
||||
);
|
||||
name = src;
|
||||
sourceTree = "<group>";
|
||||
@@ -1780,7 +1784,9 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
91CC173C1B421CA0003D9A69 /* catch.cpp in Sources */,
|
||||
91CC173E1B421CA0003D9A69 /* scen_write.cpp in Sources */,
|
||||
91CC17491B422D5C003D9A69 /* scen_read.cpp in Sources */,
|
||||
91BC33981B4481EF0008882C /* scen.fileio.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "location.hpp"
|
||||
#include "mathutil.hpp"
|
||||
#include <iostream>
|
||||
|
||||
bool operator == (location p1,location p2){
|
||||
if((p1.x == p2.x) & (p1.y == p2.y))
|
||||
@@ -186,3 +187,30 @@ rectangle rect(int top, int left, int bottom, int right) {
|
||||
return rectangle(top, left, bottom, right);
|
||||
}
|
||||
|
||||
std::ostream& operator<< (std::ostream& out, location l) {
|
||||
out << '(' << l.x << ',' << l.y << ')';
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& operator<< (std::ostream& out, spec_loc_t l) {
|
||||
out << static_cast<location&>(l);
|
||||
out << ':' << l.spec;
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& operator<< (std::ostream& out, sign_loc_t l) {
|
||||
out << static_cast<location&>(l);
|
||||
out << " -- \"" << l.text << '"';
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& operator<< (std::ostream& out, rectangle r) {
|
||||
out << "{" << r.topLeft() << " - " << r.bottomRight() << '}';
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& operator<< (std::ostream& out, info_rect_t r) {
|
||||
out << static_cast<rectangle&>(r);
|
||||
out << " -- \"" << r.descr << '"';
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
|
||||
struct rectangle;
|
||||
|
||||
@@ -136,4 +137,10 @@ rectangle rect();
|
||||
rectangle rect(location tl, location br);
|
||||
rectangle rect(int top, int left, int bottom, int right);
|
||||
|
||||
std::ostream& operator<< (std::ostream& out, location l);
|
||||
std::ostream& operator<< (std::ostream& out, spec_loc_t l);
|
||||
std::ostream& operator<< (std::ostream& out, sign_loc_t l);
|
||||
std::ostream& operator<< (std::ostream& out, rectangle r);
|
||||
std::ostream& operator<< (std::ostream& out, info_rect_t r);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -15,6 +15,14 @@
|
||||
|
||||
#include "oldstructs.hpp"
|
||||
|
||||
void cScenario::reset_version() {
|
||||
format.prog_make_ver[0] = 2;
|
||||
format.prog_make_ver[1] = 0;
|
||||
format.prog_make_ver[2] = 0;
|
||||
format.flag1 = 'O'; format.flag2 = 'B';
|
||||
format.flag3 = 'O'; format.flag4 = 'E';
|
||||
}
|
||||
|
||||
cScenario::~cScenario() {
|
||||
destroy_terrain();
|
||||
}
|
||||
|
||||
@@ -67,8 +67,8 @@ public:
|
||||
size_t which_town_start;
|
||||
spec_num_t init_spec;
|
||||
std::array<spec_loc_t,10> town_mods;
|
||||
rectangle store_item_rects[3];
|
||||
short store_item_towns[3];
|
||||
std::array<rectangle,3> store_item_rects;
|
||||
std::array<short,3> store_item_towns;
|
||||
std::vector<cSpecItem> special_items;
|
||||
std::vector<cQuest> quests;
|
||||
std::vector<cShop> shops;
|
||||
@@ -115,6 +115,7 @@ public:
|
||||
bool is_monst_used(mon_num_t monst);
|
||||
bool is_item_used(item_num_t item);
|
||||
|
||||
void reset_version();
|
||||
cScenario& operator=(cScenario&& other);
|
||||
cScenario(cScenario&) = delete;
|
||||
explicit cScenario();
|
||||
|
||||
@@ -42,9 +42,9 @@ public:
|
||||
|
||||
class cTimer {
|
||||
public:
|
||||
long time;
|
||||
short node_type;
|
||||
short node;
|
||||
long time = 0;
|
||||
short node_type = 0;
|
||||
short node = -1;
|
||||
};
|
||||
|
||||
struct pending_special_type {
|
||||
|
||||
@@ -41,6 +41,9 @@ extern bool cur_scen_is_mac;
|
||||
void print_write_position ();
|
||||
void load_spec_graphics();
|
||||
|
||||
// These aren't static solely so that the test cases can access them.
|
||||
void writeScenarioToXml(ticpp::Printer&& data, cScenario& scenario);
|
||||
|
||||
template<typename Container> static void writeSpecialNodes(std::ostream& fout, Container nodes) {
|
||||
static_assert(std::is_same<typename Container::value_type, cSpecial>::value,
|
||||
"writeSpecialNodes must be instantiated with a container of special nodes");
|
||||
@@ -111,7 +114,7 @@ static bool is_minmax(int lo, int hi, int val) {
|
||||
return minmax(lo, hi, val) == val;
|
||||
}
|
||||
|
||||
static void writeScenarioToXml(ticpp::Printer&& data) {
|
||||
void writeScenarioToXml(ticpp::Printer&& data, cScenario& scenario) {
|
||||
data.OpenElement("scenario");
|
||||
data.PushAttribute("boes", scenario.format_ed_version());
|
||||
data.PushElement("title", scenario.scen_name);
|
||||
@@ -161,7 +164,7 @@ static void writeScenarioToXml(ticpp::Printer&& data) {
|
||||
data.PushElement("outdoor-start", scenario.out_sec_start);
|
||||
data.PushElement("sector-start", scenario.out_start);
|
||||
for(int i = 0; i < 3; i++) {
|
||||
if(is_minmax(0, scenario.towns.size(), scenario.store_item_towns[i])) {
|
||||
if(scenario.store_item_towns[i] >= 0) {
|
||||
data.OpenElement("store-items");
|
||||
data.PushAttribute("top", scenario.store_item_rects[i].top);
|
||||
data.PushAttribute("left", scenario.store_item_rects[i].left);
|
||||
@@ -172,7 +175,7 @@ static void writeScenarioToXml(ticpp::Printer&& data) {
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < scenario.town_mods.size(); i++) {
|
||||
if(is_minmax(0, scenario.towns.size(), scenario.town_mods[i].spec)) {
|
||||
if(scenario.town_mods[i].spec >= 0) {
|
||||
data.OpenElement("town-flag");
|
||||
data.PushAttribute("town", scenario.town_mods[i].spec);
|
||||
data.PushAttribute("add-x", scenario.town_mods[i].x);
|
||||
@@ -286,7 +289,7 @@ static void writeScenarioToXml(ticpp::Printer&& data) {
|
||||
data.CloseElement("shop");
|
||||
}
|
||||
for(int i = 0; i < scenario.scenario_timers.size(); i++) {
|
||||
if(scenario.scenario_timers[i].time >= 0) {
|
||||
if(scenario.scenario_timers[i].time > 0 && scenario.scenario_timers[i].node >= 0) {
|
||||
data.OpenElement("timer");
|
||||
data.PushAttribute("freq", scenario.scenario_timers[i].time);
|
||||
data.PushText(scenario.scenario_timers[i].node);
|
||||
@@ -917,7 +920,7 @@ struct overrides_sheet {
|
||||
}
|
||||
};
|
||||
|
||||
std::string scenario_temp_dir_name = "ed_scenario";
|
||||
extern std::string scenario_temp_dir_name;
|
||||
void save_scenario(bool rename) {
|
||||
fs::path toFile = scenario.scen_file;
|
||||
if(rename || toFile.empty()) {
|
||||
@@ -928,11 +931,7 @@ void save_scenario(bool rename) {
|
||||
if(toFile.empty()) return;
|
||||
}
|
||||
|
||||
scenario.format.prog_make_ver[0] = 2;
|
||||
scenario.format.prog_make_ver[1] = 0;
|
||||
scenario.format.prog_make_ver[2] = 0;
|
||||
scenario.format.flag1 = 'O'; scenario.format.flag2 = 'B';
|
||||
scenario.format.flag3 = 'O'; scenario.format.flag4 = 'E';
|
||||
scenario.reset_version();
|
||||
// TODO: This is just a skeletal outline of what needs to be done to save the scenario
|
||||
tarball scen_file;
|
||||
{
|
||||
@@ -942,7 +941,7 @@ void save_scenario(bool rename) {
|
||||
|
||||
// Next, the bulk scenario data.
|
||||
std::ostream& scen_data = scen_file.newFile("scenario/scenario.xml");
|
||||
writeScenarioToXml(ticpp::Printer("scenario.xml", scen_data));
|
||||
writeScenarioToXml(ticpp::Printer("scenario.xml", scen_data), scenario);
|
||||
|
||||
// Then the terrains...
|
||||
std::ostream& terrain = scen_file.newFile("scenario/terrain.xml");
|
||||
|
||||
@@ -37,6 +37,7 @@ short mode_count = 0;
|
||||
cOutdoors* current_terrain;
|
||||
short pixel_depth,old_depth = 8;
|
||||
|
||||
std::string scenario_temp_dir_name = "ed_scenario";
|
||||
bool change_made = false, ae_loading = false;
|
||||
|
||||
// Numbers of current areas being edited
|
||||
|
||||
@@ -1595,6 +1595,7 @@ static void readTownFromXml(ticpp::Document&& data, cTown*& town, cScenario& sce
|
||||
if(num_timers >= 8)
|
||||
throw xBadNode(type, elem->Row(), elem->Column(), fname);
|
||||
readTimerFromXml(*elem, town->timers[num_timers]);
|
||||
town->timers[num_timers].node_type = 2;
|
||||
num_timers++;
|
||||
} else if(type == "flags") {
|
||||
Iterator<Element> flag;
|
||||
|
||||
@@ -10,3 +10,9 @@ sf::Texture tiny_obj_gworld, vehicle_gworld;
|
||||
sf::RenderWindow mainPtr;
|
||||
fs::path scenario_temp_dir_name = "test_scenario";
|
||||
cCustomGraphics spec_scen_g;
|
||||
|
||||
// And these are referenced from the scenario code, though not used in test cases
|
||||
#include "scenario.hpp"
|
||||
location cur_out;
|
||||
short cur_town;
|
||||
cScenario scenario;
|
||||
|
||||
132
test/scen_write.cpp
Normal file
132
test/scen_write.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include "tinyprint.h"
|
||||
#include "dialog.hpp"
|
||||
#include "catch.hpp"
|
||||
#include "scenario.hpp"
|
||||
#include "regtown.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ticpp;
|
||||
|
||||
extern Document xmlDocFromStream(istream& stream, string name);
|
||||
extern void readScenarioFromXml(Document&& data, cScenario& scenario);
|
||||
extern void writeScenarioToXml(Printer&& data, cScenario& scenario);
|
||||
|
||||
static void in_and_out(std::string name, cScenario& scen) {
|
||||
std::string fpath = "junk/";
|
||||
fpath += name;
|
||||
fpath += ".xml";
|
||||
ofstream fout;
|
||||
fout.exceptions(ios::badbit);
|
||||
fout.open(fpath);
|
||||
writeScenarioToXml(Printer(name, fout), scen);
|
||||
fout.close();
|
||||
// Reconstruct the scenario, to ensure that it doesn't just pass due to old data still being around
|
||||
scen.~cScenario();
|
||||
new(&scen) cScenario();
|
||||
ifstream fin;
|
||||
fin.exceptions(ios::badbit);
|
||||
fin.open(fpath);
|
||||
readScenarioFromXml(xmlDocFromStream(fin, name), scen);
|
||||
}
|
||||
|
||||
// NOTE: The test cases in this file are written with the implicit assumption that the read routines are trustworthy.
|
||||
// In other words, they depend on the test cases in scen_read.cpp.
|
||||
|
||||
TEST_CASE("Saving a scenario record") {
|
||||
cScenario scen;
|
||||
scen.reset_version();
|
||||
scen.format.ver[0] = 2;
|
||||
scen.format.ver[1] = 6;
|
||||
scen.format.ver[2] = 7;
|
||||
scenario_header_flags vers = scen.format;
|
||||
scen.scen_name = "Test Scenario";
|
||||
scen.intro_pic = 0;
|
||||
scen.campaign_id = "campaign";
|
||||
scen.who_wrote[0] = "Teaser 1";
|
||||
scen.who_wrote[1] = "Teaser 2";
|
||||
scen.contact_info[0] = "BoE Test Suite";
|
||||
scen.contact_info[1] = "nowhere@example.com";
|
||||
scen.intro_strs[0] = "Welcome to the test scenario!";
|
||||
scen.rating = 2;
|
||||
scen.difficulty = 2;
|
||||
scen.which_town_start = 7;
|
||||
scen.where_start = {24,28};
|
||||
scen.out_sec_start = {1,3};
|
||||
scen.out_start = {12, 21};
|
||||
SECTION("With basic header data") {
|
||||
in_and_out("basic", scen);
|
||||
CHECK(scen.format.prog_make_ver[0] == vers.prog_make_ver[0]);
|
||||
CHECK(scen.format.prog_make_ver[1] == vers.prog_make_ver[1]);
|
||||
CHECK(scen.format.prog_make_ver[2] == vers.prog_make_ver[2]);
|
||||
CHECK(scen.format.ver[0] == vers.ver[0]);
|
||||
CHECK(scen.format.ver[1] == vers.ver[1]);
|
||||
CHECK(scen.format.ver[2] == vers.ver[2]);
|
||||
CHECK(scen.scen_name == "Test Scenario");
|
||||
CHECK(scen.intro_pic == 0);
|
||||
CHECK(scen.campaign_id == "campaign");
|
||||
CHECK(scen.who_wrote[0] == "Teaser 1");
|
||||
CHECK(scen.who_wrote[1] == "Teaser 2");
|
||||
CHECK(scen.contact_info[0] == "BoE Test Suite");
|
||||
CHECK(scen.contact_info[1] == "nowhere@example.com");
|
||||
CHECK(scen.intro_strs[0] == "Welcome to the test scenario!");
|
||||
CHECK(scen.rating == 2);
|
||||
CHECK(scen.difficulty == 2);
|
||||
CHECK(scen.which_town_start == 7);
|
||||
CHECK(scen.where_start == loc(24,28));
|
||||
CHECK(scen.out_sec_start == loc(1,3));
|
||||
CHECK(scen.out_start == loc(12,21));
|
||||
}
|
||||
SECTION("With some towns and sectors") {
|
||||
scen.addTown<cTinyTown>();
|
||||
scen.addTown<cMedTown>();
|
||||
scen.addTown<cBigTown>();
|
||||
scen.outdoors.resize(2,5);
|
||||
in_and_out("town§or", scen);
|
||||
CHECK(scen.towns.size() == 3);
|
||||
CHECK(scen.outdoors.width() == 2);
|
||||
CHECK(scen.outdoors.height() == 5);
|
||||
}
|
||||
SECTION("With some optional header data") {
|
||||
REQUIRE(scen.town_mods.size() >= 1); // A safety valve for if I ever make this array dynamic
|
||||
scen.town_mods[0] = loc(12,9);
|
||||
scen.town_mods[0].spec = 4;
|
||||
REQUIRE(scen.store_item_towns.size() >= 1); // A safety valve for if I ever make this array dynamic
|
||||
REQUIRE(scen.store_item_rects.size() >= 1); // A safety valve for if I ever make this array dynamic
|
||||
scen.store_item_rects[0] = rect(1,2,3,4);
|
||||
scen.store_item_towns[0] = 5;
|
||||
REQUIRE(scen.scenario_timers.size() >= 1); // A safety valve for if I ever make this array dynamic
|
||||
scen.scenario_timers[0].node = 3;
|
||||
scen.scenario_timers[0].node_type = 1;
|
||||
scen.scenario_timers[0].time = 30000;
|
||||
scen.spec_strs.push_back("This is a sample special string!");
|
||||
scen.journal_strs.push_back("This is a sample journal string!");
|
||||
in_and_out("optional", scen);
|
||||
CHECK(scen.town_mods[0] == loc(12,9));
|
||||
CHECK(scen.town_mods[0].spec == 4);
|
||||
CHECK(scen.store_item_rects[0] == rect(1,2,3,4));
|
||||
CHECK(scen.store_item_towns[0] == 5);
|
||||
CHECK(scen.scenario_timers[0].node == 3);
|
||||
CHECK(scen.scenario_timers[0].node_type == 0); // This is inferred by the fact that it's in the scenario file
|
||||
CHECK(scen.scenario_timers[0].time == 30000);
|
||||
CHECK(scen.spec_strs.size() == 1);
|
||||
CHECK(scen.spec_strs[0] == "This is a sample special string!");
|
||||
CHECK(scen.journal_strs.size() == 1);
|
||||
CHECK(scen.journal_strs[0] == "This is a sample journal string!");
|
||||
}
|
||||
SECTION("With a special item") {
|
||||
scen.special_items.emplace_back();
|
||||
scen.special_items[0].flags = 11;
|
||||
scen.special_items[0].special = 2;
|
||||
scen.special_items[0].name = "Test Special Item";
|
||||
scen.special_items[0].descr = "This is a special item description!";
|
||||
in_and_out("special item", scen);
|
||||
CHECK(scen.special_items.size() == 1);
|
||||
CHECK(scen.special_items[0].flags == 11);
|
||||
CHECK(scen.special_items[0].special == 2);
|
||||
CHECK(scen.special_items[0].name == "Test Special Item");
|
||||
CHECK(scen.special_items[0].descr == "This is a special item description!");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user