|
|
|
/*
|
|
|
|
License (OLC-3)
|
|
|
|
~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
Copyright 2018 - 2023 OneLoneCoder.com
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
|
|
are permitted provided that the following conditions are met:
|
|
|
|
|
|
|
|
1. Redistributions or derivations of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
|
|
|
|
2. Redistributions or derivative works in binary form must reproduce the above
|
|
|
|
copyright notice. This list of conditions and the following disclaimer must be
|
|
|
|
reproduced in the documentation and/or other materials provided with the distribution.
|
|
|
|
|
|
|
|
3. Neither the name of the copyright holder nor the names of its contributors may
|
|
|
|
be used to endorse or promote products derived from this software without specific
|
|
|
|
prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
|
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
|
|
|
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
|
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
|
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
|
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "olcPixelGameEngine.h"
|
|
|
|
#include <sstream>
|
|
|
|
#include "TMXParser.h"
|
|
|
|
#include "Map.h"
|
|
|
|
#include "olcUTIL_Geometry2D.h"
|
|
|
|
#include "olcUTIL_DataFile.h"
|
|
|
|
#include "Error.h"
|
|
|
|
|
|
|
|
using namespace olc;
|
|
|
|
|
|
|
|
struct Tileset{
|
|
|
|
XMLTag ImageData;
|
|
|
|
int tilewidth,tileheight;
|
|
|
|
std::map<int,XMLTag> ForegroundTileData;
|
|
|
|
std::map<int,XMLTag> UpperForegroundTileData;
|
|
|
|
std::map<int,TileCollisionData> CollisionData;
|
|
|
|
std::map<int,XMLTag> StaircaseData;
|
|
|
|
std::map<int,std::vector<int>> AnimationData;
|
|
|
|
std::set<int> ReflectiveData;
|
|
|
|
friend std::ostream& operator << (std::ostream& os, Tileset& rhs);
|
|
|
|
};
|
|
|
|
|
|
|
|
class TSXParser{
|
|
|
|
public:
|
|
|
|
Tileset&GetData();
|
|
|
|
private:
|
|
|
|
Tileset parsedTilesetInfo;
|
|
|
|
void ParseTag(std::string tag);
|
|
|
|
std::string previousTag;
|
|
|
|
int previousTagID;
|
|
|
|
std::string staircaseTag="";
|
|
|
|
public:
|
|
|
|
TSXParser(std::string file);
|
|
|
|
};
|
|
|
|
|
|
|
|
//#define TSX_PARSER_SETUP
|
|
|
|
|
|
|
|
#ifdef TSX_PARSER_SETUP
|
|
|
|
#undef TSX_PARSER_SETUP
|
|
|
|
extern bool _DEBUG_MAP_LOAD_INFO;
|
|
|
|
Tileset&TSXParser::GetData() {
|
|
|
|
return parsedTilesetInfo;
|
|
|
|
}
|
|
|
|
std::ostream&operator<<(std::ostream& os, Tileset& rhs){
|
|
|
|
os<<rhs.ImageData.FormatTagData(rhs.ImageData.data)<<"\n";
|
|
|
|
return os;
|
|
|
|
}
|
|
|
|
void TSXParser::ParseTag(std::string tag) {
|
|
|
|
XMLTag newTag;
|
|
|
|
//First character is a '<' so we discard it.
|
|
|
|
tag.erase(0,1); tag.erase(tag.length()-1,1); //Erase the first and last characters in the tag. Now parse by spaces.
|
|
|
|
std::stringstream s(tag); //Turn it into a string stream to now parse into individual whitespaces.
|
|
|
|
std::string data;
|
|
|
|
while (s.good()) {
|
|
|
|
int quotationMarkCount=0;
|
|
|
|
bool pastEquals=false;
|
|
|
|
data="";
|
|
|
|
bool valid=false;
|
|
|
|
while(s.good()){
|
|
|
|
int character=s.get();
|
|
|
|
if(character=='"'){
|
|
|
|
quotationMarkCount++;
|
|
|
|
}
|
|
|
|
if(character==' '&"ationMarkCount%2==0){
|
|
|
|
valid=true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
data+=character;
|
|
|
|
if(pastEquals&"ationMarkCount%2==0){
|
|
|
|
valid=true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(character=='='&"ationMarkCount%2==0){
|
|
|
|
pastEquals=true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(valid&&data.length()>0){
|
|
|
|
if (newTag.tag.length()==0) { //Tag's empty, so first line is the tag.
|
|
|
|
newTag.tag=data;
|
|
|
|
if(_DEBUG_MAP_LOAD_INFO)std::cout<<"Tag: "<<newTag.tag<<"\n";
|
|
|
|
} else {
|
|
|
|
std::string key = data.substr(0,data.find("="));
|
|
|
|
std::string value = data.substr(data.find("=")+1,std::string::npos);
|
|
|
|
|
|
|
|
//Strip Quotation marks.
|
|
|
|
value = value.substr(1,std::string::npos);
|
|
|
|
value = value.substr(0,value.length()-1);
|
|
|
|
|
|
|
|
newTag.data[key]=value;
|
|
|
|
if(_DEBUG_MAP_LOAD_INFO)std::cout<<" "<<key<<":"<<newTag.data[key]<<"\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newTag.tag=="tileset") {
|
|
|
|
parsedTilesetInfo.tilewidth=stoi(newTag.data["tilewidth"]);
|
|
|
|
parsedTilesetInfo.tileheight=stoi(newTag.data["tileheight"]);
|
|
|
|
} else
|
|
|
|
if (newTag.tag=="image") {
|
|
|
|
parsedTilesetInfo.ImageData=newTag;
|
|
|
|
} else
|
|
|
|
if (newTag.tag=="tile"&&newTag.data["type"]=="ForegroundTile"){
|
|
|
|
previousTag=newTag.tag;
|
|
|
|
previousTagID=newTag.GetInteger("id");
|
|
|
|
parsedTilesetInfo.ForegroundTileData[newTag.GetInteger("id")]=newTag;
|
|
|
|
} else
|
|
|
|
if (newTag.tag=="tile"&&newTag.data["type"]=="UpperForegroundTile"){
|
|
|
|
previousTag=newTag.tag;
|
|
|
|
previousTagID=newTag.GetInteger("id");
|
|
|
|
parsedTilesetInfo.UpperForegroundTileData[newTag.GetInteger("id")]=newTag;
|
|
|
|
} else
|
|
|
|
if (newTag.tag=="tile"&&newTag.data["type"]=="Staircase"){
|
|
|
|
previousTag=newTag.tag;
|
|
|
|
staircaseTag=newTag.tag;
|
|
|
|
previousTagID=newTag.GetInteger("id");
|
|
|
|
} else
|
|
|
|
if (newTag.tag=="tile"&&newTag.data["type"]=="Reflective"){
|
|
|
|
previousTag=newTag.tag;
|
|
|
|
previousTagID=newTag.GetInteger("id");
|
|
|
|
parsedTilesetInfo.ReflectiveData.insert(newTag.GetInteger("id"));
|
|
|
|
} else
|
|
|
|
if (newTag.tag=="tile"){
|
|
|
|
previousTag=newTag.tag;
|
|
|
|
previousTagID=newTag.GetInteger("id");
|
|
|
|
} else
|
|
|
|
if (newTag.tag=="frame"){
|
|
|
|
//The way animation data is stored is every "animation_tile_precision" ms indicating which frame we should be on.
|
|
|
|
for(int i=0;i<newTag.GetInteger("duration")/"animation_tile_precision"_I;i++){
|
|
|
|
parsedTilesetInfo.AnimationData[previousTagID].push_back(newTag.GetInteger("tileid"));
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
if (newTag.tag=="property"&&staircaseTag=="tile"){
|
|
|
|
parsedTilesetInfo.StaircaseData[previousTagID]=newTag;
|
|
|
|
staircaseTag="";
|
|
|
|
} else
|
|
|
|
if (newTag.tag=="object"&&previousTag=="tile"){
|
|
|
|
TileCollisionData data;
|
|
|
|
data.collision=geom2d::rect<int>{{newTag.GetInteger("x"),newTag.GetInteger("y")},{newTag.GetInteger("width"),newTag.GetInteger("height")}};
|
|
|
|
if(parsedTilesetInfo.CollisionData.count(previousTagID)){
|
|
|
|
if(_DEBUG_MAP_LOAD_INFO)ERR("WARNING! There was already collision data defined for tile "<<previousTagID<<"!")
|
|
|
|
}
|
|
|
|
parsedTilesetInfo.CollisionData[previousTagID]=data;
|
|
|
|
}
|
|
|
|
if(_DEBUG_MAP_LOAD_INFO)std::cout<<"\n"<<"=============\n";
|
|
|
|
}
|
|
|
|
TSXParser::TSXParser(std::string file)
|
|
|
|
:previousTagID(-1){
|
|
|
|
std::ifstream f(file,std::ios::in);
|
|
|
|
|
|
|
|
std::string accumulator="";
|
|
|
|
|
|
|
|
while (f.good()) {
|
|
|
|
std::string data;
|
|
|
|
f>>data;
|
|
|
|
if (data.empty()) continue;
|
|
|
|
|
|
|
|
if (accumulator.length()>0) {
|
|
|
|
accumulator+=" "+data;
|
|
|
|
//Check if it ends with '>'
|
|
|
|
if (data[data.length()-1]=='>') {
|
|
|
|
ParseTag(accumulator);
|
|
|
|
accumulator="";
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
if (data[0]=='<') {
|
|
|
|
//Beginning of XML tag.
|
|
|
|
accumulator=data;
|
|
|
|
if(accumulator.length()>1&&accumulator.at(1)=='/'){
|
|
|
|
accumulator=""; //Restart because this is an end tag.
|
|
|
|
}
|
|
|
|
if(accumulator.length()>1&&accumulator.find('>')!=std::string::npos){
|
|
|
|
accumulator=""; //Restart because this tag has nothing in it!
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(_DEBUG_MAP_LOAD_INFO)std::cout<<"Parsed Tileset Data:\n"<<parsedTilesetInfo<<"\n";
|
|
|
|
}
|
|
|
|
#endif
|