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.
542 lines
12 KiB
542 lines
12 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "baseanimating.h" |
|
#include "Sprite.h" |
|
#include "SpriteTrail.h" |
|
#include <ctype.h> |
|
#include "animation.h" |
|
#include "eventlist.h" |
|
#include "npcevent.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
enum EffectType |
|
{ |
|
EFFECT_TYPE_TRAIL = 1, |
|
EFFECT_TYPE_SPRITE |
|
}; |
|
|
|
|
|
bool g_bUnget = false; |
|
unsigned char *buffer; |
|
char name[ 256 ]; |
|
const char *currenttoken; |
|
int tokencount; |
|
char token[ 1204 ]; |
|
|
|
class CEffectScriptElement |
|
{ |
|
public: |
|
|
|
CEffectScriptElement(); |
|
|
|
char m_szEffectName[128]; |
|
CHandle<CSpriteTrail> m_pTrail; |
|
CHandle<CSprite> m_pSprite; |
|
int m_iType; |
|
int m_iRenderType; |
|
|
|
int m_iR; |
|
int m_iG; |
|
int m_iB; |
|
int m_iA; |
|
|
|
char m_szAttachment[128]; |
|
char m_szMaterial[128]; |
|
|
|
float m_flScale; |
|
float m_flFadeTime; |
|
float m_flTextureRes; |
|
|
|
bool m_bStopFollowOnKill; |
|
|
|
bool IsActive( void ) { return m_bActive; } |
|
void Activate( void ) { m_bActive = true; } |
|
void Deactivate( void ) { m_bActive = false; } |
|
private: |
|
|
|
bool m_bActive; |
|
}; |
|
|
|
CEffectScriptElement::CEffectScriptElement() |
|
{ |
|
m_pTrail = NULL; |
|
m_pSprite = NULL; |
|
m_iType = 0; |
|
|
|
Deactivate(); |
|
m_iRenderType = kRenderTransAdd; |
|
|
|
m_iR = 255; |
|
m_iG = 0; |
|
m_iB = 0; |
|
m_iA = 255; |
|
|
|
m_flScale = 1.0f; |
|
m_flFadeTime = 1.0f; |
|
m_flTextureRes = -1.0f; |
|
m_bStopFollowOnKill = false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// An entity which emits other entities at points |
|
//----------------------------------------------------------------------------- |
|
class CEnvEffectsScript : public CBaseAnimating |
|
{ |
|
public: |
|
DECLARE_CLASS( CEnvEffectsScript, CBaseAnimating ); |
|
DECLARE_DATADESC(); |
|
|
|
virtual void Precache(); |
|
virtual void Spawn(); |
|
virtual int UpdateTransmitState(); |
|
|
|
void InputSetSequence( inputdata_t &inputdata ); |
|
void ParseScriptFile( void ); |
|
void LoadFromBuffer( const char *scriptfile, const char *buffer ); |
|
|
|
virtual void Think( void ); |
|
|
|
void ParseNewEffect( void ); |
|
|
|
const char *GetScriptFile( void ) |
|
{ |
|
return STRING( m_iszScriptName ); |
|
} |
|
|
|
void HandleAnimEvent ( animevent_t *pEvent ); |
|
void TrailEffectEvent( CEffectScriptElement *pEffect ); |
|
void SpriteEffectEvent( CEffectScriptElement *pEffect ); |
|
|
|
CEffectScriptElement *GetScriptElementByName( const char *pName ); |
|
|
|
private: |
|
|
|
string_t m_iszScriptName; |
|
|
|
CUtlVector< CEffectScriptElement > m_ScriptElements; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool IsRootCommand( void ) |
|
{ |
|
if ( !Q_stricmp( token, "effect" ) ) |
|
return true; |
|
|
|
return false; |
|
} |
|
}; |
|
|
|
inline bool ParseToken( void ) |
|
{ |
|
if ( g_bUnget ) |
|
{ |
|
g_bUnget = false; |
|
return true; |
|
} |
|
|
|
currenttoken = engine->ParseFile( currenttoken, token, sizeof( token ) ); |
|
tokencount++; |
|
return currenttoken != NULL ? true : false; |
|
} |
|
|
|
inline void Unget() |
|
{ |
|
g_bUnget = true; |
|
} |
|
|
|
inline bool TokenWaiting( void ) |
|
{ |
|
|
|
const char *p = currenttoken; |
|
while ( *p && *p!='\n') |
|
{ |
|
// Special handler for // comment blocks |
|
if ( *p == '/' && *(p+1) == '/' ) |
|
return false; |
|
|
|
if ( !V_isspace( *p ) || V_isalnum( *p ) ) |
|
return true; |
|
|
|
p++; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Save/load |
|
//----------------------------------------------------------------------------- |
|
BEGIN_DATADESC( CEnvEffectsScript ) |
|
// Inputs |
|
DEFINE_INPUTFUNC( FIELD_STRING, "SetSequence", InputSetSequence ), |
|
DEFINE_KEYFIELD( m_iszScriptName, FIELD_STRING, "scriptfile" ), |
|
// DEFINE_FIELD( m_ScriptElements, CUtlVector < CEffectScriptElement > ), |
|
|
|
DEFINE_FUNCTION( Think ), |
|
|
|
END_DATADESC() |
|
|
|
LINK_ENTITY_TO_CLASS( env_effectscript, CEnvEffectsScript ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Should we transmit it to the client? |
|
//----------------------------------------------------------------------------- |
|
int CEnvEffectsScript::UpdateTransmitState() |
|
{ |
|
return SetTransmitState( FL_EDICT_ALWAYS ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Precache |
|
//----------------------------------------------------------------------------- |
|
void CEnvEffectsScript::Precache() |
|
{ |
|
BaseClass::Precache(); |
|
PrecacheModel( STRING( GetModelName() ) ); |
|
|
|
if ( m_iszScriptName != NULL_STRING ) |
|
ParseScriptFile(); |
|
else |
|
Warning( "CEnvEffectsScript with no script!\n" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Spawn |
|
//----------------------------------------------------------------------------- |
|
void CEnvEffectsScript::Spawn() |
|
{ |
|
Precache(); |
|
BaseClass::Spawn(); |
|
|
|
// We need a model for its animation sequences even though we don't render it |
|
SetModel( STRING( GetModelName() ) ); |
|
|
|
AddEffects( EF_NODRAW ); |
|
|
|
SetThink( &CEnvEffectsScript::Think ); |
|
SetNextThink( gpGlobals->curtime + 0.1f ); |
|
} |
|
|
|
void CEnvEffectsScript::Think( void ) |
|
{ |
|
StudioFrameAdvance(); |
|
DispatchAnimEvents( this ); |
|
|
|
SetNextThink( gpGlobals->curtime + 0.1f ); |
|
} |
|
|
|
void CEnvEffectsScript::TrailEffectEvent( CEffectScriptElement *pEffect ) |
|
{ |
|
if ( pEffect->IsActive() == false ) |
|
{ |
|
//Only one type of this effect active at a time. |
|
if ( pEffect->m_pTrail == NULL ) |
|
{ |
|
pEffect->m_pTrail = CSpriteTrail::SpriteTrailCreate( pEffect->m_szMaterial, GetAbsOrigin(), true ); |
|
pEffect->m_pTrail->FollowEntity( this ); |
|
pEffect->m_pTrail->SetTransparency( pEffect->m_iRenderType, pEffect->m_iR, pEffect->m_iG, pEffect->m_iB, pEffect->m_iA, kRenderFxNone ); |
|
pEffect->m_pTrail->SetStartWidth( pEffect->m_flScale ); |
|
if ( pEffect->m_flTextureRes < 0.0f ) |
|
{ |
|
pEffect->m_pTrail->SetTextureResolution( 1.0f / ( 16.0f * pEffect->m_flScale ) ); |
|
} |
|
else |
|
{ |
|
pEffect->m_pTrail->SetTextureResolution( pEffect->m_flTextureRes ); |
|
} |
|
pEffect->m_pTrail->SetLifeTime( pEffect->m_flFadeTime ); |
|
pEffect->m_pTrail->TurnOn(); |
|
pEffect->m_pTrail->SetAttachment( this, LookupAttachment( pEffect->m_szAttachment ) ); |
|
|
|
pEffect->Activate(); |
|
} |
|
} |
|
} |
|
|
|
void CEnvEffectsScript::SpriteEffectEvent( CEffectScriptElement *pEffect ) |
|
{ |
|
if ( pEffect->IsActive() == false ) |
|
{ |
|
//Only one type of this effect active at a time. |
|
if ( pEffect->m_pSprite == NULL ) |
|
{ |
|
pEffect->m_pSprite = CSprite::SpriteCreate( pEffect->m_szMaterial, GetAbsOrigin(), true ); |
|
pEffect->m_pSprite->FollowEntity( this ); |
|
pEffect->m_pSprite->SetTransparency( pEffect->m_iRenderType, pEffect->m_iR, pEffect->m_iG, pEffect->m_iB, pEffect->m_iA, kRenderFxNone ); |
|
pEffect->m_pSprite->SetScale( pEffect->m_flScale ); |
|
pEffect->m_pSprite->TurnOn(); |
|
pEffect->m_pSprite->SetAttachment( this, LookupAttachment( pEffect->m_szAttachment ) ); |
|
|
|
pEffect->Activate(); |
|
} |
|
} |
|
} |
|
|
|
void CEnvEffectsScript::HandleAnimEvent ( animevent_t *pEvent ) |
|
{ |
|
if ( pEvent->event == AE_START_SCRIPTED_EFFECT ) |
|
{ |
|
CEffectScriptElement *pCurrent = GetScriptElementByName( pEvent->options ); |
|
|
|
if ( pCurrent ) |
|
{ |
|
if ( pCurrent->m_iType == EFFECT_TYPE_TRAIL ) |
|
TrailEffectEvent( pCurrent ); |
|
else if ( pCurrent->m_iType == EFFECT_TYPE_SPRITE ) |
|
SpriteEffectEvent( pCurrent ); |
|
} |
|
|
|
return; |
|
} |
|
|
|
if ( pEvent->event == AE_STOP_SCRIPTED_EFFECT ) |
|
{ |
|
CEffectScriptElement *pCurrent = GetScriptElementByName( pEvent->options ); |
|
|
|
if ( pCurrent && pCurrent->IsActive() ) |
|
{ |
|
pCurrent->Deactivate(); |
|
|
|
if ( pCurrent->m_iType == EFFECT_TYPE_TRAIL ) |
|
{ |
|
if ( pCurrent->m_bStopFollowOnKill == true ) |
|
{ |
|
Vector vOrigin; |
|
GetAttachment( pCurrent->m_pTrail->m_nAttachment, vOrigin ); |
|
|
|
pCurrent->m_pTrail->StopFollowingEntity(); |
|
|
|
pCurrent->m_pTrail->m_hAttachedToEntity = NULL; |
|
pCurrent->m_pTrail->m_nAttachment = 0; |
|
|
|
pCurrent->m_pTrail->SetAbsOrigin( vOrigin); |
|
} |
|
|
|
pCurrent->m_pTrail->FadeAndDie( pCurrent->m_flFadeTime ); |
|
pCurrent->m_pTrail = NULL; |
|
} |
|
|
|
else if ( pCurrent->m_iType == EFFECT_TYPE_SPRITE ) |
|
{ |
|
if ( pCurrent->m_bStopFollowOnKill == true ) |
|
{ |
|
Vector vOrigin; |
|
GetAttachment( pCurrent->m_pSprite->m_nAttachment, vOrigin ); |
|
|
|
pCurrent->m_pSprite->StopFollowingEntity(); |
|
|
|
pCurrent->m_pSprite->m_hAttachedToEntity = NULL; |
|
pCurrent->m_pSprite->m_nAttachment = 0; |
|
|
|
pCurrent->m_pSprite->SetAbsOrigin( vOrigin); |
|
} |
|
|
|
pCurrent->m_pSprite->FadeAndDie( pCurrent->m_flFadeTime ); |
|
pCurrent->m_pSprite = NULL; |
|
} |
|
} |
|
return; |
|
} |
|
|
|
BaseClass::HandleAnimEvent( pEvent ); |
|
} |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Input that sets the sequence of the entity |
|
//----------------------------------------------------------------------------- |
|
void CEnvEffectsScript::InputSetSequence( inputdata_t &inputdata ) |
|
{ |
|
if ( inputdata.value.StringID() != NULL_STRING ) |
|
{ |
|
int nSequence = LookupSequence( STRING( inputdata.value.StringID() ) ); |
|
if ( nSequence != ACT_INVALID ) |
|
{ |
|
SetSequence( nSequence ); |
|
ResetSequenceInfo(); |
|
SetCycle( 0.0f ); |
|
m_flPlaybackRate = 1.0f; |
|
} |
|
} |
|
} |
|
|
|
void CEnvEffectsScript::ParseScriptFile( void ) |
|
{ |
|
int length = 0; |
|
m_ScriptElements.RemoveAll(); |
|
const char *pScriptName = GetScriptFile(); |
|
|
|
//Reset everything. |
|
g_bUnget = false; |
|
currenttoken = NULL; |
|
tokencount = 0; |
|
memset( token, 0, 1204 ); |
|
memset( name, 0, 256 ); |
|
|
|
|
|
unsigned char *buffer = (unsigned char *)UTIL_LoadFileForMe( pScriptName, &length ); |
|
if ( length <= 0 || !buffer ) |
|
{ |
|
DevMsg( 1, "CEnvEffectsScript: failed to load %s\n", pScriptName ); |
|
return; |
|
} |
|
|
|
currenttoken = (const char *)buffer; |
|
LoadFromBuffer( pScriptName, (const char *)buffer ); |
|
|
|
UTIL_FreeFile( buffer ); |
|
} |
|
|
|
void CEnvEffectsScript::LoadFromBuffer( const char *scriptfile, const char *buffer ) |
|
{ |
|
while ( 1 ) |
|
{ |
|
ParseToken(); |
|
|
|
if ( !token[0] ) |
|
{ |
|
break; |
|
} |
|
|
|
if ( !Q_stricmp( token, "effect" ) ) |
|
{ |
|
ParseNewEffect(); |
|
} |
|
else |
|
{ |
|
Warning( "CEnvEffectsScript: Unknown entry type '%s'\n", token ); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
void CEnvEffectsScript::ParseNewEffect( void ) |
|
{ |
|
//Add a new effect to the list. |
|
CEffectScriptElement NewElement; |
|
|
|
// Effect Group Name |
|
ParseToken(); |
|
Q_strncpy( NewElement.m_szEffectName, token, sizeof( NewElement.m_szEffectName ) ); |
|
|
|
while ( 1 ) |
|
{ |
|
ParseToken(); |
|
|
|
// Oops, part of next definition |
|
if( IsRootCommand() ) |
|
{ |
|
Unget(); |
|
break; |
|
} |
|
|
|
if ( !Q_stricmp( token, "{" ) ) |
|
{ |
|
while ( 1 ) |
|
{ |
|
ParseToken(); |
|
if ( !Q_stricmp( token, "}" ) ) |
|
break; |
|
|
|
if ( !Q_stricmp( token, "type" ) ) |
|
{ |
|
ParseToken(); |
|
|
|
if ( !Q_stricmp( token, "trail" ) ) |
|
NewElement.m_iType = EFFECT_TYPE_TRAIL; |
|
else if ( !Q_stricmp( token, "sprite" ) ) |
|
NewElement.m_iType = EFFECT_TYPE_SPRITE; |
|
|
|
continue; |
|
} |
|
|
|
if ( !Q_stricmp( token, "material" ) ) |
|
{ |
|
ParseToken(); |
|
Q_strncpy( NewElement.m_szMaterial, token, sizeof( NewElement.m_szMaterial ) ); |
|
PrecacheModel( NewElement.m_szMaterial ); |
|
|
|
continue; |
|
} |
|
|
|
if ( !Q_stricmp( token, "attachment" ) ) |
|
{ |
|
ParseToken(); |
|
Q_strncpy( NewElement.m_szAttachment, token, sizeof( NewElement.m_szAttachment ) ); |
|
|
|
continue; |
|
} |
|
|
|
if ( !Q_stricmp( token, "color" ) ) |
|
{ |
|
ParseToken(); |
|
sscanf( token, "%i %i %i %i", &NewElement.m_iR, &NewElement.m_iG, &NewElement.m_iB, &NewElement.m_iA ); |
|
|
|
continue; |
|
} |
|
|
|
if ( !Q_stricmp( token, "scale" ) ) |
|
{ |
|
ParseToken(); |
|
|
|
NewElement.m_flScale = atof( token ); |
|
continue; |
|
} |
|
|
|
if ( !Q_stricmp( token, "texturescale" ) ) |
|
{ |
|
ParseToken(); |
|
|
|
float flTextureScale = atof( token ); |
|
NewElement.m_flTextureRes = (flTextureScale > 0.0f) ? 1.0f / flTextureScale : 0.0f; |
|
continue; |
|
} |
|
|
|
if ( !Q_stricmp( token, "fadetime" ) ) |
|
{ |
|
ParseToken(); |
|
|
|
NewElement.m_flFadeTime = atof( token ); |
|
continue; |
|
} |
|
|
|
if ( !Q_stricmp( token, "stopfollowonkill" ) ) |
|
{ |
|
ParseToken(); |
|
|
|
NewElement.m_bStopFollowOnKill = !!atoi( token ); |
|
continue; |
|
} |
|
|
|
} |
|
break; |
|
} |
|
} |
|
|
|
m_ScriptElements.AddToTail( NewElement ); |
|
} |
|
|
|
CEffectScriptElement *CEnvEffectsScript::GetScriptElementByName( const char *pName ) |
|
{ |
|
for ( int i = 0; i < m_ScriptElements.Count(); i++ ) |
|
{ |
|
CEffectScriptElement *pCurrent = &m_ScriptElements.Element( i ); |
|
|
|
if ( pCurrent && !Q_stricmp( pCurrent->m_szEffectName, pName ) ) |
|
{ |
|
return pCurrent; |
|
} |
|
} |
|
|
|
return NULL; |
|
}
|
|
|