|
|
|
@ -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().x<jumpTargetPos.x){ |
|
|
|
|
m.SetX(std::min(jumpTargetPos.x,m.GetPos().x+m.F(A::JUMP_MOVE_SPD)*game->GetElapsedTime())); |
|
|
|
|
} |
|
|
|
|
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().y<jumpTargetPos.y){ |
|
|
|
|
m.SetY(std::min(jumpTargetPos.y,m.GetPos().y+m.F(A::JUMP_MOVE_SPD)*game->GetElapsedTime())); |
|
|
|
|
} |
|
|
|
|
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::line<float>lineToPlayer(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<FallingDebris>(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:{ |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |