Implemented Shield mechanic into the game. Fix Special Mark Enchant test and Wizard's Soul Enchant test. Release Build 11062.

pull/65/head
sigonasr2 5 months ago
parent f866a4598d
commit d224740fd4
  1. 21
      Adventures in Lestoria Tests/EnchantTests.cpp
  2. 46
      Adventures in Lestoria Tests/PlayerTests.cpp
  3. 19
      Adventures in Lestoria/AdventuresInLestoria.cpp
  4. 2
      Adventures in Lestoria/AdventuresInLestoria.h
  5. 3
      Adventures in Lestoria/Monster.cpp
  6. 51
      Adventures in Lestoria/Player.cpp
  7. 10
      Adventures in Lestoria/Player.h
  8. 1
      Adventures in Lestoria/PlayerTimerType.h
  9. 2
      Adventures in Lestoria/Version.h
  10. 21
      Adventures in Lestoria/assets/config/Interface.txt
  11. 2
      Adventures in Lestoria/assets/config/gfx/gfx.txt
  12. BIN
      Adventures in Lestoria/assets/gamepack.pak
  13. BIN
      Adventures in Lestoria/assets/heart_outline.png
  14. BIN
      Adventures in Lestoria/assets/mana_outline.png
  15. BIN
      Adventures in Lestoria/assets/shield_heart.png
  16. BIN
      x64/Release/Adventures in Lestoria.exe

@ -471,22 +471,20 @@ namespace EnchantTests
player->GetAbility3().charges=1; //Reset the cooldown so it can be used.
player->GetAbility3().cooldown=0.f; //Reset the cooldown so it can be used.
player->CheckAndPerformAbility(player->GetAbility3(),testKeyboardInput);
game->SetElapsedTime(1.f);
game->OnUserUpdate(1.f); //It's a cast, so wait one second as the ability gets used. This also reduces cooldowns by a second...
Assert::AreEqual(player->GetRightClickAbility().GetCooldownTime()-1.f,player->GetRightClickAbility().cooldown,L"Right-click ability remains unaffected by other abilities.");
Assert::AreEqual(player->GetAbility1().GetCooldownTime()-4.f,player->GetAbility1().cooldown,L"All other abilities have cooldowns reduced by 1.5 seconds.");
Assert::AreEqual(player->GetAbility2().GetCooldownTime()-2.5f,player->GetAbility2().cooldown,L"All other abilities have cooldowns reduced by 1.5 seconds.");
Assert::AreEqual(player->GetAbility3().GetCooldownTime()-1.f,player->GetAbility3().cooldown,L"Same ability used should not be affected.");
Assert::AreEqual(player->GetAbility4().GetCooldownTime(),player->GetAbility4().cooldown,L"All other abilities have cooldowns reduced by 1.5 seconds.");
Assert::AreEqual(player->GetRightClickAbility().GetCooldownTime(),player->GetRightClickAbility().cooldown,L"Right-click ability remains unaffected by other abilities.");
Assert::AreEqual(player->GetAbility1().GetCooldownTime()-3.f,player->GetAbility1().cooldown,L"All other abilities have cooldowns reduced by 1.5 seconds.");
Assert::AreEqual(player->GetAbility2().GetCooldownTime()-1.5f,player->GetAbility2().cooldown,L"All other abilities have cooldowns reduced by 1.5 seconds.");
Assert::AreEqual(player->GetAbility3().GetCooldownTime(),player->GetAbility3().cooldown,L"Same ability used should not be affected.");
Assert::AreEqual(player->GetAbility4().GetCooldownTime()-4.5f,player->GetAbility4().cooldown,L"All other abilities have cooldowns reduced by 1.5 seconds.");
player->SetState(State::NORMAL);
player->RestoreMana(100);
player->GetAbility4().charges=1; //Reset the cooldown so it can be used.
player->GetAbility4().cooldown=0.f; //Reset the cooldown so it can be used.
player->CheckAndPerformAbility(player->GetAbility4(),testKeyboardInput);
Assert::AreEqual(player->GetRightClickAbility().GetCooldownTime()-1.f,player->GetRightClickAbility().cooldown,L"Right-click ability remains unaffected by other abilities.");
Assert::AreEqual(player->GetAbility1().GetCooldownTime()-4.f,player->GetAbility1().cooldown,L"Ability 4 isn't setup for anything and returns as unused, causing cooldowns to remain the same.");
Assert::AreEqual(player->GetAbility2().GetCooldownTime()-2.5f,player->GetAbility2().cooldown,L"Ability 4 isn't setup for anything and returns as unused, causing cooldowns to remain the same.");
Assert::AreEqual(player->GetAbility3().GetCooldownTime()-1.f,player->GetAbility3().cooldown,L"Ability 4 isn't setup for anything and returns as unused, causing cooldowns to remain the same.");
Assert::AreEqual(player->GetRightClickAbility().GetCooldownTime(),player->GetRightClickAbility().cooldown,L"Right-click ability remains unaffected by other abilities.");
Assert::AreEqual(player->GetAbility1().GetCooldownTime()-3.f,player->GetAbility1().cooldown,L"Ability 4 isn't setup for anything and returns as unused, causing cooldowns to remain the same.");
Assert::AreEqual(player->GetAbility2().GetCooldownTime()-1.5f,player->GetAbility2().cooldown,L"Ability 4 isn't setup for anything and returns as unused, causing cooldowns to remain the same.");
Assert::AreEqual(player->GetAbility3().GetCooldownTime(),player->GetAbility3().cooldown,L"Ability 4 isn't setup for anything and returns as unused, causing cooldowns to remain the same.");
Assert::AreEqual(player->GetAbility4().GetCooldownTime(),player->GetAbility4().cooldown,L"Ability 4 isn't setup for anything and returns as unused, causing cooldowns to remain the same.");
}
TEST_METHOD(LastReserveCheck){
@ -965,6 +963,7 @@ namespace EnchantTests
TEST_METHOD(OpportunityShotEnchantCheck){
game->ChangePlayerClass(TRAPPER);
player=game->GetPlayer();
player->OnLevelStart();
std::weak_ptr<Item>nullRing{Inventory::AddItem("Null Ring"s)};
Inventory::EquipItem(nullRing,EquipSlot::RING1);
nullRing.lock()->EnchantItem("Opportunity Shot");

@ -750,5 +750,51 @@ namespace PlayerTests
testGame->OnUserUpdate(0.f);
Assert::AreEqual(player->GetAbility3().GetCooldownTime(),player->GetAbility3().cooldown,L"Cooldown now matches the new reduced amount.");
}
TEST_METHOD(PlayerGetShieldCheck){
player=testGame->GetPlayer();
Assert::AreEqual(0U,player->GetShield(),L"Player should have no shields at first.");
}
TEST_METHOD(PlayerAddShieldCheck){
player=testGame->GetPlayer();
player->AddShield(60U,5.f,PlayerTimerType::ADVANCED_SHIELD_TIMER);
Assert::AreEqual(60U,player->GetShield(),L"Player has 60 shield points.");
testGame->SetElapsedTime(5.f);
testGame->OnUserUpdate(5.f);
Assert::AreEqual(0U,player->GetShield(),L"Player should lose the shield after 5 seconds.");
}
TEST_METHOD(PlayerMultiShieldCheck){
player=testGame->GetPlayer();
player->AddShield(60U,5.f,PlayerTimerType::ADVANCED_SHIELD_TIMER);
player->AddShield(100U,2.f,PlayerTimerType::PLAYER_OUTLINE_TIMER);
Assert::AreEqual(160U,player->GetShield(),L"Player has 160 shield points.");
testGame->SetElapsedTime(2.f);
testGame->OnUserUpdate(2.f);
Assert::AreEqual(60U,player->GetShield(),L"Player has 60 shield points.");
testGame->SetElapsedTime(3.f);
testGame->OnUserUpdate(3.f);
Assert::AreEqual(0U,player->GetShield(),L"Player should lose the shield after 5 seconds.");
}
TEST_METHOD(PlayerSubtractShieldCheck){
player=testGame->GetPlayer();
player->AddShield(60U,5.f,PlayerTimerType::ADVANCED_SHIELD_TIMER);
Assert::AreEqual(60U,player->GetShield(),L"Player has 60 shield points.");
player->SubtractShield(40U);
Assert::AreEqual(20U,player->GetShield(),L"Player has 20 shield points.");
player->SubtractShield(200U);
Assert::AreEqual(0U,player->GetShield(),L"Player should have 0 shield points.");
}
TEST_METHOD(PlayerDamageShieldCheck){
player=testGame->GetPlayer();
player->AddShield(60U,5.f,PlayerTimerType::ADVANCED_SHIELD_TIMER);
Assert::AreEqual(60U,player->GetShield(),L"Player has 60 shield points.");
player->Hurt(20U,player->OnUpperLevel(),player->GetZ());
Assert::AreEqual(40U,player->GetShield(),L"Player has 40 shield points.");
Assert::AreEqual(player->GetMaxHealth(),player->GetHealth(),L"Player should still be full health.");
player->Hurt(200U,player->OnUpperLevel(),player->GetZ());
Assert::AreEqual(0U,player->GetShield(),L"Player has 0 shield points.");
Assert::AreEqual(player->GetMaxHealth(),player->GetHealth(),L"Player should still be full health.");
player->Hurt(10U,player->OnUpperLevel(),player->GetZ());
Assert::AreEqual(player->GetMaxHealth()-10,player->GetHealth(),L"Player now takes damage with 0 shield.");
}
};
}

@ -300,6 +300,7 @@ bool AiL::OnUserCreate(){
InitializePlayerLevelCap();
healthCounter.Initialize(&player->hp,"Interface.HUD Health Tick Rate"_F,"Interface.HUD Health Display Color"_Pixel,"Interface.HUD Heal Damage Color"_Pixel,"Interface.HUD Take Damage Color"_Pixel,"Interface.HUD Health Change Time"_F);
shieldCounter.Initialize(&playerShieldDisplayAmt,"Interface.HUD Shield Tick Rate"_F,"Interface.HUD Shield Display Color"_Pixel,"Interface.HUD Gain Shield Color"_Pixel,"Interface.HUD Lose Shield Color"_Pixel,"Interface.HUD Shield Change Time"_F);
manaCounter.Initialize(&player->mana,"Interface.HUD Mana Tick Rate"_F,"Interface.HUD Mana Display Color"_Pixel,"Interface.HUD Restore Mana Color"_Pixel,"Interface.HUD Reduce Mana Color"_Pixel,"Interface.HUD Mana Change Time"_F);
Monster::InitializeStrategies();
@ -1926,6 +1927,7 @@ Player*const AiL::GetPlayer()const{
void AiL::RenderHud(){
if(!displayHud)return;
healthCounter.Update();
shieldCounter.Update();
manaCounter.Update();
auto RenderAimingCursor=[&](){
@ -2023,12 +2025,15 @@ void AiL::RenderHud(){
}
DrawDecal({2,2},GFX["heart_outline.png"].Decal(),{1.f,1.f},healthOutlineCol);
DrawDecal({2,2},GFX["heart.png"].Decal());
DrawDecal({2,20},GFX["mana.png"].Decal());
std::string text=player->GetHealth()>0?std::to_string(healthCounter.GetDisplayValue()):"X";
const Decal*heartImg{GFX["heart.png"].Decal()};
if(player->GetShield()>0)heartImg=GFX["shield_heart.png"].Decal();
DrawPartialDecal({2,2+(15-15*player->GetHealthRatio())},const_cast<Decal*>(heartImg),{},{17,15*player->GetHealthRatio()});
DrawDecal({2,20},GFX["mana_outline.png"].Decal());
DrawPartialDecal({2,20+(15-15*player->GetManaRatio())},GFX["mana.png"].Decal(),{},{17,15*player->GetManaRatio()});
std::string text=player->GetHealth()>0?std::to_string(healthCounter.GetDisplayValue()+shieldCounter.GetDisplayValue()):"X";
std::string text_mana=std::to_string(manaCounter.GetDisplayValue());
DrawShadowStringPropDecal({20,3},text,healthCounter.GetDisplayColor(),healthOutlineCol,{2,2},{1.8f,1.8f},INFINITE);
DrawShadowStringPropDecal({20,3},text,player->GetShield()>0?shieldCounter.GetDisplayColor():healthCounter.GetDisplayColor(),healthOutlineCol,{2,2},{1.8f,1.8f},INFINITE);
DrawShadowStringPropDecal({24,23},text_mana,manaCounter.GetDisplayColor(),BLACK,{1.5f,1.5f},{1.45f,1.45f},INFINITE);
#pragma region Show Max Health/Max Mana
@ -2915,9 +2920,11 @@ void AiL::ChangePlayerClass(Class cl){
uint8_t levelCap=player->levelCap;
uint32_t totalXPEarned=player->totalXPEarned;
uint32_t currentLevelXP=player->currentLevelXP;
std::vector<std::pair<PlayerTimerType,Player::ShieldAmount>>previousShield=player->shield;
std::vector<std::weak_ptr<MenuComponent>>moneyListeners=Player::moneyListeners;
EntityStats previousStats=player->stats;
size_t cooldownSoundInstance=player->cooldownSoundInstance;
std::unordered_map<PlayerTimerType,Timer>oldTimers=player->timers;
switch(cl){
case WARRIOR:{
player.reset(NEW Warrior(player.get()));
@ -2967,6 +2974,8 @@ void AiL::ChangePlayerClass(Class cl){
GetPlayer()->InitializeMinimapImage();
player->RecalculateEquipStats();
player->OnLevelStart();
player->timers=oldTimers;
player->shield=previousShield;
}
void AiL::InitializeClasses(){
@ -4409,6 +4418,8 @@ void AiL::GlobalGameUpdates(){
lastMousePos=GetMousePos();
}else lastMouseMovement+=GetElapsedTime();
playerShieldDisplayAmt=player->GetShield();
vignetteDisplayTime=std::max(0.f,vignetteDisplayTime-GetElapsedTime());
if(Audio::Engine().IsPlaying(GetPlayer()->cooldownSoundInstance)){

@ -196,6 +196,8 @@ private:
bool disableFadeIn=false;
DynamicCounter healthCounter;
DynamicCounter manaCounter;
DynamicCounter shieldCounter;
int playerShieldDisplayAmt{};
Pixel worldColor=WHITE;
std::function<Pixel(vi2d)>worldColorFunc=[](vi2d pos){return WHITE;};
std::map<std::string,std::vector<::ZoneData>>ZONE_LIST;

@ -267,6 +267,7 @@ bool Monster::Update(float fElapsedTime){
lastHitPlayer=std::max(0.f,lastHitPlayer-fElapsedTime);
lastPathfindingCooldown=std::max(0.f,lastPathfindingCooldown-fElapsedTime);
markApplicationTimer=std::max(0.f,markApplicationTimer-fElapsedTime);
specialMarkApplicationTimer=std::max(0.f,specialMarkApplicationTimer-fElapsedTime);
lastFacingDirectionChange+=fElapsedTime;
timeSpentAlive+=fElapsedTime;
@ -1426,7 +1427,7 @@ void Monster::ApplySpecialMark(float time,uint8_t stackCount){
}else{
game->AddToSpecialMarkedTargetList({GetWeakPointer(),stackCount,markDuration});
}
markApplicationTimer=0.5f;
specialMarkApplicationTimer=0.5f;
}
std::optional<std::weak_ptr<Monster>>Monster::GetNearestMonster(const vf2d point,const float maxDistance,const bool onUpperLevel,const float z){

@ -121,6 +121,7 @@ void Player::Initialize(){
}
afterImage.Decal()->Update();
playerOutline.Create(24,24);
shield.reserve(SHIELD_CAPACITY);
}
void Player::OnLevelStart(){
@ -928,9 +929,6 @@ bool Player::Hurt(int damage,bool onUpperLevel,float z,const TrueDamageFlag dama
mod_dmg*=GetDamageAmplificationMult();
mod_dmg=std::ceil(mod_dmg);
if(PlayHitSoundEffect)SoundEffect::PlaySFX("Player Hit",SoundEffect::CENTERED);
if(Menu::IsMenuOpen()&&mod_dmg>0)Menu::CloseAllMenus();
@ -945,7 +943,16 @@ bool Player::Hurt(int damage,bool onUpperLevel,float z,const TrueDamageFlag dama
}
if(tookLethalDamage&&survivedHitDueToDefiance)ApplyIframes("Death Defiance"_ENC["LETHAL DAMAGE SURVIVE IFRAME TIME"]);
else hp=std::max(0,hp-int(mod_dmg));
else{
if(GetShield()>0){
if(PlayHitSoundEffect)SoundEffect::PlaySFX("Warrior Block Hit",SoundEffect::CENTERED);
SubtractShield(mod_dmg);
}
else{
if(PlayHitSoundEffect)SoundEffect::PlaySFX("Player Hit",SoundEffect::CENTERED);
hp=std::max(0,hp-int(mod_dmg));
}
}
if(!IsAlive()&&GameState::STATE!=GameState::states[States::DEATH])GameState::ChangeState(States::DEATH);
@ -2146,4 +2153,40 @@ void Player::_ForceCastSpell(Ability&ability){
PrepareCast(ability);
CastSpell(ability);
}
}
uint32_t Player::GetShield()const{
return std::accumulate(shield.begin(),shield.end(),0U,[](const uint32_t shieldTotal,const std::pair<PlayerTimerType,ShieldAmount>&shieldData){return shieldTotal+shieldData.second;});
}
//NOTE: Shields of the same type will override each other! Create a timer type for each unique shield!
void Player::AddShield(const ShieldAmount shieldAmt,const float shieldTimer, const PlayerTimerType shieldType){
if(HasTimer(shieldType)){
auto shieldIt{std::find_if(shield.begin(),shield.end(),[&shieldType](const std::pair<PlayerTimerType,ShieldAmount>&shieldData){return shieldData.first==shieldType;})};
if(shieldIt!=shield.end()){
std::pair<PlayerTimerType,ShieldAmount>shieldData{*shieldIt};
shieldData.second=std::max(shieldData.second,shieldAmt);
}else ERR(std::format("WARNING! The shield type {} does not have a corresponding entry! All shields when generated should have made one! THIS SHOULD NOT BE HAPPENING!",int(shieldType)));
}else{
std::pair<PlayerTimerType,ShieldAmount>&newShield{shield.emplace_back(std::pair<PlayerTimerType,ShieldAmount>{shieldType,shieldAmt})};
if(shield.capacity()>SHIELD_CAPACITY)ERR(std::format("WARNING! Shield capacity limit has been reached! If you need more than {} shields, please expand the SHIELD_CAPACITY property of player!",SHIELD_CAPACITY));
AddTimer(shieldType,Timer{std::format("Shield Type {}",int(shieldType)),shieldTimer,[newShield,this](){std::erase_if(shield,[&newShield](const std::pair<PlayerTimerType,ShieldAmount>&shieldData){return shieldData.first==newShield.first;});}});
}
}
Player::ShieldAmount Player::SubtractShield(const ShieldAmount shieldDamage){
ShieldAmount leftoverShieldDamage{shieldDamage};
std::for_each(shield.begin(),shield.end(),[&leftoverShieldDamage](std::pair<PlayerTimerType,ShieldAmount>&shieldData){
if(leftoverShieldDamage==0U)return;
ShieldAmount currentShieldDamage{leftoverShieldDamage};
if(shieldData.second<leftoverShieldDamage){
leftoverShieldDamage-=shieldData.second;
shieldData.second=0U;
}else{
shieldData.second-=leftoverShieldDamage;
leftoverShieldDamage=0U;
}
});
return leftoverShieldDamage;
}
const float Player::GetManaRatio()const{
return GetMana()/float(GetMaxMana());
}

@ -319,7 +319,15 @@ public:
void CancelTimer(const PlayerTimerType type);
void ResetTimers();
void _ForceCastSpell(Ability&ability);
using ShieldAmount=uint32_t;
ShieldAmount GetShield()const;
//NOTE: Shields of the same type will override each other! Create a timer type for each unique shield!
void AddShield(const ShieldAmount shieldAmt,const float shieldTimer, const PlayerTimerType shieldType);
ShieldAmount SubtractShield(const ShieldAmount shieldDamage); //Returns how much damage was not absorbed by shields.
const float GetManaRatio()const;
void OnLevelStart();
private:
const int SHIELD_CAPACITY{32};
int hp="Warrior.BaseHealth"_I;
int mana="Player.BaseMana"_I;
float hpGrowthRate="Warrior.HealthGrowthRate"_F;
@ -407,7 +415,7 @@ private:
Renderable playerOutline;
void UpdatePlayerOutline();
void OnBuffAdd(Buff&newBuff);
void OnLevelStart();
std::vector<std::pair<PlayerTimerType,ShieldAmount>>shield;
protected:
const float ATTACK_COOLDOWN="Warrior.Auto Attack.Cooldown"_F;
const float MAGIC_ATTACK_COOLDOWN="Wizard.Auto Attack.Cooldown"_F;

@ -42,4 +42,5 @@ enum class PlayerTimerType{
ADRENALINE_STIM,
PLAYER_OUTLINE_TIMER,
OPPORTUNITY_SHOT_RANDOM_SPECIAL_MARK,
ADVANCED_SHIELD_TIMER,
};

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 2
#define VERSION_PATCH 3
#define VERSION_BUILD 11051
#define VERSION_BUILD 11062
#define stringify(a) stringify_(a)
#define stringify_(a) #a

@ -39,7 +39,7 @@ Interface
# The color the HUD changes when we take damage.
HUD Take Damage Color = 128,0,0,255
# The color the HUD changes when we take damage.
# The color the HUD changes when we heal damage.
HUD Heal Damage Color = 0,192,0,255
# The amount of time it takes for the health to go from a damaging color back to its original color.
@ -51,10 +51,10 @@ Interface
# The original mana display color.
HUD Mana Display Color = 192,192,255,255
# The color the HUD changes when we take damage.
# The color the HUD changes when we lose mana.
HUD Reduce Mana Color = 192,192,255,255
# The color the HUD changes when we take damage.
# The color the HUD changes when we gain mana.
HUD Restore Mana Color = 192,192,255,255
# The amount of time it takes for the health to go from a damaging color back to its original color.
@ -65,4 +65,19 @@ Interface
# How long the timer text rises up
HUD Level Up Timer = 0.2s
# How long between each reduction/addition of shield.
HUD Shield Tick Rate = 0.01s
# The original shield display color.
HUD Shield Display Color = 104,215,239,255
# The color the HUD changes when we gain shield.
HUD Gain Shield Color = 104,215,239,255
# The color the HUD changes when we lose shield.
HUD Lose Shield Color = 65,65,190,255
# The amount of time it takes for the shield to go from a damaging color back to its original color.
HUD Shield Change Time = 0.4s
}

@ -21,6 +21,7 @@ Images
GFX_FireRing4 = fire_ring4.png
GFX_Heart = heart.png
GFX_Heart_Outline = heart_outline.png
GFX_ShieldHeart = shield_heart.png
GFX_Laser = laser.png
GFX_LightningBolt = lightning_bolt.png
GFX_LightningBoltParticle1 = lightning_bolt_part1.png
@ -29,6 +30,7 @@ Images
GFX_LightningBoltParticle4 = lightning_bolt_part4.png
GFX_LightningSplash = lightning_splash_effect.png
GFX_Mana = mana.png
GFX_ManaOutline = mana_outline.png
GFX_Meteor = meteor.png
GFX_RangeIndicator = range_indicator.png
GFX_Ranger_Sheet = nico-ranger.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 654 B

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Loading…
Cancel
Save