Move fadeouttimer for bullets to be private scope. Account for hits multiple flag for bullets that strike a player and adds them to the hit list. Coincidentally, this need also address Issue #17. Fix a bug with multi-hit bullets not applying additional hit effects to monsters. Tornado Attack Implemented. Release Build 9473.

pull/57/head
sigonasr2 6 months ago
parent bbdef1cdfb
commit 3a9bd4afff
  1. 2
      Adventures in Lestoria/Bomb.cpp
  2. 41
      Adventures in Lestoria/Bullet.cpp
  3. 11
      Adventures in Lestoria/Bullet.h
  4. 2
      Adventures in Lestoria/BulletTypes.h
  5. 17
      Adventures in Lestoria/Tornado.cpp
  6. 2
      Adventures in Lestoria/Version.h
  7. 21
      Adventures in Lestoria/Zephy.cpp
  8. 10
      Adventures in Lestoria/assets/config/MonsterStrategies.txt
  9. BIN
      x64/Release/Adventures in Lestoria.exe

@ -104,5 +104,5 @@ bool Bomb::MonsterHit(Monster&monster){
void Bomb::Draw()const{ void Bomb::Draw()const{
Bullet::Draw(); Bullet::Draw();
game->view.DrawPartialRotatedDecal(pos-vf2d{0,GetZ()},bomb_animation.GetFrame(animation).GetSourceImage()->Decal(),rotates?atan2(vel.y,vel.x)-PI/2:0,bomb_animation.GetFrame(animation).GetSourceRect().size/2,bomb_animation.GetFrame(animation).GetSourceRect().pos,bomb_animation.GetFrame(animation).GetSourceRect().size,scale,fadeOutTime==0?col:Pixel{col.r,col.g,col.b,uint8_t(util::lerp(col.a,0,1-((fadeOutTime-fadeOutTimer)/fadeOutTime)))}); game->view.DrawPartialRotatedDecal(pos-vf2d{0,GetZ()},bomb_animation.GetFrame(animation).GetSourceImage()->Decal(),rotates?atan2(vel.y,vel.x)-PI/2:0,bomb_animation.GetFrame(animation).GetSourceRect().size/2,bomb_animation.GetFrame(animation).GetSourceRect().pos,bomb_animation.GetFrame(animation).GetSourceRect().size,scale,fadeOutTime==0?col:Pixel{col.r,col.g,col.b,uint8_t(util::lerp(col.a,0,1-((fadeOutTime-GetFadeoutTimer())/fadeOutTime)))});
} }

@ -47,11 +47,11 @@ INCLUDE_GFX
INCLUDE_MONSTER_LIST INCLUDE_MONSTER_LIST
INCLUDE_WINDOW_SIZE INCLUDE_WINDOW_SIZE
Bullet::Bullet(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly,Pixel col,vf2d scale,float rot) Bullet::Bullet(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly,Pixel col,vf2d scale,float image_angle)
:pos(pos),vel(vel),radius(radius),damage(damage),col(col),friendly(friendly),upperLevel(upperLevel),scale(scale),rot(rot){}; :pos(pos),vel(vel),radius(radius),damage(damage),col(col),friendly(friendly),upperLevel(upperLevel),scale(scale),image_angle(image_angle){};
Bullet::Bullet(vf2d pos,vf2d vel,float radius,int damage,std::string animation,bool upperLevel,bool hitsMultiple,float lifetime,bool rotatesWithAngle,bool friendly,Pixel col,vf2d scale,float rot) Bullet::Bullet(vf2d pos,vf2d vel,float radius,int damage,std::string animation,bool upperLevel,bool hitsMultiple,float lifetime,bool rotatesWithAngle,bool friendly,Pixel col,vf2d scale,float image_angle)
:pos(pos),vel(vel),radius(radius),damage(damage),col(col),animated(true),rotates(rotatesWithAngle),lifetime(lifetime),hitsMultiple(hitsMultiple),friendly(friendly),upperLevel(upperLevel),scale(scale),rot(rot){ :pos(pos),vel(vel),radius(radius),damage(damage),col(col),animated(true),rotates(rotatesWithAngle),lifetime(lifetime),hitsMultiple(hitsMultiple),friendly(friendly),upperLevel(upperLevel),scale(scale),image_angle(image_angle){
this->animation.AddState(animation,ANIMATION_DATA[animation]); this->animation.AddState(animation,ANIMATION_DATA[animation]);
this->animation.ChangeState(internal_animState,animation); this->animation.ChangeState(internal_animState,animation);
}; };
@ -104,18 +104,21 @@ void Bullet::_Update(const float fElapsedTime){
dead=true; dead=true;
} }
return false; return false;
} }else _MonsterHit(*m);
hitList.insert(&*m); hitList.insert(&*m);
} }
} }
} }
} else { } else {
if(geom2d::overlaps(game->GetPlayer()->Hitbox(),geom2d::circle(pos,radius))){ if(geom2d::overlaps(game->GetPlayer()->Hitbox(),geom2d::circle(pos,radius))){
if(game->GetPlayer()->Hurt(damage,OnUpperLevel(),z)){ if(hitList.find(game->GetPlayer())==hitList.end()&&game->GetPlayer()->Hurt(damage,OnUpperLevel(),z)){
if(_PlayerHit(&*game->GetPlayer())){ if(!hitsMultiple){
dead=true; if(_PlayerHit(&*game->GetPlayer())){
} dead=true;
return false; }
return false;
}else _PlayerHit(&*game->GetPlayer());
hitList.insert(game->GetPlayer());
} }
} }
} }
@ -169,22 +172,22 @@ void Bullet::Draw()const{
} }
if(animated){ if(animated){
game->view.DrawPartialRotatedDecal(pos-vf2d{0,GetZ()}+drawOffsetY,GetFrame().GetSourceImage()->Decal(),rotates?atan2(vel.y,vel.x)-PI/2+rot:rot,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,scale,blendCol); game->view.DrawPartialRotatedDecal(pos-vf2d{0,GetZ()}+drawOffsetY,GetFrame().GetSourceImage()->Decal(),rotates?atan2(vel.y,vel.x)-PI/2+image_angle:image_angle,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,scale,blendCol);
}else{ }else{
game->view.DrawRotatedDecal(pos-vf2d{0,GetZ()}+drawOffsetY-GFX["circle.png"].Sprite()->Size()*scale/2,GFX["circle.png"].Decal(),rot,GFX["circle.png"].Sprite()->Size()/2,scale,blendCol); game->view.DrawRotatedDecal(pos-vf2d{0,GetZ()}+drawOffsetY-GFX["circle.png"].Sprite()->Size()*scale/2,GFX["circle.png"].Decal(),image_angle,GFX["circle.png"].Sprite()->Size()/2,scale,blendCol);
game->view.DrawRotatedDecal(pos-vf2d{0,GetZ()}+drawOffsetY-GFX["circle.png"].Sprite()->Size()*scale/2,GFX["circle_outline.png"].Decal(),rot,GFX["circle.png"].Sprite()->Size()/2,scale,Pixel{WHITE.r,WHITE.g,WHITE.b,blendCol.a}); game->view.DrawRotatedDecal(pos-vf2d{0,GetZ()}+drawOffsetY-GFX["circle.png"].Sprite()->Size()*scale/2,GFX["circle_outline.png"].Decal(),image_angle,GFX["circle.png"].Sprite()->Size()/2,scale,Pixel{WHITE.r,WHITE.g,WHITE.b,blendCol.a});
} }
} }
bool Bullet::_PlayerHit(Player*player){ bool Bullet::_PlayerHit(Player*player){
const bool hitOccurred=PlayerHit(player); const bool destroyBullet=PlayerHit(player);
if(iframeTimerOnHit>0.f)player->ApplyIframes(iframeTimerOnHit); if(iframeTimerOnHit>0.f)player->ApplyIframes(iframeTimerOnHit);
return hitOccurred; return destroyBullet;
} }
bool Bullet::_MonsterHit(Monster&monster){ bool Bullet::_MonsterHit(Monster&monster){
const bool hitOccurred=MonsterHit(monster); const bool destroyBullet=MonsterHit(monster);
if(iframeTimerOnHit>0.f)monster.ApplyIframes(iframeTimerOnHit); if(iframeTimerOnHit>0.f)monster.ApplyIframes(iframeTimerOnHit);
return hitOccurred; return destroyBullet;
} }
bool Bullet::PlayerHit(Player*player){return true;} bool Bullet::PlayerHit(Player*player){return true;}
bool Bullet::MonsterHit(Monster&monster){return true;} bool Bullet::MonsterHit(Monster&monster){return true;}
@ -208,4 +211,8 @@ Bullet&Bullet::SetFadeinTime(float fadeInTime){
lifetime+=durationDiff; lifetime+=durationDiff;
deactivated=true; deactivated=true;
return *this; return *this;
}
const float&Bullet::GetFadeoutTimer()const{
return fadeOutTimer;
} }

@ -57,12 +57,12 @@ struct Bullet{
bool upperLevel=false; bool upperLevel=false;
bool alwaysOnTop=false; bool alwaysOnTop=false;
float z=0.f; float z=0.f;
float rot=0.f; float image_angle=0.f;
protected: protected:
float fadeOutTimer=0;
float distanceTraveled=0.f; float distanceTraveled=0.f;
vf2d scale={1,1}; vf2d scale={1,1};
private: private:
float fadeOutTimer=0;
float fadeInTime=0; //Setting the fade in time causes the bullet to be disabled and the bullet's alpha will fade in from zero to the actual alpha of the bullet. When the fade in timer reaches the fade in time automatically, the bullet will be enabled. float fadeInTime=0; //Setting the fade in time causes the bullet to be disabled and the bullet's alpha will fade in from zero to the actual alpha of the bullet. When the fade in timer reaches the fade in time automatically, the bullet will be enabled.
float fadeInTimer=0; float fadeInTimer=0;
void UpdateFadeTime(float fElapsedTime); void UpdateFadeTime(float fElapsedTime);
@ -74,14 +74,15 @@ protected:
float drawOffsetY{}; float drawOffsetY{};
bool _PlayerHit(Player*player); bool _PlayerHit(Player*player);
bool _MonsterHit(Monster&monster); bool _MonsterHit(Monster&monster);
const float&GetFadeoutTimer()const;
public: public:
Animate2D::Animation<std::string>animation; Animate2D::Animation<std::string>animation;
Animate2D::AnimationState internal_animState; Animate2D::AnimationState internal_animState;
std::set<Monster*>hitList; std::set<std::variant<Player*,Monster*>>hitList;
virtual ~Bullet()=default; virtual ~Bullet()=default;
Bullet(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float rot=0.f); Bullet(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle=0.f);
//Initializes a bullet with an animation. //Initializes a bullet with an animation.
Bullet(vf2d pos,vf2d vel,float radius,int damage,std::string animation,bool upperLevel,bool hitsMultiple=false,float lifetime=INFINITE,bool rotatesWithAngle=false,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float rot=0.f); Bullet(vf2d pos,vf2d vel,float radius,int damage,std::string animation,bool upperLevel,bool hitsMultiple=false,float lifetime=INFINITE,bool rotatesWithAngle=false,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle=0.f);
public: public:
void SimulateUpdate(const float fElapsedTime); void SimulateUpdate(const float fElapsedTime);

@ -192,6 +192,8 @@ struct Tornado:public Bullet{
float knockupDuration{}; float knockupDuration{};
float knockbackAmt{}; float knockbackAmt{};
float fadeInDuration{}; float fadeInDuration{};
float rot{0.f};
bool fadeOut{false};
Tornado(vf2d centerPoint,float distance,float initialRot,float rotSpd,int damage,const float knockupAmt,const float knockbackAmt,const float lifetime,bool upperLevel,bool friendly=false,Pixel col=WHITE,const vf2d scale={1,1}); Tornado(vf2d centerPoint,float distance,float initialRot,float rotSpd,int damage,const float knockupAmt,const float knockbackAmt,const float lifetime,bool upperLevel,bool friendly=false,Pixel col=WHITE,const vf2d scale={1,1});
void Update(float fElapsedTime)override; void Update(float fElapsedTime)override;
bool PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!! bool PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!

@ -41,14 +41,21 @@ All rights reserved.
#include "util.h" #include "util.h"
Tornado::Tornado(vf2d centerPoint,float distance,float initialRot,float rotSpd,int damage,const float knockupDuration,const float knockbackAmt,const float lifetime,bool upperLevel,bool friendly,Pixel col,const vf2d scale) Tornado::Tornado(vf2d centerPoint,float distance,float initialRot,float rotSpd,int damage,const float knockupDuration,const float knockbackAmt,const float lifetime,bool upperLevel,bool friendly,Pixel col,const vf2d scale)
:polarAngle({distance,initialRot}),rotatingSpd(rotSpd),knockupDuration(knockupDuration),knockbackAmt(knockbackAmt),Bullet(centerPoint+polarAngle.cart(),vf2d{},6.f,damage,"tornado2.png",upperLevel,true,lifetime,false,friendly,col,scale){} :polarAngle({distance,initialRot}),rot(initialRot),rotatingSpd(rotSpd),knockupDuration(knockupDuration),knockbackAmt(knockbackAmt),centerPoint(centerPoint),Bullet(centerPoint+polarAngle.cart(),vf2d{},6.f,damage,"tornado2.png",upperLevel,true,lifetime+0.5f,false,friendly,col,scale){}
void Tornado::Update(float fElapsedTime){ void Tornado::Update(float fElapsedTime){
rot+=rotatingSpd; rot+=rotatingSpd*fElapsedTime;
polarAngle={polarAngle.x,rot}; polarAngle={polarAngle.x,rot};
pos=centerPoint+polarAngle.cart(); pos=centerPoint+polarAngle.cart();
if(lifetime<=0.5f&&!fadeOut){
fadeOut=true;
fadeOutTime=0.5f;
}
hitList.clear(); //This is a slight hack to make it so these tornados will always hit things multiple times.
} }
bool Tornado::PlayerHit(Player*player){ bool Tornado::PlayerHit(Player*player){
@ -57,7 +64,7 @@ bool Tornado::PlayerHit(Player*player){
player->ApplyIframes(knockupDuration); player->ApplyIframes(knockupDuration);
return true; return false;
} }
bool Tornado::MonsterHit(Monster&monster){ bool Tornado::MonsterHit(Monster&monster){
@ -66,5 +73,7 @@ bool Tornado::MonsterHit(Monster&monster){
monster.Knockback(util::pointTo(centerPoint,monster.GetPos())*knockbackAmt); monster.Knockback(util::pointTo(centerPoint,monster.GetPos())*knockbackAmt);
monster.Knockup(knockupDuration); monster.Knockup(knockupDuration);
return true; monster.ApplyIframes(knockupDuration);
return false;
} }

@ -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 9455 #define VERSION_BUILD 9473
#define stringify(a) stringify_(a) #define stringify(a) stringify_(a)
#define stringify_(a) #a #define stringify_(a) #a

@ -91,7 +91,7 @@ void Monster::STRATEGY::ZEPHY(Monster&m,float fElapsedTime,std::string strategy)
}break; }break;
case 1:{ case 1:{
m.phase=TORNADO_ATTACK_PREPARE; m.phase=TORNADO_ATTACK_PREPARE;
m.target=ConfigVec("Tornado Attack.Arena Center"); m.target=ConfigVec("Tornado Attack.Landing Area");
}break; }break;
case 2:{ case 2:{
m.phase=WIND_ATTACK; m.phase=WIND_ATTACK;
@ -132,7 +132,7 @@ void Monster::STRATEGY::ZEPHY(Monster&m,float fElapsedTime,std::string strategy)
CreateBullet(Bullet)(m.GetPos()+vf2d{xOffset+util::random_range(-3.f,3.f),-util::random(10.f)-4.f},vf2d{0.f,ConfigFloat("Fly Across Attack.Attack Y Speed")},1,ConfigInt("Fly Across Attack.Poop Damage"),"birdpoop.png",m.OnUpperLevel(),false,INFINITY,false,false,WHITE,vf2d{util::random_range(0.2f,0.3f),util::random_range(0.2f,0.3f)},util::random(2*PI)) CreateBullet(Bullet)(m.GetPos()+vf2d{xOffset+util::random_range(-3.f,3.f),-util::random(10.f)-4.f},vf2d{0.f,ConfigFloat("Fly Across Attack.Attack Y Speed")},1,ConfigInt("Fly Across Attack.Poop Damage"),"birdpoop.png",m.OnUpperLevel(),false,INFINITY,false,false,WHITE,vf2d{util::random_range(0.2f,0.3f),util::random_range(0.2f,0.3f)},util::random(2*PI))
.SetIframeTimeOnHit(0.25f)EndBullet; .SetIframeTimeOnHit(0.25f)EndBullet;
} }
m.F(A::SHOOT_TIMER)=ConfigFloat("Fly Across Attack.Landing Area"); m.F(A::SHOOT_TIMER)=ConfigFloat("Fly Across Attack.Attack Frequency");
} }
if(m.ReachedTargetPos()){ if(m.ReachedTargetPos()){
m.phase=IDLE; m.phase=IDLE;
@ -154,19 +154,24 @@ void Monster::STRATEGY::ZEPHY(Monster&m,float fElapsedTime,std::string strategy)
//From strategy documentation: //From strategy documentation:
//# For each Ring: Distance from Boss in Units, # of tornados, Rotation Speed (degrees/sec). //# For each Ring: Distance from Boss in Units, # of tornados, Rotation Speed (degrees/sec).
const float tornadoDistance=ConfigPixelsArr(std::format("Tornado Attack.Tornados.Ring{}",tornadoRingId),0); const float tornadoDistance=ConfigPixelsArr(std::format("Tornado Attack.Tornados.Ring {}",tornadoRingId),0);
const int tornadoCount=ConfigIntArr(std::format("Tornado Attack.Tornados.Ring{}",tornadoRingId),1); const int tornadoCount=ConfigIntArr(std::format("Tornado Attack.Tornados.Ring {}",tornadoRingId),1);
const float tornadoRotSpd=util::degToRad(ConfigFloatArr(std::format("Tornado Attack.Tornados.Ring{}",tornadoRingId),2)); //It's in degrees, let's convert it to radians now. const float tornadoRotSpd=util::degToRad(ConfigFloatArr(std::format("Tornado Attack.Tornados.Ring {}",tornadoRingId),2)); //It's in degrees, let's convert it to radians now.
const float randomRotDir=util::random(2*PI); const float randomRotDir=util::random(2*PI);
CreateBullet(Tornado)(m.GetPos(),tornadoDistance,randomRotDir,tornadoRotSpd,m.GetAttack(),ConfigFloat("Tornado Attack.Knockup Duration"),ConfigFloat("Tornado Attack.Knockback Amount"),ConfigFloat("Tornado Attack.Attack Duration"),m.OnUpperLevel(),false,WHITE)EndBullet; for(int tornadoCounter=0;tornadoCounter<tornadoCount;tornadoCounter++){
BULLET_LIST.back()->SetFadeinTime(ConfigFloat("Tornado Fade-In Time")); const float startAngle=randomRotDir+tornadoCounter*((2*PI)/tornadoCount);
CreateBullet(Tornado)(m.GetPos(),tornadoDistance,startAngle,tornadoRotSpd,m.GetAttack(),ConfigFloat("Tornado Attack.Knockup Duration"),ConfigFloat("Tornado Attack.Knockback Amount"),ConfigFloat("Tornado Attack.Attack Duration"),m.OnUpperLevel(),false,WHITE)EndBullet;
BULLET_LIST.back()->SetFadeinTime(ConfigFloat("Tornado Attack.Tornado Fade-In Time"));
}
} }
} }
}break; }break;
case TORNADO_ATTACK:{ case TORNADO_ATTACK:{
m.F(A::CASTING_TIMER)-=fElapsedTime;
if(m.F(A::CASTING_TIMER)<=0.f)m.phase=IDLE;
}break; }break;
case WIND_ATTACK:{ case WIND_ATTACK:{

@ -811,10 +811,12 @@ MonsterStrategy
{ {
# For each Ring: Distance from Boss in Units, # of tornados, Rotation Speed (degrees/sec). # For each Ring: Distance from Boss in Units, # of tornados, Rotation Speed (degrees/sec).
# Freely add/subtract rings to create or remove more tornados. # Freely add/subtract rings to create or remove more tornados.
Ring 1 = 100, 2, 120 Ring 1 = 100, 2, 90
Ring 2 = 200, 3, -75 Ring 2 = 235, 3, -60
Ring 3 = 300, 5, 60 Ring 3 = 370, 5, 40
Ring 4 = 400, 8, -40 Ring 4 = 505, 8, -20
Ring 5 = 640, 12, 10
Ring 6 = 775, 18, -7
} }
} }
} }

Loading…
Cancel
Save