Implemented Spreading Pain enchant. Made applied buffs not immediately start ticking down on the same frame as they are applied since that causes some strange interactions with chained deaths. Release Build 11152.

pull/65/head
sigonasr2 6 months ago
parent dd38b8d58e
commit b0ee888b83
  1. 63
      Adventures in Lestoria Tests/EnchantTests.cpp
  2. 3
      Adventures in Lestoria Tests/ItemTests.cpp
  3. 3
      Adventures in Lestoria/Buff.cpp
  4. 2
      Adventures in Lestoria/Buff.h
  5. 39
      Adventures in Lestoria/Monster.cpp
  6. 1
      Adventures in Lestoria/Monster.h
  7. 6
      Adventures in Lestoria/Player.cpp
  8. 6
      Adventures in Lestoria/Player.h
  9. 2
      Adventures in Lestoria/Version.h
  10. 3
      Adventures in Lestoria/Witch.cpp
  11. 2
      Adventures in Lestoria/assets/config/items/ItemEnchants.txt
  12. BIN
      x64/Release/Adventures in Lestoria.exe

@ -1023,6 +1023,7 @@ namespace EnchantTests
nullRing.lock()->EnchantItem("Sword Enchantment"); nullRing.lock()->EnchantItem("Sword Enchantment");
player->CheckAndPerformAbility(player->GetAbility3(),testKeyboardInput); player->CheckAndPerformAbility(player->GetAbility3(),testKeyboardInput);
Assert::AreEqual(300.f,player->GetAttackRange(),L"Attack range of Warrior doubled."); Assert::AreEqual(300.f,player->GetAttackRange(),L"Attack range of Warrior doubled.");
game->OnUserUpdate(0.f); //Wait an extra tick for the buff to begin going down.
game->SetElapsedTime(8.f); game->SetElapsedTime(8.f);
game->OnUserUpdate(8.f); game->OnUserUpdate(8.f);
Assert::AreEqual(150.f,player->GetAttackRange(),L"Attack range of Warrior is normal again."); Assert::AreEqual(150.f,player->GetAttackRange(),L"Attack range of Warrior is normal again.");
@ -1241,5 +1242,67 @@ namespace EnchantTests
player->CheckAndPerformAbility(player->GetRightClickAbility(),testKeyboardInput); player->CheckAndPerformAbility(player->GetRightClickAbility(),testKeyboardInput);
Assert::AreEqual("Witch.Right Click Ability.Cooldown"_F,player->GetRightClickAbility().cooldown,L"Cooldown time should be 8s when already in cat form."); Assert::AreEqual("Witch.Right Click Ability.Cooldown"_F,player->GetRightClickAbility().cooldown,L"Cooldown time should be 8s when already in cat form.");
} }
TEST_METHOD(SpreadingPainNoEnchantCheck){
game->ChangePlayerClass(WITCH);
player=game->GetPlayer();
MonsterData testMonsterData{"TestName2","Test Monster2",1,10,5,{MonsterDropData{"Health Potion",100.f,1,1}},200.f};
MONSTER_DATA["TestName2"]=testMonsterData;
Monster&newMonster{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName2"])};
Monster&newMonster2{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName2"])};
Monster&newMonster3{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName2"])};
Monster&newMonster4{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName2"])};
game->OnUserUpdate(0.f);
player->SetTestScreenAimingLocation(game->GetScreenSize()/2+vf2d{30.f,0.f});
player->PrepareCast(player->GetAbility1());
player->CastSpell(player->GetAbility1());
game->OnUserUpdate(0.f);
Assert::IsTrue(newMonster.GetBuffs(BuffType::CURSE_OF_PAIN).size()>0,L"The first monster should have been targeted with Curse of Pain.");
game->SetElapsedTime(3.f);
game->OnUserUpdate(3.f);
Assert::IsTrue(newMonster.IsDead(),L"The first monster has died to Curse of Pain's tick.");
Assert::IsTrue(newMonster2.GetBuffs(BuffType::CURSE_OF_PAIN).size()==0,L"Without the enchant, Curse of Pain should not have spread to Monster 2.");
Assert::IsTrue(newMonster3.GetBuffs(BuffType::CURSE_OF_PAIN).size()==0,L"Without the enchant, Curse of Pain should not have spread to Monster 3.");
Assert::IsTrue(newMonster4.GetBuffs(BuffType::CURSE_OF_PAIN).size()==0,L"Without the enchant, Curse of Pain should not have spread to Monster 4.");
}
TEST_METHOD(SpreadingPainEnchantCheck){
game->ChangePlayerClass(WITCH);
player=game->GetPlayer();
std::weak_ptr<Item>nullRing{Inventory::AddItem("Null Ring"s)};
Inventory::EquipItem(nullRing,EquipSlot::RING1);
nullRing.lock()->EnchantItem("Spreading Pain");
MonsterData testMonsterData{"TestName2","Test Monster2",1,10,5,{MonsterDropData{"Health Potion",100.f,1,1}},200.f};
MONSTER_DATA["TestName2"]=testMonsterData;
Monster&newMonster5{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName2"])};
Monster&newMonster{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName2"])};
Monster&newMonster2{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName2"])};
Monster&newMonster3{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName2"])};
Monster&newMonster4{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName2"])};
game->OnUserUpdate(0.f);
newMonster5.Hurt(1,newMonster5.OnUpperLevel(),newMonster5.GetZ());
Assert::AreEqual(size_t(0),newMonster.GetBuffs(BuffType::CURSE_OF_PAIN).size(),L"If a target without Curse of Pain dies, it should not spread onto other targets.");
Assert::AreEqual(size_t(0),newMonster2.GetBuffs(BuffType::CURSE_OF_PAIN).size(),L"If a target without Curse of Pain dies, it should not spread onto other targets.");
Assert::AreEqual(size_t(0),newMonster3.GetBuffs(BuffType::CURSE_OF_PAIN).size(),L"If a target without Curse of Pain dies, it should not spread onto other targets.");
Assert::AreEqual(size_t(0),newMonster4.GetBuffs(BuffType::CURSE_OF_PAIN).size(),L"If a target without Curse of Pain dies, it should not spread onto other targets.");
player->SetTestScreenAimingLocation(game->GetScreenSize()/2+vf2d{30.f,0.f});
player->PrepareCast(player->GetAbility1());
player->CastSpell(player->GetAbility1());
game->OnUserUpdate(0.f);
Assert::AreEqual(size_t(1),newMonster.GetBuffs(BuffType::CURSE_OF_PAIN).size(),L"The first monster should have been targeted with Curse of Pain.");
game->SetElapsedTime(3.f);
game->OnUserUpdate(3.f);
Assert::IsTrue(newMonster.IsDead(),L"The first monster has died to Curse of Pain's tick.");
Assert::IsTrue(!newMonster2.IsDead(),L"The other monsters should not be dead.");
Assert::IsTrue(!newMonster3.IsDead(),L"The other monsters should not be dead.");
Assert::IsTrue(!newMonster4.IsDead(),L"The other monsters should not be dead.");
Assert::AreEqual(size_t(1),newMonster2.GetBuffs(BuffType::CURSE_OF_PAIN).size(),L"With the enchant, Curse of Pain should have spread to Monster 2.");
newMonster2.Hurt(1,newMonster2.OnUpperLevel(),newMonster2.GetZ());
Assert::IsTrue(newMonster2.IsDead(),L"Monster 2 should be dead.");
Assert::IsTrue(!newMonster3.IsDead(),L"The other monsters should not be dead.");
Assert::IsTrue(!newMonster4.IsDead(),L"The other monsters should not be dead.");
Assert::AreEqual(size_t(1),newMonster3.GetBuffs(BuffType::CURSE_OF_PAIN).size(),L"With the enchant, Curse of Pain should have spread to Monster 3 and only 1 stack should have been applied.");
Assert::AreEqual("Witch.Ability 1.Curse Debuff"_f[2],newMonster3.GetBuffs(BuffType::CURSE_OF_PAIN)[0].duration,L"When Curse of Pain is reapplied to a monster that already has Curse of Pain, it refreshes the duration.");
Assert::AreEqual(size_t(1),newMonster4.GetBuffs(BuffType::CURSE_OF_PAIN).size(),L"With the enchant, Curse of Pain should have spread to Monster 4 and only 1 stack should have been applied.");
Assert::AreEqual("Witch.Ability 1.Curse Debuff"_f[2],newMonster4.GetBuffs(BuffType::CURSE_OF_PAIN)[0].duration,L"When Curse of Pain is reapplied to a monster that already has Curse of Pain, it refreshes the duration.");
}
}; };
} }

@ -152,6 +152,7 @@ namespace ItemTests
game->SetLoadoutItem(0,"Flat Recovery Potion"); game->SetLoadoutItem(0,"Flat Recovery Potion");
testKey->bHeld=true; //Simulate key being pressed. testKey->bHeld=true; //Simulate key being pressed.
player->CheckAndPerformAbility(player->GetItem1(),testKeyboardInput); player->CheckAndPerformAbility(player->GetItem1(),testKeyboardInput);
game->OnUserUpdate(0.f); //Wait an extra tick for the buff to begin going down.
game->SetElapsedTime(0.05f); game->SetElapsedTime(0.05f);
game->OnUserUpdate(0.05f);//Wait some time as the item applies a buff that heals us. We're also going to gain one mana during this tick. game->OnUserUpdate(0.05f);//Wait some time as the item applies a buff that heals us. We're also going to gain one mana during this tick.
Assert::AreEqual(75,player->GetHealth(),L"Player Health is 75 after using Flat Recovery Potion."); Assert::AreEqual(75,player->GetHealth(),L"Player Health is 75 after using Flat Recovery Potion.");
@ -164,6 +165,7 @@ namespace ItemTests
game->SetLoadoutItem(1,"Pct Recovery Potion"); game->SetLoadoutItem(1,"Pct Recovery Potion");
testKey->bHeld=true; //Simulate key being pressed. testKey->bHeld=true; //Simulate key being pressed.
player->CheckAndPerformAbility(player->GetItem2(),testKeyboardInput); player->CheckAndPerformAbility(player->GetItem2(),testKeyboardInput);
game->OnUserUpdate(0.f); //Wait an extra tick for the buff to begin going down.
game->SetElapsedTime(0.05f); game->SetElapsedTime(0.05f);
game->OnUserUpdate(0.05f);//Wait some time as the item applies a buff that heals us. game->OnUserUpdate(0.05f);//Wait some time as the item applies a buff that heals us.
Assert::AreEqual(75,player->GetHealth(),L"Player Health is 75 after using Pct Recovery Potion."); Assert::AreEqual(75,player->GetHealth(),L"Player Health is 75 after using Pct Recovery Potion.");
@ -175,6 +177,7 @@ namespace ItemTests
game->SetLoadoutItem(2,"Bandages"); game->SetLoadoutItem(2,"Bandages");
testKey->bHeld=true; //Simulate key being pressed. testKey->bHeld=true; //Simulate key being pressed.
player->CheckAndPerformAbility(player->GetItem3(),testKeyboardInput); player->CheckAndPerformAbility(player->GetItem3(),testKeyboardInput);
game->OnUserUpdate(0.f); //Wait an extra tick for the buff to begin going down.
game->SetElapsedTime(0.05f); game->SetElapsedTime(0.05f);
game->OnUserUpdate(0.05f);//Wait some time as the item applies a buff that heals us. game->OnUserUpdate(0.05f);//Wait some time as the item applies a buff that heals us.
Assert::AreEqual(30,player->GetHealth(),L"Player is immediately healed for 5 health points on Bandages use."); Assert::AreEqual(30,player->GetHealth(),L"Player is immediately healed for 5 health points on Bandages use.");

@ -112,6 +112,7 @@ Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType t
:attachedTarget(attachedTarget),type(type),restorationType(restorationType),duration(duration),intensity(intensity),nextTick(timeBetweenTicks),timeBetweenTicks(timeBetweenTicks),overTimeType(overTimeType),originalDuration(this->duration){} :attachedTarget(attachedTarget),type(type),restorationType(restorationType),duration(duration),intensity(intensity),nextTick(timeBetweenTicks),timeBetweenTicks(timeBetweenTicks),overTimeType(overTimeType),originalDuration(this->duration){}
void Buff::Update(AiL*game,float fElapsedTime){ void Buff::Update(AiL*game,float fElapsedTime){
if(!waitOneTick){
duration-=fElapsedTime; duration-=fElapsedTime;
lifetime+=fElapsedTime; lifetime+=fElapsedTime;
if(enabled&&overTimeType.has_value()&&lifetime>=nextTick){ if(enabled&&overTimeType.has_value()&&lifetime>=nextTick){
@ -120,6 +121,8 @@ void Buff::Update(AiL*game,float fElapsedTime){
if(restorationType==BuffRestorationType::ONE_OFF)enabled=false; if(restorationType==BuffRestorationType::ONE_OFF)enabled=false;
} }
} }
waitOneTick=false;
}
void Buff::BuffTick(AiL*game,float fElapsedTime){ void Buff::BuffTick(AiL*game,float fElapsedTime){
if(!overTimeType.has_value())ERR("WARNING! Trying to run BuffTick without a valid over time type provided! THIS SHOULD NOT BE HAPPENING!"); if(!overTimeType.has_value())ERR("WARNING! Trying to run BuffTick without a valid over time type provided! THIS SHOULD NOT BE HAPPENING!");

@ -62,6 +62,7 @@ enum BuffType{
LETHAL_TEMPO, LETHAL_TEMPO,
BURNING_ARROW_BURN, BURNING_ARROW_BURN,
SWORD_ENCHANTMENT, SWORD_ENCHANTMENT,
CURSE_OF_PAIN,
}; };
enum class BuffRestorationType{ enum class BuffRestorationType{
ONE_OFF, //This is used as a hack fix for the RestoreDuringCast Item script since they require us to restore 1 tick immediately. Over time buffs do not apply a tick immediately. ONE_OFF, //This is used as a hack fix for the RestoreDuringCast Item script since they require us to restore 1 tick immediately. Over time buffs do not apply a tick immediately.
@ -110,6 +111,7 @@ struct Buff{
void Update(AiL*game,float fElapsedTime); void Update(AiL*game,float fElapsedTime);
private: private:
bool waitOneTick{true};
bool enabled{true}; //This is only turned off because the ONE_OFF effect. See BuffType::ONE_OFF for more details. bool enabled{true}; //This is only turned off because the ONE_OFF effect. See BuffType::ONE_OFF for more details.
std::optional<BuffOverTimeType::BuffOverTimeType>overTimeType; std::optional<BuffOverTimeType::BuffOverTimeType>overTimeType;
void BuffTick(AiL*game,float fElapsedTime); void BuffTick(AiL*game,float fElapsedTime);

@ -53,6 +53,7 @@ All rights reserved.
#endif #endif
#include "GameSettings.h" #include "GameSettings.h"
#include "ItemEnchant.h" #include "ItemEnchant.h"
#include <ranges>
INCLUDE_ANIMATION_DATA INCLUDE_ANIMATION_DATA
INCLUDE_MONSTER_DATA INCLUDE_MONSTER_DATA
@ -1064,6 +1065,41 @@ void Monster::OnDeath(){
adrenalineRushBuff.duration+="Bloodlust"_ENC["BUFF TIMER INCREASE"]; adrenalineRushBuff.duration+="Bloodlust"_ENC["BUFF TIMER INCREASE"];
adrenalineRushBuff.intensity=std::min(int("Bloodlust"_ENC["MAX ATTACK BUFF STACKS"]),int(adrenalineRushBuff.intensity)+1); adrenalineRushBuff.intensity=std::min(int("Bloodlust"_ENC["MAX ATTACK BUFF STACKS"]),int(adrenalineRushBuff.intensity)+1);
} }
if(game->GetPlayer()->HasEnchant("Spreading Pain")&&GetBuffs(BuffType::CURSE_OF_PAIN).size()>0){
//NOTE: If we have to change/modify Curse of Pain, we must also modify it in Witch.cpp (Witch::ability1.action define)
#pragma region Applies Curse of Pain to nearby targets
const float buffTimeBetweenTicks{"Witch.Ability 1.Curse Debuff"_f[1]};
const float buffDamageMult{"Witch.Ability 1.Curse Debuff"_f[0]};
const float buffDuration{"Witch.Ability 1.Curse Debuff"_f[2]};
for(std::shared_ptr<Monster>&m:MONSTER_LIST){
if(&*m==&*GetWeakPointer().lock()||m->InUndamageableState(OnUpperLevel(),GetZ()))continue;
if(m->GetBuffs(BuffType::CURSE_OF_PAIN).size()>0){
m->EditBuff(BuffType::CURSE_OF_PAIN,0U).duration=m->EditBuff(BuffType::CURSE_OF_PAIN,0U).originalDuration;
}else{
float distFromDeadMonster{geom2d::line<float>(GetPos(),m->GetPos()).length()};
if(distFromDeadMonster<"Spreading Pain"_ENC["SPREAD RANGE"]/100*24){
m->ApplyDot(buffDuration,game->GetPlayer()->GetAttack()*buffDamageMult,buffTimeBetweenTicks,BuffType::CURSE_OF_PAIN,
[](std::weak_ptr<Monster>m,Buff&b){
expireCallbackFunc:
if(!m.expired())m.lock()->Hurt(game->GetPlayer()->GetAttack()*"Witch.Ability 1.Final Tick Damage"_F,m.lock()->OnUpperLevel(),m.lock()->GetZ(),HurtFlag::DOT);
}
);
m->AddBuff(BuffType::GLOW_PURPLE,buffDuration,1.f);
}
}
DrawLineToTarget:
const vf2d targetPos{m->GetPos()};
for(int i:std::ranges::iota_view(0,int(util::distance(GetPos(),targetPos)/8))){
float drawDist{i*8.f};
float fadeInTime{i*0.05f};
float fadeOutTime{0.5f+i*0.05f};
float effectSize{util::random(0.2f)};
game->AddEffect(std::make_unique<Effect>(geom2d::line<float>(GetPos(),targetPos).rpoint(drawDist),0.f,"mark_trail.png",OnUpperLevel(),fadeInTime,fadeOutTime,vf2d{effectSize,effectSize},vf2d{},Pixel{100,0,155,uint8_t(util::random_range(0,120))},0.f,0.f),true);
}
SoundEffect::PlaySFX("Curse of Pain",m->GetPos());
}
#pragma endregion
}
SpawnDrops(); SpawnDrops();
@ -1484,6 +1520,9 @@ const std::weak_ptr<Monster>Monster::GetWeakPointer()const{
void Monster::ApplyDot(float duration,int damage,float timeBetweenTicks,Buff::MonsterBuffExpireCallbackFunction expireCallbackFunc){ void Monster::ApplyDot(float duration,int damage,float timeBetweenTicks,Buff::MonsterBuffExpireCallbackFunction expireCallbackFunc){
AddBuff(BuffRestorationType::OVER_TIME,BuffOverTimeType::HP_DAMAGE_OVER_TIME,duration,damage,timeBetweenTicks,expireCallbackFunc); AddBuff(BuffRestorationType::OVER_TIME,BuffOverTimeType::HP_DAMAGE_OVER_TIME,duration,damage,timeBetweenTicks,expireCallbackFunc);
} }
void Monster::ApplyDot(float duration,int damage,float timeBetweenTicks,BuffType identifierType,Buff::MonsterBuffExpireCallbackFunction expireCallbackFunc){
AddBuff(identifierType,BuffRestorationType::OVER_TIME,BuffOverTimeType::HP_DAMAGE_OVER_TIME,duration,damage,timeBetweenTicks,expireCallbackFunc);
}
void Monster::SetWeakPointer(std::shared_ptr<Monster>&sharedMonsterPtr){ void Monster::SetWeakPointer(std::shared_ptr<Monster>&sharedMonsterPtr){
weakPtr=sharedMonsterPtr; weakPtr=sharedMonsterPtr;

@ -214,6 +214,7 @@ public:
const bool CanMove()const; const bool CanMove()const;
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){});
void ApplyDot(float duration,int damage,float timeBetweenTicks,BuffType identifierType,Buff::MonsterBuffExpireCallbackFunction expireCallbackFunc=[](std::weak_ptr<Monster>m,Buff&b){});
const float GetDamageAmplificationMult(const bool backstabOccurred)const; const float GetDamageAmplificationMult(const bool backstabOccurred)const;
Buff&EditBuff(BuffType buff,size_t buffInd); Buff&EditBuff(BuffType buff,size_t buffInd);
std::vector<std::reference_wrapper<Buff>>EditBuffs(BuffType buff); std::vector<std::reference_wrapper<Buff>>EditBuffs(BuffType buff);

@ -1766,7 +1766,8 @@ const vf2d Player::GetWorldAimingLocation(bool useWalkDir,bool invert){
} }
const vf2d Player::GetAimingLocation(bool useWalkDir,bool invert){ const vf2d Player::GetAimingLocation(bool useWalkDir,bool invert){
if(UsingAutoAim()){ if(testAimingLoc)return testAimingLoc.value();
else if(UsingAutoAim()){
float xAxis=0.f,yAxis=0.f; float xAxis=0.f,yAxis=0.f;
#pragma region Manual Aiming #pragma region Manual Aiming
@ -2244,3 +2245,6 @@ void Player::UpdateAnimationStates(){
animation.UpdateState(internal_animState,game->GetElapsedTime()); animation.UpdateState(internal_animState,game->GetElapsedTime());
animation.UpdateState(internal_catAnimState,game->GetElapsedTime()); animation.UpdateState(internal_catAnimState,game->GetElapsedTime());
} }
void Player::SetTestScreenAimingLocation(vf2d forcedAimingLoc){
testAimingLoc=forcedAimingLoc;
}

@ -332,6 +332,9 @@ public:
void DeactivateCatForm(); void DeactivateCatForm();
const bool CatFormActive()const; const bool CatFormActive()const;
void UpdateAnimationStates(); void UpdateAnimationStates();
void CastSpell(Ability&ability); //NOTE: This usually is not called unless we are running tests! CastSpell is meant to be used when we have chosen a pre-casting target position and have released the associated key to cast the spell.
void PrepareCast(Ability&ability); //NOTE: This usually is not called unless we are running tests! PrepareCast is meant to be used before we use CastSpell in unit tests.
void SetTestScreenAimingLocation(vf2d forcedAimingLoc);
private: private:
const int SHIELD_CAPACITY{32}; const int SHIELD_CAPACITY{32};
int hp="Warrior.BaseHealth"_I; int hp="Warrior.BaseHealth"_I;
@ -423,6 +426,7 @@ private:
void OnBuffAdd(Buff&newBuff); void OnBuffAdd(Buff&newBuff);
std::vector<std::pair<PlayerTimerType,ShieldAmount>>shield; std::vector<std::pair<PlayerTimerType,ShieldAmount>>shield;
bool catForm{false}; bool catForm{false};
std::optional<vf2d>testAimingLoc{};
protected: protected:
const float ATTACK_COOLDOWN="Warrior.Auto Attack.Cooldown"_F; const float ATTACK_COOLDOWN="Warrior.Auto Attack.Cooldown"_F;
const float MAGIC_ATTACK_COOLDOWN="Wizard.Auto Attack.Cooldown"_F; const float MAGIC_ATTACK_COOLDOWN="Wizard.Auto Attack.Cooldown"_F;
@ -440,9 +444,7 @@ protected:
vf2d vel={0,0}; vf2d vel={0,0};
Key facingDirection=DOWN; Key facingDirection=DOWN;
float swordSwingTimer=0; float swordSwingTimer=0;
void CastSpell(Ability&ability);
Ability*castPrepAbility; Ability*castPrepAbility;
void PrepareCast(Ability&ability);
vf2d precastLocation={}; vf2d precastLocation={};
void SetVelocity(vf2d vel); void SetVelocity(vf2d vel);
const float RETREAT_DISTANCE=24*"Ranger.Right Click Ability.RetreatDistance"_F/100; const float RETREAT_DISTANCE=24*"Ranger.Right Click Ability.RetreatDistance"_F/100;

@ -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 5 #define VERSION_PATCH 5
#define VERSION_BUILD 11132 #define VERSION_BUILD 11152
#define stringify(a) stringify_(a) #define stringify(a) stringify_(a)
#define stringify_(a) #a #define stringify_(a) #a

@ -139,10 +139,11 @@ void Witch::InitializeClassAbilities(){
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
std::optional<std::weak_ptr<Monster>>curseTarget{Monster::GetNearestMonster(pos,"Witch.Ability 1.Casting Range"_F/100.f*24,p->OnUpperLevel(),p->GetZ())}; std::optional<std::weak_ptr<Monster>>curseTarget{Monster::GetNearestMonster(pos,"Witch.Ability 1.Casting Range"_F/100.f*24,p->OnUpperLevel(),p->GetZ())};
if(curseTarget.has_value()&&!curseTarget.value().expired()){ if(curseTarget.has_value()&&!curseTarget.value().expired()){
//NOTE: If we have to change/modify Curse of Pain, we must also modify it in Monster::OnDeath (Monster.cpp)
const float buffTimeBetweenTicks{"Witch.Ability 1.Curse Debuff"_f[1]}; const float buffTimeBetweenTicks{"Witch.Ability 1.Curse Debuff"_f[1]};
const float buffDamageMult{"Witch.Ability 1.Curse Debuff"_f[0]}; const float buffDamageMult{"Witch.Ability 1.Curse Debuff"_f[0]};
const float buffDuration{"Witch.Ability 1.Curse Debuff"_f[2]}; const float buffDuration{"Witch.Ability 1.Curse Debuff"_f[2]};
curseTarget.value().lock()->ApplyDot(buffDuration,p->GetAttack()*buffDamageMult,buffTimeBetweenTicks, curseTarget.value().lock()->ApplyDot(buffDuration,p->GetAttack()*buffDamageMult,buffTimeBetweenTicks,BuffType::CURSE_OF_PAIN,
[](std::weak_ptr<Monster>m,Buff&b){ [](std::weak_ptr<Monster>m,Buff&b){
expireCallbackFunc: expireCallbackFunc:
if(!m.expired())m.lock()->Hurt(game->GetPlayer()->GetAttack()*"Witch.Ability 1.Final Tick Damage"_F,m.lock()->OnUpperLevel(),m.lock()->GetZ(),HurtFlag::DOT); if(!m.expired())m.lock()->Hurt(game->GetPlayer()->GetAttack()*"Witch.Ability 1.Final Tick Damage"_F,m.lock()->OnUpperLevel(),m.lock()->GetZ(),HurtFlag::DOT);

@ -411,7 +411,7 @@ Item Enchants
} }
Spreading Pain Spreading Pain
{ {
Description = "If Curse of Pain kills a target, it can spread to nearby targets." Description = "If a target with Curse of Pain dies, it can spread to nearby targets."
Affects = Ability 1 Affects = Ability 1
SPREAD RANGE = 250 SPREAD RANGE = 250

Loading…
Cancel
Save