//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:		Egon
//
// $NoKeywords: $
//=============================================================================//

#include "cbase.h"
#include "npcevent.h"
#include "hl1mp_basecombatweapon_shared.h"
#include "Sprite.h"
#include "beam_shared.h"
#include "takedamageinfo.h"
//#include "basecombatcharacter.h"
//#include "AI_BaseNPC.h"

#ifdef CLIENT_DLL
#include "c_baseplayer.h"
#include "hl1/hl1_c_player.h"
#else
#include "player.h"
#include "hl1_player.h"
#endif

//#include "player.h"
#include "gamerules.h"
#include "in_buttons.h"

#ifdef CLIENT_DLL
#else
#include "soundent.h"
#include "game.h"
#endif


#include "vstdlib/random.h"
#include "engine/IEngineSound.h"
#include "IEffects.h"
#ifdef CLIENT_DLL
#include "c_te_effect_dispatch.h"
#else
#include "te_effect_dispatch.h"
#endif


enum EGON_FIRESTATE { FIRE_OFF, FIRE_STARTUP, FIRE_CHARGE };

#define EGON_PULSE_INTERVAL			0.1
#define EGON_DISCHARGE_INTERVAL		0.1

#define EGON_BEAM_SPRITE		"sprites/xbeam1.vmt"
#define EGON_FLARE_SPRITE		"sprites/XSpark1.vmt"

extern ConVar sk_plr_dmg_egon_wide;

//-----------------------------------------------------------------------------
// CWeaponEgon
//-----------------------------------------------------------------------------

#ifdef CLIENT_DLL
#define CWeaponEgon C_WeaponEgon
#endif

class CWeaponEgon : public CBaseHL1MPCombatWeapon
{
	DECLARE_CLASS( CWeaponEgon, CBaseHL1MPCombatWeapon );
public:

	DECLARE_NETWORKCLASS(); 
	DECLARE_PREDICTABLE();

    CWeaponEgon(void);

	virtual bool	Deploy( void );
	void	PrimaryAttack( void );
    virtual void    Precache( void );
    
	void	SecondaryAttack( void )
	{
		PrimaryAttack();
	}

	void	WeaponIdle( void );
	bool	Holster( CBaseCombatWeapon *pSwitchingTo = NULL );

    //	DECLARE_SERVERCLASS();
    //	DECLARE_DATADESC();

private:
	bool	HasAmmo( void );
	void	UseAmmo( int count );
	void	Attack( void );
	void	EndAttack( void );
	void	Fire( const Vector &vecOrigSrc, const Vector &vecDir );
	void	UpdateEffect( const Vector &startPoint, const Vector &endPoint );
	void	CreateEffect( void );
	void	DestroyEffect( void );

	EGON_FIRESTATE		m_fireState;
	float				m_flAmmoUseTime;	// since we use < 1 point of ammo per update, we subtract ammo on a timer.
	float				m_flShakeTime;
	float				m_flStartFireTime;
	float				m_flDmgTime;
	CHandle<CSprite>	m_hSprite;
	CHandle<CBeam>		m_hBeam;
	CHandle<CBeam>		m_hNoise;
};

IMPLEMENT_NETWORKCLASS_ALIASED( WeaponEgon, DT_WeaponEgon );

BEGIN_NETWORK_TABLE( CWeaponEgon, DT_WeaponEgon )
END_NETWORK_TABLE()    

BEGIN_PREDICTION_DATA( CWeaponEgon )
END_PREDICTION_DATA()

LINK_ENTITY_TO_CLASS( weapon_egon, CWeaponEgon );
PRECACHE_WEAPON_REGISTER( weapon_egon );

/*
IMPLEMENT_SERVERCLASS_ST( CWeaponEgon, DT_WeaponEgon )
END_SEND_TABLE()
*/

    /*
BEGIN_DATADESC( CWeaponEgon )
	DEFINE_FIELD( m_fireState, FIELD_INTEGER ),
	DEFINE_FIELD( m_flAmmoUseTime, FIELD_TIME ),
	DEFINE_FIELD( m_flShakeTime, FIELD_TIME ),
	DEFINE_FIELD( m_flStartFireTime, FIELD_TIME ),
	DEFINE_FIELD( m_flDmgTime, FIELD_TIME ),
	DEFINE_FIELD( m_hSprite, FIELD_EHANDLE ),
	DEFINE_FIELD( m_hBeam, FIELD_EHANDLE ),
	DEFINE_FIELD( m_hNoise, FIELD_EHANDLE ),
END_DATADESC()
    */

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CWeaponEgon::CWeaponEgon( void )
{
	m_bReloadsSingly	= false;
	m_bFiresUnderwater	= true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CWeaponEgon::Precache( void )
{
    PrecacheScriptSound( "Weapon_Gluon.Start" );
    PrecacheScriptSound( "Weapon_Gluon.Run" );
    PrecacheScriptSound( "Weapon_Gluon.Off" );

	PrecacheModel( EGON_BEAM_SPRITE );
	PrecacheModel( EGON_FLARE_SPRITE );

	BaseClass::Precache();
}

bool CWeaponEgon::Deploy( void )
{
	m_fireState = FIRE_OFF;

	return BaseClass::Deploy();
}

bool CWeaponEgon::HasAmmo( void )
{
	CHL1_Player *pPlayer = ToHL1Player( GetOwner() );
	if ( !pPlayer )
	{
		return false;
	}

	if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
		return false;

	return true;
}

void CWeaponEgon::UseAmmo( int count )
{
	CHL1_Player *pPlayer = ToHL1Player( GetOwner() );
	if ( !pPlayer )
	{
		return;
	}

	if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) >= count )
		pPlayer->RemoveAmmo( count, m_iPrimaryAmmoType );
	else
		pPlayer->RemoveAmmo( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ), m_iPrimaryAmmoType );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponEgon::PrimaryAttack( void )
{
	CHL1_Player *pPlayer = ToHL1Player( GetOwner() );
	if ( !pPlayer )
	{
		return;
	}

	// don't fire underwater
	if ( pPlayer->GetWaterLevel() == 3 )
	{
		if ( m_fireState != FIRE_OFF || m_hBeam )
		{
			EndAttack();
		}
		else
		{
			WeaponSound( EMPTY );
		}

		m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
		m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
		return;
	}

	Vector vecAiming	= pPlayer->GetAutoaimVector( 0 );
	Vector vecSrc		= pPlayer->Weapon_ShootPosition( );

	switch( m_fireState )
	{
		case FIRE_OFF:
		{
			if ( !HasAmmo() )
			{
				m_flNextPrimaryAttack = gpGlobals->curtime + 0.25;
				m_flNextSecondaryAttack = gpGlobals->curtime + 0.25;
				WeaponSound( EMPTY );
				return;
			}

			m_flAmmoUseTime = gpGlobals->curtime;// start using ammo ASAP.

            EmitSound( "Weapon_Gluon.Start" );                

			SendWeaponAnim( ACT_VM_PRIMARYATTACK );
						
			m_flShakeTime = 0;
			m_flStartFireTime = gpGlobals->curtime;

			SetWeaponIdleTime( gpGlobals->curtime + 0.1 );

			m_flDmgTime = gpGlobals->curtime + EGON_PULSE_INTERVAL;
			m_fireState = FIRE_STARTUP;
		}
		break;

		case FIRE_STARTUP:
		{
			Fire( vecSrc, vecAiming );
		
			if ( gpGlobals->curtime >= ( m_flStartFireTime + 2.0 ) )
			{
                EmitSound( "Weapon_Gluon.Run" );

				m_fireState = FIRE_CHARGE;
			}

			if ( !HasAmmo() )
			{
				EndAttack();
				m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
				m_flNextSecondaryAttack = gpGlobals->curtime + 1.0;
			}
		}
		case FIRE_CHARGE:
		{
			Fire( vecSrc, vecAiming );
		
			if ( !HasAmmo() )
			{
				EndAttack();
				m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
				m_flNextSecondaryAttack = gpGlobals->curtime + 1.0;
			}
		}
		break;
	}
}

void CWeaponEgon::Fire( const Vector &vecOrigSrc, const Vector &vecDir )
{
	CHL1_Player *pPlayer = ToHL1Player( GetOwner() );
	if ( !pPlayer )
	{
		return;
	}

	//CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 450, 0.1 );
    WeaponSound( SINGLE );

	Vector vecDest	= vecOrigSrc + (vecDir * MAX_TRACE_LENGTH);

	trace_t	tr;
	UTIL_TraceLine( vecOrigSrc, vecDest, MASK_SHOT, pPlayer, COLLISION_GROUP_NONE, &tr );

	if ( tr.allsolid )
		return;

	CBaseEntity *pEntity = tr.m_pEnt;
	if ( pEntity == NULL )
		return;

	if ( g_pGameRules->IsMultiplayer() )
	{
		if ( m_hSprite )
		{
			if ( pEntity->m_takedamage != DAMAGE_NO )
			{
				m_hSprite->TurnOn();
			}
			else
			{
				m_hSprite->TurnOff();
			}
		}
	}

	if ( m_flDmgTime < gpGlobals->curtime )
	{
		// wide mode does damage to the ent, and radius damage
		if ( pEntity->m_takedamage != DAMAGE_NO )
		{
			ClearMultiDamage();
			CTakeDamageInfo info( this, pPlayer, sk_plr_dmg_egon_wide.GetFloat() * g_pGameRules->GetDamageMultiplier(), DMG_ENERGYBEAM | DMG_ALWAYSGIB );
			CalculateMeleeDamageForce( &info, vecDir, tr.endpos );
			pEntity->DispatchTraceAttack( info, vecDir, &tr );
			ApplyMultiDamage();
		}

		if ( g_pGameRules->IsMultiplayer() )
		{
			// radius damage a little more potent in multiplayer.
#ifndef CLIENT_DLL
			RadiusDamage( CTakeDamageInfo( this, pPlayer, sk_plr_dmg_egon_wide.GetFloat() * g_pGameRules->GetDamageMultiplier() / 4, DMG_ENERGYBEAM | DMG_BLAST | DMG_ALWAYSGIB ), tr.endpos, 128, CLASS_NONE, NULL );
#endif
		}

		if ( !pPlayer->IsAlive() )
			return;

		if ( g_pGameRules->IsMultiplayer() )
		{
			//multiplayer uses 5 ammo/second
			if ( gpGlobals->curtime >= m_flAmmoUseTime )
			{
				UseAmmo( 1 );
				m_flAmmoUseTime = gpGlobals->curtime + 0.2;
			}
		}
		else
		{
			// Wide mode uses 10 charges per second in single player
			if ( gpGlobals->curtime >= m_flAmmoUseTime )
			{
				UseAmmo( 1 );
				m_flAmmoUseTime = gpGlobals->curtime + 0.1;
			}
		}

		m_flDmgTime = gpGlobals->curtime + EGON_DISCHARGE_INTERVAL;
		if ( m_flShakeTime < gpGlobals->curtime )
		{
#ifndef CLIENT_DLL
			UTIL_ScreenShake( tr.endpos, 5.0, 150.0, 0.75, 250.0, SHAKE_START );
#endif
			m_flShakeTime = gpGlobals->curtime + 1.5;
		}
	}

	Vector vecUp, vecRight;
	QAngle angDir;

	VectorAngles( vecDir, angDir );
	AngleVectors( angDir, NULL, &vecRight, &vecUp );

	Vector tmpSrc = vecOrigSrc + (vecUp * -8) + (vecRight * 3);
	UpdateEffect( tmpSrc, tr.endpos );
}

void CWeaponEgon::UpdateEffect( const Vector &startPoint, const Vector &endPoint )
{
	if ( !m_hBeam )
	{
		CreateEffect();
	}

	if ( m_hBeam )
	{
		m_hBeam->SetStartPos( endPoint );
	}

	if ( m_hSprite )
	{
		m_hSprite->SetAbsOrigin( endPoint );

		m_hSprite->m_flFrame += 8 * gpGlobals->frametime;
		if ( m_hSprite->m_flFrame > m_hSprite->Frames() )
			m_hSprite->m_flFrame = 0;
	}

	if ( m_hNoise )
	{
		m_hNoise->SetStartPos( endPoint );
	}
}

void CWeaponEgon::CreateEffect( void )
{
#ifndef CLIENT_DLL    
	CHL1_Player *pPlayer = ToHL1Player( GetOwner() );
	if ( !pPlayer )
	{
		return;
	}

	DestroyEffect();

	m_hBeam = CBeam::BeamCreate( EGON_BEAM_SPRITE, 3.5 );
	m_hBeam->PointEntInit( GetAbsOrigin(), this );
	m_hBeam->SetBeamFlags( FBEAM_SINENOISE );
	m_hBeam->SetEndAttachment( 1 );
	m_hBeam->AddSpawnFlags( SF_BEAM_TEMPORARY );	// Flag these to be destroyed on save/restore or level transition
	m_hBeam->SetOwnerEntity( pPlayer );
	m_hBeam->SetScrollRate( 10 );
	m_hBeam->SetBrightness( 200 );
	m_hBeam->SetColor( 50, 50, 255 );
	m_hBeam->SetNoise( 0.2 );

	m_hNoise = CBeam::BeamCreate( EGON_BEAM_SPRITE, 5.0 );
	m_hNoise->PointEntInit( GetAbsOrigin(), this );
	m_hNoise->SetEndAttachment( 1 );
	m_hNoise->AddSpawnFlags( SF_BEAM_TEMPORARY );
	m_hNoise->SetOwnerEntity( pPlayer );
	m_hNoise->SetScrollRate( 25 );
	m_hNoise->SetBrightness( 200 );
	m_hNoise->SetColor( 50, 50, 255 );
	m_hNoise->SetNoise( 0.8 );

	m_hSprite = CSprite::SpriteCreate( EGON_FLARE_SPRITE, GetAbsOrigin(), false );
	m_hSprite->SetScale( 1.0 );
	m_hSprite->SetTransparency( kRenderGlow, 255, 255, 255, 255, kRenderFxNoDissipation );
	m_hSprite->AddSpawnFlags( SF_SPRITE_TEMPORARY );
	m_hSprite->SetOwnerEntity( pPlayer );
#endif    
}


void CWeaponEgon::DestroyEffect( void )
{
#ifndef CLIENT_DLL    
	if ( m_hBeam )
	{
		UTIL_Remove( m_hBeam );
		m_hBeam = NULL;
	}
	if ( m_hNoise )
	{
		UTIL_Remove( m_hNoise );
		m_hNoise = NULL;
	}
	if ( m_hSprite )
	{
		m_hSprite->Expand( 10, 500 );
		m_hSprite = NULL;
	}
#endif
}

void CWeaponEgon::EndAttack( void )
{
    StopSound( "Weapon_Gluon.Run" );
	
	if ( m_fireState != FIRE_OFF )
	{
        EmitSound( "Weapon_Gluon.Off" );
	}

	SetWeaponIdleTime( gpGlobals->curtime + 2.0 );
	m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
	m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;

	m_fireState = FIRE_OFF;

	DestroyEffect();
}

bool CWeaponEgon::Holster( CBaseCombatWeapon *pSwitchingTo )
{
    EndAttack();

	return BaseClass::Holster( pSwitchingTo );
}

void CWeaponEgon::WeaponIdle( void )
{
	if ( !HasWeaponIdleTimeElapsed() )
		return;

	if ( m_fireState != FIRE_OFF )
		 EndAttack();
	
	int iAnim;

	float flRand = random->RandomFloat( 0,1 );
	float flIdleTime;
	if ( flRand <= 0.5 )
	{
		iAnim = ACT_VM_IDLE;
		flIdleTime = gpGlobals->curtime + random->RandomFloat( 10, 15 );
	}
	else 
	{
		iAnim = ACT_VM_FIDGET;
		flIdleTime = gpGlobals->curtime + 3.0;
	}

	SendWeaponAnim( iAnim );

	SetWeaponIdleTime( flIdleTime );
}