mirror of
https://github.com/alliedmodders/hl2sdk.git
synced 2024-12-23 01:59:43 +08:00
601 lines
16 KiB
C++
601 lines
16 KiB
C++
//========= 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 );
|