Switch controller auto-targeting to use logic from Hurt function. This means the auto targeting tries to ensure it hits some target, but prefers a currently vulnerable target over an invulnerable / unhittable one. Cleanup the code to use std::optional. Add in a helper function to get nearest monster. Apply Mark to nearest selected target when Trapper Mark Target ability is used. Release Build 10275.

This commit is contained in:
sigonasr2 2024-07-23 00:09:28 -05:00
parent c3e7c62bd3
commit 345a4abb48
15 changed files with 127 additions and 46 deletions

View File

@ -44,22 +44,32 @@ INCLUDE_ANIMATION_DATA
INCLUDE_game INCLUDE_game
Effect::Effect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,float size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending) Effect::Effect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,float size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending)
:Effect::Effect(pos,lifetime,imgFile,upperLevel,vf2d{size,size},fadeout,spd,col,rotation,rotationSpd,additiveBlending){ :Effect::Effect(pos,lifetime,imgFile,upperLevel,0.f,fadeout,vf2d{size,size},spd,col,rotation,rotationSpd,additiveBlending){
this->animation.AddState(imgFile,ANIMATION_DATA.at(imgFile)); this->animation.AddState(imgFile,ANIMATION_DATA.at(imgFile));
} }
Effect::Effect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,vf2d size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending) Effect::Effect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,vf2d size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending)
:pos(pos),lifetime(lifetime),upperLevel(upperLevel),size(size),fadeout(fadeout),original_fadeoutTime(fadeout),spd(spd),col(col),rotation(rotation),rotationSpd(rotationSpd),additiveBlending(additiveBlending){ :pos(pos),lifetime(lifetime),upperLevel(upperLevel),size(size),original_fadeInTime(fadein),fadeout(fadeout),original_fadeOutTime(fadeout),spd(spd),col(col),rotation(rotation),rotationSpd(rotationSpd),additiveBlending(additiveBlending){
this->animation.AddState(imgFile,ANIMATION_DATA.at(imgFile));
}
Effect::Effect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,float fadein,float fadeout,vf2d size,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending)
:pos(pos),lifetime(lifetime),upperLevel(upperLevel),size(size),fadein(fadein),original_fadeInTime(fadein),fadeout(fadeout),original_fadeOutTime(fadeout),spd(spd),col(col),rotation(rotation),rotationSpd(rotationSpd),additiveBlending(additiveBlending){
this->animation.AddState(imgFile,ANIMATION_DATA.at(imgFile)); this->animation.AddState(imgFile,ANIMATION_DATA.at(imgFile));
} }
bool Effect::Update(float fElapsedTime){ bool Effect::Update(float fElapsedTime){
lifetime-=fElapsedTime; aliveTime+=fElapsedTime;
if(lifetime<=0){ if(fadein<original_fadeInTime){
fadeout-=fElapsedTime; fadein=std::min(original_fadeInTime,fadein+fElapsedTime);
if(fadeout<=0){ }else{
dead=true; lifetime-=fElapsedTime;
return false; if(lifetime<=0){
fadeout-=fElapsedTime;
if(fadeout<=0){
dead=true;
return false;
}
} }
} }
rotation+=rotationSpd*fElapsedTime; rotation+=rotationSpd*fElapsedTime;
@ -70,10 +80,14 @@ bool Effect::Update(float fElapsedTime){
void Effect::Draw()const{ void Effect::Draw()const{
if(additiveBlending)game->SetDecalMode(DecalMode::ADDITIVE); if(additiveBlending)game->SetDecalMode(DecalMode::ADDITIVE);
if(fadeout==0){ if(fadeout==0&&fadein==original_fadeInTime){
game->view.DrawPartialRotatedDecal(pos,GetFrame().GetSourceImage()->Decal(),rotation,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,size,col); game->view.DrawPartialRotatedDecal(pos,GetFrame().GetSourceImage()->Decal(),rotation,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,size,col);
} else { }else
game->view.DrawPartialRotatedDecal(pos,GetFrame().GetSourceImage()->Decal(),rotation,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,size,{col.r,col.g,col.b,uint8_t(fadeout/original_fadeoutTime*col.a)}); if(fadein==original_fadeInTime){
game->view.DrawPartialRotatedDecal(pos,GetFrame().GetSourceImage()->Decal(),rotation,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,size,{col.r,col.g,col.b,uint8_t(fadein/original_fadeInTime*col.a)});
}else
if(fadeout==0){
game->view.DrawPartialRotatedDecal(pos,GetFrame().GetSourceImage()->Decal(),rotation,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,size,{col.r,col.g,col.b,uint8_t(fadeout/original_fadeOutTime*col.a)});
} }
game->SetDecalMode(DecalMode::NORMAL); game->SetDecalMode(DecalMode::NORMAL);
} }

View File

@ -54,6 +54,7 @@ struct Effect{
vf2d pos={0,0}; vf2d pos={0,0};
float lifetime=0; float lifetime=0;
float fadeout=0; float fadeout=0;
float fadein=0;
vf2d size={1,1}; vf2d size={1,1};
Pixel col=WHITE; Pixel col=WHITE;
vf2d spd={}; vf2d spd={};
@ -65,18 +66,21 @@ private:
public: public:
Effect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,float size=1.0f,float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false); Effect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,float size=1.0f,float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false);
Effect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,vf2d size={1,1},float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false); Effect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,vf2d size={1,1},float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false);
Effect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,float fadein,float fadeout,vf2d size,vf2d spd,Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false);
virtual bool Update(float fElapsedTime); virtual bool Update(float fElapsedTime);
Animate2D::Frame GetFrame()const; Animate2D::Frame GetFrame()const;
virtual void Draw()const; virtual void Draw()const;
bool OnUpperLevel(); bool OnUpperLevel();
const EffectType GetType()const; const EffectType GetType()const;
protected: protected:
float original_fadeoutTime; float original_fadeOutTime;
float original_fadeInTime;
EffectType type{EffectType::NONE}; EffectType type{EffectType::NONE};
private: private:
Animate2D::Animation<std::string>animation; Animate2D::Animation<std::string>animation;
Animate2D::AnimationState internal_animState; Animate2D::AnimationState internal_animState;
bool upperLevel=false; bool upperLevel=false;
double aliveTime{};
}; };
struct Meteor:Effect{ struct Meteor:Effect{

View File

@ -52,7 +52,7 @@ void ForegroundEffect::Draw()const{
if(fadeout==0){ if(fadeout==0){
game->DrawPartialRotatedDecal(pos,GetFrame().GetSourceImage()->Decal(),rotation,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,size,col); game->DrawPartialRotatedDecal(pos,GetFrame().GetSourceImage()->Decal(),rotation,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,size,col);
} else { } else {
game->DrawPartialRotatedDecal(pos,GetFrame().GetSourceImage()->Decal(),rotation,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,size,{col.r,col.g,col.b,uint8_t(fadeout/original_fadeoutTime*255)}); game->DrawPartialRotatedDecal(pos,GetFrame().GetSourceImage()->Decal(),rotation,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,size,{col.r,col.g,col.b,uint8_t(fadeout/original_fadeOutTime*255)});
} }
game->SetDecalMode(DecalMode::NORMAL); game->SetDecalMode(DecalMode::NORMAL);
} }

View File

@ -81,5 +81,5 @@ void Meteor::Draw()const{
if(lifetime<=0){ if(lifetime<=0){
meteorOffset=pos-vf2d{0,GetFrame().GetSourceRect().size.y/4.f}*size; meteorOffset=pos-vf2d{0,GetFrame().GetSourceRect().size.y/4.f}*size;
} }
game->view.DrawPartialRotatedDecal(meteorOffset,GetFrame().GetSourceImage()->Decal(),rotation,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,size,{col.r,col.g,col.b,uint8_t(fadeout/original_fadeoutTime*255)}); game->view.DrawPartialRotatedDecal(meteorOffset,GetFrame().GetSourceImage()->Decal(),rotation,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,size,{col.r,col.g,col.b,uint8_t(fadeout/original_fadeOutTime*255)});
} }

View File

@ -473,23 +473,23 @@ void Monster::Draw()const{
} }
#pragma region Render Trapper Marked Targets #pragma region Render Trapper Marked Targets
const uint8_t markStackCount{GetMarkStacks()}; const uint8_t markStackCount{GetMarkStacks()};
if(markStackCount>0){ if(markStackCount>0){
float markRotation{-util::lerp(0.f,10.f,markApplicationTimer/0.5f)*sin(PI*markApplicationTimer)}; 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)}; vf2d markScale{vf2d{}.lerp(vf2d{GetSizeMult(),GetSizeMult()},(0.5f-markApplicationTimer)/0.5f)};
const Animate2D::Frame&markImg{ANIMATION_DATA["target.png"].GetFrame(game->GetRunTime())}; const Animate2D::Frame&markImg{ANIMATION_DATA["target.png"].GetFrame(game->GetRunTime())};
Pixel markCol{markStackCount>1?WHITE:RED}; Pixel markCol{markStackCount>1?WHITE:RED};
const std::vector<Buff>&buffList{GetBuffs(BuffType::TRAPPER_MARK)}; const std::vector<Buff>&buffList{GetBuffs(BuffType::TRAPPER_MARK)};
float remainingStackDuration{}; float remainingStackDuration{};
for(const Buff&b:buffList){ for(const Buff&b:buffList){
if(b.type==BuffType::TRAPPER_MARK){ if(b.type==BuffType::TRAPPER_MARK){
remainingStackDuration=b.duration; remainingStackDuration=b.duration;
break; 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);
} }
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 #pragma endregion
if(GameSettings::TerrainCollisionBoxesEnabled()&&IsSolid()&&solidFadeTimer>0.f){ if(GameSettings::TerrainCollisionBoxesEnabled()&&IsSolid()&&solidFadeTimer>0.f){
@ -640,7 +640,7 @@ bool Monster::_Hurt(int damage,bool onUpperLevel,float z,const TrueDamageFlag da
const bool NormalDamageCalculationRequired{!IsDOT&&!TrueDamage}; const bool NormalDamageCalculationRequired{!IsDOT&&!TrueDamage};
const bool PlayHitSoundEffect{!IsDOT}; const bool PlayHitSoundEffect{!IsDOT};
if(!TrueDamage&&!IsDOT&&(Invulnerable()||!IsAlive()||onUpperLevel!=OnUpperLevel()||AttackAvoided(z)))return false; if(!TrueDamage&&!IsDOT&&InUndamageableState(onUpperLevel,z))return false;
if(game->InBossEncounter()){ if(game->InBossEncounter()){
game->StartBossEncounter(); game->StartBossEncounter();
} }
@ -674,6 +674,7 @@ bool Monster::_Hurt(int damage,bool onUpperLevel,float z,const TrueDamageFlag da
dotNumberPtr.get()->RecalculateSize(); dotNumberPtr.get()->RecalculateSize();
}else{ }else{
dotNumberPtr=std::make_shared<DamageNumber>(pos-vf2d{0,GetCollisionRadius()/2.f},int(mod_dmg),false,DamageNumberType::DOT); dotNumberPtr=std::make_shared<DamageNumber>(pos-vf2d{0,GetCollisionRadius()/2.f},int(mod_dmg),false,DamageNumberType::DOT);
dotNumberPtr->riseSpd=dotNumberPtr->originalRiseSpd=-10.f;
DAMAGENUMBER_LIST.push_back(dotNumberPtr); DAMAGENUMBER_LIST.push_back(dotNumberPtr);
} }
lastDotTimer=0.05f; lastDotTimer=0.05f;
@ -1260,4 +1261,38 @@ void Monster::ApplyMark(float time,uint8_t stackCount){
} }
}else AddBuff(BuffType::TRAPPER_MARK,time,stackCount); }else AddBuff(BuffType::TRAPPER_MARK,time,stackCount);
markApplicationTimer=0.5f; markApplicationTimer=0.5f;
}
std::optional<Monster*>Monster::GetNearestMonster(const vf2d point,const float maxDistance,const bool onUpperLevel,const float z){
std::optional<Monster*>closestMonster;
std::optional<Monster*>closestGenericMonster;
for(std::unique_ptr<Monster>&m:MONSTER_LIST){
geom2d::line<float>aimingLine=geom2d::line<float>(point,m->GetPos());
float distToMonster=aimingLine.length();
float distToClosestPoint,distToClosestGenericPoint;
if(closestMonster.has_value())distToClosestPoint=geom2d::line<float>(point,closestMonster.value()->GetPos()).length();
else distToClosestPoint=std::numeric_limits<float>::max();
if(closestGenericMonster.has_value())distToClosestGenericPoint=geom2d::line<float>(point,closestGenericMonster.value()->GetPos()).length();
else distToClosestGenericPoint=std::numeric_limits<float>::max();
if(!m->InUndamageableState(onUpperLevel,z)){
if(distToClosestPoint>distToMonster&&distToMonster<=maxDistance){
closestMonster=&*m;
}
}
if(m->IsAlive()&&distToClosestGenericPoint>distToMonster&&distToMonster<=maxDistance){
closestGenericMonster=&*m;
}
}
if(closestMonster.has_value()){
return closestMonster;
}else
if(closestGenericMonster.has_value()){
return closestGenericMonster;
}
return {};
}
const bool Monster::InUndamageableState(const bool onUpperLevel,const float z)const{
return Invulnerable()||!IsAlive()||onUpperLevel!=OnUpperLevel()||AttackAvoided(z);
} }

View File

@ -202,6 +202,9 @@ public:
const uint8_t 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 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. void ApplyMark(float time,uint8_t stackCount); //Adds stackCount mark stacks to the target, refreshing the buff to time time.
//Gets the nearest target that can be immediately targeted
static std::optional<Monster*>GetNearestMonster(const vf2d point,const float maxDistance,const bool onUpperLevel,const float z);
const bool InUndamageableState(const bool onUpperLevel,const float z)const;
private: private:
//NOTE: Marking a monster for deletion does not trigger any death events. It just simply removes the monster from the field!! //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. // 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.

View File

@ -1575,33 +1575,45 @@ const vf2d Player::GetAimingLocation(bool useWalkDir,bool invert){
if(xAxis!=0.f||yAxis!=0.f){ if(xAxis!=0.f||yAxis!=0.f){
return {(game->ScreenWidth()*xAxis)/2+game->ScreenWidth()/2,(game->ScreenHeight()*yAxis)/2+game->ScreenHeight()/2}; return {(game->ScreenWidth()*xAxis)/2+game->ScreenWidth()/2,(game->ScreenHeight()*yAxis)/2+game->ScreenHeight()/2};
}else{ }else{
if(useWalkDir&&movementVelocity!=vf2d{0,0}){ if(useWalkDir&&movementVelocity!=vf2d{0,0}){
xAxis=aimingAngle.cart().x; xAxis=aimingAngle.cart().x;
yAxis=aimingAngle.cart().y; yAxis=aimingAngle.cart().y;
} }
const vf2d MAX{std::numeric_limits<float>::max(),std::numeric_limits<float>::max()};
if(xAxis!=0.f||yAxis!=0.f){ if(xAxis!=0.f||yAxis!=0.f){
return {(game->ScreenWidth()*xAxis)/2+game->ScreenWidth()/2,(game->ScreenHeight()*yAxis)/2+game->ScreenHeight()/2}; return {(game->ScreenWidth()*xAxis)/2+game->ScreenWidth()/2,(game->ScreenHeight()*yAxis)/2+game->ScreenHeight()/2};
}else{ }else{
//Find the closest monster target. //Find the closest monster target. Provide a "Generic" target in case a target that is invulnerable is the only target that is available (and alive).
vf2d closestPoint={std::numeric_limits<float>::max(),std::numeric_limits<float>::max()}; std::optional<vf2d>closestPoint;
std::optional<vf2d>closestGenericPoint; //Even if the monster is invulnerable, it might be worth targeting if a normal target is not found.
for(std::unique_ptr<Monster>&m:MONSTER_LIST){ for(std::unique_ptr<Monster>&m:MONSTER_LIST){
if(m->IsAlive()&&!m->Invulnerable()){ geom2d::line<float>aimingLine=geom2d::line<float>(GetPos(),m->GetPos());
geom2d::line<float>aimingLine=geom2d::line<float>(GetPos(),m->GetPos()); float distToMonster=aimingLine.length();
float distToMonster=aimingLine.length(); float distToClosestPoint=geom2d::line<float>(GetPos(),closestPoint.value_or(MAX)).length();
float distToClosestPoint=geom2d::line<float>(GetPos(),closestPoint).length(); float distToClosestGenericPoint=geom2d::line<float>(GetPos(),closestGenericPoint.value_or(MAX)).length();
if(!m->InUndamageableState(OnUpperLevel(),GetZ())){
if(distToClosestPoint>distToMonster&&distToMonster<=operator""_Pixels("Player.Auto Aim Detection Distance"_F)){ if(distToClosestPoint>distToMonster&&distToMonster<=operator""_Pixels("Player.Auto Aim Detection Distance"_F)){
closestPoint=m->GetPos(); closestPoint=m->GetPos();
} }
} }
if(m->IsAlive()&&distToClosestGenericPoint>distToMonster&&distToMonster<=operator""_Pixels("Player.Auto Aim Detection Distance"_F)){
closestGenericPoint=m->GetPos();
}
} }
if(closestPoint!=vf2d{std::numeric_limits<float>::max(),std::numeric_limits<float>::max()}){ std::optional<geom2d::line<float>>aimingLine;
geom2d::line<float>aimingLine=geom2d::line<float>(GetPos(),closestPoint); if(closestPoint.has_value()){
vf2d aimingPoint=aimingLine.rpoint(invert?(-operator""_Pixels("Player.Aiming Cursor Max Distance"_F)):std::min(aimingLine.length()+24.f,float(operator""_Pixels("Player.Aiming Cursor Max Distance"_F)))); aimingLine=geom2d::line<float>(GetPos(),closestPoint.value());
return game->GetScreenSize()/2+aimingPoint-GetPos();
}else }else
return game->GetScreenSize()/2+vf2d{float(operator""_Pixels("Player.Aiming Cursor Max Distance"_F)),aimingAngle.y}.cart(); if(closestGenericPoint.has_value()){
aimingLine=geom2d::line<float>(GetPos(),closestGenericPoint.value());
}
if(aimingLine.has_value()){
vf2d aimingPoint=aimingLine.value().rpoint(invert?(-operator""_Pixels("Player.Aiming Cursor Max Distance"_F)):std::min(aimingLine.value().length()+24.f,float(operator""_Pixels("Player.Aiming Cursor Max Distance"_F))));
return game->GetScreenSize()/2+aimingPoint-GetPos();
}else return game->GetScreenSize()/2+vf2d{float(operator""_Pixels("Player.Aiming Cursor Max Distance"_F)),aimingAngle.y}.cart();
} }
} }
}else{ }else{

View File

@ -98,6 +98,6 @@ void PulsatingFire::Draw()const{
effectSpr=&ANIMATION_DATA["fire_ring0.png"]; effectSpr=&ANIMATION_DATA["fire_ring0.png"];
} }
const Renderable*img=effectSpr->GetFrame(0).GetSourceImage(); const Renderable*img=effectSpr->GetFrame(0).GetSourceImage();
game->view.DrawPartialDecal(pos-effectSpr->GetFrame(0).GetSourceRect().size/2*size,img->Decal(),effectSpr->GetFrame(0).GetSourceRect().pos,effectSpr->GetFrame(0).GetSourceRect().size,size,{255,uint8_t(pulsatingFireValues[i]*256),0,uint8_t((63*(sin("Wizard.Ability 3.FireRingOscillatingFrequency"_F*lifetime+PI*pulsatingFireValues[i]))+63)*(fadeout/original_fadeoutTime))}); game->view.DrawPartialDecal(pos-effectSpr->GetFrame(0).GetSourceRect().size/2*size,img->Decal(),effectSpr->GetFrame(0).GetSourceRect().pos,effectSpr->GetFrame(0).GetSourceRect().size,size,{255,uint8_t(pulsatingFireValues[i]*256),0,uint8_t((63*(sin("Wizard.Ability 3.FireRingOscillatingFrequency"_F*lifetime+PI*pulsatingFireValues[i]))+63)*(fadeout/original_fadeOutTime))});
} }
} }

View File

@ -96,7 +96,19 @@ void Trapper::InitializeClassAbilities(){
#pragma region Trapper Ability 1 (Mark Target) #pragma region Trapper Ability 1 (Mark Target)
Trapper::ability1.action= Trapper::ability1.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
std::optional<Monster*>nearestMonster{Monster::GetNearestMonster(pos,Trapper::ability1.precastInfo.range,p->OnUpperLevel(),p->GetZ())};
vf2d targetPos{pos};
if(nearestMonster.has_value()){
targetPos=nearestMonster.value()->GetPos();
nearestMonster.value()->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<Effect>(geom2d::line<float>(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 true;
}; };
#pragma endregion #pragma endregion

View File

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

View File

@ -59,7 +59,7 @@ Trapper
CancelCast = 0 CancelCast = 0
Duration = 7s Duration = 7s
Hit Count = 5 Stack Count = 5
Damage Increase Bonus = 60% Damage Increase Bonus = 60%
#RGB Values. Color 1 is the circle at full cooldown, Color 2 is the color at empty cooldown. #RGB Values. Color 1 is the circle at full cooldown, Color 2 is the color at empty cooldown.

View File

@ -110,6 +110,7 @@ Images
GFX_Dagger = dagger.png GFX_Dagger = dagger.png
GFX_Shine = shine.png GFX_Shine = shine.png
GFX_TargetMark = target.png GFX_TargetMark = target.png
GFX_MarkTrail = mark_trail.png
GFX_Thief_Sheet = nico-thief.png GFX_Thief_Sheet = nico-thief.png
GFX_Trapper_Sheet = nico-trapper.png GFX_Trapper_Sheet = nico-trapper.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 941 B