211 lines
5.5 KiB
Ucode
211 lines
5.5 KiB
Ucode
//=============================================================================
|
|
// KFSM_Stumble
|
|
//=============================================================================
|
|
// Stumble (aka Stagger, Knockback) on damage
|
|
//=============================================================================
|
|
// Killing Floor 2
|
|
// Copyright (C) 2015 Tripwire Interactive LLC
|
|
//=============================================================================
|
|
|
|
class KFSM_Stumble extends KFSM_PlaySingleAnim;
|
|
|
|
/** Anims are pulled from the pawn's AnimInfo class (see KFPawnAnimInfo) */
|
|
enum EStubmleAnim
|
|
{
|
|
ESA_Body_Forward,
|
|
ESA_Body_Backward,
|
|
ESA_Body_Left,
|
|
ESA_Body_Right,
|
|
ESA_Leg_L_Forward,
|
|
ESA_Leg_L_Backward,
|
|
ESA_Leg_L_Left,
|
|
ESA_Leg_L_Right,
|
|
ESA_Leg_R_Forward,
|
|
ESA_Leg_R_Backward,
|
|
ESA_Leg_R_Left,
|
|
ESA_Leg_R_Right,
|
|
ESA_Parry,
|
|
};
|
|
|
|
/** If true, try to perform a leg stumble */
|
|
static function bool ShouldDoLegStumble(KFPawn P, EHitZoneBodyPart HitZoneLimb)
|
|
{
|
|
if ( HitZoneLimb == BP_LeftLeg || HitZoneLimb == BP_RightLeg )
|
|
{
|
|
return (VSizeSq(P.Velocity) > 100.f);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static function byte PackBodyHitSMFlags(KFPawn P, vector HitDir)
|
|
{
|
|
local EPawnOctant HitRegion;
|
|
local byte Type;
|
|
|
|
HitRegion = P.CalcOctagonRegion(P.Rotation, -Normal(HitDir));
|
|
Type = GetStumbleTypeFromRegion(HitRegion);
|
|
|
|
// pack into two nibbles
|
|
return Type + (GetRandomVariant(P, Type) << 4);
|
|
}
|
|
|
|
static function byte PackLegHitSMFlags(KFPawn P, vector HitDir, bool bLeftLeg)
|
|
{
|
|
local EPawnOctant HitRegion;
|
|
local byte Type;
|
|
|
|
if ( P.Floor.Z < 0.98f )
|
|
{
|
|
// get direction based on footing slope
|
|
HitRegion = P.CalcOctagonRegion(P.Rotation, Normal2D(P.Floor));
|
|
Type = GetStumbleTypeFromRegion(HitRegion);
|
|
}
|
|
else
|
|
{
|
|
// get 4-way direction from hit (random reverse)
|
|
HitRegion = P.CalcOctagonRegion(P.Rotation, -Normal(HitDir));
|
|
Type = GetStumbleTypeFromRegion(HitRegion, true);
|
|
}
|
|
|
|
// offset anim index by leg
|
|
if ( bLeftLeg )
|
|
{
|
|
Type += ESA_Leg_L_Forward;
|
|
}
|
|
else
|
|
{
|
|
Type += ESA_Leg_R_Forward;
|
|
}
|
|
|
|
return Type + (GetRandomVariant(P, Type) << 4);
|
|
}
|
|
|
|
/** Initialize flags for parry animation type */
|
|
static function byte PackParrySMFlags(KFPawn P, vector HitDir)
|
|
{
|
|
return ESA_Parry + (GetRandomVariant(P, ESA_Parry) << 4);
|
|
}
|
|
|
|
/** Debugging */
|
|
static function byte PackRandomSMFlags(KFPawn P)
|
|
{
|
|
local byte Type;
|
|
Type = Rand(ESA_MAX);
|
|
return Type + (GetRandomVariant(P, Type) << 4);
|
|
}
|
|
|
|
/** Helper for PackSMFlags */
|
|
static function byte GetRandomVariant(KFPawn P, byte Type)
|
|
{
|
|
local int NumVariants;
|
|
NumVariants = P.PawnAnimInfo.StumbleAnims[Type].Anims.Length;
|
|
return Rand(NumVariants);
|
|
}
|
|
|
|
/** Helper for PackSMFlags */
|
|
static function byte GetStumbleTypeFromRegion(EPawnOctant HitRegion, optional bool bRandomReverse)
|
|
{
|
|
if ( bRandomReverse )
|
|
{
|
|
switch (HitRegion)
|
|
{
|
|
case DIR_Forward:
|
|
case DIR_Backward:
|
|
return (FRand() > 0.5) ? ESA_Body_Forward : ESA_Body_Backward;
|
|
case DIR_Left:
|
|
case DIR_Right:
|
|
return (FRand() > 0.5) ? ESA_Body_Left : ESA_Body_Right;
|
|
case DIR_ForwardLeft:
|
|
case DIR_ForwardRight:
|
|
case DIR_BackwardLeft:
|
|
case DIR_BackwardRight:
|
|
return ESA_Body_Forward + Rand(4);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (HitRegion)
|
|
{
|
|
case DIR_Forward:
|
|
return ESA_Body_Forward;
|
|
case DIR_ForwardLeft:
|
|
return (FRand() > 0.5) ? ESA_Body_Forward : ESA_Body_Left;
|
|
case DIR_ForwardRight:
|
|
return (FRand() > 0.5) ? ESA_Body_Forward : ESA_Body_Right;
|
|
case DIR_Backward:
|
|
return ESA_Body_Backward;
|
|
case DIR_BackwardLeft:
|
|
return (FRand() > 0.5) ? ESA_Body_Backward : ESA_Body_Left;
|
|
case DIR_BackwardRight:
|
|
return (FRand() > 0.5) ? ESA_Body_Backward : ESA_Body_Right;
|
|
case DIR_Left:
|
|
return ESA_Body_Left;
|
|
case DIR_Right:
|
|
return ESA_Body_Right;
|
|
}
|
|
}
|
|
|
|
return ESA_Body_Forward;
|
|
}
|
|
|
|
/**
|
|
* Checks to see if this Special Move can be done.
|
|
*/
|
|
protected function bool InternalCanDoSpecialMove()
|
|
{
|
|
// Only valid while walking or falling slowly. This makes stumble exclusive to HandleMomentum()
|
|
return (!KFPOwner.IsImpaired() || KFPOwner.IsHeadless()) && (PawnOwner.Physics == PHYS_Walking || (PawnOwner.Physics == PHYS_Falling &&
|
|
Abs(PawnOwner.Velocity.Z) < 250 && VSize2d(PawnOwner.Velocity) < 600));
|
|
}
|
|
|
|
function PlayAnimation()
|
|
{
|
|
local byte Type, Variant;
|
|
|
|
Type = KFPOwner.SpecialMoveFlags & 15;
|
|
Variant = KFPOwner.SpecialMoveFlags >> 4;
|
|
AnimName = KFPOwner.PawnAnimInfo.StumbleAnims[Type].Anims[Variant];
|
|
|
|
PlaySpecialMoveAnim(AnimName, EAS_FullBody, BlendInTime, BlendOutTime, 1.f);
|
|
|
|
// Clear the special move flags now so that SpecialMoveFlagsUpdated never fails
|
|
KFPOwner.SpecialMoveFlags = 255;
|
|
|
|
if ( KFPOwner.Role == ROLE_Authority )
|
|
{
|
|
bCanBeInterrupted = false;
|
|
KFPOwner.SetTimer(RandRange(0.5f, 0.75f), false, nameof(EnableInterrupt), Self);
|
|
}
|
|
}
|
|
|
|
function EnableInterrupt()
|
|
{
|
|
bCanBeInterrupted = true;
|
|
}
|
|
|
|
/** called when DoSpecialMove() is called again with this special move, but the special move flags have changed */
|
|
function SpecialMoveFlagsUpdated()
|
|
{
|
|
// If the move flags are updated play a new animation (chain stumble)
|
|
// This can happen on a client if the server interrupted a stumble and
|
|
// then started a new one on the same frame.
|
|
PlayAnimation();
|
|
}
|
|
|
|
DefaultProperties
|
|
{
|
|
Handle=KFSM_Stumble
|
|
|
|
bUseRootMotion=true
|
|
BlendOutTime=0.2f
|
|
AbortBlendOutTime=0.1f
|
|
|
|
// ---------------------------------------------
|
|
// AI/Navigation
|
|
DefaultAICommandClass=class'KFGame.AICommand_Stumble'
|
|
AITimeout=5.f
|
|
|
|
bDisablesWeaponFiring=true
|
|
bCanOnlyWanderAtEnd=true
|
|
}
|