aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/client/episodic
diff options
context:
space:
mode:
authorJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
committerJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
commit39ed87570bdb2f86969d4be821c94b722dc71179 (patch)
treeabc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/client/episodic
downloadsource-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz
source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/client/episodic')
-rw-r--r--mp/src/game/client/episodic/c_npc_advisor.cpp251
-rw-r--r--mp/src/game/client/episodic/c_npc_puppet.cpp167
-rw-r--r--mp/src/game/client/episodic/c_prop_coreball.cpp142
-rw-r--r--mp/src/game/client/episodic/c_prop_scalable.cpp196
-rw-r--r--mp/src/game/client/episodic/c_vehicle_jeep_episodic.cpp133
-rw-r--r--mp/src/game/client/episodic/c_vort_charge_token.cpp600
-rw-r--r--mp/src/game/client/episodic/c_weapon_hopwire.cpp421
-rw-r--r--mp/src/game/client/episodic/episodic_screenspaceeffects.cpp464
-rw-r--r--mp/src/game/client/episodic/episodic_screenspaceeffects.h119
-rw-r--r--mp/src/game/client/episodic/flesh_internal_material_proxy.cpp225
10 files changed, 2718 insertions, 0 deletions
diff --git a/mp/src/game/client/episodic/c_npc_advisor.cpp b/mp/src/game/client/episodic/c_npc_advisor.cpp
new file mode 100644
index 00000000..94cd047b
--- /dev/null
+++ b/mp/src/game/client/episodic/c_npc_advisor.cpp
@@ -0,0 +1,251 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Definition for client-side advisor.
+//
+//=====================================================================================//
+
+
+
+#include "cbase.h"
+// this file contains the definitions for the message ID constants (eg ADVISOR_MSG_START_BEAM etc)
+#include "npc_advisor_shared.h"
+
+#if NPC_ADVISOR_HAS_BEHAVIOR
+
+#include "particles_simple.h"
+#include "citadel_effects_shared.h"
+#include "particles_attractor.h"
+#include "clienteffectprecachesystem.h"
+#include "c_te_effect_dispatch.h"
+
+#include "c_ai_basenpc.h"
+#include "dlight.h"
+#include "iefx.h"
+
+
+//-----------------------------------------------------------------------------
+// Purpose: unpack a networked entity index into a basehandle.
+//-----------------------------------------------------------------------------
+inline C_BaseEntity *IndexToEntity( int eindex )
+{
+ return ClientEntityList().GetBaseEntityFromHandle(ClientEntityList().EntIndexToHandle(eindex));
+}
+
+
+
+#define ADVISOR_ELIGHT_CVARS 1 // enable/disable tuning advisor elight with console variables
+
+#if ADVISOR_ELIGHT_CVARS
+ConVar advisor_elight_e("advisor_elight_e","3");
+ConVar advisor_elight_rfeet("advisor_elight_rfeet","52");
+#endif
+
+
+/*! Client-side reflection of the advisor class.
+ */
+class C_NPC_Advisor : public C_AI_BaseNPC
+{
+ DECLARE_CLASS( C_NPC_Advisor, C_AI_BaseNPC );
+ DECLARE_CLIENTCLASS();
+
+public:
+ // Server to client message received
+ virtual void ReceiveMessage( int classID, bf_read &msg );
+ virtual void ClientThink( void );
+
+private:
+ /*
+ // broken into its own function so I can move it if necesasry
+ void Initialize();
+ */
+
+ // start/stop beam particle effect from me to a pelting object
+ void StartBeamFX( C_BaseEntity *pOnEntity );
+ void StopBeamFX( C_BaseEntity *pOnEntity );
+
+ void StartElight();
+ void StopElight();
+
+ int m_ElightKey; // test using an elight to make the escape sequence more visible. 0 is invalid.
+
+};
+
+IMPLEMENT_CLIENTCLASS_DT( C_NPC_Advisor, DT_NPC_Advisor, CNPC_Advisor )
+
+END_RECV_TABLE()
+
+// Server to client message received
+void C_NPC_Advisor::ReceiveMessage( int classID, bf_read &msg )
+{
+ if ( classID != GetClientClass()->m_ClassID )
+ {
+ // message is for subclass
+ BaseClass::ReceiveMessage( classID, msg );
+ return;
+ }
+
+ int messageType = msg.ReadByte();
+ switch( messageType )
+ {
+ case ADVISOR_MSG_START_BEAM:
+ {
+ int eindex = msg.ReadLong();
+ StartBeamFX(IndexToEntity(eindex));
+ }
+ break;
+
+ case ADVISOR_MSG_STOP_BEAM:
+ {
+ int eindex = msg.ReadLong();
+ StopBeamFX(IndexToEntity(eindex));
+
+ }
+ break;
+
+ case ADVISOR_MSG_STOP_ALL_BEAMS:
+ {
+ ParticleProp()->StopEmission();
+ }
+ break;
+ case ADVISOR_MSG_START_ELIGHT:
+ {
+ StartElight();
+ }
+ break;
+ case ADVISOR_MSG_STOP_ELIGHT:
+ {
+ StopElight();
+ }
+ break;
+
+ default:
+ AssertMsg1( false, "Received unknown message %d", messageType);
+ }
+}
+
+/// only use of the clientthink on the advisor is to update the elight
+void C_NPC_Advisor::ClientThink( void )
+{
+ // if the elight has gone away, bail out
+ if (m_ElightKey == 0)
+ {
+ SetNextClientThink( CLIENT_THINK_NEVER );
+ return;
+ }
+
+ // get the elight
+ dlight_t * el = effects->GetElightByKey(m_ElightKey);
+ if (!el)
+ {
+ // the elight has been invalidated. bail out.
+ m_ElightKey = 0;
+
+ SetNextClientThink( CLIENT_THINK_NEVER );
+ return;
+ }
+ else
+ {
+ el->origin = WorldSpaceCenter();
+
+#if ADVISOR_ELIGHT_CVARS
+ el->color.exponent = advisor_elight_e.GetFloat();
+ el->radius = advisor_elight_rfeet.GetFloat() * 12.0f;
+#endif
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Create a telekinetic beam effect from my head to an object
+// TODO: use a point attachment.
+//-----------------------------------------------------------------------------
+void C_NPC_Advisor::StartBeamFX( C_BaseEntity *pOnEntity )
+{
+ Assert(pOnEntity);
+ if (!pOnEntity)
+ return;
+
+ CNewParticleEffect *pEffect = ParticleProp()->Create( "Advisor_Psychic_Beam", PATTACH_ABSORIGIN_FOLLOW );
+
+ Assert(pEffect);
+ if (!pEffect) return;
+
+ ParticleProp()->AddControlPoint( pEffect, 1, pOnEntity, PATTACH_ABSORIGIN_FOLLOW );
+}
+
+
+//-----------------------------------------------------------------------------
+// terminate a telekinetic beam effect from my head to an object
+//-----------------------------------------------------------------------------
+void C_NPC_Advisor::StopBeamFX( C_BaseEntity *pOnEntity )
+{
+ Assert(pOnEntity);
+ if (!pOnEntity)
+ return;
+
+ ParticleProp()->StopParticlesInvolving( pOnEntity );
+}
+
+
+
+
+
+
+void C_NPC_Advisor::StartElight()
+{
+ AssertMsg(m_ElightKey == 0 , "Advisor trying to create new elight on top of old one!");
+ if ( m_ElightKey != 0 )
+ {
+ Warning("Advisor tried to start his elight when it was already one.\n");
+ }
+ else
+ {
+ m_ElightKey = LIGHT_INDEX_TE_DYNAMIC + this->entindex();
+ dlight_t * el = effects->CL_AllocElight( m_ElightKey );
+
+ if ( el )
+ {
+ // create an elight on top of me
+ el->origin = this->WorldSpaceCenter();
+
+ el->color.r = 235;
+ el->color.g = 255;
+ el->color.b = 255;
+ el->color.exponent = 3;
+
+ el->radius = 52*12;
+ el->decay = 0.0f;
+ el->die = gpGlobals->curtime + 2000.0f; // 1000 just means " a long time "
+
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+ else
+ { // null out the light value
+ m_ElightKey = 0;
+ }
+ }
+}
+
+void C_NPC_Advisor::StopElight()
+{
+ AssertMsg( m_ElightKey != 0, "Advisor tried to stop elight when none existed!");
+ dlight_t * el;
+ // note: the following conditional sets el if not short-circuited
+ if ( m_ElightKey == 0 || (el = effects->GetElightByKey(m_ElightKey)) == NULL )
+ {
+ Warning("Advisor tried to stop its elight when it had none.\n");
+ }
+ else
+ {
+ // kill the elight by setting the die value to now
+ el->die = gpGlobals->curtime;
+ }
+}
+
+
+#endif
+
+/******************************************************
+ * Tenser, said the Tensor. *
+ * Tenser, said the Tensor. *
+ * Tension, apprehension and dissension have begun. *
+ ******************************************************/
diff --git a/mp/src/game/client/episodic/c_npc_puppet.cpp b/mp/src/game/client/episodic/c_npc_puppet.cpp
new file mode 100644
index 00000000..4fa38ecd
--- /dev/null
+++ b/mp/src/game/client/episodic/c_npc_puppet.cpp
@@ -0,0 +1,167 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "c_ai_basenpc.h"
+#include "bone_setup.h"
+
+// Must be the last file included
+#include "memdbgon.h"
+
+
+extern ConVar r_sequence_debug;
+
+class C_NPC_Puppet : public C_AI_BaseNPC
+{
+ DECLARE_CLASS( C_NPC_Puppet, C_AI_BaseNPC );
+public:
+
+ virtual void ClientThink( void );
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+ virtual void BuildTransformations( CStudioHdr *pStudioHdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed );
+ virtual void AccumulateLayers( IBoneSetup &boneSetup, Vector pos[], Quaternion q[], float currentTime );
+
+ EHANDLE m_hAnimationTarget;
+ int m_nTargetAttachment;
+
+ DECLARE_CLIENTCLASS();
+};
+
+IMPLEMENT_CLIENTCLASS_DT( C_NPC_Puppet, DT_NPC_Puppet, CNPC_Puppet )
+ RecvPropEHandle( RECVINFO(m_hAnimationTarget) ),
+ RecvPropInt( RECVINFO(m_nTargetAttachment) ),
+END_RECV_TABLE()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_NPC_Puppet::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: We need to slam our position!
+//-----------------------------------------------------------------------------
+void C_NPC_Puppet::BuildTransformations( CStudioHdr *pStudioHdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed )
+{
+ if ( m_hAnimationTarget && m_nTargetAttachment != -1 )
+ {
+ C_BaseAnimating *pTarget = m_hAnimationTarget->GetBaseAnimating();
+ if ( pTarget )
+ {
+ matrix3x4_t matTarget;
+ pTarget->GetAttachment( m_nTargetAttachment, matTarget );
+
+ MatrixCopy( matTarget, GetBoneForWrite( 0 ) );
+ boneComputed.ClearAll(); // FIXME: Why is this calculated already?
+ boneComputed.MarkBone( 0 );
+ }
+ }
+
+ // Call the baseclass
+ BaseClass::BuildTransformations( pStudioHdr, pos, q, cameraTransform, boneMask, boneComputed );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_NPC_Puppet::ClientThink( void )
+{
+ if ( m_hAnimationTarget == NULL )
+ return;
+
+ C_BaseAnimating *pTarget = m_hAnimationTarget->GetBaseAnimating();
+ if ( pTarget == NULL )
+ return;
+
+ int nTargetSequence = pTarget->GetSequence();
+ const char *pSequenceName = pTarget->GetSequenceName( nTargetSequence );
+
+ int nSequence = LookupSequence( pSequenceName );
+ if ( nSequence >= 0 )
+ {
+ if ( nSequence != GetSequence() )
+ {
+ SetSequence( nSequence );
+ UpdateVisibility();
+ }
+
+ SetCycle( pTarget->GetCycle() );
+ SetPlaybackRate( pTarget->GetPlaybackRate() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_NPC_Puppet::AccumulateLayers( IBoneSetup &boneSetup, Vector pos[], Quaternion q[], float currentTime )
+{
+ if ( m_hAnimationTarget == NULL )
+ return;
+
+ C_BaseAnimatingOverlay *pTarget = dynamic_cast<C_BaseAnimatingOverlay *>( m_hAnimationTarget->GetBaseAnimating() );
+ if ( pTarget == NULL )
+ return;
+
+ // resort the layers
+ int layer[MAX_OVERLAYS];
+ int i;
+ for (i = 0; i < MAX_OVERLAYS; i++)
+ {
+ layer[i] = MAX_OVERLAYS;
+ }
+ for (i = 0; i < pTarget->m_AnimOverlay.Count(); i++)
+ {
+ if (pTarget->m_AnimOverlay[i].m_nOrder < MAX_OVERLAYS)
+ {
+ layer[pTarget->m_AnimOverlay[i].m_nOrder] = i;
+ }
+ }
+
+ int j;
+ for (j = 0; j < MAX_OVERLAYS; j++)
+ {
+ i = layer[ j ];
+ if (i < pTarget->m_AnimOverlay.Count())
+ {
+ float fWeight = pTarget->m_AnimOverlay[i].m_flWeight;
+
+ if (fWeight > 0)
+ {
+ const char *pSequenceName = pTarget->GetSequenceName( pTarget->m_AnimOverlay[i].m_nSequence );
+
+ int nSequence = LookupSequence( pSequenceName );
+ if ( nSequence >= 0 )
+ {
+ float fCycle = pTarget->m_AnimOverlay[ i ].m_flCycle;
+
+ fCycle = ClampCycle( fCycle, IsSequenceLooping( nSequence ) );
+
+ if (fWeight > 1)
+ fWeight = 1;
+
+ boneSetup.AccumulatePose( pos, q, nSequence, fCycle, fWeight, currentTime, NULL );
+
+#if _DEBUG
+ if (Q_stristr( boneSetup.GetStudioHdr()->pszName(), r_sequence_debug.GetString()) != NULL)
+ {
+ DevMsgRT( "%6.2f : %30s : %5.3f : %4.2f : %1d\n", currentTime, boneSetup.GetStudioHdr()->pSeqdesc( nSequence ).pszLabel(), fCycle, fWeight, i );
+ }
+#endif
+
+ }
+ }
+ }
+ }
+}
+
diff --git a/mp/src/game/client/episodic/c_prop_coreball.cpp b/mp/src/game/client/episodic/c_prop_coreball.cpp
new file mode 100644
index 00000000..81a7a85a
--- /dev/null
+++ b/mp/src/game/client/episodic/c_prop_coreball.cpp
@@ -0,0 +1,142 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+
+class C_PropCoreBall : public C_BaseAnimating
+{
+ DECLARE_CLASS( C_PropCoreBall, C_BaseAnimating );
+ DECLARE_CLIENTCLASS();
+ DECLARE_DATADESC();
+
+public:
+
+ C_PropCoreBall();
+
+ void ApplyBoneMatrixTransform( matrix3x4_t& transform );
+
+ float m_flScaleX;
+ float m_flScaleY;
+ float m_flScaleZ;
+
+ float m_flLerpTimeX;
+ float m_flLerpTimeY;
+ float m_flLerpTimeZ;
+
+ float m_flGoalTimeX;
+ float m_flGoalTimeY;
+ float m_flGoalTimeZ;
+
+ float m_flCurrentScale[3];
+ bool m_bRunningScale[3];
+ float m_flTargetScale[3];
+
+private:
+
+};
+
+void RecvProxy_ScaleX( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_PropCoreBall *pCoreData = (C_PropCoreBall *) pStruct;
+
+ pCoreData->m_flScaleX = pData->m_Value.m_Float;
+
+ if ( pCoreData->m_bRunningScale[0] == true )
+ {
+ pCoreData->m_flTargetScale[0] = pCoreData->m_flCurrentScale[0];
+ }
+}
+
+void RecvProxy_ScaleY( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_PropCoreBall *pCoreData = (C_PropCoreBall *) pStruct;
+
+ pCoreData->m_flScaleY = pData->m_Value.m_Float;
+
+ if ( pCoreData->m_bRunningScale[1] == true )
+ {
+ pCoreData->m_flTargetScale[1] = pCoreData->m_flCurrentScale[1];
+ }
+}
+
+void RecvProxy_ScaleZ( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_PropCoreBall *pCoreData = (C_PropCoreBall *) pStruct;
+
+ pCoreData->m_flScaleZ = pData->m_Value.m_Float;
+
+ if ( pCoreData->m_bRunningScale[2] == true )
+ {
+ pCoreData->m_flTargetScale[2] = pCoreData->m_flCurrentScale[2];
+ }
+}
+
+IMPLEMENT_CLIENTCLASS_DT( C_PropCoreBall, DT_PropCoreBall, CPropCoreBall )
+ RecvPropFloat( RECVINFO( m_flScaleX ), 0, RecvProxy_ScaleX ),
+ RecvPropFloat( RECVINFO( m_flScaleY ), 0, RecvProxy_ScaleY ),
+ RecvPropFloat( RECVINFO( m_flScaleZ ), 0, RecvProxy_ScaleZ ),
+
+ RecvPropFloat( RECVINFO( m_flLerpTimeX ) ),
+ RecvPropFloat( RECVINFO( m_flLerpTimeY ) ),
+ RecvPropFloat( RECVINFO( m_flLerpTimeZ ) ),
+
+ RecvPropFloat( RECVINFO( m_flGoalTimeX ) ),
+ RecvPropFloat( RECVINFO( m_flGoalTimeY ) ),
+ RecvPropFloat( RECVINFO( m_flGoalTimeZ ) ),
+END_RECV_TABLE()
+
+
+BEGIN_DATADESC( C_PropCoreBall )
+ DEFINE_AUTO_ARRAY( m_flTargetScale, FIELD_FLOAT ),
+ DEFINE_AUTO_ARRAY( m_bRunningScale, FIELD_BOOLEAN ),
+END_DATADESC()
+
+C_PropCoreBall::C_PropCoreBall( void )
+{
+ m_flTargetScale[0] = 1.0f;
+ m_flTargetScale[1] = 1.0f;
+ m_flTargetScale[2] = 1.0f;
+
+ m_bRunningScale[0] = false;
+ m_bRunningScale[1] = false;
+ m_bRunningScale[2] = false;
+}
+
+void C_PropCoreBall::ApplyBoneMatrixTransform( matrix3x4_t& transform )
+{
+ BaseClass::ApplyBoneMatrixTransform( transform );
+
+ float flVal[3] = { m_flTargetScale[0], m_flTargetScale[1], m_flTargetScale[2] };
+ float *flTargetScale[3] = { &m_flTargetScale[0], &m_flTargetScale[1], &m_flTargetScale[2] };
+ float flScale[3] = { m_flScaleX, m_flScaleY, m_flScaleZ };
+ float flLerpTime[3] = { m_flLerpTimeX, m_flLerpTimeY, m_flLerpTimeZ };
+ float flGoalTime[3] = { m_flGoalTimeX, m_flGoalTimeY, m_flGoalTimeZ };
+ bool *bRunning[3] = { &m_bRunningScale[0], &m_bRunningScale[1], &m_bRunningScale[2] };
+
+ for ( int i = 0; i < 3; i++ )
+ {
+ if ( *flTargetScale[i] != flScale[i] )
+ {
+ float deltaTime = (float)( gpGlobals->curtime - flGoalTime[i]) / flLerpTime[i];
+ float flRemapVal = SimpleSplineRemapVal( deltaTime, 0.0f, 1.0f, *flTargetScale[i], flScale[i] );
+
+ *bRunning[i] = true;
+
+ if ( deltaTime >= 1.0f )
+ {
+ *flTargetScale[i] = flScale[i];
+ *bRunning[i] = false;
+ }
+
+ flVal[i] = flRemapVal;
+ m_flCurrentScale[i] = flVal[i];
+ }
+ }
+
+ VectorScale( transform[0], flVal[0], transform[0] );
+ VectorScale( transform[1], flVal[1], transform[1] );
+ VectorScale( transform[2], flVal[2], transform[2] );
+} \ No newline at end of file
diff --git a/mp/src/game/client/episodic/c_prop_scalable.cpp b/mp/src/game/client/episodic/c_prop_scalable.cpp
new file mode 100644
index 00000000..117d5aec
--- /dev/null
+++ b/mp/src/game/client/episodic/c_prop_scalable.cpp
@@ -0,0 +1,196 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+
+class C_PropScalable : public C_BaseAnimating
+{
+ DECLARE_CLASS( C_PropScalable, C_BaseAnimating );
+ DECLARE_CLIENTCLASS();
+ DECLARE_DATADESC();
+
+public:
+
+ C_PropScalable();
+
+ virtual void ApplyBoneMatrixTransform( matrix3x4_t& transform );
+ virtual void GetRenderBounds( Vector &theMins, Vector &theMaxs );
+
+ // Must be available to proxy functions
+ float m_flScaleX;
+ float m_flScaleY;
+ float m_flScaleZ;
+
+ float m_flLerpTimeX;
+ float m_flLerpTimeY;
+ float m_flLerpTimeZ;
+
+ float m_flGoalTimeX;
+ float m_flGoalTimeY;
+ float m_flGoalTimeZ;
+
+ float m_flCurrentScale[3];
+ bool m_bRunningScale[3];
+ float m_flTargetScale[3];
+
+private:
+
+ void CalculateScale( void );
+ float m_nCalcFrame; // Frame the last calculation was made at
+};
+
+void RecvProxy_ScaleX( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_PropScalable *pCoreData = (C_PropScalable *) pStruct;
+
+ pCoreData->m_flScaleX = pData->m_Value.m_Float;
+
+ if ( pCoreData->m_bRunningScale[0] == true )
+ {
+ pCoreData->m_flTargetScale[0] = pCoreData->m_flCurrentScale[0];
+ }
+}
+
+void RecvProxy_ScaleY( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_PropScalable *pCoreData = (C_PropScalable *) pStruct;
+
+ pCoreData->m_flScaleY = pData->m_Value.m_Float;
+
+ if ( pCoreData->m_bRunningScale[1] == true )
+ {
+ pCoreData->m_flTargetScale[1] = pCoreData->m_flCurrentScale[1];
+ }
+}
+
+void RecvProxy_ScaleZ( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_PropScalable *pCoreData = (C_PropScalable *) pStruct;
+
+ pCoreData->m_flScaleZ = pData->m_Value.m_Float;
+
+ if ( pCoreData->m_bRunningScale[2] == true )
+ {
+ pCoreData->m_flTargetScale[2] = pCoreData->m_flCurrentScale[2];
+ }
+}
+
+IMPLEMENT_CLIENTCLASS_DT( C_PropScalable, DT_PropScalable, CPropScalable )
+ RecvPropFloat( RECVINFO( m_flScaleX ), 0, RecvProxy_ScaleX ),
+ RecvPropFloat( RECVINFO( m_flScaleY ), 0, RecvProxy_ScaleY ),
+ RecvPropFloat( RECVINFO( m_flScaleZ ), 0, RecvProxy_ScaleZ ),
+
+ RecvPropFloat( RECVINFO( m_flLerpTimeX ) ),
+ RecvPropFloat( RECVINFO( m_flLerpTimeY ) ),
+ RecvPropFloat( RECVINFO( m_flLerpTimeZ ) ),
+
+ RecvPropFloat( RECVINFO( m_flGoalTimeX ) ),
+ RecvPropFloat( RECVINFO( m_flGoalTimeY ) ),
+ RecvPropFloat( RECVINFO( m_flGoalTimeZ ) ),
+END_RECV_TABLE()
+
+
+BEGIN_DATADESC( C_PropScalable )
+ DEFINE_AUTO_ARRAY( m_flTargetScale, FIELD_FLOAT ),
+ DEFINE_AUTO_ARRAY( m_bRunningScale, FIELD_BOOLEAN ),
+END_DATADESC()
+
+C_PropScalable::C_PropScalable( void )
+{
+ m_flTargetScale[0] = 1.0f;
+ m_flTargetScale[1] = 1.0f;
+ m_flTargetScale[2] = 1.0f;
+
+ m_bRunningScale[0] = false;
+ m_bRunningScale[1] = false;
+ m_bRunningScale[2] = false;
+
+ m_nCalcFrame = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Calculates the scake of the object once per frame
+//-----------------------------------------------------------------------------
+void C_PropScalable::CalculateScale( void )
+{
+ // Don't bother to calculate this for a second time in the same frame
+ if ( m_nCalcFrame == gpGlobals->framecount )
+ return;
+
+ // Mark that we cached this value for the frame
+ m_nCalcFrame = gpGlobals->framecount;
+
+ float flVal[3] = { m_flTargetScale[0], m_flTargetScale[1], m_flTargetScale[2] };
+ float *flTargetScale[3] = { &m_flTargetScale[0], &m_flTargetScale[1], &m_flTargetScale[2] };
+ float flScale[3] = { m_flScaleX, m_flScaleY, m_flScaleZ };
+ float flLerpTime[3] = { m_flLerpTimeX, m_flLerpTimeY, m_flLerpTimeZ };
+ float flGoalTime[3] = { m_flGoalTimeX, m_flGoalTimeY, m_flGoalTimeZ };
+ bool *bRunning[3] = { &m_bRunningScale[0], &m_bRunningScale[1], &m_bRunningScale[2] };
+
+ for ( int i = 0; i < 3; i++ )
+ {
+ if ( *flTargetScale[i] != flScale[i] )
+ {
+ float deltaTime = (float)( gpGlobals->curtime - flGoalTime[i]) / flLerpTime[i];
+ float flRemapVal = SimpleSplineRemapValClamped( deltaTime, 0.0f, 1.0f, *flTargetScale[i], flScale[i] );
+
+ *bRunning[i] = true;
+
+ if ( deltaTime >= 1.0f )
+ {
+ *flTargetScale[i] = flScale[i];
+ *bRunning[i] = false;
+ }
+
+ flVal[i] = flRemapVal;
+ m_flCurrentScale[i] = flVal[i];
+ }
+ else
+ {
+ m_flCurrentScale[i] = m_flTargetScale[i];
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Scales the bones based on the current scales
+//-----------------------------------------------------------------------------
+void C_PropScalable::ApplyBoneMatrixTransform( matrix3x4_t& transform )
+{
+ BaseClass::ApplyBoneMatrixTransform( transform );
+
+ // Find the scale for this frame
+ CalculateScale();
+
+ VectorScale( transform[0], m_flCurrentScale[0], transform[0] );
+ VectorScale( transform[1], m_flCurrentScale[1], transform[1] );
+ VectorScale( transform[2], m_flCurrentScale[2], transform[2] );
+
+ UpdateVisibility();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Ensures the render bounds match the scales
+//-----------------------------------------------------------------------------
+void C_PropScalable::GetRenderBounds( Vector &theMins, Vector &theMaxs )
+{
+ BaseClass::GetRenderBounds( theMins, theMaxs );
+
+ // Find the scale for this frame
+ CalculateScale();
+
+ // Extend our render bounds to encompass the scaled object
+ theMins.x *= m_flCurrentScale[0];
+ theMins.y *= m_flCurrentScale[1];
+ theMins.z *= m_flCurrentScale[2];
+
+ theMaxs.x *= m_flCurrentScale[0];
+ theMaxs.y *= m_flCurrentScale[1];
+ theMaxs.z *= m_flCurrentScale[2];
+
+ Assert( theMins.IsValid() && theMaxs.IsValid() );
+
+}
diff --git a/mp/src/game/client/episodic/c_vehicle_jeep_episodic.cpp b/mp/src/game/client/episodic/c_vehicle_jeep_episodic.cpp
new file mode 100644
index 00000000..4627bbfb
--- /dev/null
+++ b/mp/src/game/client/episodic/c_vehicle_jeep_episodic.cpp
@@ -0,0 +1,133 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "c_prop_vehicle.h"
+#include "c_vehicle_jeep.h"
+#include "movevars_shared.h"
+#include "view.h"
+#include "flashlighteffect.h"
+#include "c_baseplayer.h"
+#include "c_te_effect_dispatch.h"
+#include "hl2_vehicle_radar.h"
+#include "usermessages.h"
+#include "hud_radar.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//=============================================================================
+//
+// Client-side Episodic Jeep (Jalopy) Class
+//
+class C_PropJeepEpisodic : public C_PropJeep
+{
+
+ DECLARE_CLASS( C_PropJeepEpisodic, C_PropJeep );
+
+public:
+ DECLARE_CLIENTCLASS();
+
+public:
+ C_PropJeepEpisodic();
+
+ void OnEnteredVehicle( C_BasePlayer *pPlayer );
+ void Simulate( void );
+
+public:
+ int m_iNumRadarContacts;
+ Vector m_vecRadarContactPos[ RADAR_MAX_CONTACTS ];
+ int m_iRadarContactType[ RADAR_MAX_CONTACTS ];
+};
+C_PropJeepEpisodic *g_pJalopy = NULL;
+
+IMPLEMENT_CLIENTCLASS_DT( C_PropJeepEpisodic, DT_CPropJeepEpisodic, CPropJeepEpisodic )
+ //CNetworkVar( int, m_iNumRadarContacts );
+ RecvPropInt( RECVINFO(m_iNumRadarContacts) ),
+
+ //CNetworkArray( Vector, m_vecRadarContactPos, RADAR_MAX_CONTACTS );
+ RecvPropArray( RecvPropVector(RECVINFO(m_vecRadarContactPos[0])), m_vecRadarContactPos ),
+
+ //CNetworkArray( int, m_iRadarContactType, RADAR_MAX_CONTACTS );
+ RecvPropArray( RecvPropInt( RECVINFO(m_iRadarContactType[0] ) ), m_iRadarContactType ),
+
+END_RECV_TABLE()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void __MsgFunc_UpdateJalopyRadar(bf_read &msg)
+{
+ // Radar code here!
+ if( !GetHudRadar() )
+ return;
+
+ // Sometimes we update more quickly when we need to track something in high resolution.
+ // Usually we do not, so default to false.
+ GetHudRadar()->m_bUseFastUpdate = false;
+
+ for( int i = 0 ; i < g_pJalopy->m_iNumRadarContacts ; i++ )
+ {
+ if( g_pJalopy->m_iRadarContactType[i] == RADAR_CONTACT_DOG )
+ {
+ GetHudRadar()->m_bUseFastUpdate = true;
+ break;
+ }
+ }
+
+ float flContactTimeToLive;
+
+ if( GetHudRadar()->m_bUseFastUpdate )
+ {
+ flContactTimeToLive = RADAR_UPDATE_FREQUENCY_FAST;
+ }
+ else
+ {
+ flContactTimeToLive = RADAR_UPDATE_FREQUENCY;
+ }
+
+ for( int i = 0 ; i < g_pJalopy->m_iNumRadarContacts ; i++ )
+ {
+ GetHudRadar()->AddRadarContact( g_pJalopy->m_vecRadarContactPos[i], g_pJalopy->m_iRadarContactType[i], flContactTimeToLive );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+C_PropJeepEpisodic::C_PropJeepEpisodic()
+{
+ if( g_pJalopy == NULL )
+ {
+ usermessages->HookMessage( "UpdateJalopyRadar", __MsgFunc_UpdateJalopyRadar );
+ }
+
+ g_pJalopy = this;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_PropJeepEpisodic::Simulate( void )
+{
+ // Keep trying to hook to the radar.
+ if( GetHudRadar() != NULL )
+ {
+ // This is not our ideal long-term solution. This will only work if you only have
+ // one jalopy in a given level. The Jalopy and the Radar Screen are currently both
+ // assumed to be singletons. This is appropriate for EP2, however. (sjb)
+ GetHudRadar()->SetVehicle( this );
+ }
+
+ BaseClass::Simulate();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_PropJeepEpisodic::OnEnteredVehicle( C_BasePlayer *pPlayer )
+{
+ BaseClass::OnEnteredVehicle( pPlayer );
+}
diff --git a/mp/src/game/client/episodic/c_vort_charge_token.cpp b/mp/src/game/client/episodic/c_vort_charge_token.cpp
new file mode 100644
index 00000000..6085ee6e
--- /dev/null
+++ b/mp/src/game/client/episodic/c_vort_charge_token.cpp
@@ -0,0 +1,600 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=====================================================================================//
+
+#include "cbase.h"
+#include "particles_simple.h"
+#include "citadel_effects_shared.h"
+#include "particles_attractor.h"
+#include "iefx.h"
+#include "dlight.h"
+#include "clienteffectprecachesystem.h"
+#include "c_te_effect_dispatch.h"
+#include "fx_quad.h"
+
+#include "c_ai_basenpc.h"
+
+// For material proxy
+#include "proxyentity.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imaterialvar.h"
+
+#define NUM_INTERIOR_PARTICLES 8
+
+#define DLIGHT_RADIUS (150.0f)
+#define DLIGHT_MINLIGHT (40.0f/255.0f)
+
+class C_NPC_Vortigaunt : public C_AI_BaseNPC
+{
+ DECLARE_CLASS( C_NPC_Vortigaunt, C_AI_BaseNPC );
+ DECLARE_CLIENTCLASS();
+
+public:
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+ virtual void ClientThink( void );
+ virtual void ReceiveMessage( int classID, bf_read &msg );
+
+public:
+ bool m_bIsBlue; ///< wants to fade to blue
+ float m_flBlueEndFadeTime; ///< when to end fading from one skin to another
+
+ bool m_bIsBlack; ///< wants to fade to black (networked)
+ float m_flBlackFade; ///< [0.00 .. 1.00] where 1.00 is all black. Locally interpolated.
+};
+
+IMPLEMENT_CLIENTCLASS_DT( C_NPC_Vortigaunt, DT_NPC_Vortigaunt, CNPC_Vortigaunt )
+ RecvPropTime( RECVINFO(m_flBlueEndFadeTime ) ),
+ RecvPropBool( RECVINFO(m_bIsBlue) ),
+ RecvPropBool( RECVINFO(m_bIsBlack) ),
+END_RECV_TABLE()
+
+
+#define VORTIGAUNT_BLUE_FADE_TIME 2.25f // takes this long to fade from green to blue or back
+#define VORT_BLACK_FADE_TIME 2.2f // time to interpolate up or down in fading to black
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : updateType -
+//-----------------------------------------------------------------------------
+void C_NPC_Vortigaunt::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ // start thinking if we need to fade.
+ if ( m_flBlackFade != (m_bIsBlack ? 1.0f : 0.0f) )
+ {
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_NPC_Vortigaunt::ClientThink( void )
+{
+ // Don't update if our frame hasn't moved forward (paused)
+ if ( gpGlobals->frametime <= 0.0f )
+ return;
+
+ if ( m_bIsBlack )
+ {
+ // are we done?
+ if ( m_flBlackFade >= 1.0f )
+ {
+ m_flBlackFade = 1.0f;
+ SetNextClientThink( CLIENT_THINK_NEVER );
+ }
+ else // interpolate there
+ {
+ float lerpQuant = gpGlobals->frametime / VORT_BLACK_FADE_TIME;
+ m_flBlackFade += lerpQuant;
+ if ( m_flBlackFade > 1.0f )
+ {
+ m_flBlackFade = 1.0f;
+ }
+ }
+ }
+ else
+ {
+ // are we done?
+ if ( m_flBlackFade <= 0.0f )
+ {
+ m_flBlackFade = 0.0f;
+ SetNextClientThink( CLIENT_THINK_NEVER );
+ }
+ else // interpolate there
+ {
+ float lerpQuant = gpGlobals->frametime / VORT_BLACK_FADE_TIME;
+ m_flBlackFade -= lerpQuant;
+ if ( m_flBlackFade < 0.0f )
+ {
+ m_flBlackFade = 0.0f;
+ }
+ }
+ }
+}
+
+// FIXME: Move to shared code!
+#define VORTFX_ZAPBEAM 0
+#define VORTFX_ARMBEAM 1
+
+//-----------------------------------------------------------------------------
+// Purpose: Receive messages from the server
+//-----------------------------------------------------------------------------
+void C_NPC_Vortigaunt::ReceiveMessage( int classID, bf_read &msg )
+{
+ // Is the message for a sub-class?
+ if ( classID != GetClientClass()->m_ClassID )
+ {
+ BaseClass::ReceiveMessage( classID, msg );
+ return;
+ }
+
+ int messageType = msg.ReadByte();
+ switch( messageType )
+ {
+ case VORTFX_ZAPBEAM:
+ {
+ // Find our attachment point
+ unsigned char nAttachment = msg.ReadByte();
+
+ // Get our attachment position
+ Vector vecStart;
+ QAngle vecAngles;
+ GetAttachment( nAttachment, vecStart, vecAngles );
+
+ // Get the final position we'll strike
+ Vector vecEndPos;
+ msg.ReadBitVec3Coord( vecEndPos );
+
+ // Place a beam between the two points
+ CNewParticleEffect *pEffect = ParticleProp()->Create( "vortigaunt_beam", PATTACH_POINT_FOLLOW, nAttachment );
+ if ( pEffect )
+ {
+ pEffect->SetControlPoint( 0, vecStart );
+ pEffect->SetControlPoint( 1, vecEndPos );
+ }
+ }
+ break;
+
+ case VORTFX_ARMBEAM:
+ {
+ int nIndex = msg.ReadLong();
+ C_BaseEntity *pEnt = ClientEntityList().GetBaseEntityFromHandle( ClientEntityList().EntIndexToHandle( nIndex ) );
+
+ if ( pEnt )
+ {
+ unsigned char nAttachment = msg.ReadByte();
+ Vector vecEndPos;
+ msg.ReadBitVec3Coord( vecEndPos );
+
+ Vector vecNormal;
+ msg.ReadBitVec3Normal( vecNormal );
+
+ CNewParticleEffect *pEffect = pEnt->ParticleProp()->Create( "vortigaunt_beam_charge", PATTACH_POINT_FOLLOW, nAttachment );
+ if ( pEffect )
+ {
+ // Set the control point's angles to be the surface normal we struct
+ Vector vecRight, vecUp;
+ VectorVectors( vecNormal, vecRight, vecUp );
+ pEffect->SetControlPointOrientation( 1, vecNormal, vecRight, vecUp );
+ pEffect->SetControlPoint( 1, vecEndPos );
+ }
+ }
+ }
+ break;
+ default:
+ AssertMsg1( false, "Received unknown message %d", messageType);
+ }
+}
+
+class C_VortigauntChargeToken : public C_BaseEntity
+{
+ DECLARE_CLASS( C_VortigauntChargeToken, C_BaseEntity );
+ DECLARE_CLIENTCLASS();
+
+public:
+ virtual void UpdateOnRemove( void );
+ virtual void ClientThink( void );
+ virtual void NotifyShouldTransmit( ShouldTransmitState_t state );
+ virtual void OnDataChanged( DataUpdateType_t type );
+
+ // For RecvProxy handlers
+ float m_flFadeOutTime;
+ float m_flFadeOutStart;
+
+private:
+ bool SetupEmitters( void );
+
+ bool m_bFadeOut;
+ CNewParticleEffect *m_hEffect;
+ dlight_t *m_pDLight;
+};
+
+void RecvProxy_FadeOutDuration( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_VortigauntChargeToken *pVortToken = (C_VortigauntChargeToken *) pStruct;
+ Assert( pOut == &pVortToken->m_flFadeOutTime );
+
+ pVortToken->m_flFadeOutStart = gpGlobals->curtime;
+ pVortToken->m_flFadeOutTime = ( pData->m_Value.m_Float - gpGlobals->curtime );
+}
+
+IMPLEMENT_CLIENTCLASS_DT( C_VortigauntChargeToken, DT_VortigauntChargeToken, CVortigauntChargeToken )
+ RecvPropBool( RECVINFO( m_bFadeOut ) ),
+END_RECV_TABLE()
+
+void C_VortigauntChargeToken::UpdateOnRemove( void )
+{
+ if ( m_hEffect )
+ {
+ m_hEffect->StopEmission();
+ m_hEffect = NULL;
+ }
+
+ if ( m_pDLight != NULL )
+ {
+ m_pDLight->die = gpGlobals->curtime;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Change our transmission state
+//-----------------------------------------------------------------------------
+void C_VortigauntChargeToken::NotifyShouldTransmit( ShouldTransmitState_t state )
+{
+ BaseClass::NotifyShouldTransmit( state );
+
+ // Turn off
+ if ( state == SHOULDTRANSMIT_END )
+ {
+ if ( m_hEffect )
+ {
+ m_hEffect->StopEmission();
+ m_hEffect = NULL;
+ }
+ }
+
+ // Turn on
+ if ( state == SHOULDTRANSMIT_START )
+ {
+ m_hEffect = ParticleProp()->Create( "vortigaunt_charge_token", PATTACH_ABSORIGIN_FOLLOW );
+ m_hEffect->SetControlPointEntity( 0, this );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_VortigauntChargeToken::OnDataChanged( DataUpdateType_t type )
+{
+ if ( m_bFadeOut )
+ {
+ if ( m_hEffect )
+ {
+ m_hEffect->StopEmission();
+ m_hEffect = NULL;
+ }
+ }
+
+ BaseClass::OnDataChanged( type );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_VortigauntChargeToken::ClientThink( void )
+{
+ //
+ // -- DLight
+ //
+
+ if ( m_pDLight != NULL )
+ {
+ m_pDLight->origin = GetAbsOrigin();
+ m_pDLight->radius = DLIGHT_RADIUS;
+ }
+}
+
+//=============================================================================
+//
+// Dispel Effect
+//
+//=============================================================================
+
+class C_VortigauntEffectDispel : public C_BaseEntity
+{
+ DECLARE_CLASS( C_VortigauntEffectDispel, C_BaseEntity );
+ DECLARE_CLIENTCLASS();
+
+public:
+ virtual void UpdateOnRemove( void );
+ virtual void ClientThink( void );
+ virtual void NotifyShouldTransmit( ShouldTransmitState_t state );
+ virtual void OnDataChanged( DataUpdateType_t type );
+
+ // For RecvProxy handlers
+ float m_flFadeOutTime;
+ float m_flFadeOutStart;
+
+private:
+ bool SetupEmitters( void );
+
+ CNewParticleEffect *m_hEffect;
+ bool m_bFadeOut;
+ dlight_t *m_pDLight;
+};
+
+void RecvProxy_DispelFadeOutDuration( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_VortigauntEffectDispel *pVortToken = (C_VortigauntEffectDispel *) pStruct;
+ Assert( pOut == &pVortToken->m_flFadeOutTime );
+
+ pVortToken->m_flFadeOutStart = gpGlobals->curtime;
+ pVortToken->m_flFadeOutTime = ( pData->m_Value.m_Float - gpGlobals->curtime );
+}
+
+IMPLEMENT_CLIENTCLASS_DT( C_VortigauntEffectDispel, DT_VortigauntEffectDispel, CVortigauntEffectDispel )
+ RecvPropBool( RECVINFO( m_bFadeOut ) ),
+END_RECV_TABLE()
+
+void C_VortigauntEffectDispel::UpdateOnRemove( void )
+{
+ if ( m_hEffect )
+ {
+ m_hEffect->StopEmission();
+ m_hEffect = NULL;
+ }
+
+ if ( m_pDLight != NULL )
+ {
+ m_pDLight->die = gpGlobals->curtime;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_VortigauntEffectDispel::OnDataChanged( DataUpdateType_t type )
+{
+ if ( m_bFadeOut )
+ {
+ if ( m_hEffect )
+ {
+ m_hEffect->StopEmission();
+ m_hEffect = NULL;
+ }
+ }
+
+ BaseClass::OnDataChanged( type );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_VortigauntEffectDispel::NotifyShouldTransmit( ShouldTransmitState_t state )
+{
+ BaseClass::NotifyShouldTransmit( state );
+
+ // Turn off
+ if ( state == SHOULDTRANSMIT_END )
+ {
+ if ( m_hEffect )
+ {
+ m_hEffect->StopEmission();
+ m_hEffect = NULL;
+ }
+ }
+
+ // Turn on
+ if ( state == SHOULDTRANSMIT_START )
+ {
+ m_hEffect = ParticleProp()->Create( "vortigaunt_hand_glow", PATTACH_ABSORIGIN_FOLLOW );
+ m_hEffect->SetControlPointEntity( 0, this );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create our emitter
+//-----------------------------------------------------------------------------
+bool C_VortigauntEffectDispel::SetupEmitters( void )
+{
+ m_pDLight = NULL;
+
+#ifndef _X360
+ m_pDLight = effects->CL_AllocDlight ( index );
+ m_pDLight->origin = GetAbsOrigin();
+ m_pDLight->color.r = 64;
+ m_pDLight->color.g = 255;
+ m_pDLight->color.b = 64;
+ m_pDLight->radius = 0;
+ m_pDLight->minlight = DLIGHT_MINLIGHT;
+ m_pDLight->die = FLT_MAX;
+#endif // _X360
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_VortigauntEffectDispel::ClientThink( void )
+{
+ if ( m_pDLight != NULL )
+ {
+ m_pDLight->origin = GetAbsOrigin();
+ m_pDLight->radius = DLIGHT_RADIUS;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void DispelCallback( const CEffectData &data )
+{
+ // Kaboom!
+ Vector startPos = data.m_vOrigin + Vector(0,0,16);
+ Vector endPos = data.m_vOrigin + Vector(0,0,-64);
+
+ trace_t tr;
+ UTIL_TraceLine( startPos, endPos, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr );
+
+ if ( tr.fraction < 1.0f )
+ {
+ //Add a ripple quad to the surface
+ FX_AddQuad( tr.endpos + ( tr.plane.normal * 8.0f ),
+ Vector( 0, 0, 1 ),
+ 64.0f,
+ 600.0f,
+ 0.8f,
+ 1.0f, // start alpha
+ 0.0f, // end alpha
+ 0.3f,
+ random->RandomFloat( 0, 360 ),
+ 0.0f,
+ Vector( 0.5f, 1.0f, 0.5f ),
+ 0.75f,
+ "effects/ar2_altfire1b",
+ (FXQUAD_BIAS_SCALE|FXQUAD_BIAS_ALPHA|FXQUAD_COLOR_FADE) );
+
+ //Add a ripple quad to the surface
+ FX_AddQuad( tr.endpos + ( tr.plane.normal * 8.0f ),
+ Vector( 0, 0, 1 ),
+ 16.0f,
+ 300.0f,
+ 0.9f,
+ 1.0f, // start alpha
+ 0.0f, // end alpha
+ 0.9f,
+ random->RandomFloat( 0, 360 ),
+ 0.0f,
+ Vector( 0.5f, 1.0f, 0.5f ),
+ 1.25f,
+ "effects/rollerglow",
+ (FXQUAD_BIAS_SCALE|FXQUAD_BIAS_ALPHA) );
+ }
+}
+
+DECLARE_CLIENT_EFFECT( "VortDispel", DispelCallback );
+
+//-----------------------------------------------------------------------------
+// Purpose: Used for emissive lightning layer on vort
+//-----------------------------------------------------------------------------
+class CVortEmissiveProxy : public CEntityMaterialProxy
+{
+public:
+ CVortEmissiveProxy( void );
+ virtual ~CVortEmissiveProxy( void );
+ virtual bool Init( IMaterial *pMaterial, KeyValues* pKeyValues );
+ virtual void OnBind( C_BaseEntity *pC_BaseEntity );
+ virtual IMaterial * GetMaterial();
+
+private:
+
+ IMaterialVar *m_pMatEmissiveStrength;
+ IMaterialVar *m_pMatDetailBlendStrength;
+};
+
+//-----------------------------------------------------------------------------
+CVortEmissiveProxy::CVortEmissiveProxy( void )
+{
+ m_pMatEmissiveStrength = NULL;
+ m_pMatDetailBlendStrength = NULL;
+}
+
+CVortEmissiveProxy::~CVortEmissiveProxy( void )
+{
+ // Do nothing
+}
+
+//-----------------------------------------------------------------------------
+bool CVortEmissiveProxy::Init( IMaterial *pMaterial, KeyValues* pKeyValues )
+{
+ Assert( pMaterial );
+
+ // Need to get the material var
+ bool bFound;
+ m_pMatEmissiveStrength = pMaterial->FindVar( "$emissiveblendstrength", &bFound );
+
+ if ( bFound )
+ {
+ // Optional
+ bool bFound2;
+ m_pMatDetailBlendStrength = pMaterial->FindVar( "$detailblendfactor", &bFound2 );
+ }
+
+ return bFound;
+}
+
+//-----------------------------------------------------------------------------
+void CVortEmissiveProxy::OnBind( C_BaseEntity *pEnt )
+{
+ C_NPC_Vortigaunt *pVort = dynamic_cast<C_NPC_Vortigaunt *>(pEnt);
+
+ float flBlendValue;
+
+ if (pVort)
+ {
+ // do we need to crossfade?
+ if (gpGlobals->curtime < pVort->m_flBlueEndFadeTime)
+ {
+ // will be 0 when fully faded and 1 when not faded at all:
+ float fadeRatio = (pVort->m_flBlueEndFadeTime - gpGlobals->curtime) / VORTIGAUNT_BLUE_FADE_TIME;
+ if (pVort->m_bIsBlue)
+ {
+ fadeRatio = 1.0f - fadeRatio;
+ }
+ flBlendValue = clamp( fadeRatio, 0.0f, 1.0f );
+ }
+ else // no crossfade
+ {
+ flBlendValue = pVort->m_bIsBlue ? 1.0f : 0.0f;
+ }
+
+ // ALEX VLACHOS:
+ // The following variable varies on [0 .. 1]. 0.0 means the vort wants to be his normal
+ // color. 1.0 means he wants to be all black. It is interpolated in the
+ // C_NPC_Vortigaunt::ClientThink() function.
+ //
+ // pVort->m_flBlackFade
+ }
+ else
+ { // if you bind this proxy to anything non-vort (eg a ragdoll) it's always green
+ flBlendValue = 0.0f;
+ }
+
+
+ /*
+ // !!! Change me !!! I'm using a clamped sin wave for debugging
+ float flBlendValue = sinf( gpGlobals->curtime * 4.0f ) * 0.75f + 0.25f;
+
+ // Clamp 0-1
+ flBlendValue = ( flBlendValue < 0.0f ) ? 0.0f : ( flBlendValue > 1.0f ) ? 1.0f : flBlendValue;
+ */
+
+ if( m_pMatEmissiveStrength != NULL )
+ {
+ m_pMatEmissiveStrength->SetFloatValue( flBlendValue );
+ }
+
+ if( m_pMatDetailBlendStrength != NULL )
+ {
+ m_pMatDetailBlendStrength->SetFloatValue( flBlendValue );
+ }
+}
+
+//-----------------------------------------------------------------------------
+IMaterial *CVortEmissiveProxy::GetMaterial()
+{
+ if ( m_pMatEmissiveStrength != NULL )
+ return m_pMatEmissiveStrength->GetOwningMaterial();
+ else if ( m_pMatDetailBlendStrength != NULL )
+ return m_pMatDetailBlendStrength->GetOwningMaterial();
+ else
+ return NULL;
+}
+
+EXPOSE_INTERFACE( CVortEmissiveProxy, IMaterialProxy, "VortEmissive" IMATERIAL_PROXY_INTERFACE_VERSION );
diff --git a/mp/src/game/client/episodic/c_weapon_hopwire.cpp b/mp/src/game/client/episodic/c_weapon_hopwire.cpp
new file mode 100644
index 00000000..807d46bc
--- /dev/null
+++ b/mp/src/game/client/episodic/c_weapon_hopwire.cpp
@@ -0,0 +1,421 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "basegrenade_shared.h"
+#include "fx_interpvalue.h"
+#include "fx_envelope.h"
+#include "materialsystem/imaterialvar.h"
+#include "particles_simple.h"
+#include "particles_attractor.h"
+
+// FIXME: Move out
+extern void DrawSpriteTangentSpace( const Vector &vecOrigin, float flWidth, float flHeight, color32 color );
+
+#define EXPLOSION_DURATION 3.0f
+
+//-----------------------------------------------------------------------------
+// Explosion effect for hopwire
+//-----------------------------------------------------------------------------
+
+class C_HopwireExplosion : public C_EnvelopeFX
+{
+ typedef C_EnvelopeFX BaseClass;
+
+public:
+ C_HopwireExplosion( void ) :
+ m_hOwner( NULL )
+ {
+ m_FXCoreScale.SetAbsolute( 0.0f );
+ m_FXCoreAlpha.SetAbsolute( 0.0f );
+ }
+
+ virtual void Update( void );
+ virtual int DrawModel( int flags );
+ virtual void GetRenderBounds( Vector& mins, Vector& maxs );
+
+ bool SetupEmitters( void );
+ void AddParticles( void );
+ void SetOwner( C_BaseEntity *pOwner );
+ void StartExplosion( void );
+ void StopExplosion( void );
+ void StartPreExplosion( void );
+
+private:
+ CInterpolatedValue m_FXCoreScale;
+ CInterpolatedValue m_FXCoreAlpha;
+
+ CSmartPtr<CSimpleEmitter> m_pSimpleEmitter;
+ CSmartPtr<CParticleAttractor> m_pAttractorEmitter;
+
+ TimedEvent m_ParticleTimer;
+
+ CHandle<C_BaseEntity> m_hOwner;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Setup the emitters we'll be using
+//-----------------------------------------------------------------------------
+bool C_HopwireExplosion::SetupEmitters( void )
+{
+ // Setup the basic core emitter
+ if ( m_pSimpleEmitter.IsValid() == false )
+ {
+ m_pSimpleEmitter = CSimpleEmitter::Create( "hopwirecore" );
+
+ if ( m_pSimpleEmitter.IsValid() == false )
+ return false;
+ }
+
+ // Setup the attractor emitter
+ if ( m_pAttractorEmitter.IsValid() == false )
+ {
+ m_pAttractorEmitter = CParticleAttractor::Create( GetRenderOrigin(), "hopwireattractor" );
+
+ if ( m_pAttractorEmitter.IsValid() == false )
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_HopwireExplosion::AddParticles( void )
+{
+ // Make sure the emitters are setup properly
+ if ( SetupEmitters() == false )
+ return;
+
+ float tempDelta = gpGlobals->frametime;
+ while( m_ParticleTimer.NextEvent( tempDelta ) )
+ {
+ // ========================
+ // Attracted dust particles
+ // ========================
+
+ // Update our attractor point
+ m_pAttractorEmitter->SetAttractorOrigin( GetRenderOrigin() );
+
+ Vector offset;
+ SimpleParticle *sParticle;
+
+ offset = GetRenderOrigin() + RandomVector( -256.0f, 256.0f );
+
+ sParticle = (SimpleParticle *) m_pAttractorEmitter->AddParticle( sizeof(SimpleParticle), g_Mat_Fleck_Cement[0], offset );
+
+ if ( sParticle == NULL )
+ return;
+
+ sParticle->m_vecVelocity = Vector(0,0,8);
+ sParticle->m_flDieTime = 0.5f;
+ sParticle->m_flLifetime = 0.0f;
+
+ sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
+ sParticle->m_flRollDelta = 1.0f;
+
+ float alpha = random->RandomFloat( 128.0f, 200.0f );
+
+ sParticle->m_uchColor[0] = alpha;
+ sParticle->m_uchColor[1] = alpha;
+ sParticle->m_uchColor[2] = alpha;
+ sParticle->m_uchStartAlpha = alpha;
+ sParticle->m_uchEndAlpha = alpha;
+
+ sParticle->m_uchStartSize = random->RandomInt( 1, 4 );
+ sParticle->m_uchEndSize = 0;
+
+ // ========================
+ // Core effects
+ // ========================
+
+ // Reset our sort origin
+ m_pSimpleEmitter->SetSortOrigin( GetRenderOrigin() );
+
+ // Base of the core effect
+ sParticle = (SimpleParticle *) m_pSimpleEmitter->AddParticle( sizeof(SimpleParticle), m_pSimpleEmitter->GetPMaterial( "effects/strider_muzzle" ), GetRenderOrigin() );
+
+ if ( sParticle == NULL )
+ return;
+
+ sParticle->m_vecVelocity = vec3_origin;
+ sParticle->m_flDieTime = 0.2f;
+ sParticle->m_flLifetime = 0.0f;
+
+ sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
+ sParticle->m_flRollDelta = 4.0f;
+
+ alpha = random->RandomInt( 32, 200 );
+
+ sParticle->m_uchColor[0] = alpha;
+ sParticle->m_uchColor[1] = alpha;
+ sParticle->m_uchColor[2] = alpha;
+ sParticle->m_uchStartAlpha = 0;
+ sParticle->m_uchEndAlpha = alpha;
+
+ sParticle->m_uchStartSize = 255;
+ sParticle->m_uchEndSize = 0;
+
+ // Make sure we encompass the complete particle here!
+ m_pSimpleEmitter->SetParticleCullRadius( sParticle->m_uchEndSize );
+
+ // =========================
+ // Dust ring effect
+ // =========================
+
+ if ( random->RandomInt( 0, 5 ) != 1 )
+ return;
+
+ Vector vecDustColor;
+ vecDustColor.x = 0.35f;
+ vecDustColor.y = 0.3f;
+ vecDustColor.z = 0.25f;
+
+ Vector color;
+
+ int numRingSprites = 8;
+ float yaw;
+ Vector forward, vRight, vForward;
+
+ vForward = Vector( 0, 1, 0 );
+ vRight = Vector( 1, 0, 0 );
+
+ float yawOfs = random->RandomFloat( 0, 359 );
+
+ for ( int i = 0; i < numRingSprites; i++ )
+ {
+ yaw = ( (float) i / (float) numRingSprites ) * 360.0f;
+ yaw += yawOfs;
+
+ forward = ( vRight * sin( DEG2RAD( yaw) ) ) + ( vForward * cos( DEG2RAD( yaw ) ) );
+ VectorNormalize( forward );
+
+ trace_t tr;
+
+ UTIL_TraceLine( GetRenderOrigin(), GetRenderOrigin()+(Vector(0, 0, -1024)), MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr );
+
+ offset = ( RandomVector( -4.0f, 4.0f ) + tr.endpos ) + ( forward * 512.0f );
+
+ sParticle = (SimpleParticle *) m_pSimpleEmitter->AddParticle( sizeof(SimpleParticle), g_Mat_DustPuff[random->RandomInt(0,1)], offset );
+
+ if ( sParticle != NULL )
+ {
+ sParticle->m_flLifetime = 0.0f;
+ sParticle->m_flDieTime = random->RandomFloat( 0.25f, 0.5f );
+
+ sParticle->m_vecVelocity = forward * -random->RandomFloat( 1000, 1500 );
+ sParticle->m_vecVelocity[2] += 128.0f;
+
+ #if __EXPLOSION_DEBUG
+ debugoverlay->AddLineOverlay( m_vecOrigin, m_vecOrigin + sParticle->m_vecVelocity, 255, 0, 0, false, 3 );
+ #endif
+
+ sParticle->m_uchColor[0] = vecDustColor.x * 255.0f;
+ sParticle->m_uchColor[1] = vecDustColor.y * 255.0f;
+ sParticle->m_uchColor[2] = vecDustColor.z * 255.0f;
+
+ sParticle->m_uchStartSize = random->RandomInt( 32, 128 );
+ sParticle->m_uchEndSize = 200;
+
+ sParticle->m_uchStartAlpha = random->RandomFloat( 16, 64 );
+ sParticle->m_uchEndAlpha = 0;
+
+ sParticle->m_flRoll = random->RandomInt( 0, 360 );
+ sParticle->m_flRollDelta = random->RandomFloat( -16.0f, 16.0f );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pOwner -
+//-----------------------------------------------------------------------------
+void C_HopwireExplosion::SetOwner( C_BaseEntity *pOwner )
+{
+ m_hOwner = pOwner;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates the internal values for the effect
+//-----------------------------------------------------------------------------
+void C_HopwireExplosion::Update( void )
+{
+ if ( m_hOwner )
+ {
+ SetRenderOrigin( m_hOwner->GetRenderOrigin() );
+ }
+
+ BaseClass::Update();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates and renders all effects
+//-----------------------------------------------------------------------------
+int C_HopwireExplosion::DrawModel( int flags )
+{
+ AddParticles();
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Flush();
+ UpdateRefractTexture();
+
+ IMaterial *pMat = materials->FindMaterial( "effects/strider_pinch_dudv", TEXTURE_GROUP_CLIENT_EFFECTS );
+
+ float refract = m_FXCoreAlpha.Interp( gpGlobals->curtime );
+ float scale = m_FXCoreScale.Interp( gpGlobals->curtime );
+
+ IMaterialVar *pVar = pMat->FindVar( "$refractamount", NULL );
+ pVar->SetFloatValue( refract );
+
+ pRenderContext->Bind( pMat, (IClientRenderable*)this );
+
+ float sin1 = sinf( gpGlobals->curtime * 10 );
+ float sin2 = sinf( gpGlobals->curtime );
+
+ float scaleY = ( sin1 * sin2 ) * 32.0f;
+ float scaleX = (sin2 * sin2) * 32.0f;
+
+ // FIXME: The ball needs to sort properly at all times
+ static color32 white = {255,255,255,255};
+ DrawSpriteTangentSpace( GetRenderOrigin() + ( CurrentViewForward() * 128.0f ), scale+scaleX, scale+scaleY, white );
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the bounds relative to the origin (render bounds)
+//-----------------------------------------------------------------------------
+void C_HopwireExplosion::GetRenderBounds( Vector& mins, Vector& maxs )
+{
+ float scale = m_FXCoreScale.Interp( gpGlobals->curtime );
+
+ mins.Init( -scale, -scale, -scale );
+ maxs.Init( scale, scale, scale );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_HopwireExplosion::StartExplosion( void )
+{
+ m_FXCoreScale.Init( 300.0f, 500.0f, 2.0f, INTERP_SPLINE );
+ m_FXCoreAlpha.Init( 0.0f, 0.1f, 1.5f, INTERP_SPLINE );
+
+ // Particle timer
+ m_ParticleTimer.Init( 60 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_HopwireExplosion::StopExplosion( void )
+{
+ m_FXCoreAlpha.InitFromCurrent( 0.0f, 1.0f, INTERP_SPLINE );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_HopwireExplosion::StartPreExplosion( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Hopwire client class
+//-----------------------------------------------------------------------------
+
+class C_GrenadeHopwire : public C_BaseGrenade
+{
+ DECLARE_CLASS( C_GrenadeHopwire, C_BaseGrenade );
+ DECLARE_CLIENTCLASS();
+
+public:
+ C_GrenadeHopwire( void );
+
+ virtual int DrawModel( int flags );
+
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+ virtual void ReceiveMessage( int classID, bf_read &msg );
+
+private:
+
+ C_HopwireExplosion m_ExplosionEffect; // Explosion effect information and drawing
+};
+
+IMPLEMENT_CLIENTCLASS_DT( C_GrenadeHopwire, DT_GrenadeHopwire, CGrenadeHopwire )
+END_RECV_TABLE()
+
+#define HOPWIRE_START_EXPLOSION 0
+#define HOPWIRE_STOP_EXPLOSION 1
+#define HOPWIRE_START_PRE_EXPLOSION 2
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+C_GrenadeHopwire::C_GrenadeHopwire( void )
+{
+ m_ExplosionEffect.SetActive( false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Receive messages from the server
+// Input : classID - class to receive the message
+// &msg - message in question
+//-----------------------------------------------------------------------------
+void C_GrenadeHopwire::ReceiveMessage( int classID, bf_read &msg )
+{
+ if ( classID != GetClientClass()->m_ClassID )
+ {
+ // Message is for subclass
+ BaseClass::ReceiveMessage( classID, msg );
+ return;
+ }
+
+ int messageType = msg.ReadByte();
+ switch( messageType )
+ {
+ case HOPWIRE_START_EXPLOSION:
+ {
+ m_ExplosionEffect.SetActive();
+ m_ExplosionEffect.SetOwner( this );
+ m_ExplosionEffect.StartExplosion();
+ }
+ break;
+ case HOPWIRE_STOP_EXPLOSION:
+ {
+ m_ExplosionEffect.StopExplosion();
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : updateType -
+//-----------------------------------------------------------------------------
+void C_GrenadeHopwire::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ m_ExplosionEffect.Update();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : flags -
+//-----------------------------------------------------------------------------
+int C_GrenadeHopwire::DrawModel( int flags )
+{
+ if ( m_ExplosionEffect.IsActive() )
+ return 1;
+
+ return BaseClass::DrawModel( flags );
+}
+
diff --git a/mp/src/game/client/episodic/episodic_screenspaceeffects.cpp b/mp/src/game/client/episodic/episodic_screenspaceeffects.cpp
new file mode 100644
index 00000000..220e750e
--- /dev/null
+++ b/mp/src/game/client/episodic/episodic_screenspaceeffects.cpp
@@ -0,0 +1,464 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Episodic screen-space effects
+//
+
+#include "cbase.h"
+#include "ScreenSpaceEffects.h"
+#include "rendertexture.h"
+#include "materialsystem/imaterialsystemhardwareconfig.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imaterialvar.h"
+#include "cdll_client_int.h"
+#include "materialsystem/itexture.h"
+#include "KeyValues.h"
+#include "clienteffectprecachesystem.h"
+
+#include "episodic_screenspaceeffects.h"
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+#ifdef _X360
+#define STUN_TEXTURE "_rt_FullFrameFB2"
+#else
+#define STUN_TEXTURE "_rt_WaterRefraction"
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CStunEffect::Init( void )
+{
+ m_flDuration = 0.0f;
+ m_flFinishTime = 0.0f;
+ m_bUpdateView = true;
+
+ KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
+ pVMTKeyValues->SetString( "$basetexture", STUN_TEXTURE );
+ m_EffectMaterial.Init( "__stuneffect", TEXTURE_GROUP_CLIENT_EFFECTS, pVMTKeyValues );
+ m_StunTexture.Init( STUN_TEXTURE, TEXTURE_GROUP_CLIENT_EFFECTS );
+}
+
+void CStunEffect::Shutdown( void )
+{
+ m_EffectMaterial.Shutdown();
+ m_StunTexture.Shutdown();
+}
+
+//------------------------------------------------------------------------------
+// Purpose: Pick up changes in our parameters
+//------------------------------------------------------------------------------
+void CStunEffect::SetParameters( KeyValues *params )
+{
+ if( params->FindKey( "duration" ) )
+ {
+ m_flDuration = params->GetFloat( "duration" );
+ m_flFinishTime = gpGlobals->curtime + m_flDuration;
+ m_bUpdateView = true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Render the effect
+//-----------------------------------------------------------------------------
+void CStunEffect::Render( int x, int y, int w, int h )
+{
+ // Make sure we're ready to play this effect
+ if ( m_flFinishTime < gpGlobals->curtime )
+ return;
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ // Set ourselves to the proper rendermode
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ // Draw the texture if we're using it
+ if ( m_bUpdateView )
+ {
+ // Save off this pass
+ Rect_t srcRect;
+ srcRect.x = x;
+ srcRect.y = y;
+ srcRect.width = w;
+ srcRect.height = h;
+ pRenderContext->CopyRenderTargetToTextureEx( m_StunTexture, 0, &srcRect, NULL );
+ m_bUpdateView = false;
+ }
+
+ float flEffectPerc = ( m_flFinishTime - gpGlobals->curtime ) / m_flDuration;
+
+ float viewOffs = ( flEffectPerc * 32.0f ) * ( cos( gpGlobals->curtime * 40.0f ) * sin( gpGlobals->curtime * 17.0f ) );
+ float vX = x + viewOffs;
+
+ if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 80 )
+ {
+ if ( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_NONE )
+ {
+ m_EffectMaterial->ColorModulate( 1.0f, 1.0f, 1.0f );
+ }
+ else
+ {
+ // This is a stupid fix, but I don't have time to do a cleaner implementation. Since
+ // the introblur.vmt material uses unlit generic, it will tone map, so I need to undo the tone mapping
+ // using color modulate. The proper fix would be to use a different material type that
+ // supports alpha blending but not tone mapping, which I don't think exists. Whatever. This works when
+ // the tone mapping scalar is less than 1.0, which it is in the cases it's used in game.
+ float flUnTonemap = pow( 1.0f / pRenderContext->GetToneMappingScaleLinear().x, 1.0f / 2.2f );
+ m_EffectMaterial->ColorModulate( flUnTonemap, flUnTonemap, flUnTonemap );
+ }
+
+ // Set alpha blend value
+ float flOverlayAlpha = clamp( ( 150.0f / 255.0f ) * flEffectPerc, 0.0f, 1.0f );
+ m_EffectMaterial->AlphaModulate( flOverlayAlpha );
+
+ // Draw full screen alpha-blended quad
+ pRenderContext->DrawScreenSpaceRectangle( m_EffectMaterial, 0, 0, w, h,
+ vX, 0, (m_StunTexture->GetActualWidth()-1)+vX, (m_StunTexture->GetActualHeight()-1),
+ m_StunTexture->GetActualWidth(), m_StunTexture->GetActualHeight() );
+ }
+
+ // Save off this pass
+ Rect_t srcRect;
+ srcRect.x = x;
+ srcRect.y = y;
+ srcRect.width = w;
+ srcRect.height = h;
+ pRenderContext->CopyRenderTargetToTextureEx( m_StunTexture, 0, &srcRect, NULL );
+
+ // Restore our state
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->PopMatrix();
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->PopMatrix();
+}
+
+// ================================================================================================================
+//
+// Ep 1. Intro blur
+//
+// ================================================================================================================
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEP1IntroEffect::Init( void )
+{
+ m_flDuration = 0.0f;
+ m_flFinishTime = 0.0f;
+ m_bUpdateView = true;
+ m_bFadeOut = false;
+
+ KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
+ pVMTKeyValues->SetString( "$basetexture", STUN_TEXTURE );
+ m_EffectMaterial.Init( "__ep1introeffect", TEXTURE_GROUP_CLIENT_EFFECTS, pVMTKeyValues );
+ m_StunTexture.Init( STUN_TEXTURE, TEXTURE_GROUP_CLIENT_EFFECTS );
+}
+
+void CEP1IntroEffect::Shutdown( void )
+{
+ m_EffectMaterial.Shutdown();
+ m_StunTexture.Shutdown();
+}
+
+
+//------------------------------------------------------------------------------
+// Purpose: Pick up changes in our parameters
+//------------------------------------------------------------------------------
+void CEP1IntroEffect::SetParameters( KeyValues *params )
+{
+ if( params->FindKey( "duration" ) )
+ {
+ m_flDuration = params->GetFloat( "duration" );
+ m_flFinishTime = gpGlobals->curtime + m_flDuration;
+ }
+
+ if( params->FindKey( "fadeout" ) )
+ {
+ m_bFadeOut = ( params->GetInt( "fadeout" ) == 1 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the alpha value depending on various factors and time
+//-----------------------------------------------------------------------------
+inline unsigned char CEP1IntroEffect::GetFadeAlpha( void )
+{
+ // Find our percentage between fully "on" and "off" in the pulse range
+ float flEffectPerc = ( m_flDuration == 0.0f ) ? 0.0f : ( m_flFinishTime - gpGlobals->curtime ) / m_flDuration;
+ flEffectPerc = clamp( flEffectPerc, 0.0f, 1.0f );
+
+ if ( m_bFadeOut )
+ {
+ // HDR requires us to be more subtle, or we get uber-brightening
+ if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE )
+ return (unsigned char) clamp( 50.0f * flEffectPerc, 0.0f, 50.0f );
+
+ // Non-HDR
+ return (unsigned char) clamp( 64.0f * flEffectPerc, 0.0f, 64.0f );
+ }
+ else
+ {
+ // HDR requires us to be more subtle, or we get uber-brightening
+ if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE )
+ return (unsigned char) clamp( 64.0f * flEffectPerc, 50.0f, 64.0f );
+
+ // Non-HDR
+ return (unsigned char) clamp( 128.0f * flEffectPerc, 64.0f, 128.0f );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Render the effect
+//-----------------------------------------------------------------------------
+void CEP1IntroEffect::Render( int x, int y, int w, int h )
+{
+ if ( ( m_flFinishTime == 0 ) || ( IsEnabled() == false ) )
+ return;
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ // Set ourselves to the proper rendermode
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ // Draw the texture if we're using it
+ if ( m_bUpdateView )
+ {
+ // Save off this pass
+ Rect_t srcRect;
+ srcRect.x = x;
+ srcRect.y = y;
+ srcRect.width = w;
+ srcRect.height = h;
+ pRenderContext->CopyRenderTargetToTextureEx( m_StunTexture, 0, &srcRect, NULL );
+ m_bUpdateView = false;
+ }
+
+ byte overlaycolor[4] = { 255, 255, 255, 0 };
+
+ // Get our fade value depending on our fade duration
+ overlaycolor[3] = GetFadeAlpha();
+ if ( g_pMaterialSystemHardwareConfig->UsesSRGBCorrectBlending() )
+ {
+ // For DX10 cards, alpha blending happens in linear space, so try to adjust by hacking alpha to 50%
+ overlaycolor[3] *= 0.7f;
+ }
+
+ // Disable overself if we're done fading out
+ if ( m_bFadeOut && overlaycolor[3] == 0 )
+ {
+ // Takes effect next frame (we don't want to hose our matrix stacks here)
+ g_pScreenSpaceEffects->DisableScreenSpaceEffect( "episodic_intro" );
+ m_bUpdateView = true;
+ }
+
+ // Calculate some wavey noise to jitter the view by
+ float vX = 2.0f * -fabs( cosf( gpGlobals->curtime ) * cosf( gpGlobals->curtime * 6.0 ) );
+ float vY = 2.0f * cosf( gpGlobals->curtime ) * cosf( gpGlobals->curtime * 5.0 );
+
+ // Scale percentage
+ float flScalePerc = 0.02f + ( 0.01f * cosf( gpGlobals->curtime * 2.0f ) * cosf( gpGlobals->curtime * 0.5f ) );
+
+ // Scaled offsets for the UVs (as texels)
+ float flUOffset = ( m_StunTexture->GetActualWidth() - 1 ) * flScalePerc * 0.5f;
+ float flVOffset = ( m_StunTexture->GetActualHeight() - 1 ) * flScalePerc * 0.5f;
+
+ // New UVs with scaling offsets
+ float flU1 = flUOffset;
+ float flU2 = ( m_StunTexture->GetActualWidth() - 1 ) - flUOffset;
+ float flV1 = flVOffset;
+ float flV2 = ( m_StunTexture->GetActualHeight() - 1 ) - flVOffset;
+
+ // Draw the "zoomed" overlay
+ pRenderContext->DrawScreenSpaceRectangle( m_EffectMaterial, vX, vY, w, h,
+ flU1, flV1,
+ flU2, flV2,
+ m_StunTexture->GetActualWidth(), m_StunTexture->GetActualHeight() );
+
+ render->ViewDrawFade( overlaycolor, m_EffectMaterial );
+
+ // Save off this pass
+ Rect_t srcRect;
+ srcRect.x = x;
+ srcRect.y = y;
+ srcRect.width = w;
+ srcRect.height = h;
+ pRenderContext->CopyRenderTargetToTextureEx( m_StunTexture, 0, &srcRect, NULL );
+
+ // Restore our state
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->PopMatrix();
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->PopMatrix();
+}
+
+// ================================================================================================================
+//
+// Ep 2. Groggy-player view
+//
+// ================================================================================================================
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEP2StunEffect::Init( void )
+{
+ m_flDuration = 0.0f;
+ m_flFinishTime = 0.0f;
+ m_bUpdateView = true;
+ m_bFadeOut = false;
+
+ KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
+ pVMTKeyValues->SetString( "$basetexture", STUN_TEXTURE );
+ m_EffectMaterial.Init( "__ep2stuneffect", TEXTURE_GROUP_CLIENT_EFFECTS, pVMTKeyValues );
+ m_StunTexture.Init( STUN_TEXTURE, TEXTURE_GROUP_CLIENT_EFFECTS );
+}
+
+void CEP2StunEffect::Shutdown( void )
+{
+ m_EffectMaterial.Shutdown();
+ m_StunTexture.Shutdown();
+}
+
+//------------------------------------------------------------------------------
+// Purpose: Pick up changes in our parameters
+//------------------------------------------------------------------------------
+void CEP2StunEffect::SetParameters( KeyValues *params )
+{
+ if( params->FindKey( "duration" ) )
+ {
+ m_flDuration = params->GetFloat( "duration" );
+ m_flFinishTime = gpGlobals->curtime + m_flDuration;
+ }
+
+ if( params->FindKey( "fadeout" ) )
+ {
+ m_bFadeOut = ( params->GetInt( "fadeout" ) == 1 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the alpha value depending on various factors and time
+//-----------------------------------------------------------------------------
+inline unsigned char CEP2StunEffect::GetFadeAlpha( void )
+{
+ // Find our percentage between fully "on" and "off" in the pulse range
+ float flEffectPerc = ( m_flDuration == 0.0f ) ? 0.0f : ( m_flFinishTime - gpGlobals->curtime ) / m_flDuration;
+ flEffectPerc = clamp( flEffectPerc, 0.0f, 1.0f );
+
+ if ( m_bFadeOut )
+ {
+ // HDR requires us to be more subtle, or we get uber-brightening
+ if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE )
+ return (unsigned char) clamp( 50.0f * flEffectPerc, 0.0f, 50.0f );
+
+ // Non-HDR
+ return (unsigned char) clamp( 64.0f * flEffectPerc, 0.0f, 64.0f );
+ }
+ else
+ {
+ // HDR requires us to be more subtle, or we get uber-brightening
+ if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE )
+ return (unsigned char) clamp( 164.0f * flEffectPerc, 128.0f, 164.0f );
+
+ // Non-HDR
+ return (unsigned char) clamp( 164.0f * flEffectPerc, 128.0f, 164.0f );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Render the effect
+//-----------------------------------------------------------------------------
+void CEP2StunEffect::Render( int x, int y, int w, int h )
+{
+ if ( ( m_flFinishTime == 0 ) || ( IsEnabled() == false ) )
+ return;
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ // Set ourselves to the proper rendermode
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ if ( m_bUpdateView )
+ {
+ // Save off this pass
+ Rect_t srcRect;
+ srcRect.x = x;
+ srcRect.y = y;
+ srcRect.width = w;
+ srcRect.height = h;
+ pRenderContext->CopyRenderTargetToTextureEx( m_StunTexture, 0, &srcRect, NULL );
+ m_bUpdateView = false;
+ }
+
+ byte overlaycolor[4] = { 255, 255, 255, 0 };
+
+ // Get our fade value depending on our fade duration
+ overlaycolor[3] = GetFadeAlpha();
+
+ // Disable overself if we're done fading out
+ if ( m_bFadeOut && overlaycolor[3] == 0 )
+ {
+ // Takes effect next frame (we don't want to hose our matrix stacks here)
+ g_pScreenSpaceEffects->DisableScreenSpaceEffect( "ep2_groggy" );
+ m_bUpdateView = true;
+ }
+
+ // Calculate some wavey noise to jitter the view by
+ float vX = 4.0f * cosf( gpGlobals->curtime ) * cosf( gpGlobals->curtime * 6.0 );
+ float vY = 2.0f * cosf( gpGlobals->curtime ) * cosf( gpGlobals->curtime * 5.0 );
+
+ float flBaseScale = 0.2f + 0.005f * sinf( gpGlobals->curtime * 4.0f );
+
+ // Scale percentage
+ float flScalePerc = flBaseScale + ( 0.01f * cosf( gpGlobals->curtime * 2.0f ) * cosf( gpGlobals->curtime * 0.5f ) );
+
+ // Scaled offsets for the UVs (as texels)
+ float flUOffset = ( m_StunTexture->GetActualWidth() - 1 ) * flScalePerc * 0.5f;
+ float flVOffset = ( m_StunTexture->GetActualHeight() - 1 ) * flScalePerc * 0.5f;
+
+ // New UVs with scaling offsets
+ float flU1 = flUOffset;
+ float flU2 = ( m_StunTexture->GetActualWidth() - 1 ) - flUOffset;
+ float flV1 = flVOffset;
+ float flV2 = ( m_StunTexture->GetActualHeight() - 1 ) - flVOffset;
+
+ // Draw the "zoomed" overlay
+ pRenderContext->DrawScreenSpaceRectangle( m_EffectMaterial, vX, vY, w, h,
+ flU1, flV1,
+ flU2, flV2,
+ m_StunTexture->GetActualWidth(), m_StunTexture->GetActualHeight() );
+
+ render->ViewDrawFade( overlaycolor, m_EffectMaterial );
+
+ // Save off this pass
+ Rect_t srcRect;
+ srcRect.x = x;
+ srcRect.y = y;
+ srcRect.width = w;
+ srcRect.height = h;
+ pRenderContext->CopyRenderTargetToTextureEx( m_StunTexture, 0, &srcRect, NULL );
+
+ // Restore our state
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->PopMatrix();
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->PopMatrix();
+}
diff --git a/mp/src/game/client/episodic/episodic_screenspaceeffects.h b/mp/src/game/client/episodic/episodic_screenspaceeffects.h
new file mode 100644
index 00000000..0ed6687e
--- /dev/null
+++ b/mp/src/game/client/episodic/episodic_screenspaceeffects.h
@@ -0,0 +1,119 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef EPISODIC_SCREENSPACEEFFECTS_H
+#define EPISODIC_SCREENSPACEEFFECTS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "ScreenSpaceEffects.h"
+
+class CStunEffect : public IScreenSpaceEffect
+{
+public:
+ CStunEffect( void ) :
+ m_flDuration( 0.0f ),
+ m_flFinishTime( 0.0f ),
+ m_bUpdateView( true ) {}
+
+ virtual void Init( void );
+ virtual void Shutdown( void );
+ virtual void SetParameters( KeyValues *params );
+ virtual void Enable( bool bEnable ) {};
+ virtual bool IsEnabled( ) { return true; }
+
+ virtual void Render( int x, int y, int w, int h );
+
+private:
+ CTextureReference m_StunTexture;
+ CMaterialReference m_EffectMaterial;
+ float m_flDuration;
+ float m_flFinishTime;
+ bool m_bUpdateView;
+};
+
+ADD_SCREENSPACE_EFFECT( CStunEffect, episodic_stun );
+
+//
+// EP1 Intro Blur
+//
+
+class CEP1IntroEffect : public IScreenSpaceEffect
+{
+public:
+ CEP1IntroEffect( void ) :
+ m_flDuration( 0.0f ),
+ m_flFinishTime( 0.0f ),
+ m_bUpdateView( true ),
+ m_bEnabled( false ),
+ m_bFadeOut( false ) {}
+
+ virtual void Init( void );
+ virtual void Shutdown( void );
+ virtual void SetParameters( KeyValues *params );
+ virtual void Enable( bool bEnable ) { m_bEnabled = bEnable; }
+ virtual bool IsEnabled( ) { return m_bEnabled; }
+
+ virtual void Render( int x, int y, int w, int h );
+
+private:
+
+ inline unsigned char GetFadeAlpha( void );
+
+ CTextureReference m_StunTexture;
+ CMaterialReference m_EffectMaterial;
+ float m_flDuration;
+ float m_flFinishTime;
+ bool m_bUpdateView;
+ bool m_bEnabled;
+ bool m_bFadeOut;
+};
+
+ADD_SCREENSPACE_EFFECT( CEP1IntroEffect, episodic_intro );
+
+//
+// EP2 Player Stunned Effect
+//
+
+//
+// EP1 Intro Blur
+//
+
+class CEP2StunEffect : public IScreenSpaceEffect
+{
+public:
+ CEP2StunEffect( void ) :
+ m_flDuration( 0.0f ),
+ m_flFinishTime( 0.0f ),
+ m_bUpdateView( true ),
+ m_bEnabled( false ),
+ m_bFadeOut( false ) {}
+
+ virtual void Init( void );
+ virtual void Shutdown( void );
+ virtual void SetParameters( KeyValues *params );
+ virtual void Enable( bool bEnable ) { m_bEnabled = bEnable; }
+ virtual bool IsEnabled( ) { return m_bEnabled; }
+
+ virtual void Render( int x, int y, int w, int h );
+
+private:
+
+ inline unsigned char GetFadeAlpha( void );
+
+ CTextureReference m_StunTexture;
+ CMaterialReference m_EffectMaterial;
+ float m_flDuration;
+ float m_flFinishTime;
+ bool m_bUpdateView;
+ bool m_bEnabled;
+ bool m_bFadeOut;
+};
+
+ADD_SCREENSPACE_EFFECT( CEP2StunEffect, ep2_groggy );
+
+#endif // EPISODIC_SCREENSPACEEFFECTS_H
diff --git a/mp/src/game/client/episodic/flesh_internal_material_proxy.cpp b/mp/src/game/client/episodic/flesh_internal_material_proxy.cpp
new file mode 100644
index 00000000..8407ee44
--- /dev/null
+++ b/mp/src/game/client/episodic/flesh_internal_material_proxy.cpp
@@ -0,0 +1,225 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// Purpose:
+//
+// $NoKeywords: $
+//=====================================================================================//
+#include "cbase.h"
+#include "proxyentity.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imaterialvar.h"
+#include "debugoverlay_shared.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+class C_FleshEffectTarget;
+void AddFleshProxyTarget( C_FleshEffectTarget *pTarget );
+void RemoveFleshProxy( C_FleshEffectTarget *pTarget );
+
+//=============================================================================
+//
+// Flesh effect target (used for orchestrating the "Invisible Alyx" moment
+//
+//=============================================================================
+
+class C_FleshEffectTarget : public C_BaseEntity
+{
+ DECLARE_CLASS( C_FleshEffectTarget, C_BaseEntity );
+
+public:
+ float GetRadius( void )
+ {
+ if ( m_flScaleTime <= 0.0f )
+ return m_flRadius;
+
+ float dt = ( gpGlobals->curtime - m_flScaleStartTime );
+ if ( dt >= m_flScaleTime )
+ return m_flRadius;
+
+ return SimpleSplineRemapVal( ( dt / m_flScaleTime ), 0.0f, 1.0f, m_flStartRadius, m_flRadius );
+ }
+
+ virtual void Release( void )
+ {
+ // Remove us from the list of targets
+ RemoveFleshProxy( this );
+ }
+
+ virtual void OnDataChanged( DataUpdateType_t updateType )
+ {
+ BaseClass::OnDataChanged( updateType );
+
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ // Add us to the list of flesh proxy targets
+ AddFleshProxyTarget( this );
+ }
+ }
+
+ float m_flRadius;
+ float m_flStartRadius;
+ float m_flScaleStartTime;
+ float m_flScaleTime;
+
+ DECLARE_CLIENTCLASS();
+};
+
+void RecvProxy_FleshEffect_Radius( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_FleshEffectTarget *pTarget = (C_FleshEffectTarget *) pStruct;
+ float flRadius = pData->m_Value.m_Float;
+
+ //If changed, update our internal information
+ if ( pTarget->m_flRadius != flRadius )
+ {
+ pTarget->m_flStartRadius = pTarget->m_flRadius;
+ pTarget->m_flScaleStartTime = gpGlobals->curtime;
+ }
+
+ pTarget->m_flRadius = flRadius;
+}
+
+IMPLEMENT_CLIENTCLASS_DT( C_FleshEffectTarget, DT_FleshEffectTarget, CFleshEffectTarget )
+ RecvPropFloat( RECVINFO(m_flRadius), 0, RecvProxy_FleshEffect_Radius ),
+ RecvPropFloat( RECVINFO(m_flScaleTime) ),
+END_RECV_TABLE()
+
+CUtlVector< C_FleshEffectTarget * > g_FleshProxyTargets;
+
+void AddFleshProxyTarget( C_FleshEffectTarget *pTarget )
+{
+ // Take it!
+ g_FleshProxyTargets.AddToTail( pTarget );
+}
+
+void RemoveFleshProxy( C_FleshEffectTarget *pTarget )
+{
+ int nIndex = g_FleshProxyTargets.Find( pTarget );
+ if ( nIndex != g_FleshProxyTargets.InvalidIndex() )
+ {
+ g_FleshProxyTargets.Remove( nIndex );
+ }
+}
+
+// $sineVar : name of variable that controls the FleshInterior level (float)
+class CFleshInteriorMaterialProxy : public CEntityMaterialProxy
+{
+public:
+ CFleshInteriorMaterialProxy();
+ virtual ~CFleshInteriorMaterialProxy();
+ virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
+ virtual void OnBind( C_BaseEntity *pEntity );
+ virtual IMaterial *GetMaterial();
+
+private:
+ IMaterialVar *m_pMaterialParamFleshEffectCenterRadius1;
+ IMaterialVar *m_pMaterialParamFleshEffectCenterRadius2;
+ IMaterialVar *m_pMaterialParamFleshEffectCenterRadius3;
+ IMaterialVar *m_pMaterialParamFleshEffectCenterRadius4;
+ IMaterialVar *m_pMaterialParamFleshGlobalOpacity;
+ IMaterialVar *m_pMaterialParamFleshSubsurfaceTint;
+};
+
+CFleshInteriorMaterialProxy::CFleshInteriorMaterialProxy()
+{
+ m_pMaterialParamFleshEffectCenterRadius1 = NULL;
+ m_pMaterialParamFleshEffectCenterRadius2 = NULL;
+ m_pMaterialParamFleshEffectCenterRadius3 = NULL;
+ m_pMaterialParamFleshEffectCenterRadius4 = NULL;
+ m_pMaterialParamFleshGlobalOpacity = NULL;
+ m_pMaterialParamFleshSubsurfaceTint = NULL;
+}
+
+CFleshInteriorMaterialProxy::~CFleshInteriorMaterialProxy()
+{
+ // Do nothing
+}
+
+bool CFleshInteriorMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
+{
+ bool bFoundVar = false;
+
+ m_pMaterialParamFleshEffectCenterRadius1 = pMaterial->FindVar( "$FleshEffectCenterRadius1", &bFoundVar, false );
+ if ( bFoundVar == false)
+ return false;
+
+ m_pMaterialParamFleshEffectCenterRadius2 = pMaterial->FindVar( "$FleshEffectCenterRadius2", &bFoundVar, false );
+ if ( bFoundVar == false)
+ return false;
+
+ m_pMaterialParamFleshEffectCenterRadius3 = pMaterial->FindVar( "$FleshEffectCenterRadius3", &bFoundVar, false );
+ if ( bFoundVar == false)
+ return false;
+
+ m_pMaterialParamFleshEffectCenterRadius4 = pMaterial->FindVar( "$FleshEffectCenterRadius4", &bFoundVar, false );
+ if ( bFoundVar == false)
+ return false;
+
+ m_pMaterialParamFleshGlobalOpacity = pMaterial->FindVar( "$FleshGlobalOpacity", &bFoundVar, false );
+ if ( bFoundVar == false)
+ return false;
+
+ m_pMaterialParamFleshSubsurfaceTint = pMaterial->FindVar( "$FleshSubsurfaceTint", &bFoundVar, false );
+ if ( bFoundVar == false)
+ return false;
+
+ return true;
+}
+
+void CFleshInteriorMaterialProxy::OnBind( C_BaseEntity *pEnt )
+{
+ IMaterialVar *pParams[] =
+ {
+ m_pMaterialParamFleshEffectCenterRadius1,
+ m_pMaterialParamFleshEffectCenterRadius2,
+ m_pMaterialParamFleshEffectCenterRadius3,
+ m_pMaterialParamFleshEffectCenterRadius4
+ };
+
+ float vEffectCenterRadius[4];
+ for ( int i = 0; i < ARRAYSIZE( pParams ); i++ )
+ {
+ if ( i < g_FleshProxyTargets.Count() )
+ {
+ // Setup the target
+ if ( g_FleshProxyTargets[i]->IsAbsQueriesValid() == false )
+ continue;
+
+ Vector vecAbsOrigin = g_FleshProxyTargets[i]->GetAbsOrigin();
+ vEffectCenterRadius[0] = vecAbsOrigin.x;
+ vEffectCenterRadius[1] = vecAbsOrigin.y;
+ vEffectCenterRadius[2] = vecAbsOrigin.z;
+ vEffectCenterRadius[3] = g_FleshProxyTargets[i]->GetRadius();
+ }
+ else
+ {
+ // Clear the target
+ vEffectCenterRadius[0] = vEffectCenterRadius[1] = vEffectCenterRadius[2] = vEffectCenterRadius[3] = 0.0f;
+ }
+
+ // Set the value either way
+ pParams[i]->SetVecValue( vEffectCenterRadius, 4 );
+ }
+
+ // Subsurface texture. NOTE: This texture bleeds through the color of the flesh texture so expect
+ // to have to set this brighter than white to really see the subsurface texture glow through.
+ if ( m_pMaterialParamFleshSubsurfaceTint != NULL )
+ {
+ float vSubsurfaceTintColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+
+ // !!! Test code. REPLACE ME!
+ // vSubsurfaceTintColor[0] = vSubsurfaceTintColor[1] = vSubsurfaceTintColor[2] = sinf( gpGlobals->curtime * 3.0f ) + 1.0f; // * 0.5f + 0.5f;
+
+ m_pMaterialParamFleshSubsurfaceTint->SetVecValue( vSubsurfaceTintColor, 4 );
+ }
+}
+
+IMaterial *CFleshInteriorMaterialProxy::GetMaterial()
+{
+ if ( m_pMaterialParamFleshEffectCenterRadius1 == NULL)
+ return NULL;
+
+ return m_pMaterialParamFleshEffectCenterRadius1->GetOwningMaterial();
+}
+
+EXPOSE_INTERFACE( CFleshInteriorMaterialProxy, IMaterialProxy, "FleshInterior" IMATERIAL_PROXY_INTERFACE_VERSION );