From 942e8e0ef78e5c38a985f2e013d905ce2b4b8020 Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Sat, 9 Sep 2023 07:10:31 -0500 Subject: [PATCH] Bullets do not die immediately when view goes offscreen anymore. Initial jump states and setup for Slime King are now implemented. --- Crawler/Animation.cpp | 1 + Crawler/Crawler.cpp | 3 +- Crawler/Crawler.h | 2 +- Crawler/Monster.cpp | 36 +++++++++++----- Crawler/Monster.h | 13 ++++-- Crawler/MonsterAttribute.h | 9 +++- Crawler/Player.cpp | 30 ++++++------- Crawler/Player.h | 6 +-- Crawler/RunTowards.cpp | 8 ++-- Crawler/ShootAfar.cpp | 18 ++++---- Crawler/SlimeKing.cpp | 45 ++++++++++++++++++++ Crawler/State.h | 36 +++++++++------- Crawler/Version.h | 2 +- Crawler/assets/config/MonsterStrategies.txt | 3 +- Crawler/assets/config/gfx/gfx.txt | 1 + Crawler/assets/range_indicator.png | Bin 0 -> 9035 bytes Crawler/utils.cpp | 4 ++ Crawler/utils.h | 1 + 18 files changed, 151 insertions(+), 67 deletions(-) create mode 100644 Crawler/assets/range_indicator.png diff --git a/Crawler/Animation.cpp b/Crawler/Animation.cpp index 87e7e747..0e0c8c58 100644 --- a/Crawler/Animation.cpp +++ b/Crawler/Animation.cpp @@ -206,6 +206,7 @@ void sig::Animation::InitializeAnimations(){ CreateStillAnimation(game->GFX_Arrow,{24,24},"ARROW"); CreateStillAnimation(game->GFX_ChargedArrow,{48,48},"CHARGED_ARROW"); CreateStillAnimation(game->GFX_Laser,{5,1},"LASER"); + CreateStillAnimation(game->GFX_RangeIndicator,{24,24},"RANGE_INDICATOR"); } void sig::Animation::SetupPlayerAnimations(){ diff --git a/Crawler/Crawler.cpp b/Crawler/Crawler.cpp index f4000d93..b6773ae1 100644 --- a/Crawler/Crawler.cpp +++ b/Crawler/Crawler.cpp @@ -116,6 +116,7 @@ bool Crawler::OnUserCreate(){ LOADIMG(GFX_Arrow) LOADIMG(GFX_Laser) LOADIMG(GFX_ChargedArrow) + LOADIMG(GFX_RangeIndicator) Monster::InitializeStrategies(); //Animations @@ -451,7 +452,7 @@ void Crawler::UpdateBullets(float fElapsedTime){ } else { b->pos+=b->vel*fElapsedTime; } - if(b->pos.x+b->radiuspos.x-b->radius>view.GetWorldBR().x||b->pos.y+b->radiuspos.y-b->radius>view.GetWorldBR().y){ + if(b->pos.x+b->radiuspos.x-b->radius>view.GetWorldBR().x+WINDOW_SIZE.x||b->pos.y+b->radiuspos.y-b->radius>view.GetWorldBR().y+WINDOW_SIZE.y){ b->dead=true; continue; } diff --git a/Crawler/Crawler.h b/Crawler/Crawler.h index 02d091f8..fea99f09 100644 --- a/Crawler/Crawler.h +++ b/Crawler/Crawler.h @@ -29,7 +29,7 @@ class Crawler : public olc::PixelGameEngine GFX_Splash_Effect,GFX_LightningBolt,GFX_LightningBoltParticle1, GFX_LightningBoltParticle2,GFX_LightningBoltParticle3,GFX_LightningBoltParticle4, GFX_ChainLightning,GFX_LightningSplash,GFX_Meteor,GFX_Arrow, - GFX_Laser,GFX_ChargedArrow; + GFX_Laser,GFX_ChargedArrow,GFX_RangeIndicator; public: Renderable GFX_BulletCircle,GFX_BulletCircleOutline,GFX_EnergyBolt,GFX_Circle; Pathfinding pathfinder; diff --git a/Crawler/Monster.cpp b/Crawler/Monster.cpp index e61300f2..a79baed0 100644 --- a/Crawler/Monster.cpp +++ b/Crawler/Monster.cpp @@ -129,11 +129,11 @@ bool Monster::Update(float fElapsedTime){ if(!HasIframes()){ for(Monster&m:MONSTER_LIST){ if(&m==this)continue; - if(!m.HasIframes()&&OnUpperLevel()==m.OnUpperLevel()&&geom2d::overlaps(geom2d::circle(pos,12*size/2),geom2d::circle(m.GetPos(),12*m.GetSizeMult()/2))){ + if(!m.HasIframes()&&OnUpperLevel()==m.OnUpperLevel()&&abs(m.GetZ()-GetZ())<=1&&geom2d::overlaps(geom2d::circle(pos,12*size/2),geom2d::circle(m.GetPos(),12*m.GetSizeMult()/2))){ m.Collision(*this); geom2d::line line(pos,m.GetPos()); float dist = line.length(); - m.SetPosition(line.rpoint(dist*1.1)); + m.SetPos(line.rpoint(dist*1.1)); if(m.IsAlive()){ vel=line.vector().norm()*-128; } @@ -142,7 +142,7 @@ bool Monster::Update(float fElapsedTime){ if(!game->GetPlayer()->HasIframes()&&game->GetPlayer()->OnUpperLevel()==OnUpperLevel()&&geom2d::overlaps(geom2d::circle(pos,12*size/2),geom2d::circle(game->GetPlayer()->GetPos(),12*game->GetPlayer()->GetSizeMult()/2))){ geom2d::line line(pos,game->GetPlayer()->GetPos()); float dist = line.length(); - SetPosition(line.rpoint(-0.1)); + SetPos(line.rpoint(-0.1)); vel=line.vector().norm()*-128; } } @@ -183,6 +183,7 @@ Key Monster::GetFacingDirection(){ return facingDirection; } void Monster::Draw(){ + strategyDraw(game); if(GetZ()>0){ vf2d shadowScale=vf2d{8*GetSizeMult()/3.f,1}/std::max(1.f,GetZ()/24); game->view.DrawDecal(GetPos()-vf2d{3,3}*shadowScale/2+vf2d{0,6*GetSizeMult()},game->GFX_Circle.Decal(),shadowScale,BLACK); @@ -204,14 +205,14 @@ void Monster::Collision(Monster&m){ Collision(); } void Monster::Collision(){ - if(strategy==0&&GetState()==MOVE_TOWARDS){//The run towards strategy causes state to return to normal upon a collision. - SetState(NORMAL); + if(strategy==0&&GetState()==State::MOVE_TOWARDS){//The run towards strategy causes state to return to normal upon a collision. + SetState(State::NORMAL); } } void Monster::SetVelocity(vf2d vel){ this->vel=vel; } -bool Monster::SetPosition(vf2d pos){ +bool Monster::SetPos(vf2d pos){ bool resultX=SetX(pos.x); bool resultY=SetY(pos.y); if(resultY&&!resultX){ @@ -301,7 +302,7 @@ void Monster::AddBuff(BuffType type,float duration,float intensity){ } void Monster::StartPathfinding(float pathingTime){ - SetState(PATH_AROUND); + SetState(State::PATH_AROUND); path=game->pathfinder.Solve_AStar(pos,target,12,OnUpperLevel()); if(path.size()>0){ pathIndex=0; @@ -315,7 +316,7 @@ void Monster::PathAroundBehavior(float fElapsedTime){ //Move towards the new path. geom2d::line moveTowardsLine=geom2d::line(pos,path[pathIndex]*24); if(moveTowardsLine.length()>2){ - SetPosition(pos+moveTowardsLine.vector().norm()*100*fElapsedTime*GetMoveSpdMult()); + SetPos(pos+moveTowardsLine.vector().norm()*100*fElapsedTime*GetMoveSpdMult()); if(moveTowardsLine.vector().x>0){ facingDirection=RIGHT; } else { @@ -341,11 +342,11 @@ std::vectorMonster::GetBuffs(BuffType buff){ return filteredBuffs; } -State Monster::GetState(){ +State::State Monster::GetState(){ return state; } -void Monster::SetState(State newState){ +void Monster::SetState(State::State newState){ state=newState; } @@ -407,4 +408,19 @@ bool&Monster::GetBool(Attribute a){ attributes[a]=false; } return std::get(attributes[a]); +} + +vf2d&Monster::GetVf2d(Attribute a){ + if(attributes.count(a)==0){ + attributes[a]=vf2d{}; + } + return std::get(attributes[a]); +} + +void Monster::SetZ(float z){ + this->z=z; +} + +void Monster::SetStrategyDrawFunction(std::functionfunc){ + strategyDraw=func; } \ No newline at end of file diff --git a/Crawler/Monster.h b/Crawler/Monster.h index 49b73976..d80124d9 100644 --- a/Crawler/Monster.h +++ b/Crawler/Monster.h @@ -9,6 +9,7 @@ #include "MonsterAttribute.h" struct Player; +class Crawler; enum MonsterAnimation{ IDLE, @@ -72,7 +73,7 @@ private: float iframe_timer=0; Key facingDirection; int strategy; - State state=State::NORMAL; + State::State state=State::NORMAL; Animate2D::Animationanimation; Animate2D::AnimationState internal_animState; float randomFrameOffset=0.f; @@ -91,6 +92,7 @@ private: bool diesNormally=true; //If set to false, the monster death is handled in a special way. Set it to true when it's time to die. float targetSize=0; std::map>attributes; + std::functionstrategyDraw=[](Crawler*pge){}; protected: public: Monster()=delete; @@ -115,7 +117,7 @@ public: void Collision(); void SetVelocity(vf2d vel); //Returns false if the monster could not be moved to the requested location due to collision. - bool SetPosition(vf2d pos); + bool SetPos(vf2d pos); //Returns false if the monster could not be moved to the requested location due to collision. bool SetX(float x); //Returns false if the monster could not be moved to the requested location due to collision. @@ -129,17 +131,20 @@ public: void PathAroundBehavior(float fElapsedTime); void AddBuff(BuffType type,float duration,float intensity); std::vectorGetBuffs(BuffType buff); - State GetState(); - void SetState(State newState); + State::State GetState(); + void SetState(State::State newState); static void InitializeStrategies(); bool HasIframes(); float GetZ(); + void SetZ(float z); std::string GetStrategy(); void SetSize(float newSize,bool immediate=true); float&GetFloat(Attribute a); int&GetInt(Attribute a); std::string&GetString(Attribute a); bool&GetBool(Attribute a); + vf2d&GetVf2d(Attribute a); + void SetStrategyDrawFunction(std::functionfunc); private: struct STRATEGY{ static int _GetInt(Monster&m,std::string param,int strategyNumber,int index=0); diff --git a/Crawler/MonsterAttribute.h b/Crawler/MonsterAttribute.h index ece168bf..d076a59c 100644 --- a/Crawler/MonsterAttribute.h +++ b/Crawler/MonsterAttribute.h @@ -1,10 +1,11 @@ #pragma once -#define VARIANTS float,int,std::string,bool +#define VARIANTS float,int,std::string,bool,vf2d #include #define F(attr) GetFloat(attr) #define I(attr) GetInt(attr) #define S(attr) GetString(attr) #define B(attr) GetBool(attr) +#define V(attr) GetVf2d(attr) enum class Attribute{ IFRAME_TIME_UPON_HIT, @@ -13,4 +14,10 @@ enum class Attribute{ SHOOT_RING_COUNTER, SHOOT_RING_RIGHT, SHOOT_RING_OFFSET, + PATTERN_REPEAT_COUNT, + JUMP_ORIGINAL_LANDING_TIMER, + JUMP_LANDING_TIMER, + JUMP_TARGET_POS, + JUMP_ORIGINAL_POS, + RECOVERY_TIME, }; \ No newline at end of file diff --git a/Crawler/Player.cpp b/Crawler/Player.cpp index 684ad052..4a2320c8 100644 --- a/Crawler/Player.cpp +++ b/Crawler/Player.cpp @@ -160,7 +160,7 @@ float Player::GetSpinAngle(){ return spin_angle; } -State Player::GetState(){ +State::State Player::GetState(){ return state; } @@ -207,7 +207,7 @@ void Player::Update(float fElapsedTime){ //Class-specific update events. OnUpdate(fElapsedTime); switch(state){ - case SPIN:{ + case State::SPIN:{ switch(facingDirection){ case UP:{ if(lastAnimationFlip==0){ @@ -233,7 +233,7 @@ void Player::Update(float fElapsedTime){ z="Warrior.Ability 2.SpinMaxHeight"_I*sin(3.3*(GROUND_SLAM_SPIN_TIME-spin_attack_timer)/GROUND_SLAM_SPIN_TIME); spin_attack_timer=std::max(0.f,spin_attack_timer-fElapsedTime); } else { - SetState(NORMAL); + SetState(State::NORMAL); spin_angle=0; z=0; float numb=4; @@ -245,14 +245,14 @@ void Player::Update(float fElapsedTime){ } animation.UpdateState(internal_animState,fElapsedTime); }break; - case BLOCK:{ + case State::BLOCK:{ if(blockTimer<=0){ - SetState(NORMAL); + SetState(State::NORMAL); } }break; - case SWING_SONIC_SWORD:{ + case State::SWING_SONIC_SWORD:{ if(ability3.COOLDOWN_TIME-ability3.cooldown>0.5){ - SetState(NORMAL); + SetState(State::NORMAL); switch(facingDirection){ case DOWN:{ UpdateAnimation("WARRIOR_IDLE_S"); @@ -270,11 +270,11 @@ void Player::Update(float fElapsedTime){ } animation.UpdateState(internal_animState,fElapsedTime); }break; - case TELEPORT:{ + case State::TELEPORT:{ teleportAnimationTimer=std::max(0.f,teleportAnimationTimer-fElapsedTime); if(teleportAnimationTimer<=0){ SetPos(teleportTarget); - SetState(NORMAL); + SetState(State::NORMAL); } animation.UpdateState(internal_animState,fElapsedTime); }break; @@ -304,13 +304,13 @@ void Player::Update(float fElapsedTime){ ability4.cooldown=0; } for(Monster&m:MONSTER_LIST){ - if(!HasIframes()&&OnUpperLevel()==m.OnUpperLevel()&&geom2d::overlaps(geom2d::circle(pos,12*size/2),geom2d::circle(m.GetPos(),12*m.GetSizeMult()/2))){ + if(!HasIframes()&&abs(m.GetZ()-GetZ())<=1&&OnUpperLevel()==m.OnUpperLevel()&&geom2d::overlaps(geom2d::circle(pos,12*size/2),geom2d::circle(m.GetPos(),12*m.GetSizeMult()/2))){ if(m.IsAlive()){ m.Collision(this); } geom2d::line line(pos,m.GetPos()); float dist = line.length(); - m.SetPosition(line.rpoint(dist*1.1)); + m.SetPos(line.rpoint(dist*1.1)); if(m.IsAlive()){ vel=line.vector().norm()*-128; } @@ -382,7 +382,7 @@ void Player::Update(float fElapsedTime){ #pragma region Warrior switch(GetState()){ - case SWING_SWORD:{ + case State::SWING_SWORD:{ switch(GetFacingDirection()){ case UP:{ UpdateAnimation("WARRIOR_SWINGSWORD_N"); @@ -407,9 +407,9 @@ void Player::Update(float fElapsedTime){ #pragma endregion #pragma region Ranger - if(GetState()==SHOOT_ARROW){ + if(GetState()==State::SHOOT_ARROW){ if(attack_cooldown_timer<=ARROW_ATTACK_COOLDOWN-0.3){ - SetState(NORMAL); + SetState(State::NORMAL); } } if(retreatTimer>0){ @@ -467,7 +467,7 @@ void Player::SetSwordSwingTimer(float val){ swordSwingTimer=val; } -void Player::SetState(State newState){ +void Player::SetState(State::State newState){ if(GetState()==State::BLOCK){ RemoveAllBuffs(BuffType::BLOCK_SLOWDOWN); } diff --git a/Crawler/Player.h b/Crawler/Player.h index 2ed03fe5..3101f482 100644 --- a/Crawler/Player.h +++ b/Crawler/Player.h @@ -41,7 +41,7 @@ private: float manaTickTimer=0; std::pair notEnoughManaDisplay={"",0}; float teleportAttemptWaitTime=0; //If a teleport fails, we wait awhile before trying again, it's expensive. - State state=State::NORMAL; + State::State state=State::NORMAL; Animate2D::Animationanimation; Animate2D::AnimationState internal_animState; Key lastReleasedMovementKey; @@ -58,7 +58,7 @@ protected: const float MAGIC_ATTACK_COOLDOWN="Wizard.Auto Attack.Cooldown"_F; float ARROW_ATTACK_COOLDOWN="Ranger.Auto Attack.Cooldown"_F; void SetSwordSwingTimer(float val); - void SetState(State newState); + void SetState(State::State newState); void SetFacingDirection(Key direction); void SetLastReleasedMovementKey(Key k); void Spin(float duration,float spinSpd); @@ -120,7 +120,7 @@ public: float GetSizeMult(); float GetAttackRangeMult(); float GetSpinAngle(); - State GetState(); + State::State GetState(); Key GetFacingDirection(); vf2d GetVelocity(); bool HasIframes(); diff --git a/Crawler/RunTowards.cpp b/Crawler/RunTowards.cpp index 4661f535..1103dd75 100644 --- a/Crawler/RunTowards.cpp +++ b/Crawler/RunTowards.cpp @@ -18,11 +18,11 @@ void Monster::STRATEGY::RUN_TOWARDS(Monster&m,float fElapsedTime,int strategyNum } else { m.target=desiredTargetLine.upoint(1.2); } - m.SetState(MOVE_TOWARDS); + m.SetState(State::MOVE_TOWARDS); m.hasHitPlayer=false; } switch(m.state){ - case MOVE_TOWARDS:{ + case State::MOVE_TOWARDS:{ if(geom2d::line(m.pos,m.target).length()>100*fElapsedTime*m.GetMoveSpdMult()){ vf2d newPos=m.pos+geom2d::line(m.pos,m.target).vector().norm()*100*fElapsedTime*m.GetMoveSpdMult(); if(!m.SetX(newPos.x)||!m.SetY(newPos.y)){ @@ -30,11 +30,11 @@ void Monster::STRATEGY::RUN_TOWARDS(Monster&m,float fElapsedTime,int strategyNum } m.PerformJumpAnimation(); } else { - m.SetState(NORMAL);//Revert state once we've finished moving towards target. + m.SetState(State::NORMAL);//Revert state once we've finished moving towards target. m.UpdateAnimation(MONSTER_DATA[m.id].GetIdleAnimation()); } }break; - case PATH_AROUND:{ + case State::PATH_AROUND:{ m.PathAroundBehavior(fElapsedTime); }break; default:{ diff --git a/Crawler/ShootAfar.cpp b/Crawler/ShootAfar.cpp index 972eecfb..0a30c1f0 100644 --- a/Crawler/ShootAfar.cpp +++ b/Crawler/ShootAfar.cpp @@ -24,23 +24,23 @@ void Monster::STRATEGY::SHOOT_AFAR(Monster&m,float fElapsedTime,int strategyNumb if(line.length()<24.f*ConfigInt("Range")/100.f){ m.target=line.upoint(-1.2); if(m.canMove){ - m.SetState(MOVE_AWAY); + m.SetState(State::MOVE_AWAY); } else { - m.SetState(NORMAL); + m.SetState(State::NORMAL); } } else if(line.length()>24.f*ConfigInt("CloseInRange")/100.0f){ m.target=line.upoint(1.2); - m.SetState(MOVE_TOWARDS); + m.SetState(State::MOVE_TOWARDS); } else { - m.SetState(NORMAL); + m.SetState(State::NORMAL); } } m.canMove=true; geom2d::line moveTowardsLine=geom2d::line(m.pos,m.target); bool pathfindingDecision=false; switch(m.state){ - case MOVE_TOWARDS:{ + case State::MOVE_TOWARDS:{ if(moveTowardsLine.length()>1){ vf2d newPos=m.pos+moveTowardsLine.vector().norm()*100*fElapsedTime*m.GetMoveSpdMult(); bool movedX=m.SetX(newPos.x); @@ -52,7 +52,7 @@ void Monster::STRATEGY::SHOOT_AFAR(Monster&m,float fElapsedTime,int strategyNumb m.StartPathfinding(2.5); }else if(line.length()<=24.f*ConfigInt("CloseInRange")/100.0f){ - m.SetState(NORMAL); + m.SetState(State::NORMAL); } if(moveTowardsLine.vector().x>0){ m.facingDirection=RIGHT; @@ -61,7 +61,7 @@ void Monster::STRATEGY::SHOOT_AFAR(Monster&m,float fElapsedTime,int strategyNumb } m.PerformJumpAnimation(); }break; - case MOVE_AWAY:{ + case State::MOVE_AWAY:{ if(moveTowardsLine.length()>1){ vf2d newPos=m.pos+moveTowardsLine.vector().norm()*100*fElapsedTime*m.GetMoveSpdMult(); bool movedX=m.SetX(newPos.x); @@ -73,7 +73,7 @@ void Monster::STRATEGY::SHOOT_AFAR(Monster&m,float fElapsedTime,int strategyNumb m.StartPathfinding(2.5); }else if(line.length()>=24.f*ConfigInt("Range")/100.f){ - m.SetState(NORMAL); + m.SetState(State::NORMAL); } if(moveTowardsLine.vector().x>0){ m.facingDirection=RIGHT; @@ -82,7 +82,7 @@ void Monster::STRATEGY::SHOOT_AFAR(Monster&m,float fElapsedTime,int strategyNumb } m.PerformJumpAnimation(); }break; - case PATH_AROUND:{ + case State::PATH_AROUND:{ m.PathAroundBehavior(fElapsedTime); }break; default:{ diff --git a/Crawler/SlimeKing.cpp b/Crawler/SlimeKing.cpp index 35011e1b..1a92e2f6 100644 --- a/Crawler/SlimeKing.cpp +++ b/Crawler/SlimeKing.cpp @@ -3,9 +3,11 @@ #include "DEFINES.h" #include "Crawler.h" #include "utils.h" +#include "safemap.h" INCLUDE_game INCLUDE_BULLET_LIST +INCLUDE_ANIMATION_DATA typedef Attribute A; @@ -15,6 +17,7 @@ void Monster::STRATEGY::SLIMEKING(Monster&m,float fElapsedTime,int strategyNumbe m.F(A::SHOOT_RING_TIMER)=std::max(0.f,m.F(A::SHOOT_RING_TIMER)-fElapsedTime); m.F(A::SHOOT_RING_DELAY)=std::max(0.f,m.F(A::SHOOT_RING_DELAY)-fElapsedTime); + m.F(A::JUMP_LANDING_TIMER)=std::max(0.f,m.F(A::JUMP_LANDING_TIMER)-fElapsedTime); auto ShootBulletRing=[&](float angleOffset){ int bulletCount=ConfigInt("Phase1.RingBulletCount"); @@ -23,6 +26,42 @@ void Monster::STRATEGY::SLIMEKING(Monster&m,float fElapsedTime,int strategyNumbe BULLET_LIST.emplace_back(std::make_unique(m.GetPos(),vf2d{cos(angle),sin(angle)}*bulletSpd,6,ConfigInt("ProjectileDamage"),m.OnUpperLevel(),false,YELLOW,vf2d{6,6})); } }; + auto StartJump=[&](float jumpDuration,vf2d targetPos,float recoveryTime){ + m.V(A::JUMP_ORIGINAL_POS)=m.GetPos(); + m.F(A::JUMP_ORIGINAL_LANDING_TIMER)=m.F(A::JUMP_LANDING_TIMER)=jumpDuration; + m.V(A::JUMP_TARGET_POS)=targetPos; + m.F(A::RECOVERY_TIME)=recoveryTime; + m.state=State::JUMP; + }; + + if(m.state==State::RECOVERY){ + m.F(A::RECOVERY_TIME)=std::max(0.f,m.F(A::RECOVERY_TIME)-fElapsedTime); + if(m.F(A::RECOVERY_TIME)==0){ + m.state=State::NORMAL; + } + return; + } + + if(m.state==State::JUMP){ + float jumpLandingTimerRatio=m.F(A::JUMP_LANDING_TIMER)/m.F(A::JUMP_ORIGINAL_LANDING_TIMER); + m.SetPos(m.V(A::JUMP_ORIGINAL_POS).lerp(m.V(A::JUMP_TARGET_POS),1-jumpLandingTimerRatio)); + if(m.F(A::JUMP_LANDING_TIMER)>=m.F(A::JUMP_ORIGINAL_LANDING_TIMER)/2){ + m.SetZ(util::lerp(0,ConfigInt("JumpHeight"),1-jumpLandingTimerRatio*2)); + }else{ + m.SetZ(util::lerp(0,ConfigInt("JumpHeight"),jumpLandingTimerRatio*2)); + } + if(m.F(A::JUMP_LANDING_TIMER)==0){ + m.state=State::RECOVERY; + m.SetStrategyDrawFunction([](Crawler*game){}); + } else + if(m.F(A::JUMP_LANDING_TIMER)<=ConfigFloat("JumpWarningIndicatorTime")){ + m.SetStrategyDrawFunction([&](Crawler*game){ + Decal*dec=ANIMATION_DATA["RANGE_INDICATOR"].GetFrame(game->GetElapsedTime()).GetSourceImage()->Decal(); + game->view.DrawRotatedDecal(m.GetPos(),dec,0,dec->sprite->Size()/2,vf2d{m.GetSizeMult(),m.GetSizeMult()}/2,RED); + }); + } + return; + } switch(m.phase){ case 0:{ @@ -38,11 +77,17 @@ void Monster::STRATEGY::SLIMEKING(Monster&m,float fElapsedTime,int strategyNumbe m.SetSize(ConfigFloat("Phase2.Size")/100,false); } if(m.F(A::SHOOT_RING_TIMER)==0){ + if(m.I(A::PATTERN_REPEAT_COUNT)>=ConfigInt("Phase1.JumpAfter")){ + StartJump(ConfigFloat("Phase1.AirborneTime"),game->GetPlayer()->GetPos(),ConfigFloat("Phase1.LandingRecoveryTime")); + m.I(A::PATTERN_REPEAT_COUNT)=0; + return; + } m.I(A::SHOOT_RING_COUNTER)=ConfigInt("Phase1.ShootRingCount")-1; m.F(A::SHOOT_RING_DELAY)=ConfigFloat("Phase1.ShootRingDelay"); ShootBulletRing(m.F(A::SHOOT_RING_OFFSET)); m.F(A::SHOOT_RING_TIMER)=ConfigFloat("Phase1.ShootRepeatTime"); m.B(A::SHOOT_RING_RIGHT)=bool(rand()%2); + m.I(A::PATTERN_REPEAT_COUNT)++; } if(m.I(A::SHOOT_RING_COUNTER)>0){ if(m.F(A::SHOOT_RING_DELAY)==0){ diff --git a/Crawler/State.h b/Crawler/State.h index 3191179a..2a41018d 100644 --- a/Crawler/State.h +++ b/Crawler/State.h @@ -1,18 +1,22 @@ #pragma once -enum State{ - NORMAL, - SWING_SWORD, - SWING_SONIC_SWORD, - SPIN, - MOVE_TOWARDS, - MOVE_AWAY, - BLOCK, - TELEPORT, - PATH_AROUND, - CASTING, - PREP_CAST, - SHOOT_ARROW, - RETREAT, - ANIMATION_LOCK, -}; \ No newline at end of file +namespace State{ + enum State{ + NORMAL, + SWING_SWORD, + SWING_SONIC_SWORD, + SPIN, + MOVE_TOWARDS, + MOVE_AWAY, + BLOCK, + TELEPORT, + PATH_AROUND, + CASTING, + PREP_CAST, + SHOOT_ARROW, + RETREAT, + ANIMATION_LOCK, + JUMP, + RECOVERY, + }; +} \ No newline at end of file diff --git a/Crawler/Version.h b/Crawler/Version.h index af864ac3..23cfb35a 100644 --- a/Crawler/Version.h +++ b/Crawler/Version.h @@ -2,7 +2,7 @@ #define VERSION_MAJOR 0 #define VERSION_MINOR 2 #define VERSION_PATCH 0 -#define VERSION_BUILD 1119 +#define VERSION_BUILD 1138 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Crawler/assets/config/MonsterStrategies.txt b/Crawler/assets/config/MonsterStrategies.txt index bb784bd2..994a4729 100644 --- a/Crawler/assets/config/MonsterStrategies.txt +++ b/Crawler/assets/config/MonsterStrategies.txt @@ -75,7 +75,7 @@ MonsterStrategy # How much time a jump will be pre-telegraphed. JumpWarningIndicatorTime = 1.0 # Distance to jump up into the sky. A higher value causes it to launch up and down seemingly faster. - JumpHeight = 1500 + JumpHeight = 300 ProjectileDamage = 10 JumpAttackDamage = 20 @@ -93,7 +93,6 @@ MonsterStrategy RingOffset = 10.0 JumpAfter = 4 shots AirborneTime = 3.0 - LandingRingCount = 1 LandingRecoveryTime = 2.0 } Phase2 diff --git a/Crawler/assets/config/gfx/gfx.txt b/Crawler/assets/config/gfx/gfx.txt index ef95be9e..212a1faf 100644 --- a/Crawler/assets/config/gfx/gfx.txt +++ b/Crawler/assets/config/gfx/gfx.txt @@ -29,4 +29,5 @@ Images GFX_Arrow = arrow.png GFX_Laser = laser.png GFX_ChargedArrow = charged_shot_arrow.png + GFX_RangeIndicator = range_indicator.png } \ No newline at end of file diff --git a/Crawler/assets/range_indicator.png b/Crawler/assets/range_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..5800ab446ca04c8d2cd967676ec6160c397f6e95 GIT binary patch literal 9035 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 hrA2v