diff --git a/rsrc/boes/town.xsd b/rsrc/boes/town.xsd index 5117096c..ae801923 100644 --- a/rsrc/boes/town.xsd +++ b/rsrc/boes/town.xsd @@ -38,13 +38,23 @@ + - + + + + + + + + + + @@ -81,7 +91,7 @@ - + @@ -91,11 +101,12 @@ - - - + + + + @@ -127,11 +138,11 @@ - + - - + + @@ -139,28 +150,25 @@ - + + - + - + - - - - - + @@ -169,14 +177,28 @@ - + + + + + + + + + + + + + + + - + diff --git a/rsrc/boes/town/t0.xml b/rsrc/boes/town/t0.xml index f010714b..f7bfdf46 100644 --- a/rsrc/boes/town/t0.xml +++ b/rsrc/boes/town/t0.xml @@ -8,7 +8,7 @@ 1 - 0 + lit 12 @@ -28,20 +28,17 @@ This is a sample sign! - - - 128 - - 17 + + 17 true 3 - - 12 + + 12 0 0 1 diff --git a/src/boe.specials.cpp b/src/boe.specials.cpp index 1bb5d2d1..0d582460 100644 --- a/src/boe.specials.cpp +++ b/src/boe.specials.cpp @@ -3222,8 +3222,8 @@ void ifthen_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, break; } i = 0; - for(j = spec.ex1b; j < std::min(spec.ex2b, univ.town->max_dim()); j++) - for(k = spec.ex1a; k < std::min(spec.ex2a, univ.town->max_dim()); k++) { + for(j = spec.ex1b; j < min(spec.ex2b, univ.town->max_dim()); j++) + for(k = spec.ex1a; k < min(spec.ex2a, univ.town->max_dim()); k++) { switch(eFieldType(spec.m1)) { // These values are not allowed case SPECIAL_EXPLORED: case SPECIAL_SPOT: case FIELD_DISPEL: case FIELD_SMASH: break; diff --git a/src/classes/monster.h b/src/classes/monster.h index e5cd1569..7ac94792 100644 --- a/src/classes/monster.h +++ b/src/classes/monster.h @@ -168,7 +168,7 @@ public: unsigned long id; mon_num_t number; short active, attitude; - unsigned char start_attitude; + unsigned int start_attitude; location start_loc, cur_loc; unsigned short mobility; eMonstTime time_flag; diff --git a/src/classes/regtown.cpp b/src/classes/regtown.cpp index 79c0ae7e..52faf681 100644 --- a/src/classes/regtown.cpp +++ b/src/classes/regtown.cpp @@ -298,38 +298,38 @@ cTinyTown::cTinyTown(cScenario& scenario, bool init_strings) : cTown(scenario, i init_start(); } -short cBigTown::max_dim() const { +size_t cBigTown::max_dim() const { return 64; } -short cMedTown::max_dim() const { +size_t cMedTown::max_dim() const { return 48; } -short cTinyTown::max_dim() const { +size_t cTinyTown::max_dim() const { return 32; } -short cBigTown::max_monst() const { +size_t cBigTown::max_monst() const { return 60; } -short cMedTown::max_monst() const { +size_t cMedTown::max_monst() const { return 40; } -short cTinyTown::max_monst() const { +size_t cTinyTown::max_monst() const { return 30; } -short cBigTown::max_items() const { +size_t cBigTown::max_items() const { return 64; } -short cMedTown::max_items() const { +size_t cMedTown::max_items() const { return 64; } -short cTinyTown::max_items() const { +size_t cTinyTown::max_items() const { return 64; } diff --git a/src/classes/regtown.h b/src/classes/regtown.h index bb0ba110..96cbcc80 100644 --- a/src/classes/regtown.h +++ b/src/classes/regtown.h @@ -32,9 +32,9 @@ public: ter_num_t& terrain(size_t x, size_t y); cCreature& creatures(size_t i); unsigned char& lighting(size_t i, size_t r); - short max_dim() const; - short max_monst() const; - short max_items() const; + size_t max_dim() const; + size_t max_monst() const; + size_t max_items() const; explicit cBigTown(cScenario& scenario, bool init_strings = false); void writeTerrainTo(std::ostream& file); @@ -51,9 +51,9 @@ public: ter_num_t& terrain(size_t x, size_t y); cCreature& creatures(size_t i); unsigned char& lighting(size_t i, size_t r); - short max_dim() const; - short max_monst() const; - short max_items() const; + size_t max_dim() const; + size_t max_monst() const; + size_t max_items() const; explicit cMedTown(cScenario& scenario, bool init_strings = false); void writeTerrainTo(std::ostream& file); @@ -70,9 +70,9 @@ public: ter_num_t& terrain(size_t x, size_t y); cCreature& creatures(size_t i); unsigned char& lighting(size_t i, size_t r); - short max_dim() const; - short max_monst() const; - short max_items() const; + size_t max_dim() const; + size_t max_monst() const; + size_t max_items() const; explicit cTinyTown(cScenario& scenario, bool init_strings = false); void writeTerrainTo(std::ostream& file); diff --git a/src/classes/tmpltown.cpp b/src/classes/tmpltown.cpp index 994ba995..e175a947 100644 --- a/src/classes/tmpltown.cpp +++ b/src/classes/tmpltown.cpp @@ -33,14 +33,14 @@ unsigned char& cBigTemplTown::lighting(size_t i, size_t r){ return _lighting[i][r]; } -short cBigTemplTown::max_dim() const { +size_t cBigTemplTown::max_dim() const { return 0; // not sure what they are yet. } -short cBigTemplTown::max_monst() const { +size_t cBigTemplTown::max_monst() const { return 30; } -short cBigTemplTown::max_items() const { +size_t cBigTemplTown::max_items() const { return 64; } diff --git a/src/classes/tmpltown.h b/src/classes/tmpltown.h index dbaceec0..12b0e65f 100644 --- a/src/classes/tmpltown.h +++ b/src/classes/tmpltown.h @@ -45,9 +45,9 @@ public: ter_num_t& terrain(size_t x, size_t y); cCreature& creatures(size_t i); unsigned char& lighting(size_t i, size_t r); - short max_dim() const; - short max_monst() const; - short max_items() const; + size_t max_dim() const; + size_t max_monst() const; + size_t max_items() const; void writeTerrainTo(std::ostream& file); void readTerrainFrom(std::istream& file); explicit cBigTemplTown(cScenario& scenario, bool init_strings = false); @@ -62,9 +62,9 @@ public: ter_num_t& terrain(size_t x, size_t y); cCreature& creatures(size_t i); unsigned char& lighting(size_t i, size_t r); - short max_dim() const; - short max_monst() const; - short max_items() const; + size_t max_dim() const; + size_t max_monst() const; + size_t max_items() const; void writeTerrainTo(std::ostream& file); void readTerrainFrom(std::istream& file); explicit cMedTemplTown(cScenario& scenario, bool init_strings = false); @@ -79,9 +79,9 @@ public: ter_num_t& terrain(size_t x, size_t y); cCreature& creatures(size_t i); unsigned char& lighting(size_t i, size_t r); - short max_dim() const; - short max_monst() const; - short max_items() const; + size_t max_dim() const; + size_t max_monst() const; + size_t max_items() const; void writeTerrainTo(std::ostream& file); void readTerrainFrom(std::istream& file); explicit cTinyTemplTown(cScenario& scenario, bool init_strings = false); diff --git a/src/classes/town.cpp b/src/classes/town.cpp index 9059d25c..505a4e62 100644 --- a/src/classes/town.cpp +++ b/src/classes/town.cpp @@ -13,6 +13,7 @@ #include "classes.h" #include "oldstructs.h" +#include "mathutil.hpp" void cTown::append(legacy::big_tr_type&, int){} void cTown::append(legacy::ave_tr_type&, int){} @@ -234,8 +235,8 @@ void cTown::set_up_lights() { l.y = j; rad = scenario.ter_types[this->terrain(i,j)].light_radius; if(rad > 0) { - for(where.x = std::max(0,i - rad); where.x < std::min(this->max_dim(),short(i + rad + 1)); where.x++) - for(where.y = std::max(0,j - rad); where.y < std::min(this->max_dim(),short(j + rad + 1)); where.y++) + for(where.x = std::max(0,i - rad); where.x < min(this->max_dim(),short(i + rad + 1)); where.x++) + for(where.y = std::max(0,j - rad); where.y < min(this->max_dim(),short(j + rad + 1)); where.y++) if(!where_lit[where.x][where.y] && dist(where,l) <= rad && can_see(l,where,get_obscurity) < 5) where_lit[where.x][where.y] = true; } @@ -264,3 +265,23 @@ short cTown::light_obscurity(short x,short y) { return 1; return 0; } + +std::ostream& operator<< (std::ostream& out, eLighting light) { + switch(light) { + case LIGHT_NORMAL: out << "lit"; break; + case LIGHT_DARK: out << "dark"; break; + case LIGHT_DRAINS: out << "drains"; break; + case LIGHT_NONE: out << "none"; break; + } + return out; +} + +std::istream& operator>> (std::istream& in, eLighting& light) { + std::string key; + if(key == "lit") light = LIGHT_NORMAL; + else if(key == "dark") light = LIGHT_DARK; + else if(key == "drains") light = LIGHT_DRAINS; + else if(key == "none") light = LIGHT_NONE; + else in.fail(); + return in; +} diff --git a/src/classes/town.h b/src/classes/town.h index 4bd41da7..7d21d484 100644 --- a/src/classes/town.h +++ b/src/classes/town.h @@ -53,7 +53,7 @@ public: public: location loc; short code,ability; - unsigned char charges; + unsigned int charges; bool always_there, property, contained; void append(legacy::preset_item_type old); @@ -77,13 +77,13 @@ public: location exit_locs[4]; short exit_specs[4]; rectangle in_town_rect; - cItem preset_items[64]; + std::array preset_items; short max_num_monst; std::vector preset_fields; short spec_on_entry,spec_on_entry_if_dead; short spec_on_hostile; - short timer_spec_times[8]; - short timer_specs[8]; + std::array timer_spec_times; + std::array timer_specs; std::array specials; bool strong_barriers : 1; bool defy_mapping : 1; @@ -107,9 +107,9 @@ public: virtual ter_num_t& terrain(size_t x, size_t y) = 0; virtual cCreature& creatures(size_t i) = 0; virtual unsigned char& lighting(size_t i, size_t r) = 0; - virtual short max_dim() const = 0; - virtual short max_monst() const = 0; - virtual short max_items() const = 0; + virtual size_t max_dim() const = 0; + virtual size_t max_monst() const = 0; + virtual size_t max_items() const = 0; void init_start(); void set_up_lights(); short light_obscurity(short x,short y); // Obscurity function used for calculating lighting @@ -120,4 +120,7 @@ public: virtual void readTerrainFrom(std::istream& file) = 0; }; +std::ostream& operator<< (std::ostream& out, eLighting light); +std::istream& operator>> (std::istream& in, eLighting& light); + #endif diff --git a/src/dialogxml/xml-parser/tinyprint.cpp b/src/dialogxml/xml-parser/tinyprint.cpp index b98b20a2..2e89294e 100644 --- a/src/dialogxml/xml-parser/tinyprint.cpp +++ b/src/dialogxml/xml-parser/tinyprint.cpp @@ -24,7 +24,7 @@ void Printer::CloseElement(std::string tagName) { if(openElements.empty()) throw std::out_of_range("No elements left to close!"); if(tagName != openElements.top()->Value()) - throw std::logic_error("Mismatched closing tag!"); + throw std::logic_error("Mismatched closing tag! (Expected " + openElements.top()->Value() + ")"); Element* top = openElements.top(); openElements.pop(); PushNode(top); diff --git a/src/scenedit/scen.fileio.cpp b/src/scenedit/scen.fileio.cpp index 9172019d..08c04a9e 100644 --- a/src/scenedit/scen.fileio.cpp +++ b/src/scenedit/scen.fileio.cpp @@ -61,6 +61,15 @@ template<> void ticpp::Printer::PushElement(std::string tagName, location pos) { CloseElement(tagName); } +template<> void ticpp::Printer::PushElement(std::string tagName, rectangle rect) { + OpenElement(tagName); + PushAttribute("top", rect.top); + PushAttribute("left", rect.left); + PushAttribute("bottom", rect.bottom); + PushAttribute("right", rect.right); + CloseElement(tagName); +} + template<> void ticpp::Printer::PushElement(std::string tagName, cMonster::cAttack attack) { OpenElement(tagName); PushAttribute("type", attack.type); @@ -507,6 +516,143 @@ void writeOutdoorsToXml(ticpp::Printer&& data, cOutdoors& sector) { data.CloseElement("sector"); } +void writeTownToXml(ticpp::Printer&& data, cTown& town) { + static const char directions[] = {'n', 'w', 's', 'e'}; + data.OpenElement("town"); + data.PushAttribute("boes", scenario.format_ed_version()); + data.PushElement("size", town.max_dim()); + data.PushElement("name", town.town_name); + for(size_t i = 0; i < town.comment.size(); i++) { + if(!town.comment[i].empty()) + data.PushElement("comment", town.comment[i]); + } + data.PushElement("bounds", town.in_town_rect); + data.PushElement("difficulty", town.difficulty); + data.PushElement("lighting", town.lighting_type); + if(town.spec_on_entry >= 0) { + data.OpenElement("onenter"); + data.PushAttribute("condition", "alive"); + data.PushText(town.spec_on_entry); + data.CloseElement("onenter"); + } + if(town.spec_on_entry_if_dead >= 0) { + data.OpenElement("onenter"); + data.PushAttribute("condition", "dead"); + data.PushText(town.spec_on_entry_if_dead); + data.CloseElement("onenter"); + } + for(int i = 0; i < 4; i++) { + if(town.exit_locs[i].x >= 0 && town.exit_locs[i].y >= 0) { + data.OpenElement("exit"); + data.PushAttribute("dir", directions[i]); + data.PushAttribute("x", town.exit_locs[i].x); + data.PushAttribute("y", town.exit_locs[i].y); + data.CloseElement("exit"); + } + } + for(int i = 0; i < 4; i++) { + if(town.exit_specs[i] >= 0) { + data.OpenElement("onexit"); + data.PushAttribute("dir", directions[i]); + data.PushText(town.exit_specs[i]); + data.CloseElement("onexit"); + } + } + if(town.spec_on_hostile >= 0) + data.PushElement("onoffend", town.spec_on_hostile); + for(size_t i = 0; i < town.timer_spec_times.size() && i < town.timer_specs.size(); i++) { + data.OpenElement("timer"); + data.PushAttribute("freq", town.timer_spec_times[i]); + data.PushText(town.timer_specs[i]); + data.CloseElement("timer"); + } + data.OpenElement("flags"); + if(town.town_chop_time > 0) { + data.OpenElement("chop"); + data.PushAttribute("day", town.town_chop_time); + data.PushAttribute("event", town.town_chop_key); + data.PushAttribute("kills", town.max_num_monst); + data.CloseElement("chop"); + } + if(town.strong_barriers) + data.PushElement("strong-barriers", true); + if(town.defy_mapping) + data.PushElement("defy-mapping", true); + if(town.defy_scrying) + data.PushElement("defy-scrying", true); + if(town.is_hidden) + data.PushElement("hidden", true); + data.CloseElement("flags"); + for(int i = 0; i < 4; i++) { + if(town.wandering[i].isNull()) continue; + data.OpenElement("wandering"); + for(int j = 0; j < 4; j++) { + if(j == 0 || town.wandering[i].monst[j] > 0) + data.PushElement("monster", town.wandering[i].monst[j]); + } + data.CloseElement("wandering"); + } + for(size_t i = 0; i < town.preset_items.size(); i++) { + data.OpenElement("item"); + data.PushAttribute("id", i); + data.PushElement("type", town.preset_items[i].code); + if(town.preset_items[i].ability >= 0) + data.PushElement("mod", town.preset_items[i].ability); + if(town.preset_items[i].charges > 0) + data.PushElement("charges", town.preset_items[i].charges); + if(town.preset_items[i].always_there) + data.PushElement("always", true); + if(town.preset_items[i].property) + data.PushElement("property", true); + if(town.preset_items[i].contained) + data.PushElement("contained", true); + data.CloseElement("item"); + } + for(size_t i = 0; i < town.max_monst(); i++) { + data.OpenElement("creature"); + data.PushAttribute("id", i); + cCreature& preset = town.creatures(i); + data.PushElement("type", preset.number); + data.PushElement("attitude", preset.start_attitude); + data.PushElement("mobility", preset.mobility); + data.PushElement("personality", preset.personality); + data.PushElement("face", preset.facial_pic); + if(preset.spec1 >= 0 && preset.spec2 >= 0) + data.PushElement("sdf", loc(preset.spec1, preset.spec2)); + if(preset.spec_enc_code > 0) + data.PushElement("encounter", preset.spec_enc_code); + if(preset.special_on_kill >= 0) + data.PushElement("onkill", preset.special_on_kill); + if(preset.time_flag != eMonstTime::ALWAYS) { + data.OpenElement("time"); + data.PushAttribute("type", preset.time_flag); + data.PushElement("param", preset.monster_time); + data.PushElement("param", preset.time_code); + data.CloseElement("time"); + } + data.CloseElement("creature"); + } + for(auto& area : town.room_rect) { + if(!area.descr.empty() && area.top < area.bottom && area.left < area.right) + data.PushElement("description", area); + } + for(size_t i = 0; i < town.sign_strs.size(); i++) { + if(town.sign_strs[i].empty()) continue; + data.OpenElement("sign"); + data.PushAttribute("id", i); + data.PushText(town.sign_strs[i]); + data.CloseElement("sign"); + } + for(size_t i = 0; i < town.spec_strs.size(); i++) { + if(town.spec_strs[i].empty()) continue; + data.OpenElement("string"); + data.PushAttribute("id", i); + data.PushText(town.spec_strs[i]); + data.CloseElement("string"); + } + data.CloseElement("town"); +} + void save_scenario(fs::path toFile) { // TODO: I'm not certain 1.0.0 is the correct version here? scenario.format.prog_make_ver[0] = 1; @@ -561,6 +707,7 @@ void save_scenario(fs::path toFile) { std::string file_basename = 't' + std::to_string(i); // First the main data. std::ostream& town = scen_file.newFile("scenario/towns/" + file_basename + ".xml"); + writeTownToXml(ticpp::Printer(file_basename + ".xml", town), *scenario.towns[i]); // Then the map. std::ostream& town_map = scen_file.newFile("scenario/towns/" + file_basename + ".map");