Update No XP/Drop Hawk labels in Tiled Editor. Added spawn controllers, setup multi-tiered spawns. Fixed collision radius bugs. Added Chapter 2 Bonus Boss and Major Hawks Tiled Templates. Fix Stone Elemental Casting circle radius. Release Build 9378.

pull/57/head
sigonasr2 9 months ago
parent 64ea11b4af
commit 21b8a9c3cb
  1. 39
      Adventures in Lestoria/Adventures in Lestoria.tiled-project
  2. 26
      Adventures in Lestoria/AdventuresInLestoria.cpp
  3. 5
      Adventures in Lestoria/DEFINES.h
  4. 27
      Adventures in Lestoria/Monster.cpp
  5. 4
      Adventures in Lestoria/MonsterData.cpp
  6. 2
      Adventures in Lestoria/Player.cpp
  7. 8
      Adventures in Lestoria/Stone_Elemental.cpp
  8. 24
      Adventures in Lestoria/TMXParser.h
  9. 2
      Adventures in Lestoria/Version.h
  10. 45
      Adventures in Lestoria/assets/Campaigns/Boss_2_B.tmx
  11. 5
      Adventures in Lestoria/assets/maps/Monster_Presets.tmx
  12. 2
      Adventures in Lestoria/assets/maps/Monsters/Hawk_NOXP.tx
  13. 5
      Adventures in Lestoria/assets/maps/Monsters/Major Hawk.tx
  14. 5
      Adventures in Lestoria/assets/maps/Monsters/Zephy, King of Birds.tx
  15. BIN
      Adventures in Lestoria/assets/maps/monsters-tileset.png
  16. BIN
      x64/Release/Adventures in Lestoria.exe

@ -526,6 +526,45 @@
], ],
"valuesAsFlags": false "valuesAsFlags": false
}, },
{
"color": "#ffa0a0a4",
"drawFill": true,
"id": 42,
"members": [
{
"name": "Spawn1",
"type": "object",
"value": 0
},
{
"name": "Spawn2",
"type": "object",
"value": 0
},
{
"name": "Spawn3",
"type": "object",
"value": 0
},
{
"name": "Spawn4",
"type": "object",
"value": 0
},
{
"name": "Spawn5",
"type": "object",
"value": 0
}
],
"name": "SpawnController",
"type": "class",
"useAs": [
"property",
"object",
"project"
]
},
{ {
"color": "#ffe67300", "color": "#ffe67300",
"drawFill": true, "drawFill": true,

@ -94,9 +94,10 @@ bool _DEBUG_MAP_LOAD_INFO = false;
vi2d WINDOW_SIZE={24*15,24*10}; vi2d WINDOW_SIZE={24*15,24*10};
safemap<std::string,Animate2D::FrameSequence>ANIMATION_DATA; safemap<std::string,Animate2D::FrameSequence>ANIMATION_DATA;
std::vector<std::unique_ptr<Monster>>MONSTER_LIST; std::vector<std::unique_ptr<Monster>>MONSTER_LIST;
std::vector<MonsterSpawner>SPAWNER_LIST; std::unordered_map<MonsterSpawnerID,MonsterSpawner>SPAWNER_LIST;
std::vector<std::shared_ptr<DamageNumber>>DAMAGENUMBER_LIST; std::vector<std::shared_ptr<DamageNumber>>DAMAGENUMBER_LIST;
std::vector<std::unique_ptr<Bullet>>BULLET_LIST; std::vector<std::unique_ptr<Bullet>>BULLET_LIST;
std::optional<std::queue<MonsterSpawnerID>>SPAWNER_CONTROLLER;
safemap<std::string,Renderable>GFX; safemap<std::string,Renderable>GFX;
utils::datafile DATA; utils::datafile DATA;
AiL*game; AiL*game;
@ -2390,6 +2391,7 @@ void AiL::_PrepareLevel(MapName map,MusicChange changeMusic){
#pragma endregion #pragma endregion
SPAWNER_LIST.clear(); SPAWNER_LIST.clear();
SPAWNER_CONTROLLER={};
foregroundTileGroups.clear(); foregroundTileGroups.clear();
upperForegroundTileGroups.clear(); upperForegroundTileGroups.clear();
MONSTER_LIST.clear(); MONSTER_LIST.clear();
@ -2452,6 +2454,17 @@ void AiL::_PrepareLevel(MapName map,MusicChange changeMusic){
#pragma region Monster Spawn Data Setup (Loading phase 2) #pragma region Monster Spawn Data Setup (Loading phase 2)
LoadingScreen::AddPhase([&](){ LoadingScreen::AddPhase([&](){
SetMosaicEffect(1U); SetMosaicEffect(1U);
std::unordered_set<MonsterSpawnerID>IdsToDisable; //
if(MAP_DATA[GetCurrentLevel()].spawnControllerIDs.has_value()){
std::queue<MonsterSpawnerID>spawnController=MAP_DATA[GetCurrentLevel()].spawnControllerIDs.value();
while(!spawnController.empty()){
const int spawnerId=spawnController.front();
spawnController.pop();
auto result=IdsToDisable.insert(spawnerId);
if(!result.second)ERR(std::format("WARNING! Duplicate spawnerId {} detected when loading monster spawners. THIS SHOULD NOT BE HAPPENING!",spawnerId));
}
}
for(auto&[key,value]:MAP_DATA[GetCurrentLevel()].SpawnerData){ for(auto&[key,value]:MAP_DATA[GetCurrentLevel()].SpawnerData){
SpawnerTag&spawnData=MAP_DATA[GetCurrentLevel()].SpawnerData[key]; SpawnerTag&spawnData=MAP_DATA[GetCurrentLevel()].SpawnerData[key];
std::vector<std::pair<std::string,vf2d>>monster_list; std::vector<std::pair<std::string,vf2d>>monster_list;
@ -2461,8 +2474,14 @@ void AiL::_PrepareLevel(MapName map,MusicChange changeMusic){
std::string monsterName=monster.GetString("value"); std::string monsterName=monster.GetString("value");
monster_list.push_back({monsterName,{monster.GetInteger("x")-spawnData.ObjectData.GetFloat("x"),monster.GetInteger("y")-spawnData.ObjectData.GetFloat("y")}}); monster_list.push_back({monsterName,{monster.GetInteger("x")-spawnData.ObjectData.GetFloat("x"),monster.GetInteger("y")-spawnData.ObjectData.GetFloat("y")}});
} }
SPAWNER_LIST.push_back(MonsterSpawner{{spawnData.ObjectData.GetFloat("x"),spawnData.ObjectData.GetFloat("y")},spawnerRadius*2,monster_list,spawnData.upperLevel,spawnData.bossNameDisplay});
const int spawnerId=spawnData.ObjectData.GetInteger("id");
if(SPAWNER_LIST.count(spawnerId))ERR(std::format("WARNING! Spawner ID {} for Map {} is somehow duplicated! THIS SHOULD NOT BE HAPPENING!",spawnData.ObjectData.GetInteger("id"),GetCurrentMapDisplayName()))
SPAWNER_LIST[spawnerId]=MonsterSpawner{{spawnData.ObjectData.GetFloat("x"),spawnData.ObjectData.GetFloat("y")},spawnerRadius*2,monster_list,spawnData.upperLevel,spawnData.bossNameDisplay};
if(IdsToDisable.count(spawnerId))SPAWNER_LIST.at(spawnerId).SetTriggered(true,false); //Force this spawner to be deactivated because it is in a spawn controller.
} }
SPAWNER_CONTROLLER=MAP_DATA[GetCurrentLevel()].spawnControllerIDs;
return true; return true;
}); });
#pragma endregion #pragma endregion
@ -3493,8 +3512,9 @@ void AiL::InitializeDefaultKeybinds(){
} }
void AiL::SetBossNameDisplay(std::string name,float time){ void AiL::SetBossNameDisplay(std::string name,float time){
const bool HasNotBeenDisplayedYet=bossName=="";
bossName=name; bossName=name;
bossDisplayTimer=time; if(HasNotBeenDisplayedYet)bossDisplayTimer=time; //Only display once.
} }
bool AiL::InBossEncounter(){ bool AiL::InBossEncounter(){

@ -41,9 +41,12 @@ All rights reserved.
using BackdropName=std::string; using BackdropName=std::string;
using MonsterSpawnerID=int;
#define INCLUDE_ANIMATION_DATA extern safemap<std::string,Animate2D::FrameSequence>ANIMATION_DATA; #define INCLUDE_ANIMATION_DATA extern safemap<std::string,Animate2D::FrameSequence>ANIMATION_DATA;
#define INCLUDE_MONSTER_LIST extern std::vector<std::unique_ptr<Monster>>MONSTER_LIST; #define INCLUDE_MONSTER_LIST extern std::vector<std::unique_ptr<Monster>>MONSTER_LIST;
#define INCLUDE_SPAWNER_LIST extern std::vector<MonsterSpawner>SPAWNER_LIST; #define INCLUDE_SPAWNER_LIST extern std::unordered_map<MonsterSpawnerID,MonsterSpawner>SPAWNER_LIST;
#define INCLUDE_SPAWNER_CONTROLLER extern std::optional<std::queue<MonsterSpawnerID>>SPAWNER_CONTROLLER;
#define INCLUDE_DAMAGENUMBER_LIST extern std::vector<std::shared_ptr<DamageNumber>>DAMAGENUMBER_LIST; #define INCLUDE_DAMAGENUMBER_LIST extern std::vector<std::shared_ptr<DamageNumber>>DAMAGENUMBER_LIST;
#define INCLUDE_game extern AiL*game; #define INCLUDE_game extern AiL*game;
#define INCLUDE_MONSTER_DATA extern std::map<std::string,MonsterData>MONSTER_DATA; #define INCLUDE_MONSTER_DATA extern std::map<std::string,MonsterData>MONSTER_DATA;

@ -60,6 +60,8 @@ INCLUDE_game
INCLUDE_BULLET_LIST INCLUDE_BULLET_LIST
INCLUDE_DATA INCLUDE_DATA
INCLUDE_GFX INCLUDE_GFX
INCLUDE_SPAWNER_LIST
INCLUDE_SPAWNER_CONTROLLER
safemap<std::string,std::function<void(Monster&,float,std::string)>>STRATEGY_DATA; safemap<std::string,std::function<void(Monster&,float,std::string)>>STRATEGY_DATA;
std::unordered_map<std::string,Renderable*>MonsterData::imgs; std::unordered_map<std::string,Renderable*>MonsterData::imgs;
@ -332,7 +334,7 @@ bool Monster::Update(float fElapsedTime){
if(!HasIframes()){ if(!HasIframes()){
for(std::unique_ptr<Monster>&m:MONSTER_LIST){ for(std::unique_ptr<Monster>&m:MONSTER_LIST){
if(&*m==this)continue; if(&*m==this)continue;
if(!m->HasIframes()&&OnUpperLevel()==m->OnUpperLevel()&&abs(m->GetZ()-GetZ())<=1&&geom2d::overlaps(geom2d::circle(pos,GetCollisionRadius()),geom2d::circle(m->GetPos(),12*m->GetSizeMult()/2))){ if(!m->HasIframes()&&OnUpperLevel()==m->OnUpperLevel()&&abs(m->GetZ()-GetZ())<=1&&geom2d::overlaps(geom2d::circle(pos,GetCollisionRadius()),geom2d::circle(m->GetPos(),m->GetCollisionRadius()))){
m->Collision(*this); m->Collision(*this);
geom2d::line line(pos,m->GetPos()); geom2d::line line(pos,m->GetPos());
float dist = line.length(); float dist = line.length();
@ -810,16 +812,25 @@ void Monster::OnDeath(){
if(isBoss){ if(isBoss){
game->ReduceBossEncounterMobCount(); game->ReduceBossEncounterMobCount();
if(game->BossEncounterMobCount()==0){ if(game->BossEncounterMobCount()==0){
ZoneData exitRing{geom2d::rect<int>{vi2d{GetPos()-vf2d{"boss_spawn_ring_radius"_F,"boss_spawn_ring_radius"_F}},vi2d{"boss_spawn_ring_radius"_I*2,"boss_spawn_ring_radius"_I*2}},OnUpperLevel()}; const bool exitRingShouldNotSpawn=SPAWNER_CONTROLLER.has_value()&&!SPAWNER_CONTROLLER.value().empty();
const geom2d::rect<int>arenaBounds=game->GetZones().at("BossArena")[0].zone; if(exitRingShouldNotSpawn){ //See if we have a spawn controller and if we do, spawn the monsters from it instead of spawning the boss ring first.
geom2d::rect<int>clampedArena{vi2d(arenaBounds.pos+"boss_spawn_ring_radius"_I),vi2d(arenaBounds.size-"boss_spawn_ring_radius"_I*2)}; const int nextSpawnerId=SPAWNER_CONTROLLER.value().front();
SPAWNER_CONTROLLER.value().pop();
SPAWNER_LIST[nextSpawnerId].SetTriggered(true);
}else{
ZoneData exitRing{geom2d::rect<int>{vi2d{GetPos()-vf2d{"boss_spawn_ring_radius"_F,"boss_spawn_ring_radius"_F}},vi2d{"boss_spawn_ring_radius"_I*2,"boss_spawn_ring_radius"_I*2}},OnUpperLevel()};
exitRing.zone.pos.x=std::clamp(exitRing.zone.pos.x,clampedArena.pos.x-"boss_spawn_ring_radius"_I,clampedArena.pos.x-"boss_spawn_ring_radius"_I+clampedArena.size.x); const geom2d::rect<int>arenaBounds=game->GetZones().at("BossArena")[0].zone;
exitRing.zone.pos.y=std::clamp(exitRing.zone.pos.y,clampedArena.pos.y-"boss_spawn_ring_radius"_I,clampedArena.pos.y-"boss_spawn_ring_radius"_I+clampedArena.size.y); geom2d::rect<int>clampedArena{vi2d(arenaBounds.pos+"boss_spawn_ring_radius"_I),vi2d(arenaBounds.size-"boss_spawn_ring_radius"_I*2)};
game->AddZone("EndZone",exitRing); //Create a 144x144 ring around the dead boss. exitRing.zone.pos.x=std::clamp(exitRing.zone.pos.x,clampedArena.pos.x-"boss_spawn_ring_radius"_I,clampedArena.pos.x-"boss_spawn_ring_radius"_I+clampedArena.size.x);
exitRing.zone.pos.y=std::clamp(exitRing.zone.pos.y,clampedArena.pos.y-"boss_spawn_ring_radius"_I,clampedArena.pos.y-"boss_spawn_ring_radius"_I+clampedArena.size.y);
game->AddZone("EndZone",exitRing); //Create a 144x144 ring around the dead boss.
}
} }
} }
@ -1043,7 +1054,7 @@ const std::optional<float>Monster::GetTotalLifetime()const{
return MONSTER_DATA.at(GetName()).GetLifetime(); return MONSTER_DATA.at(GetName()).GetLifetime();
} }
const float Monster::GetCollisionRadius()const{ const float Monster::GetCollisionRadius()const{
return MONSTER_DATA.at(GetName()).GetCollisionRadius(); return MONSTER_DATA.at(GetName()).GetCollisionRadius()*GetSizeMult();
} }
void Monster::MarkForDeletion(){ void Monster::MarkForDeletion(){

@ -185,7 +185,7 @@ void MonsterData::InitializeMonsterData(){
if(DATA["Monsters"][MonsterName].HasProperty("Invulnerable"))monster.invulnerable=DATA["Monsters"][MonsterName]["Invulnerable"].GetBool(); if(DATA["Monsters"][MonsterName].HasProperty("Invulnerable"))monster.invulnerable=DATA["Monsters"][MonsterName]["Invulnerable"].GetBool();
if(DATA["Monsters"][MonsterName].HasProperty("Lifetime"))monster.lifetime=DATA["Monsters"][MonsterName]["Lifetime"].GetReal(); if(DATA["Monsters"][MonsterName].HasProperty("Lifetime"))monster.lifetime=DATA["Monsters"][MonsterName]["Lifetime"].GetReal();
monster.collisionRadius=12*monster.GetSizeMult()/2.f; monster.collisionRadius=8;
if(DATA["Monsters"][MonsterName].HasProperty("Collision Radius"))monster.collisionRadius=DATA["Monsters"][MonsterName]["Collision Radius"].GetReal(); if(DATA["Monsters"][MonsterName].HasProperty("Collision Radius"))monster.collisionRadius=DATA["Monsters"][MonsterName]["Collision Radius"].GetReal();
if(hasFourWaySpriteSheet)monster.SetUsesFourWaySprites(); if(hasFourWaySpriteSheet)monster.SetUsesFourWaySprites();
@ -436,5 +436,5 @@ const std::optional<float>MonsterData::GetLifetime()const{
return lifetime; return lifetime;
} }
const float MonsterData::GetCollisionRadius()const{ const float MonsterData::GetCollisionRadius()const{
return collisionRadius*GetSizeMult(); return collisionRadius;
} }

@ -867,7 +867,7 @@ void Player::Moved(){
castPrepAbility->waitForRelease=true; castPrepAbility->waitForRelease=true;
CancelCast(); CancelCast();
} }
for(MonsterSpawner&spawner:SPAWNER_LIST){ for(auto&[spawnerID,spawner]:SPAWNER_LIST){
if(!spawner.SpawnTriggered()&&spawner.DoesUpperLevelSpawning()==OnUpperLevel()&&geom2d::contains(geom2d::rect<float>{spawner.GetPos(),spawner.GetRange()},pos)){ if(!spawner.SpawnTriggered()&&spawner.DoesUpperLevelSpawning()==OnUpperLevel()&&geom2d::contains(geom2d::rect<float>{spawner.GetPos(),spawner.GetRange()},pos)){
if(GameState::STATE==GameState::states[States::GAME_RUN]){ if(GameState::STATE==GameState::states[States::GAME_RUN]){
if(!Tutorial::TaskIsComplete(TutorialTaskName::USE_ATTACK)){ if(!Tutorial::TaskIsComplete(TutorialTaskName::USE_ATTACK)){

@ -96,8 +96,8 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
m.phase=STONE_PILLAR_CAST; m.phase=STONE_PILLAR_CAST;
m.F(A::CASTING_TIMER)=ConfigFloat("Stone Pillar Cast Time"); m.F(A::CASTING_TIMER)=ConfigFloat("Stone Pillar Cast Time");
m.V(A::LOCKON_POS)=game->GetPlayer()->GetPos(); m.V(A::LOCKON_POS)=game->GetPlayer()->GetPos();
game->AddEffect(std::make_unique<Effect>(m.V(A::LOCKON_POS),ConfigFloat("Stone Pillar Cast Time"),"range_indicator.png",m.OnUpperLevel(),vf2d{1.f,1.f}*(MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()/12.f)*1.1f,0.3f,vf2d{},ConfigPixel("Stone Pillar Spell Circle Color"),util::random(2*PI),util::degToRad(ConfigFloat("Stone Pillar Spell Circle Rotation Spd"))),true); game->AddEffect(std::make_unique<Effect>(m.V(A::LOCKON_POS),ConfigFloat("Stone Pillar Cast Time"),"range_indicator.png",m.OnUpperLevel(),vf2d{1.f,1.f}*(MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()*MONSTER_DATA.at("Stone Pillar").GetSizeMult()/12.f)*1.1f,0.3f,vf2d{},ConfigPixel("Stone Pillar Spell Circle Color"),util::random(2*PI),util::degToRad(ConfigFloat("Stone Pillar Spell Circle Rotation Spd"))),true);
game->AddEffect(std::make_unique<Effect>(m.V(A::LOCKON_POS),ConfigFloat("Stone Pillar Cast Time"),"spell_insignia.png",m.OnUpperLevel(),vf2d{1.f,1.f}*(MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()/12.f)*0.75f,0.3f,vf2d{},ConfigPixel("Stone Pillar Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Stone Pillar Spell Insignia Rotation Spd"))),true); game->AddEffect(std::make_unique<Effect>(m.V(A::LOCKON_POS),ConfigFloat("Stone Pillar Cast Time"),"spell_insignia.png",m.OnUpperLevel(),vf2d{1.f,1.f}*(MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()*MONSTER_DATA.at("Stone Pillar").GetSizeMult()/12.f)*0.75f,0.3f,vf2d{},ConfigPixel("Stone Pillar Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Stone Pillar Spell Insignia Rotation Spd"))),true);
}break; }break;
case 1:{ case 1:{
m.PerformAnimation("ROCK TOSS CAST"); m.PerformAnimation("ROCK TOSS CAST");
@ -128,9 +128,9 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
if(m.F(A::CASTING_TIMER)<=0.f){ if(m.F(A::CASTING_TIMER)<=0.f){
game->SpawnMonster(m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Pillar"),m.OnUpperLevel()); game->SpawnMonster(m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Pillar"),m.OnUpperLevel());
ReturnToWaitingPhase(); ReturnToWaitingPhase();
game->Hurt(m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Pillar").GetCollisionRadius(),m.GetAttack(),m.OnUpperLevel(),0.f,HurtType::PLAYER); game->Hurt(m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()*MONSTER_DATA.at("Stone Pillar").GetSizeMult(),m.GetAttack(),m.OnUpperLevel(),0.f,HurtType::PLAYER);
} }
if(geom2d::overlaps(geom2d::circle<float>{m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()},geom2d::circle<float>{m.GetPos(),m.GetCollisionRadius()})){ if(geom2d::overlaps(geom2d::circle<float>{m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()*MONSTER_DATA.at("Stone Pillar").GetSizeMult()},geom2d::circle<float>{m.GetPos(),m.GetCollisionRadius()})){
geom2d::line<float>stonePillarCastLine{m.V(A::LOCKON_POS),m.GetPos()}; geom2d::line<float>stonePillarCastLine{m.V(A::LOCKON_POS),m.GetPos()};
const vf2d targetWalkPos=stonePillarCastLine.rpoint(stonePillarCastLine.length()+48.f); const vf2d targetWalkPos=stonePillarCastLine.rpoint(stonePillarCastLine.length()+48.f);
m.target=targetWalkPos; m.target=targetWalkPos;

@ -46,6 +46,7 @@ All rights reserved.
#include "ItemMapData.h" #include "ItemMapData.h"
#include "Class.h" #include "Class.h"
#include "MonsterData.h" #include "MonsterData.h"
#include <queue>
INCLUDE_MONSTER_DATA INCLUDE_MONSTER_DATA
@ -133,7 +134,8 @@ private:
std::unordered_map<Class,float>devCompletionTrialTime; std::unordered_map<Class,float>devCompletionTrialTime;
BackdropName backdrop=""; BackdropName backdrop="";
std::set<std::string>spawns; std::set<std::string>spawns;
std::map<int,SpawnerTag> SpawnerData; //Spawn groups have IDs, mobs associate which spawner they are tied to via this ID. std::map<int,SpawnerTag>SpawnerData; //Spawn groups have IDs, mobs associate which spawner they are tied to via this ID.
std::optional<std::queue<MonsterSpawnerID>>spawnControllerIDs;
std::map<std::string,std::vector<::ZoneData>> ZoneData; std::map<std::string,std::vector<::ZoneData>> ZoneData;
const std::map<std::string,std::vector<::ZoneData>>&GetZones()const; const std::map<std::string,std::vector<::ZoneData>>&GetZones()const;
public: public:
@ -146,6 +148,8 @@ public:
const float GetDevCompletionTime(Class cl)const; const float GetDevCompletionTime(Class cl)const;
const MapName&GetMapName()const; const MapName&GetMapName()const;
const std::string_view GetMapDisplayName()const; const std::string_view GetMapDisplayName()const;
const bool HasMoreSpawns()const; //Returns whether or not there are more spawns for the spawn controller.
const int Spawn_pop(); //Grabs the next spawn controller ID and removes it from the stack.
std::string FormatLayerData(std::ostream& os, std::vector<LayerTag>tiles); std::string FormatLayerData(std::ostream& os, std::vector<LayerTag>tiles);
std::string FormatSpawnerData(std::ostream& os, std::map<int,SpawnerTag>tiles); std::string FormatSpawnerData(std::ostream& os, std::map<int,SpawnerTag>tiles);
friend std::ostream& operator << (std::ostream& os, Map& rhs); friend std::ostream& operator << (std::ostream& os, Map& rhs);
@ -184,6 +188,7 @@ class TMXParser{
XMLTag npcTag; XMLTag npcTag;
XMLTag spawnerLinkTag; XMLTag spawnerLinkTag;
StagePlate*currentStagePlate=nullptr; StagePlate*currentStagePlate=nullptr;
std::optional<XMLTag>spawnControllerTag;
std::vector<XMLTag>accumulatedMonsterTags; std::vector<XMLTag>accumulatedMonsterTags;
std::map<int,StagePlate>stagePlates; std::map<int,StagePlate>stagePlates;
bool infiniteMap=false; bool infiniteMap=false;
@ -312,6 +317,15 @@ class TMXParser{
const std::string_view Map::GetMapDisplayName()const{ const std::string_view Map::GetMapDisplayName()const{
return name; return name;
} }
const bool Map::HasMoreSpawns()const{
return spawnControllerIDs.has_value()&&spawnControllerIDs.value().size()>0;
}
const int Map::Spawn_pop(){
if(!HasMoreSpawns())ERR("WARNING! Trying to pop from queue when there are no items! Make sure to use HasMoreSpawns() first!");
const int nextSpawnId=spawnControllerIDs.value().front();
spawnControllerIDs.value().pop();
return nextSpawnId;
}
NPCData::NPCData(){} NPCData::NPCData(){}
NPCData::NPCData(XMLTag npcTag){ NPCData::NPCData(XMLTag npcTag){
const std::array<std::string,7>tags={"Function","NPC Name","Roaming Range","Unlock Condition","Spritesheet","x","y"}; const std::array<std::string,7>tags={"Function","NPC Name","Roaming Range","Unlock Condition","Spritesheet","x","y"};
@ -435,6 +449,7 @@ class TMXParser{
inNPCTag=false; inNPCTag=false;
parsedMapInfo.npcs.push_back(NPCData{npcTag}); parsedMapInfo.npcs.push_back(NPCData{npcTag});
} }
if(newTag.tag=="object"&&newTag.data["type"]!="SpawnController"&&spawnControllerTag.has_value())spawnControllerTag={};
if (newTag.tag=="map"){ if (newTag.tag=="map"){
if(stoi(newTag.data["infinite"])==1){ if(stoi(newTag.data["infinite"])==1){
@ -454,6 +469,9 @@ class TMXParser{
parsedMapInfo.LayerData.push_back(l); parsedMapInfo.LayerData.push_back(l);
currentLayerTag=&parsedMapInfo.LayerData.back(); currentLayerTag=&parsedMapInfo.LayerData.back();
}else }else
if (newTag.tag=="object"&&newTag.data["type"]=="SpawnController"){
spawnControllerTag=newTag;
}else
if (newTag.tag=="object"&&newTag.data["type"]=="SpawnGroup"){ if (newTag.tag=="object"&&newTag.data["type"]=="SpawnGroup"){
if(newTag.GetInteger("id")!=0){ if(newTag.GetInteger("id")!=0){
parsedMapInfo.SpawnerData[newTag.GetInteger("id")]={newTag}; parsedMapInfo.SpawnerData[newTag.GetInteger("id")]={newTag};
@ -518,6 +536,10 @@ class TMXParser{
monsterTag=newTag; monsterTag=newTag;
monsterPropertyTagCount=0; monsterPropertyTagCount=0;
}else }else
if(newTag.tag=="property"&&spawnControllerTag.has_value()) {
if(!parsedMapInfo.HasMoreSpawns())parsedMapInfo.spawnControllerIDs=std::queue<int>{};
parsedMapInfo.spawnControllerIDs.value().push(newTag.GetInteger("value"));
}else
if (newTag.tag=="property"&&inNPCTag) { if (newTag.tag=="property"&&inNPCTag) {
npcTag.data[newTag.data["name"]]=newTag.data["value"]; npcTag.data[newTag.data["name"]]=newTag.data["value"];
}else }else

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

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="160" height="144" tilewidth="24" tileheight="24" infinite="0" nextlayerid="6" nextobjectid="14"> <map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="160" height="144" tilewidth="24" tileheight="24" infinite="0" nextlayerid="9" nextobjectid="26">
<properties> <properties>
<property name="Backdrop" propertytype="Backdrop" value="mountain_night"/> <property name="Backdrop" propertytype="Backdrop" value="mountain_night"/>
<property name="Background Music" propertytype="BGM" value="foresty_boss"/> <property name="Background Music" propertytype="BGM" value="foresty_boss"/>
@ -601,8 +601,47 @@
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</data> </data>
</layer> </layer>
<objectgroup id="8" name="Spawn Controller/Arena">
<object id="23" name="Spawn Controller" type="SpawnController" x="1326" y="1158" width="276" height="228">
<properties>
<property name="Spawn1" type="object" value="16"/>
<property name="Spawn2" type="object" value="19"/>
</properties>
</object>
<object id="25" name="Boss Arena" type="BossArena" x="1302" y="1122" width="1448" height="1206"/>
</objectgroup>
<objectgroup id="7" name="Spawn Zone 3" visible="0">
<object id="19" name="Boss Spawn Zone" type="SpawnGroup" x="1560" y="1314" width="964" height="882">
<properties>
<property name="Boss Title Display" value="Zephy, King of Birds"/>
</properties>
<ellipse/>
</object>
<object id="22" template="../maps/Monsters/Zephy, King of Birds.tx" x="1956" y="1572">
<properties>
<property name="spawner" type="object" value="19"/>
</properties>
</object>
</objectgroup>
<objectgroup id="6" name="Spawn Zone 2" visible="0">
<object id="16" name="Major Hawk Spawn Zone" type="SpawnGroup" x="1560" y="1314" width="964" height="882">
<properties>
<property name="Boss Title Display" value="Zephy, King of Birds"/>
</properties>
<ellipse/>
</object>
<object id="17" template="../maps/Monsters/Major Hawk.tx" x="1956" y="1608">
<properties>
<property name="spawner" type="object" value="16"/>
</properties>
</object>
<object id="18" template="../maps/Monsters/Major Hawk.tx" x="2082" y="1608">
<properties>
<property name="spawner" type="object" value="16"/>
</properties>
</object>
</objectgroup>
<objectgroup id="5" name="Spawn Zones"> <objectgroup id="5" name="Spawn Zones">
<object id="1" name="Boss Arena" type="BossArena" x="1302" y="1122" width="1448" height="1206"/>
<object id="3" name="Boss Fight Start Zone" type="SpawnGroup" x="1561" y="1320" width="964" height="498"> <object id="3" name="Boss Fight Start Zone" type="SpawnGroup" x="1561" y="1320" width="964" height="498">
<properties> <properties>
<property name="Boss Title Display" value="Zephy, King of Birds"/> <property name="Boss Title Display" value="Zephy, King of Birds"/>
@ -614,7 +653,7 @@
<property name="spawner" type="object" value="3"/> <property name="spawner" type="object" value="3"/>
</properties> </properties>
</object> </object>
<object id="6" template="../maps/Monsters/Hawk_NOXP.tx" x="2129" y="1386"> <object id="6" template="../maps/Monsters/Hawk_NOXP.tx" name="Hawk (No XP/Drop)" x="2129" y="1386">
<properties> <properties>
<property name="spawner" type="object" value="3"/> <property name="spawner" type="object" value="3"/>
</properties> </properties>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?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="24" tileheight="24" infinite="1" nextlayerid="3" nextobjectid="26"> <map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="24" tileheight="24" infinite="1" nextlayerid="3" nextobjectid="29">
<tileset firstgid="1" source="Monsters.tsx"/> <tileset firstgid="1" source="Monsters.tsx"/>
<layer id="1" name="Tile Layer 1" width="30" height="20"> <layer id="1" name="Tile Layer 1" width="30" height="20">
<data encoding="csv"/> <data encoding="csv"/>
@ -23,5 +23,8 @@
<object id="18" template="Monsters/Stone Elemental.tx" type="Monster" x="180" y="156"/> <object id="18" template="Monsters/Stone Elemental.tx" type="Monster" x="180" y="156"/>
<object id="22" template="Monsters/Goblin (Dagger).tx" type="Monster" x="26" y="226"/> <object id="22" template="Monsters/Goblin (Dagger).tx" type="Monster" x="26" y="226"/>
<object id="25" template="Monsters/Goblin (Bombs).tx" type="Monster" x="142" y="222"/> <object id="25" template="Monsters/Goblin (Bombs).tx" type="Monster" x="142" y="222"/>
<object id="26" template="Monsters/Hawk_NOXP.tx" type="Monster" x="143" y="92"/>
<object id="27" template="Monsters/Major Hawk.tx" type="Monster" x="180" y="96"/>
<object id="28" template="Monsters/Zephy, King of Birds.tx" type="Monster" x="-42" y="348"/>
</objectgroup> </objectgroup>
</map> </map>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<template> <template>
<tileset firstgid="1" source="../Monsters.tsx"/> <tileset firstgid="1" source="../Monsters.tsx"/>
<object name="Hawk_NOXP" type="Monster" gid="12" width="24" height="24"/> <object name="Hawk (No XP/Drop)" type="Monster" gid="12" width="24" height="24"/>
</template> </template>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<template>
<tileset firstgid="1" source="../Monsters.tsx"/>
<object name="Major Hawk" type="Monster" gid="19" width="57.6" height="57.6"/>
</template>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<template>
<tileset firstgid="1" source="../Monsters.tsx"/>
<object name="Zephy, King of Birds" type="Monster" gid="20" width="192" height="192"/>
</template>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Loading…
Cancel
Save