Team Fortress 2 Source Code as on 22/4/2020
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.
 
 
 
 
 
 

1117 lines
28 KiB

//========= 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;
}