284 lines
7.1 KiB
Ucode
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
|
|
}
|