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

#include "cbase.h"

#include "c_portal_player.h"
#include "c_te_effect_dispatch.h"
#include "iviewrender_beams.h"
#include "model_types.h"
#include "fx_interpvalue.h"
#include "clienteffectprecachesystem.h"
#include "bone_setup.h"
#include "c_rumble.h"
#include "rumble_shared.h"

#include "weapon_portalgun_shared.h"


#define	SPRITE_SCALE 128.0f

ConVar cl_portalgun_effects_min_alpha ("cl_portalgun_effects_min_alpha", "96", FCVAR_CLIENTDLL );
ConVar cl_portalgun_effects_max_alpha ("cl_portalgun_effects_max_alpha", "128", FCVAR_CLIENTDLL );

ConVar cl_portalgun_effects_min_size ("cl_portalgun_effects_min_size", "3.0", FCVAR_CLIENTDLL );
ConVar cl_portalgun_beam_size ("cl_portalgun_beam_size", "0.04", FCVAR_CLIENTDLL );

// HACK HACK! Used to make the gun visually change when going through a cleanser!
//ConVar cl_portalgun_effects_max_size ("cl_portalgun_effects_max_size", "2.5", FCVAR_REPLICATED );


//Precahce the effects
CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectPortalgun )
CLIENTEFFECT_MATERIAL( PORTALGUN_BEAM_SPRITE )
CLIENTEFFECT_MATERIAL( PORTALGUN_BEAM_SPRITE_NOZ )
CLIENTEFFECT_MATERIAL( PORTALGUN_GLOW_SPRITE )
CLIENTEFFECT_MATERIAL( PORTALGUN_ENDCAP_SPRITE )
CLIENTEFFECT_MATERIAL( PORTALGUN_GRAV_ACTIVE_GLOW )
CLIENTEFFECT_MATERIAL( PORTALGUN_PORTAL1_FIRED_LAST_GLOW )
CLIENTEFFECT_MATERIAL( PORTALGUN_PORTAL2_FIRED_LAST_GLOW )
CLIENTEFFECT_MATERIAL( PORTALGUN_PORTAL_MUZZLE_GLOW_SPRITE )
CLIENTEFFECT_MATERIAL( PORTALGUN_PORTAL_TUBE_BEAM_SPRITE )
CLIENTEFFECT_REGISTER_END()


CPortalgunEffectBeam::CPortalgunEffectBeam( void ) 
	: m_pBeam( NULL ), 
	  m_fBrightness( 255.0f )
{}

CPortalgunEffectBeam::~CPortalgunEffectBeam( void )
{
	Release();
}

void CPortalgunEffectBeam::Release( void )
{
	if ( m_pBeam != NULL )
	{
		m_pBeam->flags = 0;
		m_pBeam->die = gpGlobals->curtime - 1;

		m_pBeam = NULL;
	}
}

void CPortalgunEffectBeam::Init( int startAttachment, int endAttachment, CBaseEntity *pEntity, bool firstPerson )
{
	if ( m_pBeam != NULL )
		return;

	BeamInfo_t beamInfo;

	beamInfo.m_pStartEnt = pEntity;
	beamInfo.m_nStartAttachment = startAttachment;
	beamInfo.m_pEndEnt = pEntity;
	beamInfo.m_nEndAttachment = endAttachment;
	beamInfo.m_nType = TE_BEAMPOINTS;
	beamInfo.m_vecStart = vec3_origin;
	beamInfo.m_vecEnd = vec3_origin;

	beamInfo.m_pszModelName = ( firstPerson ) ? PORTALGUN_BEAM_SPRITE_NOZ : PORTALGUN_BEAM_SPRITE;

	beamInfo.m_flHaloScale = 0.0f;
	beamInfo.m_flLife = 0.0f;

	if ( firstPerson )
	{
		beamInfo.m_flWidth = 0.0f;
		beamInfo.m_flEndWidth = 2.0f;
	}
	else
	{
		beamInfo.m_flWidth = 0.5f;
		beamInfo.m_flEndWidth = 2.0f;
	}

	beamInfo.m_flFadeLength = 0.0f;
	beamInfo.m_flAmplitude = 16;
	beamInfo.m_flBrightness = 128.0;
	beamInfo.m_flSpeed = 150.0f;
	beamInfo.m_nStartFrame = 0.0;
	beamInfo.m_flFrameRate = 30.0;
	beamInfo.m_flRed = 255.0;
	beamInfo.m_flGreen = 255.0;
	beamInfo.m_flBlue = 255.0;
	beamInfo.m_nSegments = 8;
	beamInfo.m_bRenderable = true;
	beamInfo.m_nFlags = FBEAM_FOREVER;

	m_pBeam = beams->CreateBeamEntPoint( beamInfo );

	if ( m_pBeam )
	{
		m_pBeam->m_bDrawInMainRender = false;
		m_pBeam->m_bDrawInPortalRender = false;
	}
}

void CPortalgunEffectBeam::SetVisibleViewModel( bool visible /*= true*/ )
{
	if ( m_pBeam == NULL )
		return;

	m_pBeam->m_bDrawInMainRender = visible;
}

int CPortalgunEffectBeam::IsVisibleViewModel( void ) const
{
	if ( m_pBeam == NULL )
		return false;

	return m_pBeam->m_bDrawInMainRender;
}

void CPortalgunEffectBeam::SetVisible3rdPerson( bool visible /*= true*/ )
{
	if ( m_pBeam == NULL )
		return;

	m_pBeam->m_bDrawInPortalRender = visible;
}

int CPortalgunEffectBeam::SetVisible3rdPerson( void ) const
{
	if ( m_pBeam == NULL )
		return false;

	return m_pBeam->m_bDrawInPortalRender;
}

void CPortalgunEffectBeam::SetBrightness( float fBrightness )
{
	m_fBrightness = clamp( fBrightness, 0.0f, 255.0f );
}

void CPortalgunEffectBeam::DrawBeam( void )
{
	if ( m_pBeam )
	m_pBeam->DrawModel( 0 );
}


IMPLEMENT_NETWORKCLASS_ALIASED( WeaponPortalgun, DT_WeaponPortalgun )

BEGIN_NETWORK_TABLE( C_WeaponPortalgun, DT_WeaponPortalgun )
	RecvPropBool( RECVINFO( m_bCanFirePortal1 ) ),
	RecvPropBool( RECVINFO( m_bCanFirePortal2 ) ),
	RecvPropInt( RECVINFO( m_iLastFiredPortal ) ),
	RecvPropBool( RECVINFO( m_bOpenProngs ) ),
	RecvPropFloat( RECVINFO( m_fCanPlacePortal1OnThisSurface ) ),
	RecvPropFloat( RECVINFO( m_fCanPlacePortal2OnThisSurface ) ),
	RecvPropFloat( RECVINFO( m_fEffectsMaxSize1 ) ), // HACK HACK! Used to make the gun visually change when going through a cleanser!
	RecvPropFloat( RECVINFO( m_fEffectsMaxSize2 ) ),
	RecvPropInt( RECVINFO( m_EffectState ) ),
END_NETWORK_TABLE()

BEGIN_PREDICTION_DATA( C_WeaponPortalgun )
	DEFINE_PRED_FIELD( m_bCanFirePortal1, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
	DEFINE_PRED_FIELD( m_bCanFirePortal2, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
	DEFINE_PRED_FIELD( m_iLastFiredPortal, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
	DEFINE_PRED_FIELD( m_bOpenProngs, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
	DEFINE_PRED_FIELD( m_fCanPlacePortal1OnThisSurface, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
	DEFINE_PRED_FIELD( m_fCanPlacePortal2OnThisSurface, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
	DEFINE_PRED_FIELD( m_EffectState,	FIELD_INTEGER,	FTYPEDESC_INSENDTABLE ),
END_PREDICTION_DATA()

LINK_ENTITY_TO_CLASS( weapon_portalgun, C_WeaponPortalgun );
PRECACHE_WEAPON_REGISTER(weapon_portalgun);


void C_WeaponPortalgun::Spawn( void )
{
	Precache();

	BaseClass::Spawn();

	//SetThink( &C_BaseEntity::Think );
	SetNextThink( gpGlobals->curtime + 0.1 );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void C_WeaponPortalgun::StartEffects( void )
{
	int i;

	CBaseEntity *pModelView = ( ( GetOwner() ) ? ( ToBasePlayer( GetOwner() )->GetViewModel() ) : ( 0 ) );
	CBaseEntity *pModelWorld = this;
	
	if ( !pModelView )
	{
		pModelView = pModelWorld;
	}

	// ------------------------------------------
	// Lights
	// ------------------------------------------

	if ( m_Parameters[PORTALGUN_GRAVLIGHT].GetMaterial() == NULL )
	{
		m_Parameters[PORTALGUN_GRAVLIGHT].GetScale().SetAbsolute( 0.018f * SPRITE_SCALE );
		m_Parameters[PORTALGUN_GRAVLIGHT].GetAlpha().SetAbsolute( 128.0f );
		m_Parameters[PORTALGUN_GRAVLIGHT].SetAttachment( pModelView->LookupAttachment( "Body_light" ) );
		m_Parameters[PORTALGUN_GRAVLIGHT].SetVisible( false );

		if ( m_Parameters[PORTALGUN_GRAVLIGHT].SetMaterial( PORTALGUN_GRAV_ACTIVE_GLOW ) == false )
		{
			// This means the texture was not found
			Assert( 0 );
		}
	}

	if ( m_Parameters[PORTALGUN_GRAVLIGHT_WORLD].GetMaterial() == NULL )
	{
		m_Parameters[PORTALGUN_GRAVLIGHT_WORLD].GetScale().SetAbsolute( 0.03f * SPRITE_SCALE );
		m_Parameters[PORTALGUN_GRAVLIGHT_WORLD].GetAlpha().SetAbsolute( 128.0f );
		m_Parameters[PORTALGUN_GRAVLIGHT_WORLD].SetAttachment( pModelWorld->LookupAttachment( "Body_light" ) );
		m_Parameters[PORTALGUN_GRAVLIGHT_WORLD].SetVisible( false );

		if ( m_Parameters[PORTALGUN_GRAVLIGHT_WORLD].SetMaterial( PORTALGUN_GRAV_ACTIVE_GLOW ) == false )
		{
			// This means the texture was not found
			Assert( 0 );
		}
	}

	if ( m_Parameters[PORTALGUN_PORTAL1LIGHT].GetMaterial() == NULL )
	{
		m_Parameters[PORTALGUN_PORTAL1LIGHT].GetScale().SetAbsolute( 0.018f * SPRITE_SCALE );
		m_Parameters[PORTALGUN_PORTAL1LIGHT].GetAlpha().SetAbsolute( 128.0f );
		m_Parameters[PORTALGUN_PORTAL1LIGHT].SetAttachment( pModelView->LookupAttachment( "Body_light" ) );
		m_Parameters[PORTALGUN_PORTAL1LIGHT].SetVisible( false );

		if ( m_Parameters[PORTALGUN_PORTAL1LIGHT].SetMaterial( PORTALGUN_PORTAL1_FIRED_LAST_GLOW ) == false )
		{
			// This means the texture was not found
			Assert( 0 );
		}
	}

	if ( m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].GetMaterial() == NULL )
	{
		m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].GetScale().SetAbsolute( 0.03f * SPRITE_SCALE );
		m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].GetAlpha().SetAbsolute( 128.0f );
		m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].SetAttachment( pModelWorld->LookupAttachment( "Body_light" ) );
		m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].SetVisible( false );

		if ( m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].SetMaterial( PORTALGUN_PORTAL1_FIRED_LAST_GLOW ) == false )
		{
			// This means the texture was not found
			Assert( 0 );
		}
	}

	if ( m_Parameters[PORTALGUN_PORTAL2LIGHT].GetMaterial() == NULL )
	{
		m_Parameters[PORTALGUN_PORTAL2LIGHT].GetScale().SetAbsolute( 0.018f * SPRITE_SCALE );
		m_Parameters[PORTALGUN_PORTAL2LIGHT].GetAlpha().SetAbsolute( 128.0f );
		m_Parameters[PORTALGUN_PORTAL2LIGHT].SetAttachment( pModelView->LookupAttachment( "Body_light" ) );
		m_Parameters[PORTALGUN_PORTAL2LIGHT].SetVisible( false );

		if ( m_Parameters[PORTALGUN_PORTAL2LIGHT].SetMaterial( PORTALGUN_PORTAL2_FIRED_LAST_GLOW ) == false )
		{
			// This means the texture was not found
			Assert( 0 );
		}
	}

	if ( m_Parameters[PORTALGUN_PORTAL2LIGHT_WORLD].GetMaterial() == NULL )
	{
		m_Parameters[PORTALGUN_PORTAL2LIGHT_WORLD].GetScale().SetAbsolute( 0.03f * SPRITE_SCALE );
		m_Parameters[PORTALGUN_PORTAL2LIGHT_WORLD].GetAlpha().SetAbsolute( 128.0f );
		m_Parameters[PORTALGUN_PORTAL2LIGHT_WORLD].SetAttachment( pModelWorld->LookupAttachment( "Body_light" ) );
		m_Parameters[PORTALGUN_PORTAL2LIGHT_WORLD].SetVisible( false );

		if ( m_Parameters[PORTALGUN_PORTAL2LIGHT_WORLD].SetMaterial( PORTALGUN_PORTAL2_FIRED_LAST_GLOW ) == false )
		{
			// This means the texture was not found
			Assert( 0 );
		}
	}

	// ------------------------------------------
	// Glows
	// ------------------------------------------

	const char *attachNamesGlow[NUM_GLOW_SPRITES] = 
	{
		"Arm1_attach1",
		"Arm1_attach2",
		"Arm2_attach1",
		"Arm2_attach2",
		"Arm3_attach1",
		"Arm3_attach2"
	};

	//Create the view glow sprites
	for ( i = PORTALGUN_GLOW1; i < (PORTALGUN_GLOW1+NUM_GLOW_SPRITES); i++ )
	{
		if ( m_Parameters[i].GetMaterial() != NULL )
			continue;

		m_Parameters[i].GetScale().SetAbsolute( 0.05f * SPRITE_SCALE );
		m_Parameters[i].GetAlpha().SetAbsolute( 24.0f );

		// Different for different views
		m_Parameters[i].SetAttachment( pModelView->LookupAttachment( attachNamesGlow[i-PORTALGUN_GLOW1] ) );
		m_Parameters[i].SetColor( Vector( 255, 128, 0 ) );
		m_Parameters[i].SetVisible( false );

		if ( m_Parameters[i].SetMaterial( PORTALGUN_GLOW_SPRITE ) == false )
		{
			// This means the texture was not found
			Assert( 0 );
		}
	}

	//Create the world glow sprites
	for ( i = PORTALGUN_GLOW1_WORLD; i < (PORTALGUN_GLOW1_WORLD+NUM_GLOW_SPRITES_WORLD); i++ )
	{
		if ( m_Parameters[i].GetMaterial() != NULL )
			continue;

		m_Parameters[i].GetScale().SetAbsolute( 0.1f * SPRITE_SCALE );
		m_Parameters[i].GetAlpha().SetAbsolute( 24.0f );

		// Different for different views
		m_Parameters[i].SetAttachment( pModelWorld->LookupAttachment( attachNamesGlow[i-PORTALGUN_GLOW1_WORLD] ) );
		m_Parameters[i].SetColor( Vector( 255, 128, 0 ) );
		m_Parameters[i].SetVisible( false );

		if ( m_Parameters[i].SetMaterial( PORTALGUN_GLOW_SPRITE ) == false )
		{
			// This means the texture was not found
			Assert( 0 );
		}
	}

	// ------------------------------------------
	// End caps
	// ------------------------------------------

	const char *attachNamesEndCap[NUM_ENDCAP_SPRITES] = 
	{
		"Arm1_attach3",
		"Arm2_attach3",
		"Arm3_attach3",
	};

	//Create the endcap sprites
	for ( i = PORTALGUN_ENDCAP1; i < (PORTALGUN_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
	{
		if ( m_Parameters[i].GetMaterial() != NULL )
			continue;

		m_Parameters[i].GetScale().SetAbsolute( 0.02f * SPRITE_SCALE );
		m_Parameters[i].GetAlpha().SetAbsolute( 128.0f );
		m_Parameters[i].SetAttachment( pModelView->LookupAttachment( attachNamesEndCap[i-PORTALGUN_ENDCAP1] ) );
		m_Parameters[i].SetVisible( false );

		if ( m_Parameters[i].SetMaterial( PORTALGUN_ENDCAP_SPRITE ) == false )
		{
			// This means the texture was not found
			Assert( 0 );
		}
	}

	//Create the world endcap sprites
	for ( i = PORTALGUN_ENDCAP1_WORLD; i < (PORTALGUN_ENDCAP1_WORLD+NUM_ENDCAP_SPRITES_WORLD); i++ )
	{
		if ( m_Parameters[i].GetMaterial() != NULL )
			continue;

		m_Parameters[i].GetScale().SetAbsolute( 0.04f * SPRITE_SCALE );
		m_Parameters[i].GetAlpha().SetAbsolute( 128.0f );
		m_Parameters[i].SetAttachment( pModelWorld->LookupAttachment( attachNamesEndCap[i-PORTALGUN_ENDCAP1_WORLD] ) );
		m_Parameters[i].SetVisible( false );

		if ( m_Parameters[i].SetMaterial( PORTALGUN_ENDCAP_SPRITE ) == false )
		{
			// This means the texture was not found
			Assert( 0 );
		}
	}

	// ------------------------------------------
	// Internals
	// ------------------------------------------

	//Create the muzzle glow sprites
	i = PORTALGUN_MUZZLE_GLOW;

	if ( m_Parameters[i].GetMaterial() == NULL )
	{
		m_Parameters[i].GetScale().SetAbsolute( 0.025f * SPRITE_SCALE );
		m_Parameters[i].GetAlpha().SetAbsolute( 64.0f );
		m_Parameters[i].SetAttachment( pModelView->LookupAttachment( "Inside_effects" ) );
		m_Parameters[i].SetVisible( false );

		if ( m_Parameters[i].SetMaterial( PORTALGUN_PORTAL_MUZZLE_GLOW_SPRITE ) == false )
		{
			// This means the texture was not found
			Assert( 0 );
		}
	}

	//Create the world muzzle glow sprites
	i = PORTALGUN_MUZZLE_GLOW_WORLD;
	
	if ( m_Parameters[i].GetMaterial() == NULL )
	{
		m_Parameters[i].GetScale().SetAbsolute( 0.025f * SPRITE_SCALE );
		m_Parameters[i].GetAlpha().SetAbsolute( 64.0f );
		m_Parameters[i].SetAttachment( pModelWorld->LookupAttachment( "Inside_effects" ) );
		m_Parameters[i].SetVisible( false );

		if ( m_Parameters[i].SetMaterial( PORTALGUN_PORTAL_MUZZLE_GLOW_SPRITE ) == false )
		{
			// This means the texture was not found
			Assert( 0 );
		}
	}

	// ------------------------------------------
	// Tube sprites
	// ------------------------------------------

	const char *attachNamesTubeBeam[NUM_TUBE_BEAM_SPRITES] = 
	{
		"Beam_point1",
		"Beam_point2",
		"Beam_point3",
		"Beam_point4",
		"Beam_point5",
	};

	//Create the tube beam sprites
	for ( i = PORTALGUN_TUBE_BEAM1; i < (PORTALGUN_TUBE_BEAM1+NUM_TUBE_BEAM_SPRITES); i++ )
	{
		if ( m_Parameters[i].GetMaterial() == NULL )
		{
			m_Parameters[i].GetScale().SetAbsolute( cl_portalgun_beam_size.GetFloat() * SPRITE_SCALE );
			m_Parameters[i].GetAlpha().SetAbsolute( 255.0f );
			m_Parameters[i].SetAttachment( pModelView->LookupAttachment( attachNamesTubeBeam[i-PORTALGUN_TUBE_BEAM1] ) );
			m_Parameters[i].SetVisible( false );

			if ( m_Parameters[i].SetMaterial( PORTALGUN_PORTAL_TUBE_BEAM_SPRITE ) == false )
			{
				// This means the texture was not found
				Assert( 0 );
			}
		}
	}

	//Create the world tube beam sprites
	for ( i = PORTALGUN_TUBE_BEAM1_WORLD; i < (PORTALGUN_TUBE_BEAM1_WORLD+NUM_TUBE_BEAM_SPRITES_WORLD); i++ )
	{
		if ( m_Parameters[i].GetMaterial() == NULL )
		{
			m_Parameters[i].GetScale().SetAbsolute( cl_portalgun_beam_size.GetFloat() * SPRITE_SCALE );
			m_Parameters[i].GetAlpha().SetAbsolute( 255.0f );
			m_Parameters[i].SetAttachment( pModelView->LookupAttachment( attachNamesTubeBeam[i-PORTALGUN_TUBE_BEAM1_WORLD] ) );
			m_Parameters[i].SetVisible( false );

			if ( m_Parameters[i].SetMaterial( PORTALGUN_PORTAL_TUBE_BEAM_SPRITE ) == false )
			{
				// This means the texture was not found
				Assert( 0 );
			}
		}
	}

	// ------------------------------------------
	// Beams
	// ------------------------------------------

	// Setup the beams
	int iBeam = 0;

	if ( pModelView != pModelWorld )
	{
		m_Beams[iBeam++].Init( pModelView->LookupAttachment( "Arm1_attach3" ), pModelView->LookupAttachment( "muzzle" ), pModelView, true );
		m_Beams[iBeam++].Init( pModelView->LookupAttachment( "Arm2_attach3" ), pModelView->LookupAttachment( "muzzle" ), pModelView, true );
		m_Beams[iBeam++].Init( pModelView->LookupAttachment( "Arm3_attach3" ), pModelView->LookupAttachment( "muzzle" ), pModelView, true );
	}
	else
	{
		iBeam += 3;
	}

	m_Beams[iBeam++].Init( pModelWorld->LookupAttachment( "Arm1_attach3" ), pModelWorld->LookupAttachment( "muzzle" ), pModelWorld, false );
	m_Beams[iBeam++].Init( pModelWorld->LookupAttachment( "Arm2_attach3" ), pModelWorld->LookupAttachment( "muzzle" ), pModelWorld, false );
	m_Beams[iBeam++].Init( pModelWorld->LookupAttachment( "Arm3_attach3" ), pModelWorld->LookupAttachment( "muzzle" ), pModelWorld, false );
}

void C_WeaponPortalgun::DestroyEffects( void )
{
	// Free our beams
	for ( int i = 0; i < NUM_PORTALGUN_BEAMS; ++i )
	{
		m_Beams[i].Release();
	}

	// Stop everything
	StopEffects();
}

//-----------------------------------------------------------------------------
// Purpose: Ready effects
//-----------------------------------------------------------------------------
void C_WeaponPortalgun::DoEffectReady( void )
{
	int i;

	// Turn on the glow sprites
	for ( i = PORTALGUN_GLOW1; i < (PORTALGUN_GLOW1+NUM_GLOW_SPRITES); i++ )
	{
		m_Parameters[i].GetScale().InitFromCurrent( 0.4f * SPRITE_SCALE, 0.2f );
		m_Parameters[i].GetAlpha().InitFromCurrent( 32.0f, 0.2f );
		m_Parameters[i].SetVisibleViewModel();
	}

	for ( i = PORTALGUN_GLOW1_WORLD; i < (PORTALGUN_GLOW1_WORLD+NUM_GLOW_SPRITES_WORLD); i++ )
	{
		m_Parameters[i].GetScale().InitFromCurrent( 0.8f * SPRITE_SCALE, 0.4f );
		m_Parameters[i].GetAlpha().InitFromCurrent( 32.0f, 0.2f );
		m_Parameters[i].SetVisible3rdPerson();
	}

	// Turn on the endcap sprites
	for ( i = PORTALGUN_ENDCAP1; i < (PORTALGUN_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
	{
		m_Parameters[i].SetVisible( false );
	}

	// Turn on the world endcap sprites
	for ( i = PORTALGUN_ENDCAP1_WORLD; i < (PORTALGUN_ENDCAP1_WORLD+NUM_ENDCAP_SPRITES_WORLD); i++ )
	{
		m_Parameters[i].SetVisible( false );
	}

	// Turn on the internal sprites
	i = PORTALGUN_MUZZLE_GLOW;
	
	Vector colorMagSprites = GetEffectColor( i );
	m_Parameters[i].SetColor( colorMagSprites );
	m_Parameters[i].SetVisibleViewModel();
	
	// Turn on the world internal sprites
	i = PORTALGUN_MUZZLE_GLOW_WORLD;

	m_Parameters[i].SetColor( colorMagSprites );
	m_Parameters[i].SetVisible3rdPerson();

	// Turn on the tube beam sprites
	for ( i = PORTALGUN_TUBE_BEAM1; i < (PORTALGUN_TUBE_BEAM1+NUM_TUBE_BEAM_SPRITES); i++ )
	{
		m_Parameters[i].SetColor( colorMagSprites );
		m_Parameters[i].SetVisibleViewModel();
	}

	// Turn on the world tube beam sprites
	for ( i = PORTALGUN_TUBE_BEAM1_WORLD; i < (PORTALGUN_TUBE_BEAM1_WORLD+NUM_TUBE_BEAM_SPRITES_WORLD); i++ )
	{
		m_Parameters[i].SetColor( colorMagSprites );
		m_Parameters[i].SetVisible3rdPerson();
	}

    // Turn off beams off
	for ( i = 0; i < NUM_PORTALGUN_BEAMS; ++i )
	{
		m_Beams[i].SetVisibleViewModel( false );
		m_Beams[i].SetVisible3rdPerson( false );
	}

	CPortal_Player* pPlayer = (CPortal_Player*)GetOwner();
	if ( pPlayer )
	{
		RumbleEffect( RUMBLE_PHYSCANNON_OPEN, 0, RUMBLE_FLAG_STOP );
	}
}


//-----------------------------------------------------------------------------
// Holding effects
//-----------------------------------------------------------------------------
void C_WeaponPortalgun::DoEffectHolding( void )
{
	int i;

	// Turn on the glow sprites
	for ( i = PORTALGUN_GLOW1; i < (PORTALGUN_GLOW1+NUM_GLOW_SPRITES); i++ )
	{
		m_Parameters[i].GetScale().InitFromCurrent( 0.5f * SPRITE_SCALE, 0.2f );
		m_Parameters[i].GetAlpha().InitFromCurrent( 64.0f, 0.2f );
		m_Parameters[i].SetVisibleViewModel();
	}

	for ( i = PORTALGUN_GLOW1_WORLD; i < (PORTALGUN_GLOW1_WORLD+NUM_GLOW_SPRITES_WORLD); i++ )
	{
		m_Parameters[i].GetScale().InitFromCurrent( 1.0f * SPRITE_SCALE, 0.4f );
		m_Parameters[i].GetAlpha().InitFromCurrent( 64.0f, 0.2f );
		m_Parameters[i].SetVisible3rdPerson();
	}

	// Turn on the endcap sprites
	for ( i = PORTALGUN_ENDCAP1; i < (PORTALGUN_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
	{
		m_Parameters[i].SetVisibleViewModel();
	}

	// Turn on the world endcap sprites
	for ( i = PORTALGUN_ENDCAP1_WORLD; i < (PORTALGUN_ENDCAP1_WORLD+NUM_ENDCAP_SPRITES_WORLD); i++ )
	{
		m_Parameters[i].SetVisible3rdPerson();
	}

	// Turn on the internal sprites
	i = PORTALGUN_MUZZLE_GLOW;

	Vector colorMagSprites = GetEffectColor( i );
	m_Parameters[i].SetColor( colorMagSprites );
	m_Parameters[i].SetVisibleViewModel();

	// Turn on the world internal sprites
	i = PORTALGUN_MUZZLE_GLOW_WORLD;
	
	m_Parameters[i].SetColor( colorMagSprites );
	m_Parameters[i].SetVisible3rdPerson();

	// Turn on the tube beam sprites
	for ( i = PORTALGUN_TUBE_BEAM1; i < (PORTALGUN_TUBE_BEAM1+NUM_TUBE_BEAM_SPRITES); i++ )
	{
		m_Parameters[i].SetColor( colorMagSprites );
		m_Parameters[i].SetVisibleViewModel();
	}

	// Turn on the world tube beam sprites
	for ( i = PORTALGUN_TUBE_BEAM1; i < (PORTALGUN_TUBE_BEAM1+NUM_TUBE_BEAM_SPRITES); i++ )
	{
		m_Parameters[i].SetColor( colorMagSprites );
		m_Parameters[i].SetVisible3rdPerson();
	}

	// Set beams them visible
	for ( i = 0; i < NUM_PORTALGUN_BEAMS / 2; ++i )
	{
		m_Beams[i].SetVisible3rdPerson( false );
		m_Beams[i].SetVisibleViewModel();
		m_Beams[i].SetBrightness( 128.0f );
	}

	for ( i; i < NUM_PORTALGUN_BEAMS; ++i )
	{
		m_Beams[i].SetVisibleViewModel( false );
		m_Beams[i].SetVisible3rdPerson();
		m_Beams[i].SetBrightness( 128.0f );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Shutdown for the weapon when it's holstered
//-----------------------------------------------------------------------------
void C_WeaponPortalgun::DoEffectNone( void )
{
	int i;

	//Turn off main glows
	m_Parameters[PORTALGUN_GRAVLIGHT].SetVisible( false );
	m_Parameters[PORTALGUN_GRAVLIGHT_WORLD].SetVisible( false );
	m_Parameters[PORTALGUN_PORTAL1LIGHT].SetVisible( false );
	m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].SetVisible( false );
	m_Parameters[PORTALGUN_PORTAL2LIGHT].SetVisible( false );
	m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].SetVisible( false );

	for ( i = PORTALGUN_GLOW1; i < (PORTALGUN_GLOW1+NUM_GLOW_SPRITES); i++ )
	{
		m_Parameters[i].SetVisible( false );
	}

	for ( i = PORTALGUN_GLOW1_WORLD; i < (PORTALGUN_GLOW1_WORLD+NUM_GLOW_SPRITES_WORLD); i++ )
	{
		m_Parameters[i].SetVisible( false );
	}

	for ( i = PORTALGUN_ENDCAP1; i < (PORTALGUN_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
	{
		m_Parameters[i].SetVisible( false );
	}

	for ( i = PORTALGUN_ENDCAP1_WORLD; i < (PORTALGUN_ENDCAP1_WORLD+NUM_ENDCAP_SPRITES_WORLD); i++ )
	{
		m_Parameters[i].SetVisible( false );
	}

	m_Parameters[PORTALGUN_MUZZLE_GLOW].SetVisible( false );
	m_Parameters[PORTALGUN_MUZZLE_GLOW_WORLD].SetVisible( false );

	for ( i = PORTALGUN_TUBE_BEAM1; i < (PORTALGUN_TUBE_BEAM1+NUM_TUBE_BEAM_SPRITES); i++ )
	{
		m_Parameters[i].SetVisible( false );
	}

	for ( i = PORTALGUN_TUBE_BEAM1_WORLD; i < (PORTALGUN_TUBE_BEAM1_WORLD+NUM_TUBE_BEAM_SPRITES_WORLD); i++ )
	{
		m_Parameters[i].SetVisible( false );
	}

	for ( i = 0; i < NUM_PORTALGUN_BEAMS; ++i )
	{
		m_Beams[i].SetVisibleViewModel( false );
		m_Beams[i].SetVisible3rdPerson( false );
	}
}

void C_WeaponPortalgun::OnPreDataChanged( DataUpdateType_t updateType )
{
	//PreDataChanged.m_matrixThisToLinked = m_matrixThisToLinked;
	m_bOldCanFirePortal1 = m_bCanFirePortal1;
	m_bOldCanFirePortal1 = m_bCanFirePortal2;

	BaseClass::OnPreDataChanged( updateType );
}

void C_WeaponPortalgun::OnDataChanged( DataUpdateType_t updateType )
{
	BaseClass::OnDataChanged( updateType );

	if ( updateType == DATA_UPDATE_CREATED )
	{
		// Start thinking (Baseclass stops it)
		SetNextClientThink( CLIENT_THINK_ALWAYS );

		{
			C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, true );
			StartEffects();
		}

		DoEffect( m_EffectState );
	}

	// Update effect state when out of parity with the server
	else if ( m_nOldEffectState != m_EffectState || m_bOldCanFirePortal1 != m_bCanFirePortal1 || m_bOldCanFirePortal2 != m_bCanFirePortal2 )
	{
		DoEffect( m_EffectState );
		m_nOldEffectState = m_EffectState;

		m_bOldCanFirePortal1 = m_bCanFirePortal1;
		m_bOldCanFirePortal2 = m_bCanFirePortal2;
	}
}

void C_WeaponPortalgun::ClientThink( void )
{
	CPortal_Player *pPlayer = ToPortalPlayer( GetOwner() );

	if ( pPlayer && dynamic_cast<C_WeaponPortalgun*>( pPlayer->GetActiveWeapon() ) )
	{
		if ( m_EffectState == EFFECT_NONE )
		{
			//DoEffect( ( GetPlayerHeldEntity( pPlayer ) ) ? ( EFFECT_HOLDING ) : ( EFFECT_READY ) );
		}

		if ( m_EffectState != EFFECT_NONE )
		{
			// Showing a special color for holding is confusing... just use the last fired color -Jeep

			//if ( m_bOpenProngs )
			//{
			//	//Turn on the grav light
			//	m_Parameters[PORTALGUN_GRAVLIGHT].SetVisibleViewModel();
			//	m_Parameters[PORTALGUN_GRAVLIGHT_WORLD].SetVisible3rdPerson();

			//	m_Parameters[PORTALGUN_PORTAL1LIGHT].SetVisibleViewModel( false );
			//	m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].SetVisible3rdPerson( false );
			//	m_Parameters[PORTALGUN_PORTAL2LIGHT].SetVisibleViewModel( false );
			//	m_Parameters[PORTALGUN_PORTAL2LIGHT_WORLD].SetVisible3rdPerson( false );
			//}
			//else
			{
				m_Parameters[PORTALGUN_GRAVLIGHT].SetVisibleViewModel( false );
				m_Parameters[PORTALGUN_GRAVLIGHT_WORLD].SetVisible3rdPerson( false );

				//Turn on and off the correct fired last lights
				m_Parameters[PORTALGUN_PORTAL1LIGHT].SetVisibleViewModel( m_iLastFiredPortal == 1 );
				m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].SetVisible3rdPerson( m_iLastFiredPortal == 1 );
				m_Parameters[PORTALGUN_PORTAL2LIGHT].SetVisibleViewModel( m_iLastFiredPortal == 2 );
				m_Parameters[PORTALGUN_PORTAL2LIGHT_WORLD].SetVisible3rdPerson( m_iLastFiredPortal == 2 );
			}
		}
	}

	// Update our effects
	DoEffectIdle();

	NetworkStateChanged();
}

Vector C_WeaponPortalgun::GetEffectColor( int iPalletIndex )
{
	Color color;

	// Showing a special color for holding is confusing... just use the last fired color -Jeep
	/*if ( m_bOpenProngs )
	{
		color = UTIL_Portal_Color( 0 );
	}
	else */if ( m_iLastFiredPortal == 1 )
	{
		color = UTIL_Portal_Color( 1 );
	}
	else if ( m_iLastFiredPortal == 2 )
	{
		color = UTIL_Portal_Color( 2 );
	}
	else
	{
		color = Color( 128, 128, 128, 255 );
	}

	Vector vColor;
	vColor.x = color.r();
	vColor.y = color.g();
	vColor.z = color.b();

	return vColor;
}

extern void FormatViewModelAttachment( Vector &vOrigin, bool bInverse );

//-----------------------------------------------------------------------------
// Purpose: Gets the complete list of values needed to render an effect from an
//			effect parameter
//-----------------------------------------------------------------------------
void C_WeaponPortalgun::GetEffectParameters( EffectType_t effectID, color32 &color, float &scale, IMaterial **pMaterial, Vector &vecAttachment, bool b3rdPerson )
{
	const float dt = gpGlobals->curtime;

	// Get alpha
	float alpha = m_Parameters[effectID].GetAlpha().Interp( dt );

	// Get scale
	scale = m_Parameters[effectID].GetScale().Interp( dt );

	// Get material
	*pMaterial = (IMaterial *) m_Parameters[effectID].GetMaterial();

	// Setup the color
	color.r = (int) m_Parameters[effectID].GetColor().x;
	color.g = (int) m_Parameters[effectID].GetColor().y;
	color.b = (int) m_Parameters[effectID].GetColor().z;
	color.a = (int) alpha;

	// Setup the attachment
	int		attachment = m_Parameters[effectID].GetAttachment();
	QAngle	angles;

	// Format for first-person
	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );

	if ( pOwner != NULL )
	{
		C_BaseAnimating *pModel;
		int originalModelIndex = 0;

		if ( b3rdPerson )
		{
			pModel = this;
			originalModelIndex = GetModelIndex();
			SetModelIndex( GetWorldModelIndex() );
		}
		else
		{
			pModel = pOwner->GetViewModel();
		}

		pModel->GetAttachment( attachment, vecAttachment, angles );

		if ( !b3rdPerson )
		{
			::FormatViewModelAttachment( vecAttachment, true );
		}
		else
		{
			SetModelIndex( originalModelIndex );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Whether or not an effect is set to display
//-----------------------------------------------------------------------------
bool C_WeaponPortalgun::IsEffectVisible( EffectType_t effectID, bool b3rdPerson )
{
	if ( b3rdPerson )
	{
		return m_Parameters[effectID].IsVisible3rdPerson();
	}
	else
	{
		return m_Parameters[effectID].IsVisibleViewModel();
	}
}

//-----------------------------------------------------------------------------
// Purpose: Draws the effect sprite, given an effect parameter ID
//-----------------------------------------------------------------------------
void C_WeaponPortalgun::DrawEffectSprite( EffectType_t effectID, bool b3rdPerson )
{
	color32 color;
	float scale;
	IMaterial *pMaterial;
	Vector	vecAttachment;

	// Don't draw invisible effects
	if ( !IsEffectVisible( effectID, b3rdPerson ) )
		return;

	// Get all of our parameters
	GetEffectParameters( effectID, color, scale, &pMaterial, vecAttachment, b3rdPerson );

	// Don't render fully translucent objects
	if ( color.a <= 0.0f )
		return;

	// Draw the sprite
	CMatRenderContextPtr pRenderContext( materials );
	pRenderContext->Bind( pMaterial, this );
	DrawSprite( vecAttachment, scale, scale, color );
}

//-----------------------------------------------------------------------------
// Purpose: Render our third-person effects
//-----------------------------------------------------------------------------
void C_WeaponPortalgun::DrawEffects( bool b3rdPerson )
{
	for ( int i = 0; i < NUM_PORTALGUN_PARAMETERS; i++ )
	{
		DrawEffectSprite( (EffectType_t) i, b3rdPerson );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Third-person function call to render world model
//-----------------------------------------------------------------------------
int C_WeaponPortalgun::DrawModel( int flags )
{
	// Only render these on the transparent pass
	/*if ( flags & STUDIO_TRANSPARENCY )
	{
		DrawEffects( true );
		return 1;
	}*/

	int iRetValue = BaseClass::DrawModel( flags );

	if ( iRetValue )
	{
		DrawEffects( true );
	}

	return iRetValue;
}

//-----------------------------------------------------------------------------
// Purpose: First-person function call after viewmodel has been drawn
//-----------------------------------------------------------------------------
void C_WeaponPortalgun::ViewModelDrawn( C_BaseViewModel *pBaseViewModel )
{
	// Render our effects
	DrawEffects( false );

	// Pass this back up
	BaseClass::ViewModelDrawn( pBaseViewModel );
}

void UpdatePoseParameter( C_BaseAnimating *pBaseAnimating, int iPose, float fValue )
{
	pBaseAnimating->SetPoseParameter( iPose, fValue );
}

void InterpToward( float *pfCurrent, float fGoal, float fRate )
{
	if ( *pfCurrent < fGoal )
	{
		*pfCurrent += fRate;

		if ( *pfCurrent > fGoal )
		{
			*pfCurrent = fGoal;
		}
	}
	else if ( *pfCurrent > fGoal )
	{
		*pfCurrent -= fRate;

		if ( *pfCurrent < fGoal )
		{
			*pfCurrent = fGoal;
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Idle effect (pulsing)
//-----------------------------------------------------------------------------
void C_WeaponPortalgun::DoEffectIdle( void )
{
	StartEffects();

	int i;

	// Turn on the glow sprites
	for ( i = PORTALGUN_GLOW1; i < (PORTALGUN_GLOW1+NUM_GLOW_SPRITES); i++ )
	{
		m_Parameters[i].GetScale().InitFromCurrent( RandomFloat( 0.0075f, 0.05f ) * SPRITE_SCALE, 0.1f );
		m_Parameters[i].GetAlpha().SetAbsolute( RandomInt( 10, 24 ) );
	}

	// Turn on the world glow sprites
	for ( i = PORTALGUN_GLOW1_WORLD; i < (PORTALGUN_GLOW1_WORLD+NUM_GLOW_SPRITES_WORLD); i++ )
	{
		m_Parameters[i].GetScale().InitFromCurrent( RandomFloat( 0.015f, 0.1f ) * SPRITE_SCALE, 0.1f );
		m_Parameters[i].GetAlpha().SetAbsolute( RandomInt( 10, 24 ) );
	}

	// Turn on the endcap sprites
	for ( i = PORTALGUN_ENDCAP1; i < (PORTALGUN_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
	{
		m_Parameters[i].GetScale().SetAbsolute( RandomFloat( 1.0f, 3.0f ) );
		m_Parameters[i].GetAlpha().SetAbsolute( RandomInt( 96, 128 ) );
	}

	// Turn on the world endcap sprites
	for ( i = PORTALGUN_ENDCAP1_WORLD; i < (PORTALGUN_ENDCAP1_WORLD+NUM_ENDCAP_SPRITES_WORLD); i++ )
	{
		m_Parameters[i].GetScale().SetAbsolute( RandomFloat( 2.0f, 6.0f ) );
		m_Parameters[i].GetAlpha().SetAbsolute( RandomInt( 96, 128 ) );
	}

	// Turn on the internal sprites
	i = PORTALGUN_MUZZLE_GLOW;

	if ( m_bPulseUp )
	{
		m_fPulse += gpGlobals->frametime;
		if ( m_fPulse > 1.0f )
		{
			m_fPulse = 1.0f;
			m_bPulseUp = !m_bPulseUp;
		}
	}
	else
	{
		m_fPulse -= gpGlobals->frametime;
		if ( m_fPulse < 0.0f )
		{
			m_fPulse = 0.0f;
			m_bPulseUp = !m_bPulseUp;
		}
	}

	m_Parameters[i].GetScale().SetAbsolute( cl_portalgun_effects_min_size.GetFloat() + ( m_fEffectsMaxSize1 - cl_portalgun_effects_min_size.GetFloat() ) * m_fPulse );
	m_Parameters[i].GetAlpha().SetAbsolute( cl_portalgun_effects_min_alpha.GetInt() + ( cl_portalgun_effects_max_alpha.GetInt() - cl_portalgun_effects_min_alpha.GetInt() ) * m_fPulse );
	Vector colorMagSprites = GetEffectColor( i );
	m_Parameters[i].SetColor( colorMagSprites );

	// Turn on the world internal sprites
	i = PORTALGUN_MUZZLE_GLOW_WORLD;

	m_Parameters[i].GetScale().SetAbsolute( cl_portalgun_effects_min_size.GetFloat() + ( m_fEffectsMaxSize1 - cl_portalgun_effects_min_size.GetFloat() ) * m_fPulse );
	m_Parameters[i].GetAlpha().SetAbsolute( cl_portalgun_effects_min_alpha.GetInt() + ( cl_portalgun_effects_max_alpha.GetInt() - cl_portalgun_effects_min_alpha.GetInt() ) * m_fPulse );
	m_Parameters[i].SetColor( colorMagSprites );

	// Turn on the tube beam sprites
	for ( i = PORTALGUN_TUBE_BEAM1; i < (PORTALGUN_TUBE_BEAM1+NUM_TUBE_BEAM_SPRITES); i++ )
	{
		m_Parameters[i].GetAlpha().SetAbsolute( cl_portalgun_effects_min_alpha.GetInt() + ( cl_portalgun_effects_max_alpha.GetInt() - cl_portalgun_effects_min_alpha.GetInt() ) * m_fPulse );
		m_Parameters[i].SetColor( colorMagSprites );
	}

	// Turn on the world tube beam sprites
	for ( i = PORTALGUN_TUBE_BEAM1_WORLD; i < (PORTALGUN_TUBE_BEAM1_WORLD+NUM_TUBE_BEAM_SPRITES_WORLD); i++ )
	{
		m_Parameters[i].GetAlpha().SetAbsolute( cl_portalgun_effects_min_alpha.GetInt() + ( cl_portalgun_effects_max_alpha.GetInt() - cl_portalgun_effects_min_alpha.GetInt() ) * m_fPulse );
		m_Parameters[i].SetColor( colorMagSprites );
	}
}