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

279 lines
8.0 KiB
Ucode
Raw Normal View History

2020-12-13 15:01:13 +00:00
//=============================================================================
// KFSM_Emerge
//=============================================================================
// Class Description
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
// - Author 3/14/2014
//=============================================================================
class KFSM_Emerge extends KFSpecialMove;
var() array<name> FloorEmerge;
var() array<name> Wall248Emerge;
var() array<name> WallHighEmerge;
var() array<name> CeilingEmerge;
var() float BlendOutTime;
/** If set, triggers a SM_RagdollKnockdown move at the end */
var bool bDoKnockdown;
/** Pack the animation index we'll be using for the zed to emerge */
static function byte PackAnimFlag( EEmergeAnim EmergeType, optional out byte LastAnimVariant )
{
local byte Variant;
local int NumAnims;
NumAnims = GetEmergeAnimNum(EmergeType);
if ( ( EmergeType == EMERGE_Wall248UU || EmergeType == EMERGE_WallHigh ) )
{
Variant = 0;
if ( NumAnims > 1 )
{
while ( Variant == LastAnimVariant )
{
Variant = Rand(NumAnims);
}
}
LastAnimVariant = Variant;
}
else
{
Variant = Rand(NumAnims);
}
// pack into two nibbles
return EmergeType + (Variant << 4);
}
static function int GetEmergeAnimNum( byte EmergeType )
{
switch( EmergeType )
{
case EMERGE_Floor:
return default.FloorEmerge.Length;
case EMERGE_Wall248UU:
return default.Wall248Emerge.Length;
case EMERGE_WallHigh:
return default.WallHighEmerge.Length;
case EMERGE_Ceiling:
return default.CeilingEmerge.Length;
}
`log(GetFuncName()@"Failed to find valid anim for:"$EmergeType);
}
function name GetEmergeAnim( byte EmergeType, int Variant )
{
switch( EmergeType )
{
case EMERGE_Floor:
return FloorEmerge[Variant];
case EMERGE_Wall248UU:
return Wall248Emerge[Variant];
case EMERGE_WallHigh:
return WallHighEmerge[Variant];
case EMERGE_Ceiling:
return CeilingEmerge[Variant];
}
`log(GetFuncName()@"Failed to find valid anim for:"$EmergeType@"variant:"$Variant);
}
/** Allow override from Knockdown (see ANIMNOTIFY_Knockdown) */
function bool CanOverrideMoveWith( Name NewMove )
{
if ( NewMove == 'KFSM_Knockdown' )
return true;
return Super.CanOverrideMoveWith(NewMove);;
}
/** Notification called when Special Move starts */
function SpecialMoveStarted(bool bForced, Name PrevMove )
{
super.SpecialMoveStarted( bForced, PrevMove );
PlayEmerge();
bDoKnockdown = false;
}
/** Stop the movement and play the stun animation for all clients */
function name PlayEmerge()
{
local byte Type, Variant;
local name EmergeAnim;
Type = KFPOwner.SpecialMoveFlags & 15;
Variant = KFPOwner.SpecialMoveFlags >> 4;
EmergeAnim = GetEmergeAnim(Type, Variant);
PlaySpecialMoveAnim( EmergeAnim, EAS_FullBody, 0.0f, BlendOutTime, 1.f, false );
// Turn on Root translation
EnableRootMotion();
// Use root rotation to rotate the actor.
KFPOwner.BodyStanceNodes[EAS_FullBody].SetRootBoneRotationOption(RRO_Extract, RRO_Extract, RRO_Extract);
KFPOwner.Mesh.RootMotionRotationMode = RMRM_RotateActor;
KFPOwner.SetCollision(KFPOwner.bCollideActors, FALSE);
KFPOwner.bCollideWorld = FALSE;
// Custom handler for destructibles
if( KFPOwner.WorldInfo.NetMode != NM_Client )
{
KFPOwner.SetTimer( 0.25f, true, nameOf(Timer_CheckForPortalDestructibles), self );
}
// Force always relevant to fix a bug where the actor becomes relevant midway through his root motion
// animation. bUpdateSimulatedPosition might be a alternative?
KFPOwner.bAlwaysRelevant = true;
return EmergeAnim;
}
/** Notification called when Special Move starts when ReplicatedSpecialMove changes*/
function SpecialMoveEnded(Name PrevMove, Name NextMove)
{
Super.SpecialMoveEnded(PrevMove, NextMove);
// Reset root rotation
KFPOwner.BodyStanceNodes[EAS_FullBody].SetRootBoneRotationOption(RRO_Discard, RRO_Discard, RRO_Discard);
KFPOwner.Mesh.RootMotionRotationMode = RMRM_Ignore;
// Reset root translation
DisableRootMotion();
KFPOwner.bAlwaysRelevant = PawnOwner.default.bAlwaysRelevant;
RestoreCollision();
if( KFPOwner.WorldInfo.NetMode != NM_Client )
{
KFPOwner.ClearTimer( nameOf(Timer_CheckForPortalDestructibles), self );
}
// Sometime after the move is over make sure our new AI was able to generate a valid path
if ( PawnOwner.Role == ROLE_Authority && !PawnOwner.IsHumanControlled() )
{
PawnOwner.SetTimer(5.f, false, nameof(FindAnchorFailsafe), self);
}
}
function Timer_CheckForPortalDestructibles()
{
local KFPawn_Monster KFPM;
local KFDestructibleActor KFDA;
if( KFPOwner.bCollideWorld )
{
return;
}
KFPM = KFPawn_Monster( KFPOwner );
if( KFPM != none )
{
foreach KFPOwner.OverlappingActors( class'KFDestructibleActor', KFDA, KFPOwner.CylinderComponent.CollisionRadius,, true )
{
KFDA.BumpedByMonster( KFPM, Normal(KFDA.Location - KFPOwner.Location) );
}
}
}
/**
* Notification called when body stance animation finished playing.
*/
function AnimEndNotify(AnimNodeSequence SeqNode, float PlayedTime, float ExcessTime)
{
// @hack: For knockdown use SM override isntead of ending this move. Ending it at the
// same time will trigger a SetPhysics call from AI state Action_Idle and break knockdown
if ( bDoKnockdown )
{
KFPOwner.Knockdown(PawnOwner.Velocity, vect(1,1,1));
}
// If we're still in emerge (e.g. no knockdown, knockdown failed) end normally
if ( KFPOwner.SpecialMove == SM_Emerge )
{
KFPOwner.EndSpecialMove();
}
}
/** When emerge is complete need to reenable collision settings */
function RestoreCollision()
{
KFPOwner.SetCollision(PawnOwner.default.bCollideActors, PawnOwner.default.bBlockActors);
KFPOwner.bCollideWorld = TRUE;
KFPOwner.FitCollision();
PushOverlappingHumans();
}
/** If the pawn is overlapping with another pawn at the end of emerging, give it a push back */
function PushOverlappingHumans()
{
local Pawn P;
if ( PawnOwner.Role < ROLE_Authority )
{
return;
}
foreach PawnOwner.WorldInfo.AllPawns(class'Pawn', P, PawnOwner.Location, PawnOwner.CylinderComponent.CollisionRadius)
{
if ( P == KFPOwner || P.Controller == none || !P.Controller.bIsPlayer )
{
continue;
}
P.Velocity += (100 + KFPOwner.CylinderComponent.CollisionRadius) * vector( KFPOwner.Rotation ) * 2.5f;
P.Velocity.Z = 200 + KFPOwner.CylinderComponent.CollisionHeight;
P.SetPhysics(PHYS_Falling);
}
}
/** Timer used to exist gracefully if something has gone wrong */
function AbortSpecialMove()
{
super.AbortSpecialMove();
// Kill the zed if it doesn't have the emerge animation yet
PawnOwner.Died( None, PawnOwner.WorldInfo.KillzDamageType, PawnOwner.Location );
}
/** Sometime after the move is over make sure our new AI was able to generate a valid path */
function FindAnchorFailsafe()
{
if ( PawnOwner.Role == ROLE_Authority && !PawnOwner.IsHumanControlled() )
{
if ( `TimeSinceEx(PawnOwner, PawnOwner.FindAnchorFailedTime) < 5.f)
{
`Warn("Zed unable to resume pathing after SM_Emerge"@PawnOwner@KFPOwner.SpecialMove);
PawnOwner.Died( None, PawnOwner.WorldInfo.KillzDamageType, PawnOwner.Location );
}
}
}
defaultproperties
{
Handle=SM_Emerge
SMRootMotionMode=RMM_Translate
bPawnRotationLocked=true
bDisablePhysics=true
bCanOnlyWanderAtEnd=true
bShouldDeferToPostTick=true
// animations
FloorEmerge=(Enter_Floor_V1, Enter_Floor_V2, Enter_Floor_V3)
Wall248Emerge=(Enter_vent_C_V1, Enter_vent_C_V2, Enter_vent_L_V1, Enter_vent_L_V2, Enter_vent_R_V1, Enter_vent_R_V2)
WallHighEmerge=(Enter_ventHigh_C_V1, Enter_ventHigh_C_V2, Enter_ventHigh_L_V1, Enter_ventHigh_L_V2, Enter_ventHigh_R_V1, Enter_ventHigh_R_V2)
CeilingEmerge=(Enter_Ceiling_V1, Enter_Ceiling_V2, Enter_Ceiling_V3, Enter_Ceiling_V4)
BlendOutTime=0.2
DefaultAICommandClass=class'KFGame.AICommand_PushedBySM'
}