From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/game/server/tanktrain.cpp | 1070 +++++++++++++++++++------------------- 1 file changed, 535 insertions(+), 535 deletions(-) (limited to 'mp/src/game/server/tanktrain.cpp') diff --git a/mp/src/game/server/tanktrain.cpp b/mp/src/game/server/tanktrain.cpp index 2b359b06..62a5acd6 100644 --- a/mp/src/game/server/tanktrain.cpp +++ b/mp/src/game/server/tanktrain.cpp @@ -1,535 +1,535 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "cbase.h" -#include "trains.h" -#include "entitylist.h" -#include "soundenvelope.h" -#include "engine/IEngineSound.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern short g_sModelIndexFireball; -#define SPRITE_FIREBALL "sprites/zerogxplode.vmt" -#define SPRITE_SMOKE "sprites/steam1.vmt" - -void UTIL_RemoveHierarchy( CBaseEntity *pDead ) -{ - if ( !pDead ) - return; - - if ( pDead->edict() ) - { - CBaseEntity *pChild = pDead->FirstMoveChild(); - while ( pChild ) - { - CBaseEntity *pEntity = pChild; - pChild = pChild->NextMovePeer(); - - UTIL_RemoveHierarchy( pEntity ); - } - } - UTIL_Remove( pDead ); -} - -class CFuncTankTrain : public CFuncTrackTrain -{ -public: - DECLARE_CLASS( CFuncTankTrain, CFuncTrackTrain ); - - void Spawn( void ); - - // Filter out damage messages that don't contain blast damage (impervious to other forms of attack) - int OnTakeDamage( const CTakeDamageInfo &info ); - void Event_Killed( const CTakeDamageInfo &info ); - void Blocked( CBaseEntity *pOther ) - { - // FIxme, set speed to zero? - } - DECLARE_DATADESC(); - -private: - - COutputEvent m_OnDeath; -}; - -LINK_ENTITY_TO_CLASS( func_tanktrain, CFuncTankTrain ); - -BEGIN_DATADESC( CFuncTankTrain ) - - // Outputs - DEFINE_OUTPUT(m_OnDeath, "OnDeath"), - -END_DATADESC() - - -void CFuncTankTrain::Spawn( void ) -{ - m_takedamage = true; - BaseClass::Spawn(); -} - -// Filter out damage messages that don't contain blast damage (impervious to other forms of attack) -int CFuncTankTrain::OnTakeDamage( const CTakeDamageInfo &info ) -{ - if ( ! (info.GetDamageType() & DMG_BLAST) ) - return 0; - - return BaseClass::OnTakeDamage( info ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Called when the train is killed. -// Input : pInflictor - What killed us. -// pAttacker - Who killed us. -// flDamage - The damage that the killing blow inflicted. -// bitsDamageType - Bitfield of damage types that were inflicted. -//----------------------------------------------------------------------------- -void CFuncTankTrain::Event_Killed( const CTakeDamageInfo &info ) -{ - m_takedamage = DAMAGE_NO; - m_lifeState = LIFE_DEAD; - - m_OnDeath.FireOutput( info.GetInflictor(), this ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Changes the target entity for a func_tank or tanktrain_ai -//----------------------------------------------------------------------------- -class CTankTargetChange : public CPointEntity -{ -public: - DECLARE_CLASS( CTankTargetChange, CPointEntity ); - - void Precache( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - - DECLARE_DATADESC(); - -private: - variant_t m_newTarget; - string_t m_newTargetName; -}; - -LINK_ENTITY_TO_CLASS( tanktrain_aitarget, CTankTargetChange ); - -BEGIN_DATADESC( CTankTargetChange ) - - // DEFINE_FIELD( m_newTarget, variant_t ), - DEFINE_KEYFIELD( m_newTargetName, FIELD_STRING, "newtarget" ), - -END_DATADESC() - - -void CTankTargetChange::Precache( void ) -{ - BaseClass::Precache(); - - // This needs to be in Precache so save/load works - m_newTarget.SetString( m_newTargetName ); -} - -void CTankTargetChange::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, m_target, NULL, pActivator, pCaller ); - - // UNDONE: This should use more of the event system - while ( pTarget ) - { - // Change the target over - pTarget->AcceptInput( "TargetEntity", this, this, m_newTarget, 0 ); - pTarget = gEntList.FindEntityByName( pTarget, m_target, NULL, pActivator, pCaller ); - } -} - - -// UNDONE: Should be just a logical entity, but we act as another static sound channel for the train -class CTankTrainAI : public CPointEntity -{ -public: - DECLARE_CLASS( CTankTrainAI, CPointEntity ); - - virtual ~CTankTrainAI( void ); - - void Precache( void ); - void Spawn( void ); - void Activate( void ); - void Think( void ); - - int SoundEnginePitch( void ); - void SoundEngineStart( void ); - void SoundEngineStop( void ); - void SoundShutdown( void ); - - CBaseEntity *FindTarget( string_t target, CBaseEntity *pActivator ); - - DECLARE_DATADESC(); - - // INPUTS - void InputTargetEntity( inputdata_t &inputdata ); - -private: - CHandle m_hTrain; - EHANDLE m_hTargetEntity; - int m_soundPlaying; - - CSoundPatch *m_soundTreads; - CSoundPatch *m_soundEngine; - - string_t m_startSoundName; - string_t m_engineSoundName; - string_t m_movementSoundName; - string_t m_targetEntityName; -}; - -LINK_ENTITY_TO_CLASS( tanktrain_ai, CTankTrainAI ); - -BEGIN_DATADESC( CTankTrainAI ) - - DEFINE_FIELD( m_hTrain, FIELD_EHANDLE), - DEFINE_FIELD( m_hTargetEntity, FIELD_EHANDLE), - DEFINE_FIELD( m_soundPlaying, FIELD_INTEGER), - DEFINE_SOUNDPATCH( m_soundTreads ), - DEFINE_SOUNDPATCH( m_soundEngine ), - - DEFINE_KEYFIELD( m_startSoundName, FIELD_STRING, "startsound" ), - DEFINE_KEYFIELD( m_engineSoundName, FIELD_STRING, "enginesound" ), - DEFINE_KEYFIELD( m_movementSoundName, FIELD_STRING, "movementsound" ), - DEFINE_FIELD( m_targetEntityName, FIELD_STRING), - - // Inputs - DEFINE_INPUTFUNC( FIELD_STRING, "TargetEntity", InputTargetEntity ), - -END_DATADESC() - - - -//----------------------------------------------------------------------------- -// Purpose: Input handler for setting the target entity by name. -//----------------------------------------------------------------------------- -void CTankTrainAI::InputTargetEntity( inputdata_t &inputdata ) -{ - m_targetEntityName = inputdata.value.StringID(); - m_hTargetEntity = FindTarget( m_targetEntityName, inputdata.pActivator ); - SetNextThink( gpGlobals->curtime ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Finds the first entity in the entity list with the given name. -// Input : target - String ID of the entity to find. -// pActivator - The activating entity if this is called from an input -// or Use handler, NULL otherwise. -//----------------------------------------------------------------------------- -CBaseEntity *CTankTrainAI::FindTarget( string_t target, CBaseEntity *pActivator ) -{ - return gEntList.FindEntityGeneric( NULL, STRING( target ), this, pActivator ); -} - - -CTankTrainAI::~CTankTrainAI( void ) -{ - CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); - - if ( m_soundTreads ) - { - controller.SoundDestroy( m_soundTreads ); - } - - if ( m_soundEngine ) - { - controller.SoundDestroy( m_soundEngine ); - } -} - -void CTankTrainAI::Precache( void ) -{ - PrecacheScriptSound( STRING( m_startSoundName ) ); - PrecacheScriptSound( STRING( m_engineSoundName ) ); - PrecacheScriptSound( STRING( m_movementSoundName ) ); -} - -int CTankTrainAI::SoundEnginePitch( void ) -{ - CFuncTrackTrain *pTrain = m_hTrain; - - // we know this isn't NULL here - if ( pTrain->GetMaxSpeed() ) - { - return 90 + (fabs(pTrain->GetCurrentSpeed()) * (20) / pTrain->GetMaxSpeed()); - } - return 100; -} - - -void CTankTrainAI::SoundEngineStart( void ) -{ - CFuncTrackTrain *pTrain = m_hTrain; - - SoundEngineStop(); - // play startup sound for train - if ( m_startSoundName != NULL_STRING ) - { - CPASAttenuationFilter filter( pTrain ); - - EmitSound_t ep; - ep.m_nChannel = CHAN_ITEM; - ep.m_pSoundName = STRING(m_startSoundName); - ep.m_flVolume = 1.0f; - ep.m_SoundLevel = SNDLVL_NORM; - - EmitSound( filter, pTrain->entindex(), ep ); - } - - // play the looping sounds using the envelope controller - CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); - - if ( m_soundTreads ) - { - controller.Play( m_soundTreads, 1.0, 100 ); - } - - if ( m_soundEngine ) - { - controller.Play( m_soundEngine, 0.5, 90 ); - controller.CommandClear( m_soundEngine ); - controller.CommandAdd( m_soundEngine, 0, SOUNDCTRL_CHANGE_PITCH, 1.5, random->RandomInt(130, 145) ); - controller.CommandAdd( m_soundEngine, 1.5, SOUNDCTRL_CHANGE_PITCH, 2, random->RandomInt(105, 115) ); - } - - m_soundPlaying = true; -} - - -void CTankTrainAI::SoundEngineStop( void ) -{ - if ( !m_soundPlaying ) - return; - - CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); - - if ( m_soundTreads ) - { - controller.SoundFadeOut( m_soundTreads, 0.25 ); - } - - if ( m_soundEngine ) - { - controller.CommandClear( m_soundEngine ); - controller.SoundChangePitch( m_soundEngine, 70, 3.0 ); - } - m_soundPlaying = false; -} - - -void CTankTrainAI::SoundShutdown( void ) -{ - CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); - if ( m_soundTreads ) - { - controller.Shutdown( m_soundTreads ); - } - - if ( m_soundEngine ) - { - controller.Shutdown( m_soundEngine ); - } - m_soundPlaying = false; -} - -//----------------------------------------------------------------------------- -// Purpose: Set up think and AI -//----------------------------------------------------------------------------- -void CTankTrainAI::Spawn( void ) -{ - Precache(); - m_soundPlaying = false; - m_hTargetEntity = NULL; -} - -void CTankTrainAI::Activate( void ) -{ - BaseClass::Activate(); - - CBaseEntity *pTarget = NULL; - - CFuncTrackTrain *pTrain = NULL; - - if ( m_target != NULL_STRING ) - { - do - { - pTarget = gEntList.FindEntityByName( pTarget, m_target ); - pTrain = dynamic_cast(pTarget); - } while (!pTrain && pTarget); - } - - m_hTrain = pTrain; - - if ( pTrain ) - { - SetNextThink( gpGlobals->curtime + 0.5f ); - CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); - - if ( m_movementSoundName != NULL_STRING ) - { - CPASAttenuationFilter filter( this, ATTN_NORM * 0.5 ); - m_soundTreads = controller.SoundCreate( filter, pTrain->entindex(), CHAN_STATIC, STRING(m_movementSoundName), ATTN_NORM*0.5 ); - } - if ( m_engineSoundName != NULL_STRING ) - { - CPASAttenuationFilter filter( this ); - m_soundEngine = controller.SoundCreate( filter, pTrain->entindex(), CHAN_STATIC, STRING(m_engineSoundName), ATTN_NORM ); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Dumb linear serach of the path -// Input : *pStart - starting path node -// &startPosition - starting position -// &destination - position to move close to -// Output : int move direction 1 = forward, -1 = reverse, 0 = stop -//----------------------------------------------------------------------------- -int PathFindDirection( CPathTrack *pStart, const Vector &startPosition, const Vector &destination ) -{ - if ( !pStart ) - return 0; // no path, don't move - - CPathTrack *pPath = pStart->m_pnext; - CPathTrack *pNearest = pStart; - - float nearestDist = (pNearest->GetLocalOrigin() - destination).LengthSqr(); - float length = 0; - float nearestForward = 0, nearestReverse = 0; - - do - { - float dist = (pPath->GetLocalOrigin() - destination).LengthSqr(); - - // This is closer than our current estimate - if ( dist < nearestDist ) - { - nearestDist = dist; - pNearest = pPath; - nearestForward = length; // current path length forward - nearestReverse = 0; // count until we hit the start again - } - CPathTrack *pNext = pPath->m_pnext; - if ( pNext ) - { - // UNDONE: Cache delta in path? - float delta = (pNext->GetLocalOrigin() - pPath->GetLocalOrigin()).LengthSqr(); - length += delta; - // add to current reverse estimate - nearestReverse += delta; - pPath = pNext; - } - else - { - // not a looping path - // traverse back to other end of the path - int fail = 0; - while ( pPath->m_pprevious ) - { - fail++; - // HACKHACK: Don't infinite loop - if ( fail > 256 ) - break; - pPath = pPath->m_pprevious; - } - // don't take the reverse path to old node - nearestReverse = nearestForward + 1; - // dont' take forward path to new node (if we find one) - length = (float)COORD_EXTENT * (float)COORD_EXTENT; // HACKHACK: Max quad length - } - - } while ( pPath != pStart ); - - // UNDONE: Fix this fudge factor - // if you are already at the path, or <100 units away, don't move - if ( pNearest == pStart || (pNearest->GetLocalOrigin() - startPosition).LengthSqr() < 100 ) - return 0; - - if ( nearestForward <= nearestReverse ) - return 1; - - return -1; -} - - -//----------------------------------------------------------------------------- -// Purpose: Find a point on my path near to the target and move toward it -//----------------------------------------------------------------------------- -void CTankTrainAI::Think( void ) -{ - CFuncTrackTrain *pTrain = m_hTrain; - - if ( !pTrain || pTrain->m_lifeState != LIFE_ALIVE ) - { - SoundShutdown(); - if ( pTrain ) - UTIL_RemoveHierarchy( pTrain ); - UTIL_Remove( this ); - return; - } - - int desired = 0; - CBaseEntity *pTarget = m_hTargetEntity; - if ( pTarget ) - { - desired = PathFindDirection( pTrain->m_ppath, pTrain->GetLocalOrigin(), pTarget->GetLocalOrigin() ); - } - - // If the train wants to stop, figure out throttle - // otherwise, just throttle in the indicated direction and let the train logic - // clip the speed - if ( !desired ) - { - if ( pTrain->m_flSpeed > 0 ) - { - desired = -1; - } - else if ( pTrain->m_flSpeed < 0 ) - { - desired = 1; - } - } - - // UNDONE: Align the think time with arrival, and bump this up to a few seconds - SetNextThink( gpGlobals->curtime + 0.5f ); - - if ( desired != 0 ) - { - int wasMoving = (pTrain->m_flSpeed == 0) ? false : true; - // chaser wants train to move, send message - pTrain->SetSpeed( desired ); - int isMoving = (pTrain->m_flSpeed == 0) ? false : true; - - if ( !isMoving && wasMoving ) - { - SoundEngineStop(); - } - else if ( isMoving ) - { - if ( !wasMoving ) - { - SoundEngineStart(); - } - } - } - else - { - SoundEngineStop(); - // UNDONE: Align the think time with arrival, and bump this up to a few seconds - SetNextThink( gpGlobals->curtime + 1.0f ); - } -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "trains.h" +#include "entitylist.h" +#include "soundenvelope.h" +#include "engine/IEngineSound.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern short g_sModelIndexFireball; +#define SPRITE_FIREBALL "sprites/zerogxplode.vmt" +#define SPRITE_SMOKE "sprites/steam1.vmt" + +void UTIL_RemoveHierarchy( CBaseEntity *pDead ) +{ + if ( !pDead ) + return; + + if ( pDead->edict() ) + { + CBaseEntity *pChild = pDead->FirstMoveChild(); + while ( pChild ) + { + CBaseEntity *pEntity = pChild; + pChild = pChild->NextMovePeer(); + + UTIL_RemoveHierarchy( pEntity ); + } + } + UTIL_Remove( pDead ); +} + +class CFuncTankTrain : public CFuncTrackTrain +{ +public: + DECLARE_CLASS( CFuncTankTrain, CFuncTrackTrain ); + + void Spawn( void ); + + // Filter out damage messages that don't contain blast damage (impervious to other forms of attack) + int OnTakeDamage( const CTakeDamageInfo &info ); + void Event_Killed( const CTakeDamageInfo &info ); + void Blocked( CBaseEntity *pOther ) + { + // FIxme, set speed to zero? + } + DECLARE_DATADESC(); + +private: + + COutputEvent m_OnDeath; +}; + +LINK_ENTITY_TO_CLASS( func_tanktrain, CFuncTankTrain ); + +BEGIN_DATADESC( CFuncTankTrain ) + + // Outputs + DEFINE_OUTPUT(m_OnDeath, "OnDeath"), + +END_DATADESC() + + +void CFuncTankTrain::Spawn( void ) +{ + m_takedamage = true; + BaseClass::Spawn(); +} + +// Filter out damage messages that don't contain blast damage (impervious to other forms of attack) +int CFuncTankTrain::OnTakeDamage( const CTakeDamageInfo &info ) +{ + if ( ! (info.GetDamageType() & DMG_BLAST) ) + return 0; + + return BaseClass::OnTakeDamage( info ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when the train is killed. +// Input : pInflictor - What killed us. +// pAttacker - Who killed us. +// flDamage - The damage that the killing blow inflicted. +// bitsDamageType - Bitfield of damage types that were inflicted. +//----------------------------------------------------------------------------- +void CFuncTankTrain::Event_Killed( const CTakeDamageInfo &info ) +{ + m_takedamage = DAMAGE_NO; + m_lifeState = LIFE_DEAD; + + m_OnDeath.FireOutput( info.GetInflictor(), this ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Changes the target entity for a func_tank or tanktrain_ai +//----------------------------------------------------------------------------- +class CTankTargetChange : public CPointEntity +{ +public: + DECLARE_CLASS( CTankTargetChange, CPointEntity ); + + void Precache( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + DECLARE_DATADESC(); + +private: + variant_t m_newTarget; + string_t m_newTargetName; +}; + +LINK_ENTITY_TO_CLASS( tanktrain_aitarget, CTankTargetChange ); + +BEGIN_DATADESC( CTankTargetChange ) + + // DEFINE_FIELD( m_newTarget, variant_t ), + DEFINE_KEYFIELD( m_newTargetName, FIELD_STRING, "newtarget" ), + +END_DATADESC() + + +void CTankTargetChange::Precache( void ) +{ + BaseClass::Precache(); + + // This needs to be in Precache so save/load works + m_newTarget.SetString( m_newTargetName ); +} + +void CTankTargetChange::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, m_target, NULL, pActivator, pCaller ); + + // UNDONE: This should use more of the event system + while ( pTarget ) + { + // Change the target over + pTarget->AcceptInput( "TargetEntity", this, this, m_newTarget, 0 ); + pTarget = gEntList.FindEntityByName( pTarget, m_target, NULL, pActivator, pCaller ); + } +} + + +// UNDONE: Should be just a logical entity, but we act as another static sound channel for the train +class CTankTrainAI : public CPointEntity +{ +public: + DECLARE_CLASS( CTankTrainAI, CPointEntity ); + + virtual ~CTankTrainAI( void ); + + void Precache( void ); + void Spawn( void ); + void Activate( void ); + void Think( void ); + + int SoundEnginePitch( void ); + void SoundEngineStart( void ); + void SoundEngineStop( void ); + void SoundShutdown( void ); + + CBaseEntity *FindTarget( string_t target, CBaseEntity *pActivator ); + + DECLARE_DATADESC(); + + // INPUTS + void InputTargetEntity( inputdata_t &inputdata ); + +private: + CHandle m_hTrain; + EHANDLE m_hTargetEntity; + int m_soundPlaying; + + CSoundPatch *m_soundTreads; + CSoundPatch *m_soundEngine; + + string_t m_startSoundName; + string_t m_engineSoundName; + string_t m_movementSoundName; + string_t m_targetEntityName; +}; + +LINK_ENTITY_TO_CLASS( tanktrain_ai, CTankTrainAI ); + +BEGIN_DATADESC( CTankTrainAI ) + + DEFINE_FIELD( m_hTrain, FIELD_EHANDLE), + DEFINE_FIELD( m_hTargetEntity, FIELD_EHANDLE), + DEFINE_FIELD( m_soundPlaying, FIELD_INTEGER), + DEFINE_SOUNDPATCH( m_soundTreads ), + DEFINE_SOUNDPATCH( m_soundEngine ), + + DEFINE_KEYFIELD( m_startSoundName, FIELD_STRING, "startsound" ), + DEFINE_KEYFIELD( m_engineSoundName, FIELD_STRING, "enginesound" ), + DEFINE_KEYFIELD( m_movementSoundName, FIELD_STRING, "movementsound" ), + DEFINE_FIELD( m_targetEntityName, FIELD_STRING), + + // Inputs + DEFINE_INPUTFUNC( FIELD_STRING, "TargetEntity", InputTargetEntity ), + +END_DATADESC() + + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for setting the target entity by name. +//----------------------------------------------------------------------------- +void CTankTrainAI::InputTargetEntity( inputdata_t &inputdata ) +{ + m_targetEntityName = inputdata.value.StringID(); + m_hTargetEntity = FindTarget( m_targetEntityName, inputdata.pActivator ); + SetNextThink( gpGlobals->curtime ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Finds the first entity in the entity list with the given name. +// Input : target - String ID of the entity to find. +// pActivator - The activating entity if this is called from an input +// or Use handler, NULL otherwise. +//----------------------------------------------------------------------------- +CBaseEntity *CTankTrainAI::FindTarget( string_t target, CBaseEntity *pActivator ) +{ + return gEntList.FindEntityGeneric( NULL, STRING( target ), this, pActivator ); +} + + +CTankTrainAI::~CTankTrainAI( void ) +{ + CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); + + if ( m_soundTreads ) + { + controller.SoundDestroy( m_soundTreads ); + } + + if ( m_soundEngine ) + { + controller.SoundDestroy( m_soundEngine ); + } +} + +void CTankTrainAI::Precache( void ) +{ + PrecacheScriptSound( STRING( m_startSoundName ) ); + PrecacheScriptSound( STRING( m_engineSoundName ) ); + PrecacheScriptSound( STRING( m_movementSoundName ) ); +} + +int CTankTrainAI::SoundEnginePitch( void ) +{ + CFuncTrackTrain *pTrain = m_hTrain; + + // we know this isn't NULL here + if ( pTrain->GetMaxSpeed() ) + { + return 90 + (fabs(pTrain->GetCurrentSpeed()) * (20) / pTrain->GetMaxSpeed()); + } + return 100; +} + + +void CTankTrainAI::SoundEngineStart( void ) +{ + CFuncTrackTrain *pTrain = m_hTrain; + + SoundEngineStop(); + // play startup sound for train + if ( m_startSoundName != NULL_STRING ) + { + CPASAttenuationFilter filter( pTrain ); + + EmitSound_t ep; + ep.m_nChannel = CHAN_ITEM; + ep.m_pSoundName = STRING(m_startSoundName); + ep.m_flVolume = 1.0f; + ep.m_SoundLevel = SNDLVL_NORM; + + EmitSound( filter, pTrain->entindex(), ep ); + } + + // play the looping sounds using the envelope controller + CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); + + if ( m_soundTreads ) + { + controller.Play( m_soundTreads, 1.0, 100 ); + } + + if ( m_soundEngine ) + { + controller.Play( m_soundEngine, 0.5, 90 ); + controller.CommandClear( m_soundEngine ); + controller.CommandAdd( m_soundEngine, 0, SOUNDCTRL_CHANGE_PITCH, 1.5, random->RandomInt(130, 145) ); + controller.CommandAdd( m_soundEngine, 1.5, SOUNDCTRL_CHANGE_PITCH, 2, random->RandomInt(105, 115) ); + } + + m_soundPlaying = true; +} + + +void CTankTrainAI::SoundEngineStop( void ) +{ + if ( !m_soundPlaying ) + return; + + CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); + + if ( m_soundTreads ) + { + controller.SoundFadeOut( m_soundTreads, 0.25 ); + } + + if ( m_soundEngine ) + { + controller.CommandClear( m_soundEngine ); + controller.SoundChangePitch( m_soundEngine, 70, 3.0 ); + } + m_soundPlaying = false; +} + + +void CTankTrainAI::SoundShutdown( void ) +{ + CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); + if ( m_soundTreads ) + { + controller.Shutdown( m_soundTreads ); + } + + if ( m_soundEngine ) + { + controller.Shutdown( m_soundEngine ); + } + m_soundPlaying = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Set up think and AI +//----------------------------------------------------------------------------- +void CTankTrainAI::Spawn( void ) +{ + Precache(); + m_soundPlaying = false; + m_hTargetEntity = NULL; +} + +void CTankTrainAI::Activate( void ) +{ + BaseClass::Activate(); + + CBaseEntity *pTarget = NULL; + + CFuncTrackTrain *pTrain = NULL; + + if ( m_target != NULL_STRING ) + { + do + { + pTarget = gEntList.FindEntityByName( pTarget, m_target ); + pTrain = dynamic_cast(pTarget); + } while (!pTrain && pTarget); + } + + m_hTrain = pTrain; + + if ( pTrain ) + { + SetNextThink( gpGlobals->curtime + 0.5f ); + CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); + + if ( m_movementSoundName != NULL_STRING ) + { + CPASAttenuationFilter filter( this, ATTN_NORM * 0.5 ); + m_soundTreads = controller.SoundCreate( filter, pTrain->entindex(), CHAN_STATIC, STRING(m_movementSoundName), ATTN_NORM*0.5 ); + } + if ( m_engineSoundName != NULL_STRING ) + { + CPASAttenuationFilter filter( this ); + m_soundEngine = controller.SoundCreate( filter, pTrain->entindex(), CHAN_STATIC, STRING(m_engineSoundName), ATTN_NORM ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Dumb linear serach of the path +// Input : *pStart - starting path node +// &startPosition - starting position +// &destination - position to move close to +// Output : int move direction 1 = forward, -1 = reverse, 0 = stop +//----------------------------------------------------------------------------- +int PathFindDirection( CPathTrack *pStart, const Vector &startPosition, const Vector &destination ) +{ + if ( !pStart ) + return 0; // no path, don't move + + CPathTrack *pPath = pStart->m_pnext; + CPathTrack *pNearest = pStart; + + float nearestDist = (pNearest->GetLocalOrigin() - destination).LengthSqr(); + float length = 0; + float nearestForward = 0, nearestReverse = 0; + + do + { + float dist = (pPath->GetLocalOrigin() - destination).LengthSqr(); + + // This is closer than our current estimate + if ( dist < nearestDist ) + { + nearestDist = dist; + pNearest = pPath; + nearestForward = length; // current path length forward + nearestReverse = 0; // count until we hit the start again + } + CPathTrack *pNext = pPath->m_pnext; + if ( pNext ) + { + // UNDONE: Cache delta in path? + float delta = (pNext->GetLocalOrigin() - pPath->GetLocalOrigin()).LengthSqr(); + length += delta; + // add to current reverse estimate + nearestReverse += delta; + pPath = pNext; + } + else + { + // not a looping path + // traverse back to other end of the path + int fail = 0; + while ( pPath->m_pprevious ) + { + fail++; + // HACKHACK: Don't infinite loop + if ( fail > 256 ) + break; + pPath = pPath->m_pprevious; + } + // don't take the reverse path to old node + nearestReverse = nearestForward + 1; + // dont' take forward path to new node (if we find one) + length = (float)COORD_EXTENT * (float)COORD_EXTENT; // HACKHACK: Max quad length + } + + } while ( pPath != pStart ); + + // UNDONE: Fix this fudge factor + // if you are already at the path, or <100 units away, don't move + if ( pNearest == pStart || (pNearest->GetLocalOrigin() - startPosition).LengthSqr() < 100 ) + return 0; + + if ( nearestForward <= nearestReverse ) + return 1; + + return -1; +} + + +//----------------------------------------------------------------------------- +// Purpose: Find a point on my path near to the target and move toward it +//----------------------------------------------------------------------------- +void CTankTrainAI::Think( void ) +{ + CFuncTrackTrain *pTrain = m_hTrain; + + if ( !pTrain || pTrain->m_lifeState != LIFE_ALIVE ) + { + SoundShutdown(); + if ( pTrain ) + UTIL_RemoveHierarchy( pTrain ); + UTIL_Remove( this ); + return; + } + + int desired = 0; + CBaseEntity *pTarget = m_hTargetEntity; + if ( pTarget ) + { + desired = PathFindDirection( pTrain->m_ppath, pTrain->GetLocalOrigin(), pTarget->GetLocalOrigin() ); + } + + // If the train wants to stop, figure out throttle + // otherwise, just throttle in the indicated direction and let the train logic + // clip the speed + if ( !desired ) + { + if ( pTrain->m_flSpeed > 0 ) + { + desired = -1; + } + else if ( pTrain->m_flSpeed < 0 ) + { + desired = 1; + } + } + + // UNDONE: Align the think time with arrival, and bump this up to a few seconds + SetNextThink( gpGlobals->curtime + 0.5f ); + + if ( desired != 0 ) + { + int wasMoving = (pTrain->m_flSpeed == 0) ? false : true; + // chaser wants train to move, send message + pTrain->SetSpeed( desired ); + int isMoving = (pTrain->m_flSpeed == 0) ? false : true; + + if ( !isMoving && wasMoving ) + { + SoundEngineStop(); + } + else if ( isMoving ) + { + if ( !wasMoving ) + { + SoundEngineStart(); + } + } + } + else + { + SoundEngineStop(); + // UNDONE: Align the think time with arrival, and bump this up to a few seconds + SetNextThink( gpGlobals->curtime + 1.0f ); + } +} -- cgit v1.2.3