329 lines
8.1 KiB
Ucode
329 lines
8.1 KiB
Ucode
|
//=============================================================================
|
||
|
// KFDemoRecSpectator
|
||
|
//=============================================================================
|
||
|
// Specator class to support demo recording/playback functionality
|
||
|
// TODO: Can (and probably should) rename this to DemoRecSpecator after
|
||
|
// We get the UDK/UT stuff out of our build folder. Named it KFDemoRecSpectator
|
||
|
// For now to avoid class name conflicts. The functionality in this class
|
||
|
// also needs to be refined for KF2
|
||
|
//=============================================================================
|
||
|
// Killing Floor 2
|
||
|
// Copyright (C) 2015 Tripwire Interactive LLC
|
||
|
// - John "Ramm-Jaeger" Gibson
|
||
|
// Portions based off of DemoRecSpectator Copyright 1998-2012 Epic Games, Inc.
|
||
|
// All Rights Reserved.
|
||
|
//=============================================================================
|
||
|
|
||
|
class KFDemoRecSpectator extends KFPlayerController;
|
||
|
|
||
|
var bool bFindPlayer;
|
||
|
|
||
|
/** local copy of RealViewTarget as the C++ code might clear it in some cases we don't want to for demo spectators */
|
||
|
var PlayerReplicationInfo MyRealViewTarget;
|
||
|
|
||
|
/** if set, camera rotation is always forced to viewtarget rotation */
|
||
|
var config bool bLockRotationToViewTarget;
|
||
|
|
||
|
/** If set, automatically switches players every AutoSwitchPlayerInterval seconds */
|
||
|
var config bool bAutoSwitchPlayers;
|
||
|
|
||
|
/** Interval to use if bAutoSwitchPlayers is TRUE */
|
||
|
var config float AutoSwitchPlayerInterval;
|
||
|
|
||
|
simulated event PostBeginPlay()
|
||
|
{
|
||
|
Super.PostBeginPlay();
|
||
|
|
||
|
if ( PlayerReplicationInfo != None )
|
||
|
{
|
||
|
PlayerReplicationInfo.bOutOfLives = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
simulated event ReceivedPlayer()
|
||
|
{
|
||
|
Super.ReceivedPlayer();
|
||
|
|
||
|
// DemoRecSpectators don't go through the login process, so manually call ClientSetHUD()
|
||
|
// so the spectator has it when playing back the demo
|
||
|
if (Role == ROLE_Authority && WorldInfo.Game != None)
|
||
|
{
|
||
|
ClientSetHUD(WorldInfo.Game.HUDType);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function InitPlayerReplicationInfo()
|
||
|
{
|
||
|
Super.InitPlayerReplicationInfo();
|
||
|
PlayerReplicationInfo.PlayerName = "DemoRecSpectator";
|
||
|
PlayerReplicationInfo.bIsSpectator = true;
|
||
|
PlayerReplicationInfo.bOnlySpectator = true;
|
||
|
PlayerReplicationInfo.bOutOfLives = true;
|
||
|
PlayerReplicationInfo.bWaitingPlayer = false;
|
||
|
}
|
||
|
|
||
|
/** Prevent Scaleform from hogging input so that RypelCam demo playback controls work */
|
||
|
function SetGFxHUD( KFGFxMoviePlayer_HUD NewGFxHud )
|
||
|
{
|
||
|
super.SetGFxHUD( NewGFxHud );
|
||
|
if( NewGFxHud != none )
|
||
|
{
|
||
|
NewGFxHud.bCaptureInput = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exec function Slomo(float NewTimeDilation)
|
||
|
{
|
||
|
WorldInfo.DemoPlayTimeDilation = NewTimeDilation;
|
||
|
}
|
||
|
|
||
|
exec function ViewClass( class<actor> aClass, optional bool bQuiet, optional bool bCheat )
|
||
|
{
|
||
|
local actor other, first;
|
||
|
local bool bFound;
|
||
|
|
||
|
first = None;
|
||
|
|
||
|
ForEach AllActors( aClass, other )
|
||
|
{
|
||
|
if ( bFound || (first == None) )
|
||
|
{
|
||
|
first = other;
|
||
|
if ( bFound )
|
||
|
break;
|
||
|
}
|
||
|
if ( other == ViewTarget )
|
||
|
bFound = true;
|
||
|
}
|
||
|
|
||
|
if ( first != None )
|
||
|
{
|
||
|
SetViewTarget(first);
|
||
|
//SetBehindView(ViewTarget != self);
|
||
|
}
|
||
|
else
|
||
|
SetViewTarget(self);
|
||
|
}
|
||
|
|
||
|
//==== Called during demo playback ============================================
|
||
|
|
||
|
exec function DemoViewNextPlayer()
|
||
|
{
|
||
|
local Pawn P, Pick;
|
||
|
local bool bFound;
|
||
|
|
||
|
// view next player
|
||
|
foreach WorldInfo.AllPawns(class'Pawn', P)
|
||
|
{
|
||
|
if (P.PlayerReplicationInfo != None)
|
||
|
{
|
||
|
if (Pick == None)
|
||
|
{
|
||
|
Pick = P;
|
||
|
}
|
||
|
if (bFound)
|
||
|
{
|
||
|
Pick = P;
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bFound = (RealViewTarget == P.PlayerReplicationInfo || ViewTarget == P);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SetViewTarget(Pick);
|
||
|
}
|
||
|
|
||
|
function SetViewTarget(Actor NewViewTarget, optional ViewTargetTransitionParams TransitionParams)
|
||
|
{
|
||
|
Super.SetViewTarget(NewViewTarget, TransitionParams);
|
||
|
|
||
|
// this check is so that a Pawn getting gibbed doesn't break finding that player again
|
||
|
// must manually clear MyRealViewTarget when player controlled switch back to viewing self
|
||
|
if (NewViewTarget != self)
|
||
|
{
|
||
|
MyRealViewTarget = RealViewTarget;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unreliable server function ServerViewSelf(optional ViewTargetTransitionParams TransitionParams)
|
||
|
{
|
||
|
Super.ServerViewSelf(TransitionParams);
|
||
|
|
||
|
MyRealViewTarget = None;
|
||
|
}
|
||
|
|
||
|
reliable client function ClientSetRealViewTarget(PlayerReplicationInfo NewTarget)
|
||
|
{
|
||
|
SetViewTarget(self); // will find Pawn from RealViewTarget next tick
|
||
|
RealViewTarget = NewTarget;
|
||
|
MyRealViewTarget = NewTarget;
|
||
|
bFindPlayer = (NewTarget == None);
|
||
|
}
|
||
|
|
||
|
function bool SetPause(bool bPause, optional delegate<CanUnpause> CanUnpauseDelegate = CanUnpause)
|
||
|
{
|
||
|
// allow the spectator to pause demo playback
|
||
|
if (WorldInfo.NetMode == NM_Client)
|
||
|
{
|
||
|
WorldInfo.Pauser = (bPause) ? PlayerReplicationInfo : None;
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exec function Pause()
|
||
|
{
|
||
|
if (WorldInfo.NetMode == NM_Client)
|
||
|
{
|
||
|
ServerPause();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
auto state Spectating
|
||
|
{
|
||
|
function BeginState(Name PreviousStateName)
|
||
|
{
|
||
|
super.BeginState(PreviousStateName);
|
||
|
|
||
|
if( bAutoSwitchPlayers )
|
||
|
{
|
||
|
SetTimer( AutoSwitchPlayerInterval, true, nameof( DemoViewNextPlayer ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exec function StartFire(optional byte FireModeNum)
|
||
|
{
|
||
|
// SetBehindView(true);
|
||
|
DemoViewNextPlayer();
|
||
|
}
|
||
|
|
||
|
/** used to start out the demo view on the local player - should be called when recording, not playback */
|
||
|
function SendInitialViewTarget()
|
||
|
{
|
||
|
local PlayerController PC;
|
||
|
|
||
|
foreach LocalPlayerControllers(class'PlayerController', PC)
|
||
|
{
|
||
|
if (!PC.PlayerReplicationInfo.bOnlySpectator)
|
||
|
{
|
||
|
ClientSetRealViewTarget(PC.PlayerReplicationInfo);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
// send None so demo playback knows it should just pick the first Pawn it can find
|
||
|
ClientSetRealViewTarget(None);
|
||
|
}
|
||
|
|
||
|
simulated event GetPlayerViewPoint(out vector CameraLocation, out rotator CameraRotation)
|
||
|
{
|
||
|
Global.GetPlayerViewPoint(CameraLocation, CameraRotation);
|
||
|
}
|
||
|
|
||
|
// exec function BehindView()
|
||
|
// {
|
||
|
// SetBehindView(!bBehindView);
|
||
|
// }
|
||
|
|
||
|
event PlayerTick( float DeltaTime )
|
||
|
{
|
||
|
local Pawn P;
|
||
|
|
||
|
Global.PlayerTick( DeltaTime );
|
||
|
|
||
|
// attempt to find a player to view.
|
||
|
if (Role == ROLE_AutonomousProxy)
|
||
|
{
|
||
|
if (RealViewTarget == None && MyRealViewTarget != None)
|
||
|
{
|
||
|
RealViewTarget = MyRealViewTarget;
|
||
|
}
|
||
|
|
||
|
if ((RealViewTarget==None || RealViewTarget==PlayerReplicationInfo) && bFindPlayer)
|
||
|
{
|
||
|
DemoViewNextPlayer();
|
||
|
if (RealViewTarget != None && RealViewTarget != PlayerReplicationInfo)
|
||
|
{
|
||
|
bFindPlayer = false;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// reacquire ViewTarget if the player switched Pawns
|
||
|
if ( RealViewTarget != None && RealViewTarget != PlayerReplicationInfo &&
|
||
|
(Pawn(ViewTarget) == None || Pawn(ViewTarget).PlayerReplicationInfo != RealViewTarget) )
|
||
|
{
|
||
|
foreach WorldInfo.AllPawns(class'Pawn', P)
|
||
|
{
|
||
|
if (P.PlayerReplicationInfo == RealViewTarget)
|
||
|
{
|
||
|
SetViewTarget(P);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (Pawn(ViewTarget) != None)
|
||
|
{
|
||
|
TargetViewRotation = ViewTarget.Rotation;
|
||
|
TargetViewRotation.Pitch = Pawn(ViewTarget).RemoteViewPitch << 8;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
Begin:
|
||
|
if (Role == ROLE_Authority)
|
||
|
{
|
||
|
// it takes two ticks to guarantee that all the relevant actors have been recorded into the demo
|
||
|
// (necessary for the reference in ClientSetRealViewTarget()'s parameter to be valid during playback)
|
||
|
Sleep(0.0);
|
||
|
Sleep(0.0);
|
||
|
SendInitialViewTarget();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
simulated event GetPlayerViewPoint(out vector CameraLocation, out rotator CameraRotation)
|
||
|
{
|
||
|
// bFreeCamera = (!bLockRotationToViewTarget && (bBehindView || Vehicle(ViewTarget) != None));
|
||
|
Super.GetPlayerViewPoint(CameraLocation, CameraRotation);
|
||
|
}
|
||
|
|
||
|
function UpdateRotation(float DeltaTime)
|
||
|
{
|
||
|
local rotator NewRotation;
|
||
|
|
||
|
if (bLockRotationToViewTarget)
|
||
|
{
|
||
|
SetRotation(ViewTarget.Rotation);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Super.UpdateRotation(DeltaTime);
|
||
|
}
|
||
|
|
||
|
if (Rotation.Roll != 0)
|
||
|
{
|
||
|
NewRotation = Rotation;
|
||
|
NewRotation.Roll = 0;
|
||
|
SetRotation(NewRotation);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exec function FOV(float F)
|
||
|
{
|
||
|
// skip over neutered kfplayercontroller version
|
||
|
super(PlayerController).FOV(F);
|
||
|
}
|
||
|
|
||
|
defaultproperties
|
||
|
{
|
||
|
RemoteRole=ROLE_AutonomousProxy
|
||
|
bDemoOwner=1
|
||
|
}
|
||
|
|