diff --git a/assets/HamsterGame.tiled-project b/assets/HamsterGame.tiled-project new file mode 100644 index 0000000..d0eb592 --- /dev/null +++ b/assets/HamsterGame.tiled-project @@ -0,0 +1,14 @@ +{ + "automappingRulesFile": "", + "commands": [ + ], + "compatibilityVersion": 1100, + "extensionsPath": "extensions", + "folders": [ + "." + ], + "properties": [ + ], + "propertyTypes": [ + ] +} diff --git a/assets/HamsterGame.tiled-session b/assets/HamsterGame.tiled-session new file mode 100644 index 0000000..4ca9167 --- /dev/null +++ b/assets/HamsterGame.tiled-session @@ -0,0 +1,23 @@ +{ + "Map/SizeTest": { + "height": 4300, + "width": 2 + }, + "activeFile": "", + "expandedProjectPaths": [ + ], + "fileStates": { + }, + "last.imagePath": "C:/Users/sigon/source/repos/hamster/assets", + "map.lastUsedFormat": "tmx", + "openFiles": [ + ], + "project": "HamsterGame.tiled-project", + "recentFiles": [ + ], + "tileset.lastUsedFormat": "tsx", + "tileset.tileSize": { + "height": 16, + "width": 16 + } +} diff --git a/assets/Terrain.tsx b/assets/Terrain.tsx new file mode 100644 index 0000000..4e940f7 --- /dev/null +++ b/assets/Terrain.tsx @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/TestLevel.tmx b/assets/TestLevel.tmx new file mode 100644 index 0000000..2a5c67d --- /dev/null +++ b/assets/TestLevel.tmx @@ -0,0 +1,28 @@ + + + + + +1206,494,494,1179,1181,1235,1151,1236,1154,1234,494,1238,1209,494,1079,1078,298,1082,1050,993,1054,1138,1022,1109,1079,1081,993,1082,1134,1137, +1262,1180,1181,1182,1209,494,494,494,1207,1206,1177,1235,1237,494,1079,1134,1135,1138,1134,1053,1107,1026,1078,1021,1054,1134,1135,1138,1022,1026, +1152,1236,1237,1235,1237,494,494,1149,1235,1237,494,1179,1181,494,1107,1026,1022,1024,1108,1109,298,1054,1078,1021,1054,1022,1025,1023,1109,1082, +1179,1265,1180,1180,1263,1181,1179,1264,1265,1263,1180,1266,1178,494,1080,1054,1050,1051,1053,1021,298,1079,1078,993,1054,1134,1136,1137,1136,1138, +1235,1152,1151,1153,1154,1178,1182,1150,1151,1236,1151,1152,1178,494,1051,1138,1134,1138,1134,1136,1053,1110,1134,1053,1107,1023,1025,1024,1024,1025, +1179,1180,1180,1263,1266,1209,1182,1178,494,494,1177,1235,1179,494,1079,1022,1026,1022,1026,1022,1109,1110,1022,1109,993,298,993,1051,1053,298, +1238,1150,1151,1153,1151,1237,1235,1237,1121,1149,494,494,1238,494,1079,1081,1054,1078,1079,1078,1051,1138,1081,993,1051,1052,1136,1138,1134,1136, +1238,1234,1179,1263,1181,1177,1121,494,1121,494,494,494,1235,494,1079,1078,1107,1109,1079,1078,1082,1022,1109,993,1082,1022,1024,1026,1022,1026, +1235,1237,1235,1152,1237,1121,1149,494,494,494,1177,494,494,494,1079,1134,1137,1052,1138,1050,1079,1050,993,298,1054,1078,1021,1054,1081,1079, +1179,1265,1264,1181,1179,1181,1149,1177,1177,1177,1121,494,1177,494,1107,1108,1024,1023,1108,1109,1054,1081,298,1021,1082,1081,1051,1138,1078,1079, +1235,1153,1154,1234,1207,1234,494,1177,494,494,494,494,494,494,1021,298,1021,298,1051,1137,1138,1106,993,1021,1110,1134,1138,1022,1109,1082, +1181,1179,1266,1178,1182,1234,494,494,494,1149,494,1179,1181,494,1051,1136,1137,1137,1138,1022,1025,1109,993,993,1110,1022,1026,1134,1137,1138, +1234,1235,1151,1237,1266,1209,1149,1179,1180,1180,1263,1266,1209,494,1107,1108,1108,1108,1108,1109,993,1021,1080,298,1107,1109,1107,1108,1108,1108, +1206,1149,1121,1238,1266,1262,1264,1266,1150,1236,1153,1236,1237,494,494,494,494,494,494,494,494,494,494,494,494,494,494,494,494,494, +1209,494,1177,1210,1150,1153,1152,1154,1206,494,494,494,494,494,1121,1121,1149,1179,1180,1181,1207,1206,1177,1121,494,494,1149,494,494,1121, +1209,494,1177,1235,1237,1121,1177,1207,1234,1149,1121,494,494,1177,494,494,1149,1235,1154,1206,1235,1237,1179,1181,494,494,494,494,494,1149, +1178,494,494,494,1121,494,1121,1238,1209,494,494,494,494,494,1149,494,1121,494,1182,1262,1263,1265,1266,1209,494,494,494,494,1179,1264, +1237,1149,1177,494,494,494,494,1238,1178,494,1121,1179,1265,1265,1180,1181,494,1121,1235,1151,1154,1150,1154,1206,1149,494,494,1149,1235,1152, +494,1121,1149,1121,494,1149,1179,1266,1206,494,1177,1207,1150,1236,1151,1237,494,1177,1121,494,1238,1234,1235,1237,494,494,494,494,494,1149, +494,494,494,494,494,494,1235,1154,1234,1179,1263,1266,1209,1121,494,1177,494,1149,1177,494,1210,1209,494,1177,494,494,1149,1121,1121,494 + + + diff --git a/hamster.vcxproj b/hamster.vcxproj index d7bf606..cd0067e 100644 --- a/hamster.vcxproj +++ b/hamster.vcxproj @@ -366,6 +366,14 @@ if %errorlevel% neq 0 goto :VCEnd + + + + + + + + diff --git a/hamster.vcxproj.filters b/hamster.vcxproj.filters index b3c8a46..207f8ab 100644 --- a/hamster.vcxproj.filters +++ b/hamster.vcxproj.filters @@ -76,5 +76,11 @@ Header Files + + Header Files + + + Header Files + \ No newline at end of file diff --git a/src/Border.cpp b/src/Border.cpp index 1e60985..5eb8877 100644 --- a/src/Border.cpp +++ b/src/Border.cpp @@ -40,7 +40,7 @@ All rights reserved. #include const std::unordered_map>Border::BORDER_TEMPLATES{ - {Border::DEFAULT,{"border.png",{{{170,97,124},{114,109,163}},0.6f},{{{252,119,144}},1.2f},{{{231,129,97}},3.6f}}}, + {Border::DEFAULT,{"border.png",{{{170,97,124},{114,109,163}},0.6f},{{{252,119,144},{230,131,131},{170,97,123},{230,131,131}},1.2f},{{{231,129,97}},3.6f}}}, }; Border::CycleTimer::CycleTimer(const std::vectorcycle,const float repeatTime) diff --git a/src/HamsterGame.cpp b/src/HamsterGame.cpp index fc9fcf9..a2d119e 100644 --- a/src/HamsterGame.cpp +++ b/src/HamsterGame.cpp @@ -1,6 +1,7 @@ #include "HamsterGame.h" #include "Hamster.h" #include +#include geom2d::rectHamsterGame::SCREEN_FRAME{{96,0},{320,288}}; std::unordered_map>HamsterGame::ANIMATIONS; @@ -20,7 +21,7 @@ bool HamsterGame::OnUserCreate(){ tv.SetWorldOffset(-SCREEN_FRAME.pos); LoadGraphics(); LoadAnimations(); - LoadLevel(); //THIS IS TEMPORARY. + LoadLevel("TestLevel.tmx"); //THIS IS TEMPORARY. border.ChangeBorder(Border::DEFAULT); return true; @@ -34,6 +35,7 @@ void HamsterGame::_LoadImage(const std::string_view img){ void HamsterGame::LoadGraphics(){ _LoadImage("border.png"); + _LoadImage("gametiles.png"); } void HamsterGame::LoadAnimations(){ @@ -56,8 +58,11 @@ void HamsterGame::LoadAnimations(){ LoadAnimation(DEFAULT,"hamster.png",{{0,32},{32,32}},0.3f); } -void HamsterGame::LoadLevel(){ +void HamsterGame::LoadLevel(const std::string_view mapName){ const vf2d levelSpawnLoc{50,50}; //TEMPORARY + + currentMap=TMXParser{ASSETS_DIR+std::string(mapName)}; + Hamster::LoadHamsters(levelSpawnLoc); camera.SetTarget(Hamster::GetPlayer().GetPos()); } @@ -70,11 +75,29 @@ void HamsterGame::UpdateGame(const float fElapsedTime){ } void HamsterGame::DrawGame(){ - tv.FillRectDecal({10,10},{500.f,150.f},WHITE); + DrawLevelTiles(); Hamster::DrawHamsters(tv); border.Draw(); } +void HamsterGame::DrawLevelTiles(){ + for(float y=tv.GetWorldTL().y-16;y<=tv.GetWorldBR().y+16;y+=16){ + for(float x=tv.GetWorldTL().x-1+SCREEN_FRAME.pos.x;x<=tv.GetWorldBR().x+16+SCREEN_FRAME.pos.x;x+=16){ + if(x<=0.f||y<=0.f||x>=currentMap.value().GetData().GetMapData().width*16||y>=currentMap.value().GetData().GetMapData().height*16)continue; + const int numTilesWide{GetGFX("gametiles.png").Sprite()->width/16}; + const int numTilesTall{GetGFX("gametiles.png").Sprite()->height/16}; + + int tileX{int(floor(x)/16)}; + int tileY{int(floor(y)/16)}; + int tileID{currentMap.value().GetData().GetLayers()[0].tiles[tileY][tileX]-1}; + + int imgTileX{tileID%numTilesWide}; + int imgTileY{tileID/numTilesWide}; + tv.DrawPartialDecal(vf2d{float(tileX),float(tileY)}*16,GetGFX("gametiles.png").Decal(),vf2d{float(imgTileX),float(imgTileY)}*16,{16,16}); + } + } +} + bool HamsterGame::OnUserUpdate(float fElapsedTime){ UpdateGame(fElapsedTime); DrawGame(); diff --git a/src/HamsterGame.h b/src/HamsterGame.h index 9867b85..e230d75 100644 --- a/src/HamsterGame.h +++ b/src/HamsterGame.h @@ -42,6 +42,7 @@ All rights reserved. #include "olcPGEX_TransformedView.h" #include "olcUTIL_Camera2D.h" #include "Border.h" +#include "TMXParser.h" class HamsterGame : public olc::PixelGameEngine { @@ -68,10 +69,12 @@ private: Camera2D camera; void LoadGraphics(); void LoadAnimations(); - void LoadLevel(); + void LoadLevel(const std::string_view mapName); void _LoadImage(const std::string_view img); static std::unordered_mapGFX; static std::unordered_map>ANIMATIONS; static PixelGameEngine*self; Border border; + void DrawLevelTiles(); + std::optionalcurrentMap; }; \ No newline at end of file diff --git a/src/TMXParser.h b/src/TMXParser.h new file mode 100644 index 0000000..d1e8761 --- /dev/null +++ b/src/TMXParser.h @@ -0,0 +1,395 @@ +#pragma region License +/* +License (OLC-3) +~~~~~~~~~~~~~~~ + +Copyright 2024 Joshua Sigona + +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. + +Portions of this software are copyright © 2024 The FreeType +Project (www.freetype.org). Please see LICENSE_FT.txt for more information. +All rights reserved. +*/ +#pragma endregion +#pragma once +#include "olcUTIL_Geometry2D.h" +#include "olcPixelGameEngine.h" +#include +#include +#include + +using MapName=std::string; +using namespace olc; +using namespace std::literals; + +struct XMLTag{ + std::string tag; + std::map data; + const std::string FormatTagData(std::maptiles); + friend std::ostream& operator << (std::ostream& os, XMLTag& rhs); + std::string str(); + int GetInteger(std::string dataTag); + float GetFloat(std::string dataTag); + double GetDouble(std::string dataTag); + bool GetBool(std::string dataTag); + std::string GetString(std::string dataTag)const; +}; + +struct ForegroundTileTag:public XMLTag{ + //Whether or not to hide this foreground tile + bool hide=true; +}; + +struct MapTag{ + int width=0,height=0; + int tilewidth=0,tileheight=0; + bool optimized=false; //An optimized map will require us to flatten it out and use it as a single tile. + bool provideOptimization=false; //An optimized map will require us to flatten it out and use it as a single tile. + vi2d playerSpawnLocation; + vi2d MapSize; //The number of tiles in width and height of this map. + vi2d TileSize; //How large in pixels the map's tiles are. + MapTag(); + MapTag(int width,int height,int tilewidth,int tileheight); + friend std::ostream& operator << (std::ostream& os, MapTag& rhs); +}; + +struct LayerTag{ + XMLTag tag; + std::vector> tiles; + std::string unlockCondition=""; + std::string str(); +}; + +struct ZoneData{ + geom2d::rectzone; + bool isUpper=false; + std::vectorproperties; +}; + +struct Map{ + friend class AiL; + friend class TMXParser; +private: + MapTag MapData; + std::string name; + Renderable*optimizedTile=nullptr; + std::vector TilesetData; + std::vector LayerData; + std::string mapType=""; + std::setspawns; + std::map>ZoneData; +public: + Map(); + void _SetMapData(MapTag data); + bool skipLoadoutScreen=false; + const MapTag&GetMapData()const; + const std::string_view GetMapType()const; + const std::vector&GetLayers()const; + const MapName&GetMapName()const; + const std::string_view GetMapDisplayName()const; + const Renderable*const GetOptimizedMap()const; + const std::map>&GetZones()const; + std::string FormatLayerData(std::ostream& os, std::vectortiles); + friend std::ostream& operator << (std::ostream& os, Map& rhs); + friend std::ostream& operator << (std::ostream& os, std::vector& rhs); +}; + +struct Property{ + std::string name; + std::string value; + int GetInteger(); + float GetFloat(); + double GetDouble(); + bool GetBool(); +}; + +class TMXParser{ + public: + Map GetData(); + private: + Map parsedMapInfo; + std::string fileName; + ZoneData*prevZoneData=nullptr; + void ParseTag(std::string tag); + int monsterPropertyTagCount=-1; + bool infiniteMap=false; + LayerTag*currentLayerTag=nullptr; + public: + TMXParser(std::string file); +}; + +//#define TMX_PARSER_SETUP //Toggle for code-writing. + +#ifdef TMX_PARSER_SETUP +#undef TMX_PARSER_SETUP + const std::string XMLTag::FormatTagData(std::maptiles){ + std::string displayStr=""; + for (std::map::iterator it=data.begin();it!=data.end();it++) { + displayStr+=" "+it->first+": "+it->second+"\n"; + } + return displayStr; + } + std::ostream& operator << (std::ostream& os, XMLTag& rhs){ + os << + rhs.tag <<"\n"<< + rhs.FormatTagData(rhs.data) <<"\n"; + return os; + } + std::ostream& operator << (std::ostream& os, MapTag& rhs){ + os << + "(width:"<tiles) { + std::string displayStr; + for (int i=0;i&Map::GetLayers()const{ + return LayerData; + } + const std::string_view Map::GetMapDisplayName()const{ + return name; + } + const Renderable*const Map::GetOptimizedMap()const{ + return optimizedTile; + } + std::ostream& operator <<(std::ostream& os, std::vector& rhs) { + for(XMLTag&tag:rhs){ + os << + tag<<"\n"; + } + return os; + } + std::ostream& operator <<(std::ostream& os, Map& rhs) { + os << + rhs.MapData <<"\n"<< + rhs.TilesetData <<"\n"<< + rhs.FormatLayerData(os,rhs.LayerData) <<"\n"; + return os; + } + Map TMXParser::GetData() { + return parsedMapInfo; + } + void TMXParser::ParseTag(std::string tag) { + auto ReadNextTag=[&](){ + 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; + }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; + } + } + } + return newTag; + }; + + XMLTag newTag=ReadNextTag(); + + auto ParseMonsterTemplateName=[](const XMLTag&monsterTag){ + std::string monsterName=monsterTag.GetString("template").substr("../maps/Monsters/"s.length()); + monsterName=monsterName.substr(0,monsterName.find(".tx")); + return monsterName; + }; + + if (newTag.tag=="map"){ + if(stoi(newTag.data["infinite"])==1){ + infiniteMap=true; + throw std::runtime_error{std::format("WARNING! Infinite maps are not supported! Invalid map: {}",fileName)}; + return; + } + parsedMapInfo.MapData={stoi(newTag.data["width"]),stoi(newTag.data["height"]),stoi(newTag.data["tilewidth"]),stoi(newTag.data["tileheight"])}; + }else + if (newTag.tag=="tileset"){ + parsedMapInfo.TilesetData.push_back(newTag); + }else + if (newTag.tag=="layer"){ + LayerTag l = {newTag}; + parsedMapInfo.LayerData.push_back(l); + currentLayerTag=&parsedMapInfo.LayerData.back(); + }else + if(newTag.tag=="property"&&prevZoneData!=nullptr){ + //This is a property for a zone that doesn't fit into the other categories, we add it to the previous zone data encountered. + prevZoneData->properties.push_back(newTag); + }else + if (newTag.tag=="object"&&newTag.data.find("type")!=newTag.data.end()){ + //This is an object with a type that doesn't fit into other categories, we can add it to ZoneData. + std::vector&zones=parsedMapInfo.ZoneData[newTag.data["type"]]; + float width=1.f; + float height=1.f; + if(newTag.data.count("width")>0)width=newTag.GetFloat("width"); + if(newTag.data.count("height")>0)height=newTag.GetFloat("height"); + zones.emplace_back(geom2d::rect{{newTag.GetInteger("x"),newTag.GetInteger("y")},{int(width),int(height)}}); + prevZoneData=&zones.back(); + } + } + TMXParser::TMXParser(std::string file){ + fileName=file; + std::ifstream f(file,std::ios::in); + + std::string accumulator=""; + + while (f.good()&&!infiniteMap) { + 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! + } + }else{ + //Start reading in data for this layer. + std::vectorrowData; + while (data.find(",")!=std::string::npos) { + std::string datapiece = data.substr(0,data.find(",")); + data = data.substr(data.find(",")+1,std::string::npos); + rowData.push_back(stoi(datapiece)); + } + if (data.length()) { + rowData.push_back(stoi(data)); + } + parsedMapInfo.LayerData[parsedMapInfo.LayerData.size()-1].tiles.push_back(rowData); + } + } + std::sort(parsedMapInfo.TilesetData.begin(),parsedMapInfo.TilesetData.end(),[](XMLTag&t1,XMLTag&t2){return t1.GetInteger("firstgid") + +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. + +Portions of this software are copyright © 2024 The FreeType +Project (www.freetype.org). Please see LICENSE_FT.txt for more information. +All rights reserved. +*/ +#pragma endregion +#pragma once +#include +#include "TMXParser.h" + +using namespace olc; + + +struct TileCollisionData{ + geom2d::rectcollision; +}; + +struct Tileset{ + XMLTag ImageData; + int tilewidth=0,tileheight=0; + int imagewidth=0,imageheight=0; + bool isTerrain=false; + std::unordered_mapCollisionData; + std::unordered_map>AnimationData; + 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; + 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<0){ + if (newTag.tag.length()==0) { //Tag's empty, so first line is the tag. + newTag.tag=data; + } 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 (newTag.tag=="tileset") { + parsedTilesetInfo.tilewidth=stoi(newTag.data["tilewidth"]); + parsedTilesetInfo.tileheight=stoi(newTag.data["tileheight"]); + if(newTag.data.find("class")!=newTag.data.end()){ + parsedTilesetInfo.isTerrain=newTag.GetString("class")=="Terrain"; + } + } else + if (newTag.tag=="image") { + parsedTilesetInfo.ImageData=newTag; + parsedTilesetInfo.imagewidth=newTag.GetInteger("width"); + parsedTilesetInfo.imageheight=newTag.GetInteger("height"); + } 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.GetFloat("x"),newTag.GetFloat("y")},{newTag.GetFloat("width"),newTag.GetFloat("height")}}; + if(!parsedTilesetInfo.CollisionData.count(previousTagID)){ + parsedTilesetInfo.CollisionData[previousTagID]=data; + } + } + } + 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! + } + } + } + } +#endif \ No newline at end of file diff --git a/src/olcPixelGameEngine.cpp b/src/olcPixelGameEngine.cpp index b756e9c..83f1540 100644 --- a/src/olcPixelGameEngine.cpp +++ b/src/olcPixelGameEngine.cpp @@ -2,4 +2,8 @@ #define OLC_PGE_APPLICATION #include "olcPixelGameEngine.h" #define OLC_PGEX_TRANSFORMEDVIEW -#include "olcPGEX_TransformedView.h" \ No newline at end of file +#include "olcPGEX_TransformedView.h" +#define TMX_PARSER_SETUP +#include "TMXParser.h" +#define TSX_PARSER_SETUP +#include "TSXParser.h" \ No newline at end of file