Added TMX Map Parsing. Added more color cycling to default border template.

main
sigonasr2 4 months ago
parent 3f9a30c16a
commit f234f1a4fe
  1. 14
      assets/HamsterGame.tiled-project
  2. 23
      assets/HamsterGame.tiled-session
  3. 74
      assets/Terrain.tsx
  4. 28
      assets/TestLevel.tmx
  5. 8
      hamster.vcxproj
  6. 6
      hamster.vcxproj.filters
  7. 2
      src/Border.cpp
  8. 29
      src/HamsterGame.cpp
  9. 5
      src/HamsterGame.h
  10. 395
      src/TMXParser.h
  11. 189
      src/TSXParser.h
  12. 6
      src/olcPixelGameEngine.cpp

@ -0,0 +1,14 @@
{
"automappingRulesFile": "",
"commands": [
],
"compatibilityVersion": 1100,
"extensionsPath": "extensions",
"folders": [
"."
],
"properties": [
],
"propertyTypes": [
]
}

@ -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
}
}

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.10" tiledversion="1.10.2" name="Terrain" tilewidth="16" tileheight="16" tilecount="1484" columns="28">
<image source="gametiles.png" width="448" height="848"/>
<tile id="493" probability="20"/>
<tile id="1120" probability="4"/>
<tile id="1148" probability="4"/>
<tile id="1176" probability="4"/>
<tile id="1207" probability="10"/>
<wangsets>
<wangset name="Sand" type="corner" tile="-1">
<wangcolor name="Desert" color="#ff0000" tile="-1" probability="1"/>
<wangcolor name="" color="#00ff00" tile="-1" probability="1"/>
<wangtile tileid="297" wangid="0,2,0,2,0,2,0,2"/>
<wangtile tileid="992" wangid="0,2,0,2,0,2,0,2"/>
<wangtile tileid="1020" wangid="0,2,0,2,0,2,0,2"/>
<wangtile tileid="1021" wangid="0,1,0,2,0,1,0,1"/>
<wangtile tileid="1022" wangid="0,1,0,2,0,2,0,1"/>
<wangtile tileid="1023" wangid="0,1,0,2,0,2,0,1"/>
<wangtile tileid="1024" wangid="0,1,0,2,0,2,0,1"/>
<wangtile tileid="1025" wangid="0,1,0,1,0,2,0,1"/>
<wangtile tileid="1049" wangid="0,2,0,2,0,1,0,1"/>
<wangtile tileid="1050" wangid="0,2,0,1,0,2,0,2"/>
<wangtile tileid="1051" wangid="0,2,0,1,0,1,0,2"/>
<wangtile tileid="1052" wangid="0,2,0,2,0,1,0,2"/>
<wangtile tileid="1053" wangid="0,1,0,1,0,2,0,2"/>
<wangtile tileid="1077" wangid="0,2,0,2,0,1,0,1"/>
<wangtile tileid="1078" wangid="0,1,0,1,0,2,0,2"/>
<wangtile tileid="1080" wangid="0,2,0,2,0,1,0,1"/>
<wangtile tileid="1081" wangid="0,1,0,1,0,2,0,2"/>
<wangtile tileid="1105" wangid="0,2,0,2,0,1,0,1"/>
<wangtile tileid="1106" wangid="0,1,0,2,0,2,0,2"/>
<wangtile tileid="1107" wangid="0,1,0,2,0,2,0,1"/>
<wangtile tileid="1108" wangid="0,2,0,2,0,2,0,1"/>
<wangtile tileid="1109" wangid="0,1,0,1,0,2,0,2"/>
<wangtile tileid="1133" wangid="0,2,0,1,0,1,0,1"/>
<wangtile tileid="1134" wangid="0,2,0,1,0,1,0,2"/>
<wangtile tileid="1135" wangid="0,2,0,1,0,1,0,2"/>
<wangtile tileid="1136" wangid="0,2,0,1,0,1,0,2"/>
<wangtile tileid="1137" wangid="0,1,0,1,0,1,0,2"/>
</wangset>
<wangset name="Grass" type="corner" tile="-1">
<wangcolor name="" color="#ff0000" tile="-1" probability="1"/>
<wangcolor name="" color="#00ff00" tile="-1" probability="1"/>
<wangtile tileid="493" wangid="0,2,0,2,0,2,0,2"/>
<wangtile tileid="1120" wangid="0,2,0,2,0,2,0,2"/>
<wangtile tileid="1148" wangid="0,2,0,2,0,2,0,2"/>
<wangtile tileid="1149" wangid="0,1,0,2,0,1,0,1"/>
<wangtile tileid="1150" wangid="0,1,0,2,0,2,0,1"/>
<wangtile tileid="1151" wangid="0,1,0,2,0,2,0,1"/>
<wangtile tileid="1152" wangid="0,1,0,2,0,2,0,1"/>
<wangtile tileid="1153" wangid="0,1,0,1,0,2,0,1"/>
<wangtile tileid="1176" wangid="0,2,0,2,0,2,0,2"/>
<wangtile tileid="1177" wangid="0,2,0,2,0,1,0,1"/>
<wangtile tileid="1178" wangid="0,2,0,1,0,2,0,2"/>
<wangtile tileid="1179" wangid="0,2,0,1,0,1,0,2"/>
<wangtile tileid="1180" wangid="0,2,0,2,0,1,0,2"/>
<wangtile tileid="1181" wangid="0,1,0,1,0,2,0,2"/>
<wangtile tileid="1205" wangid="0,2,0,2,0,1,0,1"/>
<wangtile tileid="1206" wangid="0,1,0,1,0,2,0,2"/>
<wangtile tileid="1208" wangid="0,2,0,2,0,1,0,1"/>
<wangtile tileid="1209" wangid="0,1,0,1,0,2,0,2"/>
<wangtile tileid="1233" wangid="0,2,0,2,0,1,0,1"/>
<wangtile tileid="1234" wangid="0,1,0,2,0,2,0,2"/>
<wangtile tileid="1235" wangid="0,1,0,2,0,2,0,1"/>
<wangtile tileid="1236" wangid="0,2,0,2,0,2,0,1"/>
<wangtile tileid="1237" wangid="0,1,0,1,0,2,0,2"/>
<wangtile tileid="1261" wangid="0,2,0,1,0,1,0,1"/>
<wangtile tileid="1262" wangid="0,2,0,1,0,1,0,2"/>
<wangtile tileid="1263" wangid="0,2,0,1,0,1,0,2"/>
<wangtile tileid="1264" wangid="0,2,0,1,0,1,0,2"/>
<wangtile tileid="1265" wangid="0,1,0,1,0,1,0,2"/>
</wangset>
</wangsets>
</tileset>

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="16" tileheight="16" infinite="0" nextlayerid="2" nextobjectid="1">
<tileset firstgid="1" source="Terrain.tsx"/>
<layer id="1" name="Tile Layer 1" width="30" height="20">
<data encoding="csv">
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
</data>
</layer>
</map>

@ -366,6 +366,14 @@ if %errorlevel% neq 0 goto :VCEnd</Command>
<ClInclude Include="src\olcUTIL_Animate2D.h" /> <ClInclude Include="src\olcUTIL_Animate2D.h" />
<ClInclude Include="src\olcUTIL_Camera2D.h" /> <ClInclude Include="src\olcUTIL_Camera2D.h" />
<ClInclude Include="src\olcUTIL_Geometry2D.h" /> <ClInclude Include="src\olcUTIL_Geometry2D.h" />
<ClInclude Include="src\TMXParser.h">
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="src\TSXParser.h">
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="src\util.h" /> <ClInclude Include="src\util.h" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

@ -76,5 +76,11 @@
<ClInclude Include="src\Border.h"> <ClInclude Include="src\Border.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="src\TMXParser.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\TSXParser.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>

@ -40,7 +40,7 @@ All rights reserved.
#include <ranges> #include <ranges>
const std::unordered_map<Border::BorderTemplate,std::tuple<std::string,Border::CycleTimer,Border::CycleTimer,Border::CycleTimer>>Border::BORDER_TEMPLATES{ const std::unordered_map<Border::BorderTemplate,std::tuple<std::string,Border::CycleTimer,Border::CycleTimer,Border::CycleTimer>>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::vector<Pixel>cycle,const float repeatTime) Border::CycleTimer::CycleTimer(const std::vector<Pixel>cycle,const float repeatTime)

@ -1,6 +1,7 @@
#include "HamsterGame.h" #include "HamsterGame.h"
#include "Hamster.h" #include "Hamster.h"
#include <stdexcept> #include <stdexcept>
#include <ranges>
geom2d::rect<float>HamsterGame::SCREEN_FRAME{{96,0},{320,288}}; geom2d::rect<float>HamsterGame::SCREEN_FRAME{{96,0},{320,288}};
std::unordered_map<std::string,Animate2D::Animation<HamsterGame::AnimationState>>HamsterGame::ANIMATIONS; std::unordered_map<std::string,Animate2D::Animation<HamsterGame::AnimationState>>HamsterGame::ANIMATIONS;
@ -20,7 +21,7 @@ bool HamsterGame::OnUserCreate(){
tv.SetWorldOffset(-SCREEN_FRAME.pos); tv.SetWorldOffset(-SCREEN_FRAME.pos);
LoadGraphics(); LoadGraphics();
LoadAnimations(); LoadAnimations();
LoadLevel(); //THIS IS TEMPORARY. LoadLevel("TestLevel.tmx"); //THIS IS TEMPORARY.
border.ChangeBorder(Border::DEFAULT); border.ChangeBorder(Border::DEFAULT);
return true; return true;
@ -34,6 +35,7 @@ void HamsterGame::_LoadImage(const std::string_view img){
void HamsterGame::LoadGraphics(){ void HamsterGame::LoadGraphics(){
_LoadImage("border.png"); _LoadImage("border.png");
_LoadImage("gametiles.png");
} }
void HamsterGame::LoadAnimations(){ void HamsterGame::LoadAnimations(){
@ -56,8 +58,11 @@ void HamsterGame::LoadAnimations(){
LoadAnimation(DEFAULT,"hamster.png",{{0,32},{32,32}},0.3f); 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 const vf2d levelSpawnLoc{50,50}; //TEMPORARY
currentMap=TMXParser{ASSETS_DIR+std::string(mapName)};
Hamster::LoadHamsters(levelSpawnLoc); Hamster::LoadHamsters(levelSpawnLoc);
camera.SetTarget(Hamster::GetPlayer().GetPos()); camera.SetTarget(Hamster::GetPlayer().GetPos());
} }
@ -70,11 +75,29 @@ void HamsterGame::UpdateGame(const float fElapsedTime){
} }
void HamsterGame::DrawGame(){ void HamsterGame::DrawGame(){
tv.FillRectDecal({10,10},{500.f,150.f},WHITE); DrawLevelTiles();
Hamster::DrawHamsters(tv); Hamster::DrawHamsters(tv);
border.Draw(); 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){ bool HamsterGame::OnUserUpdate(float fElapsedTime){
UpdateGame(fElapsedTime); UpdateGame(fElapsedTime);
DrawGame(); DrawGame();

@ -42,6 +42,7 @@ All rights reserved.
#include "olcPGEX_TransformedView.h" #include "olcPGEX_TransformedView.h"
#include "olcUTIL_Camera2D.h" #include "olcUTIL_Camera2D.h"
#include "Border.h" #include "Border.h"
#include "TMXParser.h"
class HamsterGame : public olc::PixelGameEngine class HamsterGame : public olc::PixelGameEngine
{ {
@ -68,10 +69,12 @@ private:
Camera2D camera; Camera2D camera;
void LoadGraphics(); void LoadGraphics();
void LoadAnimations(); void LoadAnimations();
void LoadLevel(); void LoadLevel(const std::string_view mapName);
void _LoadImage(const std::string_view img); void _LoadImage(const std::string_view img);
static std::unordered_map<std::string,Renderable>GFX; static std::unordered_map<std::string,Renderable>GFX;
static std::unordered_map<std::string,Animate2D::Animation<HamsterGame::AnimationState>>ANIMATIONS; static std::unordered_map<std::string,Animate2D::Animation<HamsterGame::AnimationState>>ANIMATIONS;
static PixelGameEngine*self; static PixelGameEngine*self;
Border border; Border border;
void DrawLevelTiles();
std::optional<TMXParser>currentMap;
}; };

@ -0,0 +1,395 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2024 Joshua Sigona <sigonasr2@gmail.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.
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 <sstream>
#include <set>
#include <queue>
using MapName=std::string;
using namespace olc;
using namespace std::literals;
struct XMLTag{
std::string tag;
std::map<std::string,std::string> data;
const std::string FormatTagData(std::map<std::string,std::string>tiles);
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<std::vector<int>> tiles;
std::string unlockCondition="";
std::string str();
};
struct ZoneData{
geom2d::rect<int>zone;
bool isUpper=false;
std::vector<XMLTag>properties;
};
struct Map{
friend class AiL;
friend class TMXParser;
private:
MapTag MapData;
std::string name;
Renderable*optimizedTile=nullptr;
std::vector<XMLTag> TilesetData;
std::vector<LayerTag> LayerData;
std::string mapType="";
std::set<std::string>spawns;
std::map<std::string,std::vector<::ZoneData>>ZoneData;
public:
Map();
void _SetMapData(MapTag data);
bool skipLoadoutScreen=false;
const MapTag&GetMapData()const;
const std::string_view GetMapType()const;
const std::vector<LayerTag>&GetLayers()const;
const MapName&GetMapName()const;
const std::string_view GetMapDisplayName()const;
const Renderable*const GetOptimizedMap()const;
const std::map<std::string,std::vector<::ZoneData>>&GetZones()const;
std::string FormatLayerData(std::ostream& os, std::vector<LayerTag>tiles);
friend std::ostream& operator << (std::ostream& os, Map& rhs);
friend std::ostream& operator << (std::ostream& os, std::vector<XMLTag>& 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::map<std::string,std::string>tiles){
std::string displayStr="";
for (std::map<std::string,std::string>::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:"<<rhs.width<<", height:"<<rhs.height<<", Tile width:"<<rhs.tilewidth<<", Tile height:"<<rhs.tileheight<<",playerSpawnLocation:"<<rhs.playerSpawnLocation<<")\n";
return os;
}
int XMLTag::GetInteger(std::string dataTag) {
return std::stoi(data[dataTag]);
}
float XMLTag::GetFloat(std::string dataTag) {
return std::stof(data[dataTag]);
}
double XMLTag::GetDouble(std::string dataTag) {
return std::stod(data[dataTag]);
}
std::string XMLTag::GetString(std::string dataTag)const{
return data.at(dataTag);
}
bool XMLTag::GetBool(std::string dataTag) {
if (data[dataTag]=="0"||data[dataTag]=="false") {
return false;
}else{
return true;
}
}
int Property::GetInteger() {
return std::stoi(value);
}
float Property::GetFloat() {
return std::stof(value);
}
double Property::GetDouble() {
return std::stod(value);
}
bool Property::GetBool() {
if (value=="0") {
return false;
}else{
return true;
}
}
Map::Map(){
ZoneData["UpperZone"];
ZoneData["LowerZone"];
}
MapTag::MapTag(){}
MapTag::MapTag(int width,int height,int tilewidth,int tileheight)
:width(width),height(height),tilewidth(tilewidth),tileheight(tileheight),MapSize({width,height}),TileSize({tilewidth,tileheight}){}
std::string XMLTag::str() {
return tag+"\n";
}
std::string LayerTag::str() {
std::string displayStr=tag.tag+"\n"+tag.FormatTagData(tag.data);
displayStr+=" DATA ("+std::to_string(tiles[0].size())+"x"+std::to_string(tiles.size())+" )\n";
return displayStr;
}
std::string Map::FormatLayerData(std::ostream& os, std::vector<LayerTag>tiles) {
std::string displayStr;
for (int i=0;i<LayerData.size();i++) {
displayStr+=LayerData[i].str();
}
return displayStr;
}
const std::string_view Map::GetMapType()const{
return mapType;
}
const MapName&Map::GetMapName()const{
return name;
}
void Map::_SetMapData(MapTag data){
MapData=data;
}
const MapTag&Map::GetMapData()const{
return MapData;
}
const std::vector<LayerTag>&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<XMLTag>& 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==' '&&quotationMarkCount%2==0){
valid=true;
break;
}
data+=character;
if(pastEquals&&quotationMarkCount%2==0){
valid=true;
break;
}
if(character=='='&&quotationMarkCount%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<ZoneData>&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<int>{{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::vector<int>rowData;
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")<t2.GetInteger("firstgid");});
}
#endif

@ -0,0 +1,189 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2024 Joshua Sigona <sigonasr2@gmail.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.
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 <sstream>
#include "TMXParser.h"
using namespace olc;
struct TileCollisionData{
geom2d::rect<float>collision;
};
struct Tileset{
XMLTag ImageData;
int tilewidth=0,tileheight=0;
int imagewidth=0,imageheight=0;
bool isTerrain=false;
std::unordered_map<int,TileCollisionData>CollisionData;
std::unordered_map<int,std::vector<int>>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<<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==' '&&quotationMarkCount%2==0){
valid=true;
break;
}
data+=character;
if(pastEquals&&quotationMarkCount%2==0){
valid=true;
break;
}
if(character=='='&&quotationMarkCount%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;
}
}
}
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.GetInteger("duration")/100;i++){
parsedTilesetInfo.AnimationData[previousTagID].push_back(newTag.GetInteger("tileid"));
}
}
if (newTag.tag=="object"&&previousTag=="tile"){
TileCollisionData data;
data.collision=geom2d::rect<float>{{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

@ -2,4 +2,8 @@
#define OLC_PGE_APPLICATION #define OLC_PGE_APPLICATION
#include "olcPixelGameEngine.h" #include "olcPixelGameEngine.h"
#define OLC_PGEX_TRANSFORMEDVIEW #define OLC_PGEX_TRANSFORMEDVIEW
#include "olcPGEX_TransformedView.h" #include "olcPGEX_TransformedView.h"
#define TMX_PARSER_SETUP
#include "TMXParser.h"
#define TSX_PARSER_SETUP
#include "TSXParser.h"
Loading…
Cancel
Save