Add visual indicator/graphics for Trapper Mark on targets. Added TriggerMark and ApplyMark helper functions. Release Build 10264.

This commit is contained in:
sigonasr2 2024-07-22 12:53:47 -05:00
parent a1eb109e70
commit c3e7c62bd3
9 changed files with 60 additions and 5 deletions

View File

@ -403,7 +403,7 @@ namespace MonsterTests
Assert::AreEqual(0,testMonster.GetMarkStacks(),L"Monster has 0 marks initially.");
testMonster.AddBuff(BuffType::TRAPPER_MARK,7.f,5); //Apply 5 marks of Trapper Mark.
testMonster.ApplyMark(7.f,5U);
Assert::AreEqual(5,testMonster.GetMarkStacks(),L"Monster has 5 marks after receiving a buff.");
@ -438,6 +438,11 @@ namespace MonsterTests
Assert::AreEqual(1,testMonster.GetMarkStacks(),L"Monster should have 1 mark remaining after using TriggerMark function");
Assert::AreEqual(24,testMonster.GetHealth(),L"Monster should not take damage from the TriggerMark function (Mark deals 6 damage though).");
game->SetElapsedTime(10.f);
testMonster.Update(10.f);
Assert::AreEqual(0,testMonster.GetMarkStacks(),L"The marks should have expired after 10 seconds.");
}
};
}

View File

@ -332,6 +332,15 @@ void sig::Animation::InitializeAnimations(){
ANIMATION_DATA[std::format("GOBLIN_BOW_ATTACK_{}",animationIndex)]=mountShootAnimation;
}
#pragma region Trapper Target Mark Debuff
AnimationData targetAnimData{.frameDuration{0.1f},.style{Animate2D::Style::Repeat}};
Animate2D::FrameSequence targetAnim(targetAnimData.frameDuration,targetAnimData.style);
targetAnim.AddFrame({&GFX["target.png"],{{int(0*24),0},{24,24}}});
targetAnim.AddFrame({&GFX["target.png"],{{int(0*24),0},{24,24}}});
targetAnim.AddFrame({&GFX["target.png"],{{int(1*24),0},{24,24}}});
ANIMATION_DATA["target.png"]=targetAnim;
#pragma endregion
for(auto&dat:GFX){
std::string imgFile=dat.first;
if(!ANIMATION_DATA.count(imgFile)){

View File

@ -265,8 +265,10 @@ bool Monster::Update(float fElapsedTime){
monsterHurtSoundCooldown=std::max(0.f,monsterHurtSoundCooldown-fElapsedTime);
lastHitPlayer=std::max(0.f,lastHitPlayer-fElapsedTime);
lastPathfindingCooldown=std::max(0.f,lastPathfindingCooldown-fElapsedTime);
markApplicationTimer=std::max(0.f,markApplicationTimer-fElapsedTime);
lastFacingDirectionChange+=fElapsedTime;
timeSpentAlive+=fElapsedTime;
if(IsSolid()){
if(GetPos().y>=game->GetPlayer()->GetPos().y)solidFadeTimer=std::min(TileGroup::FADE_TIME,solidFadeTimer+game->GetElapsedTime());
else solidFadeTimer=std::max(0.f,solidFadeTimer-game->GetElapsedTime());
@ -467,9 +469,29 @@ void Monster::Draw()const{
std::vector<Buff>shieldBuffs=GetBuffs(BARRIER_DAMAGE_REDUCTION);
if(shieldBuffs.size()>0){
game->view.DrawRotatedDecal(drawPos,GFX["block.png"].Decal(),0.f,GFX["block.png"].Sprite()->Size()/2,{GetSizeMult(),GetSizeMult()});
game->view.DrawRotatedDecal(drawPos,GFX["block.png"].Decal(),0.f,GFX["block.png"].Sprite()->Size()/2,{GetSizeMult(),GetSizeMult()},blendCol);
}
#pragma region Render Trapper Marked Targets
const uint8_t markStackCount{GetMarkStacks()};
if(markStackCount>0){
float markRotation{-util::lerp(0.f,10.f,markApplicationTimer/0.5f)*sin(PI*markApplicationTimer)};
vf2d markScale{vf2d{}.lerp(vf2d{GetSizeMult(),GetSizeMult()},(0.5f-markApplicationTimer)/0.5f)};
const Animate2D::Frame&markImg{ANIMATION_DATA["target.png"].GetFrame(game->GetRunTime())};
Pixel markCol{markStackCount>1?WHITE:RED};
const std::vector<Buff>&buffList{GetBuffs(BuffType::TRAPPER_MARK)};
float remainingStackDuration{};
for(const Buff&b:buffList){
if(b.type==BuffType::TRAPPER_MARK){
remainingStackDuration=b.duration;
break;
}
}
if(remainingStackDuration<1.f)markCol.a*=remainingStackDuration;
game->view.DrawPartialRotatedDecal(drawPos,markImg.GetSourceImage()->Decal(),markRotation,markImg.GetSourceRect().size/2.f,markImg.GetSourceRect().pos,markImg.GetSourceRect().size,markScale,markCol);
}
#pragma endregion
if(GameSettings::TerrainCollisionBoxesEnabled()&&IsSolid()&&solidFadeTimer>0.f){
float distToPlayer=geom2d::line<float>(game->GetPlayer()->GetPos(),GetPos()).length();
const float collisionRadiusFactor=GetCollisionRadius()/12.f;
@ -1201,7 +1223,7 @@ const std::optional<geom2d::rect<float>>&Monster::GetRectangleCollision()const{
return MONSTER_DATA.at(GetName()).GetRectangleCollision();
}
const int Monster::GetMarkStacks()const{
const uint8_t Monster::GetMarkStacks()const{
const std::vector<Buff>&markBuffs{GetBuffs(BuffType::TRAPPER_MARK)};
int stackCount{};
for(const Buff&b:markBuffs){
@ -1212,14 +1234,30 @@ const int Monster::GetMarkStacks()const{
void Monster::RemoveMarkStack(){
if(GetMarkStacks()<=0)ERR("WARNING! Tried to remove a mark stack, but no stacks exist. THIS SHOULD NOT BE HAPPENING!");
bool removeMarkDebuff{false};
for(Buff&b:buffList){
if(b.type==BuffType::TRAPPER_MARK&&b.intensity>0){
b.intensity--;
if(b.intensity==0)removeMarkDebuff=true;
break;
}
}
if(removeMarkDebuff)RemoveBuff(BuffType::TRAPPER_MARK);
}
void Monster::TriggerMark(){
Hurt(0,OnUpperLevel(),GetZ(),HurtFlag::PLAYER_ABILITY);
}
void Monster::ApplyMark(float time,uint8_t stackCount){
if(GetMarkStacks()>0){
for(Buff&b:buffList){
if(b.type==BuffType::TRAPPER_MARK){
b.intensity+=stackCount;
b.duration=std::max(b.duration,time);
break;
}
}
}else AddBuff(BuffType::TRAPPER_MARK,time,stackCount);
markApplicationTimer=0.5f;
}

View File

@ -199,8 +199,9 @@ public:
const float GetModdedStatBonuses(std::string_view stat)const;
//The collision rectangle is only used currently for determining the safe spots for the stone golem boss fight.
const std::optional<geom2d::rect<float>>&GetRectangleCollision()const;
const int GetMarkStacks()const; //Number of Trapper marks on this target.
const uint8_t GetMarkStacks()const; //Number of Trapper marks on this target.
void TriggerMark(); //Deals no damage, but causes a mark proc to occur.
void ApplyMark(float time,uint8_t stackCount); //Adds stackCount mark stacks to the target, refreshing the buff to time time.
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.
@ -283,6 +284,7 @@ private:
float solidFadeTimer{0.f};
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.
private:
struct STRATEGY{
static std::string ERR;

View File

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

View File

@ -109,6 +109,7 @@ Images
GFX_LargeRock = large_rock.png
GFX_Dagger = dagger.png
GFX_Shine = shine.png
GFX_TargetMark = target.png
GFX_Thief_Sheet = nico-thief.png
GFX_Trapper_Sheet = nico-trapper.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B