diff --git a/Adventures in Lestoria/AdventuresInLestoria.cpp b/Adventures in Lestoria/AdventuresInLestoria.cpp index bca87914..e3bcae67 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.cpp +++ b/Adventures in Lestoria/AdventuresInLestoria.cpp @@ -744,14 +744,16 @@ const HurtList AiL::Hurt(vf2d pos,float radius,int damage,bool upperLevel,float if(CheckForMonsterCollisions){ for(std::unique_ptr&m:MONSTER_LIST){ - if(geom2d::overlaps(geom2d::circle(pos,radius),geom2d::circle(m->GetPos(),12*m->GetSizeMult()))){ + const bool InRange=geom2d::overlaps(geom2d::circle(pos,radius),geom2d::circle(m->GetPos(),12*m->GetSizeMult())); + if(InRange){ HurtReturnValue returnVal=m->Hurt(damage,upperLevel,z); hitList.push_back({&*m,returnVal}); } } } if(CheckForPlayerCollisions){ - if(geom2d::overlaps(geom2d::circle(pos,radius),geom2d::circle(GetPlayer()->GetPos(),12*GetPlayer()->GetSizeMult()))){ + const bool InRange=geom2d::overlaps(geom2d::circle(pos,radius),geom2d::circle(GetPlayer()->GetPos(),12*GetPlayer()->GetSizeMult())); + if(InRange){ HurtReturnValue returnVal=GetPlayer()->Hurt(damage,upperLevel,z); hitList.push_back({GetPlayer(),returnVal}); } @@ -759,6 +761,21 @@ const HurtList AiL::Hurt(vf2d pos,float radius,int damage,bool upperLevel,float return hitList; } +const HurtList AiL::HurtMonsterType(vf2d pos,float radius,int damage,bool upperLevel,float z,const std::string_view monsterName)const{ + if(!MONSTER_DATA.count(std::string(monsterName)))ERR(std::format("WARNING! Cannot check for monster type name {}! Does not exist!",monsterName)); + HurtList hitList; + + for(std::unique_ptr&m:MONSTER_LIST){ + const bool InRange=geom2d::overlaps(geom2d::circle(pos,radius),geom2d::circle(m->GetPos(),12*m->GetSizeMult())); + if(m->GetName()==monsterName&&InRange){ + HurtReturnValue returnVal=m->Hurt(damage,upperLevel,z); + hitList.push_back({&*m,returnVal}); + } + } + + return hitList; +} + void AiL::ProximityKnockback(const vf2d pos,const float radius,const float knockbackAmt,const HurtType knockbackTargets)const{ const bool CheckForMonsterCollisions=knockbackTargets&HurtType::MONSTER; const bool CheckForPlayerCollisions=knockbackTargets&HurtType::PLAYER; diff --git a/Adventures in Lestoria/AdventuresInLestoria.h b/Adventures in Lestoria/AdventuresInLestoria.h index f5adbaa7..6129f75d 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.h +++ b/Adventures in Lestoria/AdventuresInLestoria.h @@ -357,6 +357,7 @@ public: Overlay&GetOverlay(); void SetWindSpeed(vf2d newWindSpd); const vf2d&GetWindSpeed()const; + const HurtList HurtMonsterType(vf2d pos,float radius,int damage,bool upperLevel,float z,const std::string_view monsterName)const; struct TileGroupData{ vi2d tilePos; diff --git a/Adventures in Lestoria/BreakingPillar.cpp b/Adventures in Lestoria/BreakingPillar.cpp index 97b55b08..11a5370c 100644 --- a/Adventures in Lestoria/BreakingPillar.cpp +++ b/Adventures in Lestoria/BreakingPillar.cpp @@ -50,8 +50,9 @@ void Monster::STRATEGY::BREAKING_PILLAR(Monster&m,float fElapsedTime,std::string m.animation.ModifyDisplaySprite(m.internal_animState,ConfigString("Break Phase 1 Animation Name")); }else m.animation.ModifyDisplaySprite(m.internal_animState,ConfigString("Break Phase 2 Animation Name")); - if(m.IsDead()&&!m.B(A::MARKED_DEAD)){ //Kill and fade out the pillar. + m.SetStrategyDeathFunction([](GameEvent&deathEvent,Monster&m,const std::string&monsterName){ m.lifetime=0.01f; m.B(A::MARKED_DEAD)=true; - } + return false; + }); } \ No newline at end of file diff --git a/Adventures in Lestoria/DamageNumber.h b/Adventures in Lestoria/DamageNumber.h index d236f48f..716e0c14 100644 --- a/Adventures in Lestoria/DamageNumber.h +++ b/Adventures in Lestoria/DamageNumber.h @@ -59,6 +59,7 @@ struct DamageNumber{ const static float MOVE_UP_TIME; float originalRiseSpd=0.f; DamageNumber(); + //The friendly flag indicates if the number was for a friendly/player target or if it's for a monster target (set to false) DamageNumber(vf2d pos,int damage,bool friendly=false,DamageNumberType type=HEALTH_LOSS); void RecalculateSize(); }; \ No newline at end of file diff --git a/Adventures in Lestoria/GameEvent.cpp b/Adventures in Lestoria/GameEvent.cpp index 4fe1ece6..cc9cabaf 100644 --- a/Adventures in Lestoria/GameEvent.cpp +++ b/Adventures in Lestoria/GameEvent.cpp @@ -37,7 +37,6 @@ All rights reserved. #pragma endregion #include "GameEvent.h" -#include "Monster.h" #include std::vector>GameEvent::events; @@ -68,6 +67,5 @@ MonsterStrategyGameEvent::MonsterStrategyGameEvent(std::functionm,this->strategy); } \ No newline at end of file diff --git a/Adventures in Lestoria/LargeStone.cpp b/Adventures in Lestoria/LargeStone.cpp index 4c61a88c..c0aaede7 100644 --- a/Adventures in Lestoria/LargeStone.cpp +++ b/Adventures in Lestoria/LargeStone.cpp @@ -65,6 +65,14 @@ void LargeStone::Update(float fElapsedTime){ game->AddEffect(std::make_unique(pos+vf2d{radius,randomDir}.cart(),0.f,"circle.png",OnUpperLevel(),vf2d{1.5f,1.5f}*util::random(2.f),util::random(1.f),vf2d{vf2d{radius,randomDir}.cart().x,-util::random(30.f)-20.f},BLACK)); } + + const HurtList pillarList{game->HurtMonsterType(pos,radius,3,OnUpperLevel(),GetZ(),"Stone Golem Pillar")}; + + for(auto&[ptr,wasHurt]:pillarList){ + Monster*const monsterPtr{std::get(ptr)}; + if(wasHurt)ERR(std::format("WARNING! Somehow a {} has taken non-true damage! THIS SHOULD NOT BE HAPPENING!",monsterPtr->GetDisplayName())); + monsterPtr->_DealTrueDamage(3); + } game->ProximityKnockback(pos,radius,knockbackAmt,HurtType::MONSTER|HurtType::PLAYER); diff --git a/Adventures in Lestoria/Monster.cpp b/Adventures in Lestoria/Monster.cpp index 3912a8ce..497f3c49 100644 --- a/Adventures in Lestoria/Monster.cpp +++ b/Adventures in Lestoria/Monster.cpp @@ -602,7 +602,11 @@ const bool Monster::AttackAvoided(const float attackZ)const{ } bool Monster::Hurt(int damage,bool onUpperLevel,float z){ - if(Invulnerable()||!IsAlive()||onUpperLevel!=OnUpperLevel()||AttackAvoided(z)) return false; + return _Hurt(damage,onUpperLevel,z,TrueDamageFlag::NORMAL_DAMAGE); +} +bool Monster::_Hurt(int damage,bool onUpperLevel,float z,const TrueDamageFlag damageRule){ + const bool TrueDamage=damageRule==TrueDamageFlag::IGNORE_DAMAGE_RULES; + if(!TrueDamage&&(Invulnerable()||!IsAlive()||onUpperLevel!=OnUpperLevel()||AttackAvoided(z)))return false; if(game->InBossEncounter()){ game->StartBossEncounter(); } @@ -620,13 +624,19 @@ bool Monster::Hurt(int damage,bool onUpperLevel,float z){ mod_dmg-=mod_dmg*GetDamageReductionFromBuffs(); mod_dmg=std::ceil(mod_dmg); + + if(TrueDamage){ + mod_dmg=damage; //True damage override, ignore all damage changes. + crit=false; //True damage disables critting. + } + hp=std::max(0,hp-int(mod_dmg)); if(lastHitTimer>0){ damageNumberPtr.get()->damage+=int(mod_dmg); damageNumberPtr.get()->pauseTime=0.4f; damageNumberPtr.get()->RecalculateSize(); - } else { + }else{ damageNumberPtr=std::make_shared(pos,int(mod_dmg)); DAMAGENUMBER_LIST.push_back(damageNumberPtr); } @@ -650,7 +660,7 @@ bool Monster::Hurt(int damage,bool onUpperLevel,float z){ SoundEffect::PlaySFX(GetHurtSound(),GetPos()); } } - if(game->InBossEncounter()){ + if(game->InBossEncounter()&&isBoss){ game->BossDamageDealt(int(mod_dmg)); } @@ -881,9 +891,7 @@ void Monster::OnDeath(){ } ) - if(hasStrategyDeathFunction){ - GameEvent::AddEvent(std::make_unique(strategyDeathFunc,*this,MONSTER_DATA[name].GetAIStrategy())); - } + if(strategyDeathFunc)GameEvent::AddEvent(std::make_unique(strategyDeathFunc,*this,MONSTER_DATA[name].GetAIStrategy())); SpawnDrops(); @@ -975,8 +983,13 @@ const float Monster::GetCollisionDamage()const{ else return MONSTER_DATA[name].GetCollisionDmg(); } +//Sets the strategy death function that runs when a monster dies. +// The function should return false to indicate the event is over. If the event should keep running, return true. +//Arguments are: +// GameEvent& - The death event itself. +// Monster& - The monster reference +// const std::string& - The strategy name. void Monster::SetStrategyDeathFunction(std::functionfunc){ - hasStrategyDeathFunction=true; strategyDeathFunc=func; } @@ -1121,4 +1134,8 @@ const float Monster::GetHealthRatio()const{ const bool Monster::IsSolid()const{ return Immovable(); +} + +void Monster::_DealTrueDamage(const uint32_t damageAmt){ + _Hurt(damageAmt,OnUpperLevel(),GetZ(),TrueDamageFlag::IGNORE_DAMAGE_RULES); } \ No newline at end of file diff --git a/Adventures in Lestoria/Monster.h b/Adventures in Lestoria/Monster.h index a82ab757..6a3e67d9 100644 --- a/Adventures in Lestoria/Monster.h +++ b/Adventures in Lestoria/Monster.h @@ -59,6 +59,11 @@ enum class Attribute; class GameEvent; +enum class TrueDamageFlag{ + NORMAL_DAMAGE, + IGNORE_DAMAGE_RULES, //Deals true damage, ignoring established invulnerability/iframe rules. Will never miss and will not have its damage modified by any buffs/stats. +}; + class DeathSpawnInfo{ std::string monsterSpawnName; uint8_t spawnAmt; @@ -181,6 +186,7 @@ public: const bool HasArrowIndicator()const; const bool ReachedTargetPos(const float maxDistanceFromTarget=4.f)const; const float GetHealthRatio()const; + void _DealTrueDamage(const uint32_t damageAmt); 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. @@ -231,7 +237,7 @@ private: bool hasStrategyDeathFunction=false; NPCData npcData; float lastPathfindingCooldown=0.f; - std::functionstrategyDeathFunc; + std::functionstrategyDeathFunc{}; void SetStrategyDeathFunction(std::functionfunc); ItemAttribute&Get(std::string_view attr); //Returns false if the monster could not be moved to the requested location due to collision. @@ -258,6 +264,7 @@ private: 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!! float solidFadeTimer{0.f}; + bool _Hurt(int damage,bool onUpperLevel,float z,const TrueDamageFlag damageRule); private: struct STRATEGY{ static std::string ERR; @@ -314,5 +321,4 @@ struct MonsterSpawner{ vf2d GetPos(); bool DoesUpperLevelSpawning(); void SetTriggered(bool trigger,bool spawnMonsters=true); - friend std::ostream&operator<<(std::ostream&os,MonsterSpawner&rhs); }; \ No newline at end of file diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index 858299bb..90076fbe 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 3 -#define VERSION_BUILD 9667 +#define VERSION_BUILD 9675 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/x64/Release/Adventures in Lestoria.exe b/x64/Release/Adventures in Lestoria.exe index c511e44f..2ad37714 100644 Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ