//=============================================================================
// 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))
}