//=============================================================================
// KFPawn_MonsterBoss
//=============================================================================
// A base (abstract) class for bosses based on unique designs.  Bosses based on
//      existing zeds simply implement the boss interface.
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
//=============================================================================

class KFPawn_MonsterBoss extends KFPawn_Monster
	abstract
	native(Pawn)
    implements(KFInterface_MonsterBoss);

`include(KFGameDialog.uci)

var KFPlayerController KFPC; //used to notify UI

/** Info for minion wave spawning */
struct native BossMinionWaveInfo
{
    /** The minion wave to spawn for Phase 1 healing*/
    var	KFAIWaveInfo				PhaseOneWave;
    /** The minion wave to spawn for Phase 2 healing*/
    var	KFAIWaveInfo				PhaseTwoWave;
    /** The minion wave to spawn for Phase 3 healing*/
    var	KFAIWaveInfo				PhaseThreeWave;
    /** The minion wave to spawn for Phase 4 healing*/
    var	KFAIWaveInfo				PhaseFourWave;
};

/** Waves to summon at each stage by difficulty level*/
var	BossMinionWaveInfo				SummonWaves[4];

/** The base amount of minions to spawn when boss goes into hunt and heal mode */
var             vector2D                 NumMinionsToSpawn;

/** The current phase of the battle we're in */
var repnotify   int 				CurrentBattlePhase;

/** Scalar to apply to attack range when there is only one player remaining in a multiplayer game */
var 			float				LastPlayerAliveAttackRangeScale;

/** Set when the speed increase timer detects that there is only one player left */
var protected 	float 				LastPlayerAliveStartTime;

/** How long after spawn before the boss starts to increase its speed */
var protected const float			TimeUntilSpeedIncrease;

/** How high the speed limit can go */
var protected const float 			SpeedLimitScalar;

/** How much to increase the sprint speed by (per minute) when the speed increase timer elapses */
var protected const	float 			SpeedPctIncreasePerMinute;

/** The final sprint speed after being modified by SetMonsterDefaults() */
var protected 	float 				ActualSprintSpeed;

//Intro Camera
var bool bUseAnimatedCamera;
var vector AnimatedBossCameraOffset;

//Localization
var localized array<string> BossCaptionStrings;

replication
{
	if( bNetDirty )
		CurrentBattlePhase;
}

/************************************
* @name	KFInterface_MonsterBoss
************************************/
//Quick access to a pawn reference
simulated function KFPawn_Monster GetMonsterPawn()
{
    return self;
}

simulated function string GetRandomBossCaption()
{
    if (default.BossCaptionStrings.Length <= 0)
    {
        return "";
    }

    return default.BossCaptionStrings[Rand(default.BossCaptionStrings.Length)];
}

//Status Accessors
static simulated event bool IsABoss()
{
    return true;
}

simulated function float GetHealthPercent()
{
    return float(Health) / float(HealthMax);
}

//Intro functionality
/** Turn on the boss camera animation mode */
simulated function SetAnimatedBossCamera(bool bEnable, optional vector CameraOffset)
{
    bUseAnimatedCamera = bEnable;
    if (bUseAnimatedCamera)
    {
        AnimatedBossCameraOffset = CameraOffset;
    }
    else
    {
        AnimatedBossCameraOffset = vect(0, 0, 0);
    }
}

/** Whether this pawn is in theatric camera mode */
simulated function bool UseAnimatedBossCamera()
{
    return bUseAnimatedCamera;
}

/** The name of the socket to use as a camera base for theatric sequences */
simulated function name GetBossCameraSocket()
{
    return 'TheatricCameraRootSocket';
}

/** The relative offset to use for the cinematic camera */
simulated function vector GetBossCameraOffset()
{
    return AnimatedBossCameraOffset;
}

function OnZedDied(Controller Killer)
{
    super.OnZedDied(Killer);

    KFGameInfo(WorldInfo.Game).BossDied(Killer);
}

function KFAIWaveInfo GetWaveInfo(int BattlePhase, int Difficulty)
{
    switch (BattlePhase)
    {
    case 1:
        return SummonWaves[Difficulty].PhaseOneWave;
        break;
    case 2:
        return SummonWaves[Difficulty].PhaseTwoWave;
        break;
    case 3:
        return SummonWaves[Difficulty].PhaseThreeWave;
        break;
    }

    return none;
}

/** Returns the number of minions to spawn based on number of players */
function byte GetNumMinionsToSpawn()
{
    return byte(Lerp(NumMinionsToSpawn.X, NumMinionsToSpawn.Y, fMax(WorldInfo.Game.NumPlayers, 1) / float(WorldInfo.Game.MaxPlayers)));
}

//
simulated event ReplicatedEvent(name VarName)
{
	if( VarName == nameOf(CurrentBattlePhase) )
	{
		OnBattlePhaseChanged();
	}
	else
	{
		Super.ReplicatedEvent( VarName );
	}
}

// Called immediately before gameplay begins.
simulated event PreBeginPlay()
{
	Super.PreBeginPlay();
	OnBattlePhaseChanged();
	KFPC = KFPlayerController(GetALocalPlayerController());
}

// Mostly indistinguishable from PreBeginPlay().  Following Pawn conventions only one is 'simulated'
simulated event PostBeginPlay()
{
	Super.PostBeginPlay();
}

static simulated function SetupHealthBar(KFInterface_MonsterBoss BossRef)
{
	local KFPawn_Monster BossMonster;
	local KFPlayerController LocalKFPC;

	BossMonster = KFPawn_Monster(BossRef);
	if (BossMonster != none && BossMonster.WorldInfo.NetMode != NM_DedicatedServer)
	{
		LocalKFPC = KFPlayerController(BossMonster.GetALocalPlayerController());
		if(LocalKFPC.MyGFxHUD != none && LocalKFPC.MyGFxHUD.BossHealthBar != none)
		{
			LocalKFPC.MyGFxHUD.BossHealthBar.SetBossPawn(BossRef);

			if (!KFGameReplicationInfo(BossMonster.WorldInfo.GRI).ShouldSetBossCamOnBossSpawn())
			{
				LocalKFPC.MyGFxHUD.BossHealthBar.OnNamePlateHidden();
			}
		}
	}
}

/** Called from Possessed event when this controller has taken control of a Pawn */
function PossessedBy( Controller C, bool bVehicleTransition )
{
	Super.PossessedBy( C, bVehicleTransition );

	PlayBossMusic();
	PlayBossEntranceTheatrics(self);

	// Set a timer to begin increasing this boss's speed after enough time has elapsed
	if( !IsHumanControlled() )
	{
		ActualSprintSpeed = SprintSpeed;
		SetTimer( TimeUntilSpeedIncrease, false, nameOf(Timer_IncreaseSpeed) );
	}
}

static function PlayBossEntranceTheatrics(KFInterface_MonsterBoss BossRef)
{
	local KFPawn_Monster BossMonster;

	BossMonster = KFPawn_Monster(BossRef);
	if (BossMonster != none && KFGameReplicationInfo(BossMonster.WorldInfo.GRI).ShouldSetBossCamOnBossSpawn())
	{
		BossMonster.ServerDoSpecialMove(SM_BossTheatrics);
	}
}

/** Gradually increases sprint speed */
function Timer_IncreaseSpeed()
{
	SetTimer( 10.f, false, nameOf(Timer_IncreaseSpeed) );

	if( IsOnePlayerLeftInTeamGame() )
	{
		if( LastPlayerAliveStartTime == 0.f )
		{
			LastPlayerAliveStartTime = WorldInfo.TimeSeconds;
		}

		// Adjust sprint speed but don't let it go below original or higher than 2x
		SprintSpeed = fClamp( ActualSprintSpeed + ((`TimeSince(LastPlayerAliveStartTime) / 60.f)
						* (ActualSprintSpeed * SpeedPctIncreasePerMinute)), ActualSprintSpeed, ActualSprintSpeed * SpeedLimitScalar );
	}
	else
	{
		LastPlayerAliveStartTime = 0.f;
		SprintSpeed = ActualSprintSpeed;
	}
}

/** sends any notifications to anything that needs to know this pawn has taken damage */
function NotifyTakeHit(Controller InstigatedBy, vector HitLocation, int Damage, class<DamageType> DamageType, vector Momentum, Actor DamageCauser)
{
	Super.NotifyTakeHit(InstigatedBy, HitLocation, Damage, DamageType, Momentum, DamageCauser);

	`DialogManager.PlayBossTakeDamageDialog( self );
}


/** Return true if AI can target this pawn */
function bool CanAITargetThisPawn(Controller TargetingController)
{
    if( TargetingController.Pawn.IsSameTeam( self ) )
    {
        // Don't let weaker zeds target a boss!!!
        return false;
    }

    return Super.CanAITargetThisPawn(TargetingController);
}

simulated function PlayDyingSound()
{
    if (!HasMouth())
    {
        return;
    }

    super.PlayDyingSound();
}

/** Called when pawn dies or is destroyed. Overloaded to call OnBattlePhaseChanged. */
simulated function TerminateEffectsOnDeath()
{
	super.TerminateEffectsOnDeath();

	OnBattlePhaseChanged();
}

/** Reapply active gameplay related MIC params (e.g. when switching to the gore mesh) */
simulated function UpdateGameplayMICParams()
{
	super.UpdateGameplayMICParams();
	OnBattlePhaseChanged();
}

/** Returns current battle phase, used by KFPawnAnimInfo */
simulated function int GetCurrentBattlePhase()
{
	return CurrentBattlePhase;
}

/** Called when current battle phase changes */
/* Network: ALL
 */
simulated function OnBattlePhaseChanged()
{
	UpdateBattlePhaseOnLocalPlayerUI();
}

simulated function UpdateBattlePhaseOnLocalPlayerUI()
{
	if( KFPC == none || KFPC.MyGFxHUD == none || KFPC.MyGFxHUD.bossHealthBar == none )
	{
		return;
	}

    KFPC.MyGFxHUD.bossHealthBar.UpdateBossBattlePhase(CurrentBattlePhase);
}

/** Returns the attack range scalar when there is only one player remaining */
function float GetAttackRangeScale()
{
	if( IsOnePlayerLeftInTeamGame() )
	{
		return LastPlayerAliveAttackRangeScale;
	}

	return 1.f;
}

/** Returns true if there is only one player remaining in a multiplayer game */
function bool IsOnePlayerLeftInTeamGame()
{

	if( WorldInfo.Game.NumPlayers > 1 )
	{
		return KFGameInfo(WorldInfo.Game).GetLivingPlayerCount() == 1;
	}

	return false;
}

/** Similar to IsOnePlayerLeftInTeamGame(), but more expensive and can be called on clients */
native function bool LocalIsOnePlayerLeftInTeamGame();

/**
 * This pawn has died.
 */
function bool Died( Controller Killer, class<DamageType> DamageType, vector HitLocation )
{
	local bool result;

	result = super.Died( Killer, DamageType, HitLocation );

	ClearTimer( nameOf(Timer_IncreaseSpeed) );

	return result;
}

/** Play music for this boss (overridden for each boss) */
function PlayBossMusic();

// bosses cannot be decapitated
function CauseHeadTrauma(float BleedOutTime=5.f);
simulated function PlayHeadAsplode();

function PlayGrabDialog()
{
	`DialogManager.PlayBossGrabDialog( self );
}

function PlayHealDialog()
{
	`DialogManager.PlayBossHealDialog( self );
}

function PlayLeapedDialog()
{
	`DialogManager.PlayBossLeapedDialog( self );
}

function PlayLandedDialog()
{
	`DialogManager.PlayBossLandedDialog( self );
}

function PlayGrabbedPlayerDialog( KFPawn_Human Target );

function PlayGrabKilledDialog();


simulated function string GetIconPath()
{
	return "ZED_Patriarch_UI.ZED-VS_Icon_Boss";
}

defaultproperties
{
`if(`notdefined(ShippingPC))
	DebugRadarTexture=Texture2D'UI_ZEDRadar_TEX.MapIcon_Patriarch';
`endif

	Begin Object Name=KFPawnSkeletalMeshComponent
		// Enabling kinematic for physics interaction while alive.  (see also MinDistFactorForKinematicUpdate)
		bUpdateKinematicBonesFromAnimation=true
	End Object

	// ---------------------------------------------
	// Special Moves
	Begin Object Name=SpecialMoveHandler_0
		SpecialMoveClasses(SM_BossTheatrics)		= class'KFGame.KFSM_Zed_Boss_Theatrics'
		//SpecialMoveClasses(SM_RecoverFromRagdoll)	= None
		//SpecialMoveClasses(SM_Knockdown)			= None
	End Object
	bAlwaysRelevant=true

	// ---------------------------------------------
	// Spawning
    MinSpawnSquadSizeType=EST_Boss

    bWeakZedGrab=false

	// ---------------------------------------------
	// Last Player Remaining Difficulty
	LastPlayerAliveAttackRangeScale=0.75f
	TimeUntilSpeedIncrease=60.0
	SpeedPctIncreasePerMinute=0.20
	SpeedLimitScalar=1.30

	bCanBePinned=false

	VortexAttracionModifier=0.3f

	bCanBeKilledByShrinking=false
}