From 6274dcefb1f2f8724a4409cf26e684b0acc436e7 Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Mon, 24 Jun 2024 12:04:06 -0500 Subject: [PATCH] Refactor ability use skills to be testable. Add in basic player damage check and ability use unit tests. --- Adventures in Lestoria Tests/PlayerTests.cpp | 71 ++++++++++++++++ Adventures in Lestoria/AdventuresInLestoria.h | 5 ++ Adventures in Lestoria/Effect.cpp | 2 + Adventures in Lestoria/Key.h | 5 ++ Adventures in Lestoria/Player.cpp | 82 +++++++++---------- Adventures in Lestoria/Player.h | 8 +- Adventures in Lestoria/Tutorial.h | 1 + Adventures in Lestoria/Version.h | 2 +- Adventures in Lestoria/olcPGEX_TTF.h | 6 +- Adventures in Lestoria/olcPixelGameEngine.h | 16 +++- 10 files changed, 149 insertions(+), 49 deletions(-) diff --git a/Adventures in Lestoria Tests/PlayerTests.cpp b/Adventures in Lestoria Tests/PlayerTests.cpp index 7ff95285..0a1da731 100644 --- a/Adventures in Lestoria Tests/PlayerTests.cpp +++ b/Adventures in Lestoria Tests/PlayerTests.cpp @@ -37,6 +37,7 @@ All rights reserved. #pragma endregion #include "CppUnitTest.h" #include "AdventuresInLestoria.h" +#include "Tutorial.h" using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace olc::utils; @@ -46,6 +47,76 @@ namespace PlayerTests TEST_CLASS(PlayerTest) { public: + std::unique_ptrtestGame; + InputGroup testKeyboardInput; + Player*player; + HWButton*testKey; + TEST_METHOD_INITIALIZE(PlayerInitialize){ + testGame.reset(new AiL()); + testGame->EnableTestingMode(); + testGame->InitializeClasses(); + testGame->InitializeDefaultKeybinds(); + Tutorial::Initialize(); + ItemAttribute::Initialize(); + testGame->InitializePlayer(); + Stats::InitializeDamageReductionTable(); + player=testGame->GetPlayer(); + //Setup key "0" as a test input + testKeyboardInput.AddKeybind(Input{InputType::KEY,0}); + testKey=&testGame->pKeyboardState[0]; + testGame->olc_UpdateKeyFocus(true); //Force the game to be "focused" for tests. Required if we want keyboard inputs to work. + } + TEST_METHOD(PlayerAlive){ + Assert::IsTrue(player->IsAlive()); + } + TEST_METHOD(PlayerTakesDamage){ + player->Hurt(30,player->OnUpperLevel(),player->GetZ()); + Assert::AreEqual(70,player->GetHealth()); + } + TEST_METHOD(PlayerBlockingTakesNoDamage){ + player->SetState(State::BLOCK); + player->Hurt(30,player->OnUpperLevel(),player->GetZ()); + Assert::AreEqual(100,player->GetHealth()); + } + TEST_METHOD(PlayerIframesPreventsDamage){ + player->ApplyIframes(0.5f); + player->Hurt(30,player->OnUpperLevel(),player->GetZ()); + Assert::AreEqual(100,player->GetHealth()); + } + TEST_METHOD(PlayerConsumesManaOnRightClickAbilityUse){ + int abilityManaCost{player->GetRightClickAbility().manaCost}; + testKey->bHeld=true; //Force the key to be held down for testing purposes. + player->CheckAndPerformAbility(player->GetRightClickAbility(),testKeyboardInput); + Assert::AreEqual(player->GetMaxMana()-abilityManaCost,player->GetMana()); + } + TEST_METHOD(PlayerConsumesManaOnAbility1Use){ + int abilityManaCost{player->GetAbility1().manaCost}; + testKey->bHeld=true; //Force the key to be held down for testing purposes. + player->CheckAndPerformAbility(player->GetAbility1(),testKeyboardInput); + Assert::AreEqual(player->GetMaxMana()-abilityManaCost,player->GetMana()); + } + TEST_METHOD(PlayerConsumesManaOnAbility2Use){ + int abilityManaCost{player->GetAbility2().manaCost}; + testKey->bHeld=true; //Force the key to be held down for testing purposes. + player->CheckAndPerformAbility(player->GetAbility2(),testKeyboardInput); + Assert::AreEqual(player->GetMaxMana()-abilityManaCost,player->GetMana()); + } + TEST_METHOD(PlayerConsumesManaOnAbility3Use){ + int abilityManaCost{player->GetAbility3().manaCost}; + testKey->bHeld=true; //Force the key to be held down for testing purposes. + player->CheckAndPerformAbility(player->GetAbility3(),testKeyboardInput); + Assert::AreEqual(player->GetMaxMana()-abilityManaCost,player->GetMana()); + } + TEST_METHOD(PlayerConsumesManaOnAbility4Use){ + int abilityManaCost{player->GetAbility4().manaCost}; + testKey->bHeld=true; //Force the key to be held down for testing purposes. + player->CheckAndPerformAbility(player->GetAbility4(),testKeyboardInput); + Assert::AreEqual(player->GetMaxMana()-abilityManaCost,player->GetMana()); + } + TEST_METHOD(WarriorBlockActivatesBlock){ + player->GetRightClickAbility().action(player,player->GetPos()); + Assert::AreEqual(int(State::BLOCK),int(player->GetState())); + } }; } diff --git a/Adventures in Lestoria/AdventuresInLestoria.h b/Adventures in Lestoria/AdventuresInLestoria.h index 819c29d0..af07379c 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.h +++ b/Adventures in Lestoria/AdventuresInLestoria.h @@ -75,6 +75,10 @@ enum class HurtType{ MONSTER=0b10, }; +namespace PlayerTests{ + class PlayerTest; +} + class AiL : public olc::PixelGameEngine { friend class GameState; @@ -83,6 +87,7 @@ class AiL : public olc::PixelGameEngine friend class sig::Animation; friend class Audio; friend class Minimap; + friend class PlayerTests::PlayerTest; std::unique_ptrplayer; SplashScreen splash; public: diff --git a/Adventures in Lestoria/Effect.cpp b/Adventures in Lestoria/Effect.cpp index 0a6c567d..de7b2f2c 100644 --- a/Adventures in Lestoria/Effect.cpp +++ b/Adventures in Lestoria/Effect.cpp @@ -45,11 +45,13 @@ INCLUDE_game Effect::Effect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,float size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending) :Effect::Effect(pos,lifetime,imgFile,upperLevel,vf2d{size,size},fadeout,spd,col,rotation,rotationSpd,additiveBlending){ + if(game->TestingModeEnabled())return; this->animation.AddState(imgFile,ANIMATION_DATA[imgFile]); } Effect::Effect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,vf2d size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending) :pos(pos),lifetime(lifetime),upperLevel(upperLevel),size(size),fadeout(fadeout),original_fadeoutTime(fadeout),spd(spd),col(col),rotation(rotation),rotationSpd(rotationSpd),additiveBlending(additiveBlending){ + if(game->TestingModeEnabled())return; this->animation.AddState(imgFile,ANIMATION_DATA[imgFile]); } diff --git a/Adventures in Lestoria/Key.h b/Adventures in Lestoria/Key.h index fdb7d2aa..608f4e1a 100644 --- a/Adventures in Lestoria/Key.h +++ b/Adventures in Lestoria/Key.h @@ -149,6 +149,10 @@ public: static uint64_t ingameControlsHandle; }; +namespace PlayerTests{ + class PlayerTest; +} + class InputGroup{ friend class AiL; friend class Menu; @@ -156,6 +160,7 @@ class InputGroup{ friend class InputListener; friend class SaveFile; friend class State_MainMenu; + friend class PlayerTests::PlayerTest; std::setkeys; std::vectorkeyOrder; //A list of keys inserted in order, for primary key mapping. static safemapmenuNamesToInputGroups; diff --git a/Adventures in Lestoria/Player.cpp b/Adventures in Lestoria/Player.cpp index bc818bb5..7a0df4bd 100644 --- a/Adventures in Lestoria/Player.cpp +++ b/Adventures in Lestoria/Player.cpp @@ -586,46 +586,6 @@ void Player::Update(float fElapsedTime){ AutoAttack(); } - auto AllowedToCast=[&](Ability&ability){return !ability.precastInfo.precastTargetingRequired&&GetState()!=State::ANIMATION_LOCK;}; - auto HasEnoughOfItem=[&](Ability&ability){ - if(!ability.itemAbility)return true; - if(&ability==&item1&&game->GetLoadoutItem(0).lock()->Amt()>0)return true; - if(&ability==&item2&&game->GetLoadoutItem(1).lock()->Amt()>0)return true; - if(&ability==&item3&&game->GetLoadoutItem(2).lock()->Amt()>0)return true; - return false; - }; - //If pressed is set to false, uses held instead. - auto CheckAndPerformAbility=[&](Ability&ability,InputGroup key){ - if(ability.name!="???"){ - if(CanAct(ability)){ - if(ability.cooldown==0&&GetMana()>=ability.manaCost){ - if(key.Held()||key.Released()&&&ability==castPrepAbility&&GetState()==State::PREP_CAST){ - #pragma region Tutorial Defensive/Use Ability Tasks - if(&ability==&rightClickAbility){ - Tutorial::GetTask(TutorialTaskName::USE_DEFENSIVE).I(A::DEFENSIVE_COUNT)++; - }else{ - Tutorial::GetTask(TutorialTaskName::USE_ABILITIES).I(A::ABILITY_COUNT)++; - } - #pragma endregion - if(AllowedToCast(ability)&&ability.action(this,{})){ - bool allowed=ability.actionPerformedDuringCast; - ability.cooldown=ability.GetCooldownTime(); - CancelCast(); - ConsumeMana(ability.manaCost); - }else - if(ability.precastInfo.precastTargetingRequired&&GetState()==State::NORMAL - &&HasEnoughOfItem(ability)){ - PrepareCast(ability); - } - } - } else - if(ability.cooldown==0&&GetMana()damage+=int(mod_dmg); damageNumberPtr.get()->pauseTime=0.4f; damageNumberPtr.get()->RecalculateSize(); - } else { + }else{ damageNumberPtr=std::make_shared(pos,int(mod_dmg),true); DAMAGENUMBER_LIST.push_back(damageNumberPtr); } @@ -851,6 +811,7 @@ void Player::AddAnimation(std::string state){ } void Player::UpdateAnimation(std::string animState,int specificClass, const float frameMult){ + if(game->TestingModeEnabled())return; if(specificClass==ANY||specificClass&GetClass()){ animation.ChangeState(internal_animState,animState,frameMult); } @@ -1600,4 +1561,41 @@ const float Player::GetHealthRatio()const{ const bool Player::IsAlive()const{ return GetHealth()>0; +} + +void Player::CheckAndPerformAbility(Ability&ability,InputGroup key){ + const bool AllowedToCast=!ability.precastInfo.precastTargetingRequired&&GetState()!=State::ANIMATION_LOCK; + const bool HasEnoughOfItem=!ability.itemAbility|| + &ability==&useItem1&&game->GetLoadoutItem(0).lock()->Amt()>0|| + &ability==&useItem2&&game->GetLoadoutItem(1).lock()->Amt()>0|| + &ability==&useItem3&&game->GetLoadoutItem(2).lock()->Amt()>0; + if(ability.name!="???"){ + if(CanAct(ability)){ + if(ability.cooldown==0&&GetMana()>=ability.manaCost){ + if(key.Held()||key.Released()&&&ability==castPrepAbility&&GetState()==State::PREP_CAST){ + #pragma region Tutorial Defensive/Use Ability Tasks + if(&ability==&GetRightClickAbility()){ + Tutorial::GetTask(TutorialTaskName::USE_DEFENSIVE).I(A::DEFENSIVE_COUNT)++; + }else{ + Tutorial::GetTask(TutorialTaskName::USE_ABILITIES).I(A::ABILITY_COUNT)++; + } + #pragma endregion + if(AllowedToCast&&ability.action(this,{})){ + bool allowed=ability.actionPerformedDuringCast; + ability.cooldown=ability.GetCooldownTime(); + CancelCast(); + ConsumeMana(ability.manaCost); + }else + if(ability.precastInfo.precastTargetingRequired&&GetState()==State::NORMAL + &&HasEnoughOfItem){ + PrepareCast(ability); + } + } + } else + if(ability.cooldown==0&&GetMana()TestingModeEnabled())return; Component(ITEM_LOADOUT,"Start Level Button")->SetGrayedOut(true); } virtual inline bool CompleteCondition()override final{ diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index d16b6e5b..3d416920 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 9798 +#define VERSION_BUILD 9814 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Adventures in Lestoria/olcPGEX_TTF.h b/Adventures in Lestoria/olcPGEX_TTF.h index 7da675ef..d73f2e45 100644 --- a/Adventures in Lestoria/olcPGEX_TTF.h +++ b/Adventures in Lestoria/olcPGEX_TTF.h @@ -45,7 +45,11 @@ All rights reserved. #include #pragma comment(lib, "freetype.lib") #else -#include +#ifdef OLC_PGE_HEADLESS + #include +#else + #include +#endif #endif #include diff --git a/Adventures in Lestoria/olcPixelGameEngine.h b/Adventures in Lestoria/olcPixelGameEngine.h index cd470915..7d9df8fd 100644 --- a/Adventures in Lestoria/olcPixelGameEngine.h +++ b/Adventures in Lestoria/olcPixelGameEngine.h @@ -572,6 +572,11 @@ namespace X11 #endif #pragma endregion + +namespace PlayerTests{ + class PlayerTest; +} + // O------------------------------------------------------------------------------O // | olcPixelGameEngine INTERFACE DECLARATION | // O------------------------------------------------------------------------------O @@ -626,7 +631,6 @@ namespace olc bool bHeld = false; // Set true for all frames between pressed and released events }; - #define OLC_IGNORE_VEC2D // O------------------------------------------------------------------------------O @@ -937,6 +941,7 @@ namespace olc class PixelGameEngine { friend class ViewPort; + friend class PlayerTests::PlayerTest; struct StringDecalData{ char c; vf2d sourcePos; @@ -1467,7 +1472,8 @@ namespace olc { typedef char GLchar; typedef ptrdiff_t GLsizeiptr; - + +#ifndef OLC_GFX_HEADLESS typedef GLuint CALLSTYLE locCreateShader_t(GLenum type); typedef GLuint CALLSTYLE locCreateProgram_t(void); typedef void CALLSTYLE locDeleteShader_t(GLuint shader); @@ -1497,7 +1503,7 @@ namespace olc typedef void CALLSTYLE locFrameBufferTexture2D_t(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); typedef void CALLSTYLE locDrawBuffers_t(GLsizei n, const GLenum* bufs); typedef void CALLSTYLE locBlendFuncSeparate_t(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); - +#endif #if defined(OLC_PLATFORM_WINAPI) typedef void __stdcall locSwapInterval_t(GLsizei n); #endif @@ -1510,7 +1516,9 @@ namespace olc typedef void CALLSTYLE locShaderSource_t(GLuint shader, GLsizei count, const GLchar* const* string, const GLint* length); typedef EGLBoolean(locSwapInterval_t)(EGLDisplay display, EGLint interval); #else - typedef void CALLSTYLE locShaderSource_t(GLuint shader, GLsizei count, const GLchar** string, const GLint* length); + #ifndef OLC_GFX_HEADLESS + typedef void CALLSTYLE locShaderSource_t(GLuint shader, GLsizei count, const GLchar** string, const GLint* length); + #endif #endif } // olc namespace