summaryrefslogtreecommitdiff
path: root/game/shared/tf/tf_wheel_of_doom.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/tf/tf_wheel_of_doom.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/shared/tf/tf_wheel_of_doom.cpp')
-rw-r--r--game/shared/tf/tf_wheel_of_doom.cpp1117
1 files changed, 1117 insertions, 0 deletions
diff --git a/game/shared/tf/tf_wheel_of_doom.cpp b/game/shared/tf/tf_wheel_of_doom.cpp
new file mode 100644
index 0000000..d9a471a
--- /dev/null
+++ b/game/shared/tf/tf_wheel_of_doom.cpp
@@ -0,0 +1,1117 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "tf/halloween/merasmus/merasmus.h"
+#include "tf/halloween/merasmus/merasmus_dancer.h"
+#include "tf_gamerules.h"
+#include "tf_weapon_jar.h"
+#include "tf_wheel_of_doom.h"
+
+
+LINK_ENTITY_TO_CLASS( wheel_of_doom, CWheelOfDoom );
+
+// Data Description
+BEGIN_DATADESC( CWheelOfDoom )
+
+ // Keyfields
+ DEFINE_KEYFIELD( m_flDuration, FIELD_FLOAT, "effect_duration" ),
+ DEFINE_KEYFIELD( m_bHasSpiral, FIELD_BOOLEAN, "has_spiral" ),
+
+ DEFINE_INPUTFUNC( FIELD_STRING, "Spin", Spin ),
+ DEFINE_INPUTFUNC( FIELD_STRING, "ClearAllEffects", ClearAllEffects ),
+
+ // Outputs
+ DEFINE_OUTPUT( m_EffectApplied, "OnEffectApplied" ),
+ DEFINE_OUTPUT( m_EffectExpired, "OnEffectExpired" ),
+
+END_DATADESC()
+
+extern ConVar sv_gravity;
+
+#define WHEEL_SPIN_TIME 5.75f
+#define WHEEL_SPIN_TIME_BIAS 0.3f
+#define WHEEL_FASTEST_SPIN_RATE 0.1f
+#define WHEEL_SLOWEST_SPIN_RATE 0.55f
+
+#define WHEEL_SPIRAL_GROW_RATE 0.55f
+#define WHEEL_SPIRAL_SHRINK_RATE 0.55f
+
+
+#define EFFECT_WHAMMY 1<<0
+#define EFFECT_DOES_NOT_REAPPLY_ON_SPAWN 1<<1
+
+
+class CWheelOfDoomSpiral : public CBaseAnimating
+{
+ DECLARE_CLASS( CWheelOfDoom, CBaseAnimating );
+ DECLARE_DATADESC();
+
+public:
+ CWheelOfDoomSpiral()
+ {
+ m_flScale = 0.f;
+ SetModelScale( 0.f );
+ }
+
+ virtual void Spawn()
+ {
+ SetThink( NULL );
+ }
+
+ virtual void Precache()
+ {
+ PrecacheModel( "models/props_lakeside_event/wof_plane2.mdl" );
+ }
+
+ void GrowAndBecomeVisible()
+ {
+ RemoveEffects( EF_NODRAW );
+
+ SetThink( &CWheelOfDoomSpiral::GrowThink );
+ SetNextThink( gpGlobals->curtime );
+ }
+
+ void ShrinkAndHide()
+ {
+ SetThink( &CWheelOfDoomSpiral::ShrinkThink );
+ SetNextThink( gpGlobals->curtime );
+ }
+
+private:
+
+ void GrowThink()
+ {
+ // Grow ourselves over time
+ m_flScale += WHEEL_SPIRAL_GROW_RATE * gpGlobals->frametime;
+
+ if( m_flScale >= 1.f )
+ {
+ m_flScale = 1.f;
+ SetThink( NULL );
+ }
+
+ SetModelScale( m_flScale );
+
+ SetNextThink( gpGlobals->curtime );
+ }
+
+ void ShrinkThink()
+ {
+ // Shrink ourselves over time
+ m_flScale -= WHEEL_SPIRAL_SHRINK_RATE * gpGlobals->frametime;
+
+ if( m_flScale <= 0.f )
+ {
+ m_flScale = 0.f;
+ AddEffects( EF_NODRAW );
+ SetThink( NULL );
+ }
+
+ SetModelScale( m_flScale );
+
+ SetNextThink( gpGlobals->curtime );
+ }
+
+ float m_flScale;
+};
+
+LINK_ENTITY_TO_CLASS( wheel_of_doom_spiral, CWheelOfDoomSpiral );
+
+// Data Description
+BEGIN_DATADESC( CWheelOfDoomSpiral )
+END_DATADESC()
+
+#ifdef STAGING_ONLY
+static void ProcWheelEffect( const CCommand &args )
+{
+ CBaseEntity *pOther = gEntList.FindEntityByClassname( NULL, "wheel_of_doom" );
+
+ CWheelOfDoom* pWheel = dynamic_cast<CWheelOfDoom*>( pOther );
+ if( pWheel )
+ {
+ pWheel->DBG_ApplyEffectByName( args.ArgS() );
+ }
+}
+ConCommand cc_proc_wheel_effect( "cc_proc_wheel_effect", ProcWheelEffect, "Force a Wheel of Doom entity to apply the specified effect" );
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWheelOfDoom::CWheelOfDoom( void ) :
+ m_EffectManager( this ),
+ m_pChosenEffect( NULL ),
+ m_pSpiral( NULL ),
+ m_flFinishBroadcastingEffectTime( 0.f )
+{
+ AddEffects( EF_NODRAW );
+
+ RegisterEffect( new WOD_UberEffect() );
+ RegisterEffect( new WOD_CritsEffect() );
+ RegisterEffect( new WOD_SuperSpeedEffect() );
+ RegisterEffect( new WOD_SuperJumpEffect() );
+ RegisterEffect( new WOD_BigHeadEffect() );
+ RegisterEffect( new WOD_SmallHeadEffect() );
+ RegisterEffect( new WOD_LowGravityEffect() );
+ RegisterEffect( new WOD_Dance(), EFFECT_DOES_NOT_REAPPLY_ON_SPAWN );
+ RegisterEffect( new WOD_Pee(), EFFECT_WHAMMY | EFFECT_DOES_NOT_REAPPLY_ON_SPAWN );
+ RegisterEffect( new WOD_Burn(), EFFECT_WHAMMY | EFFECT_DOES_NOT_REAPPLY_ON_SPAWN );
+ RegisterEffect( new WOD_Ghosts(), EFFECT_WHAMMY | EFFECT_DOES_NOT_REAPPLY_ON_SPAWN );
+}
+
+
+CWheelOfDoom::~CWheelOfDoom( void )
+{
+ m_EffectManager.ClearEffects();
+ m_vecEffects.PurgeAndDeleteElements();
+}
+
+
+void CWheelOfDoom::RegisterEffect( WOD_BaseEffect* pEffect, int nFlags )
+{
+ pEffect->SetListFlags( nFlags );
+ m_vecEffects.AddToTail( pEffect );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWheelOfDoom::Precache( void )
+{
+ PrecacheModel( GetScreenModelName() );
+ PrecacheModel( "models/props_lakeside_event/wof_plane2.mdl" );
+
+ PrecacheScriptSound( "Halloween.WheelofFate" );
+ PrecacheScriptSound( "Halloween.dance_howl" );
+ PrecacheScriptSound( "Halloween.dance_loop" );
+
+ PrecacheScriptSound( "Halloween.HeadlessBossAxeHitWorld" );
+ PrecacheScriptSound( "Halloween.LightsOn" );
+ PrecacheScriptSound( "Weapon_StickyBombLauncher.BoltForward" );
+ PrecacheScriptSound( "TFPlayer.InvulnerableOff" );
+
+ m_EffectManager.Precache();
+}
+
+const char* CWheelOfDoom::GetScreenModelName()
+{
+ return "models/props_lakeside_event/buff_plane.mdl";
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWheelOfDoom::Spawn( void )
+{
+ Precache();
+
+ SetModel( GetScreenModelName() );
+
+ SetSolid( SOLID_BBOX );
+ AddSolidFlags( FSOLID_NOT_SOLID );
+ SetMoveType( MOVETYPE_NONE );
+
+ ListenForGameEvent( "player_spawn" );
+
+ SetThink( &CWheelOfDoom::IdleThink );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ if ( TFGameRules() != NULL )
+ {
+ TFGameRules()->ClearHalloweenEffectStatus();
+ }
+
+ if( m_bHasSpiral )
+ {
+ m_pSpiral = assert_cast<CWheelOfDoomSpiral*>( CreateEntityByName( "wheel_of_doom_spiral" ) );
+ Assert( m_pSpiral );
+ m_pSpiral->SetModel( "models/props_lakeside_event/wof_plane2.mdl" );
+ m_pSpiral->AddEffects( EF_NODRAW );
+ m_pSpiral->SetAbsOrigin( GetAbsOrigin() );
+ m_pSpiral->SetAbsAngles( GetAbsAngles() );
+ m_pSpiral->SetParent( this );
+ }
+}
+
+void CWheelOfDoom::FireGameEvent( IGameEvent *gameEvent )
+{
+ if( FStrEq( gameEvent->GetName(), "player_spawn" ) )
+ {
+ const int nUserID = gameEvent->GetInt( "userid" );
+ CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByUserId( nUserID ) );
+ if( pPlayer )
+ {
+ m_EffectManager.ApplyAllEffectsToPlayer( pPlayer );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWheelOfDoom::IdleThink( void )
+{
+ if( m_EffectManager.UpdateAndClearExpiredEffects() )
+ {
+ AddEffects( EF_NODRAW );
+
+ m_EffectExpired.FireOutput( this, this );
+
+ // Clear the skin to blank
+ SetSkin( 0 );
+
+ // Clear the HUD
+ TFGameRules()->ClearHalloweenEffectStatus();
+
+ if( m_bHasSpiral )
+ {
+ // Make our spirals shrink down and hide themselves
+ m_pSpiral->ShrinkAndHide();
+ FOR_EACH_VEC( m_vecOtherWODs, i )
+ {
+ if( m_vecOtherWODs[i]->m_bHasSpiral )
+ {
+ m_vecOtherWODs[i]->m_pSpiral->ShrinkAndHide();
+ }
+ }
+ }
+ }
+
+ //Next update
+ SetNextThink( gpGlobals->curtime + 0.1f );
+}
+
+void CWheelOfDoom::SetSkin( int nSkin )
+{
+ m_nSkin = nSkin;
+
+ FOR_EACH_VEC( m_vecOtherWODs, i )
+ {
+ m_vecOtherWODs[i]->m_nSkin = nSkin;
+ }
+}
+
+void CWheelOfDoom::SetScale( float flScale )
+{
+ SetModelScale( flScale );
+
+ FOR_EACH_VEC( m_vecOtherWODs, i )
+ {
+ m_vecOtherWODs[i]->SetModelScale( flScale );
+ }
+}
+
+
+void CWheelOfDoom::PlaySound( const char* pszSound )
+{
+ EmitSound( pszSound );
+
+ FOR_EACH_VEC( m_vecOtherWODs, i )
+ {
+ m_vecOtherWODs[i]->EmitSound( pszSound );
+ }
+}
+
+
+void CWheelOfDoom::SpinThink( void )
+{
+ if( m_EffectManager.UpdateAndClearExpiredEffects() )
+ {
+ m_EffectExpired.FireOutput( this, this );
+
+ // Clear the HUD
+ TFGameRules()->ClearHalloweenEffectStatus();
+ }
+
+ // Are we done spinning?
+ if( gpGlobals->curtime > m_flStopSpinTime )
+ {
+ if( gpGlobals->curtime > m_flStopSpinTime + 1.f )
+ {
+ //PlaySound( "Halloween.LightsOn" );
+ SetScale( 1.f );
+
+ m_EffectApplied.FireOutput( this, this );
+
+ // Apply the effect!
+ SetSkin( m_EffectManager.AddEffect( m_pChosenEffect, m_flDuration ) );
+
+ SetThink( &CWheelOfDoom::IdleThink );
+
+ m_flStopSpinTime = 0.f;
+ m_flNextTickTime = 0.f;
+ m_pChosenEffect = NULL;
+ }
+ }
+ // Is it time for another tick of the wheel?
+ else if( gpGlobals->curtime > m_flNextTickTime )
+ {
+ int nRandSkin = RandomInt( 1, EFFECT_COUNT-1 );
+ if( nRandSkin == m_nSkin )
+ ++nRandSkin;
+
+ // Roll over to 1. 0 is blank so skip that one.
+ if( nRandSkin == EFFECT_COUNT )
+ nRandSkin = 1;
+
+ SetSkin( nRandSkin );
+ SetScale( RemapVal( CalcSpinCompletion(), 0.f, 1.f, 0.3f, 0.9f) );
+
+ m_flNextTickTime = CalcNextTickTime();
+ }
+
+ //Is it time for Merasmus to announce the spin?
+ if (gpGlobals->curtime > m_flNextAnnounceTime && !m_bAnnounced)
+ {
+ m_bAnnounced = true;
+ CMerasmus* pMerasmus = assert_cast< CMerasmus* >( TFGameRules()->GetActiveBoss() );
+ if (pMerasmus)
+ {
+ pMerasmus->PlayHighPrioritySound("Halloween.MerasmusWheelSpin");
+ }
+ else
+ {
+ TFGameRules()->BroadcastSound(255,"Halloween.MerasmusWheelSpin");
+ }
+ }
+
+ // Next update
+ SetNextThink( gpGlobals->curtime );
+}
+
+float CWheelOfDoom::CalcNextTickTime() const
+{
+ float flProgress = CalcSpinCompletion();
+ float flBias = Bias( flProgress, WHEEL_SPIN_TIME_BIAS );
+ return gpGlobals->curtime + (( 1 - flBias ) * WHEEL_FASTEST_SPIN_RATE) + (flBias) * WHEEL_SLOWEST_SPIN_RATE;
+}
+
+
+float CWheelOfDoom::CalcSpinCompletion() const
+{
+ const float& flDuration = WHEEL_SPIN_TIME;
+ return ( flDuration - (m_flStopSpinTime - gpGlobals->curtime) ) / flDuration;
+}
+
+void CWheelOfDoom::StartSpin( void )
+{
+ RemoveEffects( EF_NODRAW );
+ m_vecOtherWODs.Purge();
+ CBaseEntity *pOther = gEntList.FindEntityByClassname( NULL, "wheel_of_doom" );
+
+ // Play the sound of the wheel starting to spin
+ if( TFGameRules() )
+ {
+ TFGameRules()->BroadcastSound( 255, "Halloween.WheelofFate" );
+ }
+
+ // Gather all of the other WheelofDoom entities so we can control their screens as we spin
+ while ( pOther )
+ {
+ if( pOther != this )
+ {
+ m_vecOtherWODs.AddToTail( dynamic_cast<CWheelOfDoom*>( pOther ) );
+ }
+
+ // Change the target over
+ pOther = gEntList.FindEntityByClassname( pOther, "wheel_of_doom" );
+ }
+
+ if( m_bHasSpiral )
+ {
+ // Make our spirals show up and grow
+ m_pSpiral->GrowAndBecomeVisible();
+ FOR_EACH_VEC( m_vecOtherWODs, i )
+ {
+ if( m_vecOtherWODs[i]->m_bHasSpiral )
+ {
+ m_vecOtherWODs[i]->m_pSpiral->GrowAndBecomeVisible();
+ }
+ }
+ }
+
+ // Setup spin logic
+ m_flStopSpinTime = gpGlobals->curtime + WHEEL_SPIN_TIME;
+ m_flNextTickTime = CalcNextTickTime();
+ m_flNextAnnounceTime = gpGlobals->curtime + 1.6;
+ m_bAnnounced = false;
+
+ m_flFinishBroadcastingEffectTime = m_flStopSpinTime + 10.f;
+
+ SetThink( &CWheelOfDoom::SpinThink );
+ SetNextThink( gpGlobals->curtime );
+}
+
+
+void CWheelOfDoom::Spin( inputdata_t& inputdata )
+{
+ // Remember which effect was chosen so we can apply it once the spinning is done
+ m_pChosenEffect = GetRandomEffectWithFlags();
+ StartSpin();
+}
+
+
+CWheelOfDoom::WOD_BaseEffect* CWheelOfDoom::GetRandomEffectWithFlags()
+{
+ int nNumWhammys = 0;
+ int nNumGoodEffects = 0;
+ CUtlVector<WOD_BaseEffect*> vecMatchingEffects;
+
+ // Collect all of the effects that match our criteria.
+ // Buffs in the front of the vector and whammys on the end
+ FOR_EACH_VEC( m_vecEffects, i )
+ {
+ WOD_BaseEffect* pEffect = m_vecEffects[i];
+
+ if( pEffect->GetListFlags() & EFFECT_WHAMMY )
+ {
+ // Tally up all the whammys
+ ++nNumWhammys;
+ vecMatchingEffects.AddToTail( pEffect );
+ }
+ else
+ {
+ ++nNumGoodEffects;
+ vecMatchingEffects.AddToHead( pEffect );
+ }
+ }
+
+ // No matching effects. Return null
+ if( vecMatchingEffects.Count() == 0 )
+ {
+ return NULL;
+ }
+
+ // No Whammies? Just return a random one
+ if( nNumWhammys == 0 )
+ {
+ return vecMatchingEffects[RandomInt( 0, nNumGoodEffects-1 )];
+ }
+
+ // Given n good buffs, give a 1/n+1 chance of hitting a whammy
+ int nRand = RandomInt( 0, nNumGoodEffects );
+
+ // Rolled a whammy!
+ if( nRand == nNumGoodEffects )
+ {
+ //Roll again to find out which whammy we get
+ nRand = nNumGoodEffects + RandomInt( 0, nNumWhammys-1 );
+ }
+
+ return vecMatchingEffects[nRand];
+}
+
+
+void CWheelOfDoom::ClearAllEffects( inputdata_t& inputdata )
+{
+ m_EffectManager.ClearEffects();
+}
+
+
+bool CWheelOfDoom::IsDoneBoardcastingEffectSound() const
+{
+ return gpGlobals->curtime > m_flFinishBroadcastingEffectTime;
+}
+
+
+void CWheelOfDoom::DBG_ApplyEffectByName( const char* pszEffectName )
+{
+ FOR_EACH_VEC( m_vecEffects, i )
+ {
+ WOD_BaseEffect* pEffect = m_vecEffects[i];
+ if( FStrEq( pEffect->GetName(), pszEffectName ) )
+ {
+ m_EffectManager.AddEffect( pEffect, m_flDuration );
+ }
+ }
+}
+
+CWheelOfDoom::WOD_BaseEffect::WOD_BaseEffect()
+{
+ m_flExpireTime = 0;
+ m_pszEffectAnnouncementSound = NULL;
+ m_pszName = NULL;
+ m_iListFlags = 0;
+}
+
+void CWheelOfDoom::WOD_BaseEffect::InitEffect( float flDefaultDuration )
+{
+ m_flExpireTime = gpGlobals->curtime + flDefaultDuration;
+}
+
+void CWheelOfDoom::WOD_BaseEffect::SetListFlags( int iFlags )
+{
+ m_iListFlags = iFlags;
+}
+
+CWheelOfDoom::EffectManager::~EffectManager()
+{
+}
+
+int CWheelOfDoom::EffectManager::AddEffect( WOD_BaseEffect* pEffect, float flDefaultDuration )
+{
+ Assert( pEffect );
+
+ EffectData_t data;
+ data.m_pWheel = m_pWheel;
+ CollectPlayers( &data.m_vecPlayers );
+ pEffect->InitEffect( flDefaultDuration );
+ pEffect->ActivateEffect( data );
+
+ float flExpireDiff = pEffect->m_flExpireTime - gpGlobals->curtime;
+ DevMsg( "[WHEEL OF DOOM]\t Activating: \"%s\" set to expire in %3.2fs\n", pEffect->m_pszName, flExpireDiff );
+
+ if( TFGameRules() )
+ {
+ CMerasmus* pMerasmus = assert_cast< CMerasmus* >( TFGameRules()->GetActiveBoss() );
+ if (pMerasmus)
+ {
+ pMerasmus->PlayHighPrioritySound(pEffect->m_pszEffectAnnouncementSound);
+ }
+ else
+ {
+ TFGameRules()->BroadcastSound(255,pEffect->m_pszEffectAnnouncementSound);
+ }
+ // Update the HUD
+ TFGameRules()->SetHalloweenEffectStatus( int(pEffect->m_nSkin), flExpireDiff );
+ SpeakMagicConceptToAllPlayers(pEffect->m_pszEffectAnnouncementSound);
+ }
+
+ // Remember this effect
+ m_vecActiveEffects.AddToTail( pEffect );
+
+ return pEffect->m_nSkin;
+}
+
+void CWheelOfDoom::EffectManager::ApplyAllEffectsToPlayer( CTFPlayer* pPlayer )
+{
+ EffectData_t data;
+ data.m_pWheel = m_pWheel;
+ data.m_vecPlayers.AddToTail( pPlayer );
+
+ FOR_EACH_VEC( m_vecActiveEffects, i )
+ {
+ if( m_vecActiveEffects[i]->GetListFlags() & EFFECT_DOES_NOT_REAPPLY_ON_SPAWN )
+ continue;
+
+ m_vecActiveEffects[i]->ActivateEffect( data );
+ }
+}
+
+void CWheelOfDoom::EffectManager::ClearEffects()
+{
+ FOR_EACH_VEC( m_vecActiveEffects, i )
+ {
+ DevMsg( "[WHEEL OF DOOM]\t Deactivating: %s\n", m_vecActiveEffects[i]->m_pszName );
+
+ EffectData_t data;
+ data.m_pWheel = m_pWheel;
+ CollectPlayers( &data.m_vecPlayers );
+
+ m_vecActiveEffects[i]->DeactivateEffect( data );
+ }
+
+ m_vecActiveEffects.Purge();
+}
+
+bool CWheelOfDoom::EffectManager::UpdateAndClearExpiredEffects()
+{
+ bool bEffectExpired = false;
+
+ EffectData_t data;
+ data.m_pWheel = m_pWheel;
+ CollectPlayers( &data.m_vecPlayers );
+
+ FOR_EACH_VEC_BACK( m_vecActiveEffects, i )
+ {
+ // Check if the effect is expired. If so, run its DeactivateEffect and remove it
+ WOD_BaseEffect* pEffect = m_vecActiveEffects[i];
+ if( gpGlobals->curtime > pEffect->m_flExpireTime )
+ {
+ DevMsg( "[WHEEL OF DOOM]\t Effect expired: %s\n", pEffect->m_pszName );
+ bEffectExpired = true;
+
+ pEffect->DeactivateEffect( data );
+
+ m_vecActiveEffects.Remove( i );
+ }
+ else // If it's not expired, then update
+ {
+ pEffect->UpdateEffect( data );
+ }
+ }
+
+ return bEffectExpired;
+}
+
+
+void CWheelOfDoom::EffectManager::Precache()
+{
+ FOR_EACH_VEC( m_vecActiveEffects, i )
+ {
+ PrecacheScriptSound( m_vecActiveEffects[i]->m_pszEffectAnnouncementSound );
+ }
+}
+
+void CWheelOfDoom::SpeakMagicConceptToAllPlayers( const char* pszEffect )
+{
+ int iConcept = -1;
+
+ if ( !V_stricmp( pszEffect, "Halloween.MerasmusWheelBigHead") )
+ {
+ iConcept = MP_CONCEPT_MAGIC_BIGHEAD;
+ }
+ else if ( !V_stricmp( pszEffect, "Halloween.MerasmusWheelShrunkHead") )
+ {
+ iConcept = MP_CONCEPT_MAGIC_SMALLHEAD;
+ }
+ else if ( !V_stricmp( pszEffect, "Halloween.MerasmusWheelGravity") )
+ {
+ iConcept = MP_CONCEPT_MAGIC_GRAVITY;
+ }
+ else if ( (!V_stricmp( pszEffect, "Halloween.MerasmusWheelCrits")) || (!V_stricmp( pszEffect, "Halloween.MerasmusWheelUber")) || (!V_stricmp( pszEffect, "Halloween.MerasmusWheelSuperSpeed")))
+ {
+ iConcept = MP_CONCEPT_MAGIC_GOOD;
+ }
+ else if ( !V_stricmp( pszEffect, "Halloween.MerasmusWheelDance") )
+ {
+ iConcept = MP_CONCEPT_MAGIC_DANCE;
+ }
+
+ if (iConcept >= 0)
+ {
+ CUtlVector< CTFPlayer * > playerVector;
+ CollectPlayers( &playerVector );
+ FOR_EACH_VEC( playerVector, i )
+ {
+ playerVector[i]->SpeakConceptIfAllowed(iConcept);
+ }
+ }
+}
+
+
+
+void CWheelOfDoom::ApplyAttributeToAllPlayers( const char* pszAttribName, float flValue )
+{
+ CUtlVector< CTFPlayer * > playerVector;
+ CollectPlayers( &playerVector );
+ FOR_EACH_VEC( playerVector, i )
+ {
+ ApplyAttributeToPlayer( playerVector[i], pszAttribName, flValue );
+ }
+}
+
+void CWheelOfDoom::ApplyAttributeToPlayer( CTFPlayer* pPlayer, const char* pszAttribName, float flValue )
+{
+ Assert( pPlayer );
+
+ const CEconItemAttributeDefinition *pDef = GetItemSchema()->GetAttributeDefinitionByName( pszAttribName );
+
+ pPlayer->GetAttributeList()->SetRuntimeAttributeValue( pDef, flValue );
+ pPlayer->TeamFortress_SetSpeed();
+}
+
+void CWheelOfDoom::RemoveAttributeFromAllPlayers( const char* pszAttributeName )
+{
+ const CEconItemAttributeDefinition *pDef = GetItemSchema()->GetAttributeDefinitionByName( pszAttributeName );
+
+ CUtlVector< CTFPlayer * > playerVector;
+ CollectPlayers( &playerVector );
+ FOR_EACH_VEC( playerVector, i )
+ {
+ CTFPlayer* pPlayer = playerVector[i];
+ pPlayer->GetAttributeList()->RemoveAttribute( pDef );
+ }
+}
+
+void CWheelOfDoom::RemoveAttributeFromPlayer( CTFPlayer* pPlayer, const char* pszAttribName )
+{
+ const CEconItemAttributeDefinition *pDef = GetItemSchema()->GetAttributeDefinitionByName( pszAttribName );
+ pPlayer->GetAttributeList()->RemoveAttribute( pDef );
+}
+
+void CWheelOfDoom::WOD_UberEffect::InitEffect( float flDefaultExpireTime )
+{
+ m_flExpireTime = gpGlobals->curtime + Min( flDefaultExpireTime, 10.f );
+}
+
+void CWheelOfDoom::WOD_UberEffect::ActivateEffect( EffectData_t& data )
+{
+ float flDuration = m_flExpireTime - gpGlobals->curtime;
+
+ FOR_EACH_VEC( data.m_vecPlayers, i )
+ {
+ CTFPlayer* pPlayer = data.m_vecPlayers[i];
+ pPlayer->m_Shared.AddCond( TF_COND_INVULNERABLE, flDuration );
+ }
+}
+
+
+void CWheelOfDoom::WOD_CritsEffect::ActivateEffect( EffectData_t& data )
+{
+ float flDuration = m_flExpireTime - gpGlobals->curtime;
+
+ FOR_EACH_VEC( data.m_vecPlayers, i )
+ {
+ CTFPlayer* pPlayer = data.m_vecPlayers[i];
+ pPlayer->m_Shared.AddCond( TF_COND_CRITBOOSTED_PUMPKIN, flDuration );
+ }
+}
+
+
+void CWheelOfDoom::WOD_SuperSpeedEffect::ActivateEffect( EffectData_t& data )
+{
+ FOR_EACH_VEC( data.m_vecPlayers, i )
+ {
+ CTFPlayer* pPlayer = data.m_vecPlayers[i];
+ CWheelOfDoom::ApplyAttributeToPlayer( pPlayer, "major move speed bonus", 2.f );
+ }
+}
+
+
+void CWheelOfDoom::WOD_SuperSpeedEffect::DeactivateEffect( EffectData_t& data )
+{
+ FOR_EACH_VEC( data.m_vecPlayers, i )
+ {
+ CTFPlayer* pPlayer = data.m_vecPlayers[i];
+ CWheelOfDoom::RemoveAttributeFromPlayer( pPlayer, "major move speed bonus" );
+ // Recalc our max speed
+ pPlayer->TeamFortress_SetSpeed();
+ }
+}
+
+
+void CWheelOfDoom::WOD_SuperJumpEffect::ActivateEffect( EffectData_t& data )
+{
+ FOR_EACH_VEC( data.m_vecPlayers, i )
+ {
+ CTFPlayer* pPlayer = data.m_vecPlayers[i];
+ CWheelOfDoom::ApplyAttributeToPlayer( pPlayer, "major increased jump height", 3.f );
+ CWheelOfDoom::ApplyAttributeToPlayer( pPlayer, "cancel falling damage", 1.f );
+ }
+}
+
+void CWheelOfDoom::WOD_SuperJumpEffect::DeactivateEffect( EffectData_t& data )
+{
+ FOR_EACH_VEC( data.m_vecPlayers, i )
+ {
+ CTFPlayer* pPlayer = data.m_vecPlayers[i];
+ CWheelOfDoom::RemoveAttributeFromPlayer( pPlayer, "major increased jump height" );
+ CWheelOfDoom::RemoveAttributeFromPlayer( pPlayer, "cancel falling damage" );
+ }
+}
+
+
+void CWheelOfDoom::WOD_BigHeadEffect::ActivateEffect( EffectData_t& data )
+{
+ FOR_EACH_VEC( data.m_vecPlayers, i )
+ {
+ CTFPlayer* pPlayer = data.m_vecPlayers[i];
+ ApplyAttributeToPlayer( pPlayer, "voice pitch scale", 0.85f );
+ ApplyAttributeToPlayer( pPlayer, "head scale", 3.f );
+ }
+}
+
+void CWheelOfDoom::WOD_BigHeadEffect::DeactivateEffect( EffectData_t& data )
+{
+ FOR_EACH_VEC( data.m_vecPlayers, i )
+ {
+ CTFPlayer* pPlayer = data.m_vecPlayers[i];
+ RemoveAttributeFromPlayer( pPlayer, "voice pitch scale" );
+ RemoveAttributeFromPlayer( pPlayer, "head scale" );
+ }
+}
+
+
+void CWheelOfDoom::WOD_SmallHeadEffect::ActivateEffect( EffectData_t& data )
+{
+ FOR_EACH_VEC( data.m_vecPlayers, i )
+ {
+ CTFPlayer* pPlayer = data.m_vecPlayers[i];
+ ApplyAttributeToPlayer( pPlayer, "voice pitch scale", 1.3f );
+ ApplyAttributeToPlayer( pPlayer, "head scale", 0.5f );
+ }
+}
+
+void CWheelOfDoom::WOD_SmallHeadEffect::DeactivateEffect( EffectData_t& data )
+{
+ FOR_EACH_VEC( data.m_vecPlayers, i )
+ {
+ CTFPlayer* pPlayer = data.m_vecPlayers[i];
+ RemoveAttributeFromPlayer( pPlayer, "voice pitch scale" );
+ RemoveAttributeFromPlayer( pPlayer, "head scale" );
+ }
+}
+
+
+
+void CWheelOfDoom::WOD_LowGravityEffect::ActivateEffect( EffectData_t& /*data*/ )
+{
+ if ( TFGameRules() )
+ {
+ TFGameRules()->SetGravityMultiplier( 0.25f );
+ }
+}
+
+void CWheelOfDoom::WOD_LowGravityEffect::DeactivateEffect( EffectData_t& /*data*/ )
+{
+ if ( TFGameRules() )
+ {
+ TFGameRules()->SetGravityMultiplier( 1.0f );
+ }
+}
+
+
+void CWheelOfDoom::WOD_Pee::ActivateEffect( EffectData_t& data )
+{
+ m_vecClouds.Purge();
+
+ // Collect all of the "spawn_cloud" entities
+ CBaseEntity *pOther = gEntList.FindEntityByName( NULL, "spawn_cloud" );
+ while( pOther )
+ {
+ m_vecClouds.AddToTail( pOther );
+ pOther = gEntList.FindEntityByName( pOther, "spawn_cloud" );
+ }
+
+ m_flNextPeeTime = gpGlobals->curtime + 0.25f;
+}
+
+void CWheelOfDoom::WOD_Pee::UpdateEffect( EffectData_t& data )
+{
+ if( gpGlobals->curtime < m_flNextPeeTime || m_vecClouds.Count() == 0 )
+ return;
+
+ m_flNextPeeTime = gpGlobals->curtime + RandomFloat(0.2f, 0.5f);
+
+ // Choose one at random
+ int nRandIndex = RandomInt( 0, m_vecClouds.Count() - 1 );
+ CBaseEntity* pCloud = m_vecClouds[nRandIndex];
+
+ // Get a random point within the brush
+ Vector vWorldMins = pCloud->WorldAlignMins();
+ Vector vWorldMaxs = pCloud->WorldAlignMaxs();
+ Vector vBoxMin = pCloud->GetAbsOrigin() + vWorldMins;
+ Vector vBoxMax = pCloud->GetAbsOrigin() + vWorldMaxs;
+ Vector vRandomPos( RandomFloat( vBoxMin.x, vBoxMax.x ),
+ RandomFloat( vBoxMin.y, vBoxMax.y ),
+ RandomFloat( vBoxMin.z, vBoxMax.z ) );
+
+ // Drop some pee
+ CTFProjectile_Jar *pGrenade = static_cast<CTFProjectile_Jar*>( CBaseEntity::CreateNoSpawn( "tf_projectile_jar", vRandomPos, QAngle(0,0,0), NULL ) );
+ DispatchSpawn( pGrenade );
+
+ // Random angular impulse
+ Vector angImpulse( RandomFloat( -300.f, 300.f ),
+ RandomFloat( -300.f, 300.f ),
+ RandomFloat( -300.f, 300.f ) );
+
+ // Add some spin
+ IPhysicsObject *pPhysicsObject = pGrenade->VPhysicsGetObject();
+ if ( pPhysicsObject )
+ {
+ pPhysicsObject->AddVelocity( &vec3_origin, &angImpulse );
+ }
+}
+
+
+void CWheelOfDoom::WOD_Burn::InitEffect( float flDefaultDuration )
+{
+ m_flExpireTime = gpGlobals->curtime + 10.f;
+}
+
+
+void CWheelOfDoom::WOD_Burn::ActivateEffect( EffectData_t& data )
+{
+ FOR_EACH_VEC( data.m_vecPlayers, i )
+ {
+ CTFPlayer* pPlayer = data.m_vecPlayers[i];
+ pPlayer->m_Shared.SelfBurn( Min( data.m_pWheel->GetDuration(), 10.f ) );
+ }
+}
+
+
+void CWheelOfDoom::WOD_Ghosts::ActivateEffect( EffectData_t& data )
+{
+ if( TFGameRules() )
+ {
+ TFGameRules()->BeginHaunting( 4, data.m_pWheel->GetDuration() / 2.f, data.m_pWheel->GetDuration() );
+ }
+}
+
+void CWheelOfDoom::WOD_Ghosts::DeactivateEffect( EffectData_t& data )
+{
+ FOR_EACH_VEC( data.m_vecPlayers, i )
+ {
+ CTFPlayer* pPlayer = data.m_vecPlayers[i];
+ pPlayer->m_Shared.RemoveCond( TF_COND_BURNING );
+ }
+}
+
+void CWheelOfDoom::WOD_Dance::InitEffect( float /*flDefaultExpireTime*/ )
+{
+ // Don't do the same order every time
+ m_iCurrentMerasmusCreateInfo = RandomInt( 0, 1 );
+
+ // Ignore the passed in expire time. We want to have the buff for 5 seconds with 2 taunt cycles
+ m_flExpireTime = gpGlobals->curtime + 8.f;
+ m_flNextDanceTime = gpGlobals->curtime + 1.5f;
+
+ m_vecDancers.PurgeAndDeleteElements();
+
+ CUtlVector< CTFPlayer * > playerVector;
+ CollectPlayers( &playerVector, TEAM_ANY, true );
+
+ // We'll calculate the average position and place merasmus there
+ float aMerasmusY[2] = { FLT_MAX, -FLT_MAX };
+ float flMerasmusX = 0.0f, flMerasmusZ = 0.0f;
+ int nNumPositions = 0; // Keep a separate count for the average, since there's a case pOther can be NULL below
+
+ FOR_EACH_VEC( playerVector, i )
+ {
+ CTFPlayer* pPlayer = playerVector[i];
+
+ // Get our params
+ const char* pzsTeam = pPlayer->GetTeamNumber() == TF_TEAM_RED ? "red" : "blue";
+ int nInfoNumber = GetNumOFTeamDancing( pPlayer->GetTeamNumber() );
+
+ // Cook up the name of the entity we want
+ const char* pszTargetName = CFmtStr( "dance_teleport_%s%d", pzsTeam, nInfoNumber );
+ // If we got it, then do the magic
+ CBaseEntity *pOther = gEntList.FindEntityByName( NULL, pszTargetName );
+ if( pOther )
+ {
+ Dancer_t* dancer = new Dancer_t();
+
+ dancer->m_vecPos = pOther->GetAbsOrigin();
+ dancer->m_vecAngles = pOther->GetAbsAngles();
+ dancer->m_hPlayer.Set( pPlayer );
+
+ // Average in the player's position
+ aMerasmusY[0] = MIN( aMerasmusY[0], dancer->m_vecPos.y );
+ aMerasmusY[1] = MAX( aMerasmusY[1], dancer->m_vecPos.y );
+ flMerasmusX += dancer->m_vecPos.x;
+ flMerasmusZ = dancer->m_vecPos.z;
+ ++nNumPositions;
+
+ // Slam them, for the jam
+ SlamPosAndAngles( pPlayer, dancer->m_vecPos, dancer->m_vecAngles );
+
+ // Force the halloween taunt. This will force the player to stand still.
+ pPlayer->m_Shared.AddCond( TF_COND_HALLOWEEN_THRILLER );
+
+ // Remember we're effecting this player
+ m_vecDancers.AddToTail( dancer );
+ }
+ }
+
+ // Average out the Merasmus position
+ flMerasmusX /= nNumPositions;
+
+ m_vecMerasmusDancerCreateInfos.AddToTail( MerasmusCreateInfo_t( Vector( flMerasmusX, aMerasmusY[0], flMerasmusZ ), QAngle( 0.0f, 90.0f, 0.0f ) ) );
+ m_vecMerasmusDancerCreateInfos.AddToTail( MerasmusCreateInfo_t( Vector( flMerasmusX, aMerasmusY[1], flMerasmusZ ), QAngle( 0.0f,-90.0f, 0.0f ) ) );
+}
+
+
+void CWheelOfDoom::WOD_Dance::UpdateEffect( EffectData_t& /*data*/ )
+{
+ bool bShouldSlam = ( m_flNextDanceTime - gpGlobals->curtime ) < 0.2f;
+
+ bool bShouldTaunt = false;
+ if( gpGlobals->curtime > m_flNextDanceTime )
+ {
+ bShouldTaunt = true;
+ m_flNextDanceTime = gpGlobals->curtime + 3.5f;
+ }
+
+ FOR_EACH_VEC_BACK( m_vecDancers, i )
+ {
+ Dancer_t* dancer = m_vecDancers[i];
+ CTFPlayer* pPlayer = dancer->m_hPlayer.Get();
+
+ // This players is no more (disconnected). Forget about them.
+ if( pPlayer == NULL )
+ {
+ delete dancer;
+ m_vecDancers.Remove(i);
+ continue;
+ }
+
+ if( bShouldTaunt )
+ {
+ pPlayer->Taunt();
+
+ }
+
+ if( bShouldSlam )
+ {
+ // Slam them, for the jam
+ SlamPosAndAngles( pPlayer, dancer->m_vecPos, dancer->m_vecAngles );
+ }
+ }
+
+ // Party time.
+ if ( bShouldTaunt )
+ {
+ if ( m_hMerasmusDancer )
+ {
+ m_hMerasmusDancer->Vanish(); // Poof!
+ }
+
+ // Create a new Merasmus
+ const MerasmusCreateInfo_t &info = m_vecMerasmusDancerCreateInfos[ m_iCurrentMerasmusCreateInfo % m_vecMerasmusDancerCreateInfos.Count() ];
+ m_hMerasmusDancer = (CMerasmusDancer *)CBaseEntity::Create( "merasmus_dancer", info.m_vecPos, info.m_vecAngles );
+
+ if ( m_hMerasmusDancer )
+ {
+ m_hMerasmusDancer->Dance();
+ }
+
+ // Move to the next location
+ m_iCurrentMerasmusCreateInfo = ( m_iCurrentMerasmusCreateInfo + 1 ) % m_vecMerasmusDancerCreateInfos.Count();
+ }
+}
+
+void CWheelOfDoom::WOD_Dance::DeactivateEffect( EffectData_t& data )
+{
+ FOR_EACH_VEC( data.m_vecPlayers, i )
+ {
+ CTFPlayer* pPlayer = data.m_vecPlayers[i];
+ pPlayer->m_Shared.RemoveCond( TF_COND_HALLOWEEN_THRILLER );
+ }
+
+ m_vecDancers.Purge();
+
+ if ( m_hMerasmusDancer )
+ {
+ m_hMerasmusDancer->BlastOff();
+ m_vecMerasmusDancerCreateInfos.Purge();
+ }
+}
+
+
+int CWheelOfDoom::WOD_Dance::GetNumOFTeamDancing( int nTeam ) const
+{
+ int nCount = 0;
+ FOR_EACH_VEC( m_vecDancers, i )
+ {
+ if( m_vecDancers[i]->m_hPlayer.Get()->GetTeamNumber() == nTeam )
+ nCount++;
+ }
+
+ return nCount;
+}
+
+void CWheelOfDoom::WOD_Dance::SlamPosAndAngles( CTFPlayer* pPlayer, const Vector& vPos, const QAngle& vAng )
+{
+ // This calls: SetAbsOrigin(), SetAbsVelocity( vec3_origin ), SetLocalAngles(), SnapEyeAngles()
+ pPlayer->Teleport( &vPos, &vAng, &vec3_origin );
+ pPlayer->pl.v_angle = vAng;
+} \ No newline at end of file