diff --git a/Adventures in Lestoria/Buff.h b/Adventures in Lestoria/Buff.h index b9d8317e..a12ee519 100644 --- a/Adventures in Lestoria/Buff.h +++ b/Adventures in Lestoria/Buff.h @@ -55,6 +55,7 @@ enum BuffType{ OVER_TIME, ONE_OFF, OVER_TIME_DURING_CAST, + GLOW_PURPLE, }; enum class BuffRestorationType{ ONE_OFF, diff --git a/Adventures in Lestoria/Monster.cpp b/Adventures in Lestoria/Monster.cpp index 5babb617..24a5c003 100644 --- a/Adventures in Lestoria/Monster.cpp +++ b/Adventures in Lestoria/Monster.cpp @@ -436,7 +436,14 @@ void Monster::UpdateFacingDirection(vf2d facingTargetPoint){ } void Monster::Draw()const{ if(markedForDeletion)return; - Pixel blendCol=GetBuffs(BuffType::SLOWDOWN).size()>0?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)))}:WHITE; + Pixel blendCol{WHITE}; + + std::optional>glowPurpleBuff; + + if(GetBuffs(BuffType::GLOW_PURPLE).size()>0)glowPurpleBuff=GetBuffs(BuffType::GLOW_PURPLE)[0]; + + 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)))}; const vf2d hitTimerOffset=vf2d{sin(20*PI*lastHitTimer+randomFrameOffset),0.f}*2.f*GetSizeMult(); const vf2d zOffset=-vf2d{0,GetZ()}; @@ -461,12 +468,20 @@ void Monster::Draw()const{ blendCol.a=blendColAlpha; const float finalSpriteRot=HasFourWaySprites()?0.f:spriteRot; //Prevent 4-way sprites from being rotated. + + const vf2d imageScale{vf2d(GetSizeMult()*(!HasFourWaySprites()&&GetFacingDirection()==Direction::EAST?-1:1),GetSizeMult())}; + const vf2d glowPurpleImageScale{imageScale*1.1f}; - game->view.DrawPartialRotatedDecal(drawPos,GetFrame().GetSourceImage()->Decal(),finalSpriteRot,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,vf2d(GetSizeMult()*(!HasFourWaySprites()&&GetFacingDirection()==Direction::EAST?-1:1),GetSizeMult()),blendCol); + 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); if(overlaySprite.length()!=0){ - game->view.DrawPartialRotatedDecal(drawPos,GFX[overlaySprite].Decal(),finalSpriteRot,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,vf2d(GetSizeMult()*(!HasFourWaySprites()&&GetFacingDirection()==Direction::EAST?-1:1),GetSizeMult()),{blendCol.r,blendCol.g,blendCol.b,overlaySpriteTransparency}); + 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(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(HasMountedMonster())game->view.DrawPartialRotatedDecal(drawPos+mountedSprOffset,GetMountedFrame().value().GetSourceImage()->Decal(),finalSpriteRot,GetMountedFrame().value().GetSourceRect().size/2,GetMountedFrame().value().GetSourceRect().pos,GetMountedFrame().value().GetSourceRect().size,vf2d(GetSizeMult()*(!HasFourWaySprites()&&GetFacingDirection()==Direction::EAST?-1:1),GetSizeMult()),blendCol); std::vectorshieldBuffs=GetBuffs(BARRIER_DAMAGE_REDUCTION); if(shieldBuffs.size()>0){ @@ -1307,6 +1322,10 @@ void Monster::AddBuff(BuffRestorationType type,BuffOverTimeType::BuffOverTimeTyp buffList.push_back(Buff{GetWeakPointer(),type,overTimeType,duration,intensity,timeBetweenTicks}); } +void Monster::AddBuff(BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,Buff::MonsterBuffExpireCallbackFunction expireCallback){ + buffList.push_back(Buff{GetWeakPointer(),type,overTimeType,duration,intensity,timeBetweenTicks,expireCallback}); +} + const bool Monster::CanMove()const{ return knockUpTimer==0.f&&IsAlive(); } @@ -1315,8 +1334,8 @@ const std::weak_ptrMonster::GetWeakPointer()const{ 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::ApplyDot(float duration,int damage,float timeBetweenTicks,Buff::MonsterBuffExpireCallbackFunction expireCallbackFunc){ + AddBuff(BuffRestorationType::OVER_TIME,BuffOverTimeType::HP_DAMAGE_OVER_TIME,duration,damage,timeBetweenTicks,expireCallbackFunc); } void Monster::SetWeakPointer(std::shared_ptr&sharedMonsterPtr){ diff --git a/Adventures in Lestoria/Monster.h b/Adventures in Lestoria/Monster.h index 3dbd2189..a8ea8daa 100644 --- a/Adventures in Lestoria/Monster.h +++ b/Adventures in Lestoria/Monster.h @@ -141,6 +141,7 @@ public: //NOTE: If adding a % increase stat, please use the percentage version! 100% = 1!! void AddBuff(BuffType type,float duration,float intensity,std::setattr); void AddBuff(BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks); + void AddBuff(BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,Buff::MonsterBuffExpireCallbackFunction expireCallback); std::vectorGetBuffs(BuffType buff)const; //Removes all buffs of a given type. void RemoveBuff(BuffType type); @@ -208,7 +209,7 @@ public: const bool InUndamageableState(const bool onUpperLevel,const float z)const; const bool CanMove()const; const std::weak_ptrGetWeakPointer()const; - void ApplyDot(float duration,int damage,float timeBetweenTicks); + void ApplyDot(float duration,int damage,float timeBetweenTicks,Buff::MonsterBuffExpireCallbackFunction expireCallbackFunc=[](std::weak_ptrm,Buff&b){}); 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. diff --git a/Adventures in Lestoria/Player.cpp b/Adventures in Lestoria/Player.cpp index 0aae268b..a8115815 100644 --- a/Adventures in Lestoria/Player.cpp +++ b/Adventures in Lestoria/Player.cpp @@ -1082,6 +1082,9 @@ void Player::AddBuff(BuffType type,float duration,float intensity,std::setattr); void AddBuff(BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks); + void AddBuff(BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,Buff::PlayerBuffExpireCallbackFunction expireCallbackFunc); const std::vectorGetBuffs(BuffType buff)const; const std::vectorGetStatBuffs(const std::vector&attr)const; @@ -290,7 +291,7 @@ public: const bool IsUsingAdditiveBlending()const; vf2d MoveUsingPathfinding(vf2d targetPos); const std::unordered_set&GetMyClass()const; - void ApplyDot(float duration,int damage,float timeBetweenTicks); + void ApplyDot(float duration,int damage,float timeBetweenTicks,Buff::PlayerBuffExpireCallbackFunction expireCallbackFunc=[](Player*p,Buff&b){}); private: int hp="Warrior.BaseHealth"_I; int mana="Player.BaseMana"_I; diff --git a/Adventures in Lestoria/Trapper.cpp b/Adventures in Lestoria/Trapper.cpp index 6d4ca9ca..dfd49dcb 100644 --- a/Adventures in Lestoria/Trapper.cpp +++ b/Adventures in Lestoria/Trapper.cpp @@ -101,15 +101,16 @@ void Trapper::InitializeClassAbilities(){ if(nearestMonster.has_value()){ targetPos=nearestMonster.value().lock()->GetPos(); nearestMonster.value().lock()->ApplyMark("Trapper.Ability 1.Duration"_F,"Trapper.Ability 1.Stack Count"_I); + for(int i:std::ranges::iota_view(0,int(util::distance(p->GetPos(),targetPos)/16))){ + float drawDist{i*16.f}; + float fadeInTime{i*0.05f}; + float fadeOutTime{0.5f+i*0.05f}; + float effectSize{util::random(0.4f)}; + game->AddEffect(std::make_unique(geom2d::line(p->GetPos(),targetPos).rpoint(drawDist),0.f,"mark_trail.png",p->OnUpperLevel(),fadeInTime,fadeOutTime,vf2d{effectSize,effectSize},vf2d{},Pixel{255,255,255,uint8_t(util::random_range(60,150))},0.f,0.f,true),true); + } + return true; } - for(int i:std::ranges::iota_view(0,int(util::distance(p->GetPos(),targetPos)/16))){ - float drawDist{i*16.f}; - float fadeInTime{i*0.05f}; - float fadeOutTime{0.5f+i*0.05f}; - float effectSize{util::random(0.4f)}; - game->AddEffect(std::make_unique(geom2d::line(p->GetPos(),targetPos).rpoint(drawDist),0.f,"mark_trail.png",p->OnUpperLevel(),fadeInTime,fadeOutTime,vf2d{effectSize,effectSize},vf2d{},Pixel{255,255,255,uint8_t(util::random_range(60,150))},0.f,0.f,true),true); - } - return true; + return false; }; #pragma endregion #pragma region Trapper Ability 2 (Bear Trap) diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index c5a20553..080a41f5 100644 --- a/Adventures in Lestoria/Version.h +++ b/Adventures in Lestoria/Version.h @@ -39,7 +39,7 @@ All rights reserved. #define VERSION_MAJOR 1 #define VERSION_MINOR 2 #define VERSION_PATCH 3 -#define VERSION_BUILD 10359 +#define VERSION_BUILD 10363 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Adventures in Lestoria/Witch.cpp b/Adventures in Lestoria/Witch.cpp index 34281ee5..47c3c45d 100644 --- a/Adventures in Lestoria/Witch.cpp +++ b/Adventures in Lestoria/Witch.cpp @@ -44,6 +44,7 @@ All rights reserved. #include "SoundEffect.h" #include "BulletTypes.h" #include "util.h" +#include INCLUDE_MONSTER_LIST INCLUDE_BULLET_LIST @@ -131,9 +132,26 @@ void Witch::InitializeClassAbilities(){ [](Player*p,vf2d pos={}){ std::optional>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; + 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]}; + curseTarget.value().lock()->ApplyDot(buffDuration,p->GetAttack()*buffDamageMult,buffTimeBetweenTicks, + [](std::weak_ptrm,Buff&b){ + expireCallbackFunc: + m.lock()->Hurt(game->GetPlayer()->GetAttack()*"Witch.Ability 1.Final Tick Damage"_F,m.lock()->OnUpperLevel(),m.lock()->GetZ(),HurtFlag::DOT); + } + ); + curseTarget.value().lock()->AddBuff(BuffType::GLOW_PURPLE,buffDuration,1.f); + 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(0.2f)}; + game->AddEffect(std::make_unique(geom2d::line(p->GetPos(),targetPos).rpoint(drawDist),0.f,"mark_trail.png",p->OnUpperLevel(),fadeInTime,fadeOutTime,vf2d{effectSize,effectSize},vf2d{},Pixel{100,0,155,uint8_t(util::random_range(0,120))},0.f,0.f),true); + } + return true; + }else return false; }; #pragma endregion #pragma region Witch Ability 2 (???) diff --git a/x64/Release/Adventures in Lestoria.exe b/x64/Release/Adventures in Lestoria.exe index 9a5f555d..5c37d572 100644 Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ