Complete implementation of saving new-format scenarios and write version-check and loading stub for new-format scenarios
This commit is contained in:
@@ -34,6 +34,7 @@
|
||||
<xs:element name="personality">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name='title' type='xs:string'/>
|
||||
<xs:element name="look" type="xs:string"/>
|
||||
<xs:element name="name" type="xs:string"/>
|
||||
<xs:element name="job" type="xs:string"/>
|
||||
@@ -46,250 +47,9 @@
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="keyword" minOccurs="1" maxOccurs="2" type="xs:string"/>
|
||||
<xs:choice minOccurs="1" maxOccurs="1">
|
||||
<xs:element name="talk">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element ref="text" minOccurs="1" maxOccurs="2"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="if-flag">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="sdf" type="sdf"/>
|
||||
<xs:element ref="text" minOccurs="1" maxOccurs="2"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="record">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="sdf" type="sdf"/>
|
||||
<xs:element ref="text" minOccurs="1" maxOccurs="2"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="value" type="xs:integer" default="1"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="inn">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="cost" type="xs:integer"/>
|
||||
<xs:element name="quality">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:minInclusive value="0"/>
|
||||
<xs:maxInclusive value="3"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<!-- Map location, not SDF, but same data structure -->
|
||||
<xs:element name="bed" type="sdf"/>
|
||||
<xs:element ref="text" minOccurs="1" maxOccurs="2"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="if-day">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="day" type="xs:integer"/>
|
||||
<xs:element ref="text" minOccurs="1" maxOccurs="2"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="if-event">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="event" type="xs:integer"/>
|
||||
<xs:element name="day" type="xs:integer"/>
|
||||
<xs:element ref="text" minOccurs="1" maxOccurs="2"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="if-town">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="town" type="xs:integer"/>
|
||||
<xs:element ref="text" minOccurs="1" maxOccurs="2"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="shop">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="cost" type="costAdjust"/>
|
||||
<xs:element name="first" type="xs:integer"/>
|
||||
<xs:element name="count" type="xs:integer"/>
|
||||
<xs:element name="title" type="xs:string"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="type" use="required">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="items"/>
|
||||
<xs:enumeration value="mage"/>
|
||||
<xs:enumeration value="priest"/>
|
||||
<xs:enumeration value="recipe"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="train">
|
||||
<xs:complexType/>
|
||||
</xs:element>
|
||||
<xs:element name="heal">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="cost" type="costAdjust"/>
|
||||
<xs:element name="title" type="xs:string"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="sell">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<!-- Cost currently unused -->
|
||||
<xs:element name="cost" type="costAdjust"/>
|
||||
<xs:element ref="text" minOccurs="1" maxOccurs="2"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="type" use="required">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="weapons"/>
|
||||
<xs:enumeration value="armour"/>
|
||||
<xs:enumeration value="armor"/>
|
||||
<xs:enumeration value="any"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="identify">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="cost" type="xs:integer"/>
|
||||
<xs:element ref="text" minOccurs="1" maxOccurs="2"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="enchant">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<!-- Cost currently unused -->
|
||||
<xs:element name="cost" type="costAdjust"/>
|
||||
<xs:element name="type">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="+1"/>
|
||||
<xs:enumeration value="+2"/>
|
||||
<xs:enumeration value="+3"/>
|
||||
<xs:enumeration value="+5"/>
|
||||
<xs:enumeration value="shoot-flame"/>
|
||||
<xs:enumeration value="flaming"/>
|
||||
<xs:enumeration value="blessed"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element ref="text" minOccurs="1" maxOccurs="2"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="buy">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="cost" type="xs:integer"/>
|
||||
<xs:element ref="text" minOccurs="1" maxOccurs="2"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="buy-record">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="cost" type="xs:integer"/>
|
||||
<xs:element name="sdf" type="sdf"/>
|
||||
<xs:element ref="text" minOccurs="1" maxOccurs="2"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="vehicle">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="cost" type="xs:integer"/>
|
||||
<xs:element name="first" type="xs:integer"/>
|
||||
<xs:element name="count" type="xs:integer"/>
|
||||
<xs:element ref="text" minOccurs="1" maxOccurs="2"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="type">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="horse"/>
|
||||
<xs:enumeration value="boat"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="special">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="item" type="xs:integer"/>
|
||||
<xs:element name="cost" type="xs:integer"/>
|
||||
<xs:element ref="text" minOccurs="1" maxOccurs="2"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="junk">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="cost" type="costAdjust"/>
|
||||
<xs:element name="list">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:minInclusive value="0"/>
|
||||
<xs:maxInclusive value="4"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="title" type="xs:string"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="reveal-town">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="cost" type="xs:integer"/>
|
||||
<xs:element name="town" type="xs:integer"/>
|
||||
<xs:element ref="text" minOccurs="1" maxOccurs="2"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="call">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="special" type="xs:integer"/>
|
||||
<xs:element ref="text" minOccurs="1" maxOccurs="2"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="type" use="required">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="local"/>
|
||||
<xs:enumeration value="global"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:choice>
|
||||
<xs:element name="end" minOccurs="0" maxOccurs="1">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="force"/>
|
||||
<xs:enumeration value="hostile"/>
|
||||
<xs:enumeration value="crime"/>
|
||||
<xs:enumeration value="die"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="type" type="xs:integer"/>
|
||||
<xs:element name="param" minOccurs="0" maxOccurs="4" type="xs:integer"/>
|
||||
<xs:element name="text" minOccurs="1" maxOccurs="2" type="xs:string"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="for" type="xs:integer" use="required"/>
|
||||
</xs:complexType><!--
|
||||
|
||||
@@ -138,6 +138,19 @@
|
||||
<name>Sample Quest</name>
|
||||
<description>Your mission, if you choose to accept it, is...!!??</description>
|
||||
</quest>
|
||||
<!-- And shops -->
|
||||
<shop>
|
||||
<name>A Few Spells</name>
|
||||
<type>live</type>
|
||||
<prompt>spell</prompt>
|
||||
<face>43</face>
|
||||
<entries>
|
||||
<mage-spell>30</mage-spell>
|
||||
<mage-spell>31</mage-spell>
|
||||
<priest-spell>30</priest-spell>
|
||||
<priest-spell>31</priest-spell>
|
||||
</entries>
|
||||
</shop>
|
||||
</game>
|
||||
|
||||
<editor>
|
||||
|
||||
@@ -6,6 +6,15 @@
|
||||
<xs:enumeration value="false"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name='shop-amount'>
|
||||
<xs:union memberTypes='xs:integer'>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base='xs:string'>
|
||||
<xs:enumeration value='infinite'/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:union>
|
||||
</xs:simpleType>
|
||||
<xs:attributeGroup name="rect">
|
||||
<xs:attribute name="top" use="required" type="xs:integer"/>
|
||||
<xs:attribute name="left" use="required" type="xs:integer"/>
|
||||
@@ -84,6 +93,88 @@
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name='entries'>
|
||||
<xs:complexType>
|
||||
<xs:choice minOccurs='0' maxOccurs='30'>
|
||||
<xs:element name='item'>
|
||||
<xs:complexType><xs:simpleContent>
|
||||
<xs:extension base='xs:integer'>
|
||||
<xs:attribute name='quantity' type='shop-amount' default='0'/>
|
||||
<xs:attribute name='chance' type='xs:integer' default='100'/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent></xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name='mage-spell'>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base='xs:integer'>
|
||||
<xs:minInclusive value='0'/>
|
||||
<xs:maxInclusive value='61'/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name='priest-spell'>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base='xs:integer'>
|
||||
<xs:minInclusive value='0'/>
|
||||
<xs:maxInclusive value='61'/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name='recipe'>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base='xs:integer'>
|
||||
<xs:minInclusive value='0'/>
|
||||
<xs:maxInclusive value='19'/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name='skill'>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base='xs:integer'>
|
||||
<xs:minInclusive value='0'/>
|
||||
<xs:maxInclusive value='20'/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name='treasure'>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base='xs:integer'>
|
||||
<xs:minInclusive value='0'/>
|
||||
<xs:maxInclusive value='4'/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name='class'>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base='xs:integer'>
|
||||
<xs:minInclusive value='1'/>
|
||||
<xs:maxInclusive value='100'/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name='heal'>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base='xs:integer'>
|
||||
<xs:minInclusive value='0'/>
|
||||
<xs:maxInclusive value='9'/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name='special'>
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name='name' type='xs:string'/>
|
||||
<xs:element name='description' type='xs:string'/>
|
||||
<xs:element name='node' type='xs:integer'/>
|
||||
<xs:element name='quantity' type='shop-amount'/>
|
||||
<xs:element name='cost' type='xs:integer'/>
|
||||
<xs:element name='icon' type='xs:integer'/>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="game">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
@@ -136,7 +227,7 @@
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="quest" maxOccurs="unbounded">
|
||||
<xs:element name="quest" minOccurs='0' maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name='deadline' minOccurs='0'>
|
||||
@@ -162,6 +253,37 @@
|
||||
<xs:attribute name="start-with" type="bool"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="shop" minOccurs='0' maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name='name' type='xs:string'/>
|
||||
<xs:element name='type'>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base='xs:string'>
|
||||
<xs:enumeration value='live'/>
|
||||
<xs:enumeration value='dead'/>
|
||||
<xs:enumeration value='rand'/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name='prompt'>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base='xs:string'>
|
||||
<xs:enumeration value='shop'/>
|
||||
<xs:enumeration value='heal'/>
|
||||
<xs:enumeration value='mage'/>
|
||||
<xs:enumeration value='priest'/>
|
||||
<xs:enumeration value='spell'/>
|
||||
<xs:enumeration value='alch'/>
|
||||
<xs:enumeration value='train'/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name='face' type='xs:integer'/>
|
||||
<xs:element ref='entries'/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="timer" minOccurs="0" maxOccurs="20">
|
||||
<xs:complexType>
|
||||
<xs:simpleContent>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "shop.hpp"
|
||||
#include <numeric>
|
||||
#include <iostream>
|
||||
#include "mathutil.hpp"
|
||||
#include "graphtool.hpp" // for get_str
|
||||
|
||||
@@ -305,3 +306,69 @@ int cShopItem::getCost(int adj) {
|
||||
cost /= 10;
|
||||
return cost;
|
||||
}
|
||||
|
||||
std::istream& operator>>(std::istream& in, eShopType& type) {
|
||||
std::string name;
|
||||
in >> name;
|
||||
if(name == "live") type = eShopType::NORMAL;
|
||||
else if(name == "dead") type = eShopType::ALLOW_DEAD;
|
||||
else if(name == "rand") type = eShopType::RANDOM;
|
||||
else in.setstate(std::ios_base::failbit);
|
||||
return in;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, eShopType type) {
|
||||
switch(type) {
|
||||
case eShopType::NORMAL:
|
||||
out << "live";
|
||||
break;
|
||||
case eShopType::ALLOW_DEAD:
|
||||
out << "dead";
|
||||
break;
|
||||
case eShopType::RANDOM:
|
||||
out << "rand";
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::istream& operator>>(std::istream& in, eShopPrompt& type) {
|
||||
std::string name;
|
||||
in >> name;
|
||||
if(name == "shop") type = eShopPrompt::SHOPPING;
|
||||
else if(name == "heal") type = eShopPrompt::HEALING;
|
||||
else if(name == "mage") type = eShopPrompt::MAGE;
|
||||
else if(name == "priest") type = eShopPrompt::PRIEST;
|
||||
else if(name == "spell") type = eShopPrompt::SPELLS;
|
||||
else if(name == "alch") type = eShopPrompt::ALCHEMY;
|
||||
else if(name == "train") type = eShopPrompt::TRAINING;
|
||||
else in.setstate(std::ios_base::failbit);
|
||||
return in;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, eShopPrompt type) {
|
||||
switch(type) {
|
||||
case eShopPrompt::SHOPPING:
|
||||
out << "shop";
|
||||
break;
|
||||
case eShopPrompt::HEALING:
|
||||
out << "heal";
|
||||
break;
|
||||
case eShopPrompt::MAGE:
|
||||
out << "mage";
|
||||
break;
|
||||
case eShopPrompt::PRIEST:
|
||||
out << "priest";
|
||||
break;
|
||||
case eShopPrompt::SPELLS:
|
||||
out << "spell";
|
||||
break;
|
||||
case eShopPrompt::ALCHEMY:
|
||||
out << "alch";
|
||||
break;
|
||||
case eShopPrompt::TRAINING:
|
||||
out << "train";
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
#include "item.h"
|
||||
#include "pictypes.hpp" // for pic_num_t
|
||||
|
||||
@@ -89,4 +90,9 @@ public:
|
||||
void clear();
|
||||
};
|
||||
|
||||
std::istream& operator>>(std::istream& in, eShopType& type);
|
||||
std::ostream& operator<<(std::ostream& out, eShopType type);
|
||||
std::istream& operator>>(std::istream& in, eShopPrompt& type);
|
||||
std::ostream& operator<<(std::ostream& out, eShopPrompt type);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -47,6 +47,10 @@ template<> inline void ticpp::Printer::PushAttribute(std::string attrName, bool
|
||||
PushAttribute(attrName, attrVal ? "true" : "false");
|
||||
}
|
||||
|
||||
template<> inline void ticpp::Printer::PushElement(std::string attrName, bool attrVal) {
|
||||
PushAttribute(attrName, attrVal ? "true" : "false");
|
||||
}
|
||||
|
||||
template<> inline void ticpp::Printer::PushText(bool textVal) {
|
||||
PushText(textVal ? "true" : "false");
|
||||
}
|
||||
|
||||
@@ -217,6 +217,72 @@ static void writeScenarioToXml(ticpp::Printer&& data) {
|
||||
data.PushElement("description", quest.descr);
|
||||
data.CloseElement("quest");
|
||||
}
|
||||
for(size_t i = 0; i < scenario.shops.size(); i++) {
|
||||
cShop& shop = scenario.shops[i];
|
||||
data.OpenElement("shop");
|
||||
data.PushElement("name", shop.getName());
|
||||
data.PushElement("type", shop.getType());
|
||||
data.PushElement("prompt", shop.getPrompt());
|
||||
data.PushElement("face", shop.getFace());
|
||||
size_t num_entries = shop.size();
|
||||
data.OpenElement("entries");
|
||||
for(size_t j = 0; j < num_entries; j++) {
|
||||
cShopItem entry = shop.getItem(j);
|
||||
int quantity = entry.quantity, chance = 100;
|
||||
switch(entry.type) {
|
||||
case eShopItemType::EMPTY: break;
|
||||
case eShopItemType::OPTIONAL:
|
||||
quantity %= 1000;
|
||||
chance = entry.quantity / 1000;
|
||||
case eShopItemType::ITEM:
|
||||
data.OpenElement("item");
|
||||
if(quantity == 0)
|
||||
data.PushAttribute("quantity", "infinite");
|
||||
else data.PushAttribute("quantity", quantity);
|
||||
if(chance < 100)
|
||||
data.PushAttribute("chance", chance);
|
||||
data.PushText(entry.index);
|
||||
data.CloseElement("item");
|
||||
break;
|
||||
case eShopItemType::CALL_SPECIAL:
|
||||
data.OpenElement("special");
|
||||
data.PushElement("name", entry.item.full_name);
|
||||
data.PushElement("description", entry.item.desc);
|
||||
data.PushElement("node", entry.item.item_level);
|
||||
data.PushElement("quantity", entry.quantity);
|
||||
data.PushElement("cost", entry.item.value);
|
||||
data.PushElement("icon", entry.item.graphic_num);
|
||||
data.CloseElement("special");
|
||||
break;
|
||||
case eShopItemType::MAGE_SPELL:
|
||||
data.PushElement("mage-spell", entry.index);
|
||||
break;
|
||||
case eShopItemType::PRIEST_SPELL:
|
||||
data.PushElement("priest-spell", entry.index);
|
||||
break;
|
||||
case eShopItemType::ALCHEMY:
|
||||
data.PushElement("recipe", entry.index);
|
||||
break;
|
||||
case eShopItemType::SKILL:
|
||||
data.PushElement("skill", entry.index);
|
||||
break;
|
||||
case eShopItemType::TREASURE:
|
||||
data.PushElement("treasure", entry.index);
|
||||
break;
|
||||
case eShopItemType::CLASS:
|
||||
data.PushElement("class", entry.index);
|
||||
break;
|
||||
case eShopItemType::CURE_ACID: case eShopItemType::CURE_DISEASE: case eShopItemType::CURE_DUMBFOUNDING:
|
||||
case eShopItemType::CURE_PARALYSIS: case eShopItemType::CURE_POISON: case eShopItemType::DESTONE:
|
||||
case eShopItemType::HEAL_WOUNDS: case eShopItemType::RAISE_DEAD: case eShopItemType::REMOVE_CURSE:
|
||||
case eShopItemType::RESURRECT:
|
||||
data.PushElement("heal", int(entry.type) - int(eShopItemType::HEAL_WOUNDS));
|
||||
break;
|
||||
}
|
||||
}
|
||||
data.CloseElement("entries");
|
||||
data.CloseElement("shop");
|
||||
}
|
||||
for(int i = 0; i < 20; i++) {
|
||||
if(scenario.scenario_timer_times[i] > 0) {
|
||||
data.OpenElement("timer");
|
||||
@@ -676,7 +742,51 @@ static void writeTownToXml(ticpp::Printer&& data, cTown& town) {
|
||||
data.CloseElement("town");
|
||||
}
|
||||
|
||||
map_data buildOutMapData(location which) {
|
||||
static void writeDialogueToXml(ticpp::Printer&& data, cSpeech& talk) {
|
||||
data.OpenElement("dialogue");
|
||||
data.PushAttribute("boes", scenario.format_ed_version());
|
||||
for(size_t i = 0; i < 10; i++) {
|
||||
cPersonality& who = talk.people[i];
|
||||
data.OpenElement("personality");
|
||||
data.PushAttribute("id", i);
|
||||
data.PushElement("title", who.title);
|
||||
data.PushElement("look", who.look);
|
||||
data.PushElement("name", who.name);
|
||||
data.PushElement("job", who.job);
|
||||
data.CloseElement("personality");
|
||||
}
|
||||
for(size_t i = 0; i < 60; i++) {
|
||||
cSpeech::cNode& node = talk.talk_nodes[i];
|
||||
if(node.personality == -1) continue;
|
||||
// TODO: Is it safe to assume the two links run together like this?
|
||||
if(std::string(node.link1, 8) == "xxxxxxxx")
|
||||
continue;
|
||||
data.OpenElement("node");
|
||||
data.PushAttribute("for", node.personality);
|
||||
if(std::string(node.link1, 4) != "xxxx")
|
||||
data.PushElement("keyword", std::string(node.link1, 4));
|
||||
if(std::string(node.link2, 4) != "xxxx")
|
||||
data.PushElement("keyword", std::string(node.link2, 4));
|
||||
data.PushElement("type", int(node.type));
|
||||
if(node.extras[0] >= 0 || node.extras[1] >= 0 || node.extras[2] >= 0 || node.extras[3] >= 0)
|
||||
data.PushElement("param", node.extras[0]);
|
||||
if(node.extras[1] >= 0 || node.extras[2] >= 0 || node.extras[3] >= 0)
|
||||
data.PushElement("param", node.extras[1]);
|
||||
if(node.extras[2] >= 0 || node.extras[3] >= 0)
|
||||
data.PushElement("param", node.extras[2]);
|
||||
if(node.extras[3] >= 0)
|
||||
data.PushElement("param", node.extras[3]);
|
||||
if(!node.str1.empty())
|
||||
data.PushElement("text", node.str1);
|
||||
else data.PushElement("text");
|
||||
if(!node.str2.empty())
|
||||
data.PushElement("text", node.str2);
|
||||
data.CloseElement("node");
|
||||
}
|
||||
data.CloseElement("dialogue");
|
||||
}
|
||||
|
||||
static map_data buildOutMapData(location which) {
|
||||
cOutdoors& sector = *scenario.outdoors[which.x][which.y];
|
||||
map_data terrain;
|
||||
for(size_t x = 0; x < 48; x++) {
|
||||
@@ -718,7 +828,7 @@ map_data buildOutMapData(location which) {
|
||||
return terrain;
|
||||
}
|
||||
|
||||
map_data buildTownMapData(size_t which) {
|
||||
static map_data buildTownMapData(size_t which) {
|
||||
cTown& town = *scenario.towns[which];
|
||||
map_data terrain;
|
||||
for(size_t x = 0; x < town.max_dim(); x++) {
|
||||
@@ -774,11 +884,14 @@ void save_scenario(fs::path toFile) {
|
||||
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';
|
||||
// TODO: This is just a skeletal outline of what needs to be done to save the scenario
|
||||
tarball scen_file;
|
||||
{
|
||||
// First, write out the scenario header data. This is in a binary format identical to older scenarios.
|
||||
std::ostream& header = scen_file.newFile("scenario/header.exs");
|
||||
header.write(reinterpret_cast<char*>(&scenario.format), sizeof(scenario_header_flags));
|
||||
|
||||
// Next, the bulk scenario data.
|
||||
std::ostream& scen_data = scen_file.newFile("scenario/scenario.xml");
|
||||
@@ -835,16 +948,18 @@ void save_scenario(fs::path toFile) {
|
||||
writeSpecialNodes(town_spec, scenario.towns[i]->specials);
|
||||
|
||||
// Don't forget the dialogue nodes.
|
||||
std::ostream& town_talk = scen_file.newFile("scenario/towns/" + file_basename + "talk.xml");
|
||||
std::ostream& town_talk = scen_file.newFile("scenario/towns/talk" + std::to_string(i) + ".xml");
|
||||
writeDialogueToXml(ticpp::Printer("talk.xml", town_talk), scenario.towns[i]->talking);
|
||||
}
|
||||
giveError("Sorry, scenario saving is currently disabled.");
|
||||
return;
|
||||
|
||||
// Make sure it has the proper file extension
|
||||
std::string fname = toFile.filename().string();
|
||||
size_t dot = fname.find_last_of('.');
|
||||
if(dot == std::string::npos || fname.substr(dot) != ".boes")
|
||||
fname += ".boes";
|
||||
if(dot == std::string::npos || fname.substr(dot) != ".boes") {
|
||||
if(fname.substr(dot) == ".exs")
|
||||
fname.replace(dot,4,".boes");
|
||||
else fname += ".boes";
|
||||
}
|
||||
toFile = toFile.parent_path()/fname;
|
||||
|
||||
// Now write to zip file.
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include "map_parse.hpp"
|
||||
#include "graphtool.hpp"
|
||||
#include "mathutil.hpp"
|
||||
#include "gzstream.h"
|
||||
#include "tarball.hpp"
|
||||
|
||||
#include "porting.hpp"
|
||||
#include "restypes.hpp"
|
||||
@@ -35,14 +37,21 @@ static bool load_scenario_v1(fs::path file_to_load, cScenario& scenario);
|
||||
static bool load_outdoors_v1(fs::path scen_file, location which_out,cOutdoors& the_out, legacy::scenario_data_type& scenario);
|
||||
static bool load_town_v1(fs::path scen_file,short which_town,cTown& the_town,legacy::scenario_data_type& scenario,std::vector<shop_info_t>& shops);
|
||||
// Load new scenarios
|
||||
static bool load_scenario_v2(fs::path file_to_load, cScenario& scenario);
|
||||
static bool load_outdoors(fs::path out_base, location which_out, cOutdoors& the_out);
|
||||
static bool load_town(fs::path town_base, short which_town, cTown*& the_town);
|
||||
static bool load_town_talk(fs::path town_base, short which_town, cSpeech& the_talk);
|
||||
|
||||
bool load_scenario(fs::path file_to_load, cScenario& scenario) {
|
||||
scenario = cScenario();
|
||||
// TODO: Implement checking to determine whether it's old or new
|
||||
return load_scenario_v1(file_to_load, scenario);
|
||||
std::string fname = file_to_load.filename().string();
|
||||
size_t dot = fname.find_last_of('.');
|
||||
if(fname.substr(dot) == ".boes")
|
||||
return load_scenario_v2(file_to_load, scenario);
|
||||
else if(fname.substr(dot) == ".exs")
|
||||
return load_scenario_v1(file_to_load, scenario);
|
||||
giveError("That is not a Blades of Exile scenario.");
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Container> static void port_shop_spec_node(cSpecial& spec, std::vector<shop_info_t>& shops, Container strs) {
|
||||
@@ -92,6 +101,9 @@ bool load_scenario_v1(fs::path file_to_load, cScenario& scenario){
|
||||
(scenario.format.flag3 == 60) && (scenario.format.flag4 == 80)) {
|
||||
cur_scen_is_mac = false;
|
||||
file_ok = true;
|
||||
} else if(scenario.format.flag1 == 'O' && scenario.format.flag2 == 'B' && scenario.format.flag3 == 'O' && scenario.format.flag4 == 'E') {
|
||||
// This means we're looking at the scenario header file of an unpacked new-format scenario.
|
||||
return load_scenario_v2(file_to_load.parent_path(), scenario);
|
||||
}
|
||||
if(!file_ok) {
|
||||
fclose(file_id);
|
||||
@@ -235,6 +247,29 @@ bool load_scenario_v1(fs::path file_to_load, cScenario& scenario){
|
||||
return true;
|
||||
}
|
||||
|
||||
bool load_scenario_v2(fs::path file_to_load, cScenario& scenario) {
|
||||
// First determine whether we're dealing with a packed or unpacked scenario.
|
||||
bool is_packed;
|
||||
tarball pack;
|
||||
std::ifstream fin;
|
||||
if(fs::is_directory(file_to_load)) { // Unpacked
|
||||
is_packed = false;
|
||||
} else { // Packed
|
||||
igzstream gzin(file_to_load.string().c_str());
|
||||
pack.readFrom(gzin);
|
||||
}
|
||||
auto getFile = [&](std::string relpath) -> std::istream& {
|
||||
if(is_packed) return pack.getFile(relpath);
|
||||
if(fin.is_open()) fin.close();
|
||||
fin.open((file_to_load/relpath).string().c_str());
|
||||
// TODO: Suppress the warning about returning reference to local
|
||||
return fin;
|
||||
};
|
||||
// From here on, we don't have to care about whether it's packed or unpacked.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static long get_town_offset(short which_town, legacy::scenario_data_type& scenario){
|
||||
int i,j;
|
||||
long len_to_jump,store;
|
||||
|
||||
Reference in New Issue
Block a user