Refactor ability use skills to be testable. Add in basic player damage check and ability use unit tests.
This commit is contained in:
parent
f355b01171
commit
d6d02cc986
@ -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_ptr<AiL>testGame;
|
||||
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()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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_ptr<Player>player;
|
||||
SplashScreen splash;
|
||||
public:
|
||||
|
@ -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]);
|
||||
}
|
||||
|
||||
|
@ -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::set<Input>keys;
|
||||
std::vector<Input>keyOrder; //A list of keys inserted in order, for primary key mapping.
|
||||
static safemap<std::string,InputGroup*>menuNamesToInputGroups;
|
||||
|
@ -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()<ability.manaCost&&key.Pressed()){
|
||||
notEnoughManaDisplay={ability.name,1.f};
|
||||
}
|
||||
}else
|
||||
if(key.Released()||!key.Held())ability.waitForRelease=false;
|
||||
}
|
||||
};
|
||||
CheckAndPerformAbility(rightClickAbility,Player::KEY_DEFENSIVE);
|
||||
CheckAndPerformAbility(ability,Player::KEY_ABILITY1);
|
||||
CheckAndPerformAbility(ability2,Player::KEY_ABILITY2);
|
||||
@ -766,7 +726,7 @@ bool Player::CanAct(){
|
||||
return CanAct(dummyAbility);
|
||||
}
|
||||
|
||||
bool Player::CanAct(Ability&ability){
|
||||
const bool Player::CanAct(const Ability&ability){
|
||||
return knockUpTimer==0&&!ability.waitForRelease&&(ability.canCancelCast||state!=State::CASTING)&&state!=State::ANIMATION_LOCK&&
|
||||
(GameState::STATE==GameState::states[States::GAME_RUN]
|
||||
||GameState::STATE==GameState::states[States::GAME_HUB]&&!ability.itemAbility);
|
||||
@ -823,7 +783,7 @@ bool Player::Hurt(int damage,bool onUpperLevel,float z){
|
||||
damageNumberPtr.get()->damage+=int(mod_dmg);
|
||||
damageNumberPtr.get()->pauseTime=0.4f;
|
||||
damageNumberPtr.get()->RecalculateSize();
|
||||
} else {
|
||||
}else{
|
||||
damageNumberPtr=std::make_shared<DamageNumber>(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);
|
||||
}
|
||||
@ -1601,3 +1562,40 @@ 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()<ability.manaCost&&key.Pressed()){
|
||||
notEnoughManaDisplay={ability.name,1.f};
|
||||
}
|
||||
}else
|
||||
if(key.Released()||!key.Held())ability.waitForRelease=false;
|
||||
}
|
||||
}
|
@ -69,6 +69,10 @@ namespace MoveFlag{
|
||||
};
|
||||
};
|
||||
|
||||
namespace PlayerTest{
|
||||
class PlayerTests;
|
||||
}
|
||||
|
||||
class EntityStats{
|
||||
friend class Inventory;
|
||||
ItemAttributable equipStats; //The stats after gear calculations are applied.
|
||||
@ -98,6 +102,7 @@ class Player{
|
||||
friend class State_GameRun;
|
||||
friend class Inventory;
|
||||
friend void ItemOverlay::Draw();
|
||||
friend class PlayerTests::PlayerTest;
|
||||
public:
|
||||
Player();
|
||||
//So this is rather fascinating and only exists because we have the ability to change classes which means we need to initialize a class
|
||||
@ -146,7 +151,7 @@ public:
|
||||
bool CanPathfindTo(vf2d pos,vf2d targetPos,float range=8);
|
||||
bool CanMove();
|
||||
bool CanAct();
|
||||
bool CanAct(Ability&ability);
|
||||
const bool CanAct(const Ability&ability);
|
||||
void Knockback(vf2d vel);
|
||||
void ProximityKnockback(const vf2d centerPoint,const float knockbackFactor);
|
||||
void ApplyIframes(float duration);
|
||||
@ -332,6 +337,7 @@ private:
|
||||
float lowHealthSoundPlayedTimer=0.f;
|
||||
float rangerShootAnimationTimer=0.f;
|
||||
Renderable minimapImg; //An image of the character represented on a minimap. Should be 12x12 and generally be a circle.
|
||||
void CheckAndPerformAbility(Ability&ability,InputGroup key);
|
||||
protected:
|
||||
const float ATTACK_COOLDOWN="Warrior.Auto Attack.Cooldown"_F;
|
||||
const float MAGIC_ATTACK_COOLDOWN="Wizard.Auto Attack.Cooldown"_F;
|
||||
|
@ -109,6 +109,7 @@ public:
|
||||
inline SetLoadoutItemTask():TutorialTask(){};
|
||||
private:
|
||||
virtual inline void Initialize()override final{
|
||||
if(game->TestingModeEnabled())return;
|
||||
Component<MenuComponent>(ITEM_LOADOUT,"Start Level Button")->SetGrayedOut(true);
|
||||
}
|
||||
virtual inline bool CompleteCondition()override final{
|
||||
|
@ -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
|
||||
|
@ -45,7 +45,11 @@ All rights reserved.
|
||||
#include <ft2build.h>
|
||||
#pragma comment(lib, "freetype.lib")
|
||||
#else
|
||||
#include <freetype2/ft2build.h>
|
||||
#ifdef OLC_PGE_HEADLESS
|
||||
#include <ft2build.h>
|
||||
#else
|
||||
#include <freetype2/ft2build.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
|
@ -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;
|
||||
@ -1468,6 +1473,7 @@ 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
|
||||
#ifndef OLC_GFX_HEADLESS
|
||||
typedef void CALLSTYLE locShaderSource_t(GLuint shader, GLsizei count, const GLchar** string, const GLint* length);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
} // olc namespace
|
||||
|
Loading…
x
Reference in New Issue
Block a user