1
0
KF2-Dev-Scripts/KFGame/Classes/AICommand_ScriptedPawn_TraverseSpline.uc
2020-12-13 18:01:13 +03:00

138 lines
4.2 KiB
Ucode

//=============================================================================
// AICommand_ScriptedPawn_TraverseSpline
//=============================================================================
// AICommand to get a pawn to follow a spline
//=============================================================================
// Killing Floor 2
// Copyright (C) 2018 Tripwire Interactive LLC
//=============================================================================
class AICommand_ScriptedPawn_TraverseSpline extends AICommand
within KFAIController;
var transient int SegmentGranularity;
var transient KFPawn_Scripted MyScriptedPawn;
var transient array<SplineActor> Route;
var transient int SegIdx, SubSegIdx;
var transient vector Direction, SubSegEnd;
var transient SplineComponent SegSplineComp;
var transient float SegSplineLen;
static function bool TraverseSpline(KFAIController AI, SplineActor SplineStart, SplineActor SplineEnd, int Granularity)
{
local AICommand_ScriptedPawn_TraverseSpline Cmd;
if( AI != none && SplineStart != none )
{
Cmd = new(AI) class'AICommand_ScriptedPawn_TraverseSpline';
if( Cmd != none )
{
SplineStart.FindSplinePathTo(SplineEnd, Cmd.Route);
Cmd.SegmentGranularity = Granularity;
AI.PushCommand( Cmd );
return true;
}
}
return false;
}
function Pushed()
{
local SplineActor SplineEnd;
Super.Pushed();
MyScriptedPawn = KFPawn_Scripted(Pawn);
// The route represents the path between the pawn's spawn location and its goal, but it's allowed
// to continue after its goal if there are more spline actors that the goal connects to, so add
// those to the end of the route
SplineEnd = Route[Route.Length - 1];
while (SplineEnd.Connections.Length > 0)
{
SplineEnd = SplineEnd.Connections[0].ConnectTo;
Route.AddItem(SplineEnd);
}
GotoState('TraversingSpline');
}
function Paused( GameAICommand NewCommand )
{
Super.Paused( NewCommand );
}
function Resumed( Name OldCommandName )
{
Super.Resumed( OldCommandName );
}
function Popped()
{
Super.Popped();
Route.Length = 0;
MyScriptedPawn.EndedRoute(Status == 'Success');
}
function bool PawnReachedDestination(vector Dest)
{
return VSize2D(MyScriptedPawn.Location - Dest) < 10;
}
state TraversingSpline
{
Begin:
if (Route.Length == 0)
{
Status = 'Failure';
}
else
{
// consider the pawn to have "reached" the node it spawns at
MyScriptedPawn.ReachedRouteMarker(0, Route[0], 0, 0);
SegIdx = 1;
do // for each segment (space between two spline actors)...
{
SegSplineComp = Route[SegIdx-1].FindSplineComponentTo(Route[SegIdx]);
SegSplineLen = SegSplineComp.GetSplineLength();
SubSegIdx = 1;
do // for each sub-segment (space between segment "granules")...
{
SubSegEnd = SegSplineComp != none ?
SegSplineComp.GetLocationAtDistanceAlongSpline((float(SubSegIdx)/float(SegmentGranularity+1))*SegSplineLen) :
Route[SegIdx].Location;
do // update velocity and rotation toward sub-segment
{
Direction = SubSegEnd - Pawn.Location;
if (MyScriptedPawn.Physics == PHYS_Walking)
{
Direction.Z = 0.f;
}
Direction = Normal(Direction);
MyScriptedPawn.Velocity = Direction * MyScriptedPawn.GroundSpeed;
MyScriptedPawn.SetDesiredRotation(rotator(Direction),,,0.1f);
Sleep(0.f);
} until (PawnReachedDestination(SubSegEnd)); // until we've reached the next segment "granule"
MyScriptedPawn.ReachedRouteMarker(
SegIdx,
SubSegIdx == (SegmentGranularity + 1) ? Route[SegIdx] : none,
SubSegIdx,
SegSplineLen / float(SegmentGranularity+1));
++SubSegIdx;
} until (PawnReachedDestination(Route[SegIdx].Location) && SubSegIdx == (SegmentGranularity + 2)); // until we've reached the next spline actor AND we've reached each "granule" ...
// ... because we can get close enough to the next spline actor before we've made sure we've reached each "granule", and that can throw off logic
++SegIdx;
} until (SegIdx == Route.Length); // until we've reached the last spline actor in the route
MyScriptedPawn.Acceleration = vect(0, 0, 0);
Status = 'Success';
}
PopCommand(self);
}