Lethal Tempo unique enchant implemented. Added helper functions for retrieving, writing, and reading buffs easily. Added unit tests for new enchant. 124/124 tests passing. Release Build 10635.

mac-build
sigonasr2 4 months ago
parent cd986d16fa
commit f84787971d
  1. 20
      Adventures in Lestoria Tests/EnchantTests.cpp
  2. 2
      Adventures in Lestoria/Buff.h
  3. 8
      Adventures in Lestoria/ItemEnchant.cpp
  4. 1
      Adventures in Lestoria/ItemEnchant.h
  5. 20
      Adventures in Lestoria/Monster.cpp
  6. 2
      Adventures in Lestoria/Monster.h
  7. 21
      Adventures in Lestoria/Player.cpp
  8. 6
      Adventures in Lestoria/Player.h
  9. 2
      Adventures in Lestoria/Version.h
  10. 4
      Adventures in Lestoria/config.h
  11. BIN
      x64/Release/Adventures in Lestoria.exe

@ -209,5 +209,25 @@ namespace EnchantTests
Test::InRange(player->GetCritDmgPct(),{53.0_Pct,57.0_Pct},L"Crit Damage not in expected range."); Test::InRange(player->GetCritDmgPct(),{53.0_Pct,57.0_Pct},L"Crit Damage not in expected range.");
} }
} }
TEST_METHOD(LethalTempoCheck){
MonsterData testMonsterData{"TestName","Test Monster",30,10,5,{MonsterDropData{"Health Potion",100.f,1,1}},200.f};
MONSTER_DATA["TestName"]=testMonsterData;
Monster testMonster{{},MONSTER_DATA["TestName"]};
testMonster.Hurt(0,testMonster.OnUpperLevel(),testMonster.GetZ());
Assert::AreEqual(size_t(0),player->GetBuffs(BuffType::LETHAL_TEMPO).size(),L"Lethal Tempo does not stack up without the enchant.");
testMonster.Hurt(0,testMonster.OnUpperLevel(),testMonster.GetZ(),HurtFlag::PLAYER_ABILITY);
Assert::AreEqual(size_t(0),player->GetBuffs(BuffType::LETHAL_TEMPO).size(),L"Lethal Tempo does not stack up without the enchant.");
std::weak_ptr<Item>nullRing{Inventory::AddItem("Null Ring"s)};
Inventory::EquipItem(nullRing,EquipSlot::RING1);
nullRing.lock()->EnchantItem("Lethal Tempo");
testMonster.Hurt(0,testMonster.OnUpperLevel(),testMonster.GetZ(),HurtFlag::PLAYER_ABILITY);
Assert::AreEqual(size_t(1),player->GetBuffs(BuffType::LETHAL_TEMPO).size(),L"Lethal Tempo buff is active after attacking with the enchant..");
Assert::AreEqual(0.0175f,player->GetAttackRecoveryRateReduction(),L"Lethal Tempo buff reduced attack Recovery Rate by 0.0175 (5% of 0.35).");
for(int i:std::ranges::iota_view(0,10)){
testMonster.Hurt(0,testMonster.OnUpperLevel(),testMonster.GetZ(),HurtFlag::PLAYER_ABILITY);
}
Assert::AreEqual(0.0875f,player->GetAttackRecoveryRateReduction(),L"Lethal Tempo buff should cap at 5 stacks.");
Assert::AreEqual(size_t(1),player->GetBuffs(BuffType::LETHAL_TEMPO).size(),L"Lethal Tempo buff is active.");
}
}; };
} }

@ -58,6 +58,7 @@ enum BuffType{
GLOW_PURPLE, GLOW_PURPLE,
COLOR_MOD, COLOR_MOD,
DAMAGE_AMPLIFICATION, //Multiplies all incoming damage by this amount. DAMAGE_AMPLIFICATION, //Multiplies all incoming damage by this amount.
LETHAL_TEMPO,
}; };
enum class BuffRestorationType{ enum class BuffRestorationType{
ONE_OFF, ONE_OFF,
@ -78,7 +79,6 @@ namespace BuffOverTimeType{
class AiL; class AiL;
struct Buff{ struct Buff{
using PlayerBuffExpireCallbackFunction=std::function<void(Player*attachedTarget,Buff&b)>; using PlayerBuffExpireCallbackFunction=std::function<void(Player*attachedTarget,Buff&b)>;
using MonsterBuffExpireCallbackFunction=std::function<void(std::weak_ptr<Monster>attachedTarget,Buff&b)>; using MonsterBuffExpireCallbackFunction=std::function<void(std::weak_ptr<Monster>attachedTarget,Buff&b)>;

@ -53,6 +53,14 @@ const Pixel ItemEnchantInfo::enchantAttributeCol{0x00DFE2};
std::unordered_map<std::string,ItemEnchantInfo>ItemEnchantInfo::ENCHANT_LIST; std::unordered_map<std::string,ItemEnchantInfo>ItemEnchantInfo::ENCHANT_LIST;
std::unordered_map<ItemEnchantInfo::ItemEnchantCategory,ItemEnchantInfo::ItemEnchantCategoryData>ItemEnchantInfo::ENCHANT_CATEGORIES; std::unordered_map<ItemEnchantInfo::ItemEnchantCategory,ItemEnchantInfo::ItemEnchantCategoryData>ItemEnchantInfo::ENCHANT_CATEGORIES;
const ItemEnchantInfo&operator ""_ENC(const char*key,std::size_t len){
return ItemEnchantInfo::GetEnchant(std::string(key));
}
const float ItemEnchantInfo::operator[](const std::string& name)const{
return config.at(name);
}
void ItemEnchantInfo::Initialize(){ void ItemEnchantInfo::Initialize(){
ENCHANT_LIST.clear(); ENCHANT_LIST.clear();
ENCHANT_CATEGORIES.clear(); ENCHANT_CATEGORIES.clear();

@ -74,6 +74,7 @@ public:
ABILITY_2, ABILITY_2,
ABILITY_3, ABILITY_3,
}; };
const float operator[](const std::string& name)const;
private: private:
class ItemEnchantCategoryData{ class ItemEnchantCategoryData{
friend class ItemEnchantInfo; friend class ItemEnchantInfo;

@ -52,6 +52,7 @@ All rights reserved.
#include "steam/isteamuserstats.h" #include "steam/isteamuserstats.h"
#endif #endif
#include "GameSettings.h" #include "GameSettings.h"
#include "ItemEnchant.h"
INCLUDE_ANIMATION_DATA INCLUDE_ANIMATION_DATA
INCLUDE_MONSTER_DATA INCLUDE_MONSTER_DATA
@ -692,6 +693,13 @@ bool Monster::_Hurt(int damage,bool onUpperLevel,float z,const TrueDamageFlag da
RemoveMarkStack(); RemoveMarkStack();
} }
if(TriggersMark&&game->GetPlayer()->HasEnchant("Lethal Tempo")){
Buff&lethalTempoBuff{game->GetPlayer()->GetOrAddBuff(BuffType::LETHAL_TEMPO,{"Lethal Tempo"_ENC["STACK DURATION"],0.f})};
lethalTempoBuff.duration="Lethal Tempo"_ENC["STACK DURATION"];
const int maxStackCount{int("Lethal Tempo"_ENC["MAX ATTACK SPEED INCREASE"]/"Lethal Tempo"_ENC["ATTACK SPEED INCREASE"])};
lethalTempoBuff.intensity=std::min(maxStackCount,int(lethalTempoBuff.intensity)+1);
}
mod_dmg*=GetDamageAmplificationMult(); mod_dmg*=GetDamageAmplificationMult();
mod_dmg=std::ceil(mod_dmg); mod_dmg=std::ceil(mod_dmg);
@ -855,9 +863,19 @@ void Monster::PathAroundBehavior(float fElapsedTime){
} }
} }
Buff&Monster::EditBuff(BuffType buff,size_t buffInd){
return EditBuffs(buff)[buffInd];
}
std::vector<std::reference_wrapper<Buff>>Monster::EditBuffs(BuffType buff){
std::vector<std::reference_wrapper<Buff>>filteredBuffs;
std::copy_if(buffList.begin(),buffList.end(),std::back_inserter(filteredBuffs),[&buff](const Buff&b){return b.type==buff;});
return filteredBuffs;
}
std::vector<Buff>Monster::GetBuffs(BuffType buff)const{ std::vector<Buff>Monster::GetBuffs(BuffType buff)const{
std::vector<Buff>filteredBuffs; std::vector<Buff>filteredBuffs;
std::copy_if(buffList.begin(),buffList.end(),std::back_inserter(filteredBuffs),[buff](const Buff&b){return b.type==buff;}); std::copy_if(buffList.begin(),buffList.end(),std::back_inserter(filteredBuffs),[&buff](const Buff&b){return b.type==buff;});
return filteredBuffs; return filteredBuffs;
} }

@ -211,6 +211,8 @@ public:
const std::weak_ptr<Monster>GetWeakPointer()const; const std::weak_ptr<Monster>GetWeakPointer()const;
void ApplyDot(float duration,int damage,float timeBetweenTicks,Buff::MonsterBuffExpireCallbackFunction expireCallbackFunc=[](std::weak_ptr<Monster>m,Buff&b){}); void ApplyDot(float duration,int damage,float timeBetweenTicks,Buff::MonsterBuffExpireCallbackFunction expireCallbackFunc=[](std::weak_ptr<Monster>m,Buff&b){});
const float GetDamageAmplificationMult()const; const float GetDamageAmplificationMult()const;
Buff&EditBuff(BuffType buff,size_t buffInd);
std::vector<std::reference_wrapper<Buff>>EditBuffs(BuffType buff);
private: private:
//NOTE: Marking a monster for deletion does not trigger any death events. It just simply removes the monster from the field!! //NOTE: Marking a monster for deletion does not trigger any death events. It just simply removes the monster from the field!!
// The way this works is that monsters marked for deletion will cause the monster update loop to detect there's at least one or more monsters that must be deleted and will call erase_if on the list at the end of the iteration loop. // The way this works is that monsters marked for deletion will cause the monster update loop to detect there's at least one or more monsters that must be deleted and will call erase_if on the list at the end of the iteration loop.

@ -1094,7 +1094,7 @@ bool Player::OnUpperLevel(){
const std::vector<Buff>Player::GetBuffs(BuffType buff)const{ const std::vector<Buff>Player::GetBuffs(BuffType buff)const{
std::vector<Buff>filteredBuffs; std::vector<Buff>filteredBuffs;
std::copy_if(buffList.begin(),buffList.end(),std::back_inserter(filteredBuffs),[buff](const Buff b){return b.type==buff;}); std::copy_if(buffList.begin(),buffList.end(),std::back_inserter(filteredBuffs),[&buff](const Buff&b){return b.type==buff;});
return filteredBuffs; return filteredBuffs;
} }
@ -1108,6 +1108,21 @@ void Player::RemoveBuff(BuffType buff){
} }
} }
Buff&Player::GetOrAddBuff(BuffType buff,std::pair<BuffDuration,BuffIntensity>newBuff){
if(GetBuffs(buff).size()>0)return EditBuff(buff,0);
else return buffList.emplace_back(this,buff,newBuff.first,newBuff.second);
}
Buff&Player::EditBuff(BuffType buff,size_t buffInd){
return EditBuffs(buff)[buffInd];
}
std::vector<std::reference_wrapper<Buff>>Player::EditBuffs(BuffType buff){
std::vector<std::reference_wrapper<Buff>>filteredBuffs;
std::copy_if(buffList.begin(),buffList.end(),std::back_inserter(filteredBuffs),[&buff](const Buff&b){return b.type==buff;});
return filteredBuffs;
}
void Player::RemoveAllBuffs(BuffType buff){ void Player::RemoveAllBuffs(BuffType buff){
std::erase_if(buffList,[&](Buff&b){return b.type==buff;}); std::erase_if(buffList,[&](Buff&b){return b.type==buff;});
} }
@ -1574,6 +1589,9 @@ const float Player::GetAttackRecoveryRateReduction()const{
for(const Buff&b:GetBuffs(BuffType::ADRENALINE_RUSH)){ for(const Buff&b:GetBuffs(BuffType::ADRENALINE_RUSH)){
attackSpd+=ATTACK_COOLDOWN*"Thief.Ability 3.Attack Speed Increase"_F/100.f; attackSpd+=ATTACK_COOLDOWN*"Thief.Ability 3.Attack Speed Increase"_F/100.f;
} }
for(const Buff&b:GetBuffs(BuffType::LETHAL_TEMPO)){
attackSpd+=ATTACK_COOLDOWN*"Lethal Tempo"_ENC["ATTACK SPEED INCREASE"]/100.f*b.intensity;
}
return attackSpd; return attackSpd;
} }
@ -1857,5 +1875,6 @@ const float Player::GetDamageAmplificationMult()const{
} }
const bool Player::HasEnchant(const std::string_view enchantName)const{ const bool Player::HasEnchant(const std::string_view enchantName)const{
if(!ItemEnchantInfo::GetEnchants().count(std::string(enchantName)))ERR(std::format("WARNING! Enchantment {} does not exist in enchantment database!",enchantName));
return enchantList.count(std::string(enchantName)); return enchantList.count(std::string(enchantName));
} }

@ -185,6 +185,12 @@ public:
const std::vector<Buff>GetBuffs(BuffType buff)const; const std::vector<Buff>GetBuffs(BuffType buff)const;
const std::vector<Buff>GetStatBuffs(const std::vector<std::string>&attr)const; const std::vector<Buff>GetStatBuffs(const std::vector<std::string>&attr)const;
using BuffDuration=float;
using BuffIntensity=float;
Buff&GetOrAddBuff(BuffType buff,std::pair<BuffDuration,BuffIntensity>newBuff);//When you need a buff added regardless if it already exists. You'll either modify an existing one or create a new one.
Buff&EditBuff(BuffType buff,size_t buffInd);
std::vector<std::reference_wrapper<Buff>>EditBuffs(BuffType buff);
//Removes the first buff found. //Removes the first buff found.
void RemoveBuff(BuffType type); void RemoveBuff(BuffType type);
//Removes all buffs of a certain type. //Removes all buffs of a certain type.

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

@ -40,6 +40,8 @@ All rights reserved.
using namespace olc; using namespace olc;
class ItemEnchantInfo;
//Read a string array from the config. //Read a string array from the config.
utils::datafilestringdata operator ""_s(const char*key,std::size_t len); utils::datafilestringdata operator ""_s(const char*key,std::size_t len);
//Read a boolean array from the config. //Read a boolean array from the config.
@ -70,3 +72,5 @@ utils::datafile operator ""_A(const char*key,std::size_t len);
Pixel operator ""_Pixel(const char*key,std::size_t len); Pixel operator ""_Pixel(const char*key,std::size_t len);
float operator ""_FRange(const char*key,std::size_t len); float operator ""_FRange(const char*key,std::size_t len);
float operator ""_Pct(long double pct); float operator ""_Pct(long double pct);
const ItemEnchantInfo&operator ""_ENC(const char*key,std::size_t len);
Loading…
Cancel
Save