Add cat mew sound effect for Witch Leap ability. Refactor Buffs to include a player/monster callback function when a buff expires. This is used specifically for the curse debuff. Clean up buff update code to use lambdas instead of macros. Release Build 10359.

mac-build
sigonasr2 4 months ago
parent 21579b7bdf
commit a35ea5b772
  1. 4
      Adventures in Lestoria/AdventuresInLestoria.cpp
  2. 2
      Adventures in Lestoria/BearTrap.cpp
  3. 51
      Adventures in Lestoria/Buff.cpp
  4. 22
      Adventures in Lestoria/Buff.h
  5. 27
      Adventures in Lestoria/Monster.cpp
  6. 3
      Adventures in Lestoria/Monster.h
  7. 13
      Adventures in Lestoria/Player.cpp
  8. 1
      Adventures in Lestoria/Player.h
  9. 2
      Adventures in Lestoria/Version.h
  10. 9
      Adventures in Lestoria/Witch.cpp
  11. 6
      Adventures in Lestoria/assets/config/audio/events.txt
  12. BIN
      Adventures in Lestoria/assets/sounds/kitten_mew.ogg
  13. BIN
      x64/Release/Adventures in Lestoria.exe

@ -4235,7 +4235,9 @@ void AiL::UpdateMonsters(){
}
for(Monster&m:game->monstersToBeSpawned){
size_t prevCapacity=MONSTER_LIST.capacity();
MONSTER_LIST.emplace_back(std::make_shared<Monster>(m));
std::shared_ptr<Monster>newMonster{std::make_shared<Monster>(m)};
newMonster->weakPtr=newMonster;
MONSTER_LIST.emplace_back(std::move(newMonster));
if(MONSTER_LIST.capacity()>prevCapacity)LOG(std::format("WARNING! The monster list has automatically reserved more space and resized to {}! This caused one potential frame where bullet/effect hitlists that stored information on what monsters were hit to potentially be hit a second time or cause monsters that should've been hit to never be hit. Consider starting with a larger default reserved size for MONSTER_LIST if your intention was to have this many monsters!",MONSTER_LIST.capacity()));
}
if(aMonsterIsMarkedForDeletion)std::erase_if(MONSTER_LIST,[&](const std::shared_ptr<Monster>&m){return m->markedForDeletion;});

@ -70,7 +70,7 @@ BulletDestroyState BearTrap::MonsterHit(Monster&monster,const uint8_t markStacks
const uint8_t resetStackCount{uint8_t("Trapper.Ability 2.Marked Target Stack Count Reset"_I)+1U}; //Add an additional stack because we know the target hit is about to lose one stack.
const uint8_t numberOfStacksToReplenish{uint8_t(resetStackCount-monster.GetMarkStacks())};
monster.ApplyMark("Trapper.Ability 2.Marked Target Reset Time"_F,numberOfStacksToReplenish);
monster.AddBuff(BuffRestorationType::OVER_TIME,BuffOverTimeType::HP_DAMAGE_OVER_TIME,bleedDuration,bleedDamage,timeBetweenTicks);
monster.ApplyDot(bleedDuration,bleedDamage,timeBetweenTicks);
}
monster.AddBuff(BuffType::SLOWDOWN,"Trapper.Ability 2.Slowdown Time"_F,"Trapper.Ability 2.Slowdown Amount"_F/100.f);

@ -40,18 +40,28 @@ All rights reserved.
#include "Player.h"
#include "Monster.h"
Buff::Buff(std::variant<Player*,Monster*>attachedTarget,BuffType type,float duration,float intensity)
Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,float duration,float intensity)
:attachedTarget(attachedTarget),type(type),duration(duration),intensity(intensity){}
Buff::Buff(std::variant<Player*,Monster*>attachedTarget,BuffType type,float duration,float intensity,std::set<ItemAttribute> attr)
Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,float duration,float intensity,std::set<ItemAttribute> attr)
:attachedTarget(attachedTarget),type(type),duration(duration),intensity(intensity),attr(attr){}
Buff::Buff(std::variant<Player*,Monster*>attachedTarget,BuffType type,float duration,float intensity,std::set<std::string> attr)
Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,float duration,float intensity,std::set<std::string> attr)
:attachedTarget(attachedTarget),type(type),duration(duration),intensity(intensity){
for(const std::string&s:attr){
this->attr.insert(ItemAttribute::attributes.at(s));
}
}
Buff::Buff(std::variant<Player*,Monster*>attachedTarget,BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks)
Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,PlayerBuffExpireCallbackFunction expireCallbackFunc)
:Buff(attachedTarget,type,overTimeType,duration,intensity,timeBetweenTicks){
playerBuffCallbackFunc=expireCallbackFunc;
}
Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,MonsterBuffExpireCallbackFunction expireCallbackFunc)
:Buff(attachedTarget,type,overTimeType,duration,intensity,timeBetweenTicks){
monsterBuffCallbackFunc=expireCallbackFunc;
}
Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks)
:attachedTarget(attachedTarget),duration(duration),intensity(intensity),nextTick(duration-timeBetweenTicks),timeBetweenTicks(timeBetweenTicks),overTimeType(overTimeType){
switch(type){
case BuffRestorationType::ONE_OFF:{
@ -78,35 +88,42 @@ void Buff::Update(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!");
using enum BuffOverTimeType::BuffOverTimeType;
const auto OwnerIsPlayer=[&](){return std::holds_alternative<Player*>(attachedTarget);};
const auto OwnerIsMonster=[&](){return std::holds_alternative<std::weak_ptr<Monster>>(attachedTarget);};
const auto MonsterIsValid=[&](){return !std::get<std::weak_ptr<Monster>>(attachedTarget).expired();};
const auto GetPlayer=[&](){return std::get<Player*>(attachedTarget);};
const auto GetMonster=[&](){return std::get<std::weak_ptr<Monster>>(attachedTarget).lock();};
switch(int(overTimeType.value())){
case HP_RESTORATION:{
IfEntity(Player)GetEntity(Player)->Heal(intensity);
else IfEntity(Monster)GetEntity(Monster)->Heal(intensity);
if(OwnerIsPlayer())GetPlayer()->Heal(intensity);
else if(OwnerIsMonster()){if(MonsterIsValid())GetMonster()->Heal(intensity);}
else ERR("WARNING! Buff Over Time attached Target is somehow not a Player nor a Monster Pointer! THIS SHOULD NOT BE HAPPENING!")
}break;
case MP_RESTORATION:{
IfEntity(Player)GetEntity(Player)->RestoreMana(intensity);
else IfEntity(Monster)ERR("WARNING! Monsters don't have mana, this functionality is not supported!")
if(OwnerIsPlayer())GetPlayer()->RestoreMana(intensity);
else if(OwnerIsMonster())ERR("WARNING! Monsters don't have mana, this functionality is not supported!")
else ERR("WARNING! Buff Over Time attached Target is somehow not a Player nor a Monster Pointer! THIS SHOULD NOT BE HAPPENING!")
}break;
case HP_PCT_RESTORATION:{
IfEntity(Player)GetEntity(Player)->Heal(GetEntity(Player)->GetMaxHealth()*intensity/100.f);
else IfEntity(Monster)GetEntity(Monster)->Heal(GetEntity(Monster)->GetMaxHealth()*intensity/100.f);
if(OwnerIsPlayer())GetPlayer()->Heal(GetPlayer()->GetMaxHealth()*intensity/100.f);
else if(OwnerIsMonster())GetMonster()->Heal(GetMonster()->GetMaxHealth()*intensity/100.f);
else ERR("WARNING! Buff Over Time attached Target is somehow not a Player nor a Monster Pointer! THIS SHOULD NOT BE HAPPENING!")
}break;
case MP_PCT_RESTORATION:{
IfEntity(Player)GetEntity(Player)->RestoreMana(GetEntity(Player)->GetMaxMana()*intensity/100.f);
else IfEntity(Monster)ERR("WARNING! Monsters don't have mana, this functionality is not supported!")
if(OwnerIsPlayer())GetPlayer()->RestoreMana(GetPlayer()->GetMaxMana()*intensity/100.f);
else if(OwnerIsMonster())ERR("WARNING! Monsters don't have mana, this functionality is not supported!")
else ERR("WARNING! Buff Over Time attached Target is somehow not a Player nor a Monster Pointer! THIS SHOULD NOT BE HAPPENING!")
}break;
case HP_DAMAGE_OVER_TIME:{
IfEntity(Player)GetEntity(Player)->Hurt(intensity,GetEntity(Player)->OnUpperLevel(),GetEntity(Player)->GetZ(),HurtFlag::DOT);
else IfEntity(Monster)GetEntity(Monster)->Hurt(intensity,GetEntity(Monster)->OnUpperLevel(),GetEntity(Monster)->GetZ(),HurtFlag::DOT);
if(OwnerIsPlayer())GetPlayer()->Hurt(intensity,GetPlayer()->OnUpperLevel(),GetPlayer()->GetZ(),HurtFlag::DOT);
else if(OwnerIsMonster())GetMonster()->Hurt(intensity,GetMonster()->OnUpperLevel(),GetMonster()->GetZ(),HurtFlag::DOT);
else ERR("WARNING! Buff Over Time attached Target is somehow not a Player nor a Monster Pointer! THIS SHOULD NOT BE HAPPENING!")
}break;
case HP_PCT_DAMAGE_OVER_TIME:{
IfEntity(Player)GetEntity(Player)->Hurt(GetEntity(Player)->GetMaxHealth()*intensity/100.f,GetEntity(Player)->OnUpperLevel(),GetEntity(Player)->GetZ(),HurtFlag::DOT);
else IfEntity(Monster)GetEntity(Monster)->Hurt(GetEntity(Monster)->GetMaxHealth()*intensity/100.f,GetEntity(Monster)->OnUpperLevel(),GetEntity(Monster)->GetZ(),HurtFlag::DOT);
if(OwnerIsPlayer())GetPlayer()->Hurt(GetPlayer()->GetMaxHealth()*intensity/100.f,GetPlayer()->OnUpperLevel(),GetPlayer()->GetZ(),HurtFlag::DOT);
else if(OwnerIsMonster())GetMonster()->Hurt(GetMonster()->GetMaxHealth()*intensity/100.f,GetMonster()->OnUpperLevel(),GetMonster()->GetZ(),HurtFlag::DOT);
else ERR("WARNING! Buff Over Time attached Target is somehow not a Player nor a Monster Pointer! THIS SHOULD NOT BE HAPPENING!")
}break;
}

@ -52,7 +52,6 @@ enum BuffType{
SELF_INFLICTED_SLOWDOWN, //Used for monsters and can't be applied by any player abilities.
ADRENALINE_RUSH,
TRAPPER_MARK,
DAMAGE_OVER_TIME,
OVER_TIME,
ONE_OFF,
OVER_TIME_DURING_CAST,
@ -73,24 +72,29 @@ namespace BuffOverTimeType{
};
};
#define IfEntity(type) if(std::holds_alternative<type*>(attachedTarget))
#define GetEntity(type) std::get<type*>(attachedTarget)
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)>;
BuffType type;
float duration=1;
float timeBetweenTicks=1;
float intensity=1;
float nextTick=0;
std::set<ItemAttribute> attr;
std::variant<Player*,Monster*>attachedTarget; //Who has this buff.
std::variant<Player*,std::weak_ptr<Monster>>attachedTarget; //Who has this buff.
PlayerBuffExpireCallbackFunction playerBuffCallbackFunc=[](Player*p,Buff&b){};
MonsterBuffExpireCallbackFunction monsterBuffCallbackFunc=[](std::weak_ptr<Monster>m,Buff&b){};
Buff(std::variant<Player*,Monster*>attachedTarget,BuffType type,float duration,float intensity);
Buff(std::variant<Player*,Monster*>attachedTarget,BuffType type,float duration,float intensity,std::set<ItemAttribute> attr);
Buff(std::variant<Player*,Monster*>attachedTarget,BuffType type,float duration,float intensity,std::set<std::string> attr);
Buff(std::variant<Player*,Monster*>attachedTarget,BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks);
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,float duration,float intensity);
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,float duration,float intensity,std::set<ItemAttribute> attr);
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,float duration,float intensity,std::set<std::string> attr);
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks);
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,PlayerBuffExpireCallbackFunction expireCallbackFunc);
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,MonsterBuffExpireCallbackFunction expireCallbackFunc);
void Update(AiL*game,float fElapsedTime);
private:

@ -329,7 +329,14 @@ bool Monster::Update(float fElapsedTime){
if(IsAlive()){
std::for_each(buffList.begin(),buffList.end(),[&](Buff&b){b.Update(game,fElapsedTime);});
std::erase_if(buffList,[](Buff&b){return b.duration<=0;});
std::erase_if(buffList,[](Buff&b){
if(b.duration<=0.f){
if(!std::holds_alternative<std::weak_ptr<Monster>>(b.attachedTarget))ERR(std::format("WARNING! Somehow removed buff of type {} is inside the player buff list but is not attached to the Player? THIS SHOULD NOT BE HAPPENING!",int(b.type)));
b.monsterBuffCallbackFunc(std::get<std::weak_ptr<Monster>>(b.attachedTarget),b);
return true;
}
return false;
});
if(!HasIframes()){
for(std::shared_ptr<Monster>&m:MONSTER_LIST){
const float monsterRadius{GetCollisionRadius()};
@ -753,17 +760,17 @@ const bool Monster::OnUpperLevel()const{
}
void Monster::AddBuff(BuffType type,float duration,float intensity){
buffList.push_back(Buff{this,type,duration,intensity});
buffList.push_back(Buff{GetWeakPointer(),type,duration,intensity});
}
void Monster::AddBuff(BuffType type,float duration,float intensity,std::set<ItemAttribute>attr){
if(type==STAT_UP)std::for_each(attr.begin(),attr.end(),[](const ItemAttribute&attr){if(attr.ActualName()!="Health"&&attr.ActualName()!="Health %"&&attr.ActualName()!="Attack"&&attr.ActualName()!="Attack %")ERR(std::format("WARNING! Stat Up Attribute type {} is NOT IMPLEMENTED!",attr.ActualName()));});
buffList.emplace_back(this,type,duration,intensity,attr);
buffList.emplace_back(GetWeakPointer(),type,duration,intensity,attr);
}
void Monster::AddBuff(BuffType type,float duration,float intensity,std::set<std::string>attr){
if(type==STAT_UP)std::for_each(attr.begin(),attr.end(),[](const std::string&attr){if(attr!="Health"&&attr!="Health %"&&attr!="Attack"&&attr!="Attack %")ERR(std::format("WARNING! Stat Up Attribute type {} is NOT IMPLEMENTED!",attr));});
buffList.emplace_back(this,type,duration,intensity,attr);
buffList.emplace_back(GetWeakPointer(),type,duration,intensity,attr);
}
void Monster::RemoveBuff(BuffType type){
@ -1297,7 +1304,7 @@ const bool Monster::InUndamageableState(const bool onUpperLevel,const float z)co
}
void Monster::AddBuff(BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks){
buffList.push_back(Buff{this,type,overTimeType,duration,intensity,timeBetweenTicks});
buffList.push_back(Buff{GetWeakPointer(),type,overTimeType,duration,intensity,timeBetweenTicks});
}
const bool Monster::CanMove()const{
@ -1305,5 +1312,13 @@ const bool Monster::CanMove()const{
}
const std::weak_ptr<Monster>Monster::GetWeakPointer()const{
return *std::find_if(MONSTER_LIST.begin(),MONSTER_LIST.end(),[&](const std::shared_ptr<Monster>&ptr){return &*ptr==this;});
return weakPtr;
}
void Monster::ApplyDot(float duration,int damage,float timeBetweenTicks){
AddBuff(BuffRestorationType::OVER_TIME,BuffOverTimeType::HP_DAMAGE_OVER_TIME,duration,damage,timeBetweenTicks);
}
void Monster::SetWeakPointer(std::shared_ptr<Monster>&sharedMonsterPtr){
weakPtr=sharedMonsterPtr;
}

@ -208,6 +208,7 @@ public:
const bool InUndamageableState(const bool onUpperLevel,const float z)const;
const bool CanMove()const;
const std::weak_ptr<Monster>GetWeakPointer()const;
void ApplyDot(float duration,int damage,float timeBetweenTicks);
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.
@ -291,6 +292,8 @@ private:
bool _Hurt(int damage,bool onUpperLevel,float z,const TrueDamageFlag damageRule,HurtFlag::HurtFlag hurtFlags);
void RemoveMarkStack();
float markApplicationTimer{}; //Used for animations involving the mark being applied to the target.
void SetWeakPointer(std::shared_ptr<Monster>&sharedMonsterPtr);
std::weak_ptr<Monster>weakPtr;
private:
struct STRATEGY{
static std::string ERR;

@ -388,7 +388,14 @@ void Player::Update(float fElapsedTime){
RestoreMana(1,true);
}
std::for_each(buffList.begin(),buffList.end(),[&](Buff&b){b.Update(game,fElapsedTime);});
std::erase_if(buffList,[](Buff&b){return b.duration<=0;});
std::erase_if(buffList,[](Buff&b){
if(b.duration<=0.f){
if(!std::holds_alternative<Player*>(b.attachedTarget))ERR(std::format("WARNING! Somehow removed buff of type {} is inside the player buff list but is not attached to the Player? THIS SHOULD NOT BE HAPPENING!",int(b.type)));
b.playerBuffCallbackFunc(std::get<Player*>(b.attachedTarget),b);
return true;
}
return false;
});
//Class-specific update events.
OnUpdate(fElapsedTime);
switch(state){
@ -1822,4 +1829,8 @@ void Player::SetupAfterImage(){
afterImage.Decal()->Update();
removeLineTimer=TIME_BETWEEN_LINE_REMOVALS;
scanLine=1U;
}
void Player::ApplyDot(float duration,int damage,float timeBetweenTicks){
AddBuff(BuffRestorationType::OVER_TIME,BuffOverTimeType::HP_DAMAGE_OVER_TIME,duration,damage,timeBetweenTicks);
}

@ -290,6 +290,7 @@ public:
const bool IsUsingAdditiveBlending()const;
vf2d MoveUsingPathfinding(vf2d targetPos);
const std::unordered_set<std::string>&GetMyClass()const;
void ApplyDot(float duration,int damage,float timeBetweenTicks);
private:
int hp="Warrior.BaseHealth"_I;
int mana="Player.BaseMana"_I;

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

@ -122,13 +122,18 @@ void Witch::InitializeClassAbilities(){
p->SetAnimationBasedOnTargetingDirection("TRANSFORM",p->transformTargetDir);
p->ApplyIframes("Witch.Right Click Ability.Leap Max Range Time"_F+0.1f);
p->SetState(State::LEAP);
SoundEffect::PlaySFX("Meow",p->GetPos());
return true;
};
#pragma endregion
#pragma region Witch Ability 1 (???)
#pragma region Witch Ability 1 (Curse of Pain)
Witch::ability1.action=
[](Player*p,vf2d pos={}){
return false;
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()){
curseTarget.value().lock()->ApplyDot("Witch.Ability 1.Curse Debuff"_f[2],p->GetAttack()*"Witch.Ability 1.Curse Debuff"_f[0],"Witch.Ability 1.Curse Debuff"_f[1]);
}
return true;
};
#pragma endregion
#pragma region Witch Ability 2 (???)

@ -203,6 +203,12 @@ Events
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = menu_navigate.ogg, 0%
}
Meow
{
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = kitten_mew.ogg, 70%
}
Monster Hurt
{
CombatSound = True

Loading…
Cancel
Save