Modified TMX Parser to read new map spawn format. Fix missing spawns. Added errors when spawns are missing from a monster.

mac-build
sigonasr2 8 months ago
parent 1d1c12004e
commit 4557138662
  1. 1
      Adventures in Lestoria/Adventures in Lestoria.vcxproj
  2. 3
      Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
  3. 18
      Adventures in Lestoria/AdventuresInLestoria.cpp
  4. 58
      Adventures in Lestoria/Monster.h
  5. 98
      Adventures in Lestoria/MonsterData.h
  6. 72
      Adventures in Lestoria/TMXParser.h
  7. 2
      Adventures in Lestoria/Version.h
  8. 30
      Adventures in Lestoria/assets/Campaigns/1_2.tmx
  9. 6
      Adventures in Lestoria/assets/Campaigns/1_8.tmx
  10. 6
      Adventures in Lestoria/assets/Campaigns/Intro_Map.tmx
  11. BIN
      x64/Release/Adventures in Lestoria.exe

@ -415,6 +415,7 @@
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="MonsterData.h" />
<ClInclude Include="olcPGEX_SplashScreen.h" />
<ClInclude Include="PlayerMoneyLabel.h">
<SubType>

@ -633,6 +633,9 @@
<ClInclude Include="Minimap.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="MonsterData.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Player.cpp">

@ -261,15 +261,6 @@ bool AiL::OnUserCreate(){
VisualNovel::Initialize();
InitializeLevels();
//Initialize Camera.
camera=Camera2D{WINDOW_SIZE};
camera.SetMode(olc::utils::Camera2D::Mode::LazyFollow);
camera.SetTarget(player->GetPos());
camera.SetWorldBoundary({0,0},GetCurrentMap().MapData.MapSize*GetCurrentMap().MapData.TileSize);
camera.EnableWorldBoundary(false);
ItemAttribute::Initialize();
ItemInfo::InitializeItems();
@ -290,6 +281,15 @@ bool AiL::OnUserCreate(){
MonsterData::InitializeMonsterData();
MonsterData::InitializeNPCData();
InitializeLevels();
//Initialize Camera.
camera=Camera2D{WINDOW_SIZE};
camera.SetMode(olc::utils::Camera2D::Mode::LazyFollow);
camera.SetTarget(player->GetPos());
camera.SetWorldBoundary({0,0},GetCurrentMap().MapData.MapSize*GetCurrentMap().MapData.TileSize);
camera.EnableWorldBoundary(false);
sig::Animation::SetupPlayerAnimations();
view=TileTransformedView{GetScreenSize(),{1,1}};

@ -42,11 +42,11 @@ All rights reserved.
#include "olcUTIL_Animate2D.h"
#include "DEFINES.h"
#include "Attributable.h"
#include "Item.h"
#include "safemap.h"
#include "Pathfinding.h"
#include "GameEvent.h"
#include "TMXParser.h"
#include "MonsterData.h"
INCLUDE_ITEM_DATA
@ -62,62 +62,6 @@ enum class MonsterAnimation{
DEATH
};
struct MonsterDropData{
ItemInfo*item;
float dropChance;
int minQty=1;
int maxQty=1;
MonsterDropData(std::string itemName,float dropChance,int minQty=1,int maxQty=1)
:item(&ITEM_DATA.at(itemName)),dropChance(dropChance),minQty(minQty),maxQty(maxQty){}
};
struct MonsterData{
private:
std::string name;
int hp;
int atk;
uint32_t xp;
float moveSpd;//1.0=100%
float size;
std::vector<std::string> animations;
std::string strategy;
int collisionDmg;
std::string jumpAnimation="WARRIOR_IDLE_S";
std::string shootAnimation="WARRIOR_IDLE_S";
std::string deathAnimation="WARRIOR_IDLE_S";
EventName hurtSound="";
EventName deathSound="";
EventName walkSound="";
std::vector<MonsterDropData> dropData;
bool isNPC=false;
public:
MonsterData();
MonsterData(std::string name,int hp,int atk,const uint32_t xp,std::vector<std::string>animations,std::vector<MonsterDropData>drops,float moveSpd=1.0f,float size=1.0f,std::string strategy="Run Towards",int collisionDmg=0);
int GetHealth();
int GetAttack();
const uint32_t GetXP()const;
float GetMoveSpdMult();
float GetSizeMult();
const std::string&GetAIStrategy()const;
int GetCollisionDmg();
std::string GetIdleAnimation();
std::string GetJumpAnimation();
std::string GetShootAnimation();
std::string GetDeathAnimation();
const EventName&GetHurtSound();
const EventName&GetDeathSound();
const EventName&GetWalkSound();
const bool IsNPC()const;
std::vector<std::string>GetAnimations(){
return animations;
}
const std::vector<MonsterDropData>&GetDropData();
std::string GetDisplayName();
static void InitializeMonsterData();
static void InitializeNPCData();
static std::map<std::string,Renderable*>imgs;
};
class GameEvent;
class Monster:IAttributable{

@ -0,0 +1,98 @@
#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 "DEFINES.h"
#include "Item.h"
INCLUDE_ITEM_DATA
struct MonsterDropData{
ItemInfo*item;
float dropChance;
int minQty=1;
int maxQty=1;
MonsterDropData(std::string itemName,float dropChance,int minQty=1,int maxQty=1)
:item(&ITEM_DATA.at(itemName)),dropChance(dropChance),minQty(minQty),maxQty(maxQty){}
};
struct MonsterData{
private:
std::string name;
int hp;
int atk;
uint32_t xp;
float moveSpd;//1.0=100%
float size;
std::vector<std::string> animations;
std::string strategy;
int collisionDmg;
std::string jumpAnimation="WARRIOR_IDLE_S";
std::string shootAnimation="WARRIOR_IDLE_S";
std::string deathAnimation="WARRIOR_IDLE_S";
EventName hurtSound="";
EventName deathSound="";
EventName walkSound="";
std::vector<MonsterDropData> dropData;
bool isNPC=false;
public:
MonsterData();
MonsterData(std::string name,int hp,int atk,const uint32_t xp,std::vector<std::string>animations,std::vector<MonsterDropData>drops,float moveSpd=1.0f,float size=1.0f,std::string strategy="Run Towards",int collisionDmg=0);
int GetHealth();
int GetAttack();
const uint32_t GetXP()const;
float GetMoveSpdMult();
float GetSizeMult();
const std::string&GetAIStrategy()const;
int GetCollisionDmg();
std::string GetIdleAnimation();
std::string GetJumpAnimation();
std::string GetShootAnimation();
std::string GetDeathAnimation();
const EventName&GetHurtSound();
const EventName&GetDeathSound();
const EventName&GetWalkSound();
const bool IsNPC()const;
std::vector<std::string>GetAnimations(){
return animations;
}
const std::vector<MonsterDropData>&GetDropData();
std::string GetDisplayName();
static void InitializeMonsterData();
static void InitializeNPCData();
static std::map<std::string,Renderable*>imgs;
};

@ -45,7 +45,9 @@ All rights reserved.
#include "safemap.h"
#include "ItemMapData.h"
#include "Class.h"
#include "MonsterData.h"
INCLUDE_MONSTER_DATA
using MapName=std::string;
using namespace olc;
@ -61,7 +63,7 @@ struct XMLTag{
float GetFloat(std::string dataTag);
double GetDouble(std::string dataTag);
bool GetBool(std::string dataTag);
std::string GetString(std::string dataTag);
std::string GetString(std::string dataTag)const;
};
struct ForegroundTileTag:public XMLTag{
@ -224,13 +226,13 @@ class TMXParser{
double XMLTag::GetDouble(std::string dataTag) {
return std::stod(data[dataTag]);
}
std::string XMLTag::GetString(std::string dataTag) {
return 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 {
}else{
return true;
}
}
@ -246,7 +248,7 @@ class TMXParser{
bool Property::GetBool() {
if (value=="0") {
return false;
} else {
}else{
return true;
}
}
@ -396,7 +398,7 @@ class TMXParser{
#if _DEBUG
if(_DEBUG_MAP_LOAD_INFO)LOG("Tag: "<<newTag.tag<<"\n");
#endif
} else {
}else{
std::string key = data.substr(0,data.find("="));
std::string value = data.substr(data.find("=")+1,std::string::npos);
@ -416,6 +418,16 @@ class TMXParser{
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!="property"&&newTag.tag!="properties"&&monsterPropertyTagCount==0){
ERR(std::format("WARNING! Monster {} at ({},{}) in Map {} does not have a spawn set!",ParseMonsterTemplateName(monsterTag),monsterTag.GetFloat("x"),monsterTag.GetFloat("y"),fileName));
}
if (newTag.tag=="object"&&newTag.data["type"]!="StagePlate"){
currentStagePlate=nullptr;
}
@ -433,10 +445,10 @@ class TMXParser{
if(newTag.data.find("class")==newTag.data.end())ERR(std::format("WARNING! Map {} does not have a class set!",fileName));
if(newTag.data["class"]!="Map")ERR(std::format("WARNING! Map {} is not set to the map class! Class is set to {} instead.",fileName,newTag.data["class"]));
parsedMapInfo.MapData={stoi(newTag.data["width"]),stoi(newTag.data["height"]),stoi(newTag.data["tilewidth"]),stoi(newTag.data["tileheight"])};
} else
}else
if (newTag.tag=="tileset"){
parsedMapInfo.TilesetData.push_back(newTag);
} else
}else
if (newTag.tag=="layer"){
LayerTag l = {newTag};
parsedMapInfo.LayerData.push_back(l);
@ -447,86 +459,84 @@ class TMXParser{
parsedMapInfo.SpawnerData[newTag.GetInteger("id")]={newTag};
prevSpawner=newTag.GetInteger("id");
}
} else
}else
if(newTag.tag=="property"&&newTag.data["name"]=="Layer Unlock Condition"&&currentLayerTag!=nullptr){
currentLayerTag->unlockCondition=newTag.data["value"];
} else
}else
if(newTag.tag=="property"&&newTag.data["name"]=="Upper?"&&prevZoneData!=nullptr){
prevZoneData->isUpper=newTag.GetBool("value");
}else
if (newTag.tag=="property"&&newTag.data["name"]=="Optimize"&&newTag.data["value"]=="true") {
parsedMapInfo.MapData.optimized=true;
} else
}else
if (newTag.tag=="property"&&newTag.data["name"]=="Boss Title Display") {
parsedMapInfo.SpawnerData[prevSpawner].bossNameDisplay=newTag.data["value"];
} else
}else
if (newTag.tag=="property"&&newTag.data["name"]=="Level Type") {
parsedMapInfo.mapType=newTag.data["value"];
} else
}else
if (newTag.tag=="property"&&newTag.data["name"]=="Background Music") {
if(newTag.data["value"]!="None"){ //None is a default value that we ignore.
parsedMapInfo.bgmSongName=newTag.data["value"];
}
} else
}else
if (newTag.tag=="property"&&newTag.data["name"].starts_with("Dev Completion Time")) {
if(newTag.data.count("value")){ //None is a default value that we ignore.
size_t classStartPos="Dev Completion Time - "s.length();
std::string className=newTag.data["name"].substr(classStartPos,newTag.data["name"].find(' ',classStartPos)-classStartPos);
parsedMapInfo.devCompletionTrialTime[classutils::StringToClass(className)]=newTag.GetFloat("value");
}
} else
}else
if (newTag.tag=="property"&&newTag.data["name"]=="Backdrop") {
if(newTag.data["value"]!="None"){ //None is a default value that we ignore.
parsedMapInfo.backdrop=newTag.data["value"];
}
} else
}else
if (newTag.tag=="object"&&newTag.data["type"]=="AudioEnvironmentalSound") {
parsedMapInfo.environmentalAudioData.emplace_back();
prevAudioData=&parsedMapInfo.environmentalAudioData.back();
prevAudioData->SetPos({newTag.GetFloat("x"),newTag.GetFloat("y")});
} else
}else
if (newTag.tag=="property"&&newTag.data["propertytype"]=="EnvironmentalSounds") {
if(newTag.data["value"]!="None"){ //None is a default value that we ignore.
prevAudioData->SetAudioName(newTag.data["value"]);
}
} else
}else
if (newTag.tag=="object"&&newTag.data["type"]=="PlayerSpawnLocation") {
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");
parsedMapInfo.MapData.playerSpawnLocation={int(newTag.GetFloat("x")-width/2),int(newTag.GetFloat("y")-height/2)};
} else
}else
if (newTag.tag=="object"&&newTag.data["type"]=="NPC") {
if(inNPCTag)parsedMapInfo.npcs.push_back(NPCData{npcTag});
npcTag=newTag;
inNPCTag=true;
} else
if (newTag.tag=="object"&&newTag.data["type"]=="Monster") {
if (newTag.tag=="object"&&newTag.data["template"].starts_with("../maps/Monsters")) {
monsterTag=newTag;
monsterPropertyTagCount=0;
} else
}else
if (newTag.tag=="property"&&inNPCTag) {
npcTag.data[newTag.data["name"]]=newTag.data["value"];
}
}else
if (newTag.tag=="property"&&monsterPropertyTagCount==0) {
if(newTag.data["value"]=="None")ERR(std::format("WARNING! Invalid monster type provided: {} in map {}",newTag.data["value"],fileName));
monsterTag.data["value"]=newTag.data["value"];
parsedMapInfo.spawns.insert(newTag.GetString("value"));
monsterPropertyTagCount++;
} else
if (newTag.tag=="property"&&monsterPropertyTagCount==1) {
std::string monsterName=ParseMonsterTemplateName(monsterTag);
if(!MONSTER_DATA.count(monsterName))ERR(std::format("WARNING! Could not find monster type {}",monsterName));
parsedMapInfo.spawns.insert(monsterName);
spawnerLinkTag=newTag;
monsterTag.data["value"]=monsterName;
monsterTag.data["spawnerLink"]=spawnerLinkTag.data["value"];
accumulatedMonsterTags.push_back(monsterTag);
monsterPropertyTagCount=-1;
} else
}else
if (newTag.tag=="object"&&newTag.data["type"]=="StagePlate") {
if(newTag.GetInteger("id")!=0){
stagePlates[newTag.GetInteger("id")]={newTag};
currentStagePlate=&stagePlates.at(newTag.GetInteger("id"));
}
} else
}else
if(newTag.tag=="property"&&currentStagePlate!=nullptr){
currentStagePlate->properties[newTag.data["name"]]={newTag.data["name"],newTag.data["value"]};
}else
@ -586,7 +596,7 @@ class TMXParser{
if(accumulator.length()>1&&accumulator.find('>')!=std::string::npos){
accumulator=""; //Restart because this tag has nothing in it!
}
} else {
}else{
//Start reading in data for this layer.
std::vector<int>rowData;
while (data.find(",")!=std::string::npos) {

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 2
#define VERSION_PATCH 0
#define VERSION_BUILD 8926
#define VERSION_BUILD 8944
#define stringify(a) stringify_(a)
#define stringify_(a) #a

@ -885,14 +885,34 @@
<object id="23" name="Spawn 4" type="SpawnGroup" x="1385.33" y="2518.67" width="406.667" height="340">
<ellipse/>
</object>
<object id="24" template="../maps/Monsters/Flower Turret.tx" type="Monster" x="1485.33" y="2676"/>
<object id="24" template="../maps/Monsters/Flower Turret.tx" type="Monster" x="1485.33" y="2676">
<properties>
<property name="spawner" type="object" value="23"/>
</properties>
</object>
<object id="25" name="Spawn 5" type="SpawnGroup" x="1375.33" y="1923.33" width="486.667" height="476">
<ellipse/>
</object>
<object id="26" template="../maps/Monsters/Blue Slime.tx" type="Monster" x="1609.33" y="2589.33"/>
<object id="27" template="../maps/Monsters/Blue Slime.tx" type="Monster" x="1542.67" y="2630.67"/>
<object id="28" template="../maps/Monsters/Blue Slime.tx" type="Monster" x="1669.33" y="2633.33"/>
<object id="29" template="../maps/Monsters/Red Slime.tx" type="Monster" x="1589.33" y="2708"/>
<object id="26" template="../maps/Monsters/Blue Slime.tx" type="Monster" x="1609.33" y="2589.33">
<properties>
<property name="spawner" type="object" value="23"/>
</properties>
</object>
<object id="27" template="../maps/Monsters/Blue Slime.tx" type="Monster" x="1542.67" y="2630.67">
<properties>
<property name="spawner" type="object" value="23"/>
</properties>
</object>
<object id="28" template="../maps/Monsters/Blue Slime.tx" type="Monster" x="1669.33" y="2633.33">
<properties>
<property name="spawner" type="object" value="23"/>
</properties>
</object>
<object id="29" template="../maps/Monsters/Red Slime.tx" type="Monster" x="1589.33" y="2708">
<properties>
<property name="spawner" type="object" value="23"/>
</properties>
</object>
<object id="30" name="Spawn 6" type="SpawnGroup" x="1346" y="1522" width="508" height="210.667">
<ellipse/>
</object>

@ -675,7 +675,11 @@
<property name="spawner" type="object" value="2"/>
</properties>
</object>
<object id="40" template="../maps/Monsters/Windhound.tx" type="Monster" x="1117.33" y="1394.67"/>
<object id="40" template="../maps/Monsters/Windhound.tx" type="Monster" x="1117.33" y="1394.67">
<properties>
<property name="spawner" type="object" value="2"/>
</properties>
</object>
<object id="41" template="../maps/Monsters/Yellow Slime.tx" type="Monster" x="1140" y="938.667">
<properties>
<property name="spawner" type="object" value="3"/>

@ -1331,7 +1331,11 @@
<object id="24" name="Spawn 15" type="SpawnGroup" x="2638" y="1912" width="1072" height="664">
<ellipse/>
</object>
<object id="26" template="../maps/Monsters/Bear.tx" type="Monster" x="3550" y="2263"/>
<object id="26" template="../maps/Monsters/Bear.tx" type="Monster" x="3550" y="2263">
<properties>
<property name="spawner" type="object" value="16"/>
</properties>
</object>
<object id="27" name="Spawn 15" type="SpawnGroup" x="849" y="34" width="2028" height="808">
<properties>
<property name="spawner" type="object" value="27"/>

Loading…
Cancel
Save