/*
Gamebots Pogamut derivation Copyright (c) 2010-2011, Michal Bida, Radek Pibil

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

   * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
   * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software must also be in compliance with the Epic Games Inc. license for mods which states the following: "Your mods must be distributed solely for free, period. Neither you, nor any other person or party, may sell them to anyone, commercially exploit them in any way, or charge anyone for receiving or using them without prior written consent of Epic Games Inc. You may exchange them at no charge among other end-users and distribute them to others over the Internet, on magazine cover disks, or otherwise for free." Please see http://www.epicgames.com/ut2k4_eula.html for more information.

*/
//=============================================================================
// RemoteBot.
// Based on UTBot class
//=============================================================================
class RemoteBot extends UTBot
	config(GameBotsUDK);

//maybe link to bot's periphreal vision? UT bots need much less of an arc though
//like Pawn's PeriphrealVision, this value should be the cosine of the limits
//of visual field. (i.e. 0.707 = 45 degrees to each side, or a 90* arc.)
var config float remoteVisionLimit;

const MAX_VIEW_DISTANCE = 9000;

//The socket to the agent
var BotConnection myConnection;

//The three remote vars compliment the my vars right below. The only one
//that ever needs to be duplicated is RemoteEnemy and myTarget
//just need RemoteDestination || myDestination and RFocus || myFocus

//Who the remote bot is trying to shoot at. Used by the aiming code.
var Actor RemoteEnemy;

//The spot the bot is shooting at
var FocusActor myTarget;

//If false, we will update myTarget location according our current focal point
var bool bTargetLocationLocked;

//The spot the bot is moving to
var vector myDestination;
//The spot the bot is looking at
var vector myFocalPoint;

//Used for smooth movement, see StartUp state
var vector pendingDestination;

var bool bPendingDestination;

var class<UTPawn> PawnClass;

//This is an indicator that we are in StartUp:MoveContinuous state. We need this, so turn
//command work properly when the bot is moving continuous!
var bool movingContinuous;

//If true bot will respawn automatically when killed
var config bool bAutoSpawn;

//When true auto raytracing producing sync. ATR messsages will be on
var config bool bAutoTrace;

//If true we will draw trace lines (results of AutoTrace() functionsin BotConnection)
//in the game
var config bool bDrawTraceLines;

//If we should spawn an actor at the point the bot has set as focal point
var config bool bShowFocalPoint;

//If we should provide aim correct based on bot Skill also when shooting at Location
var config bool bPerfectLocationAim;

//If we should include FadeOut attribute into message text or not. for debug.
var config bool bIncludeFadeOutInMsg;

//maximum number we can multiply BaseSpeed of the bot
var config float MaxSpeed;

//Used for changing the bot appearance. see GBxPawn.Setup() function
var config string DesiredSkin;

//For disabling auto pickup, custom varialbe, will affect Pawn.bCanPickupInventory
var config bool bDisableAutoPickup;

//By this default pawn speed will be multiplied ranges from 0.1 to 2
var config float SpeedMultiplier;

//set to true if we want to do a double jump in Timer() function
var bool bShouldDoubleJump;

//force that will be used for second jump command in Timer() function completing
//double jump sequence
var float doubleJumpForce;

//helper variable to store the direction we want to go with CMOVE
var vector cmoveDirection;

//this is here, so we can properly debug the bot in java. it is the adress we need
//to connect to in order to debug the bot (in Pogamut the bots are now run each one in
//different JVM)
var string jmx;

//holding last bump time to avoid sending the message repeatably
var float lastBumpTime;

//holding last wall hit to avoid sending the message repeatably
var float lastWallHit;

//1 - normal fire, 2 - alt fire, 0 - should not fire
var byte shouldFire;

var Actor myFocus;

var GBGameHandler GBGameHandler;

//Desired precision of move action (actually, the error of move action may be slightly greater)
const EPSILON_DIST = 32.0f;

//override - using translocator in this function
function Actor FaceActor(float StrafingModifier)
{
	return none;
}

//Called at the start of the match
function StartMatch()
{
	// SENDMESSAGE
	// !!! possible new message
}


//This function will properly destroy the pawn of this conroller (otherwise conflicts with game mechanics)
function DestroyPawn()
{
	if (Pawn == None)
		return;

	Pawn.Died(none, class'DamageType', Pawn.Location);
}

//This function is called by the RESPAWN command - it makes sure everything will work ok
function GBMovePlayer(vector startLocation, rotator startRotation) {
	local Vector newFocalPoint;

	`log("GBMovePlayer:L:" $startLocation $ ";R:" $startRotation );

	newFocalPoint = startLocation + (Vector(startRotation) * 100);
	SetFocalPoint(newFocalPoint);
	myFocalPoint = newFocalPoint;
	
	Pawn.SetLocation(startLocation);
	Pawn.SetRotation(startRotation);
	Pawn.ClientSetRotation(startRotation);
}

//This function is called by the RESPAWN command - it makes sure everything will work ok
function RespawnPlayer(optional vector startLocation, optional rotator startRotation)
{
	`log("RespawnPlayer:L:" $startLocation $ ";R:" $startRotation $ ";IsInState('Dead'):" $ IsInState('Dead') $ ";bAutoSpawn:" $ bAutoSpawn );

	if (IsInState('Dead') && !bAutoSpawn) //
	{
		GBGameInterface(WorldInfo.Game).GetGameHandler().SpawnPawn( self, startLocation, startRotation);
	}
	else if (bAutoSpawn)
	{
		bAutoSpawn = false; //otherwise it would get respawned in Dead state without specifyed locatin, rotation
		DestroyPawn();
		GotoState('Dead');
		GBGameInterface(WorldInfo.Game).GetGameHandler().SpawnPawn( self, startLocation, startRotation);
		bAutoSpawn = true;

	}
	else if (!IsInState('Dead') && !bAutoSpawn)
	{
		DestroyPawn();
		GotoState('Dead');
		GBGameInterface(WorldInfo.Game).GetGameHandler().SpawnPawn( self, startLocation, startRotation);
	}

	if (Pawn != None) {
		gotostate('Alive','DoStop');
	} else {
		`log("Pawn is None after ResartPlayer");
	}
}

function Restart(bool bVehicleTransition)
{
	Enemy = None;
	if ( Pawn != None)
	{
		Pawn.Restart();		

		// if not vehicle transition, clear controller information
		if ( !bVehicleTransition && Pawn.InvManager != None )
		{
			Pawn.InvManager.UpdateController();
		}
	}
}

function HandlePickup(Inventory Inv) {
	local string InventoryId;
	local string pickupType;
	local string outstring;
	local int amount;

	if (Inv.isA('UTWeapon')) {
		InventoryId = myConnection.GetWeaponClassString(Inv.Class);
		amount =  UTWeapon(Inv).AmmoCount;
		pickupType = myConnection.GetWeaponClassString(Inv.Class) $ ".WeaponPickup";
	} else if (Inv.isA('UTJumpBoots') || Inv.isA('UTTimedPowerup')) {
		InventoryId = Mid(Inv.Class, 2);
		amount =  1;
		pickupType = Inv.Class $ ".Pickup";
	} else {
		InventoryId = string(Inv.Class);
		amount =  1;
		pickupType = Inv.Class $ ".Pickup";
	}

	outstring = "IPK {Id " $ Inv $
		"} {InventoryId " $ InventoryId $
		"} {Location " $ Inv.Location $
		"} {Amount " $ amount $
		"} {Dropped " $ (Inv.Physics == PHYS_Falling) $
		"} {Type " $ pickupType $
		"}";

	myConnection.SendLine(outstring);

	super.HandlePickup(Inv);
}

//Called when the bot land on the ground after falling
function bool NotifyLanded(vector HitNormal, Actor FloorActor)
{
	myConnection.SendLine("LAND {HitNormal " $ HitNormal $ "}");

	//restart, so we continue moving and not going back to our previous
	//destination, as we may be over it because of the fall
	if (movingContinuous)
		gotoState('Alive','MoveContRepeat');
	return true;
}

function RemotePickup(string target)
{
	local PickupFactory P;
	local DroppedPickup D;
	local string id;

	//log("My target "$ target);

	if ((target != "") && (Pawn != none))
		foreach Pawn.TouchingActors(class'PickupFactory', P) {
			id = string(P);
			`log("First touching pickup factory: " $ myConnection.GetUniquePickupFactoryInventoryId(P) $
				" and his id: " $ id);

			if (target == id)
			{
				Pawn.bCanPickupInventory = true;
				P.Touch(Pawn, Pawn.BaseSkelComponent, Pawn.Location, P.Location - Pawn.Location);
				Pawn.bCanPickupInventory = false;
			    //log("We've got it");
				break;
			}
		}

		foreach Pawn.TouchingActors(class'DroppedPickup', D) {
			id = string(D);
			`log("First touching dropped pickup: " $ myConnection.GetUniqueDroppedInventoryId(D) $
				" and his id: " $ id);

			if (target == id)
			{
				Pawn.bCanPickupInventory = true;
				D.Touch(Pawn, Pawn.BaseSkelComponent, Pawn.Location, D.Location - Pawn.Location);
				Pawn.bCanPickupInventory = false;
			    //log("We've got it");
				break;
			}
		}
}

function SpawnInventory(array<string> pickupType)
{
	local class<Inventory> InvClass;
	local class<PickupFactory> pickupFactory;
	local Inventory Inv;

	if (Pawn == None && pickupType.Length >= 2)
		return;

	//if pawn can't pickup inventory we will do it directly
	InvClass = class<Inventory>(DynamicLoadObject(pickupType[0], class'class'));
	if (InvClass != None) {
		Inv = Spawn(InvClass);
		Inv.GiveTo(self.Pawn);
	} else {
		pickupFactory = class<UTItemPickupFactory>(DynamicLoadObject(pickupType[0], class'class'));
		if (ClassIsChildOf(pickupFactory, class'UTHealthPickupFactory'))
		{
			Pawn.Health += class<UTHealthPickupFactory>(pickupFactory).default.HealingAmount;
		}
		else if (ClassIsChildOf(pickupFactory, class'UTArmorPickupFactory'))
		{
			if (ClassIsChildOf(pickupFactory, class'UTArmorPickup_BaseArmor'))
			{
				UTPawn(Pawn).VestArmor = Max(class'UTArmorPickupFactory'.default.ShieldAmount,
					UTPawn(Pawn).VestArmor);
			}
			else if (ClassIsChildOf(pickupFactory, class'UTArmorPickup_ThighPad'))
			{
				UTPawn(Pawn).ThighPadArmor = Max(class'UTArmorPickupFactory'.default.ShieldAmount,
					UTPawn(Pawn).ThighPadArmor);
			}
			else if (ClassIsChildOf(pickupFactory, class'UTArmorPickup_BaseArmor'))
			{
				UTPawn(Pawn).ShieldBeltArmor = Max(class'UTArmorPickupFactory'.default.ShieldAmount,
					UTPawn(Pawn).ShieldBeltArmor);
				Inv = Spawn(class'UTPickupInventory');
				Inv.DroppedPickupClass = class'UTDroppedShieldBelt';
				Inv.GiveTo(Pawn);
			}
		}
		else if (ClassIsChildOf(pickupFactory, class'UTAmmoPickupFactory') &&
				UTInventoryManager(Pawn.InvManager) != none)
		{
			UTInventoryManager(Pawn.InvManager).AddAmmoToWeapon(
				class<UTAmmoPickupFactory>(pickupFactory).default.AmmoAmount,
				class<UTAmmoPickupFactory>(pickupFactory).default.TargetWeapon);			
		}
		if (ClassIsChildOf(pickupFactory, class'UTItemPickupFactory'))
		{
			Pawn.PlaySound(class<UTItemPickupFactory>(pickupFactory).default.PickupSound);
		}
	}	
	Pawn.MakeNoise(0.2);
}

// Called when get new weapon or ammunition for weapon we do not have yet.
// Called just once per weapon type or jump boots NOT ammo (notify new object in our inventory, NOT pickup)
// For exporting weapons we have new attributes.
// TODO
function NotifyAddInventory( Inventory inputInventory )
{
	local string outstring;

	Super.NotifyAddInventory(inputInventory);

	if (inputInventory.isA('UTWeapon')) {
		outstring = "AIN {Id " $ inputInventory $
			"} {Type " $ myConnection.GetWeaponClassString(inputInventory.class) $		
			"} {PickupType " $ myConnection.GetWeaponClassString(inputInventory.class) $
			".WeaponPickup}";
	} else if (inputInventory.isA('UTJumpboots') || inputInventory.isA('UTTimedPowerup')) {
		outstring = "AIN {Id " $ inputInventory $
			"} {Type " $ Mid(inputInventory.class, 2) $		
			"} {PickupType " $ Mid(inputInventory.class, 2) $ ".Pickup" $
			"}";
	}
	
	myConnection.SendLine(outstring);
}

function NotifyTakeHit(Controller InstigatedBy, vector HitLocation, int Damage, class<DamageType> damageType, vector Momentum)
{
	local string outstring;

	LastUnderFire = WorldInfo.TimeSeconds;

	outstring = "DAM {Damage " $ Damage $ 
		"} {DamageType " $ damageType $ 
		"}";

	if (InstigatedBy != none && InstigatedBy.Pawn != None && CanSee(InstigatedBy.Pawn)) {
		outstring $= " {Instigator " $ InstigatedBy $ "}";
	}

	if( InstigatedBy != none && InstigatedBy.isA('RemoteBot'))
	{
	    RemoteBot(InstigatedBy).RemoteNotifyHit(self, Damage, DamageType);
    }

	myConnection.SendLine(outstring);
}

function RemoteNotifyHit(Controller Victim, int Damage, class<DamageType> damageType)
{
	local string outstring;
	local class<UTDamageType> utDamageType;

	if (Victim.Pawn == none || !CanSee(Victim.Pawn))
		return;

	outstring = "HIT {Id " $ Victim $
		"} {Damage " $ Damage $ "} {DamageType " $ damageType $"}";
	
	if (ClassIsChildOf(damageType, class'UTDamageType')) {
		utDamageType = class<UTDamageType>(damageType);
		outstring = outstring $
			" {WeaponName " $ myConnection.GetWeaponClassString(utDamageType.default.DamageWeaponClass) $
			"} {DirectDamage " $ utDamageType.default.bDirectDamage $
			"} {BulletHit " $ utDamageType.default.bBulletHit $
			"} {VehicleHit " $ utDamageType.default.bVehicleHit $ "}";
	}

	myConnection.SendLine(outstring);
}

function NotifyKilled(Controller Killer, Controller Killed, pawn KilledPawn, class<DamageType> damageType)
{
	super.NotifyKilled(Killer, Killed, KilledPawn, damageType);
	if (Killed == self) {
		ReportDied(Killer, damageType);
	} else {
		ReportKilled(Killer, Killed, KilledPawn, damageType);
	}

}

event HearNoise(float Loudness, Actor NoiseMaker, optional Name NoiseType )
{
	local string type;

	if (NoiseMaker.isA('Weapon')) {
		type = myConnection.GetWeaponClassString(NoiseMaker) $ ".WeaponPickup";
	} else if (NoiseMaker.isA('UTJumpBoots')) {
		type = "JumpBoots";
	} else {
		type = string(NoiseMaker.Class);
	}
			
	myConnection.SendLine("HRN {Source " $ myConnection.GetUniqueId(NoiseMaker) $
		"} {Type " $ type $ 
		"} {Rotation " $ rotator(Location - NoiseMaker.Location) $
		"} {Distance " $ VSize(Location - NoiseMaker.Location) $
		"}");
}

//overriding! so no code gets executed
event SeePlayer(Pawn SeenPlayer)
{

}

event bool NotifyHitWall(vector HitNormal, actor Wall)
{
	local vector JumpDir;

	if ( WorldInfo.TimeSeconds - 0.3 >= lastWallHit)
	{

		myConnection.SendLine("WAL {Id " $ myConnection.GetUniqueId(Wall) $
			"} {Normal " $ HitNormal $ 
			"} {Location " $ Wall.Location $ "}");

		lastWallHit = WorldInfo.TimeSeconds;
	}
	
	// Jump out of water? UT2004 does not need this -> candidate for removal, but test it first ;] -paletz
	if ( (UTPawn(Pawn) != None) && (Pawn.Physics == PHYS_Swimming) )
	{
		jumpDir = Normal(vector(Pawn.Rotation));
		JumpDir.Z = 0;
		if ( Pawn.CheckWaterJump(jumpDir) )
		{
			UTPawn(Pawn).JumpOutOfWater(jumpDir);
			bNotifyApex = true;
			bPendingDoubleJump = true;
		}
	}

	return true;
}

event NotifyPostLanded()
{
	bNotifyPostLanded = false;
}

//overriding! so no code gets executed 
event NotifyMissedJump()
{
}

//overriding! so no code gets executed 
event NotifyJumpApex()
{
	// double jump
	if ( bPendingDoubleJump )
	{
		Pawn.bWantsToCrouch = false;

		UTPawn(Pawn).DoDoubleJump(false);
		bPendingDoubleJump = false;
	}
	bNotifyApex = false;
}

//overriding! so no code gets executed 
event MissedDodge()
{

}

//handles sending messages to all and to team
function RemoteBroadcast( string Id, coerce string Msg, bool bGlobal, float FadeOut )
{
	local Controller C;

	if (bIncludeFadeOutInMsg)
		Msg = Msg $ " FadeOut=" $ FadeOut $ " s.";

    //Set the text bubble, which will be shown on the HUDs of Human players
    //when they see the bot
    if (Pawn != none) {
		/*
   		if (bSpeakingBots) {
			node = new(self) SoundNodeWave;
			node.bUseTTS = true;

			if (UTPlayerReplicationInfo(PlayerReplicationInfo).bIsFemale)
				node.TTSSpeaker = TTSSPEAKER_Rita;
			else
				node.TTSSpeaker = TTSSPEAKER_Paul;

			node.SpokenText = MsgForVoice;
			
	   		AudioDevice.TextToSpeech( MsgForVoice, 10 );
   		}*/
		GBPawn(Pawn).SetTextBubble(Id, Msg, bGlobal, FadeOut);
	}

	if (Id != "")
	{
		foreach WorldInfo.AllControllers(class'Controller', C){		
			if ( myConnection.GetUniqueId(C) == Id) {
				if (C.IsA('RemoteBot'))
					RemoteBot(C).RemoteNotifyTeamMessage(self,"Private:"$Msg);
				if (C.IsA('PlayerController'))
					PlayerController(C).TeamMessage(self.PlayerReplicationInfo, self.PlayerReplicationInfo.PlayerName$":Private:"$Msg, '');

       			return;
			}
		}
		//If we haven't found Id or Id doesn't belong to RemoteBot we will end
		return;
	}

	if ( bGlobal || !WorldInfo.Game.bTeamGame )
	{
	    //Send the message to the game channel
	    WorldInfo.Game.Broadcast(self, Msg, 'Say');
	    //Send the message to RemoteBots
	    SendGlobalMessage( Msg );
	    return;
	}

	if (WorldInfo.Game.bTeamGame)
	{
		//Send the message to team channel
		WorldInfo.Game.BroadcastTeam(self, Msg, 'TeamSay');
		//Send the message to RemoteBots
		SendTeamMessage( Msg );
		return;
	}
}

//Event for receiving string messages, called manually
event RemoteNotifyClientMessage( Controller C, coerce string S )
{
	//Could cause some parsing problems, so replacing
	S = Repl(S, "{", "_", false );
	S = Repl(S, "}", "_", false );

	myConnection.SendLine("VMS {Id "$ C $
		"} {Name " $ C.PlayerReplicationInfo.PlayerName $
		"} {Text " $ S $"}");
}

//Event for receiving string messages, called manually
event RemoteNotifyTeamMessage( Controller C, coerce string S )
{
	//Could cause some parsing problems, so replacing
	S = Repl(S, "{", "_", false );
	S = Repl(S, "}", "_", false );

	myConnection.SendLine("VMT {Id "$ C $
		"} {Name " $ C.PlayerReplicationInfo.PlayerName $
		"} {Text " $ S $"}");
}

//Send the team message to RemoteBots
function SendTeamMessage( string S )
{
	local Controller C;

	foreach WorldInfo.AllControllers(class'Controller', C) {
		if( C.PlayerReplicationInfo.Team != none ) {
			if (C.PlayerReplicationInfo.Team.TeamIndex == self.PlayerReplicationInfo.Team.TeamIndex) {
				if( C.isA('RemoteBot') && C != self ) {
					RemoteBot(C).RemoteNotifyTeamMessage(self, S);
				}
			}
		}
	}
}

//Send the global message to RemoteBots
function SendGlobalMessage( string S )
{
	local Controller C;

	foreach WorldInfo.AllControllers(class'Controller', C) {
		if( C.isA('RemoteBot') && C != self ) {
			RemoteBot(C).RemoteNotifyClientMessage( self , S);
		}
	}
}

/* SetOrders()
Called when player gives orders to bot
*/
function SetBotOrders(name NewOrders, Controller OrderGiver, bool bShouldAck) {}

//The bot (or some part of the bot - legs) entered new volume
event NotifyPhysicsVolumeChange( PhysicsVolume NewVolume )
{

	//this code is taken from super class (Bot). Had to disable automatic jumping out of water
	//super class is not called anymore
	if ( newVolume.bWaterVolume )
	{
		bPlannedJump = false;
		if (!Pawn.bCanSwim)
			MoveTimer = -1.0;
		else if (Pawn.Physics != PHYS_Swimming)
			Pawn.setPhysics(PHYS_Swimming);
	}
	else if (Pawn.Physics == PHYS_Swimming)
	{
		if ( Pawn.bCanFly )
			 Pawn.SetPhysics(PHYS_Flying);
		else
		{
			Pawn.SetPhysics(PHYS_Falling);
		}
	}

	myConnection.SendLine("VCH {Id " $ myConnection.GetUniqueId(NewVolume) $
		"} {ZoneVelocity " $ NewVolume.ZoneVelocity $  //vector
		"} {ZoneGravity " $ NewVolume.ZoneVelocity $  //vector
		"} {GroundFriction " $ NewVolume.GroundFriction $  //float
		"} {FluidFriction " $ NewVolume.FluidFriction $  //float
		"} {TerminalVelocity " $ NewVolume.TerminalVelocity $  //float
		"} {WaterVolume " $ NewVolume.bWaterVolume $
		"} {PainCausing " $ NewVolume.bPainCausing $
		"} {Destructive " $ NewVolume.bDestructive $
		"} {DamagePerSec " $ NewVolume.DamagePerSec $ //float
		"} {DamageType " $ NewVolume.DamageType $
		"} {NoInventory " $ NewVolume.bNoInventory $
		"} {MoveProjectiles " $ NewVolume.bMoveProjectiles $
		"} {NeutralZone " $ NewVolume.bNeutralZone $
		"}");


}

//overriding so no code gets executed
event MayDodgeToMoveTarget()
{

}


//called on collisions with other actors
event bool NotifyBump(Actor Other, Vector HitNormal)
{
	local vector VelDir, OtherDir;
	local float speed;

	speed = VSize(Velocity);
	if ( speed > 10 )
	{
		VelDir = Velocity/speed;
		VelDir.Z = 0;
		OtherDir = Other.Location - Location;
		OtherDir.Z = 0;
		OtherDir = Normal(OtherDir);
		if ( (VelDir Dot OtherDir) > 0.8 )
		{
			Velocity.X = VelDir.Y;
			Velocity.Y = -1 * VelDir.X;
			Velocity *= FMax(speed, 280);
		}
	}

	if ( WorldInfo.TimeSeconds - 0.3 >= lastBumpTime )
	{

		myConnection.SendLine("BMP {Id " $ myConnection.GetUniqueId(Other) $
			"} {Location " $ Other.Location $"}");

		lastBumpTime = WorldInfo.TimeSeconds;
	}

	// Need to disable bumping ???
	//Disable('Bump');
	//TODO: Experiment with this?
	return false;
}

function ReportKilled(Controller Killer, Controller Killed, Pawn KilledPawn, class<DamageType> damageType)
{
	local string outstring;
	local class<UTDamageType> utDamageType;

	outstring = "KIL {Id " $ Killed;
	
	if (Killer != none)
		outstring $= "} {Killer " $ Killer;
	
	outstring $=
		"} {KilledPawn " $ KilledPawn $
		"}";
	if (CanSee(KilledPawn))
	{
		outstring = outstring $ " {DamageType " $ damageType $
			"} {CausedByWorld " $ damageType.default.bCausedByWorld $ "}";
		
		if (ClassIsChildOf(damageType, class'UTDamageType')) {
			utDamageType = class<UTDamageType>(damageType);
			outstring = outstring $ " {DeathString " $
				utDamageType.static.DeathMessage(Killer.PlayerReplicationInfo,
					Killed.PlayerReplicationInfo);
			if (utDamageType.default.DamageWeaponClass != none)
				outstring $=
				"} {WeaponName " $ myConnection.GetWeaponClassString(utDamageType.default.DamageWeaponClass);
			
			outstring $=
				"} {DirectDamage " $ utDamageType.default.bDirectDamage $
				"} {BulletHit " $ utDamageType.default.bBulletHit $
				"} {VehicleHit " $ utDamageType.default.bVehicleHit $ "}";
		}
	}

	myConnection.sendLine(outstring);
}

function ReportDied(Controller Killer, class<DamageType> damageType)
{
	local class<UTDamageType> utDamageType;
	local string outstring;

	outstring = "DIE ";
	
	if (Killer != none) {
		outstring $= "{Killer " $ Killer $ "} ";
		Killer.StopFiring();
	}

	outstring $= "{DamageType " $ damageType $                
        "} {CausedByWorld " $ damageType.default.bCausedByWorld $        
		"}";
	
	if (ClassIsChildOf(damageType, class'UTDamageType')) {
		utDamageType = class<UTDamageType>(damageType);
		outstring $= " {WeaponName " $ utDamageType.default.DamageWeaponClass;

		if (Killer != none)
			outstring $= "} {DeathString " $
				utDamageType.static.DeathMessage(Killer.PlayerReplicationInfo,
				self.PlayerReplicationInfo);
		
		outstring $=
			"} {BulletHit " $ utDamageType.default.bBulletHit $
			"} {VehicleHit " $ utDamageType.default.bVehicleHit $
			"} {DirectDamage " $ utDamageType.default.bDirectDamage $ "}";
	}

	myConnection.SendLine(outstring);
}

event Timer()
{
	if (Pawn != none) {
		if (bShouldDoubleJump) {			
			Pawn.SetPhysics(PHYS_Falling);
			Pawn.Velocity.Z = doubleJumpForce;
			bShouldDoubleJump = false;
			doubleJumpForce = 0;
		}
	}

	//super.Timer();
}

//Jumps a bot
/*function RemoteJump(bool bDouble)
{
	if (Pawn == none)
		return;

	bPendingDoubleJump = bDouble;
	bNotifyApex = bDouble;
	Pawn.DoJump(true);
}*/

//Jumps a bot
function RemoteJump(bool bDouble, float delay, float force)
{
	if (Pawn == none)
		return;

	if (force == 0) {
		if (bDouble)
			force = 2 * Pawn.JumpZ;
		else
			force = Pawn.JumpZ;
	}

	if (bDouble) {
		doubleJumpForce = force - Pawn.JumpZ;
		if (doubleJumpForce < 0) {
			doubleJumpForce = 0;
			bDouble = false;
		} else if (doubleJumpForce > Pawn.JumpZ) {
			doubleJumpForce = Pawn.JumpZ;
		}
	}

	if (force > Pawn.JumpZ)
		force = Pawn.JumpZ;

	if (Pawn.bJumpCapable && !Pawn.bIsCrouched && !Pawn.bWantsToCrouch && Pawn.Physics != PHYS_Falling)  //TODO: Check all bad physics
	{
		Pawn.SetPhysics(PHYS_Falling);
		Pawn.Velocity.Z = force;

		if ( bDouble && UTPawn(Pawn).bCanDoubleJump) {
			if (delay == 0)
				delay = 0.5;
			bShouldDoubleJump = true;
			SetTimer(delay, false);
		}
	}
}

//Intercept FireWeapon - is called from other code
function bool FireWeaponAt(Actor A) {
}

//Use from local code - stops firing and ticks weapon to let it stop
function RemoteStopFire()
{
	bFire = 0;
	bAltFire = 0;
	shouldFire = 0;
	Super.StopFiring();
}

function RemoteFireWeapon(bool bUseAltMode) {

	if ((Pawn == none) || (Pawn.Weapon == none))
		return;

	if (bUseAltMode) {
		shouldFire = 2;
		Pawn.Weapon.StartFire(1);
	} else {
		shouldFire = 1;
		Pawn.Weapon.StartFire(0);
	}
}

//Function which determines if our weapon should fire again or stop firing
function bool WeaponFireAgain(bool bFinishedFire) { 
	if (Pawn == none || Pawn.Weapon == none || !Pawn.Weapon.HasAnyAmmo())
		return false;

	//`log("In WeaponFireAgain");
	if (shouldFire == 1) {
		Pawn.Weapon.StartFire(0);
	} else if (shouldFire == 2) {
		Pawn.Weapon.StartFire(1);
	}

	return false;
}


function bool TryToDuck(vector duckDir, bool bReversed) {

}

//Pointless callback
function EnemyAcquired();

//All kinds of things can call this mostly special trigger points
function Trigger( actor Other, pawn EventInstigator )
{
	myConnection.SendLine("TRG {Actor " $ Other $
		"} {EventInstigator " $ myConnection.GetUniqueId(EventInstigator) $
		"}");
}


//Don't let engine pick nodes that must be impact jumped
function bool CanImpactJump()
{
	return false;
}

//Don't handle impact jumps or low gravity manuevers for bots
/** performs an impact jump; assumes the impact hammer is already equipped and ready to fire */
//function ImpactJump();


function SetFall()
{
	if (Pawn.bCanFly)
	{
		Pawn.SetPhysics(PHYS_Flying);
		return;
	}
	if (bDebug)
		`log("In RemoteBot.uc: SetFall() enganged.");
	/*
	if ( Pawn.bNoJumpAdjust )
	{
		Pawn.bNoJumpAdjust = false;
		return;
	}
	else
	{
		bPlannedJump = true;
		Pawn.Velocity = EAdjustJump(Pawn.Velocity.Z,Pawn.GroundSpeed);
		Pawn.Acceleration = vect(0,0,0);
	} */
}


//**********************************************************************************
//Base RemoteBot AI, that controls the Pawn (makes him move)

state Alive {
	function BeginState(Name PreviousStateName) {
		`log("Alive,BeginState");
		Super.StopFiring();
		ResetSkill();
		if (Pawn != None)
			Pawn.SetMovementPhysics();
		Reset();
	}

	function EndState(Name NextStateName) {
		`log("Alive,EndState");
	}

Begin:
	movingContinuous = false;
	sleep(0.5);
	if (VSize(Pawn.Velocity) > 10) {//HACK - if the bot can't reach the destination, he would continue running - bad behavior
		StopMovement();
	}
	goto 'Begin';
DoStop:
	movingContinuous = false;
	StopMovement();
	goto 'Begin';
Move:
	//`log("Alive,Move: MoveTo( " $ myDestination $ ")");
	movingContinuous = false;

	MoveTo(myDestination, Focus, EPSILON_DIST, Pawn.bIsWalking);
	SetDestinationPosition(myDestination);
	if (bPendingDestination)
	{
		//`log("Alive,Move: MoveTo( " $ pendingDestination $ ")");
		MoveTo( pendingDestination, Focus, EPSILON_DIST, Pawn.bIsWalking);
		SetDestinationPosition(pendingDestination);
		bPendingDestination = false;
	}
	if (Focus == none) {
		//There is an issue when the bot finish its movement, sometimes he goes a bit
		//over the target point, this caused turning back, because moveTo functions sets focalpoint
		//after it ends to its target point, to prevent this, we will set our own FocalPoint counted in advance
		SetFocalPoint(myFocalPoint);
	}
	goto 'Begin';	
MoveToActor:
	movingContinuous = false;
	if (Focus == none) {
		MoveToward(MoveTarget, , EPSILON_DIST, , Pawn.bIsWalking);
		//There is an issue when the bot finish its movement, sometimes he goes a bit
		//over the target point, this caused turning back, because moveTo functions sets focalpoint
		//after it ends to its target point, to prevent this, we will set our own FocalPoint counted in advance
		SetFocalPoint(myFocalPoint);
	} else {
		MoveToward(MoveTarget, Focus, EPSILON_DIST, true, Pawn.bIsWalking);
	}
	goto 'Begin';
MoveContinuous:
	//to prevent that our focal point moves too much above or below us
	//remember we want to move
	myFocalPoint.z = Pawn.Location.z;
	cmoveDirection = vector(rotator(myFocalPoint - Pawn.Location));
MoveContRepeat:
	movingContinuous = true;
	MoveTo(Pawn.Location + 500 * cmoveDirection);

	myFocalPoint = Pawn.Location + 500 * cmoveDirection;
	SetFocalPoint(myFocalPoint);
	goto 'MoveContRepeat';
}

//Automatic intial state - we will just wait and do nothing.
auto state Waiting {
Begin:
	Sleep(1);
	goto('Begin');
}

// This state was called somehow on our bot. That is highly undersirable.
// Overriding
state Roaming
{
	function BeginState(Name PreviousStateName)
	{
		`log("In Roaming STATE! Shouldnt be!");
		gotostate('Alive','Begin');
	}
Begin:
	gotostate('Alive','Begin');
}

state TakeHit
{
	//ignores seeplayer, hearnoise, bump, hitwall;

	function Timer();

Begin:
	//error("!!!TakeHit");
}

state GameEnded
{
ignores SeePlayer, EnemyNotVisible, HearNoise, TakeDamage, Bump, Trigger, HitWall, Falling, ReceiveWarning;

	function SpecialFire()
	{
	}
	function bool TryToDuck(vector duckDir, bool bReversed)
	{
		return false;
	}
	function SetFall()
	{
	}
	function LongFall()
	{
	}
	function Killed(pawn Killer, pawn Other, name damageType)
	{
	}
	function ClientDying(class<DamageType> DamageType, vector HitLocation)
	{
	}

	function BeginState(Name PreviousStateName)
	{
		`log("In GameEnded! BeginState");
		//Pawn.SimAnim.AnimRate = 0.0;
		bFire = 0;
		//bAltFire = 0;

		SetCollision(false,false,false);
		SetPhysics(PHYS_None);
		Velocity = vect(0,0,0);
		myConnection.SendLine("FIN");
	}
}

state Dead
{
ignores SeePlayer, HearNoise, KilledBy;

function BeginState(Name PreviousStateName)
{
	`log("State: Dead, fc BeginState()");

	//We can be sent to this state sometimes, when it is not desired ( by game mechanics)
	//Escaping here
	if (Pawn != none)
		gotostate('Alive','Begin');

	//This is taken from working AI in UT2004, probably needed to assure bot
	//will behave normally after restart - 02/03/07 Michal Bida
	movingContinuous = false;
	Enemy = None;
	Focus = None;
	RouteGoal = None;
	MoveTarget = None;
	shouldFire = 0;
	bFire = 0;
	bAltFire = 0;
	if (myConnection != none)
		myConnection.RayManager.StopRays();
	//Pawn = Spawn(class'UTGame.UTPawn',self,,,);

	Super.StopFiring(); //Not needed anymore to call super, but for sure.
/*	FormerVehicle = None;
	bFrustrated = false;
	BlockedPath = None;
	bInitLifeMessage = false;
	bPlannedJump = false;
	bInDodgeMove = false;
	bReachedGatherPoint = false;
	bFinalStretch = false;
	bWasNearObjective = false;
	bPreparingMove = false;
	bEnemyEngaged = false;
	bPursuingFlag = false;
*/


}
/*
function EndState()
{
	myConnection.sendLine("SPW2");
}*/
Begin:
	`log("In Dead:Begin:");
	sleep(1.0);
	//AutoSpawn policy
	if (Pawn != None)
		gotostate('Alive','Begin');
	//RemoteRestartPlayer(); //HACK
	if (bAutoSpawn)// && !WorldInfo.Game.bWaitingToStartMatch)
	{
		RespawnPlayer();
	//	if (Pawn == none) //bug during restart?
	//		goto('Begin');
		//ServerRestartPlayer(); //Dunno if this is needed here - Could it cause troubles?
	}
/*	if (!WorldInfo.Game.bWaitingToStartMatch)
	{
		RemoteRestartPlayer();
	}
	if (Pawn != None)
		gotostate('StartUp','Begin');*/
	goto('Begin');

}



//-------------RemoteBot Specific Functions--------------------


// True if location loc is in bot's field of view. Does not take into account occlusion by geometry!
// Possible optimization: Precompute cos(obsController.FovAngle / 2) for InFOV - careful if it can change.
function bool InFOV(vector loc) {
	local vector view;   // vector pointing in the direction obsController is looking.
	local vector target; // vector from obsController's position to the target location.
	local Vector PawnLocation;

	if (Pawn == none) {
		return false;
	}

	view = vector(Pawn.GetViewRotation());
	PawnLocation = Pawn.Location;
	PawnLocation.Z += Pawn.EyeHeight;
	target = loc - PawnLocation;

	return  ACOS(Normal(view) dot Normal(target)) < ACOS(Pawn.PeripheralVision); // Angle between view and target is less than FOV	
	// 57.2957795 = 180/pi = 1 radian in degrees  --  convert from radians to degrees
	//return Acos(Normal(view) dot Normal(target)) < FovAngleRadH ; 
	//return cos(acos( Normal(view) dot Normal(target) ) * 57.2957795) < Pawn.PeripheralVision;
	//return acos( Normal(view) dot Normal(target) ) * 57.2957795 < GetFOVAngle();
}

function SetHealth(int targetHealth)
{
	if (Pawn == none)
		return;
	if ((targetHealth > 0) && (targetHealth <= 200))
		Pawn.Health = targetHealth;
}

/**
 * Sends SLF message to the client.
 * @note Messages: SLF
 *       Attributes: Id, Vehicle, Rotation, Location,
 *              Velocity, Name, Team, Health, Weapon,
 *              PrimaryAmmo, Shooting, AltFiring, Armor,
 *              HelmetArmor, VestArmor, ThighpadArmor,
 *              ShieldBeltArmor, Crouched, Walking,
 *              FloorLocation, FloorNormal
 * @todo Weapon should return invetory id; test implementation 
 * @todo SecondaryAmmo, Adrenaline, SmallArmor, Combo, UDamageTime
 *          not used any more.
 * @todo Action needs to be implemented in bot.
 * @todo Armor and powerup exports need to be implemented
 *          to GB protocol.
 * @todo PowerUps NYI 
 */ 
function checkSelf() {
    local string outstring, myId;
    local rotator PawnRotation;
    local bool bIsShooting, AltFiring;
	local int TeamIndex,PrimaryAmmo;
    local vector FloorLocation, FloorNormal;

	TeamIndex = (WorldInfo.Game.IsA('GBCopyUTTeamGame')) ? int(GetTeamNum()) : 255;

    if( Pawn != none ) {
        if( Pawn.Weapon != None)
            bIsShooting = Pawn.Weapon.IsFiring();
			if(Pawn.Weapon != none){
				if(Pawn.Weapon.IsFiring())
					AltFiring = (Pawn.Weapon.CurrentFireMode == 0) ? false : true; 

			if(Pawn.Weapon.IsA('UTWeapon'))
				PrimaryAmmo = UTWeapon(Pawn.Weapon).AmmoCount;
		}

        PawnRotation = Pawn.Rotation;
        //PawnRotation.Pitch = int(Pawn.ViewPitch) * 65556/255;

        FloorNormal = vect(0,0,0);
        Trace(FloorLocation,FloorNormal,Pawn.Location + vect(0,0,-1000),Pawn.Location, false);

		myId = myConnection.GetUniqueId(self); 
        outstring = (
                "SLF {Id SELF_" $ myId $
			    "} {BotId " $ myId $
                "} {Vehicle False" $
                "} {Rotation " $ PawnRotation $
                "} {Location " $ Pawn.Location $
                "} {Velocity " $ Pawn.Velocity $
                "} {Name " $ PlayerReplicationInfo.PlayerName $
                "} {Team " $ TeamIndex $
                "} {Health " $ Pawn.Health $
				//We need to export id of the weapon bot is holding not its type
                
                "} {Weapon " $ myConnection.GetUniqueId(Pawn.Weapon) $
                "} {PrimaryAmmo "$ PrimaryAmmo $
                "} {Shooting " $ bIsShooting $
				"} {AltFiring " $ AltFiring $
                "} {Armor " $ UTPawn(Pawn).GetShieldStrength() $
                //"} {HelmetArmor " $ int(UTPawn(Pawn).HelmetArmor) $
				//"} {VestArmor " $ int(UTPawn(Pawn).VestArmor) $
				//"} {ThighpadArmor " $ int(UTPawn(Pawn).ThighpadArmor) $
				//"} {ShieldBeltArmor " $ int(UTPawn(Pawn).ShieldBeltArmor) $
                "} {Crouched " $ Pawn.bIsCrouched $
                "} {Walking " $ Pawn.bIsWalking $
                "} {FloorLocation " $ FloorLocation $
                "} {FloorNormal " $ FloorNormal $
                "}"
        );
        
        myConnection.sendLine(outstring);
    } else
        `Log("Pawn is none in check self!");
}

function checkPlayers() {
	local Controller C;
	local string outstring, WeaponClass, PlayerName;
	local rotator PawnRotation;
	local int Firing, TeamIndex;
	//!!! view rotation sometimes falls out of synch with rotation? wtf?

	foreach WorldInfo.AllControllers(class'Controller', C){
		if( C != self && C.Pawn != none && CanSee(C.Pawn)) {//to match peripheral vision //InFOV(C.Pawn.Location) && FastTrace(C.Pawn.Location,Pawn.Location)		
			TeamIndex = (C.PlayerReplicationInfo.Team != none) ? C.PlayerReplicationInfo.Team.TeamIndex : 255;

			WeaponClass = (C.Pawn.Weapon == none) ? "None" :
				myConnection.GetWeaponClassString(C.Pawn.Weapon);
			//Firing: 1 for primary, 2 for secondary, 0 not firing 
			Firing = 0;
			//IsFiring returns 0 for primary 1 for secondary so we have to 
			//increase the return value by one to match our definition
			if(C.Pawn.Weapon != none && C.Pawn.Weapon.IsFiring())
				Firing = C.Pawn.Weapon.CurrentFireMode + 1; 

			PawnRotation = C.Pawn.Rotation;
			//PawnRotation.Pitch = int(C.Pawn.ViewPitch) * 65535/255;
			PlayerName = (C.IsA('PlayerController')) ? "Player" : C.PlayerReplicationInfo.PlayerName;

			outstring = "PLR {Id " $ myConnection.GetUniqueId(C) $
				"} {Rotation " $ PawnRotation $
				"} {Location " $ C.Pawn.Location $
				"} {Velocity " $ C.Pawn.Velocity $
				"} {Name " $ PlayerName $
				"} {Team " $ TeamIndex $
				//"} {Reachable " $ actorReachable(C.Pawn) $  //This can consume quite a lot resources
				"} {Crouched " $ C.Pawn.bIsCrouched $
				"} {Weapon " $ WeaponClass $
				"} {Firing " $ Firing $
				"}";
			myConnection.sendLine(outstring);

        }//end if
	}//end for P=Level.ControllerList
}

function checkItems() {
	local PickupFactory Pickup;
	local DroppedPickup DP;
	local string outstring;

	foreach DynamicActors(class'PickupFactory',Pickup){
		//do not export replaced PickupFactories with INV
		if((VSize(Pawn.Location - Pickup.Location) <= MAX_VIEW_DISTANCE) && (Pickup.GetStateName() == 'Pickup') 
			&& !Pickup.bHidden && CanSeeByPoints( Pawn.Location, Pickup.Location, Pawn.Rotation) && (Pickup.ReplacementFactory == none))	{
			
			outstring = "INV {Id " $ myConnection.GetUniquePickupFactoryInventoryId(Pickup) $
				"} {NavPointId " $ myConnection.GetUniqueId(Pickup) $				
		        "} {Location " $ Pickup.Location $
				"} {Amount " $ myConnection.GetPickupFactoryItemAmount(Pickup) $ //GetPickupFactoryItemAmount
				"} {Type " $ myConnection.GetPickupFactoryType(Pickup) $
				"} {Visible True}";
			myConnection.SendLine(outstring);
		}
	}

	//export all visible DroppedPickups
	foreach DynamicActors(class'DroppedPickup', DP){
		if ((VSize(Pawn.Location - DP.Location) <= MAX_VIEW_DISTANCE) && InFOV(DP.Location) && FastTrace(DP.Location,Pawn.Location)){
			outstring = "INV {Id " $ myConnection.GetUniqueDroppedInventoryId(DP) $
		        "} {Location " $ DP.Location $
				"} {Amount " $  myConnection.GetDroppedItemAmount(DP) $
				"} {Type " $ DP.Inventory.ItemName $
				"} {Dropped True" $
				"} {Visible True}";
			myConnection.SendLine(outstring);
		}//end if
	}//end froeach
}

function checkMovers() {
	local InterpActor M;
	local string outstring;
	local int l;
	local array<InterpActor> MoverArray;

	//export visible movers
	MoverArray = GBGameHandler.MoverArray;
	for (l = 0; l < MoverArray.Length; l++) {
		M = MoverArray[l];
		if(M.MyMarker != none && (VSize(Pawn.Location - M.Location) <= MAX_VIEW_DISTANCE) && ( 
			( ( Abs(Pawn.Location.x - M.Location.x) < 300 ) && ( Abs(Pawn.Location.y - M.Location.y) < 300 ) && ( Abs(Pawn.Location.Z - M.Location.Z) < 300 ))
			|| CanSeeByPoints(Pawn.Location, M.Location, Pawn.Rotation) ) 
			) {				
			outstring = "MOV {Id " $ myConnection.getUniqueId(M) 
				$ "} {Location " $ M.Location 
				$ "} {IsMoving " $ M.bIsMoving 
				$ "} {Velocity " $ M.Velocity 
				$ "} {Visible True}";				

			myConnection.SendLine(outstring);
		}//end if
    }//end foreach
}

function checkProjectiles() {
	local Projectile Proj;
	local string outstring;

	//ImpactTime - used TimeToLocation
	//Direction - used Rotation
	//Export all visible projectiles
	foreach DynamicActors(class'Projectile', Proj){
		if((VSize(Pawn.Location - Proj.Location) <= MAX_VIEW_DISTANCE) && InFOV(Proj.Location) && FastTrace(Proj.Location, Pawn.Location)){
			outstring = "PRJ {Id " $ myConnection.GetUniqueId(Proj) $
				"} {ImpactTime " $ Proj.GetTimeToLocation(Pawn.Location) $
				"} {Direction " $ Proj.Rotation $
				"} {Speed " $ Proj.Speed $
				"} {Velocity " $ Proj.Velocity $
				"} {Location " $ Proj.Location $
				"} {Origin " $ Proj.Instigator.Location $
				"} {DamageRadius " $ Proj.DamageRadius $
				"} {Type " $ Proj.Class $
				"}";
			myConnection.SendLine(outstring);
		}
	}
}

function checkNavPoints() {
	local string outstring;
	local int i;
	local array<PickupFactory> InvSpotArray;
	local array<LiftCenter> LiftCenterArray;
	//local array<xDomPoint> DomPointArray;
	//local xDomPoint D;
	local PickupFactory IS;
	local LiftCenter DR;
	
	InvSpotArray = GBGameHandler.InvSpotArray;
	LiftCenterArray = GBGameHandler.LiftCenterArray;
	
	for (i = 0; i < InvSpotArray.Length; i++) {
		IS = InvSpotArray[i];			
		if ( ( VSize(Pawn.Location - IS.Location) <= MAX_VIEW_DISTANCE ) && ( (VSize(Pawn.Location - IS.Location) <= 120)
			||  CanSeeByPoints( Pawn.Location, IS.Location, Pawn.Rotation) ) )  {
			outstring = "NAV {Id " $ myConnection.GetUniqueId(IS) $
				//"} {Location " $ IS.Location $
				"} {Visible True" $
				//"} {Reachable " $ myActorReachable(IS) $
				"} {ItemSpawned " $ IS.IsInState('Pickup') $ "}";
			myConnection.SendLine(outstring);
		}
	}

	for (i = 0; i < LiftCenterArray.Length; i++) {
		DR = LiftCenterArray[i];
		if ( (VSize(Pawn.Location - DR.Location) <= MAX_VIEW_DISTANCE) && ((VSize(Pawn.Location - DR.Location) <= 500) || 
			CanSeeByPoints( Pawn.Location, DR.Location, Pawn.Rotation)) ) {
			outstring = "NAV {Id " $ myConnection.GetUniqueId(DR) $
				"} {Location " $ DR.Location $
			"} {Visible True" $
				//"} {Reachable " $ myActorReachable(DR) $
			    "}";
			myConnection.SendLine(outstring);
		}
	}
}

/**
 * Provides Info about bot's vision.
 * 
 * @note Messages: PLR, MOV, INV, NAV, PRJ
 *       Attributes: Visible, Reachable*
 * @todo Reachable may be omited.
 * @todo gametype specific items must be added
 * @todo clean up the local variables
 * @note Replacement facories are not exported via NAV messages.
 * @note Replaced factories are not exported by INV messages.
 * @note PRJ.Direction - used Rotation
 * @note PRJ.ImpactTime - used GetTimeToLocation
 * @note VEH does not send armor
 */ 
function checkVision()
{
	if( Pawn == none ) {
		`log("In CheckVision() - Pawn is none ");
		return;
	}
	if (GBGameHandler == none)
		GBGameHandler = GBGameInterface(WorldInfo.Game).GetGameHandler();

	checkPlayers();
	checkItems();
	checkMovers();
	checkProjectiles();
	checkNavPoints();

	//checkSounds();
	//vehicles
	//Export all visible vehicles
	/*
	foreach DynamicActors(class'Vehicle', veh){
		if(InFOV(veh.Location) && FastTrace(veh.Location, Pawn.Location)){
			outstring = "VEH {Id " $ veh $
				"} {Rotation " $ veh.Rotation $
				"} {Location " $ veh.Location $
				"} {Velocity " $ veh.Velocity $
				"} {Visible True" $
				"} {Team " $ veh.GetTeamNum() $
				"} {Health " $ veh.Health $
				//"} {Armor " $ veh. $
				"} {Driver " $ class'GBUtil'.static.GetUniqueId(veh.Driver.Controller) $
				"} {Type " $ veh.Class $
				"}";
			if (veh.IsA('UTVehicle'))
					outstring @= "{TeamLocked " $ class'GBUtil'.static.GetBoolString(UTVehicle(veh).bTeamLocked) $ "}";
			myConnection.SendLine(outstring);
		}
	}
	foreach VisibleActors(class'UTVehicle', V) {
		// no armor?
		//"} {Armor " $ V.GetArmor() $
		driver = "";
		if (V.Controller != none)
			driver = "} {Driver " $ V.Controller.PlayerReplicationInfo.PlayerName;
		outstring = "VEH {Id " $ V $
			"} {Location " $ V.Location $
			"} {Rotation " $ V.Rotation $
			"} {Velocity " $ V.Velocity $
			"} {Visible true" $
			"} {Team " $ V.GetTeamNum() $
			"} {Health " $ V.Health $
			driver $
			"} {TeamLocked " $ V.bTeamLocked $
			"} {Type " $ V.Class $ "}";
		myConnection.SendLine(outstring);
	}

*/
}

simulated event Destroyed()
{
	//Destroying bot Pawn
	DestroyPawn(); //kill him properly first
	if (Pawn != None) {
		Pawn.Destroy();//destroy him
		Pawn = None;
    }

	//Destroying actor for aiming our shooting on location
    if (myTarget != none) {
		myTarget.Destroy();
	}

	if (myConnection != none)
		myConnection.SendLine("FIN");
	else
		`log("Problem with sending FIN, myConnection = none");

    Super.Destroyed();
}

state MoveToGoal
{
	function BeginState(Name PreviousStateName);

}

function NotifyChangedWeapon(Weapon OldWeapon, Weapon NewWeapon)
{
	local string outstring;

	super.NotifyChangedWeapon(OldWeapon, NewWeapon);

	outstring = "CWP {Id " $ newWeapon $ "}";
	if (NewWeapon.isA('UDKWeapon')) {
		outstring = outstring
			$ " {PrimaryAmmo " $ UDKWeapon(newWeapon).AmmoCount
			$ "} {SecondaryAmmo " $ UDKWeapon(newWeapon).AmmoCount
			$ "} {Type " $ myConnection.GetWeaponClassString(newWeapon.Class)
			$ "}";
	} else {
		outstring = outstring $ " {PrimaryAmmo 1} {SecondaryAmmo 1} {Type " $
			myConnection.GetWeaponClassString(newWeapon.Class) $ "}";
	}
	myConnection.SendLine(outstring);
}

function ReportUsedItemPickupFactory(UTItemPickupFactory pickupFactory)
{
	local string pickupType;
	local string outstring;

	if (pickupFactory == none)
		return;

	pickupType = myConnection.GetPickupFactoryType(pickupFactory);

	outstring = "IPK {Id " $ myConnection.GetUniquePickupFactoryInventoryId(pickupFactory) $
		// we know that this cant be UTWeaponPickupFactory, so no worries
		"} {InventoryId " $ myConnection.GetInventoryClassIdFromFactoryClass(pickupFactory.class) $
		"} {Location " $ pickupFactory.Location $
		"} {Amount " $ myConnection.GetPickupFactoryItemAmount(pickupFactory) $
		"} {Dropped false" $
		"} {Type " $ pickupType $
		"}";

	myConnection.SendLine(outstring);
}

function SendFlagInfo()
{
	local string outstring;
	local UTCTFFlag flag;
	local bool visible;
	local int i;

	if (WorldInfo.Game.IsA('UTCTFGame'))
	{
		for (i = 0; i < 2; ++i) {
			flag = UTCTFGame(WorldInfo.Game).Flags[i];

			visible = InFOV(flag.Location);
			outstring = "FLG {Id " $ flag;

			if (visible)
				outstring $= "} {Location " $ flag.Location;

			outstring $=
				"} {Holder " $ flag.Holder $
				"} {Team " $ flag.Team.TeamIndex $
				"} {Reachable " $ ActorReachable(flag) $
				"} {Visible " $ visible $
				"} {State " $ flag.GetStateName() $ "}";

			myConnection.SendLine(outstring);
		}
	}
}

function Initialize(float InSkill, const out CharacterInfo BotInfo)
{
	local UTPlayerReplicationInfo PRI;
	local class<UTFamilyInfo> FI;

	super.Initialize(InSkill, BotInfo);


	// UTPawn.SetCharacterMeshInfo would be nice to analyze
	if (DesiredSkin != "")
	{
		// copy visual properties
 		PRI = UTPlayerReplicationInfo(PlayerReplicationInfo);
 		if (PRI != None)
 		{
			//Get the chosen character class for this character

			//FI = class'UTCharInfo'.default.Characters.Find('CharName', DesiredSkin);

			//if (FI == none)
			FI = class'UTCharInfo'.static.FindFamilyInfo(DesiredSkin);
			
			if (FI == none)
				FI = class<UTFamilyInfo>(DynamicLoadObject(DesiredSkin, class'class'));
		}

		if (FI == None)
			return;

		PRI.CharClassInfo = FI;
	}
}

//-----------------

defaultproperties
{	
	PawnClass=Class'GameBotsUDK.GBPawn'
}
