You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
600 lines
16 KiB
600 lines
16 KiB
//========= 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 );
|
|
|