/*
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.

*/

//=============================================================================
// BotConnection.
// Based connection class
//=============================================================================
class BotConnection extends GBClientClass
	config(GameBotsUDK);

//------------Variables---------------------------

// delay between visionUpdates
var config float visionTime;

//if true we will ignore maximum player limit on the server - note this can lead
//to pawn spawning malfunctions
var config bool bIgnoreMaxPlayers;

// on / off all synchronous messages
var config bool bSynchronousMessagesOff;

// the one server and the actual bot we're in control of
var BotServer Parent;
var RemoteBot theBot;

//switches for exporting information after READY command
var config bool bExportGameInfo;
var config bool bExportMutators;
var config bool bExportITC;
var config bool bExportNavPoints;
var config bool bExportMovers;
var config bool bExportInventory;
var config bool bExportPlayers;
var config bool bExportVehicles;

var FocusActor FocusActor;

var Vehicle CarVehicle;
var() class<Vehicle> SpawnVec;

/** Set to true in GBGameHandler.SpawnPawn function for the first time 
 *  There were problems with sending respawn command right after init command.
 *  */
var bool bBotInitialized;

var RayManager RayManager;

replication
{
	if (ROLE == ROLE_Authority)
		RayManager;

}

//------------Events---------------------------

// triggered when a socket connection with the server is established
event Accepted()
{
	if(bDebug)
		`log("Accepted BotConnection" @ self);

	//log("numpl:" $ WorldInfo.Game.NumPlayers $ "max:"$WorldInfo.Game.MaxPlayers $ "con:"$Parent.ConnectionCount);
	if (!bIgnoreMaxPlayers &&
		(( WorldInfo.Game.NumPlayers >= WorldInfo.Game.MaxPlayers) ||
		(Parent.ConnectionCount >= WorldInfo.Game.MaxPlayers)) )
	{
		SendLine("HELLO_BOT {ServerFull True}");
		Destroy();
	}
	else
		SendLine("HELLO_BOT");

	if (GBGameInterface(WorldInfo.Game).GetGameHandler().bPasswordProtected)
		gotoState('checkingPassword','Waiting');
	else
		gotoState('waiting','Waiting');
}

event Closed()
{
	//log("In BotConnection, event Closed()");
	//Destroyed();
	Destroy();
}

simulated event Destroyed()
{
	//log("In BotConnection, event Destroyed()");
	if (theBot != None)
	{
		theBot.Destroy();
	}

	if (FocusActor != None)
	{
		FocusActor.Destroy();
	}

	//Destroying autotrace visualizers
	//RayManager.RemoveCustomRay("All");
	RayManager.Destroyed();

}


//------------Functions---------------------------

function PostBeginPlay()
{
	Parent = BotServer(Owner);

	if(bDebug)
		`log("Spawned BotConnection");
	SaveConfig();
}

simulated function Tick(float dt)
{
	super.Tick(dt);
	if (RayManager != none)
		RayManager.Tick(dt);
}

//triggered when weve gotten READY message from client, or after succesfull password check
//we will send game nfo, all navpoints and all items in a map
function ExportStatus()
{
	SendLine("SHS"); // StartHandShake message
	//Methods defined in super class
	if (bExportGameInfo)
		SendGameInfo();  //NFO message
	if (bExportMutators)
		ExportMutators(); //SMUT, MUT, EMUT messages
	if (bExportITC)
		ExportItemClasses(); //SITC, ITC, EITC messages
	if (bExportNavPoints)
		ExportNavPoints(); //SNAV, NAV, ENAV messages
	if (bExportMovers)
		ExportMovers();
	if (bExportInventory)
		ExportInventory(); //SINV, IINV, EINV messages
	if (bExportPlayers) {
		SendLine("SPLR");
		ExportPlayers(true); //true for limited info, PLR messages
		SendLine("EPLR");
	}
	if (bExportVehicles)
		ExportVehicles();

	SendLine("EHS"); // HandShakeEnd message
}

//Init recieved from client
function InitBot()
{
	local string clientName, className, temp, DesiredSkin, outstring;
	local int teamNum;
	local vector StartLocation;
	local rotator StartRotation;
	local float DesiredSkill;
	local bool ShouldLeadTarget;
	local int armorMax;

	`log("InitBot()");

	clientName = GetArgVal("Name");

	DesiredSkin = GetArgVal("Skin");

	className = GetArgVal("ClassName");

	temp = GetArgVal("Team");
	if( temp != "" )
		teamNum = int(temp);
	else
		teamNum = 255;

	temp = GetArgVal("DesiredSkill");
	if (temp != "")
		DesiredSkill = float(temp);
	else
		DesiredSkill = -1; //that means deufault value will be used
	
	temp = GetArgVal("ShouldLeadTarget");
	if (temp != "")
		ShouldLeadTarget = bool(temp);
	else
		ShouldLeadTarget = false;

	// add the bot into the game
	theBot = GBGameInterface(WorldInfo.Game).GetGameHandler().AddRemoteBot(
		self,
		clientName,
		teamNum,
		className,
		DesiredSkin,
		DesiredSkill,
		ShouldLeadTarget
	);

	//Here the spawning of the pawn is handled - after we've got controler created
	if(theBot != None)
	{
		if (GetArgVal("ManualSpawn")!="")
			theBot.bAutoSpawn = !bool(GetArgVal("ManualSpawn"));

		if (GetArgVal("AutoPickupOff") != "")
			theBot.bDisableAutoPickup = bool(GetArgVal("AutoPickupOff"));

		theBot.jmx = GetArgVal("Jmx");

		if (GetArgVal("AutoTrace")!="")
			theBot.bAutoTrace = bool(GetArgVal("AutoTrace"));

		RayManager = Spawn(class'RayManager',self);

		FocusActor = Spawn(class'FocusActor',self,,,);
		FocusActor.SetHidden(true);

		// for shooting at the location
		theBot.myTarget = Spawn(class'FocusActor',theBot,,,);
		theBot.myTarget.SetHidden(true);

		SendNotifyConf();
		//FocusActor.Test();
        if (bDebug)
			`log("Succesfully Added bot "$theBot);

		armorMax =
			class'UTArmorPickup_BaseArmor'.default.ShieldAmount +
			class'UTArmorPickup_ShieldBelt'.default.ShieldAmount +
			class'UTArmorPickup_Thighpad'.default.ShieldAmount;

        outstring = "INITED {BotId " $ theBot $
       		"} {HealthStart " $ theBot.PawnClass.Default.Health $
       		"} {HealthFull " $ theBot.PawnClass.Default.HealthMax $
       		"} {HealthMax " $ theBot.PawnClass.Default.SuperHealthMax $
       		"} {AdrenalineStart " $ 0 $
       		"} {AdrenalineMax " $ 0 $
       		"} {ShieldStrengthStart " $ 0 $
       		"} {ShieldStrengthMax " $ armorMax $
       		"} {MaxMultiJump " $ theBot.PawnClass.Default.MaxMultiJump $
       		"} {DamageScaling " $ theBot.PawnClass.Default.DamageScaling $
       		"} {GroundSpeed " $ theBot.PawnClass.Default.GroundSpeed $
       		"} {WaterSpeed " $ theBot.PawnClass.Default.WaterSpeed $
       		"} {AirSpeed " $ theBot.PawnClass.Default.AirSpeed $
       		"} {LadderSpeed " $ theBot.PawnClass.Default.LadderSpeed $
       		"} {AccelRate " $ theBot.PawnClass.Default.AccelRate $
       		"} {JumpZ " $ theBot.PawnClass.Default.JumpZ $
       		"} {MultiJumpBoost " $ theBot.PawnClass.Default.MultiJumpBoost $
       		"} {MaxFallSpeed " $ theBot.PawnClass.Default.MaxFallSpeed $
       		"} {DodgeSpeedFactor " $ theBot.PawnClass.Default.DodgeSpeed $
       		"} {DodgeSpeedZ " $ theBot.PawnClass.Default.DodgeSpeedZ $
       		"} {AirControl " $ theBot.PawnClass.Default.AirControl $
       		"}";

       	SendLine(outstring);

		if ( GetArgVal("Location")!="" && ( GetArgVal("Rotation")!="" )) {
        	ParseVector(StartLocation,"Location");
			ParseRot(StartRotation,"Rotation");
			GBGameInterface(WorldInfo.Game).GetGameHandler().SpawnPawn(theBot,StartLocation,StartRotation);
			`log("InitBot().spawningPawn: L:" $ startLocation $ ";R:" $ StartRotation);
		} else if (GetArgVal("Location")!="" ) {
			ParseVector(StartLocation,"Location");
			GBGameInterface(WorldInfo.Game).GetGameHandler().SpawnPawn(theBot,StartLocation,);			
			`log("InitBot().spawningPawn: L:" $ startLocation);
		} else {
			GBGameInterface(WorldInfo.Game).GetGameHandler().SpawnPawn(theBot, ,);
			`log("InitBot().spawningPawn:");
		}

		if (theBot.Pawn != none) {
			theBot.GotoState('Alive', 'Begin');
		}
		else
			theBot.GotoState('Dead', 'Begin');

		gotoState('monitoring','Running');
	}
	else
	{
		if (bDebug)
			`log("In InitReceived() - Error adding bot");
	}

}

function SendNotifyConf()
{
	local string outstring;
	local string confchId;

	confchId = theBot $ "_CONFCH";

	outstring="CONFCH {Id " $ confchId $
		"} {BotId " $ theBot $
		"} {ManualSpawn " $ !theBot.bAutoSpawn $
		"} {AutoTrace " $ theBot.bAutoTrace $
		"} {Invulnerable " $ theBot.bGodMode $
		"} {Name " $ theBot.PlayerReplicationInfo.PlayerName $
		"} {SpeedMultiplier " $ theBot.SpeedMultiplier $
		"} {RotationRate " $ theBot.RotationRate $
		"} {VisionTime " $ theBot.myConnection.VisionTime $
		"} {ShowDebug " $ theBot.bDebug $
		"} {ShowFocalPoint " $ theBot.bShowFocalPoint $
		"} {DrawTraceLines " $ theBot.bDrawTraceLines $
		"} {SynchronousOff " $ theBot.myConnection.bSynchronousMessagesOff $
		"} {AutoPickupOff " $ theBot.bDisableAutoPickup $
		"}";

	//notify that variables changed
	SendLine(outstring);

	//notify all control servers that variables changed
	GlobalSendLine(outstring,true,false);
}

function SendNotifyPause ( bool bGamePaused )
{
	local Controller C;
	local GBClientClass G;

	foreach WorldInfo.AllControllers(class'Controller', C)
	{
		if( C.IsA('RemoteBot') )
		{
			if (bGamePaused)
				RemoteBot(C).myConnection.SendLine("PAUSED");
			else
				RemoteBot(C).myConnection.SendLine("RESUMED");
		}
	}

    if (GBGameInterface(WorldInfo.Game).GetGameHandler().theControlServer != None)
	{
		for (G = GBGameInterface(WorldInfo.Game).GetGameHandler().theControlServer.ChildList; G != None; G = G.Next )
		{
			if (bGamePaused)
				G.SendLine("PAUSED");
			else
				G.SendLine("RESUMED");
		}
	}
}

function SetGBcommand(string text)
{
	if (theBot!= none) 
	{
		//log("comm set: "$ cmdType);
		if (text!="")
		{
			GBReplicationInfo(theBot.PlayerReplicationInfo).SetMyLastGBCommand(WorldInfo.TimeSeconds $ ": " $ text); 
		}
	}
}

//Main function for processing commands
function ProcessAction(string cmdType)
{
	if(bDebug)
		`log("commandType:"@cmdType);

	if (bIterative)
		WorldInfo.Pauser = None;

	//sets the last GB command
	SetGBcommand(lastGBCommand);

	if (IsInState('checkingPassword'))
	{
		switch(cmdType)
		{
			case "PASSWORD":
				ReceivedPassword();
			break;
			case "READY":
				ReceivedReady();
			break;
		}
	}
	else
	{
		ProcessRegularAction(cmdType);
	}//end if
}

function ProcessRegularAction(string cmdType)
{
		switch(cmdType)
		{
			case "ADDINV":
				ReceivedAddInv();
			break;
			case "ADDRAY":
				ReceivedAddRay();
			break;
			case "CHANGETEAM":
				ReceivedChangeTeam();
			break;
			case "CHANGEWEAPON":
				ReceivedChangeWeapon();
			break;
			case "CHATTR":
				ReceivedChAttr();
			break;
			case "CHECKREACH":
				ReceivedCheckReach();
			break;
			case "CMOVE":
				ReceivedCMove();
			break;
			case "CONF":
				ReceivedConf();
			break;
			case "DIALOG":
				ReceivedDialog();
			break;
			case "DISCONNECT":
				ReceivedDisconnect();
			break;
			case "DODGE":
				ReceivedDodge();
			break;
			case "DRIVETO":
				ReceivedDriveTo();
			break;
			case "ENTER":
				ReceivedEnter();
			break;
			case "EXIT":
				ReceivedExit();
			break;
			case "FTRACE":
				ReceivedFTrace();
			break;
			case "GETINVS":
				ExportInventory();
			break;
			case "GETITC":
				ReceivedGetItc();
			break;
			case "GETNAVS":
				ExportNavPoints();
			break;
			case "GETPATH":
				ReceivedGetPath();
			break;
			case "GIVEINV":
				ReceivedGiveInv();
			break;
			case "INCH":
				ReceivedInch();
    		break;
    		case "INIT":
				ReceivedInit();
			break;
			case "JUMP":
				ReceivedJump();
			break;
			case "LEAVE":
				ReceivedLeave();
			break;
			case "MESSAGE":
				ReceivedMessage();
			break;
			case "MOVE":
				ReceivedMove();
			break;
			case "PAUSE":
				ReceivedPause();
			break;
			case "PICK":
				ReceivedPick();
			break;
			case "PING":
				SendLine("Pong");
			break;
			case "QUIT":
				Closed();
			break;
			case "READY":
				ReceivedReady();
			break;
			case "REC":
				ReceivedRec();
			break;
			case "REMOVERAY":
				ReceivedRemoveRay();
			break;
			case "RESPAWN":
				ReceivedRespawn();
			break;
			case "ROTATE":
				ReceivedRotate();
			break;
			case "SETCROUCH":
				ReceivedSetCrouch();
			break;
			case "SETEMOT":
				ReceivedSetEmoticon();
			break;
			case "SETROUTE":
				ReceivedSetRoute();
			break;
			case "SETSKIN":
				ReceivedSetSkin();
			break;
    	    case "SETWALK":
				ReceivedSetWalk();
			break;
			case "SHOOT":
				ReceivedShoot();
			break;
			case "SPEECH":
				ReceivedSpeech(); //doesnt work right now
			break;
			case "STOP":
				ReceivedStop();
			break;
			case "STOPREC":
				ReceivedStopRec();
			break;
			case "STOPSHOOT":
				ReceivedStopShoot();
			break;
			case "THROW":
				ReceivedThrow();
			break;
			case "TRACE":
				ReceivedTrace();
			break;
			case "TURNTO":
				ReceivedTurnTo();
			break;
			case "USETRG":
				ReceivedUseTrigger();
			break;
                        default:
                                `log("Unrecognized command: "@cmdType);
                        break;
		}//end switch

}

function ReceivedAddInv()
{
	`log("BotConnection.ReceivedAddInv() NIY");
/*
	local string type;
	local array<string> pickupType;

	if (theBot == None || theBot.Pawn == None)
		return;
	
	type = GetArgVal("Type");

	//We allow to add just pickup classes
	
	pickupType = GetPickupFactoryFromPickupType(type);	
	if (pickupType.Length >= 2)
	{
		//ExportPickup(pickupFactory);

		//log("Added inventory will be "$tempInventory);

		//theBot.SpawnInventory(pickupFactory);

	}
*/
}

function ReceivedAddRay()
{
	local string target;
	local float floatNumber;
	local vector v;
	local bool fastTrace, traceActors, floorCorrection;

	target = GetArgVal("Id");

	if (target == "Default")
	{
		if (RayManager != none)
			RayManager.AddDefaultRays();
	}
	else
	{
		ParseVector(v, "Direction");
		floatNumber = float(GetArgVal("Length"));
		fastTrace = bool(GetArgVal("FastTrace"));
		traceActors = bool(GetArgVal("TraceActors"));
		floorCorrection = bool(GetArgVal("FloorCorrection"));
		if (RayManager != none)
			RayManager.AddCustomRay(target, v, floatNumber, fastTrace, traceActors, floorCorrection);
	}
}

function ReceivedChangeTeam()
{
	local string temp;

	if ((theBot != none) && WorldInfo.Game.bTeamGame)
	{
		temp = GetArgVal("Team");
		if (temp != "")
		{
	    	if (UTGame(WorldInfo.Game).ChangeTeam(theBot, int(temp), true))
			{
				SendLine("TEAMCHANGE {Success True" $
					"} {DesiredTeam " $ temp $
					"}");
			}
			else
			{
			    SendLine("TEAMCHANGE {Success False" $
					"} {DesiredTeam " $ temp $
					"}");
			}
	    }
	}
}


function ReceivedChangeWeapon()
{
	local string Target;
	local Inventory Inv;
	local Weapon TargetWeapon;
	local string outstring;

    if (theBot == none || theBot.Pawn == None || theBot.Pawn.InvManager == None )
		return;

	Target = GetArgVal("Id");
	if( Target ~= "best" )
	{
		theBot.StopFiring();
		theBot.SwitchToBestWeapon();
	}
	else
	{
		TargetWeapon = none;
		foreach theBot.Pawn.InvManager.InventoryActors(class'Inventory', Inv)
		{
			if (target == string(Inv))
			{
				if (Inv.IsA('Weapon'))
					TargetWeapon = Weapon(Inv);
				break;
			}
		}
		if (TargetWeapon == none)
			return;

        theBot.StopFiring();
		//log("Pawns Target Weapon "$TargetWeapon);
		//log("Pawns weapoin: "$theBot.Pawn.Weapon);
		theBot.Pawn.InvManager.PendingWeapon = TargetWeapon;
		if ( theBot.Pawn.InvManager.PendingWeapon == theBot.Pawn.Weapon )
			theBot.Pawn.InvManager.PendingWeapon = None;
		if ( theBot.Pawn.InvManager.PendingWeapon == None )
			return;

		//log("Strelba: "$theBot.bFire$" a alt fire "$theBot.bAltFire);
       	if ( theBot.Pawn.Weapon == None )
			theBot.Pawn.InvManager.ChangedWeapon();
		else if ( theBot.Pawn.Weapon != theBot.Pawn.InvManager.PendingWeapon )
		{
			//notify agent about the weapon state before putdown, so it is up to date
			//SLF message may be inaccurate due to dealy of sync. batch
			outstring = "WUP {Id " $ theBot.Pawn.Weapon $
				"} {PrimaryAmmo " $ UTWeapon(theBot.Pawn.Weapon).AmmoCount $
				"} {InventoryType " $ GetWeaponClassString(theBot.Pawn.Weapon.Class) $ "}";

			if (theBot.Pawn.Weapon.WeaponFireTypes.Length >= 2)
			{
				outstring = outstring $ " {SecondaryAmmo " $ UTWeapon(theBot.Pawn.Weapon).AmmoCount $ "}";
			}

			SendLine(outstring);
			//put down weapon
			theBot.Pawn.Weapon.PutDownWeapon();
		}


	}
}

function ReceivedChAttr()
{
	local string target;

	if (theBot == None || theBot.Pawn == None )
		return;

	target = GetArgVal("Health");
	if (target != "")
	{
		theBot.SetHealth(int(target));
	}

}

function ReceivedCheckReach()
{
	local Controller C;
	local string target, id;
	local vector v;
	local bool boolResult;
	local Actor targetActor;

	if (theBot == None || theBot.Pawn == None)
		return;

	target = GetArgVal("Target");
	id = GetArgVal("Id");
	if(target == "")
	{
		ParseVector(v, "Location");
		boolResult = theBot.PointReachable(v);
		sendLine("RCH {Id " $ id $
			"} {Reachable " $ boolResult $
			"} {From " $ theBot.Pawn.Location $
			"}");
	}
	else
	{
		foreach WorldInfo.AllControllers(class'Controller', C)
		{
			if( ( C $ C.PlayerReplicationInfo.PlayerID ) == target )
			{
				break;
			}
		}
		if (C != None)
		{
			boolResult = theBot.actorReachable( C.Pawn );
			sendLine("RCH {Id " $ id $
				"} {Reachable " $ boolResult $
				"} {From " $ theBot.Pawn.Location $
				"} {To " $ C.Pawn.Location $
				"}");
		}
		else
		{
			foreach DynamicActors(class'Actor', targetActor) {
				if (string(targetActor) == target)
				{
					boolResult = theBot.actorReachable( targetActor );
					sendLine("RCH {Id " $ id $
						"} {Reachable " $ boolResult $
						"} {From " $ theBot.Pawn.Location $
						"} {To " $ targetActor.Location $
						"}");

					break;
				}
			}
		}
	}

}

function ReceivedCMove()
{

	local rotator yawRotation;

	if (theBot == None || theBot.Pawn == None)
		return;

	//We need to reset focus, otherwise the focus would reset focal point to its own location
	theBot.Focus = None;
	theBot.myFocus = None;

	yawRotation.Yaw = theBot.Pawn.Rotation.Yaw;
    theBot.myFocalPoint = theBot.Pawn.Location + 500 * vector(yawRotation);
	theBot.GotoState('Alive','MoveContinuous');
}

function ReceivedDialog()
{
	local string target, dialogId;
	// local string text;
	//local string Options[10];
	//local int i;
	local Controller C;
	local bool bDialogSet;

	if (theBot == None )
		return;

	target = GetArgVal("Id");

	dialogId = GetArgVal("DialogId");
/*
	text = GetArgVal("Text");

	i = 0;
	while (GetArgVal("Option"$i) != "")
	{
		Options[i] = GetArgVal("Option"$i);
		i += 1;

		if (i > 9)
			break;
	}
*/


	foreach WorldInfo.AllControllers(class'Controller', C)
		if( target == string(C) )
			break;
	//end for

	bDialogSet = false;/*
	if ((C != none) && C.IsA('RemoteBot'))
	{
		//we will set dialog just if our bot is selected by the player
		if (RemoteBot(C).SelectedActor == theBot.Pawn)
		{
			RemoteBot(C).SetDialog(dialogId, text, theBot.PlayerReplicationInfo.PlayerName, Options);
			bDialogSet = true;
		}
	}*/

	if (bDialogSet)
	{
		SendLine("DOK {Id " $ dialogId $ "}");
	}
	else
	{
		SendLine("DFAIL {Id " $ dialogId $ "}");
	}
}

function ReceivedDisconnect() {
	if (theBot != none)
		theBot.Destroy();
	self.Destroy();
}

function ReceivedDodge()
{
	local string temp;
	local vector vec;

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

	temp = GetArgVal("Direction");
	if (temp != "")
	{
		ParseVector(vec, "Direction");
		// I should do a dodge now, but i wont :P
		//GBPawn(theBot.Pawn).RemoteDoDodge(Normal(vector(theBot.Pawn.Rotation + rotator(vec))));
	}

}

function ReceivedDriveTo()
{
	local string target;
	local Actor targetActor;

	if (theBot == None || theBot.Pawn == None)
		return;

	target = GetArgVal("Target");
	if(target != "")
	{
		foreach DynamicActors(class'Actor', targetActor) {
			if( string(targetActor) == target )
			{
				theBot.MoveTarget = targetActor;

				theBot.myFocalPoint = targetActor.Location +
					500 * vector(rotator(targetActor.Location - theBot.Pawn.Location));
					//should repair issue that the bot is turning after finishing moveto
				theBot.GotoState('Alive', 'MoveToActor');

				break;
			}
		}
	}
}

function ReceivedEnter()
{
	local Vehicle Veh;
	local string target;

	target = GetArgVal("Id");

	if (target == "")
		return;

	//if bot is created and we are alive and we are not driving another vehicle
	if ((theBot != none) && (theBot.Pawn != none) && (Vehicle(theBot.Pawn)==None))
		foreach WorldInfo.AllPawns(class'Vehicle', Veh)
		{
			// 50.0f temporary EntryRadius
			if ((vSize(theBot.Pawn.Location - Veh.Location) < 50.0f) && (target == getUniqueId(Veh)))
			{
				if (Veh.TryToDrive(theBot.Pawn))
					SendLine("ENTERED {Id " $ Veh $
						"} {Type " $ Veh.Class $
						"} {Location " $ Veh.Location $
						"}");
				else
					SendLine("LOCKED {Id " $ Veh $
						"} {Type " $ Veh.Class $
						"} {Location " $ Veh.Location $
						"}");
			}
		}

}

function ReceivedExit()
{
	// need state first...
}

function ReceivedFTrace()
{
	local vector v,v2;
	local string target;

	if (theBot == None || theBot.Pawn == None)
		return;

	if (GetArgVal("From") == "")
	{
		if (theBot.Pawn != None)
			v = theBot.Pawn.Location;
		else
			return;

	}
	else
	{
		ParseVector(v,"From");
	}
	if (GetArgVal("To") == "")
	{
		return;
	}

	ParseVector(v2,"To");
	target = GetArgVal("Id");

	SendLine("FTR {Id " $ target $
		"} {From " $ v $
		"} {To " $ v2 $
		"} {Result " $ !FastTrace(v2,v) $
		"}");
}

function ReceivedConf()
{
	local float floatNumber;
	local string target;
	local rotator r;
	local bool tempBool;

	if (theBot == none)
		return;

	if (GetArgVal("AutoTrace") != "") {
		tempBool = bool(GetArgVal("AutoTrace"));
		theBot.bAutoTrace = tempBool;
		if (tempBool == false)
			RayManager.StopRays();
	}
	if (GetArgVal("DrawTraceLines") != "") {
		tempBool = bool(GetArgVal("DrawTraceLines"));
		theBot.bDrawTraceLines = tempBool;
		if (tempBool == false)
			RayManager.StopRays();		
	}
	if (GetArgVal("ManualSpawn") != "")
		theBot.bAutoSpawn = !bool(GetArgVal("ManualSpawn"));
	if (GetArgVal("ShowFocalPoint")!="")
		theBot.bShowFocalPoint = bool(GetArgVal("ShowFocalPoint"));
	if (GetArgVal("ShowDebug")!="")
		theBot.bDebug = bool(GetArgVal("ShowDebug"));
	if (GetArgVal("Name") != "")
	{
		target = GetArgVal("Name");
		theBot.PlayerReplicationInfo.PlayerName = target;
		WorldInfo.Game.changeName( theBot, target, true );
	}
	if (GetArgVal("SpeedMultiplier") != "")
	{
		floatNumber = float(GetArgVal("SpeedMultiplier"));
		if ( (floatNumber >= 0.1) && (floatNumber <= theBot.MaxSpeed) )
		{
			theBot.SpeedMultiplier = floatNumber;
		    if (theBot.Pawn != none) {
				theBot.Pawn.GroundSpeed = floatNumber * theBot.Pawn.Default.GroundSpeed;
				theBot.Pawn.WaterSpeed = floatNumber * theBot.Pawn.Default.WaterSpeed;
				theBot.Pawn.AirSpeed = floatNumber * theBot.Pawn.Default.AirSpeed;
				theBot.Pawn.LadderSpeed = floatNumber * theBot.Pawn.Default.LadderSpeed;
			}
		}
	}
	if(GetArgVal("RotationRate") != "") {
		ParseRot(r,"RotationRate");
		if (r != rot(0,0,0)) {
			theBot.RotationRate = r;
			if (theBot.Pawn != none) {
				theBot.Pawn.RotationRate = r;
			}
		}
	}
	if ((GetArgVal("Invulnerable") != "") && (bAllowCheats == true))
		theBot.bGodMode = bool(GetArgVal("Invulnerable"));
	if (GetArgVal("VisionTime") != "")
	{
		floatNumber = float(GetArgVal("VisionTime"));
		if ((floatNumber >= 0.1) && (floatNumber <= 2))
			visionTime = floatNumber;
	}
	if (GetArgVal("SynchronousOff") != "")
	{
		theBot.myConnection.bSynchronousMessagesOff = bool(GetArgVal("SynchronousOff"));
	}
	if (GetArgVal("AutoPickupOff") != "")
	{
		theBot.bDisableAutoPickup = bool(GetArgVal("AutoPickupOff"));
		if (theBot.Pawn != none)
			theBot.Pawn.bCanPickupInventory = !theBot.bDisableAutoPickup;
	}
	SendNotifyConf();
}

function ReceivedGetItc()
{/*
	local string pickupType;
	local PickupFactory pickupFactory;

	if (theBot == None)
		return;

	pickupType = GetArgVal("Type");

	pickupFactory = GetPickupFactoryFromPickupType(pickupType);
	
	ExportPickup(pickupFactory);*/
}

function ReceivedGetPath()
{
	local vector v;
	local string id, stringPath, tmp, target;
	local int i;
	local NavigationPoint N;

	if ( theBot == None )
		return;

	//clear the old path
	for ( i = 0; i < theBot.RouteCache.Length; i++ )
	{
		if ( theBot.RouteCache[i] == None )
			break;
		else
		{
			theBot.RouteCache[i] = None;
		}
	}

    id = GetArgVal("Id");

	target = GetArgVal("Target");
	if (target != "")
	{
		foreach WorldInfo.AllNavigationPoints(class'NavigationPoint', N )
		{
			if( target == string(N) )
			{
				break;
			}
		}//end for

		if (N != none)
			theBot.FindPathToward(N, true);
	} else {
		if (GetArgVal("Location") != "")
		{
			ParseVector(v, "Location");
			theBot.NavigationHandle.ComputeValidFinalDestination(v);
			theBot.FindPathTo(v,, true);
		}
	}

	SendLine("SPTH {MessageId " $ id $ "}");
	for ( i = 0; i < theBot.RouteCache.Length; i++ )
	{
		if ( theBot.RouteCache[i] == None )
			break;
		else
		{
			SendLine("IPTH {RouteId " $ theBot.RouteCache[i] $
				"} {Location " $ theBot.RouteCache[i].Location $
				"}");
		}
	}
	SendLine("EPTH");

   	stringPath = WorldInfo.TimeSeconds $ ": Id:" $ id $ ", ";
	for ( i = 0; i < theBot.RouteCache.Length; i++ )
	{
		if ( theBot.RouteCache[i] == None )
			break;
		else
		{
			tmp = string(theBot.RouteCache[i]);
			tmp = Mid( tmp, InStr( tmp, "." ) + 1 );
			stringPath = stringPath $ i $ ": " $ tmp $ ", ";
		}
	}

	//TODO: This is just for debugging purpose - theres not GBPawn in SetLastGBPath anyway
	if (theBot.Pawn != none) {
		//GBPawn(theBot.Pawn).SetLastGBPath(stringPath);
	}
}

function ReceivedGiveInv()
{
	local Controller C;
	local string target;
	local Inventory Inv;
	local class<Inventory> InvClass;

	if ((theBot == None) || (theBot.Pawn == None))
		return;

	target = GetArgVal("Target");

	foreach WorldInfo.AllControllers(class'Controller', C)
	{
		if( string(C) == target )
		{
			if (C.Pawn != none)
			{
			 	target = GetArgVal("ItemId");
			 	InvClass = class<Inventory>(DynamicLoadObject(target, class'Class'));				
				Inv = theBot.Pawn.InvManager.FindInventoryType(InvClass);

				if (Inv != none)
				{
					if (theBot.Pawn.Weapon == Inv)
						return;
					if (C.Pawn.InvManager.AddInventory(Inv))
					{
						SendLine("LIN {Id " $ Inv $ "}");
					 	theBot.Pawn.InvManager.RemoveFromInventory( Inv );
					}
				}
			}
       	}
	}
}

function ReceivedInch()
{
	//test function
	/* must deal with target, focus and destination */
	theBot.StopLatentExecution();
	theBot.myDestination = (100 * vector(theBot.Pawn.Rotation)) + theBot.Pawn.Location;
	theBot.GotoState('Alive', 'MoveToPoint');
}

function ReceivedInit()
{
	if (IsInState('waiting')) {
		InitBot();
	} else {
		if (bDebug)
			`log("Bot already spawned");
	}
}



function ReceivedJump()
{
	local string tmp;
	local bool bDouble;

	if (theBot == None || theBot.Pawn == None)
		return;

	bDouble = false;
	tmp = GetArgVal("DoubleJump");
	if (tmp != "")
		bDouble = bool(tmp);

	theBot.RemoteJump(bDouble, float(GetArgVal("Force")), float(GetArgVal("Delay")));
}

function ReceivedLeave()
{
	if (Vehicle(theBot.Pawn)!=None)
	{
		Vehicle(theBot.Pawn).DriverLeave( true );
	}
}

function ReceivedMessage()
{
	local string target, text;
	local bool boolResult;
	local float FadeOut;

	if (theBot == None )
		return;

	//Note - currently only allow messages under 256 chars
	target = GetArgVal("Id");
	text = GetArgVal("Text");
	boolResult = bool(GetArgVal("Global"));
	FadeOut = float(GetArgVal("FadeOut"));
	if(text != "")
	{
		theBot.RemoteBroadcast(target,text,boolResult,FadeOut);
	}

}

function Vector getFloorLocation(Vector point)
{
	local Vector output;
	local Vector normal;
	local Vector to;

	to = point + vect(0, 0, -10000);
	if (Trace(output, normal, to, point, true) != none)
	{
		if (point.Z - output.Z > 50.f) {
			output.Z += 50.f;
			return output;
		}
		else
			return point;
		
	}
	else
	{
		return to;
	}
}

function ReceivedMove()
{
	local vector v, v2, focusLoc;
	local string focusId;
	local Actor tmpFocus;
	local Controller C;
	local Actor moveTarget;

	if (theBot == None || theBot.Pawn == None)
		return;

	//if first location not specified, we wont move
	if (GetArgVal("FirstLocation") == "")
		return;

	ParseVector(v, "FirstLocation");
	focusId = GetArgVal("FocusTarget");

	//`log("v: " $ v);	
	v = getFloorLocation(v);	
	//`log("v: " $ v);
	//set the destinations we want to traverse
	theBot.myDestination = v;
	theBot.SetDestinationPosition(v);
	if (GetArgVal("SecondLocation") != "")
	{
		ParseVector(v2, "SecondLocation");
		theBot.pendingDestination = v2;
		theBot.bPendingDestination = true;
		//`log("v2: " $ v2);
		v2 = getFloorLocation(v2);
		//`log("v2: " $ v2);
	}
	else
	{
		theBot.bPendingDestination = false;
	}

	if(focusId == "")
	{
		if (GetArgVal("FocusLocation") != "")
		{
			ParseVector(focusLoc, "FocusLocation");
			//Cant focus to location, but can to actor, we set position of our helper actor to desired
	        FocusActor.SetLocation(focusLoc);
	        //Lets se the bots focus to our helper actor
			theBot.Focus = FocusActor;
			theBot.myFocus = FocusActor;
			//Set FocalPoint accordingly (it would change to desired values anyway)
			theBot.myFocalPoint = FocusActor.Location;
			theBot.SetFocalPoint(FocusActor.Location);
		}
		else
		{
			//we reset old focus, if none focus set, the bot will turn towards destination
			theBot.Focus = none;
			theBot.myFocus = none;
			//todo: reset also target?

			//set myFocalPoint to prevent unwanted turning back at the end of movement
			//myFocalPoint will be set at the end of movement
			if (GetArgVal("SecondLocation") != "")
				theBot.myFocalPoint = v2 + 500 * vector(rotator(v2 - v));
			else
				theBot.myFocalPoint = v + 500 * vector(rotator(v - theBot.Pawn.Location));
		}
	}
	else //We have Id of the object we want to face
	{
		//First we determine if it is a bot id
		tmpFocus = None;
		foreach WorldInfo.AllControllers(class'Controller', C)
		{
			if( string(C) == focusId)
			{
				if( theBot.Pawn.LineOfSightTo(C.Pawn))
				{
					tmpFocus = C.Pawn;
				}
				break;
			}
		}
		//we found it is a bot id, lets set it as our focus
		if (tmpFocus != none)
		{
			//point the bot at the location of the target
			theBot.SetFocalPoint(tmpFocus.Location);
			theBot.myFocalPoint = tmpFocus.Location;
			theBot.Focus = tmpFocus;
			theBot.myFocus = tmpFocus;
		}
		else // it was not a bot id
		{
			foreach DynamicActors(class'Actor', moveTarget){
				if (string(moveTarget) == focusId) {
					if (theBot.Pawn.LineOfSightTo(moveTarget))
					{
						theBot.myFocus = moveTarget;
						theBot.Focus = moveTarget;
						theBot.myFocalPoint = moveTarget.Location;
						theBot.SetFocalPoint(moveTarget.Location);
					}
					break;
				}
			}
		}
	}
	theBot.GotoState('Alive','Move');
}

function ReceivedPause()
{
	local bool bWasPaused;

	if ((!bAllowPause) || theBot == None)
		return;

	if ((WorldInfo.bPlayersOnly == true) || (WorldInfo.Pauser != none))
		bWasPaused = true;
	else
		bWasPaused = false;


	if (GetArgVal("PauseBots")!="")
	{
		WorldInfo.bPlayersOnly = bool(GetArgVal("PauseBots"));
	}
	if (GetArgVal("PauseAll")!="")
	{
		if (bool(GetArgVal("PauseAll")))
		{
			if (WorldInfo.Pauser == None)
			{
				// we have to blame the pause on somebody
				WorldInfo.Pauser = GBGameInterface(WorldInfo.Game).GetGameHandler().LevelPauserFeed;
			}
		}
		else
		{
			WorldInfo.Pauser = None;
		}

	}

	if (bWasPaused == true)
	{
		if ((WorldInfo.bPlayersOnly == false) && (WorldInfo.Pauser == none))
			SendNotifyPause(false); //send resume message
	}
	else
	{
		if ((WorldInfo.bPlayersOnly == true) || (WorldInfo.Pauser != none))
			SendNotifyPause(true); //send pause message
	}

}

function ReceivedPick()
{
	local string target;

	if (theBot == none)
		return;

	target = GetArgVal("Id");
	if (target != "")
		theBot.RemotePickup(target);

}

function ReceivedPassword()
{
	local string target;

	target = GetArgVal("Password");
	`log("Gloat - " $ target);
	if (target == GBGameInterface(WorldInfo.Game).GetGameHandler().Password)
	{
		SendLine("PASSWDOK");
		ExportStatus();
		gotoState('waiting','Waiting');
	}
	else
	{
		SendLine("PASSWDWRONG");
		Closed();
	}

}


function ReceivedReady()
{
	if (IsInState('waiting'))
		ExportStatus();

	if ( IsInState('checkingPassword') )
		SendLine("PASSWORD {BlockedByIp " $ GBGameInterface(WorldInfo.Game).GetGameHandler().PasswordByIP $"}");

}

function ReceivedRec()
{
	ConsoleCommand("startmoviecapture ", True);
	SendLine("RECSTART");
}

function ReceivedRemoveRay()
{
	local string target;

	if (theBot == None)
		return;
	target = GetArgVal("Id");
	if (RayManager != none)
		RayManager.RemoveCustomRay(target);
}

function ReceivedRespawn()
{
	local vector v;
	local rotator r;

	if ( theBot == None || !bBotInitialized)
		return;

	ParseVector(v, "StartLocation");
	ParseRot(r, "StartRotation");
	`log("ReceivedRespawn:L:" $ v $ ";R:" $ r );
	
	theBot.RespawnPlayer(v, r);
}

function ReceivedRotate()
{
	local string target;
	local rotator r;
	local int i;

	if (theBot.Pawn == None)
		return;

	target = GetArgVal("Axis");
	r = theBot.Pawn.Rotation;
	i = int(GetArgVal("Amount"));
	if(target == "Vertical")
	{
		r.Pitch = int(theBot.Pawn.RemoteViewPitch) * 65556/255 + i;
		//r.Yaw = theBot.Pawn.Rotation.Yaw;
	}
	else
	{
		r.Pitch = int(theBot.Pawn.RemoteViewPitch) * 65556/255;
		r.Yaw += i;
	}

	theBot.myFocalPoint = theBot.Pawn.Location + ( vector(r) * 500);
	theBot.SetFocalPoint(theBot.myFocalPoint);
	theBot.myFocus = None;
	theBot.Focus = None; //theBot.Pawn.Location + ( vector(r) * 1000);

    if (theBot.movingContinuous)
		theBot.GotoState('Alive','MoveContinuous');

	//We comment this so turning commands do not interupt moving commands.
	//theBot.StopWaiting();
	//theBot.GotoState('Startup', 'Turning');

}

function ReceivedSetCrouch()
{
	local string target;

	if (theBot == None || theBot.Pawn == None)
		return;

	target = GetArgVal("Crouch");
	theBot.Pawn.ShouldCrouch( bool(target) );

}

function ReceivedSetEmoticon()
{
	local string temp;
	local GBReplicationInfo repInfo;
	
	if (theBot == none)	
		return;

	repInfo = GBReplicationInfo(theBot.PlayerReplicationInfo);

	temp = GetArgVal("Center");
	if (temp != "") {
		repInfo.SetEmoticonCenter(temp);

	}
	temp = GetArgVal("CenterSize");
	if (temp != "") {
		repInfo.SetEmoticonCenterSize(int(temp));
	}

	temp = GetArgVal("Left");
	if (temp != "") {
		repInfo.SetEmoticonLeft(temp);
	}
	temp = GetArgVal("LeftSize");
	if (temp != "") {
		repInfo.SetEmoticonLeftSize(int(temp));
	}

	temp = GetArgVal("Right");
	if (temp != "") {
		repInfo.SetEmoticonRight(temp);
	}

	temp = GetArgVal("RightSize");
	if (temp != "") {
		repInfo.SetEmoticonRightSize(int(temp));
	}

	temp = GetArgVal("BubbleType");
	if (temp != "") {
		repInfo.SetEmoticonBubbleType(temp);
	}

	temp = GetArgVal("BubbleSize");
	if (temp != "") {
		repInfo.SetEmoticonBubbleSize(int(temp));
	}

	temp = GetArgVal("Time");
	if (temp != "") {
		repInfo.SetEmoticonTime(float(temp));
	}
	
	temp = GetArgVal("Offset");
	if (temp != "") {
		repInfo.SetEmoticonOffset(float(temp));
	}	
}

function ReceivedSetRoute()
{
	local vector v;
	local int i;

	if (theBot == None)
		return;

	if (bool(GetArgVal("Erase"))) {
		for (i = 0; i < 32; i++) {
			GBReplicationInfo(theBot.PlayerReplicationInfo).SetCustomRoute(vect(0,0,0), i);
		}
	}

	for (i = 0; i < 32; i++)
	{
		ParseVector(v, "Point" $ i );
		GBReplicationInfo(theBot.PlayerReplicationInfo).SetCustomRoute(v, i);
	}
}

function ReceivedSetSkin()
{

	if (theBot == None)
		return;
	if (GetArgVal("Skin") != "")
	{

		//theBot.Pawn.LinkMesh(Mesh(DynamicLoadObject(GetArgVal("Skin"), class'Mesh')));
		theBot.DesiredSkin = GetArgVal("Skin");
		//theBot.Pawn.LinkMesh(Mesh(DynamicLoadObject(theBot.DesiredSkin, class'Mesh')));
	}	
}

function ReceivedSetWalk()
{

	local string target;

	if (theBot == None || theBot.Pawn == None)
		return;

	target = GetArgVal("Walk");
	theBot.Pawn.bIsWalking = bool(target);

}

function ReceivedShoot()
{
	local string Target;
	local Controller C;
	local vector v;
	local bool targetLocked;

	if (theBot == None || theBot.Pawn == None)
		return;

	targetLocked = false;
	theBot.bTargetLocationLocked = false;
	Target = GetArgVal("Target");
	if( Target != "")
	{
		foreach WorldInfo.AllControllers(class'Controller', C)
		{
			//We wont start shooting at non visible targets
			if( (string(C) == target) &&
				(C.Pawn != None) &&
				(theBot.LineOfSightTo(C.Pawn))
			)
			{
				//We will set desired bot as our enemy
				theBot.Focus = C.Pawn;
				theBot.myFocus = C.Pawn;
				theBot.Enemy = C.Pawn;
				theBot.RemoteEnemy = C;

				theBot.SetFocalPoint(C.Pawn.Location);
				theBot.myFocalPoint = C.Pawn.Location;
				targetLocked = true;
				break;
			}
		}
	}

	if (!targetLocked && GetArgVal("Location") != "")
	{
		ParseVector(v,"Location");

		theBot.myTarget.SetLocation(v);
		theBot.bTargetLocationLocked = true;

		//We are shooting at a location. We will set the FocalPoint
		theBot.SetFocalPoint(theBot.myTarget.Location);
		theBot.myFocalPoint = theBot.myTarget.Location;

		theBot.Focus = theBot.myTarget;
		theBot.Enemy = None;
		//theBot.Target = theBot.myTarget;
	}

	theBot.RemoteFireWeapon(bool(GetArgVal("Alt")));
}

function ReceivedSpeech()
{
	local string target;

	if (theBot == None)
		return;

	target = GetArgVal("Text");
	//theBot.TextToSpeech(target,50); //dont work dunno the reason, maybe volume bigger?
	SendLine(target);
}

function ReceivedStop()
{
	if (theBot == None || theBot.Pawn == None)
		return;

	theBot.GotoState('Alive', 'DoStop');
}

function ReceivedStopRec()
{
	ConsoleCommand("stopmoviecapture", True);
	SendLine("RECEND");
}

function ReceivedStopShoot()
{
	if (theBot == None)
		return;

	// I just hope that this isnt complete bollocks
	//theBot.StopLatentExecution();
	theBot.RemoteStopFire();
}

function ReceivedThrow()
{
	local string thrownWeaponId;

	if (theBot == None || theBot.Pawn == None)
		return;

	if (theBot.Pawn.CanThrowWeapon())
	{
		thrownWeaponId = string(theBot.Pawn.Weapon);
		theBot.Pawn.ThrowActiveWeapon();
		theBot.SwitchToBestWeapon();
		SendLine("THROWN {Id " $ thrownWeaponId $ "}");
	}
}

function ReceivedTrace()
{
	local Actor hitActor;
	local bool traceActors, result;
	local Vector traceStart, traceEnd, hitLocation, hitNormal;
	local string traceUserId, hitId;
	
	if ( GetArgVal("From") == "" )
	{
		if ( theBot.Pawn == none )
		{
			return;
		}

		traceStart = theBot.Pawn.Location;
	}
	else
	{
		ParseVector( traceStart, "From" );
	}
	
	if ( GetArgVal("To") == "" )
	{
		return;
	}

	ParseVector( traceEnd,"To" );
	traceUserId = GetArgVal("Id");
	traceActors = bool(GetArgVal("TraceActors"));

	hitActor = Trace( hitLocation, HitNormal, traceEnd, traceStart, traceActors, , );
	fixHitLocation( hitLocation, traceStart, traceEnd, hitActor );

	result = hitActor != None;
	hitId = getUniqueId( hitActor );

	SendLine(
		"TRC {Id " $ traceUserId $
		"} {From " $ traceStart $
		"} {To " $ traceEnd $
		"} {Result " $ result $
		"} {HitID " $ hitId $
		"} {HitLocation " $ hitLocation $
		"} {HitNormal " $ hitNormal $
		"} {TraceActors " $ traceActors $
		"}"
	);
}

// copy pasted from EhTraceTools
private static function fixHitLocation( out Vector hitLocation, const out Vector traceStart, const out Vector traceEnd, Actor tracedActor, optional bool isNonZeroExtent = false )
{
	local float offset;

	if ( tracedActor == none )
	{
		return;
	}
	else if ( WorldInfo(tracedActor) != none )
	{
		if ( isNonZeroExtent )
		{
			offset = 1.0;
		}
		else
		{
			offset = 0.5;
		}
	}
	else if ( StaticMeshComponent(tracedActor.CollisionComponent) != none )
	{
		if ( tracedActor.bCollideComplex )
		{
			offset = 4.0;
		}
		else
		{
			offset = 1.0;
		}
	}
	else if ( CylinderComponent(tracedActor.CollisionComponent) != none )
	{
		offset = 1.0;
	}
	else if ( SkeletalMeshComponent(tracedActor.CollisionComponent) != none )
	{
		if ( SkeletalMeshComponent(tracedActor.CollisionComponent).PhysicsAsset != none )
		{
			offset = 0.0;
		}
		else
		{
			offset = 4.0;
		}
	}
	else if ( SpeedTreeComponent(tracedActor.CollisionComponent) != none )
	{
		// FIXME offset value unknown
		offset = 0.0;
	}
	else
	{
		// unexpected collision component
	}

	hitLocation = hitLocation + Normal( traceEnd - traceStart ) * Offset;
}

function ReceivedTurnTo()
{
	local Controller C;
	local vector v;
	local rotator r;
	local string target;
	local Actor turnToTarget;

	if (theBot == none || theBot.Pawn == None)
		return;

	target = GetArgVal("Target");
	if(target == "")
	{
		ParseRot(r,"Rotation");
		if(r.Yaw == 0 && r.Pitch == 0 && r.Roll == 0)
		{
			//no target or rotation defined
			ParseVector(v,"Location");
			theBot.SetFocalPoint(v);
			theBot.myFocalPoint = v;
			//We erase possible focus actors
			theBot.myFocus = None;
			theBot.Focus = None;

			if (theBot.movingContinuous)
				theBot.GotoState('Alive','MoveContinuous');
		}
		else
		{
			//no target, yes rotation
			theBot.myFocalPoint = theBot.Pawn.Location + ( vector(r) * 500);
			theBot.SetFocalPoint(theBot.myFocalPoint);

			//We erase possible focus actors
			theBot.myFocus = None;
			theBot.Focus = None;

			if (theBot.movingContinuous)
				theBot.GotoState('Alive','MoveContinuous');
		}
	}
	else
	{
		//target defined

		//First we try to find if we should focus to a player or bot		
		foreach WorldInfo.AllControllers(class'Controller', C)
		{
			if( target == (C $ C.PlayerReplicationInfo.PlayerID) )
			{
				break;
			}
		}//end for
		if (C != None)
		{
			//Pawn must exists and must be visible
			if ((C.Pawn != None) && theBot.Pawn.LineOfSightTo(C.Pawn))
			{
				//We set the Controller as our target
				theBot.SetFocalPoint(C.Pawn.Location);
				theBot.myFocalPoint = C.Pawn.Location;
				theBot.Focus = C.Pawn;
				theBot.myFocus = C.Pawn;

				if (theBot.movingContinuous)
					theBot.GotoState('Alive','MoveContinuous');

			}
			else
			{
				return;
			}
		}
		else
		{
			foreach DynamicActors(class'Actor', turnToTarget){
				if (string(turnToTarget) == target) {
					if (theBot.Pawn.LineOfSightTo(turnToTarget))
					{
						//Actor must be visible
						theBot.myFocus = turnToTarget;
						theBot.Focus = turnToTarget;
						theBot.myFocalPoint = turnToTarget.Location;
						theBot.SetFocalPoint(theBot.myFocalPoint);

						if (theBot.movingContinuous)
							theBot.GotoState('Alive','MoveContinuous');						
					}
					break;
				}
			}
		}
	}
	//We comment this so turning commands do not interupt moving commands.
	//theBot.StopWaiting();
	//theBot.GotoState('Alive', 'Turning');

}

function ReceivedUseTrigger() {

	local bool success;
	local string targetParam;
	local Trigger target;
	local Trigger checkTarget;
	local float interactDistanceToCheck;
	local int Idx;
	local SeqEvent_Used	UseSeq;
	local bool bUsuableOnly;
        local bool bIgnoreEventInteractionDistance;

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

	//default values
	interactDistanceToCheck = 300.f;
                
	bUsuableOnly = bool(GetArgVal("UsableOnly"));
        bIgnoreEventInteractionDistance = bool(GetArgVal("IgnoreEventInteractionDistance"));

	targetParam = GetArgVal("Target");
	success = false;

	foreach theBot.Pawn.CollidingActors(class'Trigger',checkTarget,interactDistanceToCheck){
		if(string(checkTarget) == targetParam){
			target = checkTarget;
			break;
		}
	}

	if(target != none){
		//Check whether the desired trigger is in range
		for (Idx = 0; Idx < target.GeneratedEvents.Length; Idx++)
		{

			UseSeq = SeqEvent_Used(target.GeneratedEvents[Idx]);

			if( ( UseSeq != None )
				// if bUsuableOnly is true then we must get true back from CheckActivate (which tests various validity checks on the player and on the trigger's trigger count and retrigger conditions etc)
				&& ( !bUsuableOnly || ( target.GeneratedEvents[Idx].CheckActivate(target,theBot.Pawn,true)) )
				// check that we are close enough
				&& (bIgnoreEventInteractionDistance || VSize(theBot.Pawn.Location - target.Location) <= UseSeq.InteractDistance ) 
				)  
			{
				success = target.UsedBy(theBot.Pawn);
				if(bDebug)
					`log("Used " $ target $ " by "$ theBot.PawnClass $ ". Succes: " $ success);
				break;
			}
		}
	} else {
		if(bDebug)
			`log("Target not found" @ self);
	}

	SendLine("TRGUSED {Success "$ success $"} {Target " $ targetParam $"}");

}


//----------------- STATES

//Waiting state when connected
auto state waiting
{
Begin:
Waiting:
	sleep(5.0);
	goto 'Waiting';

}

state checkingPassword
{
Begin:
Waiting:
	sleep(5.0);
	goto 'Waiting';
}

state monitoring
{

Begin:
Running:
	if(theBot != none && WorldInfo.Pauser == none && !theBot.IsInState('Dead') && !theBot.IsInState('GameEnded') ) {
		//This is where synchronous batch messages are sent
		SendLine("BEG {Time " $ WorldInfo.TimeSeconds $"}");

		//log("Measuring Time of synchronous batch: ");
		//StopWatch(false);

		if (!bSynchronousMessagesOff) {
        ExportGameStatus();
		}
		theBot.checkSelf();
		if (!bSynchronousMessagesOff) {
		theBot.checkVision();
		theBot.SendFlagInfo();
		}

		// if bAutoTrace true, then send results of all rays
		if (theBot.bAutoTrace)
			RayManager.AutoTrace();

		//StopWatch(true);
		SendLine("END {Time " $ WorldInfo.TimeSeconds $"}");
	}
	//theBot.bDebug = true;
	if (theBot.bDebug && !theBot.IsInState('StartUp'))
		`log(theBot.getStateName());
	if (bIterative)
		WorldInfo.Pauser=theBot.PlayerReplicationInfo;

		sleep(visionTime);
	goto 'Running';
SynchronousOff:
	if (!bSynchronousMessagesOff)
		goto 'Running';
	sleep(1);
	goto 'SynchronousOff';
}


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

defaultproperties
{
	bDebug=false
}
