//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "baseanimating.h" #include "Sprite.h" #include "SpriteTrail.h" #include #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 m_pTrail; CHandle 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 ) || 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 ) { int nEvent = pEvent->Event(); if ( nEvent == 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 ( nEvent == 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; }