//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

#include "cbase.h"
#include "player_control.h"
#include "soundent.h"
#include "func_break.h"		// For materials
#include "hl2_player.h"
#include "ai_hull.h"
#include "decals.h"
#include "shake.h"
#include "ndebugoverlay.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

BEGIN_DATADESC( CPlayer_Control )

	DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
	DEFINE_FIELD( m_nSaveFOV,					FIELD_INTEGER ),
	DEFINE_FIELD( m_vSaveOrigin,				FIELD_POSITION_VECTOR ),
	DEFINE_FIELD( m_vSaveAngles,				FIELD_VECTOR ),
	DEFINE_FIELD( m_nSaveMoveType,			FIELD_INTEGER ),
	DEFINE_FIELD( m_nSaveMoveCollide,			FIELD_INTEGER ),
	DEFINE_FIELD( m_vSaveViewOffset,			FIELD_VECTOR ),
	DEFINE_FIELD( m_pSaveWeapon,				FIELD_CLASSPTR ),
	DEFINE_FIELD( m_nPClipTraceDir,			FIELD_INTEGER),
	DEFINE_FIELD( m_flCurrMinClipDist,		FIELD_FLOAT),
	DEFINE_FIELD( m_flLastMinClipDist,		FIELD_FLOAT),

	// Inputs
	DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ),
	DEFINE_INPUTFUNC( FIELD_VOID, "Deactivate", InputDeactivate ),
	DEFINE_INPUTFUNC( FIELD_FLOAT, "SetThrust", InputSetThrust ),
	DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSideThrust", InputSetSideThrust ),

END_DATADESC()



//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CPlayer_Control::Precache( void )
{
	return;
}

//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CPlayer_Control::Spawn( void )
{
	Precache();
	SetFriction( 0.55 ); // deading the bounce a bit
	
	m_bActive = false;

	CreateVPhysics();
}

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

bool CPlayer_Control::CreateVPhysics( void )
{
	// Create the object in the physics system
	VPhysicsInitNormal( SOLID_BBOX, 0, false );
	return true;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CPlayer_Control::ControlActivate( void )
{
	m_bActive			= true;
	AddEffects( EF_NODRAW );
	m_nPClipTraceDir	= PC_TRACE_LAST;
	m_flCurrMinClipDist = PCONTROL_CLIP_DIST;

	SetNextThink( gpGlobals->curtime );

	CHL2_Player*	pPlayer		= (CHL2_Player*)UTIL_GetLocalPlayer();

	Assert( pPlayer );

	// Save Data
	m_nSaveFOV			= pPlayer->GetFOV();
	m_vSaveOrigin		= pPlayer->GetLocalOrigin();
	m_vSaveAngles		= pPlayer->pl.v_angle;
	m_nSaveMoveType		= pPlayer->GetMoveType();
	m_nSaveMoveCollide	= pPlayer->GetMoveCollide();
	m_vSaveViewOffset	= pPlayer->GetViewOffset();
	m_pSaveWeapon		= pPlayer->GetActiveWeapon();

	pPlayer->AddSolidFlags( FSOLID_NOT_SOLID );
	pPlayer->SetLocalOrigin( GetLocalOrigin() );
	pPlayer->pl.v_angle	= GetLocalAngles();
	pPlayer->SetViewOffset( vec3_origin );

	UpdateTransmitState();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CPlayer_Control::ControlDeactivate( void )
{
	m_bActive = false;
	RemoveEffects( EF_NODRAW );

	SetThink(NULL);
	SetTouch(NULL);

	CHL2_Player*	pPlayer  = (CHL2_Player*)UTIL_GetLocalPlayer();

	Assert( pPlayer );
	
	// Restore Data
	pPlayer->SetFOV( this, m_nSaveFOV );
	pPlayer->RemoveSolidFlags( FSOLID_NOT_SOLID );
	pPlayer->SetLocalOrigin( m_vSaveOrigin );
	pPlayer->SetLocalAngles( m_vSaveAngles );	// Note: Set GetLocalAngles(), not pl->v_angle
	pPlayer->SnapEyeAngles( m_vSaveAngles );
	pPlayer->StopFollowingEntity();
	pPlayer->SetMoveType( m_nSaveMoveType, m_nSaveMoveCollide );
	pPlayer->SetViewOffset( m_vSaveViewOffset );
	pPlayer->SetControlClass( CLASS_NONE );

	UpdateTransmitState();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CPlayer_Control::InputActivate( inputdata_t &inputdata )
{
	ControlActivate();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CPlayer_Control::InputDeactivate( inputdata_t &inputdata )
{
	ControlDeactivate();
}


//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CPlayer_Control::InputSetThrust( inputdata_t &inputdata )
{
	// Should be handles by subclass
	return;
}


//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CPlayer_Control::InputSetSideThrust( inputdata_t &inputdata )
{
	// Should be handles by subclass
	return;
}


//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
float CPlayer_Control::PlayerControlClipDistance(void)
{
	// Send out a curb feeler, but not in every direction on every time.  Instead
	// send out a feeling in one direction each time and keep track of the nearest
	// player_control_clip
	
	// Reset if on last trace
	if (m_nPClipTraceDir == PC_TRACE_LAST)
	{
		m_nPClipTraceDir	= 0;
		m_flLastMinClipDist = m_flCurrMinClipDist;
		m_flCurrMinClipDist = PCONTROL_CLIP_DIST;
	}

	Vector vForward,vRight,vUp;
	AngleVectors( GetLocalAngles(), &vForward, &vRight,&vUp);

	Vector vTracePos = GetAbsOrigin();
	switch (m_nPClipTraceDir)
	{
		case PC_TRACE_LEFT:
		{
			vTracePos -= vRight*PCONTROL_CLIP_DIST;
		}
		case PC_TRACE_RIGHT:
		{
			vTracePos += vRight*PCONTROL_CLIP_DIST;
		}
		case PC_TRACE_UP:
		{
			vTracePos += vUp*PCONTROL_CLIP_DIST;
		}
		case PC_TRACE_DOWN:
		{
			vTracePos -= vUp*PCONTROL_CLIP_DIST;
		}
		case PC_TRACE_FORWARD:
		{
			vTracePos += 2*vForward*PCONTROL_CLIP_DIST;
		}
	}

	// Trace in a different direction next time
	m_nPClipTraceDir++;


	trace_t tr;
	UTIL_TraceLine(GetAbsOrigin(), vTracePos, CONTENTS_MIST, this, COLLISION_GROUP_NONE, &tr);
	
	if (tr.fraction < 1.0)
	{
		surfacedata_t* pSurface = physprops->GetSurfaceData( tr.surface.surfaceProps );
		char	  	   m_chTextureType = pSurface->game.material;

		if (m_chTextureType == CHAR_TEX_CLIP)
		{	
			float flClipDist = tr.fraction * (GetAbsOrigin()-vTracePos).Length();
			if (flClipDist < m_flCurrMinClipDist)
			{
				m_flCurrMinClipDist = flClipDist;
			}
			if (flClipDist < m_flLastMinClipDist)
			{
				m_flLastMinClipDist = flClipDist;
			}	
		}
	}

	return m_flLastMinClipDist;
}

//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
int CPlayer_Control::UpdateTransmitState()
{
	if ( m_bActive )
	{
		return SetTransmitState( FL_EDICT_ALWAYS );
	}
	else 
	{
		return BaseClass::UpdateTransmitState();
	}
}

