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. 10
      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. 6
      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_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,
COLOR_MOD,
DAMAGE_AMPLIFICATION, //Multiplies all incoming damage by this amount.
LETHAL_TEMPO,
};
enum class BuffRestorationType{
ONE_OFF,
@ -78,7 +79,6 @@ namespace BuffOverTimeType{
class AiL;
struct Buff{
using PlayerBuffExpireCallbackFunction=std::function<void(Player*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<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(){
ENCHANT_LIST.clear();
ENCHANT_CATEGORIES.clear();
@ -239,4 +247,4 @@ const std::string ItemEnchant::RollRandomEnchant(){
});
return filteredEnchants[util::random()%filteredEnchants.size()].Name();
}
}

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

@ -52,6 +52,7 @@ All rights reserved.
#include "steam/isteamuserstats.h"
#endif
#include "GameSettings.h"
#include "ItemEnchant.h"
INCLUDE_ANIMATION_DATA
INCLUDE_MONSTER_DATA
@ -692,6 +693,13 @@ bool Monster::_Hurt(int damage,bool onUpperLevel,float z,const TrueDamageFlag da
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=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>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;
}

@ -211,6 +211,8 @@ public:
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){});
const float GetDamageAmplificationMult()const;
Buff&EditBuff(BuffType buff,size_t buffInd);
std::vector<std::reference_wrapper<Buff>>EditBuffs(BuffType buff);
private:
//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.

@ -1094,7 +1094,7 @@ bool Player::OnUpperLevel(){
const std::vector<Buff>Player::GetBuffs(BuffType buff)const{
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;
}
@ -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){
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)){
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;
}
@ -1857,5 +1875,6 @@ const float Player::GetDamageAmplificationMult()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));
}

@ -185,6 +185,12 @@ public:
const std::vector<Buff>GetBuffs(BuffType buff)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.
void RemoveBuff(BuffType type);
//Removes all buffs of a certain type.

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

@ -40,6 +40,8 @@ All rights reserved.
using namespace olc;
class ItemEnchantInfo;
//Read a string array from the config.
utils::datafilestringdata operator ""_s(const char*key,std::size_t len);
//Read a boolean array from the config.
@ -69,4 +71,6 @@ utils::datafile operator ""_A(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 ""_Pct(long double pct);
float operator ""_Pct(long double pct);
const ItemEnchantInfo&operator ""_ENC(const char*key,std::size_t len);
Loading…
Cancel
Save