From dc6d333e5bb75398cba05086f25292d03ca95b65 Mon Sep 17 00:00:00 2001 From: "sigonasr2, Sig, Sigo" Date: Fri, 8 Mar 2024 20:29:30 +0000 Subject: [PATCH] Add monster jumping inside run towards monster strategy. --- Adventures in Lestoria/MonsterAttribute.h | 1 + Adventures in Lestoria/RunTowards.cpp | 132 +++++++++++++++--- .../assets/config/MonsterStrategies.txt | 20 +++ 3 files changed, 130 insertions(+), 23 deletions(-) diff --git a/Adventures in Lestoria/MonsterAttribute.h b/Adventures in Lestoria/MonsterAttribute.h index 75a00553..d65d70c3 100644 --- a/Adventures in Lestoria/MonsterAttribute.h +++ b/Adventures in Lestoria/MonsterAttribute.h @@ -104,4 +104,5 @@ enum class Attribute{ ABILITY_COUNT, DEFENSIVE_COUNT, ITEM_USE_COUNT, + LAST_JUMP_TIMER, }; \ No newline at end of file diff --git a/Adventures in Lestoria/RunTowards.cpp b/Adventures in Lestoria/RunTowards.cpp index b3e34d7f..ea71f001 100644 --- a/Adventures in Lestoria/RunTowards.cpp +++ b/Adventures in Lestoria/RunTowards.cpp @@ -45,7 +45,11 @@ INCLUDE_game INCLUDE_MONSTER_DATA void Monster::STRATEGY::RUN_TOWARDS(Monster&m,float fElapsedTime,std::string strategy){ + const bool jumpingEnabled=ConfigFloat("JumpTimer")<0.f; + m.targetAcquireTimer=std::max(0.f,m.targetAcquireTimer-fElapsedTime); + m.F(A::JUMP_LANDING_TIMER)=std::max(0.f,m.F(A::JUMP_LANDING_TIMER)-fElapsedTime); + if(m.targetAcquireTimer==0){ m.targetAcquireTimer=ConfigFloat("WaitTime"); @@ -61,31 +65,113 @@ void Monster::STRATEGY::RUN_TOWARDS(Monster&m,float fElapsedTime,std::string str if(geom2d::line(m.pos,m.target).length()>4.f){ m.UpdateFacingDirection(m.target); } - switch(m.state){ - 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(); - m.SetPos(newPos); - if(m.GetPos()!=newPos){ - bool pathFound=m.StartPathfinding(4); - if(!pathFound){ - m.SetState(State::MOVE_TOWARDS); - //Choose a random position around us and move towards it. - float randomAngle=util::random(2*PI); - float randomDist=util::random(24*6); - m.target=m.GetPos()+vf2d{sin(randomAngle),cos(randomAngle)}*randomDist; - } - } - m.PerformJumpAnimation(); - } else { - m.SetState(State::NORMAL);//Revert state once we've finished moving towards target. - m.PerformIdleAnimation(); + + const auto StartJumpTowardsPlayer=[&](float jumpDuration,float recoveryTime,float jumpMoveSpd){ + m.F(A::JUMP_ORIGINAL_LANDING_TIMER)=m.F(A::JUMP_LANDING_TIMER)=jumpDuration; + m.B(A::JUMP_TOWARDS_PLAYER)=true; + m.F(A::RECOVERY_TIME)=recoveryTime; + m.F(A::JUMP_MOVE_SPD)=jumpMoveSpd; + m.F(A::LAST_JUMP_TIMER)=ConfigFloat("JumpTimer"); + m.SetState(State::JUMP); + }; + + switch(m.I(A::PHASE)){ + case 0:{//Setup jump behaviors. + if(jumpingEnabled){ + m.F(A::LAST_JUMP_TIMER)=ConfigFloat("JumpTimer"); } + + m.I(A::PHASE)++; }break; - case State::PATH_AROUND:{ - m.PathAroundBehavior(fElapsedTime); + case 1:{//Main behaviors. + switch(m.state){ + 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(); + m.SetPos(newPos); + if(m.GetPos()!=newPos){ + bool pathFound=m.StartPathfinding(4); + if(!pathFound){ + m.SetState(State::MOVE_TOWARDS); + //Choose a random position around us and move towards it. + float randomAngle=util::random(2*PI); + float randomDist=util::random(24*6); + m.target=m.GetPos()+vf2d{sin(randomAngle),cos(randomAngle)}*randomDist; + } + } + m.PerformJumpAnimation(); + } else { + m.SetState(State::NORMAL);//Revert state once we've finished moving towards target. + m.PerformIdleAnimation(); + } + if(jumpingEnabled){ + m.F(A::LAST_JUMP_TIMER)=std::max(0.f,m.F(A::LAST_JUMP_TIMER)-fElapsedTime); + if(m.F(A::LAST_JUMP_TIMER)==0.f){ + StartJumpTowardsPlayer(ConfigFloat("JumpDelayTime"),ConfigFloat("JumpRecoveryTime",ConfigFloat("JumpMoveSpd"))); + } + } + }break; + case State::PATH_AROUND:{ + m.PathAroundBehavior(fElapsedTime); + }break; + case State::JUMP:{ + float jumpLandingTimerRatio=m.F(A::JUMP_LANDING_TIMER)/m.F(A::JUMP_ORIGINAL_LANDING_TIMER); + vf2d jumpTargetPos=m.V(A::JUMP_TARGET_POS); + if(m.B(A::JUMP_TOWARDS_PLAYER)){ + jumpTargetPos=game->GetPlayer()->GetPos(); + } + if(m.GetPos().x>jumpTargetPos.x){ + m.SetX(std::max(jumpTargetPos.x,m.GetPos().x-m.F(A::JUMP_MOVE_SPD)*game->GetElapsedTime())); + } else + if(m.GetPos().xGetElapsedTime())); + } + if(m.GetPos().y>jumpTargetPos.y){ + m.SetY(std::max(jumpTargetPos.y,m.GetPos().y-m.F(A::JUMP_MOVE_SPD)*game->GetElapsedTime())); + } else + if(m.GetPos().yGetElapsedTime())); + } + if(m.F(A::JUMP_LANDING_TIMER)>=m.F(A::JUMP_ORIGINAL_LANDING_TIMER)/2){ + m.SetZ(util::lerp(0,float(ConfigInt("JumpHeight")),1-jumpLandingTimerRatio)); + }else{ + m.SetZ(util::lerp(0,float(ConfigInt("JumpHeight")),jumpLandingTimerRatio*2)); + } + if(m.F(A::JUMP_LANDING_TIMER)==0){ + m.state=State::RECOVERY; + game->SetupWorldShake(0.6f); + geom2d::linelineToPlayer(m.GetPos(),game->GetPlayer()->GetPos()); + float dist=lineToPlayer.length(); + for(int i=0;i<200;i++){ + float randomDir=util::random(2*PI); + game->AddEffect(std::make_unique(m.GetPos()+vf2d{cos(randomDir),sin(randomDir)}*m.GetSizeMult()*8,util::random(1),"circle.png",m.OnUpperLevel(),vf2d{1,1},0.5,vf2d{cos(randomDir)*util::random(5),sin(randomDir)*-util::random(15)-5}*30,BLACK),true); + } + if(dist<12*m.GetSizeMult()){ + int jumpDamage=0; + if(ConfigInt("JumpAttackDamage")>0)jumpDamage=ConfigInt("JumpAttackDamage"); + game->GetPlayer()->Hurt(jumpDamage,m.OnUpperLevel(),m.GetZ()); + if(dist<0.001){ + float randomDir=util::random(2*PI); + lineToPlayer={m.GetPos(),m.GetPos()+vf2d{cos(randomDir),sin(randomDir)}*1}; + } + game->GetPlayer()->Knockback(lineToPlayer.vector().norm()*float(ConfigInt("JumpKnockbackFactor"))); + if(m.phase!=2){ //In phase 2, the player can get slammed multiple times. No iframes for messing up. + game->GetPlayer()->SetIframes(1); + } + } + m.SetZ(0); + Landed(m.phase); + m.SetStrategyDrawFunction([](AiL*game,Monster&m,const std::string&strategy){}); + } else + if(m.F(A::JUMP_LANDING_TIMER)<=ConfigFloat("JumpWarningIndicatorTime")){ + m.SetStrategyDrawFunction([](AiL*game,Monster&m,const std::string&strategy){ + Decal*dec=GFX["range_indicator.png"].Decal(); + game->view.DrawRotatedDecal(m.GetPos(),dec,0,dec->sprite->Size()/2,vf2d{m.GetSizeMult(),m.GetSizeMult()},RED); + }); + } + }break; + default:{} + } }break; - default:{ - } } } \ No newline at end of file diff --git a/Adventures in Lestoria/assets/config/MonsterStrategies.txt b/Adventures in Lestoria/assets/config/MonsterStrategies.txt index 03c8ae80..91b7b48b 100644 --- a/Adventures in Lestoria/assets/config/MonsterStrategies.txt +++ b/Adventures in Lestoria/assets/config/MonsterStrategies.txt @@ -44,6 +44,26 @@ MonsterStrategy MaxDistance = 999999 # 1 of X chance to stop after bumping into something. BumpStopChance = 5 + + # How long to wait before deciding to perform a Jump. Set to -1 to disable jumping. + JumpTimer = -1.0 + # How much damage the jump attack does. Uses the monster's attack stat if set to -1. + JumpAttackDamage = -1 + # How far the player gets knocked back if hit. + JumpKnockbackFactor = 0.0 + # How fast to move during a jump attack. + JumpMoveSpd = 100 + # How long the monster is in the air for. + JumpDelayTime = 1.2 + # How long the monster waits after landing. + JumpRecoveryTime = 2.0 + # 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 = 750 + # Maximum distance the player can be away from the enemy for a jump to occur. + MaxPlayerJumpEngageDistance = 900 + } Shoot Afar {