Write the code to save the general scenario data to a file

- See monster strings are now fetched from the same list as the special encounter strings instead of a list of their own
- There is now a possibility for the scenario intro dialog to have a different icon than the scenario icon
- Remove unused intro_mess_len field
- Add method to the XML printer class to push a simple element with no attributes or child elements
- Automatically close any elements before writing the document to the stream
- Fix scenario editor File menu having an invisible "Close All" option that appeared when the option key was pressed
This commit is contained in:
2015-01-13 20:54:51 -05:00
parent 0aec4c4c5d
commit f282c06bfa
12 changed files with 192 additions and 41 deletions

View File

@@ -112,16 +112,11 @@
<xs:attribute name="town" type="xs:integer" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="town-flags" minOccurs="0" maxOccurs="10">
<xs:element name="town-flag" minOccurs="0" maxOccurs="10">
<xs:complexType>
<xs:sequence>
<xs:element name="town-flag">
<xs:complexType>
<xs:attribute name="town" type="xs:integer"/>
<xs:attribute name="add" type="xs:integer"/>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="town" type="xs:integer"/>
<xs:attribute name="add-x" type="xs:integer"/>
<xs:attribute name="add-y" type="xs:integer"/>
</xs:complexType>
</xs:element>
<xs:element name="specials">
@@ -133,7 +128,9 @@
<xs:element name="name" type="xs:string"/>
<xs:element name="description" type="xs:string"/>
</xs:sequence>
<xs:attribute name="special" type="bool"/>
<xs:attribute name="special" type="xs:integer"/>
<xs:attribute name="start-with" type="bool"/>
<xs:attribute name="useable" type="bool"/>
</xs:complexType>
</xs:element>
</xs:sequence>
@@ -196,6 +193,20 @@
<xs:element ref="creator"/>
<xs:element ref="game"/>
<xs:element ref="editor"/>
<xs:element name="strings">
<xs:complexType>
<xs:sequence>
<xs:element name="string" type="xs:string" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="journal">
<xs:complexType>
<xs:sequence>
<xs:element name="string" type="xs:string" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="boes" type="xs:string"/>
</xs:complexType>

View File

@@ -242,9 +242,9 @@ void play_see_monster_str(unsigned short m, location monst_loc) {
short where1 = is_out() ? univ.party.outdoor_corner.x + univ.party.i_w_c.x : univ.town.num;
short where2 = is_out() ? univ.party.outdoor_corner.y + univ.party.i_w_c.y : univ.town.num;
std::string placename = is_out() ? univ.out->out_name : univ.town->town_name;
cStrDlog display_strings(str1 < 100 ? univ.scenario.monst_strs[str1] : "", str2 < 100 ? univ.scenario.monst_strs[str2] : "", "", pic, type, NULL);
cStrDlog display_strings(str1 < 100 ? univ.scenario.spec_strs[str1] : "", str2 < 100 ? univ.scenario.spec_strs[str2] : "", "", pic, type, NULL);
display_strings.setSound(snd);
display_strings.setRecordHandler(cStringRecorder(NOTE_MONST).string1(str1).string2(str2).from(where1,where2).at(placename));
display_strings.setRecordHandler(cStringRecorder(NOTE_SCEN).string1(str1).string2(str2).from(where1,where2).at(placename));
display_strings.show();
}
// Then run the special, if any

View File

@@ -926,10 +926,6 @@ void cStringRecorder::operator()(cDialog& me) {
str1 = univ.scenario.outdoors[label1b][label2b]->spec_strs[label1];
str2 = univ.scenario.outdoors[label1b][label2b]->spec_strs[label2];
break;
case NOTE_MONST:
str1 = univ.scenario.monst_strs[label1];
str2 = univ.scenario.monst_strs[label2];
break;
}
if(univ.party.record(type, str1, location))
give_help(58,0,&me);

View File

@@ -274,7 +274,7 @@ void put_party_in_scen(std::string scen_name) {
if(!univ.scenario.intro_strs[j].empty()) {
for(i = 0; i < 6; i++)
strs[i] = univ.scenario.intro_strs[i];
custom_choice_dialog(strs,univ.scenario.intro_pic,PIC_SCEN,buttons) ;
custom_choice_dialog(strs,univ.scenario.intro_mess_pic,PIC_SCEN,buttons) ;
j = 6;
}
give_help(1,2);

View File

@@ -770,7 +770,6 @@ std::istream& operator>>(std::istream& in, eEncNoteType& type) {
if(name == "SCEN") type = NOTE_SCEN;
else if(name == "OUT") type = NOTE_OUT;
else if(name == "TOWN") type = NOTE_TOWN;
else if(name == "MONST") type = NOTE_MONST;
else in.setstate(std::ios_base::failbit);
return in;
}
@@ -786,9 +785,6 @@ std::ostream& operator<<(std::ostream& out, eEncNoteType type) {
case NOTE_TOWN:
out << "TOWN";
break;
case NOTE_MONST:
out << "MONST";
break;
}
return out;
}

View File

@@ -107,8 +107,7 @@ void cScenario::append(legacy::scenario_data_type& old){
difficulty = old.difficulty;
intro_pic = old.intro_pic;
default_ground = old.default_ground;
intro_mess_pic = old.intro_mess_pic;
intro_mess_len = old.intro_mess_len;
intro_mess_pic = old.intro_pic;
where_start.x = old.where_start.x;
where_start.y = old.where_start.y;
out_sec_start.x = old.out_sec_start.x;
@@ -167,3 +166,17 @@ void cScenario::append(legacy::scen_item_data_type& old){
for(i = 0; i < 256; i++)
ter_types[i].name = old.ter_names[i];
}
static std::string format_version(const unsigned char(& ver)[3]) {
std::ostringstream fmt;
fmt << int(ver[0]) << '.' << int(ver[1]) << '.' << int(ver[2]);
return fmt.str();
}
std::string cScenario::format_ed_version() {
return format_version(format.prog_make_ver);
}
std::string cScenario::format_scen_version() {
return format_version(format.ver);
}

View File

@@ -49,7 +49,7 @@ public:
void destroy_terrain();
public:
unsigned short difficulty,intro_pic,default_ground;
short intro_mess_pic,intro_mess_len;
short intro_mess_pic;
location where_start,out_sec_start,out_start;
size_t which_town_start;
short town_to_add_to[10];
@@ -79,7 +79,6 @@ public:
// This'll make the transition smoother once it becomes a vector.
std::array<std::string,50> journal_strs;
std::array<std::string,100> spec_strs;
std::string monst_strs[100];
bool adjust_diff : 1;
char : 7;
bool is_legacy;
@@ -91,6 +90,8 @@ public:
void append(legacy::scenario_data_type& old);
void append(legacy::scen_item_data_type& old);
void writeTo(std::ostream& file) const;
std::string format_scen_version();
std::string format_ed_version();
cScenario& operator=(cScenario&& other);
cScenario(cScenario&) = delete;

View File

@@ -717,7 +717,6 @@ enum eEncNoteType {
NOTE_SCEN,
NOTE_OUT,
NOTE_TOWN,
NOTE_MONST,
};
// This is a slight misnomer, as a couple of these are not true fields.

View File

@@ -45,5 +45,7 @@ void Printer::PushNode(Node* node) {
}
Printer::~Printer() {
while(!openElements.empty())
CloseElement(openElements.top()->Value());
stream << doc;
}

View File

@@ -34,6 +34,11 @@ namespace ticpp {
template<typename T> void PushText(T textVal) {
PushNode(new Text(boost::lexical_cast<std::string>(textVal)));
}
template<typename T> void PushElement(std::string tagName, T elemVal) {
OpenElement(tagName);
PushText(elemVal);
CloseElement(tagName);
}
};
}

View File

@@ -15,6 +15,7 @@
#include "dlogutil.hpp"
#include "tarball.hpp"
#include "gzstream.h"
#include "tinyprint.hpp"
#define DONE_BUTTON_ITEM 1
@@ -48,6 +49,21 @@ template<typename Container> static void writeSpecialNodes(std::ostream& fout, C
}
}
static std::string boolstr(bool b) {
return b ? "true" : "false";
}
template<> void ticpp::Printer::PushElement(std::string tagName, location pos) {
OpenElement(tagName);
PushAttribute("x", pos.x);
PushAttribute("y", pos.y);
CloseElement(tagName);
}
static bool is_minmax(int lo, int hi, int val) {
return minmax(lo, hi, val) == val;
}
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;
@@ -61,6 +77,126 @@ void save_scenario(fs::path toFile) {
// Next, the bulk scenario data.
std::ostream& scen_data = scen_file.newFile("scenario/scenario.xml");
ticpp::Printer data("scenario.xml", scen_data);
data.OpenElement("scenario");
data.PushAttribute("boes", scenario.format_ed_version());
data.PushElement("title", scenario.scen_name);
data.PushElement("icon", scenario.intro_pic);
data.PushElement("id", scenario.campaign_id);
data.PushElement("version", scenario.format_scen_version());
data.PushElement("language", "en-US");
data.OpenElement("author");
data.PushElement("name", scenario.contact_info);
// TODO: Store name and email in separate fields.
data.PushElement("email", "");
data.CloseElement("author");
data.OpenElement("text");
data.PushElement("teaser", scenario.who_wrote[0]);
data.PushElement("teaser", scenario.who_wrote[1]);
if(scenario.intro_pic != scenario.intro_mess_pic)
data.PushElement("icon", scenario.intro_mess_pic);
for(int i = 0; i < 6; i++)
data.PushElement("intro-msg", scenario.intro_strs[i]);
data.CloseElement("text");
data.OpenElement("ratings");
switch(scenario.rating) {
case 0: data.PushElement("content", "g"); break;
case 1: data.PushElement("content", "pg"); break;
case 2: data.PushElement("content", "r"); break;
case 3: data.PushElement("content", "nc17"); break;
}
data.PushElement("difficulty", scenario.difficulty + 1);
data.CloseElement("ratings");
data.OpenElement("flags");
data.PushElement("adjust-difficulty", boolstr(scenario.adjust_diff));
data.PushElement("legacy", boolstr(scenario.is_legacy));
data.PushElement("custom-graphics", boolstr(scenario.uses_custom_graphics));
data.CloseElement("flags");
data.OpenElement("creator");
data.PushElement("type", "oboe");
data.PushElement("version", scenario.format_ed_version());
// TODO: fill <os> element
data.PushElement("os", "");
data.CloseElement("creator");
data.OpenElement("game");
data.PushElement("num-towns", scenario.towns.size());
data.PushElement("out-width", scenario.outdoors.width());
data.PushElement("out-height", scenario.outdoors.height());
data.PushElement("start-town", scenario.which_town_start);
data.PushElement("town-start", scenario.where_start);
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])) {
data.OpenElement("store-items");
data.PushAttribute("top", scenario.store_item_rects[i].top);
data.PushAttribute("left", scenario.store_item_rects[i].left);
data.PushAttribute("bottom", scenario.store_item_rects[i].bottom);
data.PushAttribute("right", scenario.store_item_rects[i].right);
data.PushAttribute("town", scenario.store_item_towns[i]);
data.CloseElement("store-items");
}
}
for(int i = 0; i < 10; i++) {
if(is_minmax(0, scenario.towns.size(), scenario.town_to_add_to[i])) {
data.OpenElement("town-flag");
data.PushAttribute("town", scenario.town_to_add_to[i]);
data.PushAttribute("add-x", scenario.flag_to_add_to_town[i][0]);
data.PushAttribute("add-y", scenario.flag_to_add_to_town[i][1]);
data.CloseElement("town-flag");
}
}
data.OpenElement("specials");
for(int i = 0; i < 50; i++) {
data.OpenElement("item");
data.PushAttribute("start-with", boolstr(scenario.special_items[i].flags / 10));
data.PushAttribute("useable", boolstr(scenario.special_items[i].flags % 10));
data.PushAttribute("special", scenario.special_items[i].special);
data.PushElement("name", scenario.special_items[i].name);
data.PushElement("description", scenario.special_items[i].descr);
data.CloseElement("item");
}
data.CloseElement("specials");
for(int i = 0; i < 20; i++) {
if(scenario.scenario_timer_times[i] > 0) {
data.OpenElement("timer");
data.PushAttribute("time", scenario.scenario_timer_times[i]);
data.PushText(scenario.scenario_timer_specs[i]);
data.CloseElement("timer");
}
}
data.CloseElement("game");
data.OpenElement("editor");
data.PushElement("default-ground", scenario.default_ground);
data.PushElement("last-out-section", scenario.last_out_edited);
data.PushElement("last-town", scenario.last_town_edited);
for(int i = 0; i < 10; i++) {
if(scenario.storage_shortcuts[i].ter_type >= 0) {
cScenario::cItemStorage shortcut = scenario.storage_shortcuts[i];
data.OpenElement("storage");
data.PushElement("on-terrain", shortcut.ter_type);
data.PushElement("is-property", boolstr(shortcut.property));
for(int j = 0; j < 10; j++) {
if(shortcut.item_num[j] >= 0) {
data.OpenElement("item");
data.PushAttribute("chance", shortcut.item_odds[j]);
data.PushText(shortcut.item_num[j]);
data.CloseElement("item");
}
}
data.CloseElement("storage");
}
}
data.CloseElement("editor");
data.OpenElement("strings");
for(size_t i = 0; i < scenario.spec_strs.size(); i++)
data.PushElement("string", scenario.spec_strs[i]);
data.CloseElement("strings");
data.OpenElement("journal");
for(size_t i = 0; i < scenario.journal_strs.size(); i++)
data.PushElement("string", scenario.journal_strs[i]);
data.CloseElement("journal");
data.CloseElement("scenario");
// Then the terrains...
std::ostream& terrain = scen_file.newFile("scenario/terrain.xml");

View File

@@ -996,14 +996,6 @@
</object>
<int key="connectionID">142</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">performClose:</string>
<reference key="source" ref="1014"/>
<reference key="destination" ref="776162233"/>
</object>
<int key="connectionID">193</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">showHelp:</string>
@@ -1162,11 +1154,6 @@
<reference key="object" ref="705341025"/>
<reference key="parent" ref="720053764"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">73</int>
<reference key="object" ref="776162233"/>
<reference key="parent" ref="720053764"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">112</int>
<reference key="object" ref="579971712"/>
@@ -1846,6 +1833,11 @@
<reference key="object" ref="304878788"/>
<reference key="parent" ref="784697102"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">886</int>
<reference key="object" ref="776162233"/>
<reference key="parent" ref="720053764"/>
</object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
@@ -1883,7 +1875,6 @@
<string key="690.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="691.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="72.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="73.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="75.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="79.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="795.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -1969,12 +1960,13 @@
<string key="883.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="884.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="885.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="886.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">885</int>
<int key="maxID">886</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes"/>
<int key="IBDocument.localizationMode">0</int>