//////////////////////////////////////////////////////////////////////////////
//
// Tools_Entity_PhysicsObject.cpp
//
// Copyright (c) 2006 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header.
#include "Entity/Tools_Entity_PhysicsObject.h"

// Engine independent.
#include "Animation/Asura_Hierarchy.h"
#include "Camera/Asura_Camera.h"
#include "ChunkSystem/Asura_Chunk_Loaders.h"
#include "Core/Asura_TextureManagement.h"
#include "Core/Asura_Timers.h"
#include "Entity/Asura_Entity_Classification.h"
#include "Entity/PhysicalObject/Asura_ServerEntity_PhysicsObject.h"
#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Overlay.h"
#include "Mesh/Asura_Mesh.h"
#include "Mesh/Asura_Mesh_System.h"
#include "Network/Asura_Network.h"
#include "Rendering/Asura_Text_Output.h"
#include "SpecialFX/Asura_Skybox.h"

// Tools.
#include "Entity/Tools_Entity_Data.h"
#ifndef ASURA_TARGET_SM3
#include "Entity/Tools_Entity_DestructibleLight.h"
#endif // #ifndef ASURA_TARGET_SM3
#include "Hierarchy/Tools_Animation_Files.h"
#include "Load&Save/Tools_SaveAsura.h"
#include "Misc/Tools_Timed_Messages.h"
#include "Objects/Tools_Object_System.h"
#include "Objects/Tools_ObjectSystem_Param.h"
#include "Shapes/Tools_Shapes.h"

// C O N S T A N T S /////////////////////////////////////////////////////////

TOOLS_DECLARE_SHARED_PARAM( bool, uTOOLS_SHAREDPARAMHASH_PHYSICSOBJECT_ANIM_LOOPING,       PhysicsObject_Looping             )
TOOLS_DECLARE_SHARED_PARAM( bool, uTOOLS_SHAREDPARAMHASH_PHYSICSOBJECT_ANIM_REVERSED,      PhysicsObject_Reversed            )
TOOLS_DECLARE_SHARED_PARAM( bool, uTOOLS_SHAREDPARAMHASH_PHYSICSOBJECT_USE_ANIM_MOVEMENT,  PhysicsObject_UseAnimMovement     )
TOOLS_DECLARE_SHARED_PARAM( bool, uTOOLS_SHAREDPARAMHASH_PHYSICSOBJECT_NOSHADOW,           PhysicsObject_NoShadow            )
TOOLS_DECLARE_SHARED_PARAM( bool, uTOOLS_SHAREDPARAMHASH_PHYSICSOBJECT_NOCOLLISIONS,       PhysicsObject_NoCollisions        )
TOOLS_DECLARE_SHARED_PARAM( bool, uTOOLS_SHAREDPARAMHASH_PHYSICSOBJECT_IGNOREMODLINKING,   PhysicsObject_IgnoreModuleLinking )
TOOLS_DECLARE_SHARED_PARAM( bool, uTOOLS_SHAREDPARAMHASH_PHYSICSOBJECT_HIDDEN,             PhysicsObject_Hidden              )
TOOLS_DECLARE_SHARED_PARAM( bool, uTOOLS_SHAREDPARAMHASH_PHYSICSOBJECT_IGNOREFORLOS,       PhysicsObject_IgnoreForLOS        )
TOOLS_DECLARE_SHARED_PARAM( bool, uTOOLS_SHAREDPARAMHASH_PHYSICSOBJECT_FULLBRIGHT,         PhysicsObject_FullBright          )
TOOLS_DECLARE_SHARED_PARAM( bool, uTOOLS_SHAREDPARAMHASH_PHYSICSOBJECT_BLOCKWAYPOINTLINKS, PhysicsObject_BlockWaypointLinks  )
TOOLS_DECLARE_SHARED_PARAM( bool, uTOOLS_SHAREDPARAMHASH_PHYSICSOBJECT_BLOCKNAVDATAGENERATION, PhysicsObject_BlockNavDataGeneration  )

TOOLS_DECLARE_SHARED_PARAM( u_int, uTOOLS_SHAREDPARAMHASH_PHYSICSOBJECT_COLLISIONPROXY,      PhysicsObject_CollisionProxy      )

TOOLS_DECLARE_SHARED_PARAM( float, uTOOLS_SHAREDPARAMHASH_PHYSICSOBJECT_GRAVITYSCALE,      PhysicsObject_GravityScale        )
TOOLS_DECLARE_SHARED_PARAM( float, uTOOLS_SHAREDPARAMHASH_PHYSICSOBJECT_ELASTICITY,        PhysicsObject_Elasticity          )
TOOLS_DECLARE_SHARED_PARAM( float, uTOOLS_SHAREDPARAMHASH_PHYSICSOBJECT_FRICTION,          PhysicsObject_Friction            )
TOOLS_DECLARE_SHARED_PARAM( float, uTOOLS_SHAREDPARAMHASH_PHYSICSOBJECT_DRAG,              PhysicsObject_Drag                )
TOOLS_DECLARE_SHARED_PARAM( float, uTOOLS_SHAREDPARAMHASH_PHYSICSOBJECT_ANGULARDRAG,       PhysicsObject_AngularDrag         )
TOOLS_DECLARE_SHARED_PARAM( float, uTOOLS_SHAREDPARAMHASH_PHYSICSOBJECT_MASS,              PhysicsObject_Mass                )

// S T A T I C   V A R I A B L E S 
static const char* g_aszCollisionProxies[Asura_Physics_Object::NUM_COLLISION_PROXIES] =
{
	"OBB",
	"Sphere",
	"Sphere-Stack",
	"Polygonal",
};

// C L A S S   I M P L E M E N T A T I O N S /////////////////////////////////

//
// Tools_Entity_PhysicsObject
//

const char* const Tools_Entity_PhysicsObject::s_szLongName  = "Physics object";
const char* const Tools_Entity_PhysicsObject::s_szShortName = "Physics";
Tools_Entity_PhysicsObject* Tools_Entity_PhysicsObject::s_pxCurrentlySelectedObject = NULL;
Asura_Hashed_List<Tools_Entity_PhysicsObject> Tools_Entity_PhysicsObject::s_xList( 5 );

Tools_Entity_PhysicsObject::Tools_Entity_PhysicsObject()
: PARENT(NULL) // ignored
{
	Type = TOT_Entity_PhysicsObject;

	if (m_pxPhysicalObject == NULL)
	{
		m_iAnimFlags		= AsuraAnimationFlag_Loop;
		m_iCollisionProxy	= Asura_Physics_Object::COLLISION_PROXY_OBB;
		m_fGravityScale		= 1.000f;
		m_fElasticity		= 0.200f;
		m_fFriction			= 1.000f;
		m_fDrag				= 0.001f;
		m_fAngularDrag		= 0.01f;
		m_fMass				= 1.000f;
	}
	else
	{
		const float	fInvMass = m_pxPhysicalObject->GetInvMass( false );

		m_iAnimFlags		= m_pxPhysicalObject->GetAnimFlags();
		m_iCollisionProxy	= m_pxPhysicalObject->GetCollisionProxy();
		m_fGravityScale		= m_pxPhysicalObject->GetGravityScale();
		m_fElasticity		= m_pxPhysicalObject->GetElasticity();
		m_fFriction			= m_pxPhysicalObject->GetFriction();
		m_fDrag				= m_pxPhysicalObject->GetDrag();
		m_fAngularDrag		= m_pxPhysicalObject->GetAngularDrag();
		m_fMass				= (fInvMass == 0.0f) ? 1.0f : (1.0f / fInvMass);

		s_xList.AddToList(this);

		//If using a default name , make sure this is named appropriately. RAR 18/09/2006
		UpdateName();
		UpdateAnim();
	}
}

void Tools_Entity_PhysicsObject::UpdateAnim()
{
	if (m_pxPhysicalObject->GetSkinID() != ASURA_HASH_ID_UNSET)
	{
		Asura_Animation* pxAnimation = GetAnimation();
		if(pxAnimation)
		{
			pxAnimation->m_xPrimaryWrapper.StartAnim2(m_pxPhysicalObject->GetAnimID(),AsuraAnimationFlag_Loop | m_iAnimFlags);
		}
	}
}

Tools_Entity_PhysicsObject::~Tools_Entity_PhysicsObject()
{
	s_xList.RemoveFromList(this);
}


void Tools_Entity_PhysicsObject::OnSelection()
{
	s_pxCurrentlySelectedObject = this;
    PARENT::OnSelection();
}


void Tools_Entity_PhysicsObject::OnDeselection()
{
    PARENT::OnDeselection();
    s_pxCurrentlySelectedObject = NULL;
}


void Tools_Entity_PhysicsObject::UpdateWhileSelected()
{
	PARENT::UpdateWhileSelected();
    if(Changed)
	{

		if (m_pxPhysicalObject)
		{
			m_pxPhysicalObject->SetAnimFlags(m_iAnimFlags);
			m_pxPhysicalObject->SetGravityScale(m_fGravityScale);
			m_pxPhysicalObject->SetElasticity(m_fElasticity);
			m_pxPhysicalObject->SetFriction(m_fFriction);
			m_pxPhysicalObject->SetDrag(m_fDrag);
			m_pxPhysicalObject->SetAngularDrag(m_fAngularDrag);
			m_pxPhysicalObject->SetInvMass(1.0f / m_fMass);
		}
 	}

    if ( m_pxPhysicalObject )
    {
		m_pxPhysicalObject->SetCollisionProxy(static_cast<Asura_Physics_Object::COLLISION_PROXY>(m_iCollisionProxy));
	}
}
    

void Tools_Entity_PhysicsObject::AddOverlayControls(Asura_Overlay* pxOverlay, Asura_Vector_2& xOffset)
{
	// No call to parent, project classes are multiply derived and we don't want to call it twice
	Asura_Dialog* pxDialog;

	xOffset.x += Asura_Dialog::LargeSpacing()-Asura_Dialog::DefaultSpacing();

	//Set up Tabs

	m_pxTabsDialog = new Asura_Tabs_Dialog(xOffset,0,0);
	pxOverlay->AddDialog(m_pxTabsDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();
	pxOverlay->SetCurrentParentDialog(m_pxTabsDialog);

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

	m_xTabsOffset = xOffset;

	//Skin and anim tab
	m_pxTabsDialog->AddTab("Skin & Anim");
    
    //pxDialog = new Asura_Function_Dialog("Select Shape", xOffset, SelectShape_Dialog);
	//pxOverlay->AddDialog(pxDialog);
	//xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Select Skin", xOffset, SelectSkinOnly_Dialog);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Select Skin and Animation", xOffset, SelectSkinAndAnim_Dialog);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Select Animation", xOffset, SelectAnim_Dialog);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_StringDisplay_Dialog("Animation flags:", xOffset);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

    AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_PhysicsObject_Looping );
	xOffset.y+=Asura_Dialog::DefaultSpacing();

    AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_PhysicsObject_Reversed );
	xOffset.y+=Asura_Dialog::DefaultSpacing();

    AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_PhysicsObject_UseAnimMovement );
	xOffset.y+=Asura_Dialog::LargeSpacing();


	if (Type == TOT_Entity_PhysicsObject)
	{
		pxDialog = new Asura_Function_Dialog("Select all with this shape", xOffset, SelectAllOfType);
		pxDialog->SetHelpText("Selects all physics objects that use the same shape/skin as this one.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y += Asura_Dialog::DefaultSpacing();
	}

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

    // Merge options tab.
	if(IsStatic())
	{
		m_pxTabsDialog->AddTab("Merge");
		xOffset.y = fTabsStartY;

		AddMergeWithEnvOverlayControl(pxOverlay,xOffset);

		AddMergeWithSkyOverlayControl(pxOverlay,xOffset);

	    if(xOffset.y > fTabsEndY) fTabsEndY = xOffset.y;
    }
	
    // Physics tab.
    const u_int uEnabledPhysicsOptions = GetEnabledPhysicsOptions();
    if( uEnabledPhysicsOptions )
	{
		m_pxTabsDialog->AddTab("Physics");
		xOffset.y = fTabsStartY;

        if( uEnabledPhysicsOptions & uTOOLS_PHYSICSOBJECT_OPTIONFLAG_NOCOLLISIONS )
        {
		    AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_PhysicsObject_NoCollisions );
		    xOffset.y += Asura_Dialog::DefaultSpacing();
        }

        if( uEnabledPhysicsOptions & uTOOLS_PHYSICSOBJECT_OPTIONFLAG_COLLISIONPROXY )
        {
	        pxDialog = new Asura_DropDown_List_Dialog( xOffset, g_aszCollisionProxies, &m_iCollisionProxy, 5, Asura_Physics_Object::NUM_COLLISION_PROXIES, NULL, &Changed, "Collision Proxy" );
	        pxDialog->SetHelpText( "Defines the shape of mesh used by this object for collisions." );
	        pxOverlay->AddDialog(pxDialog);
	        xOffset.y += Asura_Dialog::DefaultSpacing();
        }

        if( uEnabledPhysicsOptions & uTOOLS_PHYSICSOBJECT_OPTIONFLAG_GRAVITYSCALE )
        {
		    AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_PhysicsObject_GravityScale );
		    xOffset.y += Asura_Dialog::DefaultSpacing();
        }

        if( uEnabledPhysicsOptions & uTOOLS_PHYSICSOBJECT_OPTIONFLAG_ELASTICITY )
        {
		    AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_PhysicsObject_Elasticity );
		    xOffset.y += Asura_Dialog::DefaultSpacing();
        }

        if( uEnabledPhysicsOptions & uTOOLS_PHYSICSOBJECT_OPTIONFLAG_FRICTION )
        {
		    AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_PhysicsObject_Friction );
		    xOffset.y += Asura_Dialog::DefaultSpacing();
        }

        if( uEnabledPhysicsOptions & uTOOLS_PHYSICSOBJECT_OPTIONFLAG_DRAG )
        {
		    AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_PhysicsObject_Drag );
		    xOffset.y += Asura_Dialog::DefaultSpacing();
        }

        if( uEnabledPhysicsOptions & uTOOLS_PHYSICSOBJECT_OPTIONFLAG_ANGULARDRAG )
        {
		    AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_PhysicsObject_AngularDrag );
		    xOffset.y += Asura_Dialog::DefaultSpacing();
        }

        if( uEnabledPhysicsOptions & uTOOLS_PHYSICSOBJECT_OPTIONFLAG_MASS )
        {
		    AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_PhysicsObject_Mass );
		    xOffset.y += Asura_Dialog::DefaultSpacing();
        }

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

	//Misc stuff tab
	m_pxTabsDialog->AddTab("Misc");
	xOffset.y = fTabsStartY;

    AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_PhysicsObject_NoShadow );
	xOffset.y += Asura_Dialog::DefaultSpacing();

    AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_PhysicsObject_Hidden );
	xOffset.y += Asura_Dialog::DefaultSpacing();

    AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_PhysicsObject_IgnoreForLOS );
	xOffset.y += Asura_Dialog::DefaultSpacing();

    AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_PhysicsObject_IgnoreModuleLinking );
	xOffset.y += Asura_Dialog::DefaultSpacing();

    AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_PhysicsObject_FullBright );
	xOffset.y += Asura_Dialog::DefaultSpacing();

    AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_PhysicsObject_BlockWaypointLinks );
	xOffset.y += Asura_Dialog::DefaultSpacing();

	if ( IsStatic() )
	{
		AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_PhysicsObject_BlockNavDataGeneration );
		xOffset.y += Asura_Dialog::DefaultSpacing();
	}

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

	//end of tabs
	xOffset.y = fTabsEndY + 1.0f;
	pxOverlay->SetCurrentParentDialog(0);
	m_pxTabsDialog->SelectTab(0);

	//Maintaining different selected tab values for static and non-static objects ,since they have
	//differing number of tabs.
	//RAR 11/09/2006
	if(IsStatic())
	{
		static u_int s_uSelectedTab = 0;
		m_pxTabsDialog->SetSelectedTabPointer(&s_uSelectedTab);
	}
	else
	{
		static u_int s_uSelectedTab = 0;
		m_pxTabsDialog->SetSelectedTabPointer(&s_uSelectedTab);
	}

	if (Type == TOT_Entity_PhysicsObject)
	{
		AddHealthOverlayControl(pxOverlay, xOffset);
	}
}


u_int Tools_Entity_PhysicsObject::GetEnabledPhysicsOptions( void ) const
{
    // Default behaviour is the same as before this method was added - no
    // options are available for physics object with the static flag set and
    // all of them are there for non-static ones.

	// RAR 22/06/2007 Allowed collision proxy editing by default for static objects. 
	if( IsStatic() )
    {
        return uTOOLS_PHYSICSOBJECT_OPTIONFLAG_NOCOLLISIONS | uTOOLS_PHYSICSOBJECT_OPTIONFLAG_COLLISIONPROXY;
    }
    else
	{
        return 0xFFFFFFFF;
    }
}


bool Tools_Entity_PhysicsObject::IsNoneAnimatingObject()
{
	if (Tools_Object_System::SelectedObject)
	{
		if (s_pxCurrentlySelectedObject)
		{
			return (!s_pxCurrentlySelectedObject->m_pxPhysicalObject->HasSkinOrHierarchy());
		}
	}
	return false;
}


void Tools_Entity_PhysicsObject::SelectAllOfType()
{
	if (Tools_Object_System::SelectedObject)
	{
		if (Tools_Object_System::SelectedObject->Type == TOT_Entity_PhysicsObject)
		{
			Asura_Guid uGuid = Tools_Object_System::SelectedObject->GetGuid();
			Asura_Assert(uGuid != ASURA_GUID_UNREGISTERED, "");
			Asura_ServerEntity_PhysicalObject *pPhysicalObject = Asura_ServerEntity_PhysicalObject::FindEntity(uGuid);
			Asura_Assert(pPhysicalObject, "");
			Asura_Hash_ID xShapeHashID = pPhysicalObject->GetShapeFileID();
			Asura_Hash_ID xSkinHashID = pPhysicalObject->GetSkinID();

			List<Tools_Object*> listObjects;
			for(Asura_Classified_List_It<Asura_Entity_Server> it(Asura_Entity_Server::GetList());!it.Done();it.Next())
			{
				Asura_Entity_Server* entity = it.GetCurrent();
				if (entity->GetClassification() == AsuraEntityClass_PhysicsObject)
				{
					uGuid = entity->GetGuid();
					Asura_ServerEntity_PhysicalObject *pPhysicalObject = Asura_ServerEntity_PhysicalObject::FindEntity(uGuid);
//					Asura_Assert(pPhysicalObject, "");
					if (!pPhysicalObject)
					{
						continue;
					}
					Asura_Hash_ID xShapeHashID2 = pPhysicalObject->GetShapeFileID();
					Asura_Hash_ID xSkinHashID2 = pPhysicalObject->GetSkinID();
					if (xShapeHashID2 == xShapeHashID && xSkinHashID2 == xSkinHashID)
					{
						Tools_Object *pObject = Tools_Object_System::GetObjectFromGuid(uGuid);
						listObjects.add_entry(pObject);
					}
				}
			}
			Tools_Object_System::SelectObjects(listObjects, true);
		}
	}
}

void Tools_Entity_PhysicsObject::CloneObject()
{
#ifndef ASURA_TARGET_SM3
	if(Tools_Object_System::SelectedObject->Type == TOT_Entity_DestructibleLight)
	{
		Tools_Entity_DestructibleLight::CloneObject();
		return;
	}
#endif // #ifndef ASURA_TARGET_SM3

	Asura_Assert(s_pxCurrentlySelectedObject,"");
	
	Asura_Vector_3 xOffset;
	Asura_Vector_3 xCameraDirection = *Asura_Camera::GetPosition() - Tools_Object_System::SelectedObject->Position; 
	xCameraDirection.y = 0.0f;
	xCameraDirection.Normalise();
	xOffset = xCameraDirection * 0.5f;

	s_pxCurrentlySelectedObject->CloneObjectMain(xOffset);
}

void Tools_Entity_PhysicsObject::CloneAllSelectedObjects()
{
	Asura_Assert(Tools_Object_System::SelectedObject,"");

	//get the list of selected object
	List<Tools_Object*> xSelectedList = Tools_Object_System::SelectedObjectList;
	//make sure the primary selection is at the end of the list.
	//this should ensure that the clone of this object should be the primary
	//selection once we are done
	if(xSelectedList.contains(Tools_Object_System::SelectedObject))
	{
		xSelectedList.delete_entry(Tools_Object_System::SelectedObject);
	}
	xSelectedList.add_entry(Tools_Object_System::SelectedObject);

	List<Tools_Object*> xCreatedList;

	Asura_Vector_3 xOffset;
	Asura_Vector_3 xCameraDirection = *Asura_Camera::GetPosition() - Tools_Object_System::SelectedObject->Position; 
	xCameraDirection.y = 0.0f;
	xCameraDirection.Normalise();
	xOffset = xCameraDirection * 0.5f;


	for(LIF<Tools_Object*> xSelectedLIF(&xSelectedList);!xSelectedLIF.done();xSelectedLIF.next())
	{
		//make sure this is a physics object
		Tools_Object* pxObject = xSelectedLIF();
		if(pxObject->Type != TOT_Entity_PhysicsObject) continue;

		//find it in the list of physics objects
		Asura_Guid uGuid = pxObject->GetGuid();
		Tools_Entity_PhysicsObject* pxPhysicsObj = s_xList.FindObject(uGuid);
		if(!pxPhysicsObj) continue;

		if(pxPhysicsObj->m_pxPhysicalObject->HasSkinOrHierarchy()) continue;

		//clone it and add the newly created object to the created list
		xCreatedList.add_entry(pxPhysicsObj->CloneObjectMain(xOffset));
	}

	//select the newly created objects
	Tools_Object_System::SelectObjects(xCreatedList,false);
}

Tools_Object* Tools_Entity_PhysicsObject::CloneObjectMain(const Asura_Vector_3& xOffset)
{
	Tools_Entity_PhysicalObject::s_bCreating = true;
	// Record data from current selection.
	Asura_Assert(Type == TOT_Entity_PhysicsObject, "");
	Asura_Guid uGuid = GetGuid();
	Asura_ServerEntity_PhysicalObject *pPhysicalObject = Asura_ServerEntity_PhysicalObject::FindEntity(uGuid);
	Asura_Assert(pPhysicalObject, "");
	Asura_Vector_3 xPosition = pPhysicalObject->GetPosition();
	const Asura_Matrix_3x3& xMatrix = pPhysicalObject->GetOrientation();
	Asura_Hash_ID xFileID = pPhysicalObject->GetShapeFileID();
	bool bDestructible = pPhysicalObject->IsDestructible();
	int uFlags = pPhysicalObject->GetPhysicalObjectFlags();
	
	// Offset the position slightly so that the object is visible.
	xPosition += xOffset;
	
	// Create the new object.	
	Tools_Entity_PhysicsObject::Project_Create();
	// Set the new object to be identical to the previous.
	Asura_Assert(Tools_Object_System::SelectedObject, "");
	Asura_Assert(Tools_Object_System::SelectedObject->Type == TOT_Entity_PhysicsObject, "");
	uGuid = Tools_Object_System::SelectedObject->GetGuid();
	pPhysicalObject = Asura_ServerEntity_PhysicalObject::FindEntity(uGuid);
	Asura_Assert(pPhysicalObject, "");

	pPhysicalObject->SetPosition(xPosition);
	pPhysicalObject->SetOrientation(xMatrix);
	pPhysicalObject->SetShapeFileID(xFileID);	
	pPhysicalObject->SetDestructible(bDestructible);

	// And set the tools object to the same.
	Tools_Object_System::SelectedObject->Position = xPosition;
	Tools_Object_System::SelectedObject->Orientation = xMatrix;
	Tools_Entity_PhysicalObject *pToolsPhysicalObject = (Tools_Entity_PhysicalObject*)Tools_Object_System::SelectedObject;
	pToolsPhysicalObject->SetToolsShape(xFileID);
	pToolsPhysicalObject->SetPhysicalObjectFlags(uFlags);
		
	Tools_Entity_PhysicalObject::s_bCreating = false;

	

	return Tools_Object_System::SelectedObject;
}


void Tools_Entity_PhysicsObject::CopyShadowFlagToSelected()
{
	if(!s_pxCurrentlySelectedObject) return;

	bool bSetFlag = (s_pxCurrentlySelectedObject->GetPhysicalObjectFlags() & ASURA_PHYSICALOBJECT_FLAG_NOSHADOW) != 0;

	//Add/remove the shadow flag from all selected physics objects. (Not doing it to all physical objects , since
	//although they have the flag , there isn't any direct way for the user to change it at the moment).

	int iNumSelected = 0;
	

	for(Asura_Hashed_List_It<Tools_Entity_PhysicsObject> xIt(&s_xList);!xIt.Done();xIt.Next())
	{
		Tools_Entity_PhysicsObject* pxObject = xIt.GetCurrent();


		if(pxObject->IsSelected() || pxObject->IsGroupSelected())
		{
			//update this object'sflags
			u_int uFlags = pxObject->GetPhysicalObjectFlags();
			if(bSetFlag)
			{
				uFlags |= ASURA_PHYSICALOBJECT_FLAG_NOSHADOW;
			}
			else
			{
				uFlags &=~ ASURA_PHYSICALOBJECT_FLAG_NOSHADOW;
			}
			pxObject->SetPhysicalObjectFlags(uFlags);

			iNumSelected++;
			pxObject->Changed = true;
		}
	}

	Tools_Timed_Message_System::AddMessage(0xffffff, "'No Shadow' flag %s %d selected object(s)",bSetFlag ? "added to" : "removed from",iNumSelected);
}



void Tools_Entity_PhysicsObject::Copy(const Tools_Object* pxObject)
{
	//If using a default name , make sure this is named appropriately. RAR 18/09/2006
	UpdateName();

	const Tools_Entity_PhysicsObject* pxFromObject = s_xList.FindEntity(pxObject->GetGuid());
	if(!pxFromObject) return;

	//get the entities to copy from and to
	const Asura_ServerEntity_PhysicalObject* pxFromEntity = pxFromObject->m_pxPhysicalObject;
	Asura_ServerEntity_PhysicalObject* pxToEntity = m_pxPhysicalObject;

	//copy everything between the entities
	m_iAnimFlags = pxFromEntity->GetAnimFlags();
	pxToEntity->SetAnimFlags(m_iAnimFlags);
	UpdateAnim();

    const u_int uSharedEnabledPhysicsOptions = ( GetEnabledPhysicsOptions() & pxFromObject->GetEnabledPhysicsOptions() );
    if( uSharedEnabledPhysicsOptions )
	{
        // NB: The NOCOLLISIONS option is dealt with in the parent class.

        if( uSharedEnabledPhysicsOptions & uTOOLS_PHYSICSOBJECT_OPTIONFLAG_COLLISIONPROXY )
        {
		    pxToEntity->SetCollisionProxy( pxFromEntity->GetCollisionProxy() );
		    m_iCollisionProxy = pxFromObject->m_iCollisionProxy;
        }

        if( uSharedEnabledPhysicsOptions & uTOOLS_PHYSICSOBJECT_OPTIONFLAG_GRAVITYSCALE )
        {
		    pxToEntity->SetGravityScale( pxFromEntity->GetGravityScale() );
		    m_fGravityScale = pxFromObject->m_fGravityScale;
        }

        if( uSharedEnabledPhysicsOptions & uTOOLS_PHYSICSOBJECT_OPTIONFLAG_ELASTICITY )
        {
		    pxToEntity->SetElasticity( pxFromEntity->GetElasticity() );
		    m_fElasticity = pxFromObject->m_fElasticity;
        }

        if( uSharedEnabledPhysicsOptions & uTOOLS_PHYSICSOBJECT_OPTIONFLAG_FRICTION )
        {
		    pxToEntity->SetFriction( pxFromEntity->GetFriction() );
		    m_fFriction = pxFromObject->m_fFriction;
        }

        if( uSharedEnabledPhysicsOptions & uTOOLS_PHYSICSOBJECT_OPTIONFLAG_DRAG )
        {
            pxToEntity->SetDrag( pxFromEntity->GetDrag() );
		    m_fDrag = pxFromObject->m_fDrag;
        }

        if( uSharedEnabledPhysicsOptions & uTOOLS_PHYSICSOBJECT_OPTIONFLAG_ANGULARDRAG )
        {
		    pxToEntity->SetAngularDrag( pxFromEntity->GetAngularDrag() );
		    m_fAngularDrag = pxFromObject->m_fAngularDrag;
        }

        if( uSharedEnabledPhysicsOptions & uTOOLS_PHYSICSOBJECT_OPTIONFLAG_MASS )
        {
            pxToEntity->SetInvMass( pxFromEntity->GetInvMass() );
		    m_fMass = pxFromObject->m_fMass;
        }
    }

    //Parent Copy() not called since it should have been dealt with by the project class
}


Tools_Object* Tools_Entity_PhysicsObject::CreateObjectOfThisType() const
{
	Tools_Object* pxOldSelection = Tools_Object_System::SelectedObject;
	if (IsStatic())
	{
		Tools_Entity_PhysicsObject::Project_CreateStatic();
	}
	else
	{
		Tools_Entity_PhysicsObject::Project_Create();
	}
	Tools_Object* pxNewSelection = Tools_Object_System::SelectedObject;

	return (pxOldSelection!=pxNewSelection) ? pxNewSelection : 0;
}

bool Tools_Entity_PhysicsObject::UseModuleLinking()
{
	return !(m_uPhysicalObjectFlags & (ASURA_PHYSICALOBJECT_FLAG_IGNOREMODULELINKING));
}

bool Tools_Entity_PhysicsObject::HandlesThisMessage(int iMessageID)
{
	if(iMessageID == ASURA_MSG_HIDE ||
	   	iMessageID == ASURA_MSG_UNHIDE ||
	   	iMessageID == ASURA_MSG_COLLISIONSON ||
	   	iMessageID == ASURA_MSG_COLLISIONSOFF) return true;

	//Calling parent to avoid forcing projects to define this function for derived classes
	//(since it doesn't really matter if the parent version gets called twice)
	return PARENT::HandlesThisMessage(iMessageID);
}

bool Tools_Entity_PhysicsObject::ShouldHideDuringCutscene()
{
	//Want to show physics objects , unless they haven't had a shape/skin assigned to them
	return !GetShapeOrSkinFileID();

}


bool Tools_Entity_PhysicsObject::GetParameter(Asura_Hash_ID uParamID,class Tools_Parameter* pxParam) const
{
	switch(uParamID)
	{
		// Shape and anim...
		case g_uHash_PhysicsObject_Looping :
			pxParam->SetBool((m_iAnimFlags & AsuraAnimationFlag_Loop)!=0);
			break;

		case g_uHash_PhysicsObject_Reversed :
			pxParam->SetBool((m_iAnimFlags & AsuraAnimationFlag_Reversed)!=0);
			break;

		case g_uHash_PhysicsObject_UseAnimMovement :
			pxParam->SetBool((m_uPhysicalObjectFlags & ASURA_PHYSICALOBJECT_FLAG_USEANIMMOVEMENT)!=0);
			break;


		// Physics...
		case g_uHash_PhysicsObject_NoCollisions :
			pxParam->SetBool((m_uPhysicalObjectFlags & ASURA_PHYSICALOBJECT_FLAG_NOCOLLISIONS)!=0);
			break;

		case g_uHash_PhysicsObject_CollisionProxy :
			pxParam->SetUInt(m_iCollisionProxy);
			break;

		case g_uHash_PhysicsObject_GravityScale :
			pxParam->SetFloat(m_fGravityScale);
			break;

		case g_uHash_PhysicsObject_Elasticity :
			pxParam->SetFloat(m_fElasticity);
			break;

		case g_uHash_PhysicsObject_Friction :
			pxParam->SetFloat(m_fFriction);
			break;

		case g_uHash_PhysicsObject_Drag :
			pxParam->SetFloat(m_fDrag);
			break;

		case g_uHash_PhysicsObject_AngularDrag :
			pxParam->SetFloat(m_fAngularDrag);
			break;

		case g_uHash_PhysicsObject_Mass :
			pxParam->SetFloat(m_fMass);
			break;


		// Misc...
		case g_uHash_PhysicsObject_NoShadow :
			pxParam->SetBool((m_uPhysicalObjectFlags & ASURA_PHYSICALOBJECT_FLAG_NOSHADOW)!=0);
			break;

		case g_uHash_PhysicsObject_Hidden :
			pxParam->SetBool((m_uPhysicalObjectFlags & ASURA_PHYSICALOBJECT_FLAG_INVISIBLE)!=0);
			break;

		case g_uHash_PhysicsObject_IgnoreForLOS :
			pxParam->SetBool((m_uPhysicalObjectFlags & ASURA_PHYSICALOBJECT_FLAG_IGNOREFORLOS)!=0);
			break;

		case g_uHash_PhysicsObject_IgnoreModuleLinking :
			pxParam->SetBool((m_uPhysicalObjectFlags & ASURA_PHYSICALOBJECT_FLAG_IGNOREMODULELINKING)!=0);
			break;

		case g_uHash_PhysicsObject_FullBright :
			pxParam->SetBool((m_uPhysicalObjectFlags & ASURA_PHYSICALOBJECT_FLAG_FULLBRIGHT)!=0);
			break;

        case g_uHash_PhysicsObject_BlockWaypointLinks:
            pxParam->SetBool( ( m_uPhysicalObjectFlags & ASURA_PHYSICALOBJECT_FLAG_BLOCKWAYPOINTLINKS ) != 0 );
            break;

		case g_uHash_PhysicsObject_BlockNavDataGeneration:
			pxParam->SetBool( ( m_uPhysicalObjectFlags & ASURA_PHYSICALOBJECT_FLAG_BLOCKNAVDATAGENERATION ) != 0 );
			break;

		default :
			return PARENT::GetParameter(uParamID, pxParam);
	}
	return true;
}

bool Tools_Entity_PhysicsObject::SetParameter(Asura_Hash_ID uParamID,const class Tools_Parameter* pxParam)
{
	switch(uParamID)
	{
		// Shape and anim...
		case g_uHash_PhysicsObject_Looping :
			if(pxParam->GetBool())
			{
				m_iAnimFlags |= AsuraAnimationFlag_Loop;
			}
			else
			{
				m_iAnimFlags &=~ AsuraAnimationFlag_Loop;
			}
			m_pxPhysicalObject->SetAnimFlags(m_iAnimFlags);
			break;

		case g_uHash_PhysicsObject_Reversed :
			if(pxParam->GetBool())
			{
				m_iAnimFlags |= AsuraAnimationFlag_Reversed;
			}
			else
			{
				m_iAnimFlags &=~ AsuraAnimationFlag_Reversed;
			}
			m_pxPhysicalObject->SetAnimFlags(m_iAnimFlags);
			UpdateAnim();
			break;

		case g_uHash_PhysicsObject_UseAnimMovement :
			if(pxParam->GetBool())
			{
				m_uPhysicalObjectFlags |= ASURA_PHYSICALOBJECT_FLAG_USEANIMMOVEMENT;
			}
			else
			{
				m_uPhysicalObjectFlags &=~ ASURA_PHYSICALOBJECT_FLAG_USEANIMMOVEMENT;
			}
			m_pxPhysicalObject->SetPhysicalObjectFlags(m_uPhysicalObjectFlags);
			SetUseAnimMovement((m_uPhysicalObjectFlags & ASURA_PHYSICALOBJECT_FLAG_USEANIMMOVEMENT)!=0);
			break;


		// Physics...
		case g_uHash_PhysicsObject_NoCollisions :
			if(pxParam->GetBool())
			{
				m_uPhysicalObjectFlags |= ASURA_PHYSICALOBJECT_FLAG_NOCOLLISIONS;
			}
			else
			{
				m_uPhysicalObjectFlags &=~ ASURA_PHYSICALOBJECT_FLAG_NOCOLLISIONS;
			}
			if((m_uPhysicalObjectFlags^m_pxPhysicalObject->GetPhysicalObjectFlags()) & ASURA_PHYSICALOBJECT_FLAG_NOCOLLISIONS)
			{
				//no collisions flag has changed ,so polygons will need to be remerged
				UnmergePolygonsFromEnvironment();
			}
			m_pxPhysicalObject->SetPhysicalObjectFlags(m_uPhysicalObjectFlags);
			break;

		case g_uHash_PhysicsObject_CollisionProxy :
			m_iCollisionProxy	= pxParam->GetUInt();
			m_pxPhysicalObject->SetCollisionProxy(static_cast<Asura_Physics_Object::COLLISION_PROXY>(m_iCollisionProxy));
			break;

		case g_uHash_PhysicsObject_GravityScale :
			m_fGravityScale	= pxParam->GetFloat();
			m_pxPhysicalObject->SetGravityScale(m_fGravityScale);
			break;

		case g_uHash_PhysicsObject_Elasticity :
			m_fElasticity	= pxParam->GetFloat();
			m_pxPhysicalObject->SetElasticity(m_fElasticity);
			break;

		case g_uHash_PhysicsObject_Friction :
			m_fFriction	= pxParam->GetFloat();
			m_pxPhysicalObject->SetFriction(m_fFriction);
			break;

		case g_uHash_PhysicsObject_Drag :
			m_fDrag	= pxParam->GetFloat();
			m_pxPhysicalObject->SetDrag(m_fDrag);
			break;

		case g_uHash_PhysicsObject_AngularDrag :
			m_fAngularDrag	= pxParam->GetFloat();
			m_pxPhysicalObject->SetAngularDrag(m_fAngularDrag);
			break;


		case g_uHash_PhysicsObject_Mass :
			m_fMass	= pxParam->GetFloat();
			m_pxPhysicalObject->SetInvMass(1.0f / m_fMass);
			break;


		// Misc...
		case g_uHash_PhysicsObject_NoShadow :
			if(pxParam->GetBool())
			{
				m_uPhysicalObjectFlags |= ASURA_PHYSICALOBJECT_FLAG_NOSHADOW;
			}
			else
			{
				m_uPhysicalObjectFlags &=~ ASURA_PHYSICALOBJECT_FLAG_NOSHADOW;
			}
			m_pxPhysicalObject->SetPhysicalObjectFlags(m_uPhysicalObjectFlags);
			break;

		case g_uHash_PhysicsObject_Hidden :
			if(pxParam->GetBool())
			{
				m_uPhysicalObjectFlags |= ASURA_PHYSICALOBJECT_FLAG_INVISIBLE;
			}
			else
			{
				m_uPhysicalObjectFlags &=~ ASURA_PHYSICALOBJECT_FLAG_INVISIBLE;
			}
			if((m_uPhysicalObjectFlags^m_pxPhysicalObject->GetPhysicalObjectFlags()) & ASURA_PHYSICALOBJECT_FLAG_INVISIBLE)
			{
				//invis flag has changed ,so polygons will need to be remerged
				UnmergePolygonsFromEnvironment();
			}
			m_pxPhysicalObject->SetPhysicalObjectFlags(m_uPhysicalObjectFlags);
			break;

		case g_uHash_PhysicsObject_IgnoreForLOS :
			if(pxParam->GetBool())
			{
				m_uPhysicalObjectFlags |= ASURA_PHYSICALOBJECT_FLAG_IGNOREFORLOS;
			}
			else
			{
				m_uPhysicalObjectFlags &=~ ASURA_PHYSICALOBJECT_FLAG_IGNOREFORLOS;
			}
			if((m_uPhysicalObjectFlags^m_pxPhysicalObject->GetPhysicalObjectFlags()) & ASURA_PHYSICALOBJECT_FLAG_IGNOREFORLOS)
			{
				//ignore los flag has changed ,so polygons will need to be remerged
				UnmergePolygonsFromEnvironment();
			}
			m_pxPhysicalObject->SetPhysicalObjectFlags(m_uPhysicalObjectFlags);
			break;


		case g_uHash_PhysicsObject_IgnoreModuleLinking :
			if(pxParam->GetBool())
			{
				m_uPhysicalObjectFlags |= ASURA_PHYSICALOBJECT_FLAG_IGNOREMODULELINKING;
			}
			else
			{
				m_uPhysicalObjectFlags &=~ ASURA_PHYSICALOBJECT_FLAG_IGNOREMODULELINKING;
			}
			m_pxPhysicalObject->SetPhysicalObjectFlags(m_uPhysicalObjectFlags);
			break;

		case g_uHash_PhysicsObject_FullBright :
			if(pxParam->GetBool())
			{
				m_uPhysicalObjectFlags |= ASURA_PHYSICALOBJECT_FLAG_FULLBRIGHT;
			}
			else
			{
				m_uPhysicalObjectFlags &=~ ASURA_PHYSICALOBJECT_FLAG_FULLBRIGHT;
			}
			m_pxPhysicalObject->SetPhysicalObjectFlags(m_uPhysicalObjectFlags);

			// refresh skybox object list
			Asura_Skybox::InvalidateSkyboxObjects();

			break;

        case g_uHash_PhysicsObject_BlockWaypointLinks:
        {
            if ( m_pxPhysicalObject->ShouldBeMergedWithEnvironment() )
            {
                return false;
            }

            if ( pxParam->GetBool() )
            {
                m_uPhysicalObjectFlags |= ASURA_PHYSICALOBJECT_FLAG_BLOCKWAYPOINTLINKS;
                m_pxPhysicalObject->SetPhysicalObjectFlags( m_uPhysicalObjectFlags );

                // We need to update the blocked waypoint links, but only if we're not using streaming, as in streaming systems
                // we can only be sure we have the correct waypoint graph once we're in game.
#ifdef ASURA_USE_STREAMING
                // To be safe, make sure the object does not have any links blocked, and does not have any stored.
                // Remove the old link blocks.
                m_pxPhysicalObject->UpdateWaypointLinkBlocks( true );
                // Forget that we ever had any.
                m_pxPhysicalObject->DiscardWaypointLinkBlockInformation();
#else
                // Remove the old link blocks.
                m_pxPhysicalObject->UpdateWaypointLinkBlocks( true );
                // Calculate the new link blocks.
                Asura_ServerEntity_PhysicalObject::InitialiseWaypointLinkBlockCalculations();
                m_pxPhysicalObject->CalculateWaypointLinkBlocks();
                Asura_ServerEntity_PhysicalObject::ShutdownWaypointLinkBlockCalculations();
                // Apply the new link blocks.
                m_pxPhysicalObject->UpdateWaypointLinkBlocks();
#endif
            }
            else
            {
                m_uPhysicalObjectFlags &= ~ASURA_PHYSICALOBJECT_FLAG_BLOCKWAYPOINTLINKS;
                m_pxPhysicalObject->SetPhysicalObjectFlags( m_uPhysicalObjectFlags );

                // Remove the old link blocks.
                m_pxPhysicalObject->UpdateWaypointLinkBlocks( true );
                // Forget that we ever had any.
                m_pxPhysicalObject->DiscardWaypointLinkBlockInformation();
            }

            break;
        }

		case g_uHash_PhysicsObject_BlockNavDataGeneration:
		{
            if ( m_pxPhysicalObject->ShouldBeMergedWithEnvironment() )
            {
                return false;
            }

			if ( pxParam->GetBool() )
			{
				m_uPhysicalObjectFlags |= ASURA_PHYSICALOBJECT_FLAG_BLOCKNAVDATAGENERATION;
				m_pxPhysicalObject->SetPhysicalObjectFlags( m_uPhysicalObjectFlags );
			}
			else
			{
				m_uPhysicalObjectFlags &= ~ASURA_PHYSICALOBJECT_FLAG_BLOCKNAVDATAGENERATION;
				m_pxPhysicalObject->SetPhysicalObjectFlags( m_uPhysicalObjectFlags );
			}

            break;
		}

		default :
			return PARENT::SetParameter(uParamID, pxParam);
	}

	SetEntityModified();

	return true;
}


bool Tools_Entity_PhysicsObject::AddSharedParamDialogToOverlay( Asura_Overlay* pxOverlay, const Asura_Vector_2& xOffset, Asura_Hash_ID uParamID )
{
    Asura_Dialog* pxDialog = NULL;

    switch( uParamID )
    {
		// Shape and anim...
        case g_uHash_PhysicsObject_Looping:
            pxDialog = new Asura_SharedTickbox_Dialog( "Looping", xOffset, &g_xParam_PhysicsObject_Looping );
            break;

        case g_uHash_PhysicsObject_Reversed:
            pxDialog = new Asura_SharedTickbox_Dialog( "Reversed", xOffset, &g_xParam_PhysicsObject_Reversed );
            break;

		case g_uHash_PhysicsObject_UseAnimMovement :
            pxDialog = new Asura_SharedTickbox_Dialog( "Use Anim Movement", xOffset, &g_xParam_PhysicsObject_UseAnimMovement );
			pxDialog->SetHelpText("Causes the object to make use of the 'compass' movement from the animation for rendering and client side LOS tests.\nThis won't cause the object to move for collision purposes.");
			break;

		// Physics...
		case g_uHash_PhysicsObject_NoCollisions:
			pxDialog = new Asura_SharedTickbox_Dialog( "No collisions", xOffset, &g_xParam_PhysicsObject_NoCollisions );
			pxDialog->SetHelpText( "Stops anything from colliding with this object.\nThis flag will be copied to the polygons if merged into the environment." );
			break;

		case g_uHash_PhysicsObject_GravityScale:
			pxDialog = new Asura_Numerical_Dialog( "Gravity Scale", xOffset, &g_xParam_PhysicsObject_GravityScale, -10.0f, 10.0f, 0.100f, " %.3f" );
			pxDialog->SetHelpText( "The gravity scale factor is how much the object is affected by gravity.\n1.0 means normal gravity. 0.0 means no gravity, 2.0 means double gravity, -1.0 means gravity is inverted and objects will float up, etc." );
			break;

		case g_uHash_PhysicsObject_Elasticity:
			pxDialog = new Asura_Numerical_Dialog( "Elasticity", xOffset, &g_xParam_PhysicsObject_Elasticity, 0.0f, 1.0f, 0.010f, " %.3f " );
			pxDialog->SetHelpText( "Elasticity determines how much bounce objects have on collisions.\n1.0 means fully elastic (bounces away with same speed) and 0.0 means fully inelastic (stops dead)." );
			break;

		case g_uHash_PhysicsObject_Friction:
			pxDialog = new Asura_Numerical_Dialog( "Friction", xOffset, &g_xParam_PhysicsObject_Friction, 0.0f, 1000.0f, 0.100f, "%.3f" );
			pxDialog->SetHelpText( "0.0 means no friction, and then you can have a number that increases meaning more friction." );
			break;

		case g_uHash_PhysicsObject_Drag:
			pxDialog = new Asura_Numerical_Dialog( "Drag", xOffset, &g_xParam_PhysicsObject_Drag, 0.0f, 1.000f, 0.010f, " %.3f " );
			pxDialog->SetHelpText( "Drag is how much air resistance there is to the object.\n0.0 means no drag, 1.0 means 100% drag and the object will lose any velocity it has pretty much instantaneously." );
			break;

		case g_uHash_PhysicsObject_AngularDrag:
			pxDialog = new Asura_Numerical_Dialog( "Angular Drag", xOffset, &g_xParam_PhysicsObject_AngularDrag, 0.0f, 1.000f, 0.010f, " %.3f " );
			pxDialog->SetHelpText( "Drag is how much air resistance there is to the object.\n0.0 means no drag, 1.0 means 100% drag and the object will lose any angular velocity it has pretty much instantaneously." );
			break;


		case g_uHash_PhysicsObject_Mass:
			pxDialog = new Asura_Numerical_Dialog( "Mass", xOffset, &g_xParam_PhysicsObject_Mass, 0.100f, 100000.0f, 1.000f, "%.3f" );
			pxDialog->SetHelpText( "How heavy the object is." );
			break;


		// Misc...
        case g_uHash_PhysicsObject_NoShadow:
            pxDialog = new Asura_SharedTickbox_Dialog( "No shadow", xOffset, &g_xParam_PhysicsObject_NoShadow );
            pxDialog->SetHelpText( "Stops this object from casting a shadow." );
            break;

        case g_uHash_PhysicsObject_IgnoreModuleLinking:
            pxDialog = new Asura_SharedTickbox_Dialog( "Ignore module linking", xOffset, &g_xParam_PhysicsObject_IgnoreModuleLinking );
            pxDialog->SetHelpText( "Forces shape to be rendered regardless of whether it is in a visible module." );
            break;

        case g_uHash_PhysicsObject_Hidden:
	        pxDialog = new Asura_SharedTickbox_Dialog( "Hidden", xOffset, &g_xParam_PhysicsObject_Hidden );
	        pxDialog->SetHelpText( "Stops object from being rendered or hit by LOS tests.\nIf merged into the environment then the Don't Render and Ignore LOS flags will be applied." );
            break;

        case g_uHash_PhysicsObject_IgnoreForLOS:
	        pxDialog = new Asura_SharedTickbox_Dialog( "Ignore For LOS", xOffset, &g_xParam_PhysicsObject_IgnoreForLOS );
            pxDialog->SetHelpText( "Entity is ignored for all LOS tests.\nThis flag will be copied to the polygons if merged into the environment." );
            break;

        case g_uHash_PhysicsObject_FullBright:
	        pxDialog = new Asura_SharedTickbox_Dialog( "Full bright", xOffset, &g_xParam_PhysicsObject_FullBright );
	        pxDialog->SetHelpText( "Object ignores lighting (both static and dynamic) and is drawn at maximum normal brightness instead." );
            break;

        case g_uHash_PhysicsObject_BlockWaypointLinks:
            pxDialog = new Asura_SharedTickbox_Dialog( "Mark waypoint links as blocked", xOffset, &g_xParam_PhysicsObject_BlockWaypointLinks );
            pxDialog->SetHelpText( "Whether the object should mark any waypoint links going through it as blocked when it has collisions turned on." );
            break;

		case g_uHash_PhysicsObject_BlockNavDataGeneration:
			pxDialog = new Asura_SharedTickbox_Dialog( "Block nav data generation", xOffset, &g_xParam_PhysicsObject_BlockNavDataGeneration );
			pxDialog->SetHelpText( "Whether the object should block waypoint and link generation, regardless of the 'No collisions' flag." );
			break;

        default:
            break;
    }

    if( pxDialog )
    {
        pxOverlay->AddDialog( pxDialog );
        return true;
    }
    else
    {
        return false;
    }
}


bool Tools_Entity_PhysicsObject::IsStatic( void ) const
{
    if( m_pxPhysicalObject )
    {
        const Asura_ServerEntity_PhysicsObject* pxPhysicsObj = Asura_ServerEntity_PhysicsObject::FindEntity( m_pxPhysicalObject->GetGuid() );
        if( pxPhysicsObj )
        {
            return pxPhysicsObj->IsStatic();
        }
    }

    return false;
}


const char* Tools_Entity_PhysicsObject::GetTypeDescription( void )
{
    if( IsStatic() )
    {
        return "Static object";
    }
    else
    {
        return s_szLongName;
    }
}

void Tools_Entity_PhysicsObject::UpdateName()
{
	const char* szStaticPrefix = "Static";
	const char* szPhysicsPrefix = "Physics";

	//If the object has a default name starting with Static or Physics then make sure it
	//is named correctly according to its type
	bool bStatic = IsStatic();
	const char* szWrongPrefix = bStatic ? szPhysicsPrefix : szStaticPrefix;
	const char* szRightPrefix = bStatic ? szStaticPrefix : szPhysicsPrefix;

	const char* szName = Entity->ToolsData->Name.GetString();

	int iPrefixLength = strlen(szWrongPrefix);
	if(strncmp(szName,szWrongPrefix,iPrefixLength))
	{
		//Not using the wrong prefix , so either using the correct one , or this isn't a default name
		return;
	}

	//build a name with a changed prefix
	char szNewName[200];
	Asura_PrintfToBfr(szNewName,"%s%s",sizeof(szNewName),true,szRightPrefix,szName + iPrefixLength);

	Entity->ToolsData->Name = szNewName;
}
