//
// Tools_Movement.cpp
//
// Created by Kevin Lea - 05/10/2001 16:53:50
// 
// Copyright (C) 2001 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

#include "Camera/Asura_Camera.h"
#include "Core/Asura_UserInput.h"
#include "Core/Asura_Timers.h"
#include "Core/Asura_HashedList.h"
#include "Debugging/Asura_Assert.h"
#include "Entity/PhysicalObject/Asura_ServerEntity_PhysicalObject.h"
#include "Interface/Asura_Overlay_System.h"
#include "Interface/Asura_Overlay.h"
#include "Interface/Asura_Dialog.h"
#include "Mesh/Asura_Mesh_System.h"
#include "Mesh/Asura_Mesh.h"
#include "Physics/Asura_Physics_MovementQuery.h"
#include "Rendering/Asura_Viewport.h"
#include "Rendering/Asura_Text_Output.h"
#include "Rendering/Asura_Module.h"


#include "Tools_System.h"
#include "Tools_Overlays.h"
#include "Interface/Tools_ModuleSelect_Overlay.h"
#include "Load&Save/Tools_SaveAsura.h"
#include "Load&Save/Tools_ModeDataFile.h"
#include "Misc/Tools_Timed_Messages.h"
#include "Misc/Tools_Help.h"
#include "Misc/Tools_Render_Options.h"
#include "Misc/Tools_Render.h"
#include "Misc/Tools_Modal_Overlay.h"
#include "Misc/Tools_Module_Selection.h"
#include "Misc/Tools_Polygon_Selection.h"
#include "Misc/Tools_LOS.h"
//#include "Movement/Tools_CameraSystem.h"
#include "Movement/Tools_Movement.h"
#include "Objects/Tools_Object_System.h"




//extern Asura_Dynamic_Object* PlayerDynamicObject;

///////////////////////////////////////////////////////////////////////////////////////////////////
/*--------------------------**
** Tools_FindModule_Overlay **
**--------------------------*/


class Tools_FindModule_Overlay : public Tools_ModuleSelect_Overlay
{
	public :
		Tools_FindModule_Overlay(const char* pszTitle);

		virtual bool DoModal();

	protected :
		virtual void AddExtraOverlayControls(class Asura_Overlay* pxOverlay,class Asura_Vector_2& xOffset);

	private :
		static bool s_bSelectModule;
};

bool Tools_FindModule_Overlay::s_bSelectModule = false;


Tools_FindModule_Overlay::Tools_FindModule_Overlay(const char* pszTitle)
:Tools_ModuleSelect_Overlay(pszTitle)
{
}

bool Tools_FindModule_Overlay::DoModal()
{
	if(Tools_ModuleSelect_Overlay::DoModal())
	{
		if(Tools_Module_Selection::IsActive() && s_bSelectModule)
		{
			//select the chosen module int Tools_Module_Selection
			Tools_Module_Selection::RequestSingleSelection(GetSelectionData());
		}
		return true;
	}
	return false;
}

void Tools_FindModule_Overlay::AddExtraOverlayControls(class Asura_Overlay* pxOverlay,class Asura_Vector_2& xOffset)
{
	Tools_ModuleSelect_Overlay::AddExtraOverlayControls(pxOverlay,xOffset);
	
	xOffset.y -= 1.0f;

	Asura_Dialog* pxDialog;

	//If module selection is acitve , add a checkbox for it
	if(Tools_Module_Selection::IsActive())
	{
		pxDialog = new Asura_Tickbox_Dialog("Select Module",xOffset,&s_bSelectModule);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y += 1.5f;
	}

	xOffset.y += 1.0f;

}


///////////////////////////////////////////////////////////////////////////////////////////////////
/*----------------**
** Tools_Movement **
**----------------*/
//static member variables

Asura_Vector_2 Tools_Movement::s_xOptionsOverlayPosition(20.0f,3.0f);

float Tools_Movement::CameraMovementScale = 1.0f;
float Tools_Movement::ObjectMovementScale = 1.0f;
float Tools_Movement::CameraRotationScale = 1.0f;
float Tools_Movement::ObjectRotationScale = 1.0f;
float Tools_Movement::s_fSpeedDisplayTimer = 0.0f;

bool Tools_Movement::AttachCamera = false;
bool Tools_Movement::UseRelativeRotation = false;
bool Tools_Movement::InvertYAxis = false;
bool Tools_Movement::DisableNumberPadControls = false;
//bool Tools_Movement::ThirdPersonCamera = false;
float Tools_Movement::MouseRotationMultiplier = 1.0f;
bool Tools_Movement::s_bObjectUseWorldRelativeMovement = false;
bool Tools_Movement::s_bCameraUseWorldRelativeMovement = false;

Asura_Vector_3* Tools_Movement::SavedPosition = 0;
Asura_Matrix_3x3* Tools_Movement::SavedOrientation = 0;
Asura_Vector_3 Tools_Movement::s_xTeleportPosition(0,0,0);
Asura_Euler_3 Tools_Movement::s_xTeleportOrient(0,0,0);
u_int Tools_Movement::s_uTeleportFlags = ToolsTeleportFlag_MoveCamera|ToolsTeleportFlag_UsePosition|ToolsTeleportFlag_Absolute;
u_int Tools_Movement::s_uTeleportFlagsBackup = Tools_Movement::s_uTeleportFlags;


Tools_Object* Tools_Movement::CurrentObject = NULL;
Tools_Object* Tools_Movement::CameraObject = NULL;

bool Tools_Movement::s_bUseCameraDynamics = false;
bool Tools_Movement::s_bUseObjectDynamics = false;

bool Tools_Movement::s_bUseAltForMouseRotate = true;
bool Tools_Movement::s_bToggleMiddleMouseButton = false;
bool Tools_Movement::s_bUseMouseWheelForSpeed = true;

u_int Tools_Movement::s_uMouseRotationState = 0;

bool Tools_Movement::s_bEnabled = true;


Tools_Movement* Tools_Movement::s_pxThis = 0;

Tools_Movement::Tools_Movement()
{
	Asura_Assert(!s_pxThis,"Tools_Movement already allocated");
	s_pxThis = this;
}

Tools_Movement::~Tools_Movement()
{
	Asura_Assert(s_pxThis,"Tools_Movement already deleted");
	s_pxThis = 0;
}



void Tools_Movement::Initialise()
{
	//create the camera object
	CameraObject = new Tools_Player_Object;

	//initialise saved positions/orientations
	SavedPosition = new Asura_Vector_3[10];
	SavedOrientation = new Asura_Matrix_3x3[10];
	for(int i=0;i<10;i++)
	{
		SavedPosition[i].Zero();
		SavedOrientation[i].Identity();
	}

	LoadCameraPositionFile();

	s_uMouseRotationState = 0;
}

void Tools_Movement::Shutdown()
{
	Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_TELEPORT);

	if(CurrentObject)
	{
		CurrentObject->NoLongerFlying();
		CurrentObject = NULL;
	}
	
	
	delete CameraObject;
	CameraObject = NULL;


	delete [] SavedPosition;
	delete [] SavedOrientation;
}

static const float MAX_MOVEMENT_SPEED = 999.9f;


void Tools_Movement::Update()
{
	Asura_Assert(CameraObject,"Tools_Flying not properly initialised");


	if(!s_bEnabled) return;

	
	float* pMovementScale = NULL;
	float* pRotationScale = NULL;
	if(CurrentObject)
	{
		pMovementScale = &ObjectMovementScale;	
		pRotationScale = &ObjectRotationScale;	
		CurrentObject->UpdateMovement(pMovementScale,pRotationScale);

		if(AttachCamera)
		{
			//update the camera to be in the same position
			CameraObject->Position = CurrentObject->Position;
			CameraObject->Orientation = CurrentObject->Orientation;	
			CameraObject->UpdatePositionInAsuraData();
		}
	}
	else
	{
		pMovementScale = &CameraMovementScale;	
		pRotationScale = &CameraRotationScale;	
		//move the camera if no other object was specified
		CameraObject->UpdateMovement(pMovementScale,pRotationScale);

//		if(ThirdPersonCamera)
//		{
//			Tools_CameraSystem::Update();
//		}
	}


	//check for changes in movement speed
	{		  

		if(!Asura_Overlay_System::OverlaysHaveKeyboardFocus())
		{
			//alter rotation or movement depending on whether shift is held
			float * pScale = Asura_UserInput::KeyPressed(ASURA_KEYID_SHIFT) ? pRotationScale : pMovementScale;
			
			int iSign = 0;
			if (Asura_UserInput::DebouncedKeyPressed('['))
			{
				iSign = -1;
			}
			else if (Asura_UserInput::DebouncedKeyPressed(']'))
			{
				iSign = 1;
			}
			
			//Allow the mousewheel to be used for changing speed as well.
			//The mousewheel always seems to be in use for a minimum of two frames at a time , so
			//limit use to every other frame.
			//RAR 21/09/2006
			static bool s_bUsedMouseWheelLastFrame = false;
			if (s_bUsedMouseWheelLastFrame)
			{
				s_bUsedMouseWheelLastFrame = false;
			}
			else if(!Asura_Overlay_System::CursorIsOverAnOverlay())
			{
				if(s_bUseMouseWheelForSpeed)
				{
					if(Asura_UserInput::KeyPressed(ASURA_KEYID_MOUSEWHEELDOWN))
					{
						iSign = -1;
						s_bUsedMouseWheelLastFrame = true;
					}
					else if(Asura_UserInput::KeyPressed(ASURA_KEYID_MOUSEWHEELUP))
					{
						iSign = 1;
						s_bUsedMouseWheelLastFrame = true;
					}
				}
			}

            if( iSign )
            {
                int iChangeRate = Asura_UserInput::KeyPressed(ASURA_KEYID_ALT) ? 10 : 1;
				s_fSpeedDisplayTimer = 1.0f;
                for(int i=0;i<iChangeRate;i++)
				{
					if(iSign>0)
					{
						if(*pScale >= 1.0f)
						{
							*pScale += 1.0f;
						}
						else if(*pScale >= 0.1f)
						{
							*pScale += 0.1f;
						}
						else if(*pScale >= 0.01f)
						{
							*pScale += 0.01f;
						}				 
						else if(*pScale >= 0.001f)
						{
							*pScale += 0.001f;
						}
						else if(*pScale >= 0.0001f)
						{
							*pScale += 0.0001f;
						}
					}
					else
					{
						if(*pScale > 1.0f)
						{
							*pScale -= 1.0f;
						}
						else if(*pScale > 0.1f)
						{
							*pScale -= 0.1f;
						}
						else if(*pScale > 0.01f)
						{
							*pScale -= 0.01f;
						}
						else if(*pScale > 0.001f)
						{
							*pScale -= 0.001f;
						}
						else if(*pScale > 0.0001f)
						{
							*pScale -= 0.0001f;
						}
					}
				
					// Clamp.
					if( *pScale < 0.0001f )
					{
						*pScale = 0.0001f;
					}
					else if( *pScale < 0.001f )
					{
						*pScale = Asura_Maths::FloatToInt_Near(*pScale * 10000)/10000.0f;
					}
					else if( *pScale < 0.01f )
					{
						*pScale = Asura_Maths::FloatToInt_Near(*pScale * 1000)/1000.0f;
					}
					else if( *pScale < 0.1f )
					{
						*pScale = Asura_Maths::FloatToInt_Near(*pScale * 100)/100.0f;
					}
					else if( *pScale < 1.0f )
					{
						*pScale = Asura_Maths::FloatToInt_Near(*pScale * 10)/10.0f;
					}
					else if(*pScale < MAX_MOVEMENT_SPEED)
					{
						*pScale = (float)Asura_Maths::FloatToInt_Near(*pScale);
					}
					else
					{
						*pScale = MAX_MOVEMENT_SPEED;
					}
				}
            }

			if(!Asura_UserInput::KeyPressed(ASURA_KEYID_CTRL) && !Asura_UserInput::KeyPressed(ASURA_KEYID_SHIFT) && !Asura_UserInput::KeyPressed(ASURA_KEYID_ALT))
			{
				if (Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_F9))
				{
					ToggleUseDynamics();
				}
			}

		}
	}



	//check for some movement option key thingies being pressed
	if(!Asura_UserInput::KeyPressed(ASURA_KEYID_ALT) && !Asura_UserInput::KeyPressed(ASURA_KEYID_CTRL) && !Asura_UserInput::KeyPressed(ASURA_KEYID_SHIFT))
	{
		if (Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_F10))
		{
			UseRelativeRotation = !UseRelativeRotation;
			Tools_Timed_Message_System::AddMessage(0xffffff,"Using %s relative rotation.",UseRelativeRotation ? "Camera" : "World");
		}
	}

	if(Asura_UserInput::KeyPressed(ASURA_KEYID_ALT) && !Asura_UserInput::KeyPressed(ASURA_KEYID_CTRL) && !Asura_UserInput::KeyPressed(ASURA_KEYID_SHIFT))
	{
		if (Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_F10))
		{
			ToggleUseWorldRelativeMovement();
			Tools_Timed_Message_System::AddMessage(0xffffff,"Using %s relative movement.",IsUsingWorldRelativeMovement() ? "World" : "Camera");
		}
	}


	if(CurrentObject)
	{
		//check for attaching camera to the flying object
		if(!Asura_Overlay_System::OverlaysHaveKeyboardFocus())
		{
			if(!Asura_UserInput::KeyPressed(ASURA_KEYID_ALT) && !Asura_UserInput::KeyPressed(ASURA_KEYID_CTRL) && !Asura_UserInput::KeyPressed(ASURA_KEYID_SHIFT))
			{
				if (Asura_UserInput::DebouncedKeyPressed('c'))
				{
					AttachCamera = !AttachCamera;
	
					Tools_Timed_Message_System::AddMessage(0xffffff,"Camera %s.",AttachCamera ? "Attached" : "Detached");

				}
			}
		}
	}

	//saving/restoring camera positions
	if(!Asura_UserInput::KeyPressed(ASURA_KEYID_ALT))
	{
		if(Asura_UserInput::KeyPressed(ASURA_KEYID_SHIFT))
		{
			for(int key = ASURA_KEYID_F1;key<=ASURA_KEYID_F10;key++)
			{
				if (Asura_UserInput::DebouncedKeyPressed(key))
				{
					SaveCameraPosition(key-ASURA_KEYID_F1);
				}
			}
		}
		else if(Asura_UserInput::KeyPressed(ASURA_KEYID_CTRL))
		{
			for(int key = ASURA_KEYID_F1;key<=ASURA_KEYID_F10;key++)
			{
				if (Asura_UserInput::DebouncedKeyPressed(key))
				{
					RestoreCameraPosition(key-ASURA_KEYID_F1);
				}
			}
			if (Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_F12))
			{
				//jump to origin
				Asura_Vector_3 position(0,0,0);
				Asura_Matrix_3x3 orientation;
				orientation.Identity();

				TeleportCameraToPosition(&position,&orientation);

				Tools_Timed_Message_System::AddMessage(0xffffff,"Jumped to origin");
				
			}

			if(Asura_UserInput::DebouncedKeyPressed('f'))
			{
				FindModule(); // Ctrl+F
			}

		}
		else
		{
			if (Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_F12))
			{
				CreateTeleportOverlay();
			}
		}
	}

	UpdateTeleportOverlaySettings();

}

void Tools_Movement::Render()
{
	if (0)
	{
		// (AW - 01.10.2003) - Code to render tools dynamic object.
		//Tools_Render::RenderCube(PlayerDynamicObject->GetBoundingBox(), Asura_Maths::ZeroVector3, Asura_Maths::IdentityMatrix3x3);
	}

	float* pMovementScale = NULL;
	float* pRotationScale = NULL;
	if(CurrentObject)
	{
		pMovementScale = &ObjectMovementScale;	
		pRotationScale = &ObjectRotationScale;	
	}
	else
	{
		pMovementScale = &CameraMovementScale;	
		pRotationScale = &CameraRotationScale;	
	}

	if (s_fSpeedDisplayTimer>0.0f)
	{
		unsigned int a = (Asura_Maths::FloatToInt(s_fSpeedDisplayTimer*255.0f)<<24)+0xffffff;
		s_fSpeedDisplayTimer-=Asura_Timers::GetFrameTime();
		Asura_Text_Output::Print(((float)Asura_Viewport::GetWidth())/20.0f-0.5f,((float)Asura_Viewport::GetHeight())/28.0f-0.5f,a,ASURA_TEXT_FORMAT_CENTREJUSTIFIED,"MovementScale %0.4f",*pMovementScale);
		Asura_Text_Output::Print(((float)Asura_Viewport::GetWidth())/20.0f-0.5f,((float)Asura_Viewport::GetHeight())/28.0f+0.5f,a,ASURA_TEXT_FORMAT_CENTREJUSTIFIED,"RotationScale %0.4f",*pRotationScale);
	}
}

void Tools_Movement::SetFlyingObject(Tools_Object* NewObject)
{
	//tell the current object that it is being chucked out
	if(CurrentObject)
	{
		CurrentObject->NoLongerFlying();
	}

	//reset object dynamics and AttachCamera whenever we change object
	if(NewObject != CurrentObject)
	{
		AttachCamera = false;
		s_bUseObjectDynamics = false;
	}

	//and replace with the new one
	CurrentObject = NewObject;
}


void Tools_Movement::SaveCameraPosition(int index)
{
	Asura_Assert(index>=0 && index<10,"");


	SavedPosition[index] = CameraObject->Position;
	SavedOrientation[index] = CameraObject->Orientation;

	SaveCameraPositionFile();

	Tools_Timed_Message_System::AddMessage(0xffffff,"Camera position %d saved",index+1);
}

void Tools_Movement::RestoreCameraPosition(int index)
{
	Asura_Assert(index>=0 && index<10,"");


	TeleportCameraToPosition(&SavedPosition[index],&SavedOrientation[index]);
	
	Tools_Timed_Message_System::AddMessage(0xffffff,"Camera position %d restored",index+1);
}

void Tools_Movement::TeleportCameraToPosition( const Asura_Vector_3* pxPos, const Asura_Matrix_3x3* pxOri )
{
	s_pxThis->TeleportCameraAndOrObject(pxPos,pxOri,ToolsTeleportFlag_MoveCameraAndObjectIfAttached);
}

void Tools_Movement::TeleportCameraAndOrObject(const Asura_Vector_3* pxPos, const Asura_Matrix_3x3* pxOri,u_int uFlags)
{
	if(uFlags & ToolsTeleportFlag_MoveCameraAndObjectIfAttached)
	{
		uFlags |= ToolsTeleportFlag_MoveCamera;
		if(CurrentObject && AttachCamera) uFlags |= ToolsTeleportFlag_MoveSelected;
	}


	if(uFlags & ToolsTeleportFlag_MoveCamera)
	{
		//move the camera
		//position
		if(uFlags & ToolsTeleportFlag_RelativeLocal)
		{
			if(pxPos) CameraObject->Position += *pxPos * CameraObject->Orientation;
		}
		else if(uFlags & ToolsTeleportFlag_RelativeWorld)
		{
			if(pxPos) CameraObject->Position += *pxPos;
		}
		else
		{
			if(pxPos) CameraObject->Position = *pxPos;
		}

		//and orientation
		if(uFlags & ToolsTeleportFlag_RelativeLocal)
		{
			if(pxOri) CameraObject->Orientation =  CameraObject->Orientation * (*pxOri);
		}
		else if(uFlags & ToolsTeleportFlag_RelativeWorld)
		{
			if(pxOri) CameraObject->Orientation =  (*pxOri) * CameraObject->Orientation;
		}
		else
		{
			if(pxOri) CameraObject->Orientation = *pxOri;
		}

		CameraObject->UpdatePositionInAsuraData();
	}

	if(uFlags & ToolsTeleportFlag_MoveSelected)
	{
		Tools_Object* pxObject  = Tools_Object_System::SelectedObject;
		
		if(pxObject)
		{
			Tools_Object_System::UpdateRelativePositions();
			
			//move the the selected object
			if(uFlags & ToolsTeleportFlag_RelativeLocal)
			{
				if(pxPos) pxObject->Position += *pxPos * pxObject->Orientation;
			}
			else if(uFlags & ToolsTeleportFlag_RelativeWorld)
			{
				if(pxPos) pxObject->Position += *pxPos;
			}
			else
			{
				if(pxPos) pxObject->Position = *pxPos;
			}

			//and orientation
			if(uFlags & ToolsTeleportFlag_RelativeLocal)
			{
				if(pxOri) pxObject->Orientation =  pxObject->Orientation * (*pxOri);
			}
			else if(uFlags & ToolsTeleportFlag_RelativeWorld)
			{
				if(pxOri) pxObject->Orientation =  (*pxOri) * pxObject->Orientation;
			}
			else
			{
				if(pxOri) pxObject->Orientation = *pxOri;
			}
			pxObject->UpdatePositionInAsuraData();

			Tools_Object_System::UpdateGroupMovement();

		}
	}
}

void Tools_Movement::CreateTeleportOverlay()
{

	if(Asura_Overlay_System::DoesOverlayExist(TOOLS_OVERLAY_TELEPORT))
	{
		Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_TELEPORT);
		return;
	}

	Asura_Overlay* pxOverlay;
	pxOverlay = new Asura_Overlay("Teleport To Position",false);
	Asura_Vector_2 xOffset = Asura_Vector_2(1.0f,3.0f);

	s_pxThis->AddTeleportOverlayControls(pxOverlay,xOffset);


	pxOverlay->m_uID = TOOLS_OVERLAY_TELEPORT;

	Asura_Overlay_System::SubmitNewOverlay(pxOverlay);
		
}

void Tools_Movement::AddTeleportOverlayControls(Asura_Overlay* pxOverlay,Asura_Vector_2& xOffset)
{
	
	Asura_Dialog* pxDialog;

	//position settings
	pxDialog = new Asura_FlagTickbox_Dialog("Use Position :",xOffset,&s_uTeleportFlags,ToolsTeleportFlag_UsePosition);
	pxDialog->SetHelpText("The position will be altered using the coordinates below.\nIf unticked then the position of camera/object will be unchanged.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();
	
	pxDialog = new Asura_Numerical_Dialog("X",xOffset,&s_xTeleportPosition.x,-1000000,1000000,1,"%.3f");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Numerical_Dialog("Y",xOffset,&s_xTeleportPosition.y,-1000000,1000000,1,"%.3f");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Numerical_Dialog("Z",xOffset,&s_xTeleportPosition.z,-1000000,1000000,1,"%.3f");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::LargeSpacing();


	//orientation settings
	xOffset.x = 20.0f;
	xOffset.y = 3.0f;

	pxDialog = new Asura_FlagTickbox_Dialog("Use Orientation :",xOffset,&s_uTeleportFlags,ToolsTeleportFlag_UseOrient);
	pxDialog->SetHelpText("The orientation will be altered using the angles below.\nIf unticked then the orientation of camera/object will be unchanged.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();
	
	pxDialog = new Asura_AngleNumerical_Dialog("X (Pitch)",xOffset,&s_xTeleportOrient.x,-360,360,1,"%.3f");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_AngleNumerical_Dialog("Y (Yaw)",xOffset,&s_xTeleportOrient.y,-360,360,1,"%.3f");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_AngleNumerical_Dialog("Z (Roll)",xOffset,&s_xTeleportOrient.z,-360,360,1,"%.3f");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::LargeSpacing();

	xOffset.x = 1.0f;


	pxDialog = new Asura_FlagTickbox_Dialog("Absolute",xOffset,&s_uTeleportFlags,ToolsTeleportFlag_Absolute);
	pxDialog->SetHelpText("The position/orientation will be used as world space coordinates.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_FlagTickbox_Dialog("Relative (Local)",xOffset,&s_uTeleportFlags,ToolsTeleportFlag_RelativeLocal);
	pxDialog->SetHelpText("The position/orientation will be used as a movement relative to the camera/object.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_FlagTickbox_Dialog("Relative (World)",xOffset,&s_uTeleportFlags,ToolsTeleportFlag_RelativeWorld);
	pxDialog->SetHelpText("The position/orientation will be used as a movement in world space.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::LargeSpacing();


	
	pxDialog = new Asura_FlagTickbox_Dialog("Move Camera",xOffset,&s_uTeleportFlags,ToolsTeleportFlag_MoveCamera);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_FlagTickbox_Dialog("Move Selected",xOffset,&s_uTeleportFlags,ToolsTeleportFlag_MoveSelected);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::LargeSpacing();


	pxDialog = new Asura_Function_Dialog("Apply",xOffset,DoTeleport);
	pxDialog->SetHelpText("Applies the settings , keeping the overlay open");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Apply And Close",xOffset,DoTeleport, &pxOverlay->m_bShouldClose);
	pxDialog->SetHelpText("Applies the settings and then closes this overlay");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();
}

void Tools_Movement::DoTeleport()
{
	UpdateTeleportOverlaySettings();

	Asura_Matrix_3x3 xOrient;
	s_xTeleportOrient.ToMatrix(xOrient);

	Asura_Vector_3* pxPosition = (s_uTeleportFlags & ToolsTeleportFlag_UsePosition) ? &s_xTeleportPosition : 0;
	Asura_Matrix_3x3* pxOrient = (s_uTeleportFlags & ToolsTeleportFlag_UseOrient) ? &xOrient : 0;


	s_pxThis->TeleportCameraAndOrObject(pxPosition,pxOrient,s_uTeleportFlags | ToolsTeleportFlag_FromOverlay);
}

void Tools_Movement::UpdateTeleportOverlaySettings()
{

	if(s_uTeleportFlags != s_uTeleportFlagsBackup)
	{
		//see if any of the absolute/relative flags have changed
		const u_int uMask = ToolsTeleportFlag_Absolute|ToolsTeleportFlag_RelativeLocal|ToolsTeleportFlag_RelativeWorld;
		u_int uChangedFlags = (s_uTeleportFlagsBackup^s_uTeleportFlags)& uMask;

		
		//whichever flag was changed should be on , the rest should be off
		if(uChangedFlags)
		{
			s_uTeleportFlags &=~ uMask;
			if(!(s_uTeleportFlags & uMask))
			{
				if(uChangedFlags & ToolsTeleportFlag_RelativeLocal) s_uTeleportFlags |= ToolsTeleportFlag_RelativeLocal;
				else if(uChangedFlags & ToolsTeleportFlag_RelativeWorld) s_uTeleportFlags |= ToolsTeleportFlag_RelativeWorld;
				else  s_uTeleportFlags |= ToolsTeleportFlag_Absolute;
			}
		}

		s_uTeleportFlagsBackup = s_uTeleportFlags;
			
	}
}

extern Asura_String EnvironmentFilename;
void Tools_Movement::LoadCameraPositionFile()
{
	char filename[MAX_PATH];
	//determine the filename for the .cam file
	strcpy(filename,Tools_SaveAsura::GetMainFilename());
	char* dotPos = strrchr(filename,'.');
	if(dotPos) *dotPos = 0;
	strcat(filename,".cam");

	//open the file
	HANDLE handle = CreateFile(filename,GENERIC_READ,0,0,OPEN_EXISTING,0,0);
	if(handle==INVALID_HANDLE_VALUE) return;

	unsigned long bytes_transferred;

	//read in the saved positions
	ReadFile(handle,SavedPosition,10*sizeof(Asura_Vector_3),&bytes_transferred,0);
	ReadFile(handle,SavedOrientation,10*sizeof(Asura_Matrix_3x3),&bytes_transferred,0);

	CloseHandle(handle);
}

void Tools_Movement::SaveCameraPositionFile()
{
	char filename[MAX_PATH];
	//determine the filename for the .cam file
	strcpy(filename,Tools_SaveAsura::GetMainFilename());
	char* dotPos = strrchr(filename,'.');
	if(dotPos) *dotPos = 0;
	strcat(filename,".cam");

	//open the file
	HANDLE handle = CreateFile(filename,GENERIC_WRITE,0,0,CREATE_ALWAYS,0,0);
	if(handle==INVALID_HANDLE_VALUE)
	{
		Tools_Timed_Message_System::AddMessage(0xffff00,"Failed to write %s",filename);
		return;
		
	}

	unsigned long bytes_transferred;

	//save the saved positions
	WriteFile(handle,SavedPosition,10*sizeof(Asura_Vector_3),&bytes_transferred,0);
	WriteFile(handle,SavedOrientation,10*sizeof(Asura_Matrix_3x3),&bytes_transferred,0);

	CloseHandle(handle);

	return;
}


void Tools_Movement::FindModule()
{
	if(Asura_Overlay_System::AreAnyOverlaysAlwaysOnTop())
	{
		//Abort if another always on top overlay has already been created
		//(In case this gets called during the modal overlay update loop).
		return;
	}
	
	Tools_FindModule_Overlay xOverlay("Find Module");

	if(!xOverlay.DoModal()) return;

	//goto the selected module
	int iModuleIndex = xOverlay.GetSelectionData();
	Asura_Vector_3 xPosition = Asura_Module_System::GetCentreOfModule(iModuleIndex);

	TeleportCameraToPosition(&xPosition,0);
}

bool Tools_Movement::IsUsingDynamics()
{
	return CurrentObject ? s_bUseObjectDynamics : s_bUseCameraDynamics;	
}

void Tools_Movement::ToggleUseDynamics()
{
	if(CurrentObject)
	{
		s_bUseObjectDynamics = !s_bUseObjectDynamics;
	}
	else
	{
		s_bUseCameraDynamics = !s_bUseCameraDynamics;
	}
}

bool Tools_Movement::IsUsingWorldRelativeMovement()
{
	//World relative movement just supported for objects at the mome
	return CurrentObject ? s_bObjectUseWorldRelativeMovement : s_bCameraUseWorldRelativeMovement;
}

void Tools_Movement::ToggleUseWorldRelativeMovement()
{
	if(CurrentObject)
	{
		s_bObjectUseWorldRelativeMovement = !s_bObjectUseWorldRelativeMovement;
	}
	else
	{
		s_bCameraUseWorldRelativeMovement = !s_bCameraUseWorldRelativeMovement;
	}
}


void Tools_Movement::AddOverlayControls(class Asura_Overlay* pxOverlay, Asura_Vector_2& xOffset)
{
	Asura_Dialog* pxDialog;
	pxDialog = new Asura_Tickbox_Dialog("Invert Y Axis",xOffset,&InvertYAxis);
	pxDialog->SetHelpText("Invert Y axis of mouse for rotation");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("MMB Is Toggled",xOffset,&s_bToggleMiddleMouseButton);
	pxDialog->SetHelpText("If enabled then pressing MMB will toggle mouse rotation on and off.\nIf not enabled then mouse rotation is enabled while the MMB is held down.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();


	pxDialog = new Asura_Tickbox_Dialog("Alt Mouse Rotation",xOffset,&s_bUseAltForMouseRotate);
	pxDialog->SetHelpText("If enabled , then holding down the Alt key will cause mouse movement to rotate the camera.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();


	pxDialog = new Asura_Numerical_Dialog("Mouse Rotation Multiplier",xOffset,&MouseRotationMultiplier,0.0f,99.0f,1.0f,"%.2f");
	pxDialog->SetHelpText("Changes the rotation rate when using the mouse.\nNote that mouse rotation is also affected by the rotation speed setting below.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("Camera Relative Rotation",xOffset,&UseRelativeRotation);
	pxDialog->SetHelpText("If ticked then rotation is done relative to the camera/object being moved.\nOtherwise rotation is relative to teh world axes.\nShorcut key : F10");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("Enable mouse wheel for speed",xOffset,&s_bUseMouseWheelForSpeed);
	pxDialog->SetHelpText("Determines whether using the mouse wheel should cause the movement speed to change");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	xOffset.y += 1.0f;

	//add tab for camera/object options
	static char* s_apszTabs[2] = {"Camera","Object"};
	Asura_Tabs_Dialog* pxTabsDialog = new Asura_Tabs_Dialog(xOffset,s_apszTabs,2);
	pxOverlay->AddDialog(pxTabsDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();

	float fTabsStartY = xOffset.y;
	float fTabsEndY = xOffset.y;

	//Add camera options
	pxTabsDialog->SelectTab(0);

	pxDialog = new Asura_Numerical_Dialog("Movement Speed",xOffset,&CameraMovementScale,0.0f,MAX_MOVEMENT_SPEED,1.0f,"%.4f");
	pxDialog->SetHelpText("Movement speed multiplier when moveing the camera (1 is equivalent to 4 m/s)\nShorcut keys : [ or ] while flying camera");
	pxTabsDialog->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Numerical_Dialog("Rotation Speed",xOffset,&CameraRotationScale,0.0f,MAX_MOVEMENT_SPEED,1.0f,"%.4f");
	pxDialog->SetHelpText("Rotation speed multiplier when moveing the camera \nShorcut keys : Shift + [ or ] while flying camera");
	pxTabsDialog->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("World Relative Movement",xOffset,&s_bCameraUseWorldRelativeMovement);
	pxDialog->SetHelpText("If checked then the direction keys move the camera relative to the world axes.\nOtherwise the keys are relative to the camera's facing.\nShorcut key : Alt+F10 while flying camera");
	pxTabsDialog->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();


	pxDialog = new Asura_Tickbox_Dialog("Dynamics",xOffset,&s_bUseCameraDynamics);
	pxDialog->SetHelpText("Enables collisions/gravity for the camera.\nShorcut key : F9 while flying camera.");
	pxTabsDialog->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();


	if(xOffset.y > fTabsEndY) fTabsEndY = xOffset.y;

	//Add object options
	pxTabsDialog->SelectTab(1);
	xOffset.y = fTabsStartY;

	pxDialog = new Asura_Numerical_Dialog("Movement Speed",xOffset,&ObjectMovementScale,0.0f,MAX_MOVEMENT_SPEED,1.0f,"%.4f");
	pxDialog->SetHelpText("Movement speed multiplier when moveing an object (1 is equivalent to 4 m/s)\nShorcut keys : [ or ] while flying an object");
	pxTabsDialog->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Numerical_Dialog("Rotation Speed",xOffset,&ObjectRotationScale,0.0f,MAX_MOVEMENT_SPEED,1.0f,"%.4f");
	pxDialog->SetHelpText("Rotation speed multiplier when moveing an object \nShorcut keys : Shift + [ or ] while flying an object");
	pxTabsDialog->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("World Relative Movement",xOffset,&s_bObjectUseWorldRelativeMovement);
	pxDialog->SetHelpText("If checked then the direction keys move the object relative to the world axes.\nOtherwise the keys are relative to the object's facing.\nShorcut key : Alt+F10 while flying an object");
	pxTabsDialog->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("Dynamics",xOffset,&s_bUseObjectDynamics);
	pxDialog->SetHelpText("Enables collisions/gravity for object movement.\nThis is automatically disabled when object flying is first switched on\nShorcut key : F9 while flying an object.");
	pxTabsDialog->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("Attach Camera",xOffset,&AttachCamera);
	pxDialog->SetHelpText("Attaches the camera to the object being flown.\nThis is automatically disabled when object flying is first switched on.\nShortcut key : C while flying an object.");
	pxTabsDialog->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	if(xOffset.y > fTabsEndY) fTabsEndY = xOffset.y;

	
	xOffset.y = fTabsEndY + 1.0f;

	pxDialog = new Asura_Function_Dialog("Find Module",xOffset,FindModule);
	pxDialog->SetHelpText("Moves the camera to a module chosen from list of module names.\nShortcut key : Ctrl+F");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Teleport",xOffset,CreateTeleportOverlay);
	pxDialog->SetHelpText("Teleports the camera/object to a specified position.\nShortcut key : F12");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();


	//select tab according to whetherobject/camera currently being moved
	pxTabsDialog->SelectTab(CurrentObject ? 1 : 0);
}

void Tools_Movement::LoadSettingsFromIniFile()
{
	TransferModeData(TOOLSMODEDATAFILE_LOAD);
}

void Tools_Movement::SaveSettingsToIniFile()
{
	TransferModeData(TOOLSMODEDATAFILE_SAVE);
}

void Tools_Movement::TransferModeData(int iLoadOrSave)
{
	//(Most of the movement options are saved to a text ini file instead... just keeping the overlay
	//position here for now , since it isn't intended to be edited by hand).
	
	//open the file
	Tools_ModeDataFile xFile("Movement.dat",iLoadOrSave);

	//tranfer the data
	//(none of these should be removed , and new data should go on the end, otherwise
	//the files will get screwed up)
	if( false == xFile.GetError())
	{
		xFile.Transfer(s_xOptionsOverlayPosition);
	}
}

const Asura_Vector_3& Tools_Movement::GetOrbitCentre()
{
	static u_int s_uFrameStamp = 0xffffffff;
	static Asura_Vector_3 s_xLastOrbitCentre(0.0f,0.0f,0.0f);

	//if position has been got recently , keep it
	u_int uFrame = Asura_Timers::GetFrameNumber();
	if(uFrame == s_uFrameStamp || uFrame == s_uFrameStamp+1)
	{
		s_uFrameStamp = uFrame;
		return s_xLastOrbitCentre;
	}

	s_uFrameStamp = uFrame;

	//if object selection is enabled , take the position from the selected object
	if(Tools_Object_System::AreControlsEnabled())
	{
		if(Tools_Object_System::SelectedObject)
		{
			s_xLastOrbitCentre = Tools_Object_System::SelectedObject->Position;
			return s_xLastOrbitCentre;
		}		
	}
	//check module selection
	if(Tools_Module_Selection::IsActive() && Tools_Module_Selection::GetNumSelected())
	{
		s_xLastOrbitCentre = Tools_Module_Selection::GetCentreOfSelection();	
		return s_xLastOrbitCentre;
	}

	//check polygon selection
	if(Tools_Polygon_Selection::IsActive() && Tools_Polygon_Selection::NumSelectedModules)
	{
		s_xLastOrbitCentre = Tools_Polygon_Selection::GetCentreOfSelection();	
		return s_xLastOrbitCentre;
	}

	//get rotation position by doing an LOS in front then
	int iJunkPoly,iJunkModule;
	if(Tools_LOS::FindPolyAndPositionUnderCursor(iJunkPoly,iJunkModule,s_xLastOrbitCentre))
	{
		return s_xLastOrbitCentre;
	}

	//if all else fails , just take the world centre
	s_xLastOrbitCentre.Zero();
	return s_xLastOrbitCentre;

}

void Tools_Movement::CancelToggledMMB()
{
	s_uMouseRotationState &= ~1;
}

void Tools_Movement::CancelToggledAlt()
{
	s_uMouseRotationState &= ~4;
}

void Tools_Movement::UpdateMouseRotationState()
{
	if(s_bEnabled)
	{
		//Check for mouse rotation via mouse button 3
		if(!Asura_Overlay_System::CursorIsOverAnOverlay())
		{
			if(s_bToggleMiddleMouseButton)
			{
				if(Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_MOUSEBUTTON3))
				{
					s_uMouseRotationState^=1;
				}
			}
			else
			{
				if(Asura_UserInput::KeyPressed(ASURA_KEYID_MOUSEBUTTON3))
				{
					s_uMouseRotationState|=1;
				}
				else
				{
					s_uMouseRotationState&=~1;
				}
			}
		}
		else
		{
			//Disable rotation via MMB if the cursor is over an overlay ,to prevent it from being stuck on
			s_uMouseRotationState&=~1;
		}
		//button 4 used for same purpose , but not toggled regardless (since also used for fast movement)
		if(Asura_UserInput::KeyPressed(ASURA_KEYID_MOUSEBUTTON4))
		{
			s_uMouseRotationState|=2;
		}
		else
		{
			s_uMouseRotationState&=~2;
		}

		//check alt key
		//(disabled during snap to vertex because of clash of key useage)
		if(!Asura_Overlay_System::CursorIsOverAnOverlay() && IsUsingAltForMouseRotate() && Asura_UserInput::KeyPressed(ASURA_KEYID_ALT) && !Tools_Object_System::IsDoingSnapToVertex())
		{
			s_uMouseRotationState|=4;
		}
		else
		{
			s_uMouseRotationState&=~4;
		}
	}
	else
	{
		s_uMouseRotationState = 0;
	}
}



////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------**
** Tools_Player_Object **
**---------------------*/



Tools_Player_Object::Tools_Player_Object()
{
	Type = TOT_Camera;
	Asura_String mesh_name = "ToolsShape_CutsceneActor";
	Mesh = Asura_Mesh_System::GetMesh(mesh_name);

//	if(!Tools_Movement::ThirdPersonCamera)
	{
		//mesh starts hidden
		HiddenFlags |= ObjectHideFlag_MovementSystem;
	}

	Position = *Asura_Camera::GetPosition();
	Orientation = *Asura_Camera::GetOrientation();
	m_fHeight = Tools_Render_Options::GetPlayerHeight();
}

void Tools_Player_Object::UpdatePositionFromAsuraData()
{
//	if(!Tools_Movement::ThirdPersonCamera)
	{
		Position = *Asura_Camera::GetPosition();
		Orientation = *Asura_Camera::GetOrientation();
	}
}

void Tools_Player_Object::UpdatePositionInAsuraData(bool /*updateLinkedObjects*/)
{

//	if(!Tools_Movement::ThirdPersonCamera)
	{
		Asura_Camera::SetOrientation(Orientation);
		Asura_Camera::SetPosition(Position);
	}
}



void Tools_Player_Object::UpdateMovement(float* pMovementScale,float* pRotationScale)
{
    //PlayerDynamicObject->ResetNumCollisionPolysLastFrame();

	if(Tools_Movement::IsUsingDynamics())
	{
		//move using dynamics
		UpdatePositionFromAsuraData();
		
		//get the rotation for this frame	
		Asura_Euler_3 EulerRotate = GetRotationForFrame();
		
		if(EulerRotate.x || EulerRotate.y || EulerRotate.z)
		{
			Asura_Euler_3 EulerOrient;
			EulerOrient.FromMatrix(Orientation);
		
			//add on the pitch and yaw (ignore roll)
			EulerOrient.x += EulerRotate.x * *pRotationScale;
			EulerOrient.y += EulerRotate.y * *pRotationScale;
			EulerOrient.z = 0;

	
			if(EulerOrient.x<-Asura_Maths::Deg90) EulerOrient.x = -Asura_Maths::Deg90;
			else if (EulerOrient.x>Asura_Maths::Deg90) EulerOrient.x = Asura_Maths::Deg90;
		
			EulerOrient.ToMatrix(Orientation);
		}
				

		Asura_Vector_3 delta,deltah;
 		//get the leveled out orientation for picking the forward and right directions
 		Asura_Euler_3 xEuler;
 		Asura_Matrix_3x3 xLevelOrient;
 		xEuler.FromMatrix(Orientation);
 		xEuler.x = 0;
 		xEuler.z = 0;
 		xEuler.ToMatrix(xLevelOrient);

		if(Tools_Movement::IsUsingWorldRelativeMovement())
		{
			delta = Asura_Vector_3(0,0,1);
			deltah = Asura_Vector_3(1,0,0);
		}
		else
		{

			
			delta = Asura_Vector_3(xLevelOrient.e13,
								  0,
								  xLevelOrient.e33);

			deltah = Asura_Vector_3(xLevelOrient.e11,
								   0,
								   xLevelOrient.e31);
		}

		delta *= *pMovementScale;
		deltah *= *pMovementScale;


		Asura_Vector_3 xLinVelocity(0.0f, 0.0f, 0.0f);

		if(!Asura_Overlay_System::OverlaysHaveKeyboardFocus())
		{

			if(!Asura_UserInput::KeyPressed(ASURA_KEYID_CTRL))
			{
/*
				if (Asura_UserInput::KeyPressed('w'))
				{
					xLinVelocity += delta*4.0f;
				}
*/
				if (Asura_UserInput::KeyPressed('s'))
				{
					xLinVelocity -= delta*4.0f;
				}
				if (Asura_UserInput::KeyPressed('a'))
				{
					xLinVelocity -= deltah*4.0f;
				}
				if (Asura_UserInput::KeyPressed('d'))
				{
					xLinVelocity += deltah*4.0f;
				}
/*
				#if 01
				if (Asura_UserInput::KeyPressed('q'))
				{
					xLinVelocity += delta*8.0f;//double speed
				}
				#endif
*/
				if (Asura_UserInput::KeyPressed('e'))
				{
					xLinVelocity.y = -4.0f;
				}
			
				if(!Asura_UserInput::KeyPressed(ASURA_KEYID_SHIFT))
				{
#if 01
					if (Asura_UserInput::KeyPressed('q'))
					{
						xLinVelocity += delta*8.0f;//double speed
					}
#endif
					if (Asura_UserInput::KeyPressed('w'))
					{
						xLinVelocity += delta*4.0f;
					}
				}

				if(!Asura_UserInput::KeyPressed(ASURA_KEYID_SHIFT) && !Asura_UserInput::KeyPressed(ASURA_KEYID_ALT))
				{
					if(!Tools_Help::IsDisplayingHelp())
					{

						//cursor key controls
						if (Asura_UserInput::KeyPressed(ASURA_KEYID_LEFT))
						{
							xLinVelocity -= deltah*4.0f;
						}
						if (Asura_UserInput::KeyPressed(ASURA_KEYID_RIGHT))
						{
							xLinVelocity += deltah*4.0f;
						}
						if (Asura_UserInput::KeyPressed(ASURA_KEYID_UP))
						{
							xLinVelocity.y = -4.0f;
						}


					}
				}
			}
			if(!Tools_Movement::DisableNumberPadControls)
			{
				if (Asura_UserInput::KeyPressed(ASURA_KEYID_NUMPAD1))
				{
					xLinVelocity -= deltah*4.0f;
				}
				if (Asura_UserInput::KeyPressed(ASURA_KEYID_NUMPAD3))
				{
					xLinVelocity += deltah*4.0f;
				}
				
				if(Asura_UserInput::KeyPressed(ASURA_KEYID_SHIFT))
				{
					if (Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_NUMPADSUB))
					{
						Tools_Render_Options::MoveTowardsStandPosture();
					}
					if (Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_NUMPADADD))
					{
						Tools_Render_Options::MoveTowardsPronePosture();
					}

				}
				else
				{
					if (Asura_UserInput::KeyPressed(ASURA_KEYID_NUMPADSUB))
					{
						xLinVelocity.y = -4.0f;
					}
				}
			}
		}


		float fHeight = Tools_Render_Options::GetPlayerHeight();
		if(fHeight != m_fHeight)
		{
			//height has changed... move the camera up or down to track it
			Position.y += m_fHeight - fHeight;
		}

		if(xLinVelocity.y)
		{
			m_fVerticalSpeed = xLinVelocity.y;
		}
		else
		{
			m_fVerticalSpeed += 10.0f * Asura_Timers::GetFrameTime();
			m_fVerticalSpeed = Asura_Maths::Min(m_fVerticalSpeed, 10.0f);
			xLinVelocity.y = m_fVerticalSpeed;
		}

		Asura_Vector_3 xDisplacement = xLinVelocity*Asura_Timers::GetFrameTime();
		Asura_Bounding_Box xBB;
		xBB.MinX = xBB.MinZ = -0.3f;
		xBB.MaxX = xBB.MaxZ = 0.3f;
		xBB.MinY = -fHeight;
		xBB.MaxY = 0.0f;
		Asura_OBB xOBB = Asura_OBB(xBB/*, Asura_AMatrix3x3(xLevelOrient)*/);
		
		//put the position on the ground for the physics test
		Position.y += fHeight;

		Asura_Physics_MovementQuery xInput(Asura_AVector3(Position), Asura_AVector3(xDisplacement), xOBB);
		xInput.SetStopAtFirstCollision(false);
		xInput.SetCollideWithStaticObjects(true);
		xInput.SetCollideWithExternalControlObjects(true);
		xInput.SetCollideWithActorObjects(true);
		xInput.SetCollideWithDynamicObjects(true);
		Asura_Physics_MovementQuery_Results xResults(xInput);
	

		Position = xResults.GetEndPosition();
		Position.y -= fHeight;

		Asura_AVector3 xCollisionNormal = xResults.GetLastCollisionNormal();
		if(xCollisionNormal.m_fY < -0.5f)
		{
			m_fVerticalSpeed = 0.0f;
		}

		//if(Tools_Movement::ThirdPersonCamera)
		//{
		//	Position.y +=Tools_Render_Options::GetPlayerHeight();
		//}
	
		if(!Asura_Overlay_System::OverlaysHaveKeyboardFocus())
		{
			if(Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_NUMPADMULTIPLY))
			{
				bool bAxisSnap = false;
				
				if(Asura_UserInput::KeyPressed(ASURA_KEYID_ALT))
				{
					//remove roll component
					Asura_Euler_3 xEulerOrient;
					xEulerOrient.FromMatrix(Orientation);
					xEulerOrient.z = 0;
					xEulerOrient.ToMatrix(Orientation);
				}
				else if(Asura_UserInput::KeyPressed(ASURA_KEYID_SHIFT))
				{
					SnapToNearestAxes(false);
					bAxisSnap = true;
				}
				else if(Asura_UserInput::KeyPressed(ASURA_KEYID_CTRL))
				{
					SnapTo45DegreesFromAxes(false);
					bAxisSnap = true;

				}
				else
				{
					//snap to level (remove roll and pitch)
					SnapToLevel(false);
				}
				if(bAxisSnap)
				{
					static float s_fLastAxisSnapTime = 0.0;
					//if axis snap is done repeatedly in a short time , rotate by 90 degrees
					float fTimer = Asura_Timers::GetLocalUnscaledTime();

					if(fTimer - s_fLastAxisSnapTime < 1.0f)
					{
						Asura_Matrix_3x3 xMat90(0,0,1,
												0,1,0,
												-1,0,0);

						Orientation = Orientation * xMat90;
					}
					s_fLastAxisSnapTime = fTimer;
				}

			}
		}
	}
	else
	{
		m_fVerticalSpeed = 0.0f;
		//call the default update function
		Tools_Object::UpdateMovement(pMovementScale,pRotationScale);
	}
	m_fHeight = Tools_Render_Options::GetPlayerHeight();
	UpdatePositionInAsuraData(false);
}

void Tools_Player_Object::Render()
{
//	if(Tools_Movement::ThirdPersonCamera)
//	{
//		//Offset the position down to the ground temporarily for rendering the third person tools object
//		Position.y += m_fHeight;
//		Tools_Object::Render();
//		Position.y -= m_fHeight;
//	}
}

