mirror of
https://github.com/sigonasr2/hamster.git
synced 2025-04-17 22:29:40 -05:00
Added TMX Map Parsing. Added more color cycling to default border template.
This commit is contained in:
parent
3f9a30c16a
commit
f234f1a4fe
14
assets/HamsterGame.tiled-project
Normal file
14
assets/HamsterGame.tiled-project
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"automappingRulesFile": "",
|
||||
"commands": [
|
||||
],
|
||||
"compatibilityVersion": 1100,
|
||||
"extensionsPath": "extensions",
|
||||
"folders": [
|
||||
"."
|
||||
],
|
||||
"properties": [
|
||||
],
|
||||
"propertyTypes": [
|
||||
]
|
||||
}
|
23
assets/HamsterGame.tiled-session
Normal file
23
assets/HamsterGame.tiled-session
Normal file
@ -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
|
||||
}
|
||||
}
|
74
assets/Terrain.tsx
Normal file
74
assets/Terrain.tsx
Normal file
@ -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>
|
28
assets/TestLevel.tmx
Normal file
28
assets/TestLevel.tmx
Normal file
@ -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_Camera2D.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" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
@ -76,5 +76,11 @@
|
||||
<ClInclude Include="src\Border.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\TMXParser.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\TSXParser.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -40,7 +40,7 @@ All rights reserved.
|
||||
#include <ranges>
|
||||
|
||||
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)
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "HamsterGame.h"
|
||||
#include "Hamster.h"
|
||||
#include <stdexcept>
|
||||
#include <ranges>
|
||||
|
||||
geom2d::rect<float>HamsterGame::SCREEN_FRAME{{96,0},{320,288}};
|
||||
std::unordered_map<std::string,Animate2D::Animation<HamsterGame::AnimationState>>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();
|
||||
|
@ -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_map<std::string,Renderable>GFX;
|
||||
static std::unordered_map<std::string,Animate2D::Animation<HamsterGame::AnimationState>>ANIMATIONS;
|
||||
static PixelGameEngine*self;
|
||||
Border border;
|
||||
void DrawLevelTiles();
|
||||
std::optional<TMXParser>currentMap;
|
||||
};
|
395
src/TMXParser.h
Normal file
395
src/TMXParser.h
Normal file
@ -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==' '&"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<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
|
189
src/TSXParser.h
Normal file
189
src/TSXParser.h
Normal file
@ -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==' '&"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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
@ -3,3 +3,7 @@
|
||||
#include "olcPixelGameEngine.h"
|
||||
#define OLC_PGEX_TRANSFORMEDVIEW
|
||||
#include "olcPGEX_TransformedView.h"
|
||||
#define TMX_PARSER_SETUP
|
||||
#include "TMXParser.h"
|
||||
#define TSX_PARSER_SETUP
|
||||
#include "TSXParser.h"
|
Loading…
x
Reference in New Issue
Block a user