diff --git a/Adventures in Lestoria Tests/EnchantTests.cpp b/Adventures in Lestoria Tests/EnchantTests.cpp index 2799a3b2..d7327799 100644 --- a/Adventures in Lestoria Tests/EnchantTests.cpp +++ b/Adventures in Lestoria Tests/EnchantTests.cpp @@ -664,5 +664,41 @@ namespace EnchantTests game->OnUserUpdate(0.5f); Assert::AreEqual(size_t(4),BULLET_LIST.size(),L"The Triple Toss enchant should add 3 more daggers to the list."); } + TEST_METHOD(DeadlyMirageCheck){ + testKey->bHeld=true; //Force the key to be held down for testing purposes. + game->ChangePlayerClass(THIEF); + player=game->GetPlayer(); + player->CheckAndPerformAbility(player->GetAbility2(),testKeyboardInput); + Assert::AreEqual(uint8_t(0),Thief::ability2.charges,L"Deadly Dash's charges should've been used up."); + Assert::AreEqual("Thief.Ability 2.Mana Cost"_I,Thief::ability2.manaCost,L"Deadly Dash's mana cost is normal."); + Assert::AreEqual("Thief.Ability 2.Cooldown"_F,Thief::ability2.cooldown,L"Deadly Dash's cooldown should've been used up."); + Thief::ability2.charges=1; + Thief::ability2.cooldown=0.f; + player->SetState(State::NORMAL); + std::weak_ptrnullRing{Inventory::AddItem("Null Ring"s)}; + Inventory::EquipItem(nullRing,EquipSlot::RING1); + nullRing.lock()->EnchantItem("Deadly Mirage"); + player->CheckAndPerformAbility(player->GetAbility2(),testKeyboardInput); + Assert::AreEqual(uint8_t(1),Thief::ability2.charges,L"Deadly Dash should be available to use again."); + Assert::AreEqual(0.f,Thief::ability2.cooldown,L"Deadly Dash should no longer be on cooldown."); + Assert::AreEqual(0,Thief::ability2.manaCost,L"Deadly Dash's mana cost should be zero."); + Assert::AreEqual("Deadly Mirage"_ENC["REACTIVATE TIMING WINDOW"],player->GetTimer(Player::TimerType::DEADLY_MIRAGE_SECOND_CAST).RemainingTime(),L"Deadly Mirage's second cast timer should have started."); + player->SetState(State::NORMAL); + player->CheckAndPerformAbility(player->GetAbility2(),testKeyboardInput); + Assert::AreEqual(uint8_t(0),Thief::ability2.charges,L"Deadly Dash should have been used up."); + Assert::AreEqual("Thief.Ability 2.Cooldown"_F,Thief::ability2.cooldown,L"Deadly Dash should now be on cooldown"); + Assert::AreEqual("Thief.Ability 2.Mana Cost"_I,Thief::ability2.manaCost,L"Deadly Dash's mana cost is back to normal."); + Assert::AreEqual(true,player->GetTimer(Player::TimerType::DEADLY_MIRAGE_SECOND_CAST).IsDone(),L"Deadly Mirage's second cast timer should have been cancelled."); + Thief::ability2.charges=1; + Thief::ability2.cooldown=0.f; + player->RestoreMana(100); + player->SetState(State::NORMAL); + player->CheckAndPerformAbility(player->GetAbility2(),testKeyboardInput); + testGame->SetElapsedTime("Deadly Mirage"_ENC["REACTIVATE TIMING WINDOW"]+1.f); + testGame->OnUserUpdate("Deadly Mirage"_ENC["REACTIVATE TIMING WINDOW"]+1.f); + Assert::AreEqual(uint8_t(0),Thief::ability2.charges,L"Deadly Dash should no longer be useable."); + Assert::AreEqual("Thief.Ability 2.Mana Cost"_I,Thief::ability2.manaCost,L"Deadly Dash's mana cost is back to normal."); + Assert::AreEqual("Thief.Ability 2.Cooldown"_F-3.f,Thief::ability2.cooldown,L"Deadly Dash should now be on cooldown"); + } }; } \ No newline at end of file diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj b/Adventures in Lestoria/Adventures in Lestoria.vcxproj index a1283cf7..585e7a89 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj @@ -704,6 +704,7 @@ + @@ -1128,6 +1129,10 @@ + + + + diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters index 34c94e64..fa9c24b4 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters @@ -678,6 +678,9 @@ Header Files + + Source Files + @@ -1193,6 +1196,9 @@ Source Files\Effects + + Source Files + diff --git a/Adventures in Lestoria/Player.cpp b/Adventures in Lestoria/Player.cpp index 1d2e959d..a19a248b 100644 --- a/Adventures in Lestoria/Player.cpp +++ b/Adventures in Lestoria/Player.cpp @@ -362,6 +362,7 @@ void Player::Update(float fElapsedTime){ hpRecoveryTimer=std::max(0.f,hpRecoveryTimer-fElapsedTime); hp6RecoveryTimer=std::max(0.f,hp6RecoveryTimer-fElapsedTime); hp4RecoveryTimer=std::max(0.f,hp4RecoveryTimer-fElapsedTime); + RunTimers(); PerformHPRecovery(); @@ -1952,6 +1953,9 @@ const bool Player::HasEnchant(const std::string_view enchantName)const{ void Player::OnAbilityUse(const Ability&ability){ if(ability.itemAbility||ability.name==GetRightClickAbility().name)return; + + const auto UsedAbility=[&ability](const std::string_view abilityName){return ability.name==abilityName;}; + if(HasEnchant("Wizard's Soul")){ const auto ReduceCooldown=[this,&ability](Ability&a){ if(ability.name==a.name)return; @@ -1962,6 +1966,25 @@ void Player::OnAbilityUse(const Ability&ability){ ReduceCooldown(GetAbility3()); ReduceCooldown(GetAbility4()); } + + if(UsedAbility("Deadly Dash")){ + const bool IsFirstDash{Thief::ability2.manaCost!=0.f}; + if(HasEnchant("Deadly Mirage")&&IsFirstDash){ + Thief::ability2.charges++; + Thief::ability2.manaCost=0; + Thief::ability2.cooldown=0.f; + AddTimer(TimerType::DEADLY_MIRAGE_SECOND_CAST,Timer{"Deadly Mirage Second Cast","Deadly Mirage"_ENC["REACTIVATE TIMING WINDOW"],[](){ + Thief::ability2.manaCost="Thief.Ability 2.Mana Cost"_I; + Thief::ability2.cooldown="Thief.Ability 2.Cooldown"_F; + Thief::ability2.charges--; + },Timer::MANUAL}); + } + if(!IsFirstDash){ + Thief::ability2.manaCost="Thief.Ability 2.Mana Cost"_I; + Thief::ability2.cooldown="Thief.Ability 2.Cooldown"_F; + CancelTimer(TimerType::DEADLY_MIRAGE_SECOND_CAST); + } + } } Ability&Player::GetItem1(){ @@ -2022,4 +2045,25 @@ const std::vector>Player::GetAbilities(){ const bool Player::HasEnchantWithAbilityAffected(const std::string_view abilityName)const{ return enchantAbilityList.count(std::string(abilityName)); +} + +Timer&Player::AddTimer(const TimerType type,const Timer&timer){ + auto timerReturnResult{timers.insert({type,timer})}; + Timer&newTimer{(*(timerReturnResult.first)).second}; + newTimer=timer; + return newTimer; +} + +Timer&Player::GetTimer(const TimerType type){ + return timers.at(type); +} + +void Player::CancelTimer(const TimerType type){ + GetTimer(type).Cancel(); +} + +void Player::RunTimers(){ + for(auto&[type,timer]:timers){ + timer.Update(game->GetElapsedTime()); + } } \ No newline at end of file diff --git a/Adventures in Lestoria/Player.h b/Adventures in Lestoria/Player.h index 621eab41..84ccd3de 100644 --- a/Adventures in Lestoria/Player.h +++ b/Adventures in Lestoria/Player.h @@ -48,6 +48,7 @@ All rights reserved. #include "Class.h" #include "Item.h" #include "AttributableStat.h" +#include "Timer.h" #undef GetClassName @@ -106,6 +107,10 @@ class Player{ friend class ItemInfo; friend void ItemOverlay::Draw(); public: + enum class TimerType{ + DEADLY_MIRAGE_SECOND_CAST, + }; + 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 //using a new object type... Because of that we'll take the pointer reference to the old object and copy some of its properties to this new @@ -309,6 +314,9 @@ public: const int RemainingRapidFireShots()const; const std::vector>GetAbilities();//Returns player defensive, core abilities (1-4) and item abilities. const bool HasEnchantWithAbilityAffected(const std::string_view abilityName)const; //Returns whether or not the player has an enchant that affects the provided name. + Timer&AddTimer(const TimerType type,const Timer&timer); + Timer&GetTimer(const TimerType type); + void CancelTimer(const TimerType type); private: int hp="Warrior.BaseHealth"_I; int mana="Player.BaseMana"_I; @@ -391,6 +399,8 @@ private: const bool LastReserveEnchantConditionsMet()const; float poisonArrowLastParticleTimer{}; const vf2d GetAimingLocation(bool useWalkDir=false,bool invert=false); + std::unordered_maptimers; + void RunTimers(); protected: const float ATTACK_COOLDOWN="Warrior.Auto Attack.Cooldown"_F; const float MAGIC_ATTACK_COOLDOWN="Wizard.Auto Attack.Cooldown"_F; diff --git a/Adventures in Lestoria/Timer.cpp b/Adventures in Lestoria/Timer.cpp new file mode 100644 index 00000000..33d280bf --- /dev/null +++ b/Adventures in Lestoria/Timer.cpp @@ -0,0 +1,71 @@ +#pragma region License +/* +License (OLC-3) +~~~~~~~~~~~~~~~ + +Copyright 2024 Joshua Sigona + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions or derivations of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions or derivative works in binary form must reproduce the above +copyright notice. This list of conditions and the following disclaimer must be +reproduced in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may +be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +Portions of this software are copyright © 2024 The FreeType +Project (www.freetype.org). Please see LICENSE_FT.txt for more information. +All rights reserved. +*/ +#pragma endregion + +#include "Timer.h" + +Timer::Timer(const std::string timerName,const float timer,const std::functioncallback,const TimerType autoRepeat) +:timerName(timerName),callback(callback),autoRepeat(autoRepeat),originalVal(timer),timer(originalVal){} +const bool Timer::Update(const float fElapsedTime){ + if(IsDone())return false; + + timer=std::max(0.f,timer-fElapsedTime); + if(IsDone()){ + callback(); + if(autoRepeat==TimerType::REPEAT)timer=originalVal+(timer-fElapsedTime); + return false; + } + return true; +} +void Timer::Reset(){ + timer=originalVal; +} +void Timer::Reset(const float newTime){ + timer=newTime; +} +const bool Timer::IsRunning()const{ + return timer>0.f; +} +const bool Timer::IsDone()const{ + return !IsRunning(); +} +void Timer::Cancel(){ + timer=0.f; +} +const float Timer::RemainingTime()const{ + return timer; +} \ No newline at end of file diff --git a/Adventures in Lestoria/Timer.h b/Adventures in Lestoria/Timer.h new file mode 100644 index 00000000..0485498c --- /dev/null +++ b/Adventures in Lestoria/Timer.h @@ -0,0 +1,39 @@ +#pragma once +#include +#include + +class Timer{ + friend struct std::hash; +public: + enum class TimerType{ + REPEAT, + MANUAL, + }; + + using enum TimerType; +private: + std::string timerName; + float originalVal; + float timer; + TimerType autoRepeat; + std::functioncallback; +public: + Timer(const std::string timerName,const float timer,const std::functioncallback,const TimerType autoRepeat=MANUAL); + const bool Update(const float fElapsedTime); //Returns false when the timer is completed. + void Reset(); //Resets the timer to the original given time it had. + void Reset(const float newTime); //Resets the timer to the specified time. + void Cancel(); //Stops the timer such that it is no longer running. (Doesn't invoke the callback) + const bool IsRunning()const; + const bool IsDone()const; + const float RemainingTime()const; + bool operator==(const Timer&timer){return timerName==timer.timerName;}; +}; + +template <> +struct std::hash +{ + std::size_t operator()(const Timer&timer) const + { + return std::hash()(timer.timerName); + } +}; \ No newline at end of file diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index fbcaa609..25854b57 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 10952 +#define VERSION_BUILD 10966 #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 b80a6e8d..f4a32aeb 100644 Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ