Implement saving monster definitions to XML

This commit is contained in:
2015-01-22 14:33:28 -05:00
parent 670e35f9d5
commit 3e0e3d3b8a
7 changed files with 376 additions and 71 deletions

View File

@@ -11,6 +11,7 @@
#include <map>
#include <sstream>
#include <iostream>
#include <boost/lexical_cast.hpp>
#include "classes.h"
#include "oldstructs.h"
@@ -467,15 +468,82 @@ std::istream& operator >> (std::istream& in, eStatus& e){
}
std::ostream& operator << (std::ostream& out, eRace e){
return out << (int) e;
switch(e) {
case eRace::HUMAN: out << "human"; break;
case eRace::NEPHIL: out << "nephil"; break;
case eRace::SLITH: out << "slith"; break;
case eRace::VAHNATAI: out << "vahnatai"; break;
case eRace::HUMANOID: out << "humanoid"; break;
case eRace::BEAST: out << "beast"; break;
case eRace::BIRD: out << "bird"; break;
case eRace::BUG: out << "bug"; break;
case eRace::DEMON: out << "demon"; break;
case eRace::DRAGON: out << "dragon"; break;
case eRace::GIANT: out << "giant"; break;
case eRace::IMPORTANT: out << "important"; break;
case eRace::MAGE: out << "mage"; break;
case eRace::PRIEST: out << "priest"; break;
case eRace::MAGICAL: out << "magic"; break;
case eRace::PLANT: out << "plant"; break;
case eRace::REPTILE: out << "reptile"; break;
case eRace::SLIME: out << "slime"; break;
case eRace::STONE: out << "stone"; break;
case eRace::UNDEAD: out << "undead"; break;
case eRace::UNKNOWN: out << "humanoid"; break;
}
return out;
}
std::istream& operator >> (std::istream& in, eRace& e){
int i;
in >> i;
if(i > 0 && i < 20)
e = (eRace) i;
else e = eRace::HUMAN;
std::string key;
in >> key;
e = eRace::HUMANOID;
try {
int i = boost::lexical_cast<int>(key);
if(i > 0 && i < 20)
e = (eRace) i;
} catch(boost::bad_lexical_cast) {
if(key == "human")
e = eRace::HUMAN;
else if(key == "nephil")
e = eRace::NEPHIL;
else if(key == "slith")
e = eRace::SLITH;
else if(key == "vahnatai")
e = eRace::VAHNATAI;
else if(key == "humanoid")
e = eRace::HUMANOID;
else if(key == "beast")
e = eRace::BEAST;
else if(key == "bird")
e = eRace::BIRD;
else if(key == "bug")
e = eRace::BUG;
else if(key == "demon")
e = eRace::DEMON;
else if(key == "dragon")
e = eRace::DRAGON;
else if(key == "giant")
e = eRace::GIANT;
else if(key == "important")
e = eRace::IMPORTANT;
else if(key == "mage")
e = eRace::MAGE;
else if(key == "priest")
e = eRace::PRIEST;
else if(key == "magic")
e = eRace::MAGICAL;
else if(key == "plant")
e = eRace::PLANT;
else if(key == "reptile")
e = eRace::REPTILE;
else if(key == "slime")
e = eRace::SLIME;
else if(key == "stone")
e = eRace::STONE;
else if(key == "undead")
e = eRace::UNDEAD;
}
return in;
}

View File

@@ -116,17 +116,17 @@ public:
unsigned short dice, sides;
eMonstMelee type;
};
unsigned char level;
unsigned int level;
std::string m_name;
short m_health;
unsigned char armor;
unsigned char skill;
unsigned int armor;
unsigned int skill;
cAttack a[3];
eRace m_type;
unsigned char speed;
unsigned char mu;
unsigned char cl;
unsigned char treasure;
unsigned int speed;
unsigned int mu;
unsigned int cl;
unsigned int treasure;
std::map<eMonstAbil, uAbility> abil;
item_num_t corpse_item;
short corpse_item_chance;
@@ -139,9 +139,9 @@ public:
bool invisible : 1;
bool guard : 1;
char : 4;
unsigned char x_width,y_width;
unsigned char default_attitude;
unsigned char summon_type;
unsigned int x_width,y_width;
unsigned int default_attitude;
unsigned int summon_type;
pic_num_t default_facial_pic;
pic_num_t picture_num;
snd_num_t ambient_sound; // has a chance of being played every move

View File

@@ -30,6 +30,10 @@ void Printer::CloseElement(std::string tagName) {
PushNode(top);
}
void Printer::PushElement(std::string tagName) {
PushNode(new Element(tagName));
}
void Printer::PushComment(std::string comment) {
PushNode(new Comment(comment));
}

View File

@@ -25,6 +25,7 @@ namespace ticpp {
~Printer();
void OpenElement(std::string tagName);
void CloseElement(std::string tagName);
void PushElement(std::string tagName);
void PushComment(std::string comment);
void PushStylesheet(std::string value, std::string href);
void PushNode(Node* node);

View File

@@ -4,6 +4,7 @@
#include "classes.h"
#include <iostream>
#include <fstream>
#include <iomanip>
#include "scen.fileio.h"
#include "scen.keydlgs.h"
#include "graphtool.hpp"
@@ -60,6 +61,15 @@ template<> void ticpp::Printer::PushElement(std::string tagName, location pos) {
CloseElement(tagName);
}
template<> void ticpp::Printer::PushElement(std::string tagName, cMonster::cAttack attack) {
OpenElement(tagName);
PushAttribute("type", attack.type);
std::ostringstream strength;
strength << attack.dice << 'd' << attack.sides;
PushText(strength.str());
CloseElement(tagName);
}
static bool is_minmax(int lo, int hi, int val) {
return minmax(lo, hi, val) == val;
}
@@ -291,6 +301,150 @@ void writeItemsToXml(ticpp::Printer&& data) {
data.CloseElement("items");
}
void writeMonstersToXml(ticpp::Printer&& data) {
std::ostringstream str;
data.OpenElement("monsters");
for(size_t i = 1; i < scenario.scen_monsters.size(); i++) {
data.OpenElement("monster");
data.PushAttribute("id", i);
cMonster& monst = scenario.scen_monsters[i];
data.PushElement("name", monst.m_name);
if(monst.default_facial_pic >= 0)
data.PushElement("default-face", monst.default_facial_pic);
data.OpenElement("pic");
data.PushAttribute("w", monst.x_width);
data.PushAttribute("h", monst.y_width);
data.PushText(monst.picture_num);
data.CloseElement("pic");
data.PushElement("race", monst.m_type);
data.PushElement("level", monst.level);
data.PushElement("armor", monst.armor);
data.PushElement("skill", monst.skill);
data.PushElement("hp", monst.m_health);
data.PushElement("speed", monst.speed);
data.PushElement("attitude", monst.default_attitude);
data.PushElement("summon", monst.summon_type);
data.PushElement("treasure", monst.treasure);
if(monst.mu > 0)
data.PushElement("mage", monst.mu);
if(monst.cl > 0)
data.PushElement("priest", monst.cl);
if(monst.ambient_sound > 0)
data.PushElement("voice", monst.ambient_sound);
if(monst.see_spec >= 0)
data.PushElement("onsight", monst.see_spec);
data.OpenElement("attacks");
data.PushElement("attack", monst.a[0]);
if(monst.a[1].dice > 0 || monst.a[2].dice > 0)
data.PushElement("attack", monst.a[1]);
if(monst.a[2].dice > 0)
data.PushElement("attack", monst.a[2]);
data.CloseElement("attacks");
data.OpenElement("immunity");
if(monst.magic_res != 100)
data.PushElement("magic", monst.magic_res);
if(monst.fire_res != 100)
data.PushElement("fire", monst.magic_res);
if(monst.cold_res != 100)
data.PushElement("cold", monst.magic_res);
if(monst.poison_res != 100)
data.PushElement("poison", monst.magic_res);
if(monst.mindless) data.PushElement("fear", true);
if(monst.invuln) data.PushElement("all", true);
data.CloseElement("immunity");
if(monst.corpse_item_chance > 0) {
data.OpenElement("loot");
data.PushElement("type", monst.corpse_item);
data.PushElement("chance", monst.corpse_item_chance);
data.CloseElement("loot");
}
if(monst.invisible || monst.guard || !monst.abil.empty()) {
data.OpenElement("abilities");
if(monst.invisible) data.PushElement("invisible");
if(monst.guard) data.PushElement("guard");
for(auto& p : monst.abil) {
if(p.first == eMonstAbil::NO_ABIL || !p.second.active) continue;
str.str("");
eMonstAbil abil = p.first;
uAbility& param = p.second;
switch(getMonstAbilCategory(abil)) {
case eMonstAbilCat::GENERAL:
data.OpenElement("general");
data.PushAttribute("type", abil);
data.PushElement("type", param.gen.type);
if(param.gen.type != eMonstGen::TOUCH) {
data.PushElement("missile", param.gen.pic);
data.PushElement("range", param.gen.range);
}
data.PushElement("strength", param.gen.strength);
str << std::fixed << std::setprecision(1) << float(param.gen.odds)/10;
data.PushElement("chance", str.str());
if(abil == eMonstAbil::DAMAGE || abil == eMonstAbil::DAMAGE2)
data.PushElement("extra", param.gen.dmg);
else if(abil == eMonstAbil::STATUS || abil == eMonstAbil::STATUS2 || abil == eMonstAbil::STUN)
data.PushElement("extra", param.gen.stat);
else if(abil == eMonstAbil::FIELD)
data.PushElement("extra", param.gen.fld);
data.CloseElement("general");
break;
case eMonstAbilCat::MISSILE:
data.OpenElement("missile");
data.PushAttribute("type", abil);
data.PushElement("type", param.missile.type);
data.PushElement("missile", param.missile.pic);
data.OpenElement("strength");
data.PushText(std::to_string(param.missile.dice) + 'd' + std::to_string(param.missile.sides));
data.CloseElement("strength");
data.PushElement("skill", param.missile.skill);
data.PushElement("range", param.missile.range);
str << std::fixed << std::setprecision(1) << float(param.missile.odds)/10;
data.PushElement("chance", str.str());
data.CloseElement("missile");
break;
case eMonstAbilCat::SUMMON:
data.OpenElement("summon");
data.PushAttribute("type", abil);
data.PushElement("type", param.summon.type);
data.PushElement("min", param.summon.min);
data.PushElement("max", param.summon.max);
data.PushElement("duration", param.summon.len);
data.PushElement("chance", param.summon.chance);
data.CloseElement("summon");
break;
case eMonstAbilCat::RADIATE:
data.OpenElement("radiate");
data.PushAttribute("type", abil);
data.PushElement("type", param.radiate.type);
data.PushElement("chance", param.radiate.chance);
data.CloseElement("radiate");
break;
case eMonstAbilCat::SPECIAL:
data.OpenElement("special");
data.PushAttribute("type", abil);
data.PushElement("param", param.special.extra1);
if(abil != eMonstAbil::SPLITS && abil != eMonstAbil::DEATH_TRIGGER) {
data.PushElement("param", param.special.extra2);
if(abil == eMonstAbil::RAY_HEAT)
data.PushElement("param", param.special.extra3);
}
data.CloseElement("special");
break;
case eMonstAbilCat::INVALID: break;
}
}
data.CloseElement("abilities");
}
data.CloseElement("monster");
}
data.CloseElement("monsters");
}
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;
@@ -316,6 +470,7 @@ void save_scenario(fs::path toFile) {
// ...and monsters
std::ostream& monsters = scen_file.newFile("scenario/monsters.xml");
writeMonstersToXml(ticpp::Printer("monsters.xml", monsters));
// And the special nodes.
std::ostream& scen_spec = scen_file.newFile("scenario/scenario.spec");