//=============================================================================
// NPCMaleOneBot.
//=============================================================================
class NPCMaleOneBot expands MaleOneBot;

var() string NPCName;
var() Bool DefenderTeam;
var() Bool DefenderFinal;

Function PostBeginPlay()
{
	PlayerReplicationInfo.Team = 3 ;
	if ( NPCName == "")
		{
		PlayerReplicationInfo.PlayerName = default.npcname;
		NpcName=PlayerReplicationInfo.PlayerName ;
		}
	else
		PlayerReplicationInfo.PlayerName = npcname ;
		NpcName=PlayerReplicationInfo.PlayerName ;
}


state Dying
{
ignores SeePlayer, EnemyNotVisible, HearNoise, Died, Bump, Trigger, HitWall, HeadZoneChange, FootZoneChange, ZoneChange, Falling, WarnTarget, LongFall, SetFall, PainTimer;

	function ReStartPlayer()
	{
		if( bHidden && Level.Game.RestartPlayer(self) )
		{
			Velocity = vect(0,0,0);
			Acceleration = vect(0,0,0);
			ViewRotation = Rotation;
			ReSetSkill();
			SetPhysics(PHYS_Falling);
			GotoState('Roaming');
		}
		else
			GotoState('Dying', 'TryAgain');

	}

	function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation,
							Vector momentum, name damageType)
	{
		if ( !bHidden )
			Super.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
	}

	function BeginState()
	{
		SetTimer(0, false);
		Enemy = None;
		AmbushSpot = None;
		bFire = 0;
		bAltFire = 0;
	}

	function EndState()
	{
		if ( Health <= 0 )
			log(self$" health still <0");
	}

Begin:
	Sleep(0.2);
	if ( !bHidden )
	{
		SpawnCarcass();
		HidePlayer();
	}
TryAgain:
	Sleep(0.25 + DeathMatchGame(Level.Game).NumBots * FRand());
	// ReStartPlayer();
	// Goto('TryAgain');
WaitingForStart:
	bHidden = true;
}

/* Acquisition -
Creature has just reacted to stimulus, and set an enemy
- depending on strength of stimulus, and ongoing stimulii, vary time to focus on target and start attacking (or whatever.  FIXME - need some acquisition specific animation
HearNoise and SeePlayer used to improve/change stimulus
*/

state Acquisition
{
ignores falling, landed; //fixme


	function WarnTarget(Pawn shooter, float projSpeed, vector FireDir)
	{
		local eAttitude att;

		if (DefenderTeam)
			{
			if (PlayerReplicationInfo.Team != Assault(Level.Game).Defender.TeamIndex)
				{
				PlayerReplicationInfo.Team = Assault(Level.Game).Defender.TeamIndex;
				}
			}
		else
			{
			if (PlayerReplicationInfo.Team != Assault(Level.Game).Attacker.TeamIndex)
				{
				PlayerReplicationInfo.Team = Assault(Level.Game).Attacker.TeamIndex;
				}
			}

		att = AttitudeTo(shooter);
		if ( ((att == ATTITUDE_Ignore) || (att == ATTITUDE_Threaten)) )
			damageAttitudeTo(shooter);
	}

	function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation,
							Vector momentum, name damageType)
	{

		if (DefenderTeam)
			{
			if (PlayerReplicationInfo.Team != Assault(Level.Game).Defender.TeamIndex)
				{
				PlayerReplicationInfo.Team = Assault(Level.Game).Defender.TeamIndex;
				}
			}
		else
			{
			if (PlayerReplicationInfo.Team != Assault(Level.Game).Attacker.TeamIndex)
				{
				PlayerReplicationInfo.Team = Assault(Level.Game).Attacker.TeamIndex;
				}
			}

		LastSeenPos = Enemy.Location;
		Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
		if ( health <= 0 )
			return;
		if (NextState == 'TakeHit')
		{
			NextState = 'Attacking';
			NextLabel = 'Begin';
			GotoState('TakeHit');
		}
		else
			GotoState('Attacking');
	}

	function HearNoise(float Loudness, Actor NoiseMaker)
	{
		local vector OldLastSeenPos;

		if (DefenderTeam)
			{
			if (PlayerReplicationInfo.Team != Assault(Level.Game).Defender.TeamIndex)
				{
				PlayerReplicationInfo.Team = Assault(Level.Game).Defender.TeamIndex;
				}
			}
		else
			{
			if (PlayerReplicationInfo.Team != Assault(Level.Game).Attacker.TeamIndex)
				{
				PlayerReplicationInfo.Team = Assault(Level.Game).Attacker.TeamIndex;
				}
			}

		if ( SetEnemy(NoiseMaker.instigator) )
		{
			OldLastSeenPos = LastSeenPos;
			if ( Enemy ==  NoiseMaker.instigator  )
				LastSeenPos = 0.5 * (NoiseMaker.Location + VSize(NoiseMaker.Location - Location) * vector(Rotation));
			else if ( (Pawn(NoiseMaker) != None) && (Enemy == Pawn(NoiseMaker).Enemy) )
				LastSeenPos = 0.5 * (Pawn(NoiseMaker).Enemy.Location + VSize(Pawn(NoiseMaker).Enemy.Location - Location) * vector(Rotation));
			if ( VSize(OldLastSeenPos - Enemy.Location) < VSize(LastSeenPos - Enemy.Location) )
				LastSeenPos = OldLastSeenPos;
		}

	}

	function SeePlayer(Actor SeenPlayer)
	{
		if ( SetEnemy(Pawn(SeenPlayer)) )
		{
			MakeNoise(1.0);
			NextAnim = '';
			LastSeenPos = Enemy.Location;
			GotoState('Attacking');

		if (DefenderTeam)
			{
			if (PlayerReplicationInfo.Team != Assault(Level.Game).Defender.TeamIndex)
				{
				PlayerReplicationInfo.Team = Assault(Level.Game).Defender.TeamIndex;
				}
			}
		else
			{
			if (PlayerReplicationInfo.Team != Assault(Level.Game).Attacker.TeamIndex)
				{
				PlayerReplicationInfo.Team = Assault(Level.Game).Attacker.TeamIndex;
				}
			}

		}
	}

	function BeginState()
	{

		if (DefenderTeam)
			{
			if (PlayerReplicationInfo.Team != Assault(Level.Game).Defender.TeamIndex)
				{
				PlayerReplicationInfo.Team = Assault(Level.Game).Defender.TeamIndex;
				}
			}
		else
			{
			if (PlayerReplicationInfo.Team != Assault(Level.Game).Attacker.TeamIndex)
				{
				PlayerReplicationInfo.Team = Assault(Level.Game).Attacker.TeamIndex;
				}
			}

		if (health <= 0)
			log(self$" acquisition while dead");
		Disable('Tick'); //only used for bounding anim time
		SetAlertness(-0.5);
	}

PlayOut:
	Acceleration = vect(0,0,0);

		if (DefenderTeam)
			{
			if (PlayerReplicationInfo.Team != Assault(Level.Game).Defender.TeamIndex)
				{
				PlayerReplicationInfo.Team = Assault(Level.Game).Defender.TeamIndex;
				}
			}
		else
			{
			if (PlayerReplicationInfo.Team != Assault(Level.Game).Attacker.TeamIndex)
				{
				PlayerReplicationInfo.Team = Assault(Level.Game).Attacker.TeamIndex;
				}
			}

	if ( (AnimFrame < 0.6) && IsAnimating() )
	{
		Sleep(0.05);
		Goto('PlayOut');
	}

Begin:
		if (DefenderTeam)
			{
			if (PlayerReplicationInfo.Team != Assault(Level.Game).Defender.TeamIndex)
				{
				PlayerReplicationInfo.Team = Assault(Level.Game).Defender.TeamIndex;
				}
			}
		else
			{
			if (PlayerReplicationInfo.Team != Assault(Level.Game).Attacker.TeamIndex)
				{
				PlayerReplicationInfo.Team = Assault(Level.Game).Attacker.TeamIndex;
				}
			}

	Acceleration = vect(0,0,0);
	if (NeedToTurn(LastSeenPos))
	{
		PlayTurning();
		TurnTo(LastSeenPos);
	}
	DesiredRotation = Rotator(LastSeenPos - Location);
	TweenToFighter(0.2);
	FinishAnim();
	////log("Stimulus = "$Stimulus);
	if ( AttitudeTo(Enemy) == ATTITUDE_Fear )  //will run away from noise
	{
		LastSeenPos = Enemy.Location;
		MakeNoise(1.0);
		NextAnim = '';
		GotoState('Attacking');
	}
	else //investigate noise
	{
		////log("investigate noise");
		if ( pointReachable((Location + LastSeenPos) * 0.5) )
		{
			TweenToWalking(0.3);
			FinishAnim();
			PlayWalking();
			MoveTo((Location + LastSeenPos) * 0.5, WalkingSpeed);
			Acceleration = vect(0,0,0);
		}
		WhatToDoNext('','');
	}

//		if (DefenderTeam)
//			{
//			if (PlayerReplicationInfo.Team != Assault(Level.Game).Defender.TeamIndex)
//				{
//				PlayerReplicationInfo.Team = Assault(Level.Game).Defender.TeamIndex;
//				}
//			}
//		else
//			{
//			if (PlayerReplicationInfo.Team != Assault(Level.Game).Attacker.TeamIndex)
//				{
//				PlayerReplicationInfo.Team = Assault(Level.Game).Attacker.TeamIndex;
//				}
//			}

}



state Roaming
{
	ignores EnemyNotVisible;

	function Bump(actor Other)
	{
		local vector VelDir, OtherDir;
		local float speed;

		//log(Other.class$" bumped "$class);
		if (Pawn(Other) != None)
		{
			if ( (Other == Enemy) || SetEnemy(Pawn(Other)) )
			{
				bReadyToAttack = true;
				GotoState('Attacking');
			}
			return;
		}
		if ( TimerRate <= 0 )
			setTimer(1.0, false);
		speed = VSize(Velocity);
		if ( speed > 1 )
		{
			VelDir = Velocity/speed;
			VelDir.Z = 0;
			OtherDir = Other.Location - Location;
			OtherDir.Z = 0;
			OtherDir = Normal(OtherDir);
			if ( (VelDir Dot OtherDir) > 0.9 )
			{
				Velocity.X = VelDir.Y;
				Velocity.Y = -1 * VelDir.X;
				Velocity *= FMax(speed, 200);
			}
		}
		else if ( bCamping )
			GotoState('Wandering');
		Disable('Bump');
	}

	function HandleHelpMessageFrom(Pawn Other)
	{
		if ( (Health > 70) && (Weapon.AIRating > 0.5) && (Other.PlayerReplicationInfo.Team == PlayerReplicationInfo.Team)
			&& (Other.Enemy != None)
			&& (VSize(Other.Enemy.Location - Location) < 1200) )
		{
			SetEnemy(Other.Enemy);
			GotoState('Attacking');
		}
	}

	function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation,
							Vector momentum, name damageType)
	{
		Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);


		if (DefenderFinal)
			{
			if (PlayerReplicationInfo.Team != Assault(Level.Game).Defender.TeamIndex)
				{
				PlayerReplicationInfo.Team = Assault(Level.Game).Defender.TeamIndex;
				}
			}


		if ( health <= 0 )
			return;
		if (NextState == 'TakeHit')
		{
			NextState = 'Attacking';
			NextLabel = '';
			GotoState('TakeHit');
		}
		else if ( !bCanFire && (skill > 3 * FRand()) )
			GotoState('Attacking');
	}

	function FearThisSpot(Actor aSpot)
	{
		Destination = Location + 120 * Normal(Location - aSpot.Location);
		GotoState('Wandering', 'Moving');
	}

	function Timer()
	{
		bReadyToAttack = True;
		Enable('Bump');
	}

	function SetFall()
	{
		bWallAdjust = false;
		NextState = 'Roaming';
		NextLabel = 'Landed';
		NextAnim = AnimSequence;
		GotoState('FallingState');
	}

	function EnemyAcquired()
	{


		if (DefenderFinal)
			{
			if (PlayerReplicationInfo.Team != Assault(Level.Game).Defender.TeamIndex)
				{
				PlayerReplicationInfo.Team = Assault(Level.Game).Defender.TeamIndex;
				}
			}

		GotoState('Acquisition');
	}

	function HitWall(vector HitNormal, actor Wall)
	{
		if (Physics == PHYS_Falling)
			return;
		if ( Wall.IsA('Mover') && Mover(Wall).HandleDoor(self) )
		{
			if ( SpecialPause > 0 )
				Acceleration = vect(0,0,0);
			GotoState('Roaming', 'SpecialNavig');
			return;
		}
		Focus = Destination;
		if ( !bWallAdjust && PickWallAdjust() )
			GotoState('Roaming', 'AdjustFromWall');
		else
		{
			MoveTimer = -1.0;
			bWallAdjust = false;
		}
	}

//
//
//
//  P  I  C  K  D  E  S  T  I  N  A  T  I  O  N
//
//
//


	function PickDestination()
	{
		local inventory Inv, BestInv, KnowPath;
		local float Bestweight, NewWeight, DroppedDist;
		local actor BestPath, HitActor;
		local vector HitNormal, HitLocation;
		local decoration Dec;
		local bool bCanReach;
		local NavigationPoint N;
		local int i;

		if (DefenderFinal)
			{
			if (PlayerReplicationInfo.Team != Assault(Level.Game).Defender.TeamIndex)
				{
				PlayerReplicationInfo.Team = Assault(Level.Game).Defender.TeamIndex;
				}
			}

		if ( (EnemyDropped != None) && !EnemyDropped.bDeleteMe
			&& (EnemyDropped.Owner == None) )
		{
			DroppedDist = VSize(EnemyDropped.Location - Location);
			if ( (DroppedDist < 800) && ActorReachable(EnemyDropped) )
			{
				BestWeight = EnemyDropped.BotDesireability(self);

				if ( BestWeight > 0.4 )
				{
					MoveTarget = EnemyDropped;
					EnemyDropped = None;
					return;
				}
				BestInv = EnemyDropped;
				BestWeight = BestWeight/DroppedDist;
				KnowPath = BestInv;

			}
			else
				BestWeight = 0;
		}
		else
			BestWeight = 0;

		EnemyDropped = None;

		//first look at nearby inventory < 600 dist
		foreach visiblecollidingactors(class'Inventory', Inv, 600)
			if ( (Inv.IsInState('PickUp')) && (Inv.MaxDesireability/50 > BestWeight)
				&& (Inv.Location.Z < Location.Z + MaxStepHeight + CollisionHeight) )
			{
				NewWeight = inv.BotDesireability(self)/VSize(Inv.Location - Location);
				// log("looking at local "$Inv$" weight "$100000*NewWeight);
				if ( NewWeight > BestWeight )
				{
					BestWeight = NewWeight;
					BestInv = Inv;

				}
			}

		if ( BestInv != None )
		{
			bCanJump = ( BestInv.Location.Z > Location.Z - CollisionHeight - MaxStepHeight );
			bCanReach = ActorReachable(BestInv);
		}
		else
			bCanReach = false;
			bCanJump = true;
		if ( bCanReach )
		{
			//log("Roam to local "$BestInv);
			MoveTarget = BestInv;

			return;
		}
		else if ( KnowPath != None )
		{
			MoveTarget = KnowPath;
			return;
		}

//c		if ( (Weapon.AIRating > 0.5) && (Health > 90) )
//c		{
//			bWantsToCamp = ( bWantsToCamp || (FRand() < CampingRate * FMin(1.0, Level.TimeSeconds - LastCampCheck)) );
//			LastCampCheck = Level.TimeSeconds;
//					 log("FOUR" );
//c
//c		}
//c		else
//			bWantsToCamp = false;

		if ( bWantsToCamp && FindAmbushSpot() )
			return;

		// if none found, check for decorations with inventory
		if ( !bNoShootDecor )
			foreach visiblecollidingactors(class'Decoration', Dec, 500)
				if ( Dec.Contents != None )
				{
					bNoShootDecor = true;
					Target = Dec;
					GotoState('Roaming', 'ShootDecoration');

					return;
				}

		bNoShootDecor = false;
		BestWeight = 0;

		// look for long distance inventory
		BestPath = FindBestInventoryPath(BestWeight, (skill >= 2));
		//log("roam to "$BestPath);
		//log("---------------------------------");
		if ( BestPath != None )
		{
			MoveTarget = BestPath;

			return;
		}

		 // if nothing, then wander or camp
		if ( FRand() < 0.35 )
		{
			GotoState('Wandering');
		}
		else
		{
			CampTime = 3.5 + FRand() - skill;
			GotoState('Roaming', 'Camp');

		}
	}

	function bool FindAmbushSpot()
	{
		if ( (AmbushSpot == None) && (Ambushpoint(MoveTarget) != None) )
			AmbushSpot = Ambushpoint(MoveTarget);

		if ( Ambushspot != None )
		{
			Ambushspot.taken = true;
			if ( VSize(Ambushspot.Location - Location) < 2 * CollisionRadius )
			{
				CampTime = 10.0;
				GotoState('Roaming', 'LongCamp');
				return true;
			}
			if ( ActorReachable(Ambushspot) )
			{
				MoveTarget = Ambushspot;
				return true;
			}
			MoveTarget = FindPathToward(Ambushspot);
			if ( MoveTarget != None )
				return true;
			Ambushspot.taken = false;
			Ambushspot = None;
		}
		return false;
	}

	function AnimEnd()
	{
		if ( bCamping )
			PlayWaiting();
		else
			PlayRunning();
	}

	function ShareWithTeam()
	{
		local bool bHaveItem, bIsHealth, bOtherHas;
		local Inventory goalItem;
		local Pawn P;

		goalItem = InventorySpot(MoveTarget).markedItem;
		if ( goalItem == None ) // FIXME REMOVE
		{
			log(" No marked item for "$MoveTarget);
			return;
		}

		if ( goalItem.IsA('Weapon') )
		{
			if ( (Weapon == None) || (Weapon.AIRating < 0.45) )
				return;
			bHaveItem = (FindInventoryType(goalItem.class) != None);
		}
		else if ( goalItem.IsA('Health') )
		{
			bIsHealth = true;
			if ( Health < 60 )
				return;
		}
		else
			return;

		CampTime = 2.0;

		for ( P=Level.PawnList; P!=None; P=P.nextPawn )
			if ( P.bIsPlayer && (P.PlayerReplicationInfo.Team == PlayerReplicationInfo.Team)
				&& ((P.MoveTarget == MoveTarget) || (P.MoveTarget == goalItem)
					|| (!bIsHealth && P.IsA('PlayerPawn') && !P.IsA('Spectator')
						&& (VSize(P.Location - Location) < 1250) && LineOfSightTo(P))) )
			{
				//decide who needs it more
				if ( bIsHealth )
				{
					if ( Health > P.Health + 10 )
					{
						GotoState('Roaming', 'GiveWay');
						return;
					}
					else if ( (P.IsInState('Roaming')) && (Health < P.Health - 10) )
						P.GotoState('Roaming', 'GiveWay');
				}
				else
				{
					bOtherHas = (P.FindInventoryType(goalItem.class) != None);
					if ( !bHaveItem && bOtherHas )
					{
						if ( P.IsInState('Roaming') )
							P.GotoState('Roaming', 'GiveWay');
					}
					else if ( bHaveItem && !bOtherHas )
					{
						GotoState('Roaming', 'GiveWay');
						return;
					}
				}
			}
	}

	function BeginState()
	{

		if (DefenderFinal)
			{
			if (PlayerReplicationInfo.Team != Assault(Level.Game).Defender.TeamIndex)
				{
				PlayerReplicationInfo.Team = Assault(Level.Game).Defender.TeamIndex;
				}
			}
		bNoShootDecor = false;
		bCanFire = false;
		bCamping = false;
		if ( bNoClearSpecial )
			bNoClearSpecial = false;
		else
		{
			bSpecialPausing = false;
			bSpecialGoal = false;
			SpecialGoal = None;
			SpecialPause = 0.0;
		}
	}

	function EndState()
	{
		if ( AmbushSpot != None )
		{
			AmbushSpot.taken = false;
			if ( Enemy != None )
				AmbushSpot = None;
		}
		bCamping = false;
		bWallAdjust = false;
	}

LongCamp:
	bCamping = true;
	Acceleration = vect(0,0,0);
	TweenToWaiting(0.15);
	if ( OrderObject.IsA('AmbushPoint') )
		TurnTo(Location + (Ambushpoint(OrderObject)).lookdir);
	Sleep(CampTime);
	Goto('Begin');

GiveWay:
	// log("sharing");
	bCamping = true;
	Acceleration = vect(0,0,0);
	TweenToWaiting(0.15);
	if ( NearWall(200) )
	{
		PlayTurning();
		TurnTo(MoveTarget.Location);
	}
	Sleep(CampTime);
	Goto('Begin');

Camp:
	bCamping = true;
	Acceleration = vect(0,0,0);
	TweenToWaiting(0.15);
ReCamp:
	if ( NearWall(200) )
	{
		PlayTurning();
		TurnTo(Focus);
	}
	Sleep(CampTime);
	if ( (Weapon != None) && (Weapon.AIRating > 0.4) && (3 * FRand() > skill + 1) )
		Goto('ReCamp');
Begin:
	bCamping = false;
	TweenToRunning(0.1);
	WaitForLanding();

RunAway:
	PickDestination();
SpecialNavig:
	if (SpecialPause > 0.0)
	{
		Disable('AnimEnd');
		Acceleration = vect(0,0,0);
		TweenToPatrolStop(0.3);
		Sleep(SpecialPause);
		SpecialPause = 0.0;
		Enable('AnimEnd');
		TweenToRunning(0.1);
		Goto('RunAway');
	}
Moving:
	if ( !IsAnimating() )
		AnimEnd();
	if ( MoveTarget == None )
	{
		Acceleration = vect(0,0,0);
		Sleep(0.0);
		Goto('RunAway');
	}
	if ( MoveTarget.IsA('InventorySpot') )
	{
		if ( Level.Game.bTeamGame )
			ShareWithTeam();
		if ( InventorySpot(MoveTarget).markedItem.BotDesireability(self) > 0 )
		{
			if ( InventorySpot(MoveTarget).markedItem.GetStateName() == 'Pickup' )
				MoveTarget = InventorySpot(MoveTarget).markedItem;
			else if ( VSize(Location - MoveTarget.Location) < CollisionRadius )
			{
				CampTime = 3.5 + FRand() - skill;
				Goto('Camp');
			}
		}
	}
	bCamping = false;
	MoveToward(MoveTarget);
	Goto('RunAway');

TakeHit:
	TweenToRunning(0.12);
	Goto('Moving');

Landed:
	if ( MoveTarget == None ) //FIXME - do this in all landed: !!!
		Goto('RunAway');
	Goto('Moving');

AdjustFromWall:
	bWallAdjust = true;
	bCamping = false;
	StrafeTo(Destination, Focus);
	Destination = Focus;
	MoveTo(Destination);
	bWallAdjust = false;
	Goto('Moving');

ShootDecoration:
	TurnToward(Target);
	if ( Target != None )
	{
		FireWeapon();
		bAltFire = 0;
		bFire = 0;
	}
	Goto('RunAway');
}

