/*************************************************************************
Crytek Source File.
Copyright (C), Crytek Studios, 2001-2004.
-------------------------------------------------------------------------
$Id$
$DateTime$

-------------------------------------------------------------------------
History:
- 29:9:2004: Created by Filippo De Luca

*************************************************************************/
#include "StdAfx.h"
#include "Game.h"
#include "Player.h"
#include "PlayerView.h"
#include "GameUtils.h"

#include "Weapon.h"
#include "WeaponSystem.h"

#include <IViewSystem.h>
#include <IItemSystem.h>
#include <IPhysics.h>
#include <ICryAnimation.h>
#include "IAISystem.h"
#include "IAgent.h"
#include <IVehicleSystem.h>
#include <ISerialize.h>

#include <IRenderAuxGeom.h>

#include <IGameTokens.h>

#include "PlayerMovementController.h"

#include "HUD/HUD.h"

#include "PlayerMovement.h"
#include "PlayerRotation.h"
#include "PlayerInput.h"
#include "NetPlayerInput.h"

#define REUSE_VECTOR(table, name, value)	\
	{ if (table->GetValueType(name) != svtObject) \
	{ \
	table->SetValue(name, (value)); \
	} \
		else \
	{ \
	SmartScriptTable v; \
	table->GetValue(name, v); \
	v->SetValue("x", (value).x); \
	v->SetValue("y", (value).y); \
	v->SetValue("z", (value).z); \
	} \
	}

#define RANDOM() ((((float)rand()/(float)RAND_MAX)*2.0f)-1.0f)
#define RANDOMR(a,b) ((float)a + ((rand()/(float)RAND_MAX)*(float)(b-a)))

ICVar * pWalkMul = 0;
ICVar * pDebugAimIK = 0;

//--------------------
//this function will be called from the engine at the right time, since bones editing must be placed at the right time.
int PlayerProcessBones(ICharacterInstance *pCharacter,void *pPlayer)
{
	//FIXME: do something to remove GetISystem()->GetITimer()->GetFrameTime()
	//process bones specific stuff (IK, torso rotation, etc)
	float timeFrame = GetISystem()->GetITimer()->GetFrameTime();
	((CPlayer *)pPlayer)->ProcessBonesRotation(pCharacter, timeFrame);

	return 1;
}
//--------------------

CPlayer::CPlayer()
{
	if (!pDebugAimIK)
	{
		pDebugAimIK = GetISystem()->GetIConsole()->RegisterInt("ag_debugaimik", 0, VF_CHEAT);
	}
}

CPlayer::~CPlayer()
{
	m_pPlayerInput.reset();
	ICharacterInstance *pCharacter = GetEntity()->GetCharacter(0);
	if(pCharacter)
		pCharacter->GetISkeleton()->SetProceduralAnimCallback(0,0);
}

bool CPlayer::Init(IGameObject * pGameObject)
{
	if (!CActor::Init(pGameObject))
		return false;

	Revive(true);

	return true;
}

void CPlayer::PostInit( IGameObject * pGameObject )
{
	CActor::PostInit(pGameObject);
}

void CPlayer::BindInputs( IAnimationGraph * pGraph )
{
	CActor::BindInputs(pGraph);

	if (pGraph)
	{
		m_inputItem = pGraph->LookupInputId("Item");
		m_inputVehicleName = pGraph->LookupInputId("Vehicle");
		m_inputVehicleSeat = pGraph->LookupInputId("VehicleSeat");
		m_inputUsingLookIK = pGraph->LookupInputId("UsingLookIK");
	}
}

void CPlayer::ProcessEvent(SEntityEvent& event)
{
	if (event.event == ENTITY_EVENT_HIDE || event.event == ENTITY_EVENT_UNHIDE)
	{
		ICharacterInstance *pCharacter = GetEntity()->GetCharacter(0);
		if (pCharacter)
		{
			IPhysicalEntity *pPhysicalEntity = pCharacter->GetCharacterPhysics();

			if (event.event == ENTITY_EVENT_HIDE)
			{
				pCharacter->SetFlags(pCharacter->GetFlags() & ~CS_FLAG_UPDATE_ALWAYS);
				if (pPhysicalEntity)
					GetISystem()->GetIPhysicalWorld()->DestroyPhysicalEntity(pPhysicalEntity, 1);
			}
			else
			{
				if (pPhysicalEntity)
				{
					GetISystem()->GetIPhysicalWorld()->DestroyPhysicalEntity(pPhysicalEntity, 2);
					pe_params_articulated_body pab;
					if (pab.pHost = GetEntity()->GetPhysics())
						pPhysicalEntity->SetParams(&pab);
				}
			}
		}
	}
	else if (event.event == ENTITY_EVENT_XFORM)
	{
		int flags = event.nParam[0];
		if (flags & ENTITY_XFORM_ROT && !(flags & (ENTITY_XFORM_USER|ENTITY_XFORM_PHYSICS_STEP)))
		{
			if (flags & ENTITY_XFORM_TRACKVIEW|ENTITY_XFORM_EDITOR)
				m_forcedRotation = true;
			else
				m_forcedRotation = false;

			m_baseMtx.SetRotationVDir( -GetEntity()->GetRotation().GetColumn1() );
		}
	}
	else if (event.event == ENTITY_EVENT_PREPHYSICSUPDATE)
	{
		if (m_pPlayerInput.get())
			m_pPlayerInput->PreUpdate();
	}

	CActor::ProcessEvent(event);
}

void CPlayer::Draw(bool draw)
{
	if (!GetEntity())
		return;

	uint32 slotFlags = GetEntity()->GetSlotFlags(0);

	if (draw)
		slotFlags |= ENTITY_SLOT_RENDER;
	else
		slotFlags &= ~ENTITY_SLOT_RENDER;

	GetEntity()->SetSlotFlags(0,slotFlags);
}

// Hook for inheritance.
IPlayerInput * CPlayer::CreatePlayerInput()
{
	return new CPlayerInput(this);
}

void CPlayer::Update(SEntityUpdateContext& ctx, int updateSlot)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	CActor::Update(ctx,updateSlot);

	const float frameTime = ctx.fFrameTime;

	if (!m_stats.isRagDoll && GetHealth()>0 && (!m_stats.isFrozen || IsPlayer()))
	{
		m_nanoSuit.Update(this,frameTime);

		IEntity* pLinkedEntity(GetLinkedEntity());
		bool client(IsClient());

		UpdateAsLiveAndMobile(ctx); // Callback for inherited CPlayer classes

		//

		UpdateStats(frameTime);

		if (m_pPlayerInput.get())
			m_pPlayerInput->Update();
		else
		{
			// init input systems if required
			if (GetGameObject()->GetChannelId())
			{
				if (IsClient())
					m_pPlayerInput.reset( CreatePlayerInput() );
				else
					m_pPlayerInput.reset( new CNetPlayerInput(this) );
			}
			if (m_pPlayerInput.get())
				GetGameObject()->EnablePostUpdates(this);
		}

		//FIXME:
		// small hack to make first person ignore animation speed, and everything else use it
		// will need to be reconsidered for multiplayer
		SAnimatedCharacterParams params = m_pAnimatedCharacter->GetParams();
		params.flags &= ~(eACF_AlwaysAnimation | eACF_PerAnimGraph | eACF_AlwaysPhysics);
		
		// massive hack to temporarily force using physics instead of anim graph until a 
		// human mean to tweak speed is ever implemented in the loco manager
		IEntity *pThisEnt=this->GetEntity();
		IEntity *pOtherEnt=GetISystem()->GetIEntitySystem()->GetEntity("Companion1");		
		//if ((client && (!IsThirdPerson() || m_stats.inAir>0.01f)) || (pOtherEnt==pThisEnt))
		if (client || (pOtherEnt==pThisEnt))
			params.flags |= eACF_AlwaysPhysics; // params.flags |= eACF_AlwaysPhysics | eACF_ImmediateStance;
		else
		{
			//params.flags |= eACF_PerAnimGraph;
			//params.flags |= eACF_ImmediateStance | eACF_UseHumanBlending | eACF_AlwaysAnimation;
			params.flags |= eACF_ImmediateStance | eACF_PerAnimGraph | eACF_UseHumanBlending;
		}
		m_pAnimatedCharacter->SetParams(params);

		if (m_pMovementController && !pLinkedEntity)
		{
			// Test of look IK
			if(IsClient())
			{
				IEntity *pLookTarget=0;
				CMovementRequest mr;
				
				const char *pLookAtName = g_pGame->GetCVars()->cl_lookTargetName->GetString();

				if(pLookAtName && (pLookAtName!="") && ((0!=strcmp("none",pLookAtName))) )
					pLookTarget=GetISystem()->GetIEntitySystem()->GetEntity(pLookAtName);
				if(pLookTarget)
				{
					mr.SetLookTarget( pLookTarget->GetWorldPos() );
					m_pMovementController->RequestMovement(mr);

					// Debug draw the direction helper
					{
						ICharacterInstance * pCharacter = GetEntity()->GetCharacter(0);
						if (pCharacter)
						{
							ISkeleton * pSkeleton = pCharacter->GetISkeleton();
							if(pSkeleton)
							{
								int idJoint=pSkeleton->GetIDByName("Bip01 Neck");

								if(-1!=idJoint)
								{

									Vec3 vLookDir;//pSkeleton->GetMoveDirection();
									//	right=		Matrix.GetColumn(0)
									// forward=		Matrix.GetColumn(1)
									// up=				Matrix.GetColumn(2)
									//Vec3 vNeckDir0=pSkeleton->GetAbsJMatrixByID(idJoint).GetColumn(0);
									Vec3 vNeckDir1=pSkeleton->GetAbsJMatrixByID(idJoint).GetColumn(1);
									//Vec3 vNeckDir2=pSkeleton->GetAbsJMatrixByID(idJoint).GetColumn(2);
									{
										IRenderAuxGeom * pRAG = GetISystem()->GetIRenderer()->GetIRenderAuxGeom();
										Vec3 pos = GetEntity()->GetWorldPos();
										vLookDir = (pLookTarget->GetWorldPos()-GetEntity()->GetWorldPos()); //.GetNormalized();
										//vNeckDir0 = GetEntity()->GetRotation() * vNeckDir0;
										vNeckDir1 = GetEntity()->GetRotation() * vNeckDir1;
										//vNeckDir2 = GetEntity()->GetRotation() * vNeckDir2;

										pRAG->DrawLine( pos+Vec3(0,0,0.25f), ColorF(0.7f,0.5f,0.0f,1.0f), pos +Vec3(0,0,0.5f)+ vLookDir, ColorF(0.7f,0.5f,0.0f,1.0f) );
										//pRAG->DrawLine( pos+Vec3(0,0,0.25f), ColorF(0.1f,0.1f,0.0f,1.0f), pos +Vec3(0,0,0.5f)+ vNeckDir0, ColorF(0.1f,0.1f,0.1f,1.0f) );
										pRAG->DrawLine( pos+Vec3(0,0,0.25f), ColorF(0.9f,0.9f,0.9f,1.0f), pos +Vec3(0,0,0.5f)+ vNeckDir1, ColorF(0.9f,0.9f,0.9f,1.0f) );
										//pRAG->DrawLine( pos+Vec3(0,0,0.25f), ColorF(0.1f,0.1f,0.1f,1.0f), pos +Vec3(0,0,0.5f)+ vNeckDir2, ColorF(0.9f,0.9f,0.9f,1.0f) );
										//pRAG->DrawSphere( m_lmPos, 0.2f, lmColor );
									}
								}
							}
						}
					}
				}
				else
				{
					mr.ClearLookTarget();
					m_pMovementController->RequestMovement(mr);
				}
				// Debug draw the direction helper
				{
					ICharacterInstance * pCharacter = GetEntity()->GetCharacter(0);
					if (pCharacter)
					{
						ISkeleton * pSkeleton = pCharacter->GetISkeleton();
						if(pSkeleton)
						{
							int idJoint=pSkeleton->GetIDByName("Bip01 Neck");

							if(-1!=idJoint)
							{

								Vec3 vLookDir;//pSkeleton->GetMoveDirection();
								//	right=		Matrix.GetColumn(0)
								// forward=		Matrix.GetColumn(1)
								// up=				Matrix.GetColumn(2)
								//Vec3 vNeckDir0=pSkeleton->GetAbsJMatrixByID(idJoint).GetColumn(0);
								Vec3 vNeckDir1=pSkeleton->GetAbsJMatrixByID(idJoint).GetColumn(1);
								//Vec3 vNeckDir2=pSkeleton->GetAbsJMatrixByID(idJoint).GetColumn(2);
								{
									IRenderAuxGeom * pRAG = GetISystem()->GetIRenderer()->GetIRenderAuxGeom();
									Vec3 pos = GetEntity()->GetWorldPos();
									//vLookDir = (pLookTarget->GetWorldPos()-GetEntity()->GetWorldPos()); //.GetNormalized();
									//vNeckDir0 = GetEntity()->GetRotation() * vNeckDir0;
									vNeckDir1 = GetEntity()->GetRotation() * vNeckDir1;
									//vNeckDir2 = GetEntity()->GetRotation() * vNeckDir2;

									//pRAG->DrawLine( pos+Vec3(0,0,0.25f), ColorF(0.7f,0.5f,0.0f,1.0f), pos +Vec3(0,0,0.5f)+ vLookDir, ColorF(0.7f,0.5f,0.0f,1.0f) );
									//pRAG->DrawLine( pos+Vec3(0,0,0.25f), ColorF(0.1f,0.1f,0.0f,1.0f), pos +Vec3(0,0,0.5f)+ vNeckDir0, ColorF(0.1f,0.1f,0.1f,1.0f) );
									pRAG->DrawLine( pos, ColorF(0.9f,0.0f,0.0f,1.0f), pos +GetEntity()->GetRotation()*Vec3(0,3,0), ColorF(0.9f,0.0f,0.9f,1.0f) );
									pRAG->DrawLine( pos+Vec3(0,0,0.25f), ColorF(0.9f,0.9f,0.9f,1.0f), pos +Vec3(0,0,0.5f)+ vNeckDir1, ColorF(0.9f,0.9f,0.9f,1.0f) );
									//pRAG->DrawLine( pos+Vec3(0,0,0.25f), ColorF(0.1f,0.1f,0.1f,1.0f), pos +Vec3(0,0,0.5f)+ vNeckDir2, ColorF(0.9f,0.9f,0.9f,1.0f) );
									//pRAG->DrawSphere( m_lmPos, 0.2f, lmColor );
								}
							}
						}
					}
				}

			}

			SActorFrameMovementParams frameMovementParams;
			if (m_pMovementController->Update(frameTime, frameMovementParams))
			{
				Matrix33 baseMtxBackup = m_baseMtx;

				// process and apply movement requests
				CPlayerRotation playerRotation( *this, frameMovementParams, frameTime );
				playerRotation.Process();
				playerRotation.Commit(*this);

				if (m_forcedRotation)
				{
					m_baseMtx = baseMtxBackup;
					m_forcedRotation = false;
				}

				CPlayerMovement playerMovement( *this, frameMovementParams, frameTime );
				playerMovement.Process();
				playerMovement.Commit(*this);

				if (frameMovementParams.stance != STANCE_NULL)
					SetStance(frameMovementParams.stance);

				SetIK(frameMovementParams);
			}
		}
		else if (pLinkedEntity)
		{
			SActorFrameMovementParams frameMovementParams;
			m_pMovementController->Update(frameTime, frameMovementParams);
		}

		//offset the character so its hip is at entity's origin
		ICharacterInstance *pCharacter = GetEntity() ? GetEntity()->GetCharacter(0) : NULL;

		if (pCharacter)
		{
			if (!pLinkedEntity && !m_stats.followCharacterHead)
			{
				int32 joint_id = GetBoneID(BONE_BIP01);

				m_modelOffset.y = 0.0f;
				m_modelOffset.x = 0.0f;

				/*if (joint_id>=0)
				//m_modelOffset.y = -bip01->GetBonePosition().y;
				m_modelOffset.y = -pCharacter->GetISkeleton()->GetAbsJPositionByID(joint_id).y;*/

				if (client && !IsThirdPerson()) 
				{
					Vec3 offset(GetStanceViewOffset(m_stance,true,true));
					Vec3 viewDir(m_viewMtxFinal.GetColumn(1));

					float lookDown(viewDir * m_baseMtx.GetColumn(2));

					Matrix33 mdlMtx(GetEntity()->GetRotation());
					mdlMtx.Invert();

					viewDir = mdlMtx * viewDir;
					viewDir.z = 0;
					viewDir.Normalize();

					float offsetY(offset.y + max(-lookDown,0.0f)*0.3f + m_stats.flatSpeed*0.025f);
					if (joint_id>=0)
						//offsetY -= bip01->GetBonePosition().y;
						offsetY -= pCharacter->GetISkeleton()->GetAbsJPositionByID(joint_id).y;

					m_modelOffset.y = -viewDir.y * offsetY;
					m_modelOffset.x = -viewDir.x * offsetY;
				}

				GetEntity()->SetSlotLocalTM(0,Matrix34::CreateTranslationMat(m_modelOffset));
			}

			if (client)
			{
				//pCharacter->SetFlags(pCharacter->GetFlags() | CS_FLAG_UPDATE_ALWAYS);
				pCharacter->GetISkeleton()->SetAlwaysUpdate(1);
			}
		}
		//

		//DebugMessage("flatSpeed:%f", m_stats.flatSpeed);

		DisplayDebugInfo();
	}
	else
	{
		UpdateAsDoll(ctx);  // Callback for inherited CPlayer classes
	}
}

IActorMovementController * CPlayer::CreateMovementController()
{
	return new CPlayerMovementController(this);
}

void CPlayer::SetIK( const SActorFrameMovementParams& frameMovementParams )
{
	if (!IsThirdPerson())
		return;

	IAnimationGraphState * pGraph = m_pAnimatedCharacter?m_pAnimatedCharacter->GetAnimationGraphState():NULL;
	if (!pGraph)
		return;

	SMovementState curMovementState;
	m_pMovementController->GetMovementState(curMovementState);

	IEntity * pEntity = GetEntity();
	pGraph->SetInput( m_inputUsingLookIK, int(frameMovementParams.lookIK || frameMovementParams.aimIK) );
	if (ICharacterInstance * pCharacter = pEntity->GetCharacter(0))
	{
		ISkeleton * pSkeleton = pCharacter->GetISkeleton();
		if (frameMovementParams.lookIK)
		{
			if(IsClient())
			{
				float lookIKBlends[5];
				// Emphasize the head more for the player.
				lookIKBlends[0] = 0.04f;		// spine 1
				lookIKBlends[1] = 0.06f;		// spine 2
				lookIKBlends[2] = 0.08f;		// spine 3
				lookIKBlends[3] = 0.10f;		// neck
				lookIKBlends[4] = 0.85f;		// head   // 0.60f

				pSkeleton->SetLookIK( true, gf_PI*0.7f, frameMovementParams.lookTarget,lookIKBlends );
			}
			else
			{
				pSkeleton->SetLookIK( true, gf_PI*0.7f, frameMovementParams.lookTarget);
			}
		}
		else
			pSkeleton->SetLookIK( false, 0, ZERO );
		const char * aimPose = 0;
		if (frameMovementParams.aimIK)
			aimPose = pGraph->QueryOutput("AimPose");

		if (frameMovementParams.aimIK)
		{
			if (aimPose[0] == 0)
			{
				if (!IsPlayer() && pDebugAimIK->GetIVal()) // TODO: remove IsPlayer() when player uses LM
					GameWarning("Animation state %s has no aim pose set", m_pAnimatedCharacter->GetAnimationGraphState()->GetCurrentStateName());
			}
			else
			{
				/*SMovementState ms;
				m_pMovementController->GetMovementState(ms);
				GetISystem()->GetIRenderer()->GetIRenderAuxGeom()->DrawLine(ms.handPosition, ColorB(255,255,0,255), frameMovementParams.aimTarget, ColorB(255,255,0,255));*/

				pSkeleton->SetAimIK( 1, gf_PI*0.7f, frameMovementParams.aimTarget, aimPose );
			}
		}
		else
			pSkeleton->SetAimIK( 0, gf_PI*0.7f, frameMovementParams.aimTarget, aimPose );

		if (frameMovementParams.aimIK)
		{
			ICVar *pg_aimDebug = GetISystem()->GetIConsole()->GetCVar("g_aimdebug");
			if (pg_aimDebug && pg_aimDebug->GetIVal()!=0)
				GetISystem()->GetIRenderer()->GetIRenderAuxGeom()->DrawSphere(  frameMovementParams.aimTarget, 0.5f, ColorB(255,0,255,255) );
		}
	}
}

void CPlayer::UpdateView(SViewParams &viewParams)
{
	CPlayerView playerView(*this,viewParams);
	playerView.Process(viewParams);
	playerView.Commit(*this,viewParams);

	if (!IsThirdPerson())
  {
    float animControlled = m_pAnimatedCharacter->FilterView(viewParams);
		
    if (animControlled >= 1.f)
    { 
      m_baseMtx = m_viewMtx = m_viewMtxFinal = Matrix33(viewParams.rotation);
      m_lastAnimContPos = viewParams.position;
      m_lastAnimContRot = viewParams.rotation;
    }
    else if (animControlled>0.f && m_lastAnimControlled > animControlled)
    {
      m_viewMtx = m_viewMtxFinal = Matrix33(m_lastAnimContRot);      
      viewParams.position = m_lastAnimContPos;
      viewParams.rotation = m_lastAnimContRot;
    }    
    m_lastAnimControlled = animControlled;
  }
}

void CPlayer::LinkToVehicle(EntityId vehicleId) 
{
	m_vehicleId = vehicleId;
	bool enabled = false;

	IPhysicalEntity *pPhysEnt = GetEntity()->GetPhysics();

	if (pPhysEnt)
	{
		pe_player_dynamics pD;

		IEntity* pLinkedEntity = GetLinkedEntity();

		if (pLinkedEntity)
		{
			enabled = true;

			pD.bActive = false;

			m_modelOffset.Set(0,0,0);
			GetEntity()->SetSlotLocalTM(0,Matrix34::CreateTranslationMat(m_modelOffset));

			pe_action_move actionMove;
			actionMove.dir = Vec3(0,0,0);
			pPhysEnt->Action(&actionMove);

			ResetAnimations();

			IAnimationGraphState * pState = m_pAnimatedCharacter ? m_pAnimatedCharacter->GetAnimationGraphState() : NULL;
			if (pState)
			{
//				CActor::UpdateState(pState);

				strcpy(m_stats.vehicleName, pLinkedEntity->GetClass()->GetName());
				//pState->SetInput(m_inputVehicleName, m_stats.vehicleName);
				//pState->SetInput(m_inputVehicleSeat, m_stats.vehicleSeat);
			}
		}
		else
		{
			pD.bActive = true;
		}

		pPhysEnt->SetParams(&pD);
	}

	if (m_pAnimatedCharacter)
	{
		SAnimatedCharacterParams params = m_pAnimatedCharacter->GetParams();
		
		if (enabled)
		{
			params.flags &= ~eACF_EnableMovementProcessing;
			params.flags |= eACF_NoLMErrorCorrection;
		}
		else
		{
			params.flags |= eACF_EnableMovementProcessing;
			params.flags &= ~eACF_NoLMErrorCorrection;
		}
		
		m_pAnimatedCharacter->SetParams( params );
	}
}

void CPlayer::StanceChanged(EStance last)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	float delta(GetStanceInfo(last)->modelOffset.z - GetStanceInfo(m_stance)->modelOffset.z);
	if (delta>0.0f)
		m_modelOffset.z -= delta;

	IPhysicalEntity *pPhysEnt = GetEntity()->GetPhysics();

	//this is to keep track of the eyes height
	if (pPhysEnt)
	{
		pe_player_dimensions playerDim;
		pPhysEnt->GetParams(&playerDim);
		m_stats.heightPivot = playerDim.heightPivot;
	}

	if(GetEntity() && GetEntity()->GetAI())
	{
		IAIObject* pAIObject = GetEntity()->GetAI();
		int iGroupID = pAIObject->GetParameters().m_nGroup;
		IAIObject* pLeader = GetISystem()->GetAISystem()->GetLeaderAIObject(iGroupID);
		if(pLeader==pAIObject) // if the leader is changing stance
		{
			IAISignalExtraData* pData = GetISystem()->GetAISystem()->CreateSignalExtraData();
			pData->iValue = m_stance;
			GetISystem()->GetAISystem()->SendSignal(SIGNALFILTER_LEADER,1,"OnChangeStance",pLeader,pData);
		}
	}

	bool player(IsPlayer());

	//TODO:move the dive impulse in the processmovement function, I want all the movement related there.
	//and remove the client check!
	if (pPhysEnt && player && m_stance == STANCE_PRONE && m_stats.flatSpeed>1.0)
	{
		pe_action_impulse actionImp;

		Vec3 diveDir(m_stats.velocity.GetNormalized());
		diveDir += m_baseMtx.GetColumn(2) * 0.35f;

		actionImp.impulse = diveDir.GetNormalized() * m_stats.mass * 3.0f;
		actionImp.iApplyTime = 0;
		pPhysEnt->Action(&actionImp);
	}

/*
	if (!player)
		m_stats.waitStance = 1.0f;
	else if (m_stance == STANCE_PRONE || last == STANCE_PRONE)
		m_stats.waitStance = 0.5f;
*/
}

float CPlayer::GetStanceMaxSpeed(EStance stance) const
{
	return GetStanceInfo(stance)->maxSpeed * m_params.speedMultiplier;
}

void CPlayer::SetParams(SmartScriptTable &rTable,bool resetFirst)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	//not sure about this
	if (resetFirst)
	{
		m_params = SPlayerParams();
	}

	CActor::SetParams(rTable,resetFirst);

	rTable->GetValue("sprintMultiplier",m_params.sprintMultiplier);
	rTable->GetValue("strafeMultiplier",m_params.strafeMultiplier);
	rTable->GetValue("backwardMultiplier",m_params.backwardMultiplier);
	rTable->GetValue("grabMultiplier",m_params.grabMultiplier);
	rTable->GetValue("afterburnerMultiplier",m_params.afterburnerMultiplier);

	rTable->GetValue("inertia",m_params.inertia);
	rTable->GetValue("inertiaAccel",m_params.inertiaAccel);

	bool activateNanoSuit;
	if (rTable->GetValue("activateNanoSuit",activateNanoSuit))
	{
		m_nanoSuit.Activate(activateNanoSuit);
	}

	rTable->GetValue("jumpHeight",m_params.jumpHeight);

	rTable->GetValue("leanShift",m_params.leanShift);
	rTable->GetValue("leanAngle",m_params.leanAngle);

	rTable->GetValue("thrusterImpulse",m_params.thrusterImpulse);
	rTable->GetValue("thrusterStabilizeImpulse",m_params.thrusterStabilizeImpulse);

	rTable->GetValue("gravityBootsMultipler",m_params.gravityBootsMultipler);

	rTable->GetValue("headBobbingMultiplier",m_params.headBobbingMultiplier);
	rTable->GetValue("weaponBobbingMultiplier",m_params.weaponBobbingMultiplier);
	rTable->GetValue("weaponInertiaMultiplier",m_params.weaponInertiaMultiplier);

	rTable->GetValue("viewPivot",m_params.viewPivot);
	rTable->GetValue("viewDistance",m_params.viewDistance);
	rTable->GetValue("viewHeightOffset",m_params.viewHeightOffset);

	rTable->GetValue("ladderTop",m_params.ladderTop);
	rTable->GetValue("ladderBottom",m_params.ladderBottom);

	rTable->GetValue("speedMultiplier",m_params.speedMultiplier);

	ICharacterInstance *pCharacter = GetEntity()->GetCharacter(0);
	if (pCharacter)
	{
		//if (m_IKLeftHandPos.len2()<0.01f)
		//pCharacter->SetLimbIKGoal(LIMB_LEFT_ARM);

		//if (m_IKRightHandPos.len2()<0.01f)
		//pCharacter->SetLimbIKGoal(LIMB_RIGHT_ARM);
	}

	//view related
	rTable->GetValue("viewLimitDir",m_params.vLimitDir);
	rTable->GetValue("viewLimitYaw",m_params.vLimitRangeH);
	rTable->GetValue("viewLimitPitch",m_params.vLimitRangeV);

	rTable->GetValue("hudOffset", m_params.hudOffset);
	rTable->GetValue("hudAngleOffset", (Vec3 &)m_params.hudAngleOffset);

	rTable->GetValue("viewFoVScale",m_params.viewFoVScale);
	rTable->GetValue("viewSensitivity",m_params.viewSensitivity);

	//TODO:move to SetStats()
	rTable->GetValue("followCharacterHead",m_stats.followCharacterHead);
}

//fill the status table for the scripts
bool CPlayer::GetParams(SmartScriptTable &rTable)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	rTable->SetValue("sprintMultiplier", m_params.sprintMultiplier);
	rTable->SetValue("strafeMultiplier", m_params.strafeMultiplier);
	rTable->SetValue("backwardMultiplier", m_params.backwardMultiplier);
	rTable->SetValue("grabMultiplier",m_params.grabMultiplier);

	rTable->SetValue("jumpHeight", m_params.jumpHeight);
	rTable->SetValue("leanShift", m_params.leanShift);
	rTable->SetValue("leanAngle", m_params.leanAngle);
	rTable->SetValue("thrusterImpulse", m_params.thrusterImpulse);
	rTable->SetValue("thrusterStabilizeImpulse", m_params.thrusterStabilizeImpulse);
	rTable->SetValue("gravityBootsMultiplier", m_params.gravityBootsMultipler);


	rTable->SetValue("headBobbingMultiplier",m_params.headBobbingMultiplier);
	rTable->SetValue("weaponInertiaMultiplier",m_params.weaponInertiaMultiplier);
	rTable->SetValue("weaponBobbingMultiplier",m_params.weaponBobbingMultiplier);

	rTable->SetValue("speedMultiplier",m_params.speedMultiplier);

	REUSE_VECTOR(rTable, "viewPivot", m_params.viewPivot);

	rTable->SetValue("viewDistance",m_params.viewDistance);
	rTable->SetValue("viewHeightOffset",m_params.viewHeightOffset);

	REUSE_VECTOR(rTable, "hudOffset", m_params.hudOffset);
	REUSE_VECTOR(rTable, "hudAngleOffset", m_params.hudAngleOffset);

	//view related
	REUSE_VECTOR(rTable, "viewLimitDir",m_params.vLimitDir);
	rTable->SetValue("viewLimitYaw",m_params.vLimitRangeH);
	rTable->SetValue("viewLimitPitch",m_params.vLimitRangeV);

	rTable->SetValue("viewFoVScale",m_params.viewFoVScale);
	rTable->SetValue("viewSensitivity",m_params.viewSensitivity);

	return true;
}
void CPlayer::SetStats(SmartScriptTable &rTable)
{
	CActor::SetStats(rTable);

	rTable->GetValue("inFiring",m_stats.inFiring);
	rTable->GetValue("vehicleSeat",m_stats.vehicleSeat);

	const char *pStr;
	if (rTable->GetValue("vehicleName",pStr))
	{
		strncpy(m_stats.vehicleName,pStr,32);
		m_stats.vehicleName[31] = 0;
	}

	int godMode;
	if (rTable->GetValue("godMode",godMode))
		m_stats.godMode = godMode?true:false;
}

//fill the status table for the scripts
bool CPlayer::GetStats(SmartScriptTable &rTable)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	REUSE_VECTOR(rTable, "HUDAngles", m_stats.HUDAngles);
	REUSE_VECTOR(rTable, "HUDPos", m_stats.HUDPos);
	REUSE_VECTOR(rTable, "FPWeaponAngles", m_stats.FPWeaponAngles);
	REUSE_VECTOR(rTable, "FPWeaponPos", m_stats.FPWeaponPos);
	REUSE_VECTOR(rTable, "FPSecWeaponAngles", m_stats.FPSecWeaponAngles);
	REUSE_VECTOR(rTable, "FPSecWeaponPos", m_stats.FPSecWeaponPos);
	REUSE_VECTOR(rTable, "velocity", m_stats.velocity);
	/*REUSE_VECTOR(rTable, "leftFootPos", m_feetWpos[0]);
	REUSE_VECTOR(rTable, "rightFootPos", m_feetWpos[1]);*/
	REUSE_VECTOR(rTable, "groundNormal", Vec3(0,0,0));

	rTable->SetValue("inAir",m_stats.inAir);
	rTable->SetValue("onGround",m_stats.onGround);
	rTable->SetValue("inWater",m_stats.inWater);
	rTable->SetValue("flatSpeed",m_stats.flatSpeed);
	rTable->SetValue("thirdPerson",IsThirdPerson());
	rTable->SetValue("firstPersonBody",m_stats.firstPersonBody);
	rTable->SetValue("isFrozen",m_stats.isFrozen);
	rTable->SetValue("isWalkingOnWater",m_stats.isWalkingOnWater);
	rTable->SetValue("waterLevel",m_stats.waterLevel);
	rTable->SetValue("shakeAmount",m_stats.shakeAmount);	
	rTable->SetValue("followCharacterHead",m_stats.followCharacterHead);	

	rTable->SetValue("godMode",m_stats.godMode);
	rTable->SetValue("inFiring",m_stats.inFiring);

	bool gboots(IsZeroG() && m_actions & ACTION_GRAVITYBOOTS);
	rTable->SetValue("gravityBoots",gboots);

	rTable->SetValue("stance",m_stance);

	rTable->SetToNull("groundSurfaceType");
	rTable->SetToNull("groundCollider");
	rTable->SetToNull("groundColliderPartId");

	REUSE_VECTOR(rTable, "groundNormal", m_stats.groundNormal);

	rTable->SetValue("nanoSuitHeal", m_nanoSuit.GetHealthRegenAdjuster());
	rTable->SetValue("nanoSuitArmor",m_nanoSuit.GetSlotValue(NANOSLOT_ARMOR));

	IPhysicalEntity *pPhysEnt = GetEntity()->GetPhysics();

	if (pPhysEnt)
	{
		pe_status_living livStat;

		if (pPhysEnt->GetStatus(&livStat))
		{
			if (livStat.groundSurfaceIdx >= 0)
			{
				ISurfaceType *pSurfaceType = GetISystem()->GetI3DEngine()->GetMaterialManager()->GetSurfaceType(livStat.groundSurfaceIdx);
				IScriptTable *pTable = pSurfaceType ? pSurfaceType->GetScriptTable() : NULL;

				if (pTable)
					rTable->SetValue("groundSurfaceType",pTable);
			}

			if (livStat.pGroundCollider)
			{
				IEntity *pEntity = GetISystem()->GetIEntitySystem()->GetEntityFromPhysics(livStat.pGroundCollider);
				if (pEntity)
				{
					IScriptTable *pTable = pEntity->GetScriptTable();

					if (pTable)
					{
						rTable->SetValue("groundCollider", pTable);
						rTable->SetValue("groundColliderPartId", livStat.iGroundColliderPart);
					}
				}
			}
		}
	}

	return true;
}

void CPlayer::UpdateStats(float frameTime)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	if (!GetEntity())
		return;

	CHUD * pHud = GetHUD();

	//update god mode status
	if(pHud && IsClient())
		pHud->SetGODMode(m_stats.godMode);

	IPhysicalEntity *pPhysEnt = GetEntity()->GetPhysics();

	if (!pPhysEnt)
		return;

	pe_player_dynamics simPar;
	pPhysEnt->GetParams(&simPar);

	//if (GetLinkedVehicle())
	if (!simPar.bActive || m_stats.flyMode)
	{
		m_stats.velocity = m_stats.velocityUnconstrained.Set(0,0,0);
		m_stats.flatSpeed = 0.0f;
		m_stats.isFalling = false;
		m_stats.fallDistance = 0.0f;
		m_stats.inFiring = 0;
		m_stats.jumpLock = 0;

		pe_player_dynamics simParSet;
		simParSet.bSwimming = true;
		pPhysEnt->SetParams(&simParSet);

		return;
	}

	//retrieve some information about the status of the player
	pe_status_dynamics dynStat;
	pe_status_living livStat;

	pPhysEnt->GetStatus(&dynStat);
	pPhysEnt->GetStatus(&livStat);

	m_stats.physCamOffset = livStat.camOffset;
	m_stats.gravity = simPar.gravity;

	//check for ZeroG
	m_stats.inZeroG = CheckZeroG(m_stats.gyroScopeNormal);

	//
	if (livStat.bFlying)
		m_stats.groundNormal.Set(0,0,1);
	else
		m_stats.groundNormal = livStat.groundSlope;
	//

	Vec3 ppos(GetEntity()->GetWorldPos());
	bool bootableSurface(false);

	bool groundMatBootable(IsMaterialBootable(livStat.groundSurfaceIdx));

	if (m_actions & ACTION_GRAVITYBOOTS && IsZeroG() && m_stats.onGroundWBoots>=0.0f)
	{
		bootableSurface = true;

		Vec3 surfaceNormal(0,0,0);
		int surfaceNum(0);

		ray_hit hit;
		int rayFlags = (COLLISION_RAY_PIERCABILITY & rwi_pierceability_mask);

		if (m_stats.onGroundWBoots>0.001f)
		{
			Vec3 testSpots[5];
			testSpots[0] = m_baseMtx.GetColumn(2) * -1.3f;

			Vec3 offset(dynStat.v * 0.35f);
			testSpots[1] = testSpots[0] + m_baseMtx.GetColumn(0) * 0.75f + offset;
			testSpots[2] = testSpots[0] + m_baseMtx.GetColumn(1) * 0.75f + offset;
			testSpots[3] = testSpots[0] - m_baseMtx.GetColumn(0) * 0.75f + offset;
			testSpots[4] = testSpots[0] - m_baseMtx.GetColumn(1) * 0.75f + offset;

			for(int i=0;i<5;++i)
			{
				if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(ppos, testSpots[i], ent_terrain|ent_static|ent_rigid, rayFlags, &hit, 1, &pPhysEnt, 1) && IsMaterialBootable(hit.surface_idx))
				{
					surfaceNormal += hit.n;
					//GetISystem()->GetIRenderer()->GetIRenderAuxGeom()->DrawLine(hit.pt, ColorB(0,255,0,100), hit.pt + hit.n, ColorB(255,0,0,100));
					++surfaceNum;
				}
			}
		}
		else
		{
			if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(ppos, m_baseMtx.GetColumn(2) * -1.3f, ent_terrain|ent_static|ent_rigid, rayFlags, &hit, 1, &pPhysEnt, 1) && IsMaterialBootable(hit.surface_idx))
			{
				surfaceNormal = hit.n;
				surfaceNum = 1;
			}
		}

		Vec3 gravityVec;

		if (surfaceNum)
		{
			Vec3 newUp(surfaceNormal/(float)surfaceNum);

			m_stats.upVector = Vec3::CreateSlerp(m_stats.upVector,newUp.GetNormalized(),3.0f*frameTime);

			gravityVec = -m_stats.upVector * 9.81f;
			//GetISystem()->GetIRenderer()->GetIRenderAuxGeom()->DrawLine(ppos, ColorB(255,255,0,255), ppos - gravityVec, ColorB(255,255,0,255));
		}
		else 
		{
			m_stats.upVector = m_upVector;
			gravityVec = -m_stats.upVector * 9.81f;
		}

		if (!livStat.bFlying || m_stats.onGroundWBoots>0.001f)
		{
			if (groundMatBootable)
				m_stats.onGroundWBoots = 0.5f;

			livStat.bFlying = false;

			pe_player_dynamics newGravity;
			newGravity.gravity = gravityVec;
			pPhysEnt->SetParams(&newGravity);
		}
	}
	else if (!IsZeroG())
	{
		if (IsOnLadder())
			m_stats.upVector = (m_params.ladderTop - m_params.ladderBottom).GetNormalized();
		else if (m_stance != STANCE_PRONE)
			m_stats.upVector.Set(0,0,1);
		else
			m_stats.upVector = m_stats.groundNormal;
	}
	else
	{
		m_stats.upVector = m_upVector;
	}

	//
	if (m_stats.forceUpVector.len2()>0.01f)
	{
		m_stats.upVector = m_stats.forceUpVector;
		m_stats.forceUpVector.zero();
	}
	//

	//update status table
	if (livStat.bFlying)
	{
		m_stats.inAir += frameTime;
		m_stats.onGround = 0.0f;
	}
	else
	{
		if (bootableSurface && m_stats.onGroundWBoots<0.001f && !groundMatBootable)
		{
			bootableSurface = false;
		}
		else
		{
			bool landed(false);
			if (m_stats.inAir>0.5f && m_stats.onGround<0.0001f)
				landed = true;

			m_stats.onGround += frameTime;
			m_stats.inAir = 0.0f;

			if (landed)
			{
				m_stats.landed = true;
				m_stats.jumpLock = 0.3f;
			}

			if (landed || m_stats.fallDistance)
			{
				CreateScriptEvent("fall",max(0.0f,m_stats.fallDistance));
				m_stats.fallDistance = 0.0f;
			}
		}
	}

	m_stats.velocity = livStat.vel-livStat.velGround;//dynStat.v;
	m_stats.velocityUnconstrained = dynStat.v;

	//calculate the flatsped from the player ground orientation
	Matrix33 baseMtxZ(m_baseMtx * Matrix33::CreateScale(Vec3(0,0,1)));

	Vec3 flatVel = m_stats.velocity - baseMtxZ * m_stats.velocity;
	m_stats.flatSpeed = flatVel.len();

	if (m_stats.inAir && m_stats.velocity*m_stats.gravity>0.0f && !m_stats.inWater && !IsOnLadder())
	{
		if (!m_stats.isFalling)
		{
			// fall begins here
			m_stats.startFallPos = ppos;
			m_stats.isFalling = true;
			m_stats.fallDistance = 0.0f;
		}
		else
		{
			float dh = (m_baseMtx.GetInverted() * (m_stats.startFallPos - ppos)).z;

			if (dh > m_stats.fallDistance)
				m_stats.fallDistance = dh;

			//CryLogAlways( "[player] falling %f", m_stats.fallDistance);
		}
	}
	else
	{
		m_stats.isFalling = false;
		m_stats.fallDistance = 0.0f;
		//CryLogAlways( "[player] end falling %f", ppos.z);
	}

	m_stats.mass = dynStat.mass;

	if (m_stats.flatSpeed>0.1f)
	{
		m_stats.inMovement += frameTime;
		m_stats.inRest = 0;
	}
	else
	{
		m_stats.inMovement = 0;
		m_stats.inRest += frameTime;
	}

	// check if walking on water/underwater
	float waterLevel(GetISystem()->GetI3DEngine()->GetWaterLevel(&ppos));

	m_stats.waterLevel = ppos.z + GetStanceViewOffset(m_stance).z - 0.3f - waterLevel;

	if (m_stats.waterLevel<=0.25f)
	{
		if (m_stats.inWater<=0.0f)
			CreateScriptEvent("splash",0);
		if (m_stats.waterLevel<0.0f)
		{
			m_stats.inWater += frameTime;
			m_stats.isWalkingOnWater = true && (m_stats.inAir == 0);

			m_stats.inAir = 0.0f;
		}
	}
	else
	{
		m_stats.inWater = 0.0f;
		m_stats.isWalkingOnWater = (waterLevel > ppos.z - 1);// (waterLevel>m_feetWpos[0].z || waterLevel>m_feetWpos[1].z);
	}

	/*if (m_stats.inAir>0.001f)
	Interpolate(m_modelOffset,GetStanceInfo(m_stance)->modelOffset,3.0f,frameTime,3.0f);
	else*/
	Interpolate(m_modelOffset,GetStanceInfo(m_stance)->modelOffset,5.0f,frameTime);

	if (livStat.groundHeight>-0.001f)
		m_groundElevation = livStat.groundHeight - ppos*GetEntity()->GetRotation().GetColumn2();

	if (m_stats.inAir<0.5f)
	{
		/*Vec3 touch_point = ppos + GetEntity()->GetRotation().GetColumn2()*m_groundElevation;
		GetISystem()->GetIRenderer()->GetIRenderAuxGeom()->DrawLine(touch_point, ColorB(0,255,255,255), touch_point - GetEntity()->GetRotation().GetColumn2()*m_groundElevation, ColorB(0,255,255,255));   
		GetISystem()->GetIRenderer()->GetIRenderAuxGeom()->DrawSphere(touch_point,0.12f,ColorB(0,255,0,100) );*/

		//Interpolate(m_modelOffset.z,m_groundElevation,5.0f,frameTime);
		//m_modelOffset.z = m_groundElevation;
	}

	//
	/*pe_player_dimensions ppd;

	switch (m_stance)
	{
	default:
	case STANCE_STAND:ppd.heightHead = 1.6f;break;
	case STANCE_CROUCH:ppd.heightHead = 0.8f;break;
	case STANCE_PRONE:ppd.heightHead = 0.35f;break;
	}

	ppd.headRadius = 0.3f;

	pPhysEnt->SetParams(&ppd);*/

	pe_player_dynamics simParSet;
	simParSet.bSwimming = (m_stats.flyMode || m_stats.waterLevel<0.0f || (IsZeroG()&&!bootableSurface) || IsOnLadder());
	pPhysEnt->SetParams(&simParSet);

	//update some timers
	m_stats.inFiring = max(0.0f,m_stats.inFiring - frameTime);
	m_stats.jumpLock = max(0.0f,m_stats.jumpLock - frameTime);
//	m_stats.waitStance = max(0.0f,m_stats.waitStance - frameTime);

	if (m_stats.onGroundWBoots < 0.0f)
		m_stats.onGroundWBoots = min(m_stats.onGroundWBoots + frameTime,0.0f);
	else
		m_stats.onGroundWBoots = max(m_stats.onGroundWBoots - frameTime,0.0f);

	m_stats.thrusterSprint = min(m_stats.thrusterSprint + frameTime, 1.0f);
}

void CPlayer::ToggleThirdPerson()
{
	m_stats.isThirdPerson = !m_stats.isThirdPerson;
	m_stats.firstPersonBody = (char)GetISystem()->GetIConsole()->GetCVar("cl_fpBody")->GetFVal();
}

bool CPlayer::IsThirdPerson() const
{
	//force thirdperson view for non-clients
	if (!IsClient())
		return true;
	else
		return m_stats.isThirdPerson;
}


void CPlayer::Revive( bool fromInit )
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	CActor::Revive(fromInit);

	m_actions = 0;
	m_forcedRotation = false;

	m_viewAnglesOffset.Set(0,0,0);

	m_stats = SPlayerStats();

	m_headAngles.Set(0,0,0);
	m_eyeOffset.Set(0,0,0);
	m_modelOffset.Set(0,0,0);
	m_groundElevation = 0.0f;

	m_velocity.Set(0,0,0);
	m_bobOffset.Set(0,0,0);

	m_FPWeaponOffset.Set(0,0,0);
	m_FPWeaponAngleOffset.Set(0,0,0);
	m_FPWeaponLastDirVec.Set(0,0,0);

	m_angleOffset.Set(0,0,0);

	//m_baseMtx.SetIdentity();
	m_viewMtxFinal = m_baseMtx = m_viewMtx = Matrix33(GetEntity()->GetRotation() * Quat::CreateRotationZ(gf_PI));
	m_turnTarget = GetEntity()->GetRotation();
	m_lastRequestedVelocity.Set(0,0,0);
  m_lastAnimControlled = 0.f;

	m_viewRoll = 0;
	m_upVector.Set(0,0,1);

	m_vehicleId = 0;

	GetEntity()->SetFlags(GetEntity()->GetFlags() | (ENTITY_FLAG_CASTSHADOW|ENTITY_FLAG_RECVSHADOW));
	GetEntity()->SetSlotFlags(0,GetEntity()->GetSlotFlags(0)|ENTITY_SLOT_RENDER);

	if (m_pPlayerInput.get())
		m_pPlayerInput->Reset();

	ICharacterInstance *pCharacter = GetEntity()->GetCharacter(0);

	if (pCharacter)
		pCharacter->EnableStartAnimation(true);

	ResetAnimations();
	// if we're coming from initialize, then we're not part of the game object yet -- therefore we can't
	// receive events from the animation graph, and hence, we'll delay the initial update until PostInit()
	// is called...
	//if (!fromInit)
	//	UpdateAnimGraph();

	m_nanoSuit.Reset(this);
	m_nanoSuit.Activate(m_params.nanoSuitActive);

	m_pAnimatedCharacter->GetAnimationGraphState()->SetInput( m_inputItem, "nw" );
}

bool CPlayer::IsZeroG()
{
	return m_stats.inZeroG;
}

bool CPlayer::IsOnLadder()
{
	return (m_params.ladderTop.len2()>0.01f && m_params.ladderBottom.len2()>0.01f);
}

bool CPlayer::IsMaterialBootable(int matId) const
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	ISurfaceType *pSurfaceType = GetISystem()->GetI3DEngine()->GetMaterialManager()->GetSurfaceType(matId);
	IScriptTable *pTable = pSurfaceType ? pSurfaceType->GetScriptTable() : NULL;

	bool GBootable(false);

	if (pTable)
		pTable->GetValue("GBootable",GBootable);

	return GBootable;
}

Vec3 CPlayer::GetStanceViewOffset(EStance stance,bool withLean,bool withY) const
{	
	Vec3 offset(GetStanceInfo(stance)->viewOffset);

	if (!withY)
		offset.y = 0.0f;

	//apply leaning
	if (withLean)
	{
		offset.x += m_stats.leanAmount * m_params.leanShift;
		if (stance != STANCE_PRONE)
			offset.z -= fabs(m_stats.leanAmount) * m_params.leanShift * 0.33f;
	}

	return offset;
}		

void CPlayer::RagDollize()
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	m_modelOffset.Set(0,0,0);
	GetEntity()->SetSlotLocalTM(0,Matrix34::CreateTranslationMat(m_modelOffset));

	ResetAnimations();

	CActor::RagDollize();

	m_stats.followCharacterHead = true;

	IPhysicalEntity *pPhysEnt = GetEntity()->GetPhysics();

	if (pPhysEnt)
	{
		pe_simulation_params sp;
		sp.gravity = sp.gravityFreefall = m_stats.gravity;
		sp.damping = 1.0f;
		sp.dampingFreefall = 0.0f;
		sp.mass = m_stats.mass * 2.0f;

		pPhysEnt->SetParams(&sp);
	}

	ICharacterInstance *pCharacter = GetEntity()->GetCharacter(0);
	if (pCharacter)
	{
		pCharacter->EnableStartAnimation(false);
	}
}

void CPlayer::PostPhysicalize()
{
	if (m_pAnimatedCharacter)
	{
		m_pAnimatedCharacter->ResetState();

		//set inertia, it will get changed again soon, with slidy surfaces and such
		SAnimatedCharacterParams params = m_pAnimatedCharacter->GetParams();
		params.SetInertia(m_params.inertia,m_params.inertiaAccel);
		params.flags |= eACF_EnableMovementProcessing | eACF_ZCoordinateFromPhysics | eACF_ConstrainDesiredSpeedToXY;
		m_pAnimatedCharacter->SetParams(params);
	}

	ICharacterInstance *pCharacter = GetEntity()->GetCharacter(0);
	if (!pCharacter)
		return;

	pCharacter->GetISkeleton()->SetProceduralAnimCallback(PlayerProcessBones,this);

	//set a default offset for the character, so in the editor the bbox is correct
	GetEntity()->SetSlotLocalTM(0,Matrix34::CreateTranslationMat(GetStanceInfo(STANCE_STAND)->modelOffset));

	// temporary hack for flipped client actor
	if (IsClient())
	{
		GetEntity()->SetSlotLocalTM(0, 
			Matrix34::CreateRotationZ(gf_PI) * 
			Matrix34::CreateTranslationMat(GetStanceInfo(STANCE_STAND)->modelOffset));
	}

	//force the physical proxy to be rebuilt
	m_stance = STANCE_NULL;
	SetStance(STANCE_STAND);
}

void CPlayer::UpdateAnimGraph(IAnimationGraphState * pState)
{
	CActor::UpdateAnimGraph( pState );

	if (pState)
	{
		pState->SetInput( m_inputVehicleName, m_stats.vehicleName );
		pState->SetInput( m_inputVehicleSeat, m_stats.vehicleSeat );
	}
}

void CPlayer::UpdateHUD()
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	CActor::UpdateHUD();

	float clFov(g_pGame->GetCVars()->cl_fov->GetFVal()*m_params.viewFoVScale);
	//TODO?:keep the pointer?
	g_pGame->GetIGameFramework()->GetIGameTokenSystem()->SetOrCreateToken("hud.fov", TFlowInputData((float)clFov, true));

	m_nanoSuit.UpdateHUD();	
}

void CPlayer::PostUpdate(float frameTime)
{ 
	if (m_pPlayerInput.get())
		m_pPlayerInput->PostUpdate();
}

void CPlayer::CameraShake(float angle,float shift,float duration,float frequency,Vec3 pos,int ID) 
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	float angleAmount(max(-90.0f,min(90.0f,angle)) * gf_PI/180.0f);
	float shiftAmount(shift);

	Ang3 shakeAngle(RANDOMR(0.0f,1.0f)*angleAmount*0.15f, RANDOM()*angleAmount*1.15f, RANDOM()*angleAmount*0.05f);
	Vec3 shakeShift(RANDOM()*shiftAmount,0,RANDOM()*shiftAmount);

	IView *pView = g_pGame->GetIGameFramework()->GetIViewSystem()->GetViewByEntityId(GetEntityId());
	if (pView)
		pView->SetViewShake(shakeAngle,shakeShift,duration,frequency,0.5f,ID);

	/*//if a position is defined, execute directional shake
	if (pos.len2()>0.01f)
	{
	Vec3 delta(pos - GetEntity()->GetWorldPos());
	delta.NormalizeSafe();

	float dotSide(delta * m_viewMtxFinal.GetColumn(0));
	float dotFront(delta * m_viewMtxFinal.GetColumn(1) - delta * m_viewMtxFinal.GetColumn(2));

	float randomRatio(0.5f);
	dotSide += RANDOM() * randomRatio;
	dotFront += RANDOM() * randomRatio;

	m_viewShake.angle.Set(dotFront*shakeAngle, -dotSide*shakeAngle*RANDOM()*0.5f, dotSide*shakeAngle);
	}
	else
	{
	m_viewShake.angle.Set(RANDOMR(0.0f,1.0f)*shakeAngle, RANDOM()*shakeAngle*0.15f, RANDOM()*shakeAngle*0.75f);
	}*/
}

void CPlayer::ResetAnimations()
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	ICharacterInstance *pCharacter = GetEntity()->GetCharacter(0);

	if (pCharacter)
	{
		pCharacter->GetISkeleton()->StopAnimationsAllLayers();

		if (m_pAnimatedCharacter)
			m_pAnimatedCharacter->GetAnimationGraphState()->Pause(true, eAGP_StartGame);
		//disable any IK
		//pCharacter->SetLimbIKGoal(LIMB_LEFT_LEG);
		//pCharacter->SetLimbIKGoal(LIMB_RIGHT_LEG);
		//pCharacter->SetLimbIKGoal(LIMB_LEFT_ARM);
		//pCharacter->SetLimbIKGoal(LIMB_RIGHT_ARM);

		//
		for (int i=0;i<BONE_ID_NUM;++i)
		{
			int boneID = GetBoneID(i);
			/*if (boneID>-1)
				pCharacter->GetISkeleton()->SetPlusRotation(boneID, IDENTITY);*/
		}

		pCharacter->GetISkeleton()->SetLookIK(false,0,ZERO);
	}
}

void CPlayer::SetHealth(int health )
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	CActor::SetHealth(health);

	if (GetHealth() <= 0)
		ResetAnimations();

	GetGameObject()->ChangedNetworkState(eEA_GameServerDynamic);
}

void CPlayer::LinkToVehicleRemotely(EntityId vehicleId)
{
	m_vehicleId = vehicleId;
}

IEntity *CPlayer::GetLinkedEntity() const
{
	if (!m_vehicleId)
		return NULL;

	return GetISystem()->GetIEntitySystem()->GetEntity(m_vehicleId);  
}

IVehicle *CPlayer::GetLinkedVehicle() const
{
	if (!m_vehicleId)
		return NULL;

	return g_pGame->GetIGameFramework()->GetIVehicleSystem()->GetVehicle(m_vehicleId);	
}

void CPlayer::SerializeXML( XmlNodeRef& node, bool bLoading )
{
}

void CPlayer::SetAuthority( bool auth )
{
}

//------------------------------------------------------------------------
void CPlayer::Freeze(bool frozen, float mass, const char *material)
{
	CActor::Freeze(frozen, mass, material);

  m_stats.isFrozen = frozen;

	if (IsPlayer())
	{    
		m_params.vLimitDir = (frozen) ? m_viewMtx.GetColumn(1) : Vec3(ZERO);
	}
}



float CPlayer::GetMassFactor() const
{
	/*  //code regarding complete inventory
	IInventory* pInventory = static_cast<IInventory *>(GetGameObject()->QueryExtension("Inventory"));
	if(!pInventory)
	return 1.0f;

	int itemCount = pInventory->GetCount();
	int weight = 0;
	EntityId itemId = 0;
	for(int i = 0; i < itemCount; i++)
	{
	itemId = pInventory->GetItem(i);
	if(itemId)
	weight += GetWeapon(itemId)->GetParams().mass;
	}

	if(weight)
	{
	float massFactor = 1.0f - weight / (GetActorParams()->maxGrabMass*GetActorStrength()*10.0f);
	if(massFactor <= 0)
	massFactor = 0.25f;
	if(IsZeroG())
	massFactor += (1.0f - massFactor) * 0.5f;
	return massFactor;
	}

	return 1.0f;*/

	//code regarding currentItem only
	EntityId itemId = (static_cast<IInventory *>(GetGameObject()->QueryExtension("Inventory")))->GetCurrentItem();
	if (itemId)
	{
		float mass = 0;
		if(CWeapon* weap = GetWeapon(itemId))
			mass = weap->GetParams().mass;
		else if(CItem* item = GetItem(itemId))
			mass = item->GetParams().mass;
		float massFactor = 1.0f - (mass / (m_params.maxGrabMass*GetActorStrength()*2.0f));
		if (m_stats.inZeroG)
			massFactor += (1.0f - massFactor) * 0.5f;
		return massFactor;
	}
	return 1.0f;
}

float CPlayer::GetFrozenAmount() const
{
  static ICVar* pVar = GetISystem()->GetIConsole()->GetCVar("cl_frozenSteps");    

  float steps = pVar->GetFVal();
  
  // return the next step value
  return m_frozenAmount>0 ? min(((int)(m_frozenAmount * steps))+1.f, steps)*1.f/steps : 0;
}

void CPlayer::VectorToLocal(Vec3 &v) 
{
	Matrix33 mtxRot(Matrix33::CreateFromVectors(m_viewMtx.GetColumn(0),m_viewMtx.GetColumn(1),m_upVector));
	mtxRot.NoScale();

	//Matrix33 mtxRot(m_baseMtx);
	mtxRot.Invert();

	Matrix34 finalMtx;
	finalMtx.SetIdentity();
	finalMtx.SetTranslation(-GetEntity()->GetWorldPos());

	finalMtx = mtxRot * finalMtx;

	v = finalMtx * v;
}

void CPlayer::SetAngles(const Ang3 &angles) 
{
	Matrix33 rot(Matrix33::CreateRotationXYZ(angles));
	CMovementRequest mr;
	mr.SetLookTarget( GetEntity()->GetWorldPos() + 20.0f * rot.GetColumn(1) );
	m_pMovementController->RequestMovement(mr);
}

Ang3 CPlayer::GetAngles() 
{
	Ang3 angles;
	Matrix33 rotMat(m_viewMtxFinal);
	rotMat.NoScale();

	angles.SetAnglesXYZ(rotMat);

	return angles;
}

void CPlayer::AddAngularImpulse(const Ang3 &angular,float deceleration,float duration)
{
	m_stats.angularImpulse = angular;
	m_stats.angularImpulseDeceleration = deceleration;
	m_stats.angularImpulseTimeMax = m_stats.angularImpulseTime = duration;
}

CHUD * CPlayer::GetHUD(void) 
{
	return (CHUD*)GetGameObject()->QueryExtension("HUD");
}

void CPlayer::SelectNextItem(int direction, bool keepHistory)
{
	if (m_stats.animationControlled)
		return;

	CActor::SelectNextItem(direction,keepHistory);
}

void CPlayer::Serialize( TSerialize ser, unsigned aspects )
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	CActor::Serialize(ser, aspects);

	if (ser.GetSerializationTarget() != eST_Network)
	{
		ser.BeginGroup( "BasicProperties" );
		//ser.EnumValue("stance", this, &CPlayer::GetStance, &CPlayer::SetStance, STANCE_NULL, STANCE_LAST);
		ser.Value("isFrozen", m_stats.isFrozen);
		// skip matrices... not supported
		ser.Value( "velocity", m_velocity );
		ser.Value( "vehicleId", m_vehicleId );
		ser.Value( "feetWpos0", m_feetWpos[0] );
		ser.Value( "feetWpos1", m_feetWpos[1] );
		// skip animation to play for now
		// skip currAnimW
		ser.Value( "eyeOffset", m_eyeOffset );
		ser.Value( "bobOffset", m_bobOffset );
		ser.Value( "FPWeaponLastDirVec", m_FPWeaponLastDirVec );
		ser.Value( "FPWeaponOffset", m_FPWeaponOffset );
		ser.Value( "FPWeaponAngleOffset", m_FPWeaponAngleOffset );
		ser.Value( "viewAnglesOffset", m_viewAnglesOffset );
		ser.Value( "angleOffset", m_angleOffset );
		ser.Value( "modelOffset", m_modelOffset );
		ser.Value( "headAngles", m_headAngles );
		ser.Value( "viewRoll", m_viewRoll );
		ser.Value( "upVector", m_upVector );
		ser.EndGroup();

		//set viewVector to the current view direction
		m_stats.Serialize(ser, aspects);
		m_nanoSuit.Serialize(ser, aspects);

		// perform post-reading fixups
		if (ser.IsReading())
		{
			// fixup matrices here
		}
	}
	else
	{
		ser.Value("isFrozen", m_stats.isFrozen, NSerPolicy::A_Bool());    
		if (aspects & eEA_GameServerDynamic)
		{
			ser.Value("health", m_health, NSerPolicy::AC_SimpleFloat(0, 255, 8, 15, 0));
		}
		else if (aspects & eEA_GameClientDynamic)
		{
			ser.Value("currentItemId", static_cast<CActor*>(this), &CActor::NetGetCurrentItem, &CActor::NetSetCurrentItem,
				NSerPolicy::AC_EntityId());
		}
		if (aspects & IPlayerInput::INPUT_ASPECT)
		{
			SSerializedPlayerInput serializedInput;
			if (m_pPlayerInput.get() && ser.IsWriting())
				m_pPlayerInput->GetState(serializedInput);
			serializedInput.Serialize(ser);
			if (m_pPlayerInput.get() && ser.IsReading())
				m_pPlayerInput->SetState(serializedInput);
		}
	}
}

void SPlayerStats::Serialize(TSerialize ser, unsigned aspects)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	assert( ser.GetSerializationTarget() != eST_Network );
	ser.BeginGroup("PlayerStats");

	if (ser.GetSerializationTarget() != eST_Network)
	{
		//when reading, reset the structure first.
		if (ser.IsReading())
			*this = SPlayerStats();

		ser.Value("inAir", inAir);
		ser.Value("onGround", onGround);
		ser.Value("landed", landed);
		ser.Value("jumped", jumped);
		ser.Value("inMovement", inMovement);
		ser.Value("inRest", inRest);
		ser.Value("inWater", inWater);
		ser.Value("waterLevel", waterLevel);
		ser.Value("flatSpeed", flatSpeed);
		ser.Value("gravity", gravity);
		ser.Value("mass", mass);
		ser.Value("bobCycle", bobCycle);
		ser.Value("leanAmount", leanAmount);
		ser.Value("shakeAmount", shakeAmount);
		ser.Value("physCamOffset", physCamOffset);
		ser.Value("fallDistance", fallDistance);
		ser.Value("isFalling", isFalling);
		ser.Value("startFallPos", startFallPos);
		ser.Value("isFiring", isFiring);
		ser.Value("isRagDoll", isRagDoll);
		ser.Value("isWalkingOnWater", isWalkingOnWater);
		ser.Value("followCharacterHead", followCharacterHead);
		ser.Value("firstPersonBody", firstPersonBody);
		ser.Value("flyMode", flyMode);
		ser.Value("velocity", velocity);
		ser.Value("velocityUnconstrained", velocityUnconstrained);

		ser.Value("upVector", upVector);
		ser.Value("groundNormal", groundNormal);
		ser.Value("HUDPos", HUDPos);
		ser.Value("HUDAngles", HUDAngles);
		ser.Value("FPWeaponPos", FPWeaponPos);
		ser.Value("FPWeaponAngles", FPWeaponAngles);
		ser.Value("FPSecWeaponPos", FPSecWeaponPos);
		ser.Value("FPSecWeaponAngles", FPSecWeaponAngles);
		ser.Value("isThirdPerson", isThirdPerson);
		ser.Value("godMode", godMode);
		ser.Value("thrusterSprint", thrusterSprint);
	}

	ser.EndGroup();
}

void CPlayer::CreateCodeEvent(SmartScriptTable &rTable)
{
	const char *event = NULL;
	rTable->GetValue("event",event);

	if (event && !strcmp(event,"addSuitEnergy"))
	{
		float amount(0.0f);
		rTable->GetValue("amount",amount);

		m_nanoSuit.SetEnergy(m_nanoSuit.GetSuitEnergy() + amount);
	}
	else if (event && !strcmp(event,"animationControl"))
	{
		ScriptHandle id;
		id.n = 0;

		rTable->GetValue("entId",id);
		IEntity *pEnt = (id.n)?GetISystem()->GetIEntitySystem()->GetEntity(id.n):NULL;

		Matrix34 animationTM;

		if (pEnt)
		{
			//TODO:I guess this should also attach the player to the entity, so in the case the entity moves the player does too.
			animationTM = pEnt->GetWorldTM();
		}
		else
		{
			Vec3 vecIn;
			animationTM.SetIdentity();

			if (rTable->GetValue("angle",vecIn))
				animationTM.SetRotationXYZ(vecIn);

			if (rTable->GetValue("pos",vecIn))
				animationTM.SetTranslation(vecIn);
		}

		m_pAnimatedCharacter->SetAnimationTM(animationTM);
	}
	else
		CActor::CreateCodeEvent(rTable);
}

void CPlayer::PlayAction(const char *action,const char *extension) 
{
	if (extension && strlen(extension)>1)
		strncpy(m_params.animationAppendix,extension,32);
	else
		strcpy(m_params.animationAppendix,"nw");
	m_pAnimatedCharacter->GetAnimationGraphState()->SetInput( m_inputItem, m_params.animationAppendix );
}

void CPlayer::AnimationControlled(bool activate)
{
	if (m_stats.animationControlled != activate)
	{
		m_stats.animationControlled = activate;
		m_stats.followCharacterHead = activate;

		HolsterItem(activate);

		/*
		//before the sequence starts, remove any local offset of the player model.
		if (activate)
		{
			if (m_forceWorldTM.GetTranslation().len2()<0.01f)
				m_forceWorldTM = pEnt->GetWorldTM();

			pEnt->SetWorldTM(m_forceWorldTM * Matrix34::CreateTranslationMat(-GetEntity()->GetSlotLocalTM(0,false).GetTranslation()));
		}
		//move the player at the exact position animation brought him during the sequence.
		else
		{
			Vec3 localPos(GetStanceInfo(m_stance)->modelOffset);

			int bip01ID = GetBoneID(BONE_BIP01);
			if (bip01ID>-1)
			{
				Vec3 bipPos = pEnt->GetCharacter(0)->GetISkeleton()->GetAbsJPositionByID(bip01ID);
				bipPos.z = 0;
				localPos -= bipPos;
			}

			pEnt->SetWorldTM(pEnt->GetSlotWorldTM(0) * Matrix34::CreateTranslationMat(-localPos));

			m_forceWorldTM.SetIdentity();
		}*/
	}
}

void CPlayer::HandleEvent( const SGameObjectEvent& event )
{
	if (!stricmp(event.event, "AnimationControlled"))
	{
		AnimationControlled(true);
	}
	else if (!stricmp(event.event, "GameControlled"))
	{
		AnimationControlled(false);
	}
	/*else if (!stricmp(event.event, "CameraFollowHead"))
	{
		m_stats.followCharacterHead = true;
	}
	else if (!stricmp(event.event, "NormalCamera"))
	{
		m_stats.followCharacterHead = false;
	}*/
	else if (!stricmp(event.event, "AnimateHands"))
	{
		CreateScriptEvent("AnimateHands",0,(const char *)event.param);
	}
	else
		CActor::HandleEvent(event);
}

void CPlayer::UpdateGrab(float frameTime)
{
	if (m_grabStats.grabId>0 && !IsThirdPerson())
	{
		m_grabStats.wHoldPos = GetEntity()->GetWorldPos() + m_baseMtx * (GetStanceViewOffset(m_stance)*0.25f + Vec3(-m_grabStats.lHoldPos.x,-m_grabStats.lHoldPos.y,0));
		m_grabStats.wHoldQuat = Quat(m_baseMtx);
	}

	CActor::UpdateGrab(frameTime);
}

float CPlayer::GetActorStrength() const
{
	return 1.0f + (m_nanoSuit.GetSlotValue(NANOSLOT_STRENGTH)*0.01f)*(m_pStrengthScale?m_pStrengthScale->GetFVal():1.0f);
}

//FIXME!: remove all the angles stuff from here
void CPlayer::ProcessBonesRotation(ICharacterInstance *pCharacter,float frameTime)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	CActor::ProcessBonesRotation(pCharacter,frameTime);

	if (m_stats.isRagDoll || !pCharacter)
		return;

	if (GetLinkedEntity())
		return;

	ProcessIKLegs(pCharacter,frameTime);

	Matrix33 modelMtx(GetEntity()->GetRotation());
	modelMtx.Invert();

	Vec3 localDir(modelMtx * m_viewMtx.GetColumn(1));
	Ang3 headAnglesGoal(0,0,0);

	headAnglesGoal.x = -asin(localDir.z);
	headAnglesGoal.z = cry_atan2f(-localDir.x,-localDir.y);
	headAnglesGoal.y = m_viewRoll * 3.0f;

//	return;

	Interpolate(m_headAngles,headAnglesGoal,10.0f,frameTime,30.0f);

	Ang3 headAngles(m_headAngles);

	headAngles.z *= 0.25f;//divided by 4 because four bones are used to accomplish this rotation
	headAngles.y *= 0.25f;
	headAngles.x *= 0.25f;//0.66f;

	Quat qtH;
	Quat qtV;
	Quat qtR;
	Quat qtTotal;
	Quat qtParent;
	Quat qtParentCnj;

	int16 id[3];
	id[0] = GetBoneID(BONE_SPINE);
	id[1] = GetBoneID(BONE_SPINE2);
	id[2] = GetBoneID(BONE_SPINE3);

	float leanValues[3];
	leanValues[0] = m_headAngles.y * 0.9f;
	leanValues[1] = -m_headAngles.y * 0.15f;
	leanValues[2] = -m_headAngles.y * 0.15f;

	for (int i=0;i<3;++i)
	{
		//IJoint* pBone = spineBones[i];
		int16 JointID = id[i];

		if (JointID >= 0)
		{
			if (i==2) 
			{
				headAngles.z *= 2.0f;
			}

			qtH.SetRotationAA( headAngles.z, Vec3(0.0f, 0.0f, 1.0f) );
			qtV.SetRotationAA( -headAngles.x, Vec3(1.0f, 0.0f, 0.0f) );
			qtR.SetRotationAA( leanValues[i],  Vec3(0.0f, 1.0f, 0.0f) );

			int16 parentID = pCharacter->GetISkeleton()->GetParentIDByID(id[i]);
			Quat wquat(IDENTITY);
			if (parentID>=0)
				wquat=!Quat( pCharacter->GetISkeleton()->GetAbsJMatrixByID(parentID) );

			qtParent = wquat;
			qtParentCnj = qtParent;
			qtParentCnj.w = -qtParentCnj.w;
			qtTotal = qtParent*qtR*qtV*qtH*qtParentCnj;

			//pCharacter->GetISkeleton()->SetPlusRotation(JointID, qtTotal.GetInverted() );
		}
	}
}

//TODO: clean it up, less redundancy, more efficiency etc..
void CPlayer::ProcessIKLegs(ICharacterInstance *pCharacter,float frameTime)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	static bool bOnce = true;
	static int nDrawIK = 0;
	static int nNoIK = 0;
	if (bOnce)
	{
		bOnce = false;
		GetISystem()->GetIConsole()->Register( "player_DrawIK",&nDrawIK,0 );
		GetISystem()->GetIConsole()->Register( "player_NoIK",&nNoIK,0 );
	}

	if (nNoIK)
	{
		//pCharacter->SetLimbIKGoal(LIMB_LEFT_LEG);
		//pCharacter->SetLimbIKGoal(LIMB_RIGHT_LEG);
		return;
	}

	float stretchLen(0.9f);

	switch(m_stance)
	{
	default: break;
	case STANCE_STAND: stretchLen = 1.1f;break;
	}

	//Vec3 localCenter(GetEntity()->GetSlotLocalTM(0,false).GetTranslation());
	int32 id = GetBoneID(BONE_BIP01);
	Vec3 localCenter(0,0,0);
	if (id>=0)
		//localCenter = bip01->GetBonePosition();
		localCenter = pCharacter->GetISkeleton()->GetAbsJPositionByID(id);

	Vec3 feetLpos[2];

	Matrix33 transMtx(GetEntity()->GetSlotWorldTM(0));
	transMtx.Invert();

	for (int i=0;i<2;++i)
	{
		int limb = (i==0)?LIMB_LEFT_LEG:LIMB_RIGHT_LEG;
		feetLpos[i] = Vec3(ZERO); //pCharacter->GetLimbEndPos(limb);

		Vec3 feetWpos = GetEntity()->GetSlotWorldTM(0) * feetLpos[i];
		ray_hit hit;

		float testExcursion(localCenter.z);

		int rayFlags = (COLLISION_RAY_PIERCABILITY & rwi_pierceability_mask);
		if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(feetWpos + m_baseMtx.GetColumn(2)*testExcursion, m_baseMtx.GetColumn(2)*(testExcursion*-0.95f), ent_terrain|ent_static/*|ent_rigid*/, rayFlags, &hit, 1))
		{		
			m_feetWpos[i] = hit.pt; 
			Vec3 footDelta = transMtx * (m_feetWpos[i] - feetWpos);
			Vec3 newLPos(feetLpos[i] + footDelta);

			//pCharacter->SetLimbIKGoal(limb,newLPos,ik_leg,0,transMtx * hit.n);

			//CryLogAlways("%.1f,%.1f,%.1f",hit.n.x,hit.n.y,hit.n.z);
		}
		//else
		//pCharacter->SetLimbIKGoal(limb);
	}
}
