//======== (C) Copyright 1999, 2000 Valve, L.L.C. All rights reserved. ========
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose: Client's sheild entity
//
// $Workfile:     $
// $Date:         $
// $NoKeywords: $
//=============================================================================
#include "cbase.h"
#include "C_Shield.h"
#include "tf_shieldshared.h"
#include "c_basetfplayer.h"

enum
{
	NUM_SUBDIVISIONS = 21,
};

#define EMP_WAVE_AMPLITUDE 6.0f
#define EMP_GROW_WIDTH_DELAY 0.2f
#define EMP_GROW_TIME 0.6f
#define EMP_GROW_ATTEN 0.5f
#define EMP_MIN_WIDTH 5.0f

//-----------------------------------------------------------------------------
// Flat version of the shield 
//-----------------------------------------------------------------------------

class C_ShieldFlat : public C_Shield
{
public:
	DECLARE_CLASS( C_ShieldFlat, C_Shield );
	DECLARE_CLIENTCLASS();

	C_ShieldFlat();
	~C_ShieldFlat();

	virtual void GetBounds( Vector& mins, Vector& maxs );

	virtual void AddEntity( );

	virtual void SetDormant( bool bDormant );

	// Return true if the panel is active 
	virtual bool IsPanelActive( int x, int y );

	// Gets at the control point data; who knows how it was made?
	virtual void GetShieldData( Vector const** ppVerts, float* pOpacity, float* pBlend );
	virtual const Vector& GetPoint( int x, int y );

	// Draws the model
	virtual int	DrawModel( int flags );

public:
	// networked data
	unsigned char	m_ShieldState;
	float			m_Width;
	float			m_Height;

	float			m_DeathFade;
	float			m_EMPFade;

private:
	void	ShieldMoved( void );

private:
	C_ShieldFlat( const C_ShieldFlat& );
	void ComputeEMPFade();
	void ComputeDeathFade();
	void ComputeSize( float& w, float& h );
	void PreRender( );

	Vector			m_pPositions[4];
	Vector			m_Forward;
	float			m_EnterPVSTime;

	QAngle	m_LastAngles;
	Vector	m_LastPosition;
	Vector	m_Pos[4];
};


//-----------------------------------------------------------------------------
// Data table
//-----------------------------------------------------------------------------

IMPLEMENT_CLIENTCLASS_DT(C_ShieldFlat, DT_Shield_Flat, CShieldFlat)

	RecvPropInt( RECVINFO(m_ShieldState) ),
	RecvPropFloat( RECVINFO(m_Width) ),
	RecvPropFloat( RECVINFO(m_Height) ),

END_RECV_TABLE()


//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------

C_ShieldFlat::C_ShieldFlat()
{
	m_DeathFade = 1.0f;
	m_EMPFade = 1.0f;

	InitShield( 2, 2, 6 );
}

C_ShieldFlat::~C_ShieldFlat()
{
}

//-----------------------------------------------------------------------------
// Leaving/entering the PVS on the server.
//-----------------------------------------------------------------------------

void C_ShieldFlat::SetDormant( bool bDormant )
{
	if (!bDormant)
	{
		if (m_ShieldState & SHIELD_FLAT_EMP)
		{
			m_EMPFade = 0.0f;
		}
		else
		{
			m_EMPFade = 1.0f;
		}

		m_EnterPVSTime = 0.0f;
		if (m_ShieldState & SHIELD_FLAT_INACTIVE)
		{
			m_DeathFade = 0.0f;
		}
		else
		{
			m_EnterPVSTime = gpGlobals->curtime;
			m_DeathFade = 1.0f;
		}
	}

	BaseClass::SetDormant(bDormant);
}

//-----------------------------------------------------------------------------
// Figures the EMP fade factor
//-----------------------------------------------------------------------------

void C_ShieldFlat::ComputeEMPFade()
{
	if (m_ShieldState & SHIELD_FLAT_EMP)
	{
		// Decay fade if we've been EMPed or if we're inactive
		if (m_EMPFade > 0.0f)
		{
			m_EMPFade -= gpGlobals->frametime / SHIELD_EMP_FADE_TIME;
			if (m_EMPFade < 0.0f)
			{
				m_EMPFade = 0.0f;
			}
			else
			{
				Vector dir;

				// Futz with the control points if we've been EMPed
				for (int i = 0; i < 4; ++i)
				{
					float dist = -EMP_WAVE_AMPLITUDE * sin( i * M_PI * 0.5f + gpGlobals->curtime * M_PI / SHIELD_EMP_WOBBLE_TIME );
					VectorMA( m_pPositions[i], dist, m_Forward, m_pPositions[i] );
				}
			}
		}
	}
	else
	{
		// Fade back in, no longer EMPed
		if (m_EMPFade < 1.0f)
		{
			m_EMPFade += gpGlobals->frametime / SHIELD_EMP_FADE_TIME;
			if (m_EMPFade >= 1.0f)
			{
				m_EMPFade = 1.0f;
			}
		}
	}
}


//-----------------------------------------------------------------------------
// Figures the networked fade factor
//-----------------------------------------------------------------------------

void C_ShieldFlat::ComputeDeathFade()
{
	if (m_ShieldState & SHIELD_FLAT_INACTIVE)
	{
		// Fade out when we become inactive
		if (m_DeathFade > 0.0f)
		{
			m_DeathFade -= gpGlobals->frametime / SHIELD_FLAT_SHUTDOWN_TIME;
			if (m_DeathFade < 0.0f)
			{
				m_DeathFade = 0.0f;
			}
		}
	}
	else
	{
		// Active? We should be visible
		m_DeathFade = 1.0f;
	}
}

//-----------------------------------------------------------------------------
// A little pre-render processing
//-----------------------------------------------------------------------------

void C_ShieldFlat::ComputeSize( float& w, float& h )
{
	w = m_Width;
	h = m_Height;

	float dt = gpGlobals->curtime - m_EnterPVSTime;
	if (dt > EMP_GROW_TIME)
	{
		return;
	}

	if (dt < 0)
		dt = 0.0f;

	// Attenuate it up
	w *= 1.0f - pow ( EMP_GROW_ATTEN, 10 * (dt / EMP_GROW_TIME ));

	if (w < EMP_MIN_WIDTH)
		w = EMP_MIN_WIDTH;
}

//-----------------------------------------------------------------------------
// A little pre-render processing
//-----------------------------------------------------------------------------

void C_ShieldFlat::PreRender( )
{
	// Compute the shield positions...
	Vector right, up;
	AngleVectors( GetRenderAngles(), &m_Forward, &right, &up );

	float w, h;
	ComputeSize( w, h );

	VectorMA( GetRenderOrigin(), -w * 0.5, right, m_pPositions[0] );
	VectorMA( m_pPositions[0], -h * 0.5, up, m_pPositions[0] );
	VectorMA( m_pPositions[0], w, right, m_pPositions[1] );
	VectorMA( m_pPositions[0], h, up, m_pPositions[2] );
	VectorMA( m_pPositions[2], w, right, m_pPositions[3] );

	ComputeEMPFade();
	ComputeDeathFade();

	m_FadeValue = m_DeathFade * m_EMPFade;
}

void C_ShieldFlat::AddEntity( )
{
	BaseClass::AddEntity( );
	PreRender();
}

//-----------------------------------------------------------------------------
// Bounds computation
//-----------------------------------------------------------------------------

void C_ShieldFlat::GetBounds( Vector& mins, Vector& maxs )
{
	mins.Init( -1.0/16.0f, -m_Width * 0.5f, -m_Height * 0.5f );
	maxs.Init(  1.0/16.0f,  m_Width * 0.5f,  m_Height * 0.5f );
}

//-----------------------------------------------------------------------------
// Return true if the panel is active 
//-----------------------------------------------------------------------------

bool C_ShieldFlat::IsPanelActive( int x, int y )
{
	return true;
}

//-----------------------------------------------------------------------------
// Gets at the control point data; who knows how it was made?
//-----------------------------------------------------------------------------

void C_ShieldFlat::GetShieldData( Vector const** ppVerts, float* pOpacity, float* pBlend )
{
	for ( int i = 0; i < 4; ++i )
	{
		ppVerts[i] = &m_pPositions[i];
		pOpacity[i] = 32.0f;
		pBlend[i] = 0.0f;
	}
}

//-----------------------------------------------------------------------------
// Shield points
//-----------------------------------------------------------------------------
const Vector& C_ShieldFlat::GetPoint( int x, int y ) 
{
	if ((m_LastAngles != GetAbsAngles()) || (m_LastPosition != GetAbsOrigin() ))
	{
		ShieldMoved();
	}

	int i = (x >= 1);
	i += (y >= 1) * 2;
	return m_Pos[i];
}

//-----------------------------------------------------------------------------
// Purpose: Computes the shield bounding box
//-----------------------------------------------------------------------------
void C_ShieldFlat::ShieldMoved( void )
{
	Vector forward, right, up;
	AngleVectors( GetAbsAngles(), &forward, &right, &up );

	VectorMA( GetAbsOrigin(), -m_Width * 0.5, right, m_Pos[0] );
	VectorMA( m_Pos[0], -m_Height * 0.5, up, m_Pos[0] );
	VectorMA( m_Pos[0], m_Width, right, m_Pos[1] );
	VectorMA( m_Pos[0], m_Height, up, m_Pos[2] );
	VectorMA( m_Pos[2], m_Width, right, m_Pos[3] );

	m_LastAngles = GetAbsAngles();
	m_LastPosition = GetAbsOrigin();
}

//-----------------------------------------------------------------------------
// Suppress rendering if the player owns it
//-----------------------------------------------------------------------------

int	C_ShieldFlat::DrawModel( int flags )
{
	if ( !m_bReadyToDraw )
		return 0;

	// Don't draw it if the owner is the local player
//	if ( m_OwnerEntity == C_BasePlayer::GetLocalPlayer()->index )
//		return 0;

	return BaseClass::DrawModel( flags );
}

