1
0
KF2-Dev-Scripts/KFGame/Classes/KFSM_MeleeAttack.uc
2020-12-13 18:01:13 +03:00

221 lines
5.9 KiB
Ucode

//=============================================================================
// KFSM_MeleeAttack
//=============================================================================
// Handles close-range melee attacks, and base class for all NPC melee attacks.
// An attack is considered to be a melee attack if there are no projectiles
// involved (i.e, just play an animation-based attack)
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
//=============================================================================
class KFSM_MeleeAttack extends KFSM_PlaySingleAnim
native(SpecialMoves);
/** If Interruptible, is this attack affected by parry stumble */
var transient bool bCannotBeParried;
static function byte PackFlagsBase(KFPawn P)
{
local byte StrikeFlags;
local KFPawn_Monster KFPM;
KFPM = KFPawn_Monster(P);
if ( KFPM == none || P.MyKFAIC == none )
{
return 255;
}
if( KFPM != none && KFPM.PawnAnimInfo != none )
{
StrikeFlags = KFPM.PawnAnimInfo.GetStrikeFlags(P.MyKFAIC.PendingAnimStrikeIndex);
if( StrikeFlags != 255 )
{
//class'AICommand_Attack_Melee'.static.Melee( self, Enemy, StrikeFlags );
P.MyKFAIC.UpdatePendingStrike();
}
}
return StrikeFlags;
}
/** Unpack Special move flags */
function UnpackSpecialMoveFlags()
{
local byte AtkIndex, AtkVariant;
AtkIndex = KFPOwner.SpecialMoveFlags & 15;
AtkVariant = KFPOwner.SpecialMoveFlags >> 4;
// setup next attack animation based on SpecialMoveFlags
AnimName = KFPOwner.PawnAnimInfo.InitMeleeSpecialMove(self, AtkIndex, AtkVariant);
// for now all non-RootMotion are also interruptible
bCanBeInterrupted = (bCanBeInterrupted || !bUseRootMotion);
}
/**
* Can a new special move override this one before it is finished?
* This is only if CanDoSpecialMove() == TRUE && !bForce when starting it.
*/
function bool CanOverrideMoveWith( Name NewMove )
{
if ( bCanBeInterrupted && (NewMove == 'KFSM_Stunned' || NewMove == 'KFSM_Stumble' || NewMove == 'KFSM_Knockdown' || NewMove == 'KFSM_Frozen') )
{
return TRUE; // for NotifyAttackParried
}
return FALSE;
}
function SpecialMoveStarted( bool bForced, name PrevMove )
{
local KFPawn_Monster KFPM;
bUseRootMotion = false;
bCanBeInterrupted = false;
UnpackSpecialMoveFlags();
KFPM = KFPawn_Monster(KFPOwner);
if ( KFPM != none && KFPM.bCanCloak && KFPM.bIsCloaking )
{
KFPM.SetCloaked(false);
}
Super.SpecialMoveStarted( bForced, PrevMove );
}
function SpecialMoveEnded(Name PrevMove, Name NextMove)
{
local KFPawn_Monster KFPM;
PawnOwner.ClearTimer(nameof(InterruptCheckTimer), self);
// prevent this anim from triggering melee impact notifies during AbortBlendOutTime
if ( bCanBeInterrupted && KFPOwner.BodyStanceNodes[AnimStance].bIsPlayingCustomAnim )
{
KFPOwner.BodyStanceNodes[AnimStance].SetNotifyWeightThreshold(0.8f);
}
KFPM = KFPawn_Monster(KFPOwner);
if ( KFPM != none )
{
if( KFPM.bCanCloak && KFPM.bCloakOnMeleeEnd && !KFPM.bIsCloaking )
{
KFPM.SetCloaked(true);
}
KFPM.MeleeSpecialMoveEnded();
}
Super.SpecialMoveEnded(PrevMove, NextMove);
}
/** Called from SpecialMoveStarted */
function PlayAnimation()
{
local float InterruptTime;
if( AnimName == '' )
{
`warn( KFPOwner$" "$GetFuncName()$" "$self$" attempting special move attack but the AttackAnims array is empty!" );
return;
}
`AILog_Ext( GetFuncName()$" "$self$" chose attack animation "$AnimName, 'Command_SpecialMove', AIOwner );
if( bCanBeInterrupted )
{
InterruptTime = KFSkeletalMeshComponent(PawnOwner.Mesh).GetAnimInterruptTime(AnimName);
PawnOwner.SetTimer(InterruptTime, false, nameof(InterruptCheckTimer), self);
}
PlaySpecialMoveAnim( AnimName, AnimStance, BlendInTime, BlendOutTime, KFPOwner.AttackSpeedModifier);
}
/** Notification from the pawn that a medium (aka gun) or heavy (aka melee) affliction has been activated */
function NotifyHitReactionInterrupt()
{
// only non-RM attacks can be aborted
if ( bCanBeInterrupted )
{
KFPOwner.EndSpecialMove();
// Reset melee timer after interrupt
if( AIOwner != None )
{
AIOwner.LastGetStrikeTime = -1;
}
}
}
/** Called by owning AI Command when AI behavior decides to stop this attack */
function AbortedByAICommand()
{
local KFPawn_Monster KFPMOwner;
if ( bCanBeInterrupted )
{
if( !KFPOwner.IsTimerActive( nameof(InterruptCheckTimer), self) )
{
KFPMOwner = KFPawn_Monster(KFPOwner);
KFPOwner.EndSpecialMove();
if( KFPMOwner != none )
{
KFPMOwner.NotifyAnimInterrupt();
}
if( AIOwner != None )
{
// Reset melee timer after interrupt
AIOwner.LastGetStrikeTime = -1;
}
}
}
}
/** Called when it's safe to interrupt this animation */
function InterruptCheckTimer()
{
local AICommand_Attack_Melee MeleeCommand;
local KFPawn_Monster KFPMOwner;
if ( bCanBeInterrupted && AIOwner != None )
{
MeleeCommand = AICommand_Attack_Melee(AIOwner.GetActiveCommand());
if( MeleeCommand != none && MeleeCommand.bAttackAborted )
{
KFPMOwner = KFPawn_Monster(KFPOwner);
if( KFPMOwner != none )
{
KFPMOwner.NotifyAnimInterrupt();
}
KFPOwner.EndSpecialMove();
// Reset melee timer after interrupt
AIOwner.LastGetStrikeTime = -1;
}
}
}
/** Called when owning pawn has damage parried during this move */
function bool CanInterruptWithParry()
{
return !bCannotBeParried;
}
defaultproperties
{
BlendInTime=0.2f
BlendOutTime=0.33f
// ---------------------------------------------
// Animations
bUseRootMotion=false
AbortBlendOutTime=0.5f
// ---------------------------------------------
// SpecialMove
Handle=KFSM_MeleeAttack
bDisableSteering=false
bDisableMovement=true
bUseHigherMeshSmoothingThreshold=true
}