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

284 lines
7.1 KiB
Ucode

//=============================================================================
// KFEmit_Path
//=============================================================================
// Path emitter
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
// - Christian "schneidzekk" Schneider
//============================================================================
class KFEmit_Path extends Emitter
config(Game)
abstract;
const MAX_WAYPOINTS = 24;
/** custom height offset for waypoints */
var const int WAYPOINT_HEIGHT;
/** custom height offset for waypoints on nav mesh */
var const int WAYPOINT_HEIGHT_NAV_MESH;
var const float MinDistanceBetweenWayPointsOnNavMesh;
/** The Template to use for this emitter */
var ParticleSystem EmitterTemplate;
/** path points to travel to */
var array<vector> WayPoints;
/** total number of valid points in WayPoints list */
var int NumPoints;
/** current position in WayPoints list */
var int Position;
/*********************************************************************************************
* Acceleration physics (KF1 style trails)
********************************************************************************************* */
/** Increases velocity correction to tigthen curve */
var const float CURVE_TURNRATE;
/** Acceleration magnitude */
var const float ACCEL_RATE;
/** If set, use KF1 style acceleration movement physics */
var bool bUseAccelerationPhysics;
/** heading direction used for accel based movement */
var bool bHeadedRight;
var const bool bShowEmitPathDebugArtifacts;
simulated function PostBeginPlay()
{
local int i, Start;
local KFPlayerController P;
local Actor HitActor;
local Vector HitLocation,HitNormal;
Super.PostBeginPlay();
P = KFPlayerController(Owner);
if ( P == None || P.Pawn == None )
{
Destroy();
}
else
{
SetTemplate(EmitterTemplate,true);
SetLocation(P.Pawn.Location);
WayPoints[0] = Location + (WAYPOINT_HEIGHT * vect(0,0,1)) + (200.0 * vector(P.Rotation));
HitActor = Trace(HitLocation, HitNormal, WayPoints[0], Location, false);
if ( HitActor != None )
{
WayPoints[0] = HitLocation;
}
NumPoints++;
if( class'KFAIController'.default.bUseNavMesh )
{
//BuildWaypointsFromNavMeshPathCache();
//if( class'KFAIController'.default.bShowDoorNavigationDebugArtifacts )
//{
// class'KFAIController'.static.ShowAdvancedRouteEdgeDebugInfo( P.MyKFNavigationHandle, true );
//}
}
else
{
if ( P.RouteCache[0] != None && P.RouteCache.length > 1 && P.ActorReachable(P.RouteCache[1]) )
{
Start = 1;
}
for (i = Start; NumPoints < MAX_WAYPOINTS && i < P.RouteCache.length && P.RouteCache[i] != None; i++)
{
WayPoints[NumPoints++] = P.RouteCache[i].Location + (WAYPOINT_HEIGHT * vect(0,0,1));
}
}
StartNextPath();
}
}
/** Override the final waypoint (optional) */
simulated function SetDestination(vector Destination)
{
if ( NumPoints > 1 )
{
WayPoints[NumPoints-1] = Destination;
}
}
simulated event SetInitialState()
{
bScriptInitialized = true;
if ( PlayerController(Owner).IsLocalPlayerController() )
{
StartNextPath();
}
else
{
//@warning: can't set bHidden because that would get replicated
ParticleSystemComponent.DeactivateSystem();
SetPhysics(PHYS_None);
}
}
simulated event SetTemplate(ParticleSystem NewTemplate, optional bool bDestroyOnFinish)
{
Super(Emitter).SetTemplate(NewTemplate, bDestroyOnFinish);
}
simulated function StartNextPath()
{
local Pawn OwnerPawn;
local KFPlayerController KFPC;
KFPC = KFPlayerController(Owner);
if ( ShouldStopPathing() )
{
Destroy();
return;
}
++Position;
if ( Position >= NumPoints || Position >= WayPoints.Length )
{
LifeSpan = 5.0;
Velocity = vect(0,0,0);
ParticleSystemComponent.DeactivateSystem();
SetPhysics(PHYS_None);
GotoState('');
}
else if ( bUseAccelerationPhysics )
{
OwnerPawn = (KFPC != None) ? KFPC.Pawn : None;
if ( Position == 0 && OwnerPawn != None )
{
SetLocation(OwnerPawn.Location + (WAYPOINT_HEIGHT * vect(0,0,1)));
// initial forward velocity to make the path loop around
Velocity = 4000 * CURVE_TURNRATE * Normal(WayPoints[0] - Location) + OwnerPawn.Velocity;
}
bHeadedRight = false;
Acceleration = ACCEL_RATE * Normal(WayPoints[Position] - Location);
Velocity *= 0.5;
Velocity.Z = 0.5 * (Velocity.Z + Acceleration.Z);
SetRotation(rotator(WayPoints[Position] - Location));
GotoState('Pathing');
}
else
{
OwnerPawn = (KFPC != None) ? KFPC.Pawn : None;
if ( Position == 0 && OwnerPawn != None )
{
SetLocation(OwnerPawn.Location + (WAYPOINT_HEIGHT * vect(0,0,1)) + vector(OwnerPawn.Rotation) * 100.0);
}
SetRotation(rotator(WayPoints[Position] - Location));
GotoState('Pathing');
}
}
simulated function bool ShouldStopPathing()
{
local KFPlayerController KFPC;
KFPC = KFPlayerController(Owner);
if( KFPC != none && KFPC.Pawn != none )
{
return KFPC.Pawn.IsA('KFPawn_Customization');
}
return false;
}
state Pathing
{
simulated function Tick(float DeltaTime)
{
local KFPlayerController KFPC;
KFPC = KFPlayerController(Owner);
if ( ShouldStopPathing() )
{
Destroy();
return;
}
//DrawDebugSphere(Location, 12, 12, 255, 0, 0, false);
if ( !bUseAccelerationPhysics )
{
TickUDK(DeltaTime, KFPC.Pawn);
}
else
{
TickClassic(DeltaTime);
}
}
/** Velocity based movement based on UDK */
simulated function TickUDK(float DeltaTime, Pawn OwnerPawn)
{
local float MaxSpeed;
SetRotation (rotator(WayPoints[Position] - Location));
if ( OwnerPawn != None )
{
if ( VSize(Location - OwnerPawn.Location) < OwnerPawn.GroundSpeed && vector(Rotation) dot vector(OwnerPawn.Rotation) > 0.0 )
{
// go faster when near owner to get some separation
MaxSpeed = OwnerPawn.GroundSpeed + 400.0;
}
else
{
MaxSpeed = VSize(OwnerPawn.Velocity) + 200.0;
RotationRate = default.RotationRate;
}
}
else
{
MaxSpeed = 200.0;
}
Velocity = vector(Rotation) * MaxSpeed;
if (VSize(WayPoints[Position] - Location) < FMax(80.0, VSize(Velocity) * DeltaTime * 3.0))
{
StartNextPath();
}
}
/** Acceleration based movement from KF1 */
simulated function TickClassic(float DeltaTime)
{
Acceleration = ACCEL_RATE * Normal(WayPoints[Position] - Location);
Velocity = Velocity + CURVE_TURNRATE * DeltaTime * Acceleration; // force double acceleration
// use heading change to detect if we curved around WayPoint.
if ( !bHeadedRight )
bHeadedRight = ( (Velocity Dot Acceleration) > 0 );
else if ( Velocity Dot Acceleration < 0 )
StartNextPath();
if ( WayPoints.Length > Position && VSize(WayPoints[Position] - Location) < 80 )
StartNextPath();
}
simulated function BeginState(name PreviousStateName)
{
SetPhysics(PHYS_Projectile);
}
}
defaultproperties
{
WAYPOINT_HEIGHT_NAV_MESH=25
MinDistanceBetweenWayPointsOnNavMesh=250
}