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