Tidied up monster drawing functions with portable lambdas to get rid of some copy-pasted code, making draw manipulation simpler. Implement Curse of Death ability. Release Build 10391.

mac-build
sigonasr2 6 months ago
parent 19f3483b44
commit c36f783fc9
  1. 1
      Adventures in Lestoria/Buff.h
  2. 36
      Adventures in Lestoria/Monster.cpp
  3. 1
      Adventures in Lestoria/Monster.h
  4. 11
      Adventures in Lestoria/Player.cpp
  5. 1
      Adventures in Lestoria/Player.h
  6. 2
      Adventures in Lestoria/Version.h
  7. 19
      Adventures in Lestoria/Witch.cpp
  8. 2
      Adventures in Lestoria/assets/config/audio/events.txt
  9. 3
      Adventures in Lestoria/assets/config/classes/Witch.txt
  10. BIN
      x64/Release/Adventures in Lestoria.exe

@ -57,6 +57,7 @@ enum BuffType{
OVER_TIME_DURING_CAST,
GLOW_PURPLE,
COLOR_MOD,
DAMAGE_AMPLIFICATION, //Multiplies all incoming damage by this amount.
};
enum class BuffRestorationType{
ONE_OFF,

@ -442,7 +442,8 @@ void Monster::Draw()const{
if(GetBuffs(BuffType::GLOW_PURPLE).size()>0)glowPurpleBuff=GetBuffs(BuffType::GLOW_PURPLE)[0];
if(GetBuffs(BuffType::COLOR_MOD).size()>0)blendCol=Pixel{uint8_t(PixelRaw(GetBuffs(BuffType::COLOR_MOD)[0].intensity).r*abs(sin(1.4*GetBuffs(BuffType::COLOR_MOD)[0].duration))),uint8_t(PixelRaw(GetBuffs(BuffType::COLOR_MOD)[0].intensity).g*abs(sin(1.4*GetBuffs(BuffType::COLOR_MOD)[0].duration))),uint8_t(PixelRaw(GetBuffs(BuffType::COLOR_MOD)[0].intensity).b*abs(sin(1.4*GetBuffs(BuffType::COLOR_MOD)[0].duration)))};
if(GetBuffs(BuffType::DAMAGE_AMPLIFICATION).size()>0)blendCol=Pixel{uint8_t(128+127*abs(sin(1.4*PI*GetBuffs(BuffType::DAMAGE_AMPLIFICATION)[0].duration))),uint8_t(0*abs(sin(1.4*PI*GetBuffs(BuffType::DAMAGE_AMPLIFICATION)[0].duration))),uint8_t(0*abs(sin(1.4*PI*GetBuffs(BuffType::DAMAGE_AMPLIFICATION)[0].duration)))};
else if(GetBuffs(BuffType::COLOR_MOD).size()>0)blendCol=Pixel{uint8_t(PixelRaw(GetBuffs(BuffType::COLOR_MOD)[0].intensity).r*abs(sin(1.4*GetBuffs(BuffType::COLOR_MOD)[0].duration))),uint8_t(PixelRaw(GetBuffs(BuffType::COLOR_MOD)[0].intensity).g*abs(sin(1.4*GetBuffs(BuffType::COLOR_MOD)[0].duration))),uint8_t(PixelRaw(GetBuffs(BuffType::COLOR_MOD)[0].intensity).b*abs(sin(1.4*GetBuffs(BuffType::COLOR_MOD)[0].duration)))};
else if(GetBuffs(BuffType::SLOWDOWN).size()>0)blendCol=Pixel{uint8_t(255*abs(sin(1.4*GetBuffs(BuffType::SLOWDOWN)[0].duration))),uint8_t(255*abs(sin(1.4*GetBuffs(BuffType::SLOWDOWN)[0].duration))),uint8_t(128+127*abs(sin(1.4*GetBuffs(BuffType::SLOWDOWN)[0].duration)))};
else if(glowPurpleBuff.has_value())blendCol=Pixel{uint8_t(255*abs(sin(1.4*glowPurpleBuff.value().get().duration))),uint8_t(255*abs(sin(1.4*glowPurpleBuff.value().get().duration))),uint8_t(128+127*abs(sin(1.4*glowPurpleBuff.value().get().duration)))};
@ -473,15 +474,25 @@ void Monster::Draw()const{
const vf2d imageScale{vf2d(GetSizeMult()*(!HasFourWaySprites()&&GetFacingDirection()==Direction::EAST?-1:1),GetSizeMult())};
const vf2d glowPurpleImageScale{imageScale*1.1f};
if(glowPurpleBuff.has_value())game->view.DrawPartialRotatedDecal(drawPos,GetFrame().GetSourceImage()->Decal(),finalSpriteRot,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,glowPurpleImageScale,{43,0,66,blendCol.a});
game->view.DrawPartialRotatedDecal(drawPos,GetFrame().GetSourceImage()->Decal(),finalSpriteRot,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,imageScale,blendCol);
const auto DrawBaseMonster=[&](vf2d scale={1.f,1.f},Pixel col=WHITE){
game->view.DrawPartialRotatedDecal(drawPos,GetFrame().GetSourceImage()->Decal(),finalSpriteRot,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,scale,col);
};
const auto DrawOverlayMonster=[&](vf2d scale={1.f,1.f},Pixel col=WHITE){
game->view.DrawPartialRotatedDecal(drawPos,GFX[overlaySprite].Decal(),finalSpriteRot,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,scale,col);
};
const auto DrawMountedMonster=[&](vf2d scale={1.f,1.f},Pixel col=WHITE){
game->view.DrawPartialRotatedDecal(drawPos+mountedSprOffset,GetMountedFrame().value().GetSourceImage()->Decal(),finalSpriteRot,GetMountedFrame().value().GetSourceRect().size/2,GetMountedFrame().value().GetSourceRect().pos,GetMountedFrame().value().GetSourceRect().size,scale,col);
};
if(glowPurpleBuff.has_value())DrawBaseMonster(glowPurpleImageScale,{43,0,66,blendCol.a});
DrawBaseMonster(imageScale,blendCol);
if(overlaySprite.length()!=0){
if(glowPurpleBuff.has_value())game->view.DrawPartialRotatedDecal(drawPos,GFX[overlaySprite].Decal(),finalSpriteRot,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,glowPurpleImageScale,{43,0,66,overlaySpriteTransparency});
game->view.DrawPartialRotatedDecal(drawPos,GFX[overlaySprite].Decal(),finalSpriteRot,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,imageScale,{blendCol.r,blendCol.g,blendCol.b,overlaySpriteTransparency});
if(glowPurpleBuff.has_value())DrawOverlayMonster(imageScale,{43,0,66,overlaySpriteTransparency});
DrawOverlayMonster(imageScale,{blendCol.r,blendCol.g,blendCol.b,overlaySpriteTransparency});
}
if(HasMountedMonster()){
if(glowPurpleBuff.has_value())game->view.DrawPartialRotatedDecal(drawPos+mountedSprOffset,GetMountedFrame().value().GetSourceImage()->Decal(),finalSpriteRot,GetMountedFrame().value().GetSourceRect().size/2,GetMountedFrame().value().GetSourceRect().pos,GetMountedFrame().value().GetSourceRect().size,glowPurpleImageScale,{43,0,66,blendCol.a});
game->view.DrawPartialRotatedDecal(drawPos+mountedSprOffset,GetMountedFrame().value().GetSourceImage()->Decal(),finalSpriteRot,GetMountedFrame().value().GetSourceRect().size/2,GetMountedFrame().value().GetSourceRect().pos,GetMountedFrame().value().GetSourceRect().size,imageScale,blendCol);
if(glowPurpleBuff.has_value())DrawMountedMonster(imageScale,{43,0,66,blendCol.a});
DrawMountedMonster(imageScale,blendCol);
}
std::vector<Buff>shieldBuffs=GetBuffs(BARRIER_DAMAGE_REDUCTION);
@ -681,6 +692,8 @@ bool Monster::_Hurt(int damage,bool onUpperLevel,float z,const TrueDamageFlag da
RemoveMarkStack();
}
mod_dmg*=GetDamageAmplificationMult();
mod_dmg=std::ceil(mod_dmg);
hp=std::max(0,hp-int(mod_dmg));
@ -1342,3 +1355,12 @@ void Monster::ApplyDot(float duration,int damage,float timeBetweenTicks,Buff::Mo
void Monster::SetWeakPointer(std::shared_ptr<Monster>&sharedMonsterPtr){
weakPtr=sharedMonsterPtr;
}
const float Monster::GetDamageAmplificationMult()const{
float damageAmpMult{1.f};
const std::vector<Buff>&buffList{GetBuffs(BuffType::DAMAGE_AMPLIFICATION)};
for(const Buff&buff:buffList){
damageAmpMult+=buff.intensity;
}
return damageAmpMult;
}

@ -210,6 +210,7 @@ public:
const bool CanMove()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){});
const float GetDamageAmplificationMult()const;
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.

@ -895,6 +895,8 @@ bool Player::Hurt(int damage,bool onUpperLevel,float z,const TrueDamageFlag dama
}
}
mod_dmg*=GetDamageAmplificationMult();
mod_dmg=std::ceil(mod_dmg);
if(PlayHitSoundEffect)SoundEffect::PlaySFX("Player Hit",SoundEffect::CENTERED);
@ -1837,3 +1839,12 @@ void Player::SetupAfterImage(){
void Player::ApplyDot(float duration,int damage,float timeBetweenTicks,Buff::PlayerBuffExpireCallbackFunction expireCallbackFunc){
AddBuff(BuffRestorationType::OVER_TIME,BuffOverTimeType::HP_DAMAGE_OVER_TIME,duration,damage,timeBetweenTicks,expireCallbackFunc);
}
const float Player::GetDamageAmplificationMult()const{
float damageAmpMult{1.f};
const std::vector<Buff>&buffList{GetBuffs(BuffType::DAMAGE_AMPLIFICATION)};
for(const Buff&buff:buffList){
damageAmpMult+=buff.intensity;
}
return damageAmpMult;
}

@ -292,6 +292,7 @@ public:
vf2d MoveUsingPathfinding(vf2d targetPos);
const std::unordered_set<std::string>&GetMyClass()const;
void ApplyDot(float duration,int damage,float timeBetweenTicks,Buff::PlayerBuffExpireCallbackFunction expireCallbackFunc=[](Player*p,Buff&b){});
const float GetDamageAmplificationMult()const;
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 10390
#define VERSION_BUILD 10391
#define stringify(a) stringify_(a)
#define stringify_(a) #a

@ -164,10 +164,25 @@ void Witch::InitializeClassAbilities(){
return true;
};
#pragma endregion
#pragma region Witch Ability 3 (???)
#pragma region Witch Ability 3 (Curse of Death)
Witch::ability3.action=
[](Player*p,vf2d pos={}){
return false;
std::optional<std::weak_ptr<Monster>>curseTarget{Monster::GetNearestMonster(pos,"Witch.Ability 3.Casting Range"_F/100.f*24,p->OnUpperLevel(),p->GetZ())};
if(curseTarget.has_value()&&!curseTarget.value().expired()){
const float curseDuration{"Witch.Ability 3.Curse Duration"_F};
const float damageAmpMult{"Witch.Ability 3.Damage Amplification"_F/100.f};
curseTarget.value().lock()->AddBuff(BuffType::DAMAGE_AMPLIFICATION,curseDuration,damageAmpMult);
const vf2d targetPos{curseTarget.value().lock()->GetPos()};
for(int i:std::ranges::iota_view(0,int(util::distance(p->GetPos(),targetPos)/8))){
float drawDist{i*8.f};
float fadeInTime{i*0.05f};
float fadeOutTime{0.5f+i*0.05f};
float effectSize{util::random_range(0.6f,1.2f)};
game->AddEffect(std::make_unique<Effect>(geom2d::line<float>(p->GetPos(),targetPos).rpoint(drawDist),0.f,"mark_trail.png",p->OnUpperLevel(),fadeInTime,fadeOutTime,vf2d{effectSize,effectSize},vf2d{},Pixel{162,0,0,uint8_t(util::random_range(150,255))},0.f,0.f),true);
}
SoundEffect::PlaySFX("Curse of Death",curseTarget.value().lock()->GetPos());
return true;
}else return false;
};
#pragma endregion
}

@ -84,7 +84,7 @@ Events
{
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = curse_of_death.ogg, 80%
File[0] = curse_of_death.ogg, 100%
}
Curse of Pain
{

@ -117,7 +117,8 @@ Witch
# Whether or not this ability cancels casts.
CancelCast = 0
Damage Amplification = 2x
# How much extra damage increase this ability provides to all incoming sources of damage.
Damage Amplification = 100%
Curse Duration = 7s
#RGB Values. Color 1 is the circle at full cooldown, Color 2 is the color at empty cooldown.

Loading…
Cancel
Save