Add cooldown charge system. Abilities can now only be used when they are holding charges. One charge gets restored for each full cooldown duration. The cooldown timer stops going down when maximum charges are reached. Fix item tests to use cooldown charges. Add in item checks for CDR test (Items should not be affected by CDR). Add GetPlayerAbilities function to collect and manipulate all player abilities at the same time (cleaner code structure + easier test functionality). Add helper functions util::vformat and util::wformat and to_wstring to make code using the std:: versions much more concise. Add unit test for cooldown charges system. Add helper functions to reset abilities to their original stats before enchant modifications tweak them. Added a helper function to retrieve which ability an enchant affects. Adapt ability HUD with new charge count features. Add Mountain theme to Chapter 2 maps. Multi-Multishot Enchant implemented. Release Build 10901.
Assert::AreEqual(player->GetRightClickAbility().GetCooldownTime(),player->GetRightClickAbility().cooldown,L"Right-click ability goes on cooldown like normal.");
Assert::AreEqual(player->GetRightClickAbility().GetCooldownTime(),player->GetRightClickAbility().cooldown,L"Right-click ability goes on cooldown like normal.");
@ -441,6 +442,7 @@ namespace EnchantTests
Assert::AreEqual(player->GetAbility4().GetCooldownTime(),player->GetAbility4().cooldown,L"All other abilities are unaffected by Right-click ability being used.");
Assert::AreEqual(player->GetAbility4().GetCooldownTime(),player->GetAbility4().cooldown,L"All other abilities are unaffected by Right-click ability being used.");
player->SetState(State::NORMAL);
player->SetState(State::NORMAL);
player->RestoreMana(100);
player->RestoreMana(100);
player->GetAbility1().charges=1;//Reset the cooldown so it can be used.
player->GetAbility1().cooldown=0.f;//Reset the cooldown so it can be used.
player->GetAbility1().cooldown=0.f;//Reset the cooldown so it can be used.
Assert::AreEqual(player->GetRightClickAbility().GetCooldownTime(),player->GetRightClickAbility().cooldown,L"Right-click ability remains unaffected by other abilities.");
Assert::AreEqual(player->GetRightClickAbility().GetCooldownTime(),player->GetRightClickAbility().cooldown,L"Right-click ability remains unaffected by other abilities.");
@ -450,6 +452,7 @@ namespace EnchantTests
Assert::AreEqual(player->GetAbility4().GetCooldownTime()-1.5f,player->GetAbility4().cooldown,L"All other abilities have cooldowns reduced by 1.5 seconds.");
Assert::AreEqual(player->GetAbility4().GetCooldownTime()-1.5f,player->GetAbility4().cooldown,L"All other abilities have cooldowns reduced by 1.5 seconds.");
player->SetState(State::NORMAL);
player->SetState(State::NORMAL);
player->RestoreMana(100);
player->RestoreMana(100);
player->GetAbility2().charges=1;//Reset the cooldown so it can be used.
player->GetAbility2().cooldown=0.f;//Reset the cooldown so it can be used.
player->GetAbility2().cooldown=0.f;//Reset the cooldown so it can be used.
Assert::AreEqual(player->GetRightClickAbility().GetCooldownTime(),player->GetRightClickAbility().cooldown,L"Right-click ability remains unaffected by other abilities.");
Assert::AreEqual(player->GetRightClickAbility().GetCooldownTime(),player->GetRightClickAbility().cooldown,L"Right-click ability remains unaffected by other abilities.");
@ -459,6 +462,7 @@ namespace EnchantTests
Assert::AreEqual(player->GetAbility4().GetCooldownTime()-3.f,player->GetAbility4().cooldown,L"All other abilities have cooldowns reduced by 1.5 seconds.");
Assert::AreEqual(player->GetAbility4().GetCooldownTime()-3.f,player->GetAbility4().cooldown,L"All other abilities have cooldowns reduced by 1.5 seconds.");
player->SetState(State::NORMAL);
player->SetState(State::NORMAL);
player->RestoreMana(100);
player->RestoreMana(100);
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->GetAbility3().cooldown=0.f;//Reset the cooldown so it can be used.
Assert::AreEqual(player->GetAbility4().GetCooldownTime(),player->GetAbility4().cooldown,L"All other abilities have cooldowns reduced by 1.5 seconds.");
Assert::AreEqual(player->GetAbility4().GetCooldownTime(),player->GetAbility4().cooldown,L"All other abilities have cooldowns reduced by 1.5 seconds.");
player->SetState(State::NORMAL);
player->SetState(State::NORMAL);
player->RestoreMana(100);
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->GetAbility4().cooldown=0.f;//Reset the cooldown so it can be used.
Assert::AreEqual(player->GetRightClickAbility().GetCooldownTime()-1.f,player->GetRightClickAbility().cooldown,L"Right-click ability remains unaffected by other abilities.");
Assert::AreEqual(player->GetRightClickAbility().GetCooldownTime()-1.f,player->GetRightClickAbility().cooldown,L"Right-click ability remains unaffected by other abilities.");
Assert::AreEqual("Warrior.Ability 2.Precast Time"_F,player->GetAbility2().precastInfo.castTime,L"Non-Ranger class' precast times should be unaffected with the item.");
Assert::AreEqual("Warrior.Ability 2.Precast Time"_F,player->GetAbility2().precastInfo.castTime,L"Non-Ranger class' precast times should be unaffected with the item.");
game->ChangePlayerClass(RANGER);
game->ChangePlayerClass(RANGER);
player=game->GetPlayer();
player=game->GetPlayer();
@ -583,5 +588,18 @@ namespace EnchantTests
Inventory::UnequipItem(EquipSlot::RING1);
Inventory::UnequipItem(EquipSlot::RING1);
Assert::AreEqual("Ranger.Ability 2.Precast Time"_F,player->GetAbility2().precastInfo.castTime,L"Ranger class' precast time should be back to normal.");
Assert::AreEqual("Ranger.Ability 2.Precast Time"_F,player->GetAbility2().precastInfo.castTime,L"Ranger class' precast time should be back to normal.");
}
}
TEST_METHOD(MultiMultiShotCheck){
game->ChangePlayerClass(RANGER);
player=game->GetPlayer();
Assert::AreEqual(uint8_t(1),player->GetAbility3().MAX_CHARGES,L"Player starts with 1 max charge of Multishot.");
Assert::AreEqual("Ranger.Ability 3.Cooldown"_F,player->GetAbility3().GetCooldownTime(),util::wformat("Player starts with {} seconds of cooldown on Multishot.",multishotCooldownTime).c_str());
Assert::AreEqual(newMultishotCooldownTime,player->GetAbility3().GetCooldownTime(),util::wformat("Player starts with {} seconds of cooldown on Multishot.",newMultishotCooldownTime).c_str());
Assert::AreEqual(uint8_t(i+1),a.get().charges,util::wformat("Ability {} should increment their charge count when they are completely off cooldown.",a.get().name).c_str());
if(i!=4)Assert::AreEqual(a.get().COOLDOWN_TIME,a.get().cooldown,util::wformat("Ability {} should go on cooldown again when their stack count is not maxed out.",a.get().name).c_str());
elseAssert::AreEqual(0.f,a.get().cooldown,util::wformat("Ability {} should not go on cooldown again when their stack count is maxed out.",a.get().name).c_str());
}
player->RestoreMana(100);
player->SetState(State::NORMAL);
}
}
TEST_METHOD(ChargesEquipBehaviorCheck){
testGame->ChangePlayerClass(RANGER);
player=testGame->GetPlayer();
player->GetAbility3().charges=3;
testKey->bHeld=true;//Force the key to be held down for testing purposes.
testGame->SetElapsedTime(0.25f);
testGame->OnUserUpdate(0.25f);
Assert::AreEqual(uint8_t(1),player->GetAbility3().charges,L"Back down to 1 charge without having Multi-Multishot enchant equipped.");
//If set to true, this ability instead activates immediately when a cast occurs. When the cast finishes, nothing happens instead.
//If set to true, this ability instead activates immediately when a cast occurs. When the cast finishes, nothing happens instead.
boolactionPerformedDuringCast=false;
boolactionPerformedDuringCast=false;
boolwaitForRelease=false;
boolwaitForRelease=false;
boolkeyReleaseRequiredToReactivate{false};//When the player presses an ability, they cannot use it again until they have released the key to press it down again. So this gets set to true everytime an ability activates, and set to false once the player releases that key.
//Ability action function, returns true if the ability can be casted, otherwise returns false.
//Ability action function, returns true if the ability can be casted, otherwise returns false.
// Argument 1: Player* - player pointer
// Argument 1: Player* - player pointer
// Argument 2: vf2d - The returned precast target position (if the ability needs to be aimed, otherwise {})
// Argument 2: vf2d - The returned precast target position (if the ability needs to be aimed, otherwise {})
player->upperLevel=false;//Assume player starts on lower level.
player->upperLevel=false;//Assume player starts on lower level.
player->ForceSetPos(MAP_DATA[GetCurrentLevel()].MapData.playerSpawnLocation);//Normal set pos does one axis and then the other, so this will make sure that we actually end up at the right spot and ignore collision rules.
player->ForceSetPos(MAP_DATA[GetCurrentLevel()].MapData.playerSpawnLocation);//Normal set pos does one axis and then the other, so this will make sure that we actually end up at the right spot and ignore collision rules.
Component<MenuLabel>(CHARACTER_MENU,"Level Class Display")->SetLabel(std::format("Lv{} {}",game->GetPlayer()->Level(),game->GetPlayer()->GetClassName()));
Component<MenuLabel>(CHARACTER_MENU,"Level Class Display")->SetLabel(std::format("Lv{} {}",game->GetPlayer()->Level(),game->GetPlayer()->GetClassName()));
//NOTE: We have to compare to the max cooldown time when reducing cooldowns since it's possible when we equip/unequip items that the cooldown remaining is longer than the max cooldown, so we will immediately set the cooldown to that value if necessary.
if(a.get().itemAbility)a.get().cooldown=std::min(a.get().GetCooldownTime(),a.get().cooldown-fElapsedTime);//Item abilities are not affected by CDR.
item1.cooldown-=fElapsedTime;
elseif(a.get().charges>=a.get().MAX_CHARGES)a.get().cooldown=0.f;//We got in a state where we probably unequipped something and no longer need to be on cooldown.
for(inti=int(EquipSlot::RING2);i>=int(EquipSlot::HELMET);i>>=1){//I'm inverting the order of this equip slot check because typically the player will have enchants in the ring slots, so check those earlier to terminate the loop quicker.
for(inti=int(EquipSlot::RING2);i>=int(EquipSlot::HELMET);i>>=1){//I'm inverting the order of this equip slot check because typically the player will have enchants in the ring slots, so check those earlier to terminate the loop quicker.
#pragma region Reset Abilities before Enchant Modifications
GetAbility2().precastInfo.castTime=Ranger::ability2.precastInfo.castTime;//This resets the cast time of this ability since it's possible for an enchant to modify it.
Warrior::ResetToOriginalAbilities();
Ranger::ResetToOriginalAbilities();
Wizard::ResetToOriginalAbilities();
Thief::ResetToOriginalAbilities();
Trapper::ResetToOriginalAbilities();
Witch::ResetToOriginalAbilities();
#pragma endregion
if(GetClass()&RANGER){
if(HasEnchant("Mega Charged Shot"))GetAbility2().precastInfo.castTime+="Mega Charged Shot"_ENC["CAST TIME INCREASE"];
if(HasEnchant("Mega Charged Shot"))GetAbility2().precastInfo.castTime+="Mega Charged Shot"_ENC["CAST TIME INCREASE"];
constvf2dGetFacingDirVector()const;//Returns a normalized vector based on the facing direction of the character. Ex. {0,-1} for north and {1,0} for east.
constvf2dGetFacingDirVector()const;//Returns a normalized vector based on the facing direction of the character. Ex. {0,-1} for north and {1,0} for east.
constboolPoisonArrowAutoAttackReady()const;//NOTE: Also checks to make sure we have the enchant built-in...
constboolPoisonArrowAutoAttackReady()const;//NOTE: Also checks to make sure we have the enchant built-in...
constintRemainingRapidFireShots()const;
constintRemainingRapidFireShots()const;
conststd::vector<std::reference_wrapper<Ability>>GetAbilities();//Returns player defensive, core abilities (1-4) and item abilities.
constboolHasEnchantWithAbilityAffected(conststd::string_viewabilityName)const;//Returns whether or not the player has an enchant that affects the provided name.
private:
private:
inthp="Warrior.BaseHealth"_I;
inthp="Warrior.BaseHealth"_I;
intmana="Player.BaseMana"_I;
intmana="Player.BaseMana"_I;
@ -385,6 +387,7 @@ private:
std::unordered_set<std::string>myClass{};
std::unordered_set<std::string>myClass{};
floatdaggerThrowWaitTimer{INFINITY};
floatdaggerThrowWaitTimer{INFINITY};
std::unordered_set<std::string>enchantList;
std::unordered_set<std::string>enchantList;
std::unordered_set<std::string>enchantAbilityList;//Which abilities are currently affected by our enchants.
voidOnAbilityUse(constAbility&ability);//Callback when an ability successfully is used and has gone on cooldown.
voidOnAbilityUse(constAbility&ability);//Callback when an ability successfully is used and has gone on cooldown.
//Modifies angle argument directly to turn towards said direction. rate is in radians, please multiply by fElapsedTime if you intend to use this per frame.
//Modifies angle argument directly to turn towards said direction. rate is in radians, please multiply by fElapsedTime if you intend to use this per frame.