diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj b/Adventures in Lestoria/Adventures in Lestoria.vcxproj index 7b83359d..d1838906 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj @@ -693,6 +693,10 @@ + + + + diff --git a/Adventures in Lestoria/AdventuresInLestoria.cpp b/Adventures in Lestoria/AdventuresInLestoria.cpp index 13c67911..55621217 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.cpp +++ b/Adventures in Lestoria/AdventuresInLestoria.cpp @@ -3951,6 +3951,10 @@ rcode AiL::LoadResource(Renderable&renderable,std::string_view imgPath,bool filt void AiL::UpdateMonsters(){ for(std::unique_ptr&m:MONSTER_LIST){ + if(m->markedForDeletion){ + AMonsterIsMarkedForDeletion(); + continue; + } m->Update(game->GetElapsedTime()); } for(Monster&m:game->monstersToBeSpawned){ @@ -3958,6 +3962,8 @@ void AiL::UpdateMonsters(){ MONSTER_LIST.push_back(std::make_unique(m)); if(MONSTER_LIST.capacity()>prevCapacity)LOG(std::format("WARNING! The monster list has automatically reserved more space and resized to {}! This caused one potential frame where bullet/effect hitlists that stored information on what monsters were hit to potentially be hit a second time or cause monsters that should've been hit to never be hit. Consider starting with a larger default reserved size for MONSTER_LIST if your intention was to have this many monsters!",MONSTER_LIST.capacity())); } + if(aMonsterIsMarkedForDeletion)std::erase_if(MONSTER_LIST,[](const std::unique_ptr&m){return m->markedForDeletion;}); + aMonsterIsMarkedForDeletion=false; game->monstersToBeSpawned.clear(); } @@ -4077,4 +4083,8 @@ void AiL::UpdateEntities(){ ItemDrop::UpdateDrops(GetElapsedTime()); UpdateBullets(GetElapsedTime()); +} + +void AiL::AMonsterIsMarkedForDeletion(){ + aMonsterIsMarkedForDeletion=true; } \ No newline at end of file diff --git a/Adventures in Lestoria/AdventuresInLestoria.h b/Adventures in Lestoria/AdventuresInLestoria.h index 34ee26cf..8c3e91a1 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.h +++ b/Adventures in Lestoria/AdventuresInLestoria.h @@ -168,6 +168,7 @@ private: States::State transitionState=States::State::GAME_RUN; bool gameEnd=false; std::vectormonstersToBeSpawned; + bool aMonsterIsMarkedForDeletion=false; //DO NOT MODIFY DIRECTLY! Use AMonsterIsMarkedForDeletion() instead! time_t gameStarted; std::functionresponseCallback; float fadeInDuration=0; @@ -338,6 +339,7 @@ public: void ResetCompletedStageFlag(); void UpdateEntities(); Minimap minimap; + void AMonsterIsMarkedForDeletion(); //The way this is implemented is that monsters marked for deletion will cause the monster update loop to detect there's at least one or more monsters that must be deleted and will call erase_if on the list at the end of the iteration loop. struct TileGroupData{ vi2d tilePos; diff --git a/Adventures in Lestoria/Do_Nothing.cpp b/Adventures in Lestoria/Do_Nothing.cpp new file mode 100644 index 00000000..f5061687 --- /dev/null +++ b/Adventures in Lestoria/Do_Nothing.cpp @@ -0,0 +1,41 @@ +#pragma region License +/* +License (OLC-3) +~~~~~~~~~~~~~~~ + +Copyright 2024 Joshua Sigona + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions or derivations of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions or derivative works in binary form must reproduce the above +copyright notice. This list of conditions and the following disclaimer must be +reproduced in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may +be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +Portions of this software are copyright © 2024 The FreeType +Project (www.freetype.org). Please see LICENSE_FT.txt for more information. +All rights reserved. +*/ +#pragma endregion + +#include "Monster.h" + +void Monster::STRATEGY::DONOTHING(Monster&m,float fElapsedTime,std::string strategy){} \ No newline at end of file diff --git a/Adventures in Lestoria/Monster.cpp b/Adventures in Lestoria/Monster.cpp index 1dbb03ea..f1729b81 100644 --- a/Adventures in Lestoria/Monster.cpp +++ b/Adventures in Lestoria/Monster.cpp @@ -65,7 +65,7 @@ safemap>STRATEGY_DAT std::mapMonsterData::imgs; Monster::Monster(vf2d pos,MonsterData data,bool upperLevel,bool bossMob): - pos(pos),spawnPos(pos),hp(data.GetHealth()),size(data.GetSizeMult()),targetSize(data.GetSizeMult()),strategy(data.GetAIStrategy()),name(data.GetDisplayName()),upperLevel(upperLevel),isBoss(bossMob),facingDirection(Direction::SOUTH){ + pos(pos),spawnPos(pos),hp(data.GetHealth()),size(data.GetSizeMult()),targetSize(data.GetSizeMult()),strategy(data.GetAIStrategy()),name(data.GetDisplayName()),upperLevel(upperLevel),isBoss(bossMob),facingDirection(Direction::SOUTH),lifetime(GetTotalLifetime()){ for(const std::string&anim:data.GetAnimations()){ animation.AddState(anim,ANIMATION_DATA[std::format("{}_{}",name,anim)]); } @@ -269,6 +269,20 @@ bool Monster::Update(float fElapsedTime){ lastFacingDirectionChange+=fElapsedTime; timeSpentAlive+=fElapsedTime; + #pragma region Handle Monster Lifetime and fade timer. + if(fadeTimer>0.f){ + fadeTimer=std::max(0.f,fadeTimer-fElapsedTime); + if(fadeTimer==0.f)MarkForDeletion(); + }else + if(GetLifetime().has_value()){ + lifetime.value()=std::max(0.f,lifetime.value()-fElapsedTime); + if(lifetime.value()==0.f){ + fadeTimer=1.f; + lifetime={}; //Nullify the lifetime value as the monster has expired. So we'll prevent a loop by doing this. + } + } + #pragma endregion + if(size!=targetSize){ if(size>targetSize){ size=std::max(targetSize,size-AiL::SIZE_CHANGE_SPEED*fElapsedTime); @@ -323,12 +337,13 @@ bool Monster::Update(float fElapsedTime){ geom2d::line line(pos,m->GetPos()); float dist = line.length(); m->SetPos(line.rpoint(dist*1.1f)); - if(m->IsAlive()){ + if(!Immovable()&&m->IsAlive()){ vel=line.vector().norm()*-128; } } } - if(!game->GetPlayer()->HasIframes()&&abs(game->GetPlayer()->GetZ()-GetZ())<=1&&game->GetPlayer()->OnUpperLevel()==OnUpperLevel()&&geom2d::overlaps(geom2d::circle(pos,12*size/2),geom2d::circle(game->GetPlayer()->GetPos(),12*game->GetPlayer()->GetSizeMult()/2))){ + if(!Immovable()&& + !game->GetPlayer()->HasIframes()&&abs(game->GetPlayer()->GetZ()-GetZ())<=1&&game->GetPlayer()->OnUpperLevel()==OnUpperLevel()&&geom2d::overlaps(geom2d::circle(pos,12*size/2),geom2d::circle(game->GetPlayer()->GetPos(),12*game->GetPlayer()->GetSizeMult()/2))){ geom2d::line line(pos,game->GetPlayer()->GetPos()); float dist = line.length(); SetPos(line.rpoint(-0.1f)); @@ -400,9 +415,14 @@ void Monster::Draw()const{ } const bool NotOnTitleScreen=GameState::STATE!=GameState::states[States::MAIN_MENU]; + uint8_t blendColAlpha=255U; if(NotOnTitleScreen - &&(game->GetPlayer()->HasIframes()||OnUpperLevel()!=game->GetPlayer()->OnUpperLevel()||abs(GetZ()-game->GetPlayer()->GetZ())>1))blendCol.a=160; + &&(game->GetPlayer()->HasIframes()||OnUpperLevel()!=game->GetPlayer()->OnUpperLevel()||abs(GetZ()-game->GetPlayer()->GetZ())>1))blendColAlpha=160; + + if(fadeTimer>0.f)blendColAlpha=uint8_t(util::lerp(0,blendCol.a,fadeTimer)); //Fade timer goes from 1 to 0 seconds. + + blendCol.a=blendColAlpha; game->view.DrawPartialRotatedDecal(GetPos()-vf2d{0,GetZ()},GetFrame().GetSourceImage()->Decal(),spriteRot,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,vf2d(GetSizeMult()*(!HasFourWaySprites()&&GetFacingDirection()==Direction::EAST?-1:1),GetSizeMult()),blendCol); if(overlaySprite.length()!=0){ @@ -559,7 +579,7 @@ const bool Monster::AttackAvoided(const float attackZ)const{ } bool Monster::Hurt(int damage,bool onUpperLevel,float z){ - if(!IsAlive()||onUpperLevel!=OnUpperLevel()||AttackAvoided(z)) return false; + if(Invulnerable()||!IsAlive()||onUpperLevel!=OnUpperLevel()||AttackAvoided(z)) return false; if(game->InBossEncounter()){ game->StartBossEncounter(); } @@ -1011,8 +1031,15 @@ const bool Monster::Invulnerable()const{ return MONSTER_DATA.at(GetName()).Invulnerable(); } const std::optionalMonster::GetLifetime()const{ + return lifetime; +} +const std::optionalMonster::GetTotalLifetime()const{ return MONSTER_DATA.at(GetName()).GetLifetime(); } const std::optionalMonster::GetCollisionRadius()const{ return MONSTER_DATA.at(GetName()).GetCollisionRadius(); +} + +void Monster::MarkForDeletion(){ + markedForDeletion=true; } \ No newline at end of file diff --git a/Adventures in Lestoria/Monster.h b/Adventures in Lestoria/Monster.h index 11f7872d..a76dee2f 100644 --- a/Adventures in Lestoria/Monster.h +++ b/Adventures in Lestoria/Monster.h @@ -168,9 +168,13 @@ public: const bool Invulnerable()const; //If an object has a lifetime set, returns it. const std::optionalGetLifetime()const; + const std::optionalGetTotalLifetime()const; //If an object has a collision radius, returns it. const std::optionalGetCollisionRadius()const; private: + //NOTE: Marking a monster for deletion does not trigger any death events. It just simply removes the monster from the field!! + // The way this works is that monsters marked for deletion will cause the monster update loop to detect there's at least one or more monsters that must be deleted and will call erase_if on the list at the end of the iteration loop. + void MarkForDeletion(); std::string name; vf2d pos; vf2d vel={0,0}; @@ -240,6 +244,9 @@ private: std::vectordeathData; //Data that contains further actions and information when this monster dies. Mostly used to spawn sub-monsters from a defeated monster. vf2d mountedSprOffset{}; float timeSpentAlive{0.f}; + std::optionallifetime{}; + float fadeTimer{0.f}; + bool markedForDeletion{false}; //DO NOT MODIFY DIRECTLY. Use MarkForDeletion() if this monster needs to be marked. NOTE: Marking a monster for deletion does not trigger any death events. It just simply removes the monster from the field!! private: struct STRATEGY{ static std::string ERR; @@ -269,6 +276,7 @@ private: static void GOBLIN_BOMB(Monster&m,float fElapsedTime,std::string strategy); static void HAWK(Monster&m,float fElapsedTime,std::string strategy); static void STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string strategy); + static void DONOTHING(Monster&m,float fElapsedTime,std::string strategy); }; bool bumpedIntoTerrain=false; //Gets set to true before a strategy executes if the monster runs into some terrain on this frame. bool attackedByPlayer=false; //Gets set to true before a strategy executes if the monster has been attacked by the player. diff --git a/Adventures in Lestoria/RUN_STRATEGY.cpp b/Adventures in Lestoria/RUN_STRATEGY.cpp index 13dab894..a3258bcf 100644 --- a/Adventures in Lestoria/RUN_STRATEGY.cpp +++ b/Adventures in Lestoria/RUN_STRATEGY.cpp @@ -62,6 +62,7 @@ void Monster::InitializeStrategies(){ STRATEGY_DATA.insert("Goblin Bomb",Monster::STRATEGY::GOBLIN_BOMB); STRATEGY_DATA.insert("Hawk",Monster::STRATEGY::HAWK); STRATEGY_DATA.insert("Stone Elemental",Monster::STRATEGY::STONE_ELEMENTAL); + STRATEGY_DATA.insert("Do Nothing",Monster::STRATEGY::DONOTHING); STRATEGY_DATA.SetInitialized(); } diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index 9a3f737d..c1b31d7f 100644 --- a/Adventures in Lestoria/Version.h +++ b/Adventures in Lestoria/Version.h @@ -39,7 +39,7 @@ All rights reserved. #define VERSION_MAJOR 1 #define VERSION_MINOR 2 #define VERSION_PATCH 0 -#define VERSION_BUILD 9270 +#define VERSION_BUILD 9271 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Adventures in Lestoria/assets/config/MonsterStrategies.txt b/Adventures in Lestoria/assets/config/MonsterStrategies.txt index 31487294..c8ba2289 100644 --- a/Adventures in Lestoria/assets/config/MonsterStrategies.txt +++ b/Adventures in Lestoria/assets/config/MonsterStrategies.txt @@ -725,4 +725,8 @@ MonsterStrategy # Minimum Distance the Stone Elemental will always perform a dive Auto Dive Range = 1200 } + Do Nothing + { + + } } \ No newline at end of file diff --git a/Adventures in Lestoria/assets/config/Monsters.txt b/Adventures in Lestoria/assets/config/Monsters.txt index 1eb1a805..ee96c686 100644 --- a/Adventures in Lestoria/assets/config/Monsters.txt +++ b/Adventures in Lestoria/assets/config/Monsters.txt @@ -786,17 +786,17 @@ Monsters Invulnerable = True MoveSpd = 0% - # Due to the Stone Elemental's base sprite size being 36x36, 220% is actually 146.666666667% size when factoring in a 50% larger sprite. - Size = 100% - Collision Radius = 59 + # The Pillar is supposed to be 350 radius. + Size = 350% + Collision Radius = 8 Lifetime = 5s XP = 0 - Strategy = Run Towards + Strategy = Do Nothing #Size of each animation frame - SheetFrameSize = 168,528 + SheetFrameSize = 24,96 # Setting this to true means every four rows indicates one animation, the ordering of the directions is: NORTH, EAST, SOUTH, WEST 4-Way Spritesheet = False @@ -806,7 +806,7 @@ Monsters # Frame Count, Frame Speed (s), Frame Cycling (Repeat,OneShot,PingPong,Reverse) # Animations must be defined in the same order as they are in their sprite sheets # The First Four animations must represent a standing, walking, attack, and death animation. Their names are up to the creator. - IDLE = 1, 0.6, Repeat + IDLE = 4, 0.4, OneShot WALK = 1, 0.2, Repeat STAND BEHIND ME = 1, 0.2, OneShot DEATH = 1, 0.15, OneShot diff --git a/Adventures in Lestoria/assets/config/credits.txt b/Adventures in Lestoria/assets/config/credits.txt index b9de5468..758d8ab3 100644 --- a/Adventures in Lestoria/assets/config/credits.txt +++ b/Adventures in Lestoria/assets/config/credits.txt @@ -45,32 +45,34 @@ Credits LINE[42]="ERA OF FANTASY - GRASSLANDS" LINE[43]="X: @Namatnieks" LINE[44]=" " - LINE[45]="*** Minifantasy - Tiny Overworld v1.0 ***" - LINE[46]="Minifantasy is an original idea by Krishna Palacio" - LINE[47]=" " - LINE[48]="Public Domain Font Authors" - LINE[49]="c64esque by andraaspar" - LINE[50]="Habbo by Omni" - LINE[51]="Unknown by Anonymous" - LINE[52]=" " - LINE[53]="Nb Pixel Font Bundle" - LINE[54]="Nb Pixel Font Bundle 2" - LINE[55]=" " - LINE[56]=" " - LINE[57]="Game License" - LINE[58]="~~~~~~~~~~~~~~~~" - LINE[59]="License (OLC-3)" - LINE[60]="~~~~~~~~~~~~~~~" - LINE[61]=" " - LINE[62]="Copyright ` 2024 Sig Productions " + LINE[45]="Fantastic Pillar by InThePixel" + LINE[46]=" " + LINE[47]="*** Minifantasy - Tiny Overworld v1.0 ***" + LINE[48]="Minifantasy is an original idea by Krishna Palacio" + LINE[49]=" " + LINE[50]="Public Domain Font Authors" + LINE[51]="c64esque by andraaspar" + LINE[52]="Habbo by Omni" + LINE[53]="Unknown by Anonymous" + LINE[54]=" " + LINE[55]="Nb Pixel Font Bundle" + LINE[56]="Nb Pixel Font Bundle 2" + LINE[57]=" " + LINE[58]=" " + LINE[59]="Game License" + LINE[60]="~~~~~~~~~~~~~~~~" + LINE[61]="License (OLC-3)" + LINE[62]="~~~~~~~~~~~~~~~" LINE[63]=" " - LINE[64]="Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:" + LINE[64]="Copyright ` 2024 Sig Productions " LINE[65]=" " - LINE[66]="1. Redistributions or derivations of source code must retain the above copyright notice, this list of conditions and the following disclaimer." + LINE[66]="Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:" LINE[67]=" " - LINE[68]="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." + LINE[68]="1. Redistributions or derivations of source code must retain the above copyright notice, this list of conditions and the following disclaimer." LINE[69]=" " - LINE[70]="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." + LINE[70]="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." LINE[71]=" " - LINE[72]="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." + LINE[72]="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." + LINE[73]=" " + LINE[74]="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." } \ No newline at end of file diff --git a/Adventures in Lestoria/assets/gamepack.pak b/Adventures in Lestoria/assets/gamepack.pak deleted file mode 100644 index ca363a9e..00000000 Binary files a/Adventures in Lestoria/assets/gamepack.pak and /dev/null differ diff --git a/x64/Release/Adventures in Lestoria.exe b/x64/Release/Adventures in Lestoria.exe index 8fa8466f..63ee9fd7 100644 Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ