246 lines
6.3 KiB
Ucode
246 lines
6.3 KiB
Ucode
//=============================================================================
|
|
// KFSM_PlayerMeleeBase
|
|
//=============================================================================
|
|
// A base class for third person, player-controlled, creature melee attacks
|
|
//=============================================================================
|
|
// Killing Floor 2
|
|
// Copyright (C) 2015 Tripwire Interactive LLC
|
|
// - Andrew "Strago" Ladenberger
|
|
//=============================================================================
|
|
|
|
class KFSM_PlayerMeleeBase extends KFSM_MeleeAttack
|
|
abstract;
|
|
|
|
/** Max 16 types, 0-15 */
|
|
enum EPlayerZedAtkType
|
|
{
|
|
PZA_Default,
|
|
PZA_Sprinting,
|
|
PZA_SprintBackwards,
|
|
PZA_SprintCloaked,
|
|
PZA_Cloaked,
|
|
PZA_Jumping,
|
|
PZA_Headless,
|
|
PZA_Left,
|
|
PZA_Right,
|
|
PZA_Backwards,
|
|
PZA_SpecialDefault,
|
|
PZA_SpecialSprinting,
|
|
PZA_SpecialBackwards,
|
|
};
|
|
|
|
struct PlayerZedAtkInfo
|
|
{
|
|
var array<name> Anims;
|
|
var bool bIsInputHeld;
|
|
var EPlayerZedAtkType Type;
|
|
var EAnimSlotStance Stance;
|
|
var float MomentumPush;
|
|
var bool bForceDisableRootMotion;
|
|
var bool bCannotBeParried;
|
|
};
|
|
|
|
var bool bAnimCanBeInterrupted;
|
|
|
|
var array<PlayerZedAtkInfo> Attacks;
|
|
|
|
/** global stop event for looping sounds played during the animation */
|
|
var AkEvent SoundStopEvent;
|
|
|
|
static function byte PackFlagsBase( KFPawn P )
|
|
{
|
|
local byte AtkIdx, Variant;
|
|
local EPlayerZedAtkType Type;
|
|
local bool bUsingSpecialAttacks;
|
|
local EPawnOctant PawnMoveDir;
|
|
|
|
if ( P != None )
|
|
{
|
|
PawnMoveDir = GetFourWayMoveDirection( P );
|
|
bUsingSpecialAttacks = default.class.static.IsInSpecialMode( P );
|
|
|
|
if ( P.IsHeadless() )
|
|
{
|
|
Type = PZA_Headless;
|
|
}
|
|
else if ( P.Physics == PHYS_Falling )
|
|
{
|
|
Type = PZA_Jumping;
|
|
}
|
|
else if ( P.bIsSprinting )
|
|
{
|
|
if( P.bIsCloaking )
|
|
{
|
|
Type = PZA_SprintCloaked;
|
|
}
|
|
else if( bUsingSpecialAttacks )
|
|
{
|
|
Type = PawnMoveDir == DIR_Backward ? PZA_SpecialBackwards : PZA_SpecialSprinting;
|
|
}
|
|
else if( PawnMoveDir == DIR_Right )
|
|
{
|
|
Type = PZA_Right;
|
|
}
|
|
else
|
|
{
|
|
Type = PawnMoveDir == DIR_Backward ? PZA_SprintBackwards : PZA_Sprinting;
|
|
}
|
|
}
|
|
else if ( P.bIsCloaking )
|
|
{
|
|
Type = PZA_Cloaked;
|
|
}
|
|
else if ( PawnMoveDir == DIR_Backward )
|
|
{
|
|
Type = bUsingSpecialAttacks ? PZA_SpecialBackwards : PZA_Backwards;
|
|
}
|
|
else if( PawnMoveDir == DIR_Right )
|
|
{
|
|
Type = PZA_Right;
|
|
}
|
|
else if( PawnMoveDir == DIR_Left )
|
|
{
|
|
Type = PZA_Left;
|
|
}
|
|
else
|
|
{
|
|
Type = bUsingSpecialAttacks ? PZA_SpecialDefault : PZA_Default;
|
|
}
|
|
}
|
|
|
|
AtkIdx = default.Attacks.Find('Type', Type);
|
|
if ( AtkIdx == 255 )
|
|
{
|
|
AtkIdx = 0;
|
|
}
|
|
Variant = Rand(default.Attacks[AtkIdx].Anims.Length);
|
|
return AtkIdx + (Variant << 4);
|
|
}
|
|
|
|
function SpecialMoveStarted( bool bForced, name PrevMove )
|
|
{
|
|
super.SpecialMoveStarted( bForced, PrevMove );
|
|
|
|
bAnimCanBeInterrupted = false;
|
|
bPendingStopFire = false;
|
|
|
|
// Must be able to interrupt a parriable (e.g. stumble) attack
|
|
// @todo: all these interrupt variables are confusing and have a lot of overlap
|
|
bCanBeInterrupted = !bCannotBeParried;
|
|
}
|
|
|
|
function SpecialMoveEnded( Name PrevMove, Name NextMove )
|
|
{
|
|
Super.SpecialMoveEnded(PrevMove, NextMove);
|
|
|
|
if ( SoundStopEvent != None && PawnOwner.WorldInfo.NetMode != NM_DedicatedServer )
|
|
{
|
|
PawnOwner.PostAkEvent(SoundStopEvent);
|
|
}
|
|
}
|
|
|
|
/** Can be overridden in subclasses to determine if special attacks are used over defaults */
|
|
static function bool IsInSpecialMode( KFPawn P )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/** Returns a 4-way direction based on pawn movement to use with SpecialMoveFlags */
|
|
static function EPawnOctant GetFourWayMoveDirection(Pawn P)
|
|
{
|
|
local vector MoveDir;
|
|
|
|
// Prefer to use acceleration, but if the player only has velocity use that.
|
|
// This allows the player to let go off the movement keys just before an attack.
|
|
MoveDir = (IsZero(P.Acceleration)) ? P.Velocity : P.Acceleration;
|
|
|
|
return class'KFPawn'.static.CalcQuadRegion(P.Rotation, MoveDir);
|
|
}
|
|
|
|
/** Unpack Special move flags */
|
|
function UnpackSpecialMoveFlags()
|
|
{
|
|
local byte Variant, AtkIdx;
|
|
|
|
AtkIdx = KFPOwner.SpecialMoveFlags & 15;
|
|
Variant = KFPOwner.SpecialMoveFlags >> 4;
|
|
|
|
AnimName = Attacks[AtkIdx].Anims[Variant];
|
|
AnimStance = Attacks[AtkIdx].Stance;
|
|
|
|
bDisableMovement = (AnimStance == EAS_FullBody);
|
|
bUseRootMotion = (AnimStance == EAS_FullBody && !Attacks[AtkIdx].bForceDisableRootMotion);
|
|
|
|
// If we're doing a PHYS_Falling attack we need to allow RM take over
|
|
// which means disabling bAllowMomentumPush.
|
|
bAllowMomentumPush = (!bUseRootMotion || Attacks[AtkIdx].Type != PZA_Jumping);
|
|
|
|
// Set parry flag on a per-attack basis
|
|
bCannotBeParried = Attacks[AtkIdx].bCannotBeParried;
|
|
|
|
if( Attacks[AtkIdx].bIsInputHeld )
|
|
{
|
|
bCanBeInterrupted = true;
|
|
if( KFPOwner.IsLocallyControlled() )
|
|
{
|
|
KFPOwner.SetTimer( KFSkeletalMeshComponent(KFPOwner.Mesh).GetAnimInterruptTime(AnimName), false, nameOf(Timer_AnimInterrupt), self );
|
|
}
|
|
}
|
|
|
|
// Apply momentum push, if we allow it and momentum is > 0
|
|
if( bAllowMomentumPush && Attacks[AtkIdx].MomentumPush > 0.f && (KFPOwner.Role == ROLE_Authority || KFPOwner.IsLocallyControlled()) )
|
|
{
|
|
KFPOwner.Velocity += vector(KFPOwner.Rotation) * Attacks[AtkIdx].MomentumPush;
|
|
}
|
|
}
|
|
|
|
/** Called on animations that can be interrupted */
|
|
function Timer_AnimInterrupt()
|
|
{
|
|
bAnimCanBeInterrupted = true;
|
|
|
|
if( bPendingStopFire )
|
|
{
|
|
SpecialMoveButtonReleased();
|
|
}
|
|
}
|
|
|
|
/** When the grapple animation ends, continue it with a different grapple anim */
|
|
function SpecialMoveFlagsUpdated()
|
|
{
|
|
if( KFPOwner.SpecialMoveFlags == FLAG_SpecialMoveButtonReleased )
|
|
{
|
|
KFPOwner.EndSpecialMove();
|
|
}
|
|
else
|
|
{
|
|
super.SpecialMoveFlagsUpdated();
|
|
}
|
|
}
|
|
|
|
/* Called on some player-controlled moves when a firemode input has been pressed */
|
|
function SpecialMoveButtonRetriggered()
|
|
{
|
|
bPendingStopFire = false;
|
|
}
|
|
|
|
/** Called on some player-controlled moves when a firemode input has been released */
|
|
function SpecialMoveButtonReleased()
|
|
{
|
|
bPendingStopFire = true;
|
|
|
|
if( !bAnimCanBeInterrupted )
|
|
{
|
|
return;
|
|
}
|
|
|
|
KFPOwner.DoSpecialMove( KFPOwner.SpecialMove, true,, FLAG_SpecialMoveButtonReleased );
|
|
if( KFPOwner.Role < ROLE_Authority && KFPOwner.IsLocallyControlled() )
|
|
{
|
|
KFPOwner.ServerDoSpecialMove( KFPOwner.SpecialMove, true,, FLAG_SpecialMoveButtonReleased );
|
|
}
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
} |