From 3024111b4ac137b73fff488f745d47086775ccc1 Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Tue, 1 Oct 2024 23:00:02 -0700 Subject: [PATCH] Remove unnecessary variables from old monster update code, remove return value, create unnecessary ambiguity with why we would need to return true/false for no purpose. Add Unconscious timer and tests for Monsters. --- Adventures in Lestoria Tests/MonsterTests.cpp | 18 +++++++ Adventures in Lestoria/Monster.cpp | 54 +++++++++++-------- Adventures in Lestoria/Monster.h | 5 +- Adventures in Lestoria/MonsterData.cpp | 5 ++ Adventures in Lestoria/MonsterData.h | 2 + Adventures in Lestoria/Parrot.cpp | 4 -- Adventures in Lestoria/Pirate_Captain.cpp | 1 - 7 files changed, 60 insertions(+), 29 deletions(-) diff --git a/Adventures in Lestoria Tests/MonsterTests.cpp b/Adventures in Lestoria Tests/MonsterTests.cpp index ae920e36..7a021df1 100644 --- a/Adventures in Lestoria Tests/MonsterTests.cpp +++ b/Adventures in Lestoria Tests/MonsterTests.cpp @@ -506,5 +506,23 @@ namespace MonsterTests m.Heal(50); Assert::IsTrue(m._DealTrueDamage(50,HurtFlag::PLAYER_ABILITY),L"When hurting a monster that was alive and takes enough damage to die, it should still take damage and should be triggering like normal."); } + TEST_METHOD(UnconsciousMonsterTest){ + Monster&parrot{game->SpawnMonster({},MONSTER_DATA.at("Parrot"))}; + Assert::IsFalse(parrot.IsUnconscious(),L"Parrot should not be immediately unconscious."); + parrot.Hurt(1,parrot.OnUpperLevel(),parrot.GetZ()); + Assert::IsFalse(parrot.IsUnconscious(),L"Parrot should not be unconscious after taking 1 damage."); + parrot.Hurt(parrot.GetMaxHealth(),parrot.OnUpperLevel(),parrot.GetZ()); + Assert::IsTrue(parrot.IsUnconscious(),L"Parrot should now be unconscious."); + Assert::IsTrue(parrot.IsDead(),L"Parrot is considered dead."); + Assert::IsTrue(parrot.InUndamageableState(parrot.OnUpperLevel(),parrot.GetZ()),L"Parrot should be in an undamageable state. Hopefully for obvious reasons."); + game->OnUserUpdate(MONSTER_DATA.at("Parrot").UnconsciousTime()/2); + Assert::IsTrue(parrot.IsUnconscious(),L"Parrot should still be unconscious."); + Assert::IsTrue(parrot.IsDead(),L"Parrot should still be considered dead."); + Assert::IsTrue(parrot.InUndamageableState(parrot.OnUpperLevel(),parrot.GetZ()),L"Parrot should still be in an undamageable state."); + game->OnUserUpdate(MONSTER_DATA.at("Parrot").UnconsciousTime()/2); + Assert::IsTrue(!parrot.IsUnconscious(),L"Parrot should no longer be unconscious."); + Assert::IsTrue(parrot.IsAlive(),L"Parrot should be alive."); + Assert::IsTrue(!parrot.InUndamageableState(parrot.OnUpperLevel(),parrot.GetZ()),L"Parrot should not be in an undamageable state."); + } }; } \ No newline at end of file diff --git a/Adventures in Lestoria/Monster.cpp b/Adventures in Lestoria/Monster.cpp index be28091a..4af81922 100644 --- a/Adventures in Lestoria/Monster.cpp +++ b/Adventures in Lestoria/Monster.cpp @@ -267,7 +267,7 @@ bool Monster::SetY(float y){ return _SetY(y); } -bool Monster::Update(float fElapsedTime){ +void Monster::Update(const float fElapsedTime){ lastHitTimer=std::max(0.f,lastHitTimer-fElapsedTime); lastDotTimer=std::max(0.f,lastDotTimer-fElapsedTime); iframe_timer=std::max(0.f,iframe_timer-fElapsedTime); @@ -280,6 +280,14 @@ bool Monster::Update(float fElapsedTime){ lastFacingDirectionChange+=fElapsedTime; timeSpentAlive+=fElapsedTime; + if(IsUnconscious()){ + unconsciousTimer=std::max(0.f,unconsciousTimer-fElapsedTime); + if(unconsciousTimer==0.f){ + Heal(GetMaxHealth()); + } + + } + if(IsSolid()&&FadeoutWhenStandingBehind()){ if(GetPos().y>=game->GetPlayer()->GetPos().y)solidFadeTimer=std::min(TileGroup::FADE_TIME,solidFadeTimer+game->GetElapsedTime()); else solidFadeTimer=std::max(0.f,solidFadeTimer-game->GetElapsedTime()); @@ -394,17 +402,9 @@ bool Monster::Update(float fElapsedTime){ } if(!game->TestingModeEnabled()&&CanMove())Monster::STRATEGY::RUN_STRATEGY(*this,fElapsedTime); } - if(!IsAlive()){ - deathTimer+=fElapsedTime; - if(deathTimer>3){ - return false; - } - } animation.UpdateState(internal_animState,randomFrameOffset+fElapsedTime); if(HasMountedMonster())mounted_animation.value().UpdateState(internal_mounted_animState,fElapsedTime); - randomFrameOffset=0; attackedByPlayer=false; - return true; } Direction Monster::GetFacingDirection()const{ return facingDirection; @@ -636,6 +636,8 @@ void Monster::DrawReflection(float drawRatioX,float multiplierX){ }; } + if(IsUnconscious()&&animation.HasState(MONSTER_DATA.at(name).GetDefaultDeathAnimation()))animation.ChangeState(internal_animState,GetDeathAnimationName()); + game->view.DrawPartialWarpedDecal(GetFrame().GetSourceImage()->Decal(),points,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size); game->SetDecalMode(DecalMode::NORMAL); } @@ -1144,21 +1146,21 @@ void Monster::OnDeath(){ } if(game->GetPlayer()->HasEnchant("Curse of Doom")&&accumulatedCurseOfDeathDamage>0){ #pragma region Go Boom - float radius{"Curse of Doom"_ENC["EXPLODE RANGE"]/100.f*24}; - const HurtList list{game->Hurt(pos,radius,accumulatedCurseOfDeathDamage,OnUpperLevel(),GetZ(),HurtType::MONSTER,HurtFlag::PLAYER_ABILITY)}; - for(const auto&[targetPtr,wasHit]:list){ - if(wasHit){ - std::get(targetPtr)->ProximityKnockback(pos,"Curse of Doom"_ENC["EXPLODE KNOCKBACK AMOUNT"]); - std::get(targetPtr)->Knockup("Curse of Doom"_ENC["EXPLODE KNOCKUP AMOUNT"]); + float radius{"Curse of Doom"_ENC["EXPLODE RANGE"]/100.f*24}; + const HurtList list{game->Hurt(pos,radius,accumulatedCurseOfDeathDamage,OnUpperLevel(),GetZ(),HurtType::MONSTER,HurtFlag::PLAYER_ABILITY)}; + for(const auto&[targetPtr,wasHit]:list){ + if(wasHit){ + std::get(targetPtr)->ProximityKnockback(pos,"Curse of Doom"_ENC["EXPLODE KNOCKBACK AMOUNT"]); + std::get(targetPtr)->Knockup("Curse of Doom"_ENC["EXPLODE KNOCKUP AMOUNT"]); + } } - } - float targetScale{radius/24}; - float startingScale{GetCollisionRadius()/24}; - std::unique_ptrexplodeEffect{std::make_unique(pos,ANIMATION_DATA["explosionframes.png"].GetTotalAnimationDuration()-0.2f,"explosionframes.png",OnUpperLevel(),startingScale,0.2f,vf2d{0,-6.f},WHITE,util::random(2*PI),0.f)}; - explodeEffect->scaleSpd={(targetScale-startingScale)/ANIMATION_DATA["explosionframes.png"].GetTotalAnimationDuration(),(targetScale-startingScale)/ANIMATION_DATA["explosionframes.png"].GetTotalAnimationDuration()}; - game->AddEffect(std::move(explodeEffect)); - SoundEffect::PlaySFX("Explosion",pos); + float targetScale{radius/24}; + float startingScale{GetCollisionRadius()/24}; + std::unique_ptrexplodeEffect{std::make_unique(pos,ANIMATION_DATA["explosionframes.png"].GetTotalAnimationDuration()-0.2f,"explosionframes.png",OnUpperLevel(),startingScale,0.2f,vf2d{0,-6.f},WHITE,util::random(2*PI),0.f)}; + explodeEffect->scaleSpd={(targetScale-startingScale)/ANIMATION_DATA["explosionframes.png"].GetTotalAnimationDuration(),(targetScale-startingScale)/ANIMATION_DATA["explosionframes.png"].GetTotalAnimationDuration()}; + game->AddEffect(std::move(explodeEffect)); + SoundEffect::PlaySFX("Explosion",pos); #pragma endregion } @@ -1354,7 +1356,7 @@ const bool Monster::Immovable()const{ return MONSTER_DATA.at(GetName()).Immovable(); } const bool Monster::Invulnerable()const{ - return MONSTER_DATA.at(GetName()).Invulnerable(); + return MONSTER_DATA.at(GetName()).Invulnerable()||IsUnconscious(); } const std::optionalMonster::GetLifetime()const{ return lifetime; @@ -1643,4 +1645,10 @@ void Monster::AddAddedVelocity(vf2d vel){ void Monster::MoveForward(const vf2d&moveForwardVec,const float fElapsedTime){ if(moveForwardVec.mag()==0.f)ERR("WARNING! Passed a zero length vector into Move Forward! THIS IS NOT ALLOWED!"); SetPos(pos+moveForwardVec.norm()*100.f*fElapsedTime*GetMoveSpdMult()); +} +const bool Monster::IsUnconscious()const{ + return unconsciousTimer>0.f; +} +const float Monster::UnconsciousTime()const{ + return MONSTER_DATA.at(name).UnconsciousTime(); } \ No newline at end of file diff --git a/Adventures in Lestoria/Monster.h b/Adventures in Lestoria/Monster.h index 3eff672b..4a31c6e0 100644 --- a/Adventures in Lestoria/Monster.h +++ b/Adventures in Lestoria/Monster.h @@ -91,7 +91,7 @@ public: float GetSizeMult()const; Animate2D::Frame GetFrame()const; const std::optionalGetMountedFrame()const; - bool Update(float fElapsedTime); + void Update(const float fElapsedTime); //Returns true when damage is actually dealt. Provide whether or not the attack is on the upper level or not. Monsters must be on the same level to get hit by it. (there is a death check and level check here.) //If you need to hurt multiple enemies try AiL::HurtEnemies() bool Hurt(HurtDamageInfo damageData); @@ -321,6 +321,9 @@ private: int accumulatedCurseOfDeathDamage{}; vf2d addedVel{}; std::weak_ptrattachedTarget; //A monster attached to another monster can then use this to alter behaviors based on the state of that other monster. + float unconsciousTimer{}; + const bool IsUnconscious()const; + const float UnconsciousTime()const; struct STRATEGY{ static std::string ERR; diff --git a/Adventures in Lestoria/MonsterData.cpp b/Adventures in Lestoria/MonsterData.cpp index 4c53d581..573b3104 100644 --- a/Adventures in Lestoria/MonsterData.cpp +++ b/Adventures in Lestoria/MonsterData.cpp @@ -199,6 +199,8 @@ void MonsterData::InitializeMonsterData(){ monster.collisionRadius=8*(std::min(DATA["Monsters"][MonsterName]["SheetFrameSize"].GetInt(0),DATA["Monsters"][MonsterName]["SheetFrameSize"].GetInt(1))/24.f); if(DATA["Monsters"][MonsterName].HasProperty("Collision Radius"))monster.collisionRadius=DATA["Monsters"][MonsterName]["Collision Radius"].GetReal(); if(DATA["Monsters"][MonsterName].HasProperty("ShowBossIndicator"))monster.hasArrowIndicator=DATA["Monsters"][MonsterName]["ShowBossIndicator"].GetBool(); + + if(DATA["Monsters"][MonsterName].HasProperty("Unconscious Time"))monster.totalUnconsciousTime=DATA["Monsters"][MonsterName]["Unconscious Time"].GetReal(); if(DATA["Monsters"][MonsterName].HasProperty("Rectangle Collision")){ const datafile&rectData{DATA["Monsters"][MonsterName]["Rectangle Collision"]}; @@ -475,4 +477,7 @@ const bool MonsterData::FadeoutWhenStandingBehind()const{ } const bool MonsterData::FaceTarget()const{ return !noFacing; +} +const float MonsterData::UnconsciousTime()const{ + return totalUnconsciousTime; } \ No newline at end of file diff --git a/Adventures in Lestoria/MonsterData.h b/Adventures in Lestoria/MonsterData.h index 4745104e..641ff930 100644 --- a/Adventures in Lestoria/MonsterData.h +++ b/Adventures in Lestoria/MonsterData.h @@ -108,6 +108,7 @@ public: const std::optional>&GetRectangleCollision()const; const bool FadeoutWhenStandingBehind()const; const bool FaceTarget()const; + const float UnconsciousTime()const; private: std::string name; int hp; @@ -140,4 +141,5 @@ private: float collisionRadius{}; bool hasArrowIndicator{false}; std::optional>rectCollision; + float totalUnconsciousTime{}; }; \ No newline at end of file diff --git a/Adventures in Lestoria/Parrot.cpp b/Adventures in Lestoria/Parrot.cpp index e449799a..3f75b311 100644 --- a/Adventures in Lestoria/Parrot.cpp +++ b/Adventures in Lestoria/Parrot.cpp @@ -48,8 +48,4 @@ INCLUDE_game void Monster::STRATEGY::PARROT(Monster&m,float fElapsedTime,std::string strategy){ HAWK(m,fElapsedTime,"Hawk"); - m.SetStrategyDeathFunction([](GameEvent&event,Monster&monster,const std::string&strategyName){ - - return true; - }); } \ No newline at end of file diff --git a/Adventures in Lestoria/Pirate_Captain.cpp b/Adventures in Lestoria/Pirate_Captain.cpp index 03817e5d..58b0e840 100644 --- a/Adventures in Lestoria/Pirate_Captain.cpp +++ b/Adventures in Lestoria/Pirate_Captain.cpp @@ -71,7 +71,6 @@ void Monster::STRATEGY::PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std::string switch(m.phase){ case INIT:{ m.F(A::TARGET_TIMER)=ConfigFloat("Shooting Frequency"); - m.F(A::WAIT_TIMER)=ConfigFloat(""); m.mounted_animation=Animate2D::Animation{}; for(bool firstAnimation=true;const std::string&animation:Config("Imposed Monster Animations").GetValues()){ m.mounted_animation.value().AddState(animation,ANIMATION_DATA.at(animation));