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

*/
class GBHUD extends UTHUDBase
	config(GameBotsUDK);

//How many points we should shift in y coordinate to write next line of the text on the HUD
//properly
var float shift;

//We will be drawing names of navigation points that are in radius below
var config float NavPointBeaconDrawDistance;

//private boolean
var private bool bPressedAlt;

// custom Interaction through which we handle key inputs (thanks Wormbo :-) )
var Interaction KeyCaptureInteraction; 

//Some variables for setting what should be visible on the HUD
var config bool bDrawNavPointsNames;
var config bool bDisplayDebug;
var config bool bDisplayHelp;
var config bool bDisplayInformation;
var config bool bDisplayPlayerList;
var config bool bDisplayRoute;
var config bool bDisplayHealthBar;
var config bool bDisplayTextBubble;
var config bool bDisplayMyLocation;
var config bool bDrawNavPointsGrid;
var config bool bDisplayNavCubes;
var config int DisplayPlayerPositions;
var config bool bDisableKeyControls;

//for key exporting
var name lastInputKey;
var EInputEvent lastInputAction;

var bool bNavCubesSpawned;

var Font GBFont;
var config string GBFontName;

//here we will store information about player view - will be used in FOV checking code (InFOV fc)
var vector ViewLocation;
var rotator ViewRotation;

//setting of our custom colors
var int DefaultColorR, DefaultColorG, DefaultColorB, DefaultColorA;
var int EnabledColorR, EnabledColorG, EnabledColorB, EnabledColorA;
var int DisabledColorR, DisabledColorG, DisabledColorB, DisabledColorA;

//Cubes floating at NavPoint positions.
var array<PathMarker> pathMarkers;

var config float TextBubbleMaxDist;
//used for drawing emoticons in correct order
var array<GBReplicationInfo> repInfoList;

var protected const Texture2D WHITE_BACKGROUND;

const MAX_VIEW_DISTANCE = 7000;

simulated function PostBeginPlay()
{
	local int iInput;
	
	Super.PostBeginPlay();

	GBFont = Font(DynamicLoadObject(GBFontName, class'Font'));
	
	KeyCaptureInteraction = new (self) class'Interaction';
	KeyCaptureInteraction.OnReceivedNativeInputKey = ReceivedNativeInputKey;
	
	// insert before input interaction to capture movement keys, if necessary
	iInput = PlayerOwner.Interactions.Find(PlayerOwner.PlayerInput);
	PlayerOwner.Interactions.InsertItem(Max(iInput, 0), KeyCaptureInteraction);

	if (bDisplayNavCubes)
	{
		SpawnNavCubes();
    }
}


event PostRender() {
	local float XPos, YPos;
	local Font oldFont;

	oldFont = Canvas.Font;
	Canvas.Font = GBFont;

	//set up global variables ViewLocation and ViewRotation - will be used in FOV checking code (InFOV fc)
	GetPlayerViewInformation(ViewLocation, ViewRotation);

	XPos = 25;
	YPos = 25;

    if (bDisplayMyLocation)
		DrawMyLocation(XPos, YPos);

    if (bDisplayHelp)
		DrawHelp(XPos, YPos);

	if (bDisplayPlayerList)
		DrawPlayerList(XPos,YPos);

	if (DisplayPlayerPositions > 0)
		DrawPlayerDebug();

	if (bDisplayRoute)
		DrawCustomRoute();

	if (bDrawNavPointsNames)
		DrawNavPointsNames();

	if (bDrawNavPointsGrid)
		DrawNavPointsGrid();	

	DrawEmoticons();

	ProcessVisiblePawns();

	Canvas.Font = oldFont;
}
function DisplayPawnDoll(){}

function ProcessVisiblePawns() {
	local GBReplicationInfo MyRepInfo;
	local Pawn P;
	local Vector PawnPos;
	local Vector CanvasPawnPos;

	if (!bDisplayTextBubble && !bDisplayHealthBar && !bDisplayDebug)
		return;

	foreach DynamicActors(Class'GBReplicationInfo', MyRepInfo) {
		if (MyRepInfo.getMyPawn() != none) {
			P = MyRepInfo.getMyPawn();
			 //TODO: Check also distance? - YES!!
			
			if (InFOV(P.Location, PlayerOwner.GetFOVAngle(), ViewLocation, ViewRotation) && HUDLoS(P,ViewLocation)) {
				PawnPos = P.Location;
				PawnPos.Z += P.EyeHeight;
				CanvasPawnPos = Canvas.Project(PawnPos);

				if (bDisplayTextBubble)
					DrawTextBubble( P, CanvasPawnPos.X, CanvasPawnPos.Y);
				if (bDisplayHealthBar)
					DrawHealthBar( P, MyRepInfo.getMyHealth(), CanvasPawnPos.X, CanvasPawnPos.Y);
				if (bDisplayDebug)
					DrawGBDebug( MyRepInfo, CanvasPawnPos.X, CanvasPawnPos.Y);			
			}
					
		}
	}
}

function DrawCustomRoute()
{
	local int i;
	local vector lastPoint, currentPoint, resVect, resNormal;
	local vector CanvasPosOne, CanvasPosTwo;
	local GBReplicationInfo MyRepInfo;

	foreach DynamicActors(Class'GBReplicationInfo', MyRepInfo) {
	    	for ( i=0; i<32; i++ ) {
    	        currentPoint = MyRepInfo.GetCustomRoute(i);

				if ( (lastPoint != vect(0,0,0)) && (currentPoint != vect(0,0,0))
					&& InFOV(lastPoint, PlayerOwner.GetFOVAngle(), ViewLocation, ViewRotation)
					&& InFOV(currentPoint, PlayerOwner.GetFOVAngle(), ViewLocation, ViewRotation))
				{
					//C.DrawText("From: "$lastPoint$" To: "$theBot.GetCustomRoute(i));
					CanvasPosOne = Canvas.Project(lastPoint);
					CanvasPosTwo = Canvas.Project(currentPoint);

					Canvas.Draw2DLine(CanvasPosOne.x, CanvasPosOne.y, CanvasPosTwo.x, CanvasPosTwo.y,MyMakeColor(255,0,0,255));

					resNormal = Normal((currentPoint - lastPoint) cross vect(0, 0, 1));
				    resVect = currentPoint - (Normal(currentPoint - lastPoint) * 14);

					//Right line of the arrow
					CanvasPosOne = Canvas.Project(resVect + resNormal * 5);
					CanvasPosTwo = Canvas.Project(currentPoint);

					Canvas.Draw2DLine(CanvasPosOne.x, CanvasPosOne.y, CanvasPosTwo.x, CanvasPosTwo.y,MyMakeColor(255,0,0,255));
					//DrawDebugLine(resVect + resNormal * 5,currentPoint,0,255,0);

					//Left line of the arrow
					CanvasPosOne = Canvas.Project(resVect - resNormal * 5);
					CanvasPosTwo = Canvas.Project(currentPoint);

					Canvas.Draw2DLine(CanvasPosOne.x, CanvasPosOne.y, CanvasPosTwo.x, CanvasPosTwo.y,MyMakeColor(255,0,0,255));

					//Line connecting the ends of our arrow lines
					//DrawDebugLine(resVect + resNormal * 5,resVect - resNormal * 5,0, 255, 0);
				}
	            lastPoint = currentPoint;
    	    }
	}
}

function DrawGBDebug( GBReplicationInfo GBRI, float ScreenLocX, float ScreenLocY )
{
	local float XL,YL;

	//last GB command
	canvas.SetDrawColor(0,255,255,255);
	canvas.StrLen(GBRI.GetLastGBCommand(), XL, YL);
	ScreenLocY += 50;
	canvas.SetPos(ScreenLocX - 0.5*XL , ScreenLocY - YL);
	canvas.DrawText(GBRI.GetLastGBCommand(),true);

	//last GB path
	//C.StrLen(P.LastGBPath, XL, YL);
	//ScreenLocY += 50;
	//C.SetPos(ScreenLocX - 0.5*XL , ScreenLocY - YL);
	//C.DrawText(P.LastGBPath,true);
}

//Will draw the texture emoticons above character heads
function DrawEmoticons()
{
	local float EmotLength, bubbleSizeX, bubbleSizeY;
	local string emoticon, emoticonLeft, emoticonRight, bubble;
	local GBReplicationInfo MyRepInfo;
	local vector PawnLocation, EmoticonLocation, CanvasEmoticonLocation, CanvasBarEndLocation;
	local int bubbleSize;
	local bool bLeft, bCenter, bRight;
	local int i;

    //MyRepInfo = GetNextRepInfoForEmoticons(none);

    repInfoList.Remove(0, repInfoList.Length);
	foreach DynamicActors(Class'GBReplicationInfo', MyRepInfo)
	{
		repInfoList[repInfoList.Length] = MyRepInfo;
	}
	for (i=0;i<repInfoList.Length; i++) {
		MyRepInfo = GetNextRepInfoForEmoticons();
		if (MyRepInfo == none)
			continue;

		PawnLocation = MyRepInfo.GetLocation();
		EmoticonLocation = PawnLocation;
		EmoticonLocation.z += 130;
		EmoticonLocation.z += MyRepInfo.GetEmoticonOffset(); //adding custom offset

        emoticon = MyRepInfo.GetEmoticonCenter();
        emoticonLeft = MyRepInfo.GetEmoticonLeft();
        emoticonRight = MyRepInfo.GetEmoticonRight();
        bubble = MyRepInfo.GetEmoticonBubbleType();

        if (MyRepInfo.GetEmoticonCenterSize() == 0)
        	emoticon = "";
        if (MyRepInfo.GetEmoticonLeftSize() == 0)
        	emoticonLeft = "";
        if (MyRepInfo.GetEmoticonRightSize() == 0)
        	emoticonRight = "";
        if (MyRepInfo.GetEmoticonBubbleSize() == 0)
        	bubble = "";

        if ((emoticon == "") && (emoticonLeft == "") && (emoticonRight == "")) {
	        continue;
	        //emoticon = "205_rovnase";
    	    //emoticonLeft = "013_cizipanacekacizipanenka";
        	//emoticonRight = "098_siluetapanaeckapanenka";
		}

		CanvasEmoticonLocation = Canvas.Project(EmoticonLocation);

		//emoticon will be max 100 ut units big
		//We want to scale the emoticon according to the distance
		EmoticonLocation.z -= 120;
		CanvasBarEndLocation = Canvas.Project(EmoticonLocation);
		EmotLength = Abs(CanvasEmoticonLocation.y - CanvasBarEndLocation.y);

		if (EmotLength > 120)
			EmotLength = 120;
		else if (EmotLength < 10)
			EmotLength = 10;

		Canvas.SetDrawColor(255, 255, 255);

        bubbleSize = 0;
		if (emoticonLeft != "") {
			bLeft = true;
			bubbleSize++;
		}
		if (emoticon != "") {
			bCenter = true;
			bubbleSize++;
		}
		if (emoticonRight != "") {
			bRight = true;
			bubbleSize++;
		}
		if (bubble != "") {
			if (bubbleSize == 1) {
				bubbleSizeX = 1.8 * EmotLength;
				bubbleSizeY = 1.8 * EmotLength;
			} else if (bubbleSize == 2) {
				bubbleSizeX = 3 * EmotLength;
				bubbleSizeY = 1.8 * EmotLength;
			} else if (bubbleSize == 3) {
				bubbleSizeX = 4.5 * EmotLength;
				bubbleSizeY = 1.8 * EmotLength;
			}

        	if (bubbleSize > 0) {
				Canvas.SetPos(CanvasEmoticonLocation.x - bubbleSizeX /2, CanvasEmoticonLocation.y  - bubbleSizeY * 0.7);
				Canvas.DrawRect(bubbleSizeX, bubbleSizeY, Texture2D(DynamicLoadObject("Emoticons.Textures." $ bubble, class'Texture2D')));				

			}
		}
		// we are showing two emoticons!
		if (bLeft && !bCenter && bRight) {
			Canvas.SetPos(CanvasEmoticonLocation.X - 0.5 * EmotLength - (EmotLength / 2), CanvasEmoticonLocation.Y - EmotLength);
			Canvas.DrawRect(EmotLength,EmotLength, Texture2D(DynamicLoadObject("Emoticons.Textures." $ emoticonLeft, class'Texture2D')));
			//Canvas.DrawTexture(Texture2D(DynamicLoadObject("Emoticons.Textures." $ emoticonLeft, class'Texture2D')),EmotLength);

			Canvas.SetPos(CanvasEmoticonLocation.X - 0.5 * EmotLength + (EmotLength / 2), CanvasEmoticonLocation.Y - EmotLength);
			Canvas.DrawRect(EmotLength,EmotLength,Texture2D(DynamicLoadObject("Emoticons.Textures." $ emoticonRight, class'Texture2D')));
			//Canvas.DrawTexture(Texture2D(DynamicLoadObject("Emoticons.Textures." $ emoticonRight, class'Texture2D')),EmotLength);
		} else {
			if (bLeft) {
				Canvas.SetPos(CanvasEmoticonLocation.X - 0.5 * EmotLength - EmotLength, CanvasEmoticonLocation.Y - EmotLength);
				Canvas.DrawRect(EmotLength,EmotLength, Texture2D(DynamicLoadObject("Emoticons." $ emoticonLeft, class'Texture2D')));
				//Canvas.DrawTexture(Texture2D(DynamicLoadObject("Emoticons.Textures." $ emoticonLeft, class'Texture2D')),EmotLength);
			}
			if (bCenter) {
				Canvas.SetPos(CanvasEmoticonLocation.X - 0.5 * EmotLength, CanvasEmoticonLocation.Y - EmotLength);
				Canvas.DrawRect(EmotLength,EmotLength, Texture2D(DynamicLoadObject("Emoticons." $ emoticon, class'Texture2D')));
				//Canvas.DrawTexture(Texture2D(DynamicLoadObject("Emoticons.Textures." $ emoticon, class'Texture2D')),EmotLength);
			}
			if (bRight) {
				Canvas.SetPos(CanvasEmoticonLocation.X - 0.5 * EmotLength + EmotLength, CanvasEmoticonLocation.Y - EmotLength);
				Canvas.DrawRect(EmotLength,EmotLength, Texture2D(DynamicLoadObject("Emoticons." $ emoticonRight, class'Texture2D')));
				//Canvas.DrawTexture(Texture2D(DynamicLoadObject("Emoticons.Textures." $ emoticonRight, class'Texture2D')),EmotLength);
			}
		}
	}
}

/**
* Returns next closest GBReplicationInfo to the player that is visible and in the field of view based on the previous
* provided.
*/
function GBReplicationInfo GetNextRepInfoForEmoticons() {
	local GBReplicationInfo resultInfo, MyRepInfo;
	local float resultDistance, tempDistance;
	local int i, resultIndex;

	resultInfo = none;
	resultDistance = 0;

	for (i=0;i<repInfoList.Length; i++)
	{
		MyRepInfo = repInfoList[i];
		if (
			MyRepInfo == none 
			||
			(MyRepInfo.GetMyPawn() == none)
			||
			!InFOV(MyRepInfo.GetLocation(), PlayerOwner.GetFOVAngle(), ViewLocation, ViewRotation)
			||
			!HUDLoS(MyRepInfo.GetMyPawn(),ViewLocation) 
		)  
		{
			repInfoList[i] = none;
			continue;
		}
		tempDistance = VSize(MyRepInfo.GetLocation() - ViewLocation);
		if (tempDistance >= resultDistance) {
			resultInfo = MyRepInfo;
			resultDistance = tempDistance;
			resultIndex = i;
		}
	}

	if (resultInfo != none)
		repInfoList[resultIndex] = none;

	return resultInfo;
}

//Will draw the bots current health ammount using text and health bar
function DrawHealthBar(Pawn P, int health, float ScreenLocX, float ScreenLocY)
{
	//local texture HealthTex;
	local vector PawnLocation, CanvasBarEndLocation, CanvasHealthTexLocation;
	local float BarLength;

	PawnLocation = P.Location;
	//We will draw HealthTex a little bit higher
	PawnLocation.z += 20;
    CanvasHealthTexLocation.X = ScreenLocX;
	CanvasHealthTexLocation.Y = ScreenLocY;
	Canvas.SetPos(CanvasHealthTexLocation.x, CanvasHealthTexLocation.y - 10);
	Canvas.SetDrawColor(155,0,0,255);
	Canvas.DrawText(health $ "%",true);

    //Health bar will be 100 ut units big
    //We want to scale the bar according to the distance
	PawnLocation.z -= 100;
	CanvasBarEndLocation = Canvas.Project(PawnLocation);
	BarLength = CanvasBarEndLocation.y - CanvasHealthTexLocation.y;

	Canvas.SetPos( CanvasHealthTexLocation.x, CanvasHealthTexLocation.y );

	//First we will draw white bar showing 100 health
	//,MyMakeColor(255,255,255,255)
	Canvas.SetDrawColor(255,255,255,255);
	Canvas.DrawTile(Canvas.DefaultTexture, 6, BarLength, 0, 0, Canvas.DefaultTexture.SizeX, Canvas.DefaultTexture.SizeY );	

	//Then prepare everything for the second red health bar
	Canvas.SetPos( CanvasHealthTexLocation.x,  CanvasHealthTexLocation.y );

	//We have to do this so (division later in DrawTile fc) because we loose
	//everything behind . because of replication (all floats truncated)
	BarLength = health * BarLength;
	//,MyMakeColor(155,0,0,255)
	Canvas.SetDrawColor(155,0,0,255);
	if (health > 0)
		Canvas.DrawTile(Canvas.DefaultTexture, 6, BarLength / 100, 0, 0, Canvas.DefaultTexture.SizeX, Canvas.DefaultTexture.SizeY );
}

function DrawPlayerDebug()
{
	local GBReplicationInfo MyRepInfo;
	//some needed vectors
	local vector PawnVelocity, PawnPosition;
	//some vectors for canvas position counting
	local vector CanvasPawnPosition, CanvasPawnFocus;

	//local vector CanvasPosOne, CanvasPosTwo;
	local vector CanvasPosTwo;
	local string PlayerName, FocusName;
	local rotator fovLimit, PawnRotation;
	local float XL, YL;

	foreach DynamicActors(Class'GBReplicationInfo', MyRepInfo) {

		//We want to show here just relevant information
		if (!MyRepInfo.PawnIsNone() && MyRepInfo.getMyPawn() != PlayerOwner.Pawn) {
			/*if ( (PlayerOwner.ViewTarget != none) && (PlayerOwner.ViewTarget.Controller != none) && (PlayerOwner.ViewTarget.Controller.PlayerReplicationInfo != none ) && (PlayerOwner.ViewTarget.Controller.PlayerReplicationInfo == MyRepInfo.MyPRI) )
			{
				continue;
			}*/
			PawnPosition = MyRepInfo.GetLocation();
			if (InFOV(PawnPosition, PlayerOwner.GetFOVAngle(), ViewLocation, ViewRotation)) {

				PlayerName = MyRepInfo.GetHumanReadableName();

				PawnVelocity = MyRepInfo.GetVelocity();

				//Need to draw the name and distance properly
				
                CanvasPawnPosition = Canvas.Project(PawnPosition);

				//Add information about distance and PlayerName
				if (MyRepInfo.Team != none) {
					if (MyRepInfo.GetTeamNum() == 0)
						Canvas.SetDrawColor(200,55,55,255); // red
					else
						Canvas.SetDrawColor(55,55,200,255); //blue
				} else
					Canvas.SetDrawColor(200,55,55,255); // red

				PlayerName = VSize(PawnPosition - ViewLocation) $ " " $ PlayerName;
				Canvas.StrLen(PlayerName, XL, YL);
				Canvas.SetPos(CanvasPawnPosition.x - 0.5*XL , CanvasPawnPosition.y - YL);
				Canvas.DrawText(PlayerName,true);

				/*
				if (MyRepInfo.MyPRI.bHasFlag) {
					Canvas.SetDrawColor(255,255,255,255); //white
					Canvas.SetPos(CanvasPawnPosition.x - 0.5 * XL - 10, CanvasPawnPosition.y - YL - 5);
                	Canvas.DrawBox(XL + 10,YL + 5);
                }
				*/
				//draw velocity line
				//if (InFOV(PawnPosition + PawnVelocity, PlayerOwner.GetFOVAngle(), ViewLocation, ViewRotation))
				//{
					CanvasPosTwo = Canvas.Project(PawnPosition + PawnVelocity);
					Canvas.Draw2DLine(CanvasPawnPosition.x, CanvasPawnPosition.y, CanvasPosTwo.x, CanvasPosTwo.y,MyMakeColor(255,0,0,255));
				//}
                //DrawDebugLine(PawnPosition, PawnPosition + PawnVelocity, 255,0,0);

				//Draw3DLine(PawnPosition,PawnPosition + MyRepInfo.GetVelocity(),class'Canvas'.Static.MakeColor(255,0,0));


				
				if (DisplayPlayerPositions >= 2)
				{
					//DrawFocus				
					CanvasPawnFocus = Canvas.Project(MyRepInfo.GetFocus());
					FocusName = MyRepInfo.GetFocusName();

					//if (InFOV(MyRepInfo.GetFocus(), PlayerOwner.GetFOVAngle(), ViewLocation, ViewRotation))
					//{
						CanvasPosTwo = Canvas.Project(MyRepInfo.GetFocus());

						Canvas.Draw2DLine(CanvasPawnPosition.x, CanvasPawnPosition.y, CanvasPosTwo.x, CanvasPosTwo.y,MyMakeColor(255,255,255,255));
					//}
        	        //DrawDebugLine(PawnPosition, MyRepInfo.GetFocus(),255,255,255);

            	    if (FocusName != "")
                	{
	                	Canvas.StrLen(FocusName, XL, YL);
						Canvas.SetPos(CanvasPawnFocus.x - 0.5*XL , CanvasPawnFocus.y - YL);
						Canvas.DrawText(FocusName,true);
					}

					//DrawFOV - approx right now
					fovLimit.pitch = 0;					
					fovLimit.Yaw = ((acos(MyRepInfo.getMyPawn().PeripheralVision) * 57.2957795) * 182.1);
					fovLimit.Roll = 0;

					PawnRotation = MyRepInfo.GetRotation();

					//First FOV line
					//if (InFOV(PawnPosition + vector(PawnRotation - fovLimit) * 300, PlayerOwner.GetFOVAngle(), ViewLocation, ViewRotation))
					//{
						CanvasPosTwo = Canvas.Project(PawnPosition + vector(PawnRotation - fovLimit) * 300);

						Canvas.Draw2DLine(CanvasPawnPosition.x, CanvasPawnPosition.y, CanvasPosTwo.x, CanvasPosTwo.y,MyMakeColor(255,255,0,255));
					//}
	       	        //DrawDebugLine(PawnPosition, PawnPosition + vector(PawnRotation - fovLimit) * 300, 255,255,0);

					//Second FOV line
					//if (InFOV(PawnPosition + vector(PawnRotation + fovLimit) * 300, PlayerOwner.GetFOVAngle(), ViewLocation, ViewRotation))
					//{
						CanvasPosTwo = Canvas.Project(PawnPosition + vector(PawnRotation + fovLimit) * 300);

						Canvas.Draw2DLine(CanvasPawnPosition.x, CanvasPawnPosition.y, CanvasPosTwo.x, CanvasPosTwo.y,MyMakeColor(255,255,0,255));
					//}
	       	        //DrawDebugLine(PawnPosition, PawnPosition + vector(PawnRotation + fovLimit) * 300, 255,255,0);
				}				
			}
		}
	}
}


/* 
 * Will go through all GBReplicationInfo classes and will display the information
	about players currently on the server.
*/
function DrawPlayerList(out float ScreenLocX, out float ScreenLocY)
{
	local GBReplicationInfo MyRepInfo;

	Canvas.setDrawColor(DefaultColorR,DefaultColorG,DefaultcolorB,DefaultColorA);
    Canvas.SetPos(ScreenLocX, ScreenLocY);
	Canvas.DrawText("Player List: ",true);

	foreach DynamicActors(Class'GBReplicationInfo', MyRepInfo)
	{
		//We want to show here just relevant information
		if (!MyRepInfo.PawnIsNone())
		{
			ScreenLocY += shift;
			Canvas.SetPos(ScreenLocX, ScreenLocY);
			Canvas.DrawText("Name: "$MyRepInfo.GetHumanReadableName() $
				" Location: "$MyRepInfo.GetLocation() $
				" Rotation: "$MyRepInfo.GetRotation() $
				" Velocity: "$MyRepInfo.GetVelocity(),true);
		}
	}

	ScreenLocY += shift;
}

//Will display Help (keys for GB and/or HUD control)
function DrawHelp(out float ScreenLocX, out float ScreenLocY)
{
	Canvas.SetDrawColor(EnabledColorR,EnabledColorG,EnabledColorB,EnabledColorA);
	Canvas.SetPos(ScreenLocX, ScreenLocY);
	Canvas.DrawText("GameBots 2004 HUD Help (Red features are off, green on):",true);
	ScreenLocY += shift;
	
	Canvas.SetPos(ScreenLocX, ScreenLocY);
	Canvas.DrawText("ALT + H - Enables/Disables this help",true);
	ScreenLocY += shift;

    if (bDisplayInformation)
	    Canvas.SetDrawColor(EnabledColorR,EnabledColorG,EnabledColorB,EnabledColorA);
	else
		Canvas.SetDrawColor(DisabledColorR,DisabledColorG,DisabledColorB,DisabledColorA);
	Canvas.SetPos(ScreenLocX, ScreenLocY);
	Canvas.DrawText("ALT + I - Enables/Disables additional info (about reachability GRID, etCanvas.)",true);
	ScreenLocY += shift;

    if (bDisplayMyLocation)
	    Canvas.SetDrawColor(EnabledColorR,EnabledColorG,EnabledColorB,EnabledColorA);
	else
		Canvas.SetDrawColor(DisabledColorR,DisabledColorG,DisabledColorB,DisabledColorA);
    Canvas.SetPos(ScreenLocX, ScreenLocY);
	Canvas.DrawText("ALT + M - Enables/Disables my location and rotation info.",true);
	ScreenLocY += shift;

    if (bDrawNavPointsNames)
	    Canvas.SetDrawColor(EnabledColorR,EnabledColorG,EnabledColorB,EnabledColorA);
	else
		Canvas.SetDrawColor(DisabledColorR,DisabledColorG,DisabledColorB,DisabledColorA);
   	Canvas.SetPos(ScreenLocX, ScreenLocY);
	Canvas.DrawText("ALT + N - Enables/Disables NavPoint names.",true);
	ScreenLocY += shift;

    Canvas.SetDrawColor(EnabledColorR,EnabledColorG,EnabledColorB,EnabledColorA);
   	Canvas.SetPos(ScreenLocX, ScreenLocY);
	Canvas.DrawText("ALT + '[' or ']' - Incerase/Decrease drawing range (" $ NavPointBeaconDrawDistance $ ")of NavPoint names.",true);
	ScreenLocY += shift;

    if (bDisplayNavCubes)
    {
	    Canvas.SetDrawColor(EnabledColorR,EnabledColorG,EnabledColorB,EnabledColorA);
    }
	else
	{
		Canvas.SetDrawColor(DisabledColorR,DisabledColorG,DisabledColorB,DisabledColorA);
	}
	Canvas.SetPos(ScreenLocX, ScreenLocY);
	Canvas.DrawText("ALT + C - Enables/Disables Navigation Points cubes visualization.",true);
	ScreenLocY += shift;

    if (bDrawNavPointsGrid)
	    Canvas.SetDrawColor(EnabledColorR,EnabledColorG,EnabledColorB,EnabledColorA);
	else
		Canvas.SetDrawColor(DisabledColorR,DisabledColorG,DisabledColorB,DisabledColorA);
	Canvas.SetPos(ScreenLocX, ScreenLocY);
	Canvas.DrawText("ALT + G - Enables/Disables reachability GRID.",true);
	ScreenLocY += shift;

    if (bDisplayPlayerList)
	    Canvas.SetDrawColor(EnabledColorR,EnabledColorG,EnabledColorB,EnabledColorA);
	else
		Canvas.SetDrawColor(DisabledColorR,DisabledColorG,DisabledColorB,DisabledColorA);
	Canvas.SetPos(ScreenLocX, ScreenLocY);
	Canvas.DrawText("ALT + L - Enables/Disables Player List.",true);
	ScreenLocY += shift;

    if (DisplayPlayerPositions > 0)
	    Canvas.SetDrawColor(EnabledColorR,EnabledColorG,EnabledColorB,EnabledColorA);
	else
		Canvas.SetDrawColor(DisabledColorR,DisabledColorG,DisabledColorB,DisabledColorA);
	Canvas.SetPos(ScreenLocX, ScreenLocY);
	Canvas.DrawText("ALT + P - Cycles through additional player info modes.",true);
	ScreenLocY += shift;

    if (bDisplayRoute)
	    Canvas.SetDrawColor(EnabledColorR,EnabledColorG,EnabledColorB,EnabledColorA);
	else
		Canvas.SetDrawColor(DisabledColorR,DisabledColorG,DisabledColorB,DisabledColorA);
	Canvas.SetPos(ScreenLocX, ScreenLocY);
	Canvas.DrawText("ALT + R - Enables/Disables route drawing (when spectating the bot)",true);
	ScreenLocY += shift;

    if (bDisplayHealthBar)
	    Canvas.SetDrawColor(EnabledColorR,EnabledColorG,EnabledColorB,EnabledColorA);
	else
		Canvas.SetDrawColor(DisabledColorR,DisabledColorG,DisabledColorB,DisabledColorA);
	Canvas.SetPos(ScreenLocX, ScreenLocY);
	Canvas.DrawText("ALT + B - Enables/Disables HealthBar",true);
	ScreenLocY += shift;

    if (bDisplayTextBubble)
	    Canvas.SetDrawColor(EnabledColorR,EnabledColorG,EnabledColorB,EnabledColorA);
	else
		Canvas.SetDrawColor(DisabledColorR,DisabledColorG,DisabledColorB,DisabledColorA);
	Canvas.SetPos(ScreenLocX, ScreenLocY);
	Canvas.DrawText("ALT + U - Enables/Disables text bubbles",true);
	ScreenLocY += shift;
    
	Canvas.SetDrawColor(EnabledColorR,EnabledColorG,EnabledColorB,EnabledColorA);	
	Canvas.SetPos(ScreenLocX, ScreenLocY);
	Canvas.DrawText("F6 or ALT + S  - Enables/Disables spectator mode",true);
	ScreenLocY += shift;

    if (bDisplayDebug)
	    Canvas.SetDrawColor(EnabledColorR,EnabledColorG,EnabledColorB,EnabledColorA);
	else
		Canvas.SetDrawColor(DisabledColorR,DisabledColorG,DisabledColorB,DisabledColorA);
	Canvas.SetPos(ScreenLocX, ScreenLocY);
	Canvas.DrawText("ALT + D - Enables/Disables debug information",true);
	ScreenLocY += shift;


}

//Will draw our current location, rotation and velocity on the HUD
function DrawMyLocation(out float ScreenLocX, out float ScreenLocY)
{
	local vector PlayerLocation, PlayerVelocity;
	local rotator myRotation;
	local string PlayerRotation;

	Canvas.setDrawColor(DefaultColorR,DefaultColorG,DefaultcolorB,DefaultColorA);
	//If we currently control Pawn, we will take its coordinates
	if (PlayerOwner.Pawn != none) {
		PlayerLocation = PlayerOwner.Pawn.Location;
		myRotation = PlayerOwner.Pawn.Rotation;
		//The ViewPitch is something else then Pawn rotation!!!
		//myRotation.Pitch = int(PlayerOwner.Pawn.ViewPitch) * 65556/255;
		PlayerRotation = string(myRotation);
		PlayerVelocity = PlayerOwner.Pawn.Velocity;
	} else if (PlayerOwner.ViewTarget != none) { //If are spectating someone, we will put his coordinates
		PlayerLocation = PlayerOwner.ViewTarget.Location;
		PlayerRotation = string(PlayerOwner.ViewTarget.Rotation);
		PlayerVelocity = PlayerOwner.ViewTarget.Velocity;
	} else { //Otherwise put coordinates of the Controller class (we are spectating now) don't have the body
		PlayerLocation = PlayerOwner.Location;
		PlayerRotation = string(PlayerOwner.Rotation);
		PlayerVelocity = PlayerOwner.Velocity;
	}
	Canvas.SetPos(ScreenLocX, ScreenLocY);
	Canvas.DrawText("My Location: "$PlayerLocation$" My Rotation:"$PlayerRotation$" My Velocity:"$PlayerVelocity,true);
	ScreenLocY += shift;
}


//Will go through all navigation points in the level and find those we can see
//and display their Ids
function DrawNavPointsNames()
{
	local NavigationPoint N;
	local vector CanvasPosition;
	local Color tmpColor;

	local float XL,YL,floatDist;
	Canvas.setDrawColor(DefaultColorR,DefaultColorG,DefaultcolorB,DefaultColorA);

	foreach WorldInfo.AllNavigationPoints(class'NavigationPoint',N) {
		//distance = N.Location - ViewLocation;
		floatDist = sqrt(square(N.Location.x - ViewLocation.x) + square(N.Location.y - ViewLocation.y) + square(N.Location.z - ViewLocation.z));
		if (floatDist <= NavPointBeaconDrawDistance) {
			if(InFOV(N.Location, PlayerOwner.GetFOVAngle(), ViewLocation, ViewRotation) && PlayerOwner.FastTrace(N.Location, ViewLocation) )
			{
				CanvasPosition = Canvas.Project(N.Location);
				tmpColor = Canvas.DrawColor;

				//This will draw the game Id of the NavPoint
				Canvas.SetDrawColor(255, 255, 255);
				Canvas.StrLen(string(N), XL, YL);

				Canvas.SetPos(CanvasPosition.X - 0.5 * XL - 5, CanvasPosition.Y - YL - 3);
				Canvas.DrawRect(XL + 10, YL + 6, WHITE_BACKGROUND );
				Canvas.SetPos(CanvasPosition.X - 0.5 * XL - 5, CanvasPosition.Y - YL - 3);
				Canvas.SetDrawColor(0, 0, 0);
				Canvas.DrawBox(XL + 11,YL + 7);

				Canvas.SetPos(CanvasPosition.X - 0.5*XL , CanvasPosition.Y - YL);								
				Canvas.SetDrawColor(255, 0, 0, 255);
				Canvas.DrawText(string(N),true);
				Canvas.SetDrawColor(tmpColor.R, tmpColor.G, tmpColor.B, tmpColor.A);
			}
		}
	}
}

//Here we will draw the navigation points GRID in the game by DrawStayingDebugLine function
function DrawNavPointsGrid()
{
	local NavigationPoint N;
	local int i, PathListLength;
	local int bPlayersOnly, bForced, bProscribed, bLadder, bSpecial, bDoor, bJump, bSwim, bFly, bWalk;
	local vector resVect, resNormal;
	local Color myColor;

	foreach WorldInfo.AllNavigationPoints(class'NavigationPoint',N) {
		i = 0;
		PathListLength = N.PathList.Length;
		for (i=0; i < PathListLength; i++) {
			//First we will get the flags of this line between two NavPoints
            GetFlags(N.PathList[i].reachFlags, bPlayersOnly, bForced, bProscribed, bLadder, bSpecial, bDoor, bJump, bSwim, bFly, bWalk);

			//Darker Yellow
			myColor = MyMakeColor(255,255,0,255);

			//Here we will design what color we will use - hierarchical
			if ( (bFly == 1) || (bProscribed == 1) || (bPlayersOnly == 1) ) {
				myColor = MyMakeColor(255,0,0,255); //Red
			} else if ((bJump == 1) && (bSpecial == 1)) {
				myColor = MyMakeColor(255,255,255,255); //White
			} else if ( bSpecial == 1 ) {				
				myColor = MyMakeColor(0,0,255,255); //Blue
			} else if (bJump == 1) {
				myColor = MyMakeColor(255,255,155,255);//Yellow with some blue :-)
			} else if ( bDoor == 1 || bLadder == 1 || bSwim == 1 ) {
				myColor = MyMakeColor(0,0,0,255); //Black
			}
			
			//Draw the line between two NavPoints
			Draw3DLine(N.Location,N.PathList[i].GetEnd().Location,myColor);
			//ViewportOwner.Actor.DrawStayingDebugLine(N.Location,N.PathList[i].End.Location,red,green,blue);

			//We need this, so we can draw the arrow at the end of the line correctly
			resNormal = Normal((N.PathList[i].GetEnd().Location - N.Location) cross vect(0, 0, 1));
			resVect = N.PathList[i].GetEnd().Location - (Normal(N.PathList[i].GetEnd().Location - N.Location) * 14);

			//Right line of the arrow
			Draw3DLine(resVect + resNormal * 5,N.PathList[i].GetEnd().Location,myColor);
			//Left line of the arrow
			Draw3DLine(resVect - resNormal * 5,N.PathList[i].GetEnd().Location,myColor);
			//Line connecting the ends of our arrow lines
			Draw3DLine(resVect + resNormal * 5,resVect - resNormal * 5,myColor);
			//GBHUD(MyHUD).DrawLine(N.Location,N.PathList[i].End.Location);
		}

	}
}

/* ================================================ */
/* HELPER FUNCTIONS */
/* ================================================ */

//Will return the bools from supported flag int
function GetFlags(int flag, out int bPlayerOnly, out int bForced,
out int bProscribed, out int bLadder, out int bSpecial,
out int bDoor, out int bJump, out int bSwim, out int bFly, out int bWalk)
{
	bPlayerOnly = 0;
	bForced = 0;
	bProscribed = 0;
	bLadder = 0;
	bSpecial = 0;
	bDoor = 0;
	bJump = 0;
	bSwim = 0;
	bFly = 0;
	bWalk = 0;

	if ( flag >= 512 ) {
		flag -= 512;
		bPlayerOnly = 1;
	}
    if ( flag >= 256 ) {
		flag -= 256;
		//It is FORCED path
		bForced = 1;
	}
	if ( flag >= 128) {
		flag -= 128;
		bProscribed = 1;
	}
	if ( flag >= 64) {
		flag -= 64;
		bLadder = 1;
	}
	if ( flag >= 32) {
		flag -= 32;
		bSpecial = 1;
	}
	if ( flag >= 16) {
		flag -= 16;
		bDoor = 1;
	}
	if ( flag >= 8) {
		flag -= 8;
		bJump = 1;
	}
	if ( flag >= 4) {
		flag -= 4;
		bSwim = 1;
	}
	if ( flag >= 2) {
		flag -= 2;
		bFly = 1;
	}
	if ( flag >= 1 ) {
		flag -= 1;
		bWalk = 1;
	}
}

function SpawnNavCubes() {
	local NavigationPoint N;
	local PathMarker p;

	foreach WorldInfo.AllNavigationPoints(class'NavigationPoint',N) {
    	if(!N.IsA('PickupFactory')) {
			p = Spawn(class'GameBotsUDK.PathMarker',N,,N.Location);
   			pathMarkers.AddItem(p);
		}
	}
	bNavCubesSpawned = true;
}

function DisplayNavCubes()
{
	local int i;
	if (!bNavCubesSpawned) {
		SpawnNavCubes();
	}
	for (i=0;i<pathMarkers.Length;i++) {
 		if (pathMarkers[i] != none) {
			pathMarkers[i].SetHidden(false);
		}
	}
}

function HideNavCubes() {
	local int i;
	for (i=0;i<pathMarkers.Length;i++) {
 		if (pathMarkers[i] != none) {
			pathMarkers[i].SetHidden(true);
		}
	}
}


/* Returns our current view - camera location, rotation and bool (by int) bIsCamera if we are currently
	spectating and don't have any player selected.
*/
function GetPlayerViewInformation(out vector CameraLocation, out rotator CameraRotation)
{
	if (PlayerOwner.Pawn != none) {
		CameraLocation = PlayerOwner.Pawn.Location;
		CameraRotation = PlayerOwner.Pawn.Rotation;
		//CameraRotation.Pitch = int(PlayerOwner.Pawn.ViewPitch) * 65556/255;
	} else {
		CameraLocation = PlayerOwner.Location;
		CameraRotation = PlayerOwner.Rotation;
	}
}

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

	view = vector(CameraRotation);

	target = loc - CameraLocation;

	return Acos(Normal(view) dot Normal(target)) * 57.2957795 < FovAngle / 2; // Angle between view and target is less than FOV
	// 57.2957795 = 180/pi = 1 radian in degrees  --  convert from radians to degrees
}

//HUD line of sight, but limited to MAX_DISTANCE distance
function bool HUDLoS(Pawn pawn, vector CamLocation) {
	local float distance;

	distance = VSize(Pawn.Location - ViewLocation);//sqrt(square(pawn.Location.x - CamLocation.x) + square(pawn.Location.y - CamLocation.y) + square(pawn.Location.z - CamLocation.z));
	if (distance > MAX_VIEW_DISTANCE)
		return false;
	if (!FastTrace(ViewLocation, Pawn.Location)) //PlayerOwner.LineOfSightTo(pawn))
		return false;

	return true;
}

/**
Checks for registered keys or commands.
*/
function bool ReceivedNativeInputKey(int ControllerId, name InputKey, EInputEvent InputAction, float AmountDepressed, bool bGamepad)
{
	local GBPlayer myPlayer;

	myPlayer = GBPlayer(PlayerOwner);
	

    // don't send mouse events
	if (InStr(InputKey, "Mouse") == -1)
	{
		if ( (InputKey != lastInputKey) || (InputAction != lastInputAction) )
		{
			if ( myPlayer.PlayerReplicationInfo != none ) // player might not be completely loaded yet, happens after ServerTravel()
			{
        		myPlayer.SendKeyEvent(InputKey, InputAction);
			}
        	lastInputKey = InputKey;
        	lastInputAction = InputAction;
        }
	}

	//use for disabling key control.
	if (bDisableKeyControls) {
		return false;
	}

	//`log("Key: " $ InputKey);
	if (InputKey == 'LeftAlt' || InputKey == 'RightAlt')
    	if (InputAction == IE_Pressed)
			bPressedAlt = True;
    	else if (InputAction == IE_Released)
			bPressedAlt = False;

	if (InputAction == IE_Pressed && InputKey == 'F6')
	{
		if (myPlayer.IsSpectating())
			myPlayer.ServerRemoteStopSpectate();				
		else {
			myPlayer.ServerRemoteSpectate();
		}
	}

	if (bPressedAlt && InputAction == IE_Pressed)
	{
		switch (InputKey)
		{
			case 'B':
				bDisplayHealthBar = !bDisplayHealthBar;
			break;
			case 'C':
				bDisplayNavCubes = !bDisplayNavCubes;
				if (bDisplayNavCubes)
					DisplayNavCubes();
				else
					HideNavCubes();
			break;
			case 'D':
				bDisplayDebug = !bDisplayDebug;
			break;
			case 'G':
				bDrawNavPointsGrid = !bDrawNavPointsGrid;
			break;
			case 'H':
    			bDisplayHelp = !bDisplayHelp;
			break;
			case 'I':
				bDisplayInformation = !bDisplayInformation;
			break;
			case 'L':
				bDisplayPlayerList = !bDisplayPlayerList;
			break;
			case 'M':
				bDisplayMyLocation = !bDisplayMyLocation;
			break;
			case 'N':
				bDrawNavPointsNames = !bDrawNavPointsNames;
			break;
			case 'P':
				DisplayPlayerPositions += 1;
				if (DisplayPlayerPositions > 2)
					DisplayPlayerPositions = 0;
			break;
			case 'R':
				bDisplayRoute = !bDisplayRoute;
			break;
			case 'S':
				if (myPlayer.IsSpectating())
					myPlayer.ServerRemoteStopSpectate();				
				else {
					myPlayer.ServerRemoteSpectate();
				}
			break;
			case 'T':
    			//DoTest();
			break;
			case 'U':
				bDisplayTextBubble = !bDisplayTextBubble;
			break;
			case 'LeftBracket':
				if (NavPointBeaconDrawDistance < 4000)
					NavPointBeaconDrawDistance += 100;
			break;
			case 'RightBracket':
				if (NavPointBeaconDrawDistance > 100)
					NavPointBeaconDrawDistance -= 100;
			break;
			case 'LeftShift':
			break;
		}
		return true; //This means that this key combinations won't be parsed by other KeyEvents
	}
	return false;
}

function Color MyMakeColor(int R, int G, int B, int A) {
	local Color myColor;

	myColor.R = R;
	myColor.G = G;
	myColor.B = B;
	myColor.A = A;

	return myColor;
}

//Will draw the text bubble containing the last string the bot was sending to communicate
function DrawTextBubble( Pawn P, float ScreenLocX, float ScreenLocY )
{
	local float XL,YL;
	local float Dist;
	local vector CameraLocation;
	local float HeightOffset;

	if ( P != none && GBPawn(P).bDrawTextBubble && P != PlayerOwner.Pawn && PlayerOwner.CanSee(P) )
	{
		CameraLocation = PlayerOwner.Pawn.Location;
		CameraLocation.Z += 50;

		Dist = VSize(CameraLocation - P.Location);				

		if ( Dist < TextBubbleMaxDist )
		{			
			HeightOffset = 30 + 100 / ( (Dist / 100) ** 1.1);
			canvas.SetDrawColor(255, 255, 255);
			canvas.StrLen(GBPawn(P).TextBubble, XL, YL);
			canvas.SetPos(ScreenLocX - 0.8 * XL, ScreenLocY - 0.7 * YL - HeightOffset); // maybe move it up a bit by a factor of Dist
			canvas.DrawRect(XL * 1.6, YL * 1.4, WHITE_BACKGROUND );

			canvas.SetDrawColor(0, 0, 0);
			canvas.SetPos(ScreenLocX - 0.8 * XL, ScreenLocY - 0.7 * YL - HeightOffset);
			canvas.DrawBox(XL * 1.6 + 2, YL * 1.4 + 2);

			canvas.SetPos(ScreenLocX - 0.5 * XL , ScreenLocY - 0.5 * YL - HeightOffset);
			canvas.DrawText(GBPawn(P).TextBubble, false);
		}
	}
}


DefaultProperties
{
	//enabled feature color
	EnabledColorR=0
	EnabledColorG=155
	EnabledColorB=55
	EnabledColorA=255
	//disabled feature color
	DisabledColorR=155
	DisabledColorG=0
	DisabledColorB=55
	DisabledColorA=255
	//default color
	DefaultColorR=255
	DefaultColorG=255
	DefaultColorB=255
	DefaultColorA=255
	shift=15


	WHITE_BACKGROUND=Texture2D'GameBotsUDK_UI.HUD.WhiteBackground'
}
