class KF_StoryNPC_Spawnable_SE extends KF_RingMasterNPC;

var(Bleed) bool 	bBleed; // should NPC bleed at all?
var(Bleed) float 	MinBleedDamage, MaxBleedDamage; // minand max amount of health that NPC looses per bleed

var(KF_StoryNPC) bool	bActivateOnHealing;
var(KF_StoryNPC) bool	bActivateOnHealingOnlyOnce;
var(KF_StoryNPC) float	HealthPctToActivate;

var(Events) name DeathEvent;
var(Events) name UseEvent; 

var(KF_StoryNPC) bool 	bHealthBarOnlyIfVisible; // draw HP bar only if player can see NPC
var(KF_StoryNPC) float 	HealthBarMaxDistance; 
var protected float HealthBarMaxDistanceSquared;

var(KF_StoryNPC) float ZombieDamageReduction;

var bool bUseCoverAnim;

// display message when touching by a player
var() localized string TouchMessage; 
var float NextTouchMessageTime;
var() name TouchMessageType;


var Touchable Touchable;
var() bool bTouchable; // players can touch me, i.e. call Touch() event
var() bool bUsable;    // players can use me, i.e. press <use> button on me, calling UsedBy() event


// used in AssessThreatTo()
var protected transient KFMonsterController LastThreatMonster;
var protected transient float LastThreatTime, LastThreat;


simulated function PostBeginPlay()
{
	super.PostBeginPlay();
	
	HealthBarMaxDistanceSquared = Square(HealthBarMaxDistance);
	
	if ( bTouchable || bUsable )
		SpawnTouchable();
}

function GetEvents(out array<name> TriggeredEvents,  out array<name>  ReceivedEvents)
{
	super.GetEvents(TriggeredEvents, ReceivedEvents);
	
    if( DeathEvent != '' )
        TriggeredEvents[TriggeredEvents.length] = DeathEvent;	
	if( UseEvent != '' )
        TriggeredEvents[TriggeredEvents.length] = UseEvent;
}

simulated function Tick(float DeltaTime)
{
    if( bBleed && bActive && Level.TimeSeconds - LastBleedOutTime > BleedOutInterval) {
		TakeDamage(RandRange(MinBleedDamage, MaxBleedDamage),self,Location + Vect(0,0,1)*CollisionHeight,vect(0,0,0),class'DamTypeBleedFixed');
		LastBleedOutTime = Level.TimeSeconds;
	}

    Super(KF_StoryNPC_Spawnable).Tick(DeltaTime);

	// do we really need this?  -- PooSH
	if ( Controller == none || Controller.Enemy == none ) {
		/* Update his idle state to reflect his injuries , etc. */
		IdleRestAnim = GetIdleAnim();
		IdleWeaponAnim = IdleRestAnim;
	}
}

simulated function AddHealth()
{
	super.AddHealth();
	
	if ( bActivateOnHealing && !bActive && float(Health)/HealthMax >= HealthPctToActivate ) {
		SetActive(true);
		if ( bActivateOnHealingOnlyOnce )
			bActivateOnHealing = false;
	}
}

simulated function  name GetIdleAnim()
{
    if( !bActive )
    {
        return IdleSeatedAnim;
    }

    return IdleStandingInjuredAnim;
}

function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
	if( DeathEvent != '' )
    {
        TriggerEvent(DeathEvent,Killer,self);
    }
	
	if ( Touchable != none )
		Touchable.Destroy();	
	
	super.Died(Killer, damageType, HitLocation);
}


simulated event PostRender2D(Canvas C, float ScreenLocX, float ScreenLocY)  // called if bScriptPostRender is true, overrides native team beacon drawing code
{
	local PlayerController PC;
	local float Opacity;
//	local float Dist;
	local HUDKillingFloor KFHUD;
    local vector CameraLocation,CamDir;
    local rotator CameraRotation;

	if(!bShowHealthBar ||
	!bActive ||
    GetStateName() == 'Dying')
	{
        return;
	}

	PC = Level.GetLocalPlayerController();

	if ( PC != None 
			&& (HealthBarMaxDistanceSquared == 0 || VSizeSquared(PC.Location - Location) < HealthBarMaxDistanceSquared) 
			&& (!bHealthBarOnlyIfVisible || PC.CanSee(self)) )
	{
        KFHUD = HUDKillingFloor(PC.myHUD);
        if(KFHUD != none)
        {
            C.GetCameraLocation(CameraLocation, CameraRotation);
            CamDir  = vector(CameraRotation);

            /* Rendering behind us... */
            if ( (Normal(Location - CameraLocation) dot CamDir) < 0 )
            {
                return;
            }

            Opacity =  FClamp(1.f - (VSize(PC.CalcViewLocation - Location) / 3000.f),0.25f,1.f) ;
            KFHUD.DrawKFBar(C,ScreenLocX,ScreenLocY,Health/HealthMax,Byte(Opacity * 255.f),false);
        }
    }
}

function TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<DamageType> damageType, optional int HitIdx )
{
    // Reduce damage for zombie attacks
    if( (class<DamTypeZombieAttack>(damageType) != none || class<DamTypeBurned>(damageType) != none
        || class<DamTypeLawRocketImpact>(damageType) != none) && Damage * ZombieDamageReduction >= 1 )
    {
        Damage *= ZombieDamageReduction;
    }

    // Cancel out the 5 we are going to lose when TakeDamage is called in the super
    // This is done so that healing works better for the Ringmaster, as his constant
    // bleedout is making healing not work well
    if( damageType == class'DamTypeBleedOut' && healthToGive > 0 )
    {
        healthtoGive+=5;
    }

    //log("Taking "$Damage$" damage of type "$damageType);

	// pushing the Ringmaster off his path can be problematic, so zero the momentum
 	super(KF_StoryNPC).TakeDamage( Damage, instigatedBy, hitlocation, vect(0,0,0), damageType, HitIdx );
}

simulated function Destroyed()
{
	if ( Touchable != none )
		Touchable.Destroy();
		
	super.Destroyed();
}

function SpawnTouchable()
{
	if ( Touchable != none )
		return;
		
	Touchable = Spawn(class'Touchable');
	Touchable.SetMyPawn(self);
	Touchable.bTouchable = bTouchable;
	Touchable.bUsable = bUsable;
}


function Touch( Actor Other )
{
	local Pawn P;
	
	if( TouchMessage != "" && Level.TimeSeconds > NextTouchMessageTime ) {
		// Send a string message to the toucher.
		P = Pawn(Other);
		if ( P != None && P.Controller != none && P.Controller.CanSee(self) )
		{
		    P.ClientMessage( TouchMessage, TouchMessageType );
			NextTouchMessageTime = Level.TimeSeconds + 1.0;
		}
	}
}

function UsedBy( Pawn user )
{
	if ( KFHumanPawn(user) != none && PlayerController(user.Controller) != none ) {
		if ( UseEvent != '' )
			TriggerEvent(UseEvent, user, self);
	}
}
/**
	Monster threat assessment functionality
	
	Changes by PooSH:
	1)	KFGameType.bUseZEDThreatAssessment set to true, i.e. in regular game new AssessThreatTo() 
		function will be used too.
	2)	Distance between monster and player will always be in place.
	3) 	AssessThreatTo will always return value > 0. Because zeds should not ignore players.
	4) 	Added randomization - zeds can choose different targets in the same circumstances.
	5)  Blood smell. Wounded players will attract zeds slightly more than their healthy teammates.
	
	* @param	Monster 		Monster's controller, for which we are calculating the threat level
	* @param	CheckDistance	Not used!
	* @return	threat level between 0 and 100, where 100 is the max threat level.
	*
	* @author 	PooSH
*/
function  float AssessThreatTo(KFMonsterController  Monster, optional bool CheckDistance)
{
	local float DistancePart, RandomPart, TacticalPart;
	local float DistanceSquared; // squared distance is calculated faster
	
	if ( !Level.Game.IsA('ScrnStoryGameInfo') )
		return super.AssessThreatTo(Monster, CheckDistance);

	// the following code is for ScrnBalance only
	
    if(Monster == none || KFMonster(Monster.Pawn) == none)
    {
        return -1.f;
    }
	
    if(bNoThreatToZEDs ||
		TeamIndex == 255   ||
		Health <= 0        ||
		!bActive           ||
		!bDamageable       ||
		(!IsThreateningTo(Monster.Pawn)) )
    {
        return -1.f;
    }	
	
	if ( LastThreatMonster == Monster && LastThreatTime == Level.TimeSeconds )
		return LastThreat; // threat level for the given monster has been calculated already during the current tick

	DistanceSquared = VSizeSquared(Monster.Pawn.Location - Location);

	DistancePart = 65.0;
	RandomPart = 100.0 - DistancePart;
	// let zeds smell blood within 25m radius - wounded players attract zeds more
	if ( DistanceSquared < 1562500.0 )
		TacticalPart += RandomPart * 0.40 * (HealthMax - Health) / HealthMax;  
	// more chance to attack the same enemy multiple times
	if ( Monster.Enemy == self || Monster.Target == self )
		TacticalPart += RandomPart * 0.25; 
	// more chance to focus on the player, who are attacking the monster
	if ( KFMonster(Monster.Pawn).LastDamagedBy == self)
		TacticalPart += RandomPart * 0.25;
	RandomPart = 100.0 - DistancePart - TacticalPart;

	// If target is closer than 1 meter, max DistancePart value will be used,
	// otherwise DistancePart is lowering by 10% per meter
	// 1 meter = 50 ups (2500 squared)
	if ( DistanceSquared > 2500.0 )
		DistancePart /= 1.0 + DistanceSquared / 250000.0;
	RandomPart *= frand();
	
	// save threat level for this tick
	LastThreatMonster = Monster;
	LastThreatTime = Level.TimeSeconds;
	LastThreat = DistancePart + TacticalPart + RandomPart;
	LastThreat *= InventoryThreatModifier();
	LastThreat += BaseAIThreatRating;
    return LastThreat;
}

// used in story game mode to attract zeds when holding some mission items
function float InventoryThreatModifier()
{
    local float ThreatRating;
    local Inventory CurInv;
    local KF_StoryInventoryItem StoryInv;

    ThreatRating = 1.0;

    /* Factor in story Items which adjust your desirability to ZEDs */
    for ( CurInv = Inventory; CurInv != none; CurInv = CurInv.Inventory )  {
        StoryInv = KF_StoryInventoryItem(CurInv);
        if(StoryInv != none)
            ThreatRating *= StoryInv.AIThreatModifier ;
    }

    return ThreatRating;
}

simulated function PlayDirectionalHit(Vector HitLoc)
{
	if ( bUseCoverAnim )
		super.PlayDirectionalHit(HitLoc);
	else 
		super(KFHumanPawn).PlayDirectionalHit(HitLoc);
}

function SetScriptedAnimData(name BaseAnim, float BlendInTime, float BlendOutTime, float AnimRate,
			byte AnimIterations, bool bLoopAnim, float StartFrame)
{
	super.SetScriptedAnimData(BaseAnim, BlendInTime, BlendOutTime, AnimRate, 
		AnimIterations, bLoopAnim, StartFrame);
	// tell server to replicate animation data immediately  -- PooSH
	// but it doesn't work anyway...
	NetUpdateTime = Level.TimeSeconds - 1; 
}

defaultproperties
{
     ZombieDamageReduction=1.000000
     bUseCoverAnim=True
     CowerAnim="CHIdle_LAW"
     IdleSeatedAnim="CHIdle_Knife"
     IdleStandingAnim="Profile_Idle"
     IdleStandingInjuredAnim="Profile_Idle"
     StandUpAnim="Profile_Idle"
     BleedingSounds(0)=SoundGroup'KF_MaleVoiceOne.Automatic_Commands.Auto_Dying'
     BleedingSounds(1)=SoundGroup'KF_MaleVoiceOne.Automatic_Commands.Auto_Dying'
     BleedingSounds(2)=SoundGroup'KF_MaleVoiceOne.SUPPORT.MEDIC'
     BleedingSounds(3)=SoundGroup'KF_MaleVoiceOne.SUPPORT.MEDIC'
     BleedingSounds(4)=SoundGroup'KF_MaleVoiceOne.SUPPORT.MEDIC'
     HitAnims(0)="HitF_Knife"
     HitAnims(1)="HitB_Knife"
     HitAnims(2)="HitL_Knife"
     HitAnims(3)="HitR_Knife"
     SkeletonMesh=SkeletalMesh'KFSoldiers.Soldier'
     RagdollOverride="British_Soldier1"
     MovementAnims(0)="JogF_Knife"
     MovementAnims(1)="JogB_Knife"
     MovementAnims(2)="JogL_Knife"
     MovementAnims(3)="JogR_Knife"
     IdleChatAnim="Profile_Idle"
     Mesh=SkeletalMesh'KF_Soldier_Trip.British_Soldier1'
}
