269 lines
7.4 KiB
Ucode
269 lines
7.4 KiB
Ucode
|
//=============================================================================
|
||
|
// KFSM_Block
|
||
|
//=============================================================================
|
||
|
// Handles playing directional blocks
|
||
|
//=============================================================================
|
||
|
// Killing Floor 2
|
||
|
// Copyright (C) 2016 Tripwire Interactive LLC
|
||
|
//=============================================================================
|
||
|
class KFSM_Block extends KFSM_PlaySingleAnim;
|
||
|
|
||
|
/** Animation to play */
|
||
|
var const array<AnimVariants> BlockAnims;
|
||
|
|
||
|
/** Cached gameinfo reference */
|
||
|
var protected KFGameInfo MyKFGI;
|
||
|
|
||
|
/** Cached reference to monster pawn */
|
||
|
var protected KFPawn_Monster MyMonsterPawn;
|
||
|
|
||
|
/** How many times a block has been executed while in the special move */
|
||
|
var protected int NumBlocks;
|
||
|
|
||
|
/** The direction of the hit reaction when a block is broken */
|
||
|
var protected EPawnOctant ReactionDir;
|
||
|
|
||
|
/** Used to determine if the animation needs to be blended out when the special move ends */
|
||
|
var protected bool bPlayedBlockBreak;
|
||
|
|
||
|
/** When TRUE, will use the Monster pawn's BlockSprintSpeedModifier */
|
||
|
var protected bool bUseBlockSprintSpeed;
|
||
|
|
||
|
/** How long from start of block until Zed can actually block. */
|
||
|
var protected float WindupTime;
|
||
|
|
||
|
static function byte PackBlockSMFLags( byte BlockDir )
|
||
|
{
|
||
|
local byte Variant;
|
||
|
|
||
|
Variant = Rand( 2 );
|
||
|
return BlockDir + ( Variant << 4 );
|
||
|
}
|
||
|
|
||
|
/** Checks to see if this Special Move can be done */
|
||
|
protected function bool InternalCanDoSpecialMove()
|
||
|
{
|
||
|
// Can block if not doing a special move or already in a block
|
||
|
return ( !KFPOwner.IsDoingSpecialMove() || KFPOwner.IsDoingSpecialMove(SM_Block) );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Allow melee attacks to override blocks. This is so if the AI bumps an enemy,
|
||
|
* they will always be able attack
|
||
|
*/
|
||
|
function bool CanOverrideMoveWith( Name NewMove )
|
||
|
{
|
||
|
if( NewMove == class'KFSM_MeleeAttack'.default.Handle )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return super.CanOverrideMoveWith( NewMove );
|
||
|
}
|
||
|
|
||
|
/** Can the special move be chained after the current one finishes? */
|
||
|
function bool CanChainMove( Name NextMove )
|
||
|
{
|
||
|
return NextMove == class.name;
|
||
|
}
|
||
|
|
||
|
function SpecialMoveStarted( bool bForced, name PrevMove )
|
||
|
{
|
||
|
// Cache our pawn
|
||
|
if( MyMonsterPawn == none )
|
||
|
{
|
||
|
MyMonsterPawn = KFPawn_Monster( KFPOwner );
|
||
|
}
|
||
|
|
||
|
// Reset block breaker values
|
||
|
bPlayedBlockBreak = false;
|
||
|
ReactionDir = DIR_None;
|
||
|
NumBlocks = 1;
|
||
|
|
||
|
if( MyMonsterPawn.Role == ROLE_Authority )
|
||
|
{
|
||
|
bUseBlockSprintSpeed = true;
|
||
|
AdjustSprintSpeed();
|
||
|
|
||
|
MyMonsterPawn.SetTimer( MyMonsterPawn.GetBlockSettings().Duration, false, nameOf(Timer_BlockDurationExpired), self );
|
||
|
MyMonsterPawn.SetTimer( WindupTime, false, nameOf(Timer_EnableBlocking), self );
|
||
|
}
|
||
|
|
||
|
super.SpecialMoveStarted( bForced, PrevMove );
|
||
|
}
|
||
|
|
||
|
function AdjustSprintSpeed()
|
||
|
{
|
||
|
local float GCMoveSpeedMod;
|
||
|
|
||
|
// Only allow for server and local player
|
||
|
if( MyMonsterPawn.Role == ROLE_Authority )
|
||
|
{
|
||
|
GCMoveSpeedMod = 1.0f;
|
||
|
|
||
|
// Set movement speed modifier
|
||
|
if( MyMonsterPawn.WorldInfo.Game != none )
|
||
|
{
|
||
|
if( MyKFGI == none )
|
||
|
{
|
||
|
MyKFGI = KFGameInfo( MyMonsterPawn.WorldInfo.Game );
|
||
|
}
|
||
|
|
||
|
if( MyKFGI != none && MyKFGI.GameConductor != none )
|
||
|
{
|
||
|
GCMoveSpeedMod = MyKFGI.GameConductor.CurrentAIMovementSpeedMod;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
MyMonsterPawn.AdjustMovementSpeed( GCMoveSpeedMod );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Network: SERVER
|
||
|
*/
|
||
|
function float GetSprintSpeedModifier()
|
||
|
{
|
||
|
return bUseBlockSprintSpeed ? MyMonsterPawn.GetBlockingSprintSpeedModifier() : 1.0f;
|
||
|
}
|
||
|
|
||
|
function PlayAnimation()
|
||
|
{
|
||
|
local byte Type, Variant;
|
||
|
|
||
|
Type = MyMonsterPawn.SpecialMoveFlags & 15;
|
||
|
Variant = MyMonsterPawn.SpecialMoveFlags >> 4;
|
||
|
|
||
|
AnimName = BlockAnims[Type].Anims[Variant];
|
||
|
|
||
|
// Call super; play selected animation
|
||
|
super.PlayAnimation();
|
||
|
|
||
|
// Clear the special move flags on the next frame
|
||
|
MyMonsterPawn.SetTimer( MyMonsterPawn.WorldInfo.DeltaSeconds, false, nameOf(Timer_ResetSpecialMoveFlags), self );
|
||
|
|
||
|
// Make sure that everything is sent this frame so that the special move flags can be safely cleared
|
||
|
MyMonsterPawn.bForceNetUpdate = true;
|
||
|
}
|
||
|
|
||
|
/** Resets the special move flags on the pawn for future updates */
|
||
|
function Timer_ResetSpecialMoveFlags()
|
||
|
{
|
||
|
MyMonsterPawn.SpecialMoveFlags = 255;
|
||
|
}
|
||
|
|
||
|
/** Called when DoSpecialMove() is called again with this special move, but the special move flags have changed */
|
||
|
function SpecialMoveFlagsUpdated()
|
||
|
{
|
||
|
local EPawnOctant BlockDir;
|
||
|
|
||
|
if( MyMonsterPawn.SpecialMoveFlags != 255 && !MyMonsterPawn.IsTimerActive( nameOf(Timer_BlockBroken), self) )
|
||
|
{
|
||
|
// Increment our block number
|
||
|
++NumBlocks;
|
||
|
|
||
|
// End the block cycle if we've reached the maximum number of hits
|
||
|
if( NumBlocks > MyMonsterPawn.GetBlockSettings().MaxBlocks )
|
||
|
{
|
||
|
BlockDir = EPawnOctant( MyMonsterPawn.SpecialMoveFlags & 15 );
|
||
|
if( BlockDir == DIR_Right )
|
||
|
{
|
||
|
ReactionDir = DIR_ForwardLeft;
|
||
|
}
|
||
|
else if( BlockDir == DIR_Left )
|
||
|
{
|
||
|
ReactionDir = DIR_ForwardRight;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ReactionDir = DIR_Forward;
|
||
|
}
|
||
|
|
||
|
// End the special move with a hit reaction (block breaker!)
|
||
|
MyMonsterPawn.ClearTimer( nameOf(Timer_BlockDurationExpired), self );
|
||
|
MyMonsterPawn.SetTimer( BlendInTime, false, nameOf(Timer_BlockBroken), self );
|
||
|
}
|
||
|
else if( MyMonsterPawn.Role == ROLE_Authority )
|
||
|
{
|
||
|
MyMonsterPawn.SetTimer( MyMonsterPawn.GetBlockSettings().Duration, false, nameOf(Timer_BlockDurationExpired), self );
|
||
|
}
|
||
|
|
||
|
PlayAnimation();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Toggles the blocking flag on the owner pawn to TRUE */
|
||
|
function Timer_EnableBlocking()
|
||
|
{
|
||
|
MyMonsterPawn.bIsBlocking = true;
|
||
|
}
|
||
|
|
||
|
/** We've reached the end of our block, end the special move */
|
||
|
function Timer_BlockDurationExpired()
|
||
|
{
|
||
|
if( MyMonsterPawn != none )
|
||
|
{
|
||
|
MyMonsterPawn.EndSpecialMove();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Block was broken by too many successive hits */
|
||
|
function Timer_BlockBroken()
|
||
|
{
|
||
|
bPlayedBlockBreak = true;
|
||
|
MyMonsterPawn.StopBodyAnim( AnimStance, BlendOutTime );
|
||
|
MyMonsterPawn.PawnAnimInfo.PlayHitReactionAnim( MyMonsterPawn, HIT_Heavy, ReactionDir );
|
||
|
MyMonsterPawn.EndSpecialMove();
|
||
|
}
|
||
|
|
||
|
/** Hit reactions won't interrupt blocks */
|
||
|
function NotifyHitReactionInterrupt();
|
||
|
|
||
|
function SpecialMoveEnded( Name PrevMove, Name NextMove )
|
||
|
{
|
||
|
super(KFSpecialMove).SpecialMoveEnded( PrevMove, NextMove );
|
||
|
|
||
|
// Stop looping anim, clear timers and blocking flag
|
||
|
if( MyMonsterPawn != none )
|
||
|
{
|
||
|
if( !bPlayedBlockBreak )
|
||
|
{
|
||
|
MyMonsterPawn.StopBodyAnim( AnimStance, BlendOutTime );
|
||
|
}
|
||
|
|
||
|
if( MyMonsterPawn.Role == ROLE_Authority )
|
||
|
{
|
||
|
bUseBlockSprintSpeed = false;
|
||
|
AdjustSprintSpeed();
|
||
|
|
||
|
MyMonsterPawn.ClearTimer( nameOf(Timer_ResetSpecialMoveFlags), self );
|
||
|
MyMonsterPawn.ClearTimer( nameOf(Timer_EnableBlocking), self );
|
||
|
MyMonsterPawn.ClearTimer( nameOf(Timer_BlockDurationExpired), self );
|
||
|
MyMonsterPawn.ClearTimer( nameOf(Timer_BlockBroken), self );
|
||
|
MyMonsterPawn.bIsBlocking = false;
|
||
|
MyMonsterPawn.LastBlockTime = MyMonsterPawn.WorldInfo.TimeSeconds;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DefaultProperties
|
||
|
{
|
||
|
// ---------------------------------------------
|
||
|
// SpecialMove
|
||
|
Handle=KFSM_Block
|
||
|
bCanBeInterrupted=true
|
||
|
|
||
|
WindupTime=0.25f
|
||
|
|
||
|
// ---------------------------------------------
|
||
|
// Animations
|
||
|
AnimStance=EAS_UpperBody
|
||
|
bUseRootMotion=false
|
||
|
bLoopAnim=true
|
||
|
BlendInTime=0.2f
|
||
|
|
||
|
BlockAnims(DIR_Forward)=(Anims=(Block_Idle, Block_Idle))
|
||
|
BlockAnims(DIR_Backward)=(Anims=(Block_Idle, Block_Idle))
|
||
|
BlockAnims(DIR_Left)=(Anims=(Block_R, Block_R))
|
||
|
BlockAnims(DIR_Right)=(Anims=(Block_L, Block_L))
|
||
|
}
|