1
0
KF2-Dev-Scripts/KFGame/Classes/KFSpecialMoveHandler.uc

403 lines
13 KiB
Ucode
Raw Normal View History

2020-12-13 15:01:13 +00:00
//=============================================================================
// KFSpecialMoveHandler
//=============================================================================
// Manages starting and stopping special moves for pawns
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
// - Andrew "Strago" Ladenberger
//=============================================================================
class KFSpecialMoveHandler extends Object within KFPawn
config(Game);
/** Array matching above enumeration. List of classes to Instance */
var Array<class<KFSpecialMove> > SpecialMoveClasses;
/** Special move pending, activated after the current one terminates. */
var KFSpecialMoveStruct PendingSpecialMoveStruct;
/*********************************************************************************************
* Functions
*********************************************************************************************/
/**
* Start a special move.
* @Note this doesn't handle replication to owning client if called from server.
* See ServerDoSpecialMove() and LocalDoSpecialMove() for alternatives.
* @network: local player and server
*/
simulated event DoSpecialMove(ESpecialMove NewMove, optional bool bForceMove, optional Pawn InInteractionPawn, optional INT InSpecialMoveFlags, optional bool bSkipReplication, optional bool bIsPendingMove)
{
local ESpecialMove PrevMove;
`Log("New special move requested:" @ NewMove @ InInteractionPawn @ InSpecialMoveFlags @ "bForceMove:" @ bForceMove, bLogSpecialMove);
// Make sure NewMove is instanced.
if( NewMove != SM_None && !VerifySpecialMoveInstance(NewMove) )
{
`Warn(WorldInfo.TimeSeconds @ Self @ GetFuncName() @ "couldn't instance special move" @ NewMove);
return;
}
// If we are starting our pending special move, clear pending move struct
if( bIsPendingMove )
{
`Log("- PendingSpecialMove is being started.", bLogSpecialMove);
PendingSpecialMoveStruct.SpecialMove = SM_None;
PendingSpecialMoveStruct.InteractionPawn = None;
PendingSpecialMoveStruct.Flags = 0;
}
// Ignore redundant calls to the same move
if( NewMove == SpecialMove )
{
ReassignSpecialMove(InInteractionPawn, InSpecialMoveFlags, bSkipReplication);
return;
}
PrevMove = SpecialMove;
if ( !bIsPendingMove )
{
// If doing a special move transition, see if we can have an override or a chaining.
if( !bForceMove && SpecialMove != SM_None && NewMove != SM_None )
{
if ( !CanOverrideSpecialMove(NewMove, InInteractionPawn, InSpecialMoveFlags) )
{
return;
}
bForceMove = TRUE; // Override successful!
}
// Stop previous special move
if( SpecialMove != SM_None )
{
`Log("- leaving move:" @ SpecialMove, bLogSpecialMove);
// clear the special move so that checks like IsDoingSpecialMove and IsEvading no longer pass
SpecialMove = SM_None;
SpecialMoveEnded(PrevMove, NewMove);
}
// If we're ending a specialmove, and we have a pending one. Start it now.
if( NewMove == SM_None && PendingSpecialMoveStruct.SpecialMove != SM_None )
{
if ( DoPendingSpecialMove(bForceMove) )
{
return; // success, we can leave
}
}
}
// Check that we can do special move and special move has been/can be instanced
if( NewMove != SM_None && !bForceMove && !CanDoSpecialMove(NewMove) )
{
`Warn(WorldInfo.TimeSeconds @ Self @ GetFuncName() @ "cannot do requested special move" @ NewMove);
return;
}
// Set new special move
SpecialMove = NewMove;
InteractionPawn = KFPawn(InInteractionPawn);
SpecialMoveFlags = InSpecialMoveFlags;
if ( !bSkipReplication &&
(((WorldInfo.NetMode != NM_Standalone || WorldInfo.IsRecordingDemo()) && Role == ROLE_Authority) &&
(SpecialMove == SM_None || SpecialMoves[SpecialMove].ShouldReplicate())) )
{
ReplicateSpecialMove(NewMove, InInteractionPawn, InSpecialMoveFlags);
}
// if it's a valid special move
if( SpecialMove != SM_None )
{
// notify the special move it should start
SpecialMoveStarted(SpecialMove, PrevMove, bForceMove);
// if this was a forced move clear any pending moves since this was an interrupt of the current move
if( bForceMove )
{
PendingSpecialMoveStruct.SpecialMove = SM_None;
PendingSpecialMoveStruct.InteractionPawn = None;
PendingSpecialMoveStruct.Flags = 0;
}
}
}
/** helper for DoSpecialMove */
simulated private function bool CanOverrideSpecialMove(ESpecialMove NewMove, Pawn InInteractionPawn, int InSpecialMoveFlags)
{
local name SpecialMoveName, NewMoveName;
NewMoveName = SpecialMoves[NewMove].Handle;
SpecialMoveName = SpecialMoves[SpecialMove].Handle;
// See if we can override current special move, otherwise just queue new one until current is finished.
if( SpecialMoves[SpecialMove].CanOverrideMoveWith(NewMoveName) || SpecialMoves[NewMove].CanOverrideSpecialMove(SpecialMoveName) )
{
`Log("- Overriding" @ SpecialMove @ "with" @ NewMove @ "(previous pending:" @ PendingSpecialMoveStruct.SpecialMove $ ")", bLogSpecialMove);
return true;
}
else
{
// extra check to see if we can chain since non-owning clients can call DoSpecialMove directly in certain cases
if( SpecialMoves[SpecialMove].CanChainMove(NewMoveName) )
{
`Log("- chaining" @ NewMove @ "after" @ SpecialMove @ "(previous pending:" @ PendingSpecialMoveStruct.SpecialMove $ ")", bLogSpecialMove);
PendingSpecialMoveStruct.SpecialMove = NewMove;
PendingSpecialMoveStruct.InteractionPawn = InInteractionPawn;
PendingSpecialMoveStruct.Flags = InSpecialMoveFlags;
}
else
{
`Warn(WorldInfo.TimeSeconds @ Self @ GetFuncName() @ "Cannot override, cannot chain." @ NewMove @ "is lost! SpecialMove:" @ SpecialMove @ "Pending:" @ PendingSpecialMoveStruct.SpecialMove );
}
return false;
}
}
/** helper for DoSpecialMove */
simulated private function bool DoPendingSpecialMove(bool bForceMove)
{
local ESpecialMove NextMove;
`Log("- triggering pending special move:" @ PendingSpecialMoveStruct.SpecialMove, bLogSpecialMove);
NextMove = PendingSpecialMoveStruct.SpecialMove;
DoSpecialMove(PendingSpecialMoveStruct.SpecialMove, bForceMove, PendingSpecialMoveStruct.InteractionPawn, PendingSpecialMoveStruct.Flags, false, true);
if( SpecialMove == NextMove )
{
`Log("- PendingSpecialMove trigger successful for" @ NextMove, bLogSpecialMove);
// success, we can leave
return true;
}
else
{
`Log("- PendingSpecialMove trigger failed for" @ NextMove @ "Proceeding to clear SpecialMove", bLogSpecialMove);
return false;
}
}
/** helper for DoSpecialMove */
simulated private function ReassignSpecialMove(Pawn InInteractionPawn, int InSpecialMoveFlags, bool bSkipReplication)
{
if (InSpecialMoveFlags != SpecialMoveFlags)
{
`Log("- updating special move flags", bLogSpecialMove);
SpecialMoveFlags = InSpecialMoveFlags;
if (Role == ROLE_Authority && !bSkipReplication)
{
ReplicatedSpecialMove.Flags = SpecialMoveFlags;
bForceNetUpdate = true;
}
if ( SpecialMove != SM_None )
{
SpecialMoves[SpecialMove].SpecialMoveFlagsUpdated();
}
}
// In JIP situations, InteractionPawn will show up late
if (InInteractionPawn != InteractionPawn)
{
`Log("- updating interaction pawn", bLogSpecialMove);
InteractionPawn = KFPawn(InInteractionPawn);
if( Role == ROLE_Authority && !bSkipReplication )
{
ReplicatedSpecialMove.InteractionPawn = InteractionPawn;
bForceNetUpdate = true;
}
if( SpecialMove != SM_None )
{
SpecialMoves[SpecialMove].InteractionPawnUpdated();
}
}
`Log("- ignoring redundant call", bLogSpecialMove);
}
/** helper for DoSpecialMove */
simulated private function ReplicateSpecialMove(ESpecialMove NewMove, Pawn InInteractionPawn, int InSpecialMoveFlags)
{
`Log("- Replicating New SM" @ SpecialMove, bLogSpecialMove);
// Force replication now to non-owning clients
// If previous replicated SM and new one are the SM,
// we have to make sure something is different so it is correctly replicated.
if( ReplicatedSpecialMove.SpecialMove == NewMove )
{
// If SM_None, we can just bump up the flags, they don't matter in that case.
if( ReplicatedSpecialMove.SpecialMove == SM_None )
{
ReplicatedSpecialMove.Flags++;
}
else if( ReplicatedSpecialMove.InteractionPawn == InInteractionPawn
&& ReplicatedSpecialMove.Flags == InSpecialMoveFlags )
{
// Report an error, so it can be fixed.
`Warn("Special Move cannot be replicated properly!!!");
ScriptTrace();
}
else
{
// SMs are different, so they can be replicated properly.
ReplicatedSpecialMove.SpecialMove = NewMove;
ReplicatedSpecialMove.InteractionPawn = InInteractionPawn;
ReplicatedSpecialMove.Flags = InSpecialMoveFlags;
}
}
else
{
// SMs are different, so they can be replicated properly.
ReplicatedSpecialMove.SpecialMove = NewMove;
ReplicatedSpecialMove.InteractionPawn = InInteractionPawn;
ReplicatedSpecialMove.Flags = InSpecialMoveFlags;
}
bForceNetUpdate = TRUE;
}
/** helper for DoSpecialMove */
simulated final event bool CanDoSpecialMove(ESpecialMove AMove, optional bool bForceCheck)
{
// Can always end special moves.
if( AMove == SM_None )
{
return TRUE;
}
// if it is a valid move and we have a class for the move
if (AMove != SM_None && SpecialMoveClasses.length > AMove && SpecialMoveClasses[AMove] != None)
{
// Make sure special move is instanced
if( VerifySpecialMoveInstance(AMove) )
{
// and check the instance
return (CanChainSpecialMove(AMove) && SpecialMoves[AMove].CanDoSpecialMove(bForceCheck));
}
`log(GetFuncName() @ "Failed with special move:" @ AMove @ "class:" @ SpecialMoveClasses[AMove] @ Self);
}
return FALSE;
}
/** Returns TRUE if the pawn can chain this special move after the current one finishes (or if there currently isn't a special move) */
simulated final private function bool CanChainSpecialMove(ESpecialMove NextMove)
{
local name SpecialMoveName, NextMoveName;
NextMoveName = SpecialMoves[NextMove].Handle;
if ( SpecialMove != SM_None )
{
SpecialMoveName = SpecialMoves[SpecialMove].Handle;
}
return (SpecialMove == SM_None || SpecialMoves[SpecialMove].CanChainMove(NextMoveName) || SpecialMoves[SpecialMove].CanOverrideMoveWith(NextMoveName) || SpecialMoves[NextMove].CanOverrideSpecialMove(SpecialMoveName));
}
/** Event called when A new special move has started */
simulated final private function SpecialMoveStarted(ESpecialMove NewMove, ESpecialMove PrevMove, bool bForced)
{
local name PrevMoveName;
`Log("NewMove:" @ NewMove @ "PrevMove:" @ PrevMove, bLogSpecialMove);
if( NewMove != SM_None )
{
// forward notification to special move instance
if( SpecialMoves[NewMove] != None )
{
if ( PrevMove != SM_None )
{
PrevMoveName = SpecialMoves[PrevMove].Handle;
}
SpecialMoves[NewMove].SpecialMoveStarted(bForced, PrevMoveName);
}
}
}
/** Event called when A new special move has stopped */
simulated final function SpecialMoveEnded(ESpecialMove PrevMove, ESpecialMove NextMove)
{
local name NextMoveName;
`Log("PrevMove:" @ PrevMove, bLogSpecialMove);
if( PrevMove != SM_None && SpecialMoves[PrevMove] != None )
{
if ( NextMove != SM_None )
{
NextMoveName = SpecialMoves[NextMove].Handle;
}
SpecialMoves[PrevMove].SpecialMoveEnded(SpecialMoves[PrevMove].Handle, NextMoveName);
}
}
/** helper for DoSpecialMove */
simulated function bool VerifySpecialMoveInstance(ESpecialMove AMove)
{
if( AMove != SM_None )
{
if( AMove >= SpecialMoves.Length || SpecialMoves[AMove] == None )
{
if( AMove < SpecialMoveClasses.Length && SpecialMoveClasses[AMove] != None )
{
SpecialMoves[AMove] = new(Outer) SpecialMoveClasses[AMove];
// Cache a reference to the owner to avoid passing parameters around.
SpecialMoves[AMove].InitSpecialMove(Outer, 'MoveName');
}
else
{
`log(GetFuncName() @ "Failed with special move:" @ AMove @ "class:" @ SpecialMoveClasses[AMove] @ Self);
SpecialMoves[AMove] = None;
return FALSE;
}
}
return TRUE;
}
return FALSE;
}
/** */
simulated function ClearPendingSpecialMove()
{
`Log("- Clearing PendingSpecialMove:", bLogSpecialMove);
PendingSpecialMoveStruct.SpecialMove = SM_None;
PendingSpecialMoveStruct.InteractionPawn = None;
PendingSpecialMoveStruct.Flags = 0;
}
/**
* Request to abort/stop current SpecialMove
* @bForceNetSync - (deprecated) we're now sending ReplicatedSpecialMove to the owning client
*/
simulated final function EndSpecialMove(optional ESpecialMove SpecialMoveToEnd, optional bool bForceNetSync)
{
`Log("SpecialMoveToEnd:" @ SpecialMoveToEnd, bLogSpecialMove);
if( IsDoingSpecialMove() )
{
// clear the pending move
if( SpecialMoveToEnd != SM_None && PendingSpecialMoveStruct.SpecialMove == SpecialMoveToEnd )
{
ClearPendingSpecialMove();
}
// if no move specified, or it matches the current move
if( SpecialMoveToEnd == SM_None || IsDoingSpecialMove(SpecialMoveToEnd) )
{
// force it to end
DoSpecialMove(SM_None, TRUE);
}
}
}
defaultproperties
{
}