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

removeExposedPackKey
sigonasr2 6 months ago
parent a1eb109e70
commit c3e7c62bd3
  1. 7
      Adventures in Lestoria Tests/MonsterTests.cpp
  2. 9
      Adventures in Lestoria/Animation.cpp
  3. 42
      Adventures in Lestoria/Monster.cpp
  4. 4
      Adventures in Lestoria/Monster.h
  5. 2
      Adventures in Lestoria/Version.h
  6. 1
      Adventures in Lestoria/assets/config/gfx/gfx.txt
  7. BIN
      Adventures in Lestoria/assets/gamepack.pak
  8. BIN
      Adventures in Lestoria/assets/target.png
  9. BIN
      x64/Release/Adventures in Lestoria.exe

@ -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.");
}
};
}

@ -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)){

@ -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,8 +469,28 @@ 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();
@ -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;
}

@ -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;

@ -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

@ -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

Loading…
Cancel
Save