//=============================================================================
// Bots
//=============================================================================
class Bots expands ScriptedPawn;

//-----------------------------------------------------------------------------
// Sound functions 

function PlayAcquisitionSound()
{
}

function PlayFearSound()
{
}

function PlayRoamingSound()
{
}

function PlayThreateningSound()
{
}


//-----------------------------------------------------------------------------
// Bot functions

function string[64] KillMessage(name damageType, pawn Other)
{
	return ( Level.Game.PlayerKillMessage(damageType, Other)$PlayerName );
}

function PreBeginPlay()
{
	bIsPlayer = true;
	if (Orders == '')
		Orders = 'Roaming';
	CallParent.PreBeginPlay();
	RotationRate.Yaw *= FMax(0.75, Skill);
}

function ChangedWeapon()
{
	CallParent.ChangedWeapon();
	if ( Weapon != None )
		Weapon.SetHand(0);
}

function PreSetMovement()
{
	if (JumpZ > 0)
		bCanJump = true;
	bCanWalk = true;
	bCanSwim = true;
	bCanFly = false;
	MinHitWall = -0.5;
	bCanOpenDoors = true;
	bCanDoSpecial = true;
	bCanDuck = true;
}

function PainTimer()
{
	local float depth;
	//log("Pain Timer");
	if (Health < 0)
		return;
		
	if (HeadRegion.Zone.bWaterZone)
	{
		if (bDrowning)
			self.Damage(5, None, Location, vect(0,0,0), 'drowned'); 
		else
		{
			bDrowning = true;
			GotoState('FindAir');
		}
		if (Health > 0)
			PainTime = 2.0;
	}
	if (FootRegion.Zone.bPainZone)
	{
		depth = 0.33;
		if (Region.Zone.bPainZone)
			depth += 0.33;
		if (HeadRegion.Zone.bPainZone)
			depth += 0.34;
		if (FootRegion.Zone.DamagePerSec > 0)
			self.Damage(depth * FootRegion.Zone.DamagePerSec, None, Location, vect(0,0,0), FootRegion.Zone.DamageType); 
		else
			Health = Min(Default.Health, Health - depth * FootRegion.Zone.DamagePerSec);
		if (Health > 0)
			PainTime = 1.0;
	}
}	
function bool NeedToTurn(vector targ)
{
	local int YawErr;

	DesiredRotation = Rotation(targ - location);
	DesiredRotation.Yaw = DesiredRotation.Yaw & 65535;
	YawErr = (DesiredRotation.Yaw - (Rotation.Yaw & 65535)) & 65535;
	if ( YawErr < 6000 )
		return false;
	else if ( YawErr > 59535 )
		return false;
	return true;
}

function AnnoyedBy(Pawn Other)
{
}

function TryToDuck(vector duckDir, bool bReversed)
{
	local vector HitLocation, HitNormal, Extent;
	local actor HitActor;
	
	//log("duck");			
	duckDir.Z = 0;

	Extent.X = CollisionRadius;
	Extent.Y = CollisionRadius;
	Extent.Z = CollisionHeight;
	HitActor = Trace(HitLocation, HitNormal, Location + 256 * duckDir, Location, false, Extent);
	if (HitActor != None)
	{
		duckDir *= -1;
		HitActor = Trace(HitLocation, HitNormal, Location + 256 * duckDir, Location, false, Extent);
	}
	if (HitActor != None)
		return;
	
	HitActor = Trace(HitLocation, HitNormal, Location + 256 * duckDir - MaxStepHeight * vect(0,0,1), Location + 256 * duckDir, false, Extent);
	if (HitActor == None)
		return;
		
	//log("good duck");

	SetFall();
	PlayDuck();
	Velocity = duckDir * 400;
	Velocity.Z = 160;
	SetPhysics(PHYS_Falling);
	GotoState('Falling','Ducking');
}

//FIXME - here decide when to pause/start firing based on weapon,etc
function PlayCombatMove()
{
	if ( bMovingRangedAttack && bReadyToAttack && bCanFire )
	{
		if ( NeedToTurn(Enemy.Location) )
		{
			bAltFire = 0;
			bFire = 0;
		}
		else 
			FireWeapon(); 
		PlayMovingAttack();
		if ( FRand() > 0.5 * (1 + ReFireRate) )
		{
			bReadyToAttack = false;
			SetTimer(TimeBetweenAttacks  * (0.2 + FMax(0,(2 - 0.66 * skill)) * FRand()),false); //FIXME single spot (function)
		}
	}		
	else 
	{
		bFire = 0;
		bAltFire = 0;
		PlayRunning();
	}
}

function PlayMeleeAttack()
{
	//log("play melee attack");
	Acceleration = AccelRate * VRand();
	TweenToWaiting(0.15); 
	FireWeapon();
}

function PlayRangedAttack()
{
	//log("play ranged attack");
	TweenToWaiting(0.15);
	FireWeapon();
}

function PlayMovingAttack()
{
	PlayRunning();
	FireWeapon();
}

function PlayOutOfWater()
{
	PlayDuck();
}

function FireWeapon()
{
	ChooseWeapon();
	
	if( Weapon!=None )
	{
		ViewRotation = Rotation;
		if (FRand() < 0.65) //FIXME - intelligently choose fire or altfire
		{
			bFire = int( FRand() < ReFireRate );
			bAltFire = 0;
			Weapon.Fire(1.0);
		}
		else
		{
			bFire = 0;
			bAltFire = int( FRand() < ReFireRate );
			Weapon.AltFire(1.0);
		}
		PlayFiring();
	}
}

function ChooseWeapon()
{
	local Weapon OldWeapon;
	local Weapon newWeapon;
	local float decision;
	OldWeapon = Weapon;
	
	if (( Weapon == None) || (Weapon.AmmoType.AmmoAmount <= 0) ) 	//temp, until bots get inventory
	{
		decision = FRand();
		If (decision < 0.25) newWeapon = Spawn(class 'EightBall',self,'', Location);
		else If (decision < 0.5) newWeapon = Spawn(class 'FlakCannon',self,'', Location);
		else If (decision < 0.75) newWeapon = Spawn(class 'RazorJack',self,'', Location);			
		else  newWeapon = Spawn(class 'Stinger',self,'', Location);
		if (newWeapon != None)
			newWeapon.Touch(self);
	}
}

function WhatToDoNext(name LikelyState, name LikelyLabel)
{
	bFire = 0;
	bAltFire = 0;
	bReadyToAttack = false;
	Enemy = None;
	if ( OldEnemy != None )
	{
		Enemy = OldEnemy;
		OldEnemy = None;
		GotoState('Attacking','SetAttackTimer');
	}
	else if ( (LikelyState != 'Waiting') && (LikelyState != '') && (FRand() < 0.5) )
		GotoState(LikelyState, LikelyLabel);
	else
	{
		OrderObject = None;
		OrderTag = '';
		GotoState('Roaming');
	}
}

function Killed(pawn Killer, pawn Other, name damageType)
{
	bFire = 0;
	bAltFire = 0;
	bReadyToAttack = false;
	if (Other == Enemy)
	{
		Enemy = None;
		GotoState('Attacking');
	}
	//log(Other$" killed");
}	


//FIXME - add inventory to path network
// bot roaming state is actually gathering
// also, retreating should always be towards some needed item
// also, needs to dodge


//===============================================================================
// Bot states 

state Dying
{
ignores SeePlayer, EnemyNotVisible, HearNoise, Die, Bump, HitWall, HeadZoneChange, FootZoneChange, ZoneChange, Falling, WarnTarget;

	function ReStartPlayer()
	{
		log("restarting bot");
		if( Level.Game.RestartPlayer(self) )
		{
			Velocity = vect(0,0,0);
			Acceleration = vect(0,0,0);
			ViewRotation = Rotation;
			GotoState('Roaming');
		}
	}
	
	function Damage( int Damage, Pawn instigatedBy, Vector hitlocation, 
							Vector momentum, name damageType)
	{
	}
	
	function BeginState()
	{
	}

Begin:
	Enemy = None;
	Sleep(0.2);
	SpawnCarcass();
	HidePlayer();
TryAgain:
	Sleep(1.0);	
	ReStartPlayer();
	Goto('TryAgain');
}


state MeleeAttack
{
ignores SeePlayer, HearNoise, Bump;

	function KeepAttacking()
	{
		if ( (Enemy == None) || (Enemy.Health <= 0)
			|| (Size(Enemy.Location - Location) > (0.9 * MeleeRange + Enemy.CollisionRadius + CollisionRadius)) ) 
			GotoState('Attacking', 'SetAttackTimer');
		else 
		{
			bReadyToAttack = true;
			SetTimer(TimeBetweenAttacks, false);
			GotoState('TacticalMove', 'NoCharge');
		}
	}
	
	function BeginState()
	{
		Target = Enemy;
		bCanJump = false;
	}
	
	function EndState()
	{
		bCanJump = (JumpZ > 0);
	}			
}

state RangedAttack
{
ignores SeePlayer, HearNoise;

	function KeepAttacking()
	{
		if ( (FRand() > ReFireRate) || (Enemy == None) || (Enemy.Health <= 0) || !LineOfSightTo(Enemy) )
		{
			bFire = 0;
			bAltFire = 0; 
			GotoState('Attacking', 'SetAttackTimer');
		}
		else if ( Skill > 2.5 * FRand() )
		{
			bReadyToAttack = true;
			GotoState('TacticalMove');
		}	
	}

	function BeginState()
	{
		Target = Enemy;
		Disable('AnimEnd');
	}

}

state Hunting
{
ignores EnemyNotVisible; 

	function AnimEnd()
	{
		PlayRunning();
		bFire = 0;
		bAltFire = 0;
		Disable('AnimEnd');
	}


	function Bump(actor Other)
	{
		//log(Other.class$" bumped "$class);
		if (Pawn(Other) != None)
		{
			if (Enemy == Other)
				bReadyToAttack = True; //can melee right away
			SetEnemy(Pawn(Other));
			LastSeenPos = Enemy.Location;
		}
		setTimer(2.0, false);
		Disable('Bump');
	}
	
}

//FIXME - improve FindAir (use paths)
state FindAir
{
ignores SeePlayer, HearNoise, Bump;

	function HeadZoneChange(ZoneInfo newHeadZone)
	{
		CallGlobal.HeadZoneChange(newHeadZone);
		if (!newHeadZone.bWaterZone)
			GotoState('Attacking');
	}

	function Damage( int Damage, Pawn instigatedBy, Vector hitlocation, 
						Vector momentum, name damageType)
	{
		CallParent.Damage(Damage, instigatedBy, hitlocation, momentum, damageType);
		if ( NextState == 'TakeHit' )
		{
			NextState = 'FindAir'; 
			NextLabel = 'TakeHit';
			GotoState('TakeHit'); 
		}
	}

	function HitWall(vector HitNormal, actor Wall)
	{
		//change directions
		Destination = 200 * (Normal(Destination - Location) + HitNormal);
	}

	function AnimEnd() 
	{
		if (Enemy != None)
			PlayCombatMove();
		else
			PlayRunning();
	}

	function Timer()
	{
		bReadyToAttack = True;
		settimer(0.5, false);
	}

	function EnemyNotVisible()
	{
		////log("enemy not visible");
		bReadyToAttack = false;
	}

/* PickDestination()
*/
	function PickDestination(bool bNoCharge)
	{
		Destination = VRand();
		Destination.Z = 1;
		Destination = Location + 200 * Destination;				
	}

Begin:
	//log("Find air");
	TweenToRunning(0.2);
	Enable('AnimEnd');
	PickDestination(false);

DoMove:	
	if ( Enemy == None )
		MoveTo(Destination);
	else
	{
		bCanFire = true;
		StrafeFacing(Destination, Enemy);	
	}
	GotoState('Attacking');

TakeHit:
	TweenToRunning(0.15);
	Goto('DoMove');

}

state TacticalMove
{
ignores SeePlayer, HearNoise, Bump;
}
