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.

1116 lines
28 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Auto Repair
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "tf/halloween/merasmus/merasmus.h"
  9. #include "tf/halloween/merasmus/merasmus_dancer.h"
  10. #include "tf_gamerules.h"
  11. #include "tf_weapon_jar.h"
  12. #include "tf_wheel_of_doom.h"
  13. LINK_ENTITY_TO_CLASS( wheel_of_doom, CWheelOfDoom );
  14. // Data Description
  15. BEGIN_DATADESC( CWheelOfDoom )
  16. // Keyfields
  17. DEFINE_KEYFIELD( m_flDuration, FIELD_FLOAT, "effect_duration" ),
  18. DEFINE_KEYFIELD( m_bHasSpiral, FIELD_BOOLEAN, "has_spiral" ),
  19. DEFINE_INPUTFUNC( FIELD_STRING, "Spin", Spin ),
  20. DEFINE_INPUTFUNC( FIELD_STRING, "ClearAllEffects", ClearAllEffects ),
  21. // Outputs
  22. DEFINE_OUTPUT( m_EffectApplied, "OnEffectApplied" ),
  23. DEFINE_OUTPUT( m_EffectExpired, "OnEffectExpired" ),
  24. END_DATADESC()
  25. extern ConVar sv_gravity;
  26. #define WHEEL_SPIN_TIME 5.75f
  27. #define WHEEL_SPIN_TIME_BIAS 0.3f
  28. #define WHEEL_FASTEST_SPIN_RATE 0.1f
  29. #define WHEEL_SLOWEST_SPIN_RATE 0.55f
  30. #define WHEEL_SPIRAL_GROW_RATE 0.55f
  31. #define WHEEL_SPIRAL_SHRINK_RATE 0.55f
  32. #define EFFECT_WHAMMY 1<<0
  33. #define EFFECT_DOES_NOT_REAPPLY_ON_SPAWN 1<<1
  34. class CWheelOfDoomSpiral : public CBaseAnimating
  35. {
  36. DECLARE_CLASS( CWheelOfDoom, CBaseAnimating );
  37. DECLARE_DATADESC();
  38. public:
  39. CWheelOfDoomSpiral()
  40. {
  41. m_flScale = 0.f;
  42. SetModelScale( 0.f );
  43. }
  44. virtual void Spawn()
  45. {
  46. SetThink( NULL );
  47. }
  48. virtual void Precache()
  49. {
  50. PrecacheModel( "models/props_lakeside_event/wof_plane2.mdl" );
  51. }
  52. void GrowAndBecomeVisible()
  53. {
  54. RemoveEffects( EF_NODRAW );
  55. SetThink( &CWheelOfDoomSpiral::GrowThink );
  56. SetNextThink( gpGlobals->curtime );
  57. }
  58. void ShrinkAndHide()
  59. {
  60. SetThink( &CWheelOfDoomSpiral::ShrinkThink );
  61. SetNextThink( gpGlobals->curtime );
  62. }
  63. private:
  64. void GrowThink()
  65. {
  66. // Grow ourselves over time
  67. m_flScale += WHEEL_SPIRAL_GROW_RATE * gpGlobals->frametime;
  68. if( m_flScale >= 1.f )
  69. {
  70. m_flScale = 1.f;
  71. SetThink( NULL );
  72. }
  73. SetModelScale( m_flScale );
  74. SetNextThink( gpGlobals->curtime );
  75. }
  76. void ShrinkThink()
  77. {
  78. // Shrink ourselves over time
  79. m_flScale -= WHEEL_SPIRAL_SHRINK_RATE * gpGlobals->frametime;
  80. if( m_flScale <= 0.f )
  81. {
  82. m_flScale = 0.f;
  83. AddEffects( EF_NODRAW );
  84. SetThink( NULL );
  85. }
  86. SetModelScale( m_flScale );
  87. SetNextThink( gpGlobals->curtime );
  88. }
  89. float m_flScale;
  90. };
  91. LINK_ENTITY_TO_CLASS( wheel_of_doom_spiral, CWheelOfDoomSpiral );
  92. // Data Description
  93. BEGIN_DATADESC( CWheelOfDoomSpiral )
  94. END_DATADESC()
  95. #ifdef STAGING_ONLY
  96. static void ProcWheelEffect( const CCommand &args )
  97. {
  98. CBaseEntity *pOther = gEntList.FindEntityByClassname( NULL, "wheel_of_doom" );
  99. CWheelOfDoom* pWheel = dynamic_cast<CWheelOfDoom*>( pOther );
  100. if( pWheel )
  101. {
  102. pWheel->DBG_ApplyEffectByName( args.ArgS() );
  103. }
  104. }
  105. ConCommand cc_proc_wheel_effect( "cc_proc_wheel_effect", ProcWheelEffect, "Force a Wheel of Doom entity to apply the specified effect" );
  106. #endif
  107. //-----------------------------------------------------------------------------
  108. // Purpose:
  109. //-----------------------------------------------------------------------------
  110. CWheelOfDoom::CWheelOfDoom( void ) :
  111. m_EffectManager( this ),
  112. m_pChosenEffect( NULL ),
  113. m_pSpiral( NULL ),
  114. m_flFinishBroadcastingEffectTime( 0.f )
  115. {
  116. AddEffects( EF_NODRAW );
  117. RegisterEffect( new WOD_UberEffect() );
  118. RegisterEffect( new WOD_CritsEffect() );
  119. RegisterEffect( new WOD_SuperSpeedEffect() );
  120. RegisterEffect( new WOD_SuperJumpEffect() );
  121. RegisterEffect( new WOD_BigHeadEffect() );
  122. RegisterEffect( new WOD_SmallHeadEffect() );
  123. RegisterEffect( new WOD_LowGravityEffect() );
  124. RegisterEffect( new WOD_Dance(), EFFECT_DOES_NOT_REAPPLY_ON_SPAWN );
  125. RegisterEffect( new WOD_Pee(), EFFECT_WHAMMY | EFFECT_DOES_NOT_REAPPLY_ON_SPAWN );
  126. RegisterEffect( new WOD_Burn(), EFFECT_WHAMMY | EFFECT_DOES_NOT_REAPPLY_ON_SPAWN );
  127. RegisterEffect( new WOD_Ghosts(), EFFECT_WHAMMY | EFFECT_DOES_NOT_REAPPLY_ON_SPAWN );
  128. }
  129. CWheelOfDoom::~CWheelOfDoom( void )
  130. {
  131. m_EffectManager.ClearEffects();
  132. m_vecEffects.PurgeAndDeleteElements();
  133. }
  134. void CWheelOfDoom::RegisterEffect( WOD_BaseEffect* pEffect, int nFlags )
  135. {
  136. pEffect->SetListFlags( nFlags );
  137. m_vecEffects.AddToTail( pEffect );
  138. }
  139. //-----------------------------------------------------------------------------
  140. // Purpose:
  141. //-----------------------------------------------------------------------------
  142. void CWheelOfDoom::Precache( void )
  143. {
  144. PrecacheModel( GetScreenModelName() );
  145. PrecacheModel( "models/props_lakeside_event/wof_plane2.mdl" );
  146. PrecacheScriptSound( "Halloween.WheelofFate" );
  147. PrecacheScriptSound( "Halloween.dance_howl" );
  148. PrecacheScriptSound( "Halloween.dance_loop" );
  149. PrecacheScriptSound( "Halloween.HeadlessBossAxeHitWorld" );
  150. PrecacheScriptSound( "Halloween.LightsOn" );
  151. PrecacheScriptSound( "Weapon_StickyBombLauncher.BoltForward" );
  152. PrecacheScriptSound( "TFPlayer.InvulnerableOff" );
  153. m_EffectManager.Precache();
  154. }
  155. const char* CWheelOfDoom::GetScreenModelName()
  156. {
  157. return "models/props_lakeside_event/buff_plane.mdl";
  158. }
  159. //-----------------------------------------------------------------------------
  160. // Purpose:
  161. //-----------------------------------------------------------------------------
  162. void CWheelOfDoom::Spawn( void )
  163. {
  164. Precache();
  165. SetModel( GetScreenModelName() );
  166. SetSolid( SOLID_BBOX );
  167. AddSolidFlags( FSOLID_NOT_SOLID );
  168. SetMoveType( MOVETYPE_NONE );
  169. ListenForGameEvent( "player_spawn" );
  170. SetThink( &CWheelOfDoom::IdleThink );
  171. SetNextThink( gpGlobals->curtime + 0.1f );
  172. if ( TFGameRules() != NULL )
  173. {
  174. TFGameRules()->ClearHalloweenEffectStatus();
  175. }
  176. if( m_bHasSpiral )
  177. {
  178. m_pSpiral = assert_cast<CWheelOfDoomSpiral*>( CreateEntityByName( "wheel_of_doom_spiral" ) );
  179. Assert( m_pSpiral );
  180. m_pSpiral->SetModel( "models/props_lakeside_event/wof_plane2.mdl" );
  181. m_pSpiral->AddEffects( EF_NODRAW );
  182. m_pSpiral->SetAbsOrigin( GetAbsOrigin() );
  183. m_pSpiral->SetAbsAngles( GetAbsAngles() );
  184. m_pSpiral->SetParent( this );
  185. }
  186. }
  187. void CWheelOfDoom::FireGameEvent( IGameEvent *gameEvent )
  188. {
  189. if( FStrEq( gameEvent->GetName(), "player_spawn" ) )
  190. {
  191. const int nUserID = gameEvent->GetInt( "userid" );
  192. CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByUserId( nUserID ) );
  193. if( pPlayer )
  194. {
  195. m_EffectManager.ApplyAllEffectsToPlayer( pPlayer );
  196. }
  197. }
  198. }
  199. //-----------------------------------------------------------------------------
  200. // Purpose:
  201. //-----------------------------------------------------------------------------
  202. void CWheelOfDoom::IdleThink( void )
  203. {
  204. if( m_EffectManager.UpdateAndClearExpiredEffects() )
  205. {
  206. AddEffects( EF_NODRAW );
  207. m_EffectExpired.FireOutput( this, this );
  208. // Clear the skin to blank
  209. SetSkin( 0 );
  210. // Clear the HUD
  211. TFGameRules()->ClearHalloweenEffectStatus();
  212. if( m_bHasSpiral )
  213. {
  214. // Make our spirals shrink down and hide themselves
  215. m_pSpiral->ShrinkAndHide();
  216. FOR_EACH_VEC( m_vecOtherWODs, i )
  217. {
  218. if( m_vecOtherWODs[i]->m_bHasSpiral )
  219. {
  220. m_vecOtherWODs[i]->m_pSpiral->ShrinkAndHide();
  221. }
  222. }
  223. }
  224. }
  225. //Next update
  226. SetNextThink( gpGlobals->curtime + 0.1f );
  227. }
  228. void CWheelOfDoom::SetSkin( int nSkin )
  229. {
  230. m_nSkin = nSkin;
  231. FOR_EACH_VEC( m_vecOtherWODs, i )
  232. {
  233. m_vecOtherWODs[i]->m_nSkin = nSkin;
  234. }
  235. }
  236. void CWheelOfDoom::SetScale( float flScale )
  237. {
  238. SetModelScale( flScale );
  239. FOR_EACH_VEC( m_vecOtherWODs, i )
  240. {
  241. m_vecOtherWODs[i]->SetModelScale( flScale );
  242. }
  243. }
  244. void CWheelOfDoom::PlaySound( const char* pszSound )
  245. {
  246. EmitSound( pszSound );
  247. FOR_EACH_VEC( m_vecOtherWODs, i )
  248. {
  249. m_vecOtherWODs[i]->EmitSound( pszSound );
  250. }
  251. }
  252. void CWheelOfDoom::SpinThink( void )
  253. {
  254. if( m_EffectManager.UpdateAndClearExpiredEffects() )
  255. {
  256. m_EffectExpired.FireOutput( this, this );
  257. // Clear the HUD
  258. TFGameRules()->ClearHalloweenEffectStatus();
  259. }
  260. // Are we done spinning?
  261. if( gpGlobals->curtime > m_flStopSpinTime )
  262. {
  263. if( gpGlobals->curtime > m_flStopSpinTime + 1.f )
  264. {
  265. //PlaySound( "Halloween.LightsOn" );
  266. SetScale( 1.f );
  267. m_EffectApplied.FireOutput( this, this );
  268. // Apply the effect!
  269. SetSkin( m_EffectManager.AddEffect( m_pChosenEffect, m_flDuration ) );
  270. SetThink( &CWheelOfDoom::IdleThink );
  271. m_flStopSpinTime = 0.f;
  272. m_flNextTickTime = 0.f;
  273. m_pChosenEffect = NULL;
  274. }
  275. }
  276. // Is it time for another tick of the wheel?
  277. else if( gpGlobals->curtime > m_flNextTickTime )
  278. {
  279. int nRandSkin = RandomInt( 1, EFFECT_COUNT-1 );
  280. if( nRandSkin == m_nSkin )
  281. ++nRandSkin;
  282. // Roll over to 1. 0 is blank so skip that one.
  283. if( nRandSkin == EFFECT_COUNT )
  284. nRandSkin = 1;
  285. SetSkin( nRandSkin );
  286. SetScale( RemapVal( CalcSpinCompletion(), 0.f, 1.f, 0.3f, 0.9f) );
  287. m_flNextTickTime = CalcNextTickTime();
  288. }
  289. //Is it time for Merasmus to announce the spin?
  290. if (gpGlobals->curtime > m_flNextAnnounceTime && !m_bAnnounced)
  291. {
  292. m_bAnnounced = true;
  293. CMerasmus* pMerasmus = assert_cast< CMerasmus* >( TFGameRules()->GetActiveBoss() );
  294. if (pMerasmus)
  295. {
  296. pMerasmus->PlayHighPrioritySound("Halloween.MerasmusWheelSpin");
  297. }
  298. else
  299. {
  300. TFGameRules()->BroadcastSound(255,"Halloween.MerasmusWheelSpin");
  301. }
  302. }
  303. // Next update
  304. SetNextThink( gpGlobals->curtime );
  305. }
  306. float CWheelOfDoom::CalcNextTickTime() const
  307. {
  308. float flProgress = CalcSpinCompletion();
  309. float flBias = Bias( flProgress, WHEEL_SPIN_TIME_BIAS );
  310. return gpGlobals->curtime + (( 1 - flBias ) * WHEEL_FASTEST_SPIN_RATE) + (flBias) * WHEEL_SLOWEST_SPIN_RATE;
  311. }
  312. float CWheelOfDoom::CalcSpinCompletion() const
  313. {
  314. const float& flDuration = WHEEL_SPIN_TIME;
  315. return ( flDuration - (m_flStopSpinTime - gpGlobals->curtime) ) / flDuration;
  316. }
  317. void CWheelOfDoom::StartSpin( void )
  318. {
  319. RemoveEffects( EF_NODRAW );
  320. m_vecOtherWODs.Purge();
  321. CBaseEntity *pOther = gEntList.FindEntityByClassname( NULL, "wheel_of_doom" );
  322. // Play the sound of the wheel starting to spin
  323. if( TFGameRules() )
  324. {
  325. TFGameRules()->BroadcastSound( 255, "Halloween.WheelofFate" );
  326. }
  327. // Gather all of the other WheelofDoom entities so we can control their screens as we spin
  328. while ( pOther )
  329. {
  330. if( pOther != this )
  331. {
  332. m_vecOtherWODs.AddToTail( dynamic_cast<CWheelOfDoom*>( pOther ) );
  333. }
  334. // Change the target over
  335. pOther = gEntList.FindEntityByClassname( pOther, "wheel_of_doom" );
  336. }
  337. if( m_bHasSpiral )
  338. {
  339. // Make our spirals show up and grow
  340. m_pSpiral->GrowAndBecomeVisible();
  341. FOR_EACH_VEC( m_vecOtherWODs, i )
  342. {
  343. if( m_vecOtherWODs[i]->m_bHasSpiral )
  344. {
  345. m_vecOtherWODs[i]->m_pSpiral->GrowAndBecomeVisible();
  346. }
  347. }
  348. }
  349. // Setup spin logic
  350. m_flStopSpinTime = gpGlobals->curtime + WHEEL_SPIN_TIME;
  351. m_flNextTickTime = CalcNextTickTime();
  352. m_flNextAnnounceTime = gpGlobals->curtime + 1.6;
  353. m_bAnnounced = false;
  354. m_flFinishBroadcastingEffectTime = m_flStopSpinTime + 10.f;
  355. SetThink( &CWheelOfDoom::SpinThink );
  356. SetNextThink( gpGlobals->curtime );
  357. }
  358. void CWheelOfDoom::Spin( inputdata_t& inputdata )
  359. {
  360. // Remember which effect was chosen so we can apply it once the spinning is done
  361. m_pChosenEffect = GetRandomEffectWithFlags();
  362. StartSpin();
  363. }
  364. CWheelOfDoom::WOD_BaseEffect* CWheelOfDoom::GetRandomEffectWithFlags()
  365. {
  366. int nNumWhammys = 0;
  367. int nNumGoodEffects = 0;
  368. CUtlVector<WOD_BaseEffect*> vecMatchingEffects;
  369. // Collect all of the effects that match our criteria.
  370. // Buffs in the front of the vector and whammys on the end
  371. FOR_EACH_VEC( m_vecEffects, i )
  372. {
  373. WOD_BaseEffect* pEffect = m_vecEffects[i];
  374. if( pEffect->GetListFlags() & EFFECT_WHAMMY )
  375. {
  376. // Tally up all the whammys
  377. ++nNumWhammys;
  378. vecMatchingEffects.AddToTail( pEffect );
  379. }
  380. else
  381. {
  382. ++nNumGoodEffects;
  383. vecMatchingEffects.AddToHead( pEffect );
  384. }
  385. }
  386. // No matching effects. Return null
  387. if( vecMatchingEffects.Count() == 0 )
  388. {
  389. return NULL;
  390. }
  391. // No Whammies? Just return a random one
  392. if( nNumWhammys == 0 )
  393. {
  394. return vecMatchingEffects[RandomInt( 0, nNumGoodEffects-1 )];
  395. }
  396. // Given n good buffs, give a 1/n+1 chance of hitting a whammy
  397. int nRand = RandomInt( 0, nNumGoodEffects );
  398. // Rolled a whammy!
  399. if( nRand == nNumGoodEffects )
  400. {
  401. //Roll again to find out which whammy we get
  402. nRand = nNumGoodEffects + RandomInt( 0, nNumWhammys-1 );
  403. }
  404. return vecMatchingEffects[nRand];
  405. }
  406. void CWheelOfDoom::ClearAllEffects( inputdata_t& inputdata )
  407. {
  408. m_EffectManager.ClearEffects();
  409. }
  410. bool CWheelOfDoom::IsDoneBoardcastingEffectSound() const
  411. {
  412. return gpGlobals->curtime > m_flFinishBroadcastingEffectTime;
  413. }
  414. void CWheelOfDoom::DBG_ApplyEffectByName( const char* pszEffectName )
  415. {
  416. FOR_EACH_VEC( m_vecEffects, i )
  417. {
  418. WOD_BaseEffect* pEffect = m_vecEffects[i];
  419. if( FStrEq( pEffect->GetName(), pszEffectName ) )
  420. {
  421. m_EffectManager.AddEffect( pEffect, m_flDuration );
  422. }
  423. }
  424. }
  425. CWheelOfDoom::WOD_BaseEffect::WOD_BaseEffect()
  426. {
  427. m_flExpireTime = 0;
  428. m_pszEffectAnnouncementSound = NULL;
  429. m_pszName = NULL;
  430. m_iListFlags = 0;
  431. }
  432. void CWheelOfDoom::WOD_BaseEffect::InitEffect( float flDefaultDuration )
  433. {
  434. m_flExpireTime = gpGlobals->curtime + flDefaultDuration;
  435. }
  436. void CWheelOfDoom::WOD_BaseEffect::SetListFlags( int iFlags )
  437. {
  438. m_iListFlags = iFlags;
  439. }
  440. CWheelOfDoom::EffectManager::~EffectManager()
  441. {
  442. }
  443. int CWheelOfDoom::EffectManager::AddEffect( WOD_BaseEffect* pEffect, float flDefaultDuration )
  444. {
  445. Assert( pEffect );
  446. EffectData_t data;
  447. data.m_pWheel = m_pWheel;
  448. CollectPlayers( &data.m_vecPlayers );
  449. pEffect->InitEffect( flDefaultDuration );
  450. pEffect->ActivateEffect( data );
  451. float flExpireDiff = pEffect->m_flExpireTime - gpGlobals->curtime;
  452. DevMsg( "[WHEEL OF DOOM]\t Activating: \"%s\" set to expire in %3.2fs\n", pEffect->m_pszName, flExpireDiff );
  453. if( TFGameRules() )
  454. {
  455. CMerasmus* pMerasmus = assert_cast< CMerasmus* >( TFGameRules()->GetActiveBoss() );
  456. if (pMerasmus)
  457. {
  458. pMerasmus->PlayHighPrioritySound(pEffect->m_pszEffectAnnouncementSound);
  459. }
  460. else
  461. {
  462. TFGameRules()->BroadcastSound(255,pEffect->m_pszEffectAnnouncementSound);
  463. }
  464. // Update the HUD
  465. TFGameRules()->SetHalloweenEffectStatus( int(pEffect->m_nSkin), flExpireDiff );
  466. SpeakMagicConceptToAllPlayers(pEffect->m_pszEffectAnnouncementSound);
  467. }
  468. // Remember this effect
  469. m_vecActiveEffects.AddToTail( pEffect );
  470. return pEffect->m_nSkin;
  471. }
  472. void CWheelOfDoom::EffectManager::ApplyAllEffectsToPlayer( CTFPlayer* pPlayer )
  473. {
  474. EffectData_t data;
  475. data.m_pWheel = m_pWheel;
  476. data.m_vecPlayers.AddToTail( pPlayer );
  477. FOR_EACH_VEC( m_vecActiveEffects, i )
  478. {
  479. if( m_vecActiveEffects[i]->GetListFlags() & EFFECT_DOES_NOT_REAPPLY_ON_SPAWN )
  480. continue;
  481. m_vecActiveEffects[i]->ActivateEffect( data );
  482. }
  483. }
  484. void CWheelOfDoom::EffectManager::ClearEffects()
  485. {
  486. FOR_EACH_VEC( m_vecActiveEffects, i )
  487. {
  488. DevMsg( "[WHEEL OF DOOM]\t Deactivating: %s\n", m_vecActiveEffects[i]->m_pszName );
  489. EffectData_t data;
  490. data.m_pWheel = m_pWheel;
  491. CollectPlayers( &data.m_vecPlayers );
  492. m_vecActiveEffects[i]->DeactivateEffect( data );
  493. }
  494. m_vecActiveEffects.Purge();
  495. }
  496. bool CWheelOfDoom::EffectManager::UpdateAndClearExpiredEffects()
  497. {
  498. bool bEffectExpired = false;
  499. EffectData_t data;
  500. data.m_pWheel = m_pWheel;
  501. CollectPlayers( &data.m_vecPlayers );
  502. FOR_EACH_VEC_BACK( m_vecActiveEffects, i )
  503. {
  504. // Check if the effect is expired. If so, run its DeactivateEffect and remove it
  505. WOD_BaseEffect* pEffect = m_vecActiveEffects[i];
  506. if( gpGlobals->curtime > pEffect->m_flExpireTime )
  507. {
  508. DevMsg( "[WHEEL OF DOOM]\t Effect expired: %s\n", pEffect->m_pszName );
  509. bEffectExpired = true;
  510. pEffect->DeactivateEffect( data );
  511. m_vecActiveEffects.Remove( i );
  512. }
  513. else // If it's not expired, then update
  514. {
  515. pEffect->UpdateEffect( data );
  516. }
  517. }
  518. return bEffectExpired;
  519. }
  520. void CWheelOfDoom::EffectManager::Precache()
  521. {
  522. FOR_EACH_VEC( m_vecActiveEffects, i )
  523. {
  524. PrecacheScriptSound( m_vecActiveEffects[i]->m_pszEffectAnnouncementSound );
  525. }
  526. }
  527. void CWheelOfDoom::SpeakMagicConceptToAllPlayers( const char* pszEffect )
  528. {
  529. int iConcept = -1;
  530. if ( !V_stricmp( pszEffect, "Halloween.MerasmusWheelBigHead") )
  531. {
  532. iConcept = MP_CONCEPT_MAGIC_BIGHEAD;
  533. }
  534. else if ( !V_stricmp( pszEffect, "Halloween.MerasmusWheelShrunkHead") )
  535. {
  536. iConcept = MP_CONCEPT_MAGIC_SMALLHEAD;
  537. }
  538. else if ( !V_stricmp( pszEffect, "Halloween.MerasmusWheelGravity") )
  539. {
  540. iConcept = MP_CONCEPT_MAGIC_GRAVITY;
  541. }
  542. else if ( (!V_stricmp( pszEffect, "Halloween.MerasmusWheelCrits")) || (!V_stricmp( pszEffect, "Halloween.MerasmusWheelUber")) || (!V_stricmp( pszEffect, "Halloween.MerasmusWheelSuperSpeed")))
  543. {
  544. iConcept = MP_CONCEPT_MAGIC_GOOD;
  545. }
  546. else if ( !V_stricmp( pszEffect, "Halloween.MerasmusWheelDance") )
  547. {
  548. iConcept = MP_CONCEPT_MAGIC_DANCE;
  549. }
  550. if (iConcept >= 0)
  551. {
  552. CUtlVector< CTFPlayer * > playerVector;
  553. CollectPlayers( &playerVector );
  554. FOR_EACH_VEC( playerVector, i )
  555. {
  556. playerVector[i]->SpeakConceptIfAllowed(iConcept);
  557. }
  558. }
  559. }
  560. void CWheelOfDoom::ApplyAttributeToAllPlayers( const char* pszAttribName, float flValue )
  561. {
  562. CUtlVector< CTFPlayer * > playerVector;
  563. CollectPlayers( &playerVector );
  564. FOR_EACH_VEC( playerVector, i )
  565. {
  566. ApplyAttributeToPlayer( playerVector[i], pszAttribName, flValue );
  567. }
  568. }
  569. void CWheelOfDoom::ApplyAttributeToPlayer( CTFPlayer* pPlayer, const char* pszAttribName, float flValue )
  570. {
  571. Assert( pPlayer );
  572. const CEconItemAttributeDefinition *pDef = GetItemSchema()->GetAttributeDefinitionByName( pszAttribName );
  573. pPlayer->GetAttributeList()->SetRuntimeAttributeValue( pDef, flValue );
  574. pPlayer->TeamFortress_SetSpeed();
  575. }
  576. void CWheelOfDoom::RemoveAttributeFromAllPlayers( const char* pszAttributeName )
  577. {
  578. const CEconItemAttributeDefinition *pDef = GetItemSchema()->GetAttributeDefinitionByName( pszAttributeName );
  579. CUtlVector< CTFPlayer * > playerVector;
  580. CollectPlayers( &playerVector );
  581. FOR_EACH_VEC( playerVector, i )
  582. {
  583. CTFPlayer* pPlayer = playerVector[i];
  584. pPlayer->GetAttributeList()->RemoveAttribute( pDef );
  585. }
  586. }
  587. void CWheelOfDoom::RemoveAttributeFromPlayer( CTFPlayer* pPlayer, const char* pszAttribName )
  588. {
  589. const CEconItemAttributeDefinition *pDef = GetItemSchema()->GetAttributeDefinitionByName( pszAttribName );
  590. pPlayer->GetAttributeList()->RemoveAttribute( pDef );
  591. }
  592. void CWheelOfDoom::WOD_UberEffect::InitEffect( float flDefaultExpireTime )
  593. {
  594. m_flExpireTime = gpGlobals->curtime + Min( flDefaultExpireTime, 10.f );
  595. }
  596. void CWheelOfDoom::WOD_UberEffect::ActivateEffect( EffectData_t& data )
  597. {
  598. float flDuration = m_flExpireTime - gpGlobals->curtime;
  599. FOR_EACH_VEC( data.m_vecPlayers, i )
  600. {
  601. CTFPlayer* pPlayer = data.m_vecPlayers[i];
  602. pPlayer->m_Shared.AddCond( TF_COND_INVULNERABLE, flDuration );
  603. }
  604. }
  605. void CWheelOfDoom::WOD_CritsEffect::ActivateEffect( EffectData_t& data )
  606. {
  607. float flDuration = m_flExpireTime - gpGlobals->curtime;
  608. FOR_EACH_VEC( data.m_vecPlayers, i )
  609. {
  610. CTFPlayer* pPlayer = data.m_vecPlayers[i];
  611. pPlayer->m_Shared.AddCond( TF_COND_CRITBOOSTED_PUMPKIN, flDuration );
  612. }
  613. }
  614. void CWheelOfDoom::WOD_SuperSpeedEffect::ActivateEffect( EffectData_t& data )
  615. {
  616. FOR_EACH_VEC( data.m_vecPlayers, i )
  617. {
  618. CTFPlayer* pPlayer = data.m_vecPlayers[i];
  619. CWheelOfDoom::ApplyAttributeToPlayer( pPlayer, "major move speed bonus", 2.f );
  620. }
  621. }
  622. void CWheelOfDoom::WOD_SuperSpeedEffect::DeactivateEffect( EffectData_t& data )
  623. {
  624. FOR_EACH_VEC( data.m_vecPlayers, i )
  625. {
  626. CTFPlayer* pPlayer = data.m_vecPlayers[i];
  627. CWheelOfDoom::RemoveAttributeFromPlayer( pPlayer, "major move speed bonus" );
  628. // Recalc our max speed
  629. pPlayer->TeamFortress_SetSpeed();
  630. }
  631. }
  632. void CWheelOfDoom::WOD_SuperJumpEffect::ActivateEffect( EffectData_t& data )
  633. {
  634. FOR_EACH_VEC( data.m_vecPlayers, i )
  635. {
  636. CTFPlayer* pPlayer = data.m_vecPlayers[i];
  637. CWheelOfDoom::ApplyAttributeToPlayer( pPlayer, "major increased jump height", 3.f );
  638. CWheelOfDoom::ApplyAttributeToPlayer( pPlayer, "cancel falling damage", 1.f );
  639. }
  640. }
  641. void CWheelOfDoom::WOD_SuperJumpEffect::DeactivateEffect( EffectData_t& data )
  642. {
  643. FOR_EACH_VEC( data.m_vecPlayers, i )
  644. {
  645. CTFPlayer* pPlayer = data.m_vecPlayers[i];
  646. CWheelOfDoom::RemoveAttributeFromPlayer( pPlayer, "major increased jump height" );
  647. CWheelOfDoom::RemoveAttributeFromPlayer( pPlayer, "cancel falling damage" );
  648. }
  649. }
  650. void CWheelOfDoom::WOD_BigHeadEffect::ActivateEffect( EffectData_t& data )
  651. {
  652. FOR_EACH_VEC( data.m_vecPlayers, i )
  653. {
  654. CTFPlayer* pPlayer = data.m_vecPlayers[i];
  655. ApplyAttributeToPlayer( pPlayer, "voice pitch scale", 0.85f );
  656. ApplyAttributeToPlayer( pPlayer, "head scale", 3.f );
  657. }
  658. }
  659. void CWheelOfDoom::WOD_BigHeadEffect::DeactivateEffect( EffectData_t& data )
  660. {
  661. FOR_EACH_VEC( data.m_vecPlayers, i )
  662. {
  663. CTFPlayer* pPlayer = data.m_vecPlayers[i];
  664. RemoveAttributeFromPlayer( pPlayer, "voice pitch scale" );
  665. RemoveAttributeFromPlayer( pPlayer, "head scale" );
  666. }
  667. }
  668. void CWheelOfDoom::WOD_SmallHeadEffect::ActivateEffect( EffectData_t& data )
  669. {
  670. FOR_EACH_VEC( data.m_vecPlayers, i )
  671. {
  672. CTFPlayer* pPlayer = data.m_vecPlayers[i];
  673. ApplyAttributeToPlayer( pPlayer, "voice pitch scale", 1.3f );
  674. ApplyAttributeToPlayer( pPlayer, "head scale", 0.5f );
  675. }
  676. }
  677. void CWheelOfDoom::WOD_SmallHeadEffect::DeactivateEffect( EffectData_t& data )
  678. {
  679. FOR_EACH_VEC( data.m_vecPlayers, i )
  680. {
  681. CTFPlayer* pPlayer = data.m_vecPlayers[i];
  682. RemoveAttributeFromPlayer( pPlayer, "voice pitch scale" );
  683. RemoveAttributeFromPlayer( pPlayer, "head scale" );
  684. }
  685. }
  686. void CWheelOfDoom::WOD_LowGravityEffect::ActivateEffect( EffectData_t& /*data*/ )
  687. {
  688. if ( TFGameRules() )
  689. {
  690. TFGameRules()->SetGravityMultiplier( 0.25f );
  691. }
  692. }
  693. void CWheelOfDoom::WOD_LowGravityEffect::DeactivateEffect( EffectData_t& /*data*/ )
  694. {
  695. if ( TFGameRules() )
  696. {
  697. TFGameRules()->SetGravityMultiplier( 1.0f );
  698. }
  699. }
  700. void CWheelOfDoom::WOD_Pee::ActivateEffect( EffectData_t& data )
  701. {
  702. m_vecClouds.Purge();
  703. // Collect all of the "spawn_cloud" entities
  704. CBaseEntity *pOther = gEntList.FindEntityByName( NULL, "spawn_cloud" );
  705. while( pOther )
  706. {
  707. m_vecClouds.AddToTail( pOther );
  708. pOther = gEntList.FindEntityByName( pOther, "spawn_cloud" );
  709. }
  710. m_flNextPeeTime = gpGlobals->curtime + 0.25f;
  711. }
  712. void CWheelOfDoom::WOD_Pee::UpdateEffect( EffectData_t& data )
  713. {
  714. if( gpGlobals->curtime < m_flNextPeeTime || m_vecClouds.Count() == 0 )
  715. return;
  716. m_flNextPeeTime = gpGlobals->curtime + RandomFloat(0.2f, 0.5f);
  717. // Choose one at random
  718. int nRandIndex = RandomInt( 0, m_vecClouds.Count() - 1 );
  719. CBaseEntity* pCloud = m_vecClouds[nRandIndex];
  720. // Get a random point within the brush
  721. Vector vWorldMins = pCloud->WorldAlignMins();
  722. Vector vWorldMaxs = pCloud->WorldAlignMaxs();
  723. Vector vBoxMin = pCloud->GetAbsOrigin() + vWorldMins;
  724. Vector vBoxMax = pCloud->GetAbsOrigin() + vWorldMaxs;
  725. Vector vRandomPos( RandomFloat( vBoxMin.x, vBoxMax.x ),
  726. RandomFloat( vBoxMin.y, vBoxMax.y ),
  727. RandomFloat( vBoxMin.z, vBoxMax.z ) );
  728. // Drop some pee
  729. CTFProjectile_Jar *pGrenade = static_cast<CTFProjectile_Jar*>( CBaseEntity::CreateNoSpawn( "tf_projectile_jar", vRandomPos, QAngle(0,0,0), NULL ) );
  730. DispatchSpawn( pGrenade );
  731. // Random angular impulse
  732. Vector angImpulse( RandomFloat( -300.f, 300.f ),
  733. RandomFloat( -300.f, 300.f ),
  734. RandomFloat( -300.f, 300.f ) );
  735. // Add some spin
  736. IPhysicsObject *pPhysicsObject = pGrenade->VPhysicsGetObject();
  737. if ( pPhysicsObject )
  738. {
  739. pPhysicsObject->AddVelocity( &vec3_origin, &angImpulse );
  740. }
  741. }
  742. void CWheelOfDoom::WOD_Burn::InitEffect( float flDefaultDuration )
  743. {
  744. m_flExpireTime = gpGlobals->curtime + 10.f;
  745. }
  746. void CWheelOfDoom::WOD_Burn::ActivateEffect( EffectData_t& data )
  747. {
  748. FOR_EACH_VEC( data.m_vecPlayers, i )
  749. {
  750. CTFPlayer* pPlayer = data.m_vecPlayers[i];
  751. pPlayer->m_Shared.SelfBurn( Min( data.m_pWheel->GetDuration(), 10.f ) );
  752. }
  753. }
  754. void CWheelOfDoom::WOD_Ghosts::ActivateEffect( EffectData_t& data )
  755. {
  756. if( TFGameRules() )
  757. {
  758. TFGameRules()->BeginHaunting( 4, data.m_pWheel->GetDuration() / 2.f, data.m_pWheel->GetDuration() );
  759. }
  760. }
  761. void CWheelOfDoom::WOD_Ghosts::DeactivateEffect( EffectData_t& data )
  762. {
  763. FOR_EACH_VEC( data.m_vecPlayers, i )
  764. {
  765. CTFPlayer* pPlayer = data.m_vecPlayers[i];
  766. pPlayer->m_Shared.RemoveCond( TF_COND_BURNING );
  767. }
  768. }
  769. void CWheelOfDoom::WOD_Dance::InitEffect( float /*flDefaultExpireTime*/ )
  770. {
  771. // Don't do the same order every time
  772. m_iCurrentMerasmusCreateInfo = RandomInt( 0, 1 );
  773. // Ignore the passed in expire time. We want to have the buff for 5 seconds with 2 taunt cycles
  774. m_flExpireTime = gpGlobals->curtime + 8.f;
  775. m_flNextDanceTime = gpGlobals->curtime + 1.5f;
  776. m_vecDancers.PurgeAndDeleteElements();
  777. CUtlVector< CTFPlayer * > playerVector;
  778. CollectPlayers( &playerVector, TEAM_ANY, true );
  779. // We'll calculate the average position and place merasmus there
  780. float aMerasmusY[2] = { FLT_MAX, -FLT_MAX };
  781. float flMerasmusX = 0.0f, flMerasmusZ = 0.0f;
  782. int nNumPositions = 0; // Keep a separate count for the average, since there's a case pOther can be NULL below
  783. FOR_EACH_VEC( playerVector, i )
  784. {
  785. CTFPlayer* pPlayer = playerVector[i];
  786. // Get our params
  787. const char* pzsTeam = pPlayer->GetTeamNumber() == TF_TEAM_RED ? "red" : "blue";
  788. int nInfoNumber = GetNumOFTeamDancing( pPlayer->GetTeamNumber() );
  789. // Cook up the name of the entity we want
  790. const char* pszTargetName = CFmtStr( "dance_teleport_%s%d", pzsTeam, nInfoNumber );
  791. // If we got it, then do the magic
  792. CBaseEntity *pOther = gEntList.FindEntityByName( NULL, pszTargetName );
  793. if( pOther )
  794. {
  795. Dancer_t* dancer = new Dancer_t();
  796. dancer->m_vecPos = pOther->GetAbsOrigin();
  797. dancer->m_vecAngles = pOther->GetAbsAngles();
  798. dancer->m_hPlayer.Set( pPlayer );
  799. // Average in the player's position
  800. aMerasmusY[0] = MIN( aMerasmusY[0], dancer->m_vecPos.y );
  801. aMerasmusY[1] = MAX( aMerasmusY[1], dancer->m_vecPos.y );
  802. flMerasmusX += dancer->m_vecPos.x;
  803. flMerasmusZ = dancer->m_vecPos.z;
  804. ++nNumPositions;
  805. // Slam them, for the jam
  806. SlamPosAndAngles( pPlayer, dancer->m_vecPos, dancer->m_vecAngles );
  807. // Force the halloween taunt. This will force the player to stand still.
  808. pPlayer->m_Shared.AddCond( TF_COND_HALLOWEEN_THRILLER );
  809. // Remember we're effecting this player
  810. m_vecDancers.AddToTail( dancer );
  811. }
  812. }
  813. // Average out the Merasmus position
  814. flMerasmusX /= nNumPositions;
  815. m_vecMerasmusDancerCreateInfos.AddToTail( MerasmusCreateInfo_t( Vector( flMerasmusX, aMerasmusY[0], flMerasmusZ ), QAngle( 0.0f, 90.0f, 0.0f ) ) );
  816. m_vecMerasmusDancerCreateInfos.AddToTail( MerasmusCreateInfo_t( Vector( flMerasmusX, aMerasmusY[1], flMerasmusZ ), QAngle( 0.0f,-90.0f, 0.0f ) ) );
  817. }
  818. void CWheelOfDoom::WOD_Dance::UpdateEffect( EffectData_t& /*data*/ )
  819. {
  820. bool bShouldSlam = ( m_flNextDanceTime - gpGlobals->curtime ) < 0.2f;
  821. bool bShouldTaunt = false;
  822. if( gpGlobals->curtime > m_flNextDanceTime )
  823. {
  824. bShouldTaunt = true;
  825. m_flNextDanceTime = gpGlobals->curtime + 3.5f;
  826. }
  827. FOR_EACH_VEC_BACK( m_vecDancers, i )
  828. {
  829. Dancer_t* dancer = m_vecDancers[i];
  830. CTFPlayer* pPlayer = dancer->m_hPlayer.Get();
  831. // This players is no more (disconnected). Forget about them.
  832. if( pPlayer == NULL )
  833. {
  834. delete dancer;
  835. m_vecDancers.Remove(i);
  836. continue;
  837. }
  838. if( bShouldTaunt )
  839. {
  840. pPlayer->Taunt();
  841. }
  842. if( bShouldSlam )
  843. {
  844. // Slam them, for the jam
  845. SlamPosAndAngles( pPlayer, dancer->m_vecPos, dancer->m_vecAngles );
  846. }
  847. }
  848. // Party time.
  849. if ( bShouldTaunt )
  850. {
  851. if ( m_hMerasmusDancer )
  852. {
  853. m_hMerasmusDancer->Vanish(); // Poof!
  854. }
  855. // Create a new Merasmus
  856. const MerasmusCreateInfo_t &info = m_vecMerasmusDancerCreateInfos[ m_iCurrentMerasmusCreateInfo % m_vecMerasmusDancerCreateInfos.Count() ];
  857. m_hMerasmusDancer = (CMerasmusDancer *)CBaseEntity::Create( "merasmus_dancer", info.m_vecPos, info.m_vecAngles );
  858. if ( m_hMerasmusDancer )
  859. {
  860. m_hMerasmusDancer->Dance();
  861. }
  862. // Move to the next location
  863. m_iCurrentMerasmusCreateInfo = ( m_iCurrentMerasmusCreateInfo + 1 ) % m_vecMerasmusDancerCreateInfos.Count();
  864. }
  865. }
  866. void CWheelOfDoom::WOD_Dance::DeactivateEffect( EffectData_t& data )
  867. {
  868. FOR_EACH_VEC( data.m_vecPlayers, i )
  869. {
  870. CTFPlayer* pPlayer = data.m_vecPlayers[i];
  871. pPlayer->m_Shared.RemoveCond( TF_COND_HALLOWEEN_THRILLER );
  872. }
  873. m_vecDancers.Purge();
  874. if ( m_hMerasmusDancer )
  875. {
  876. m_hMerasmusDancer->BlastOff();
  877. m_vecMerasmusDancerCreateInfos.Purge();
  878. }
  879. }
  880. int CWheelOfDoom::WOD_Dance::GetNumOFTeamDancing( int nTeam ) const
  881. {
  882. int nCount = 0;
  883. FOR_EACH_VEC( m_vecDancers, i )
  884. {
  885. if( m_vecDancers[i]->m_hPlayer.Get()->GetTeamNumber() == nTeam )
  886. nCount++;
  887. }
  888. return nCount;
  889. }
  890. void CWheelOfDoom::WOD_Dance::SlamPosAndAngles( CTFPlayer* pPlayer, const Vector& vPos, const QAngle& vAng )
  891. {
  892. // This calls: SetAbsOrigin(), SetAbsVelocity( vec3_origin ), SetLocalAngles(), SnapEyeAngles()
  893. pPlayer->Teleport( &vPos, &vAng, &vec3_origin );
  894. pPlayer->pl.v_angle = vAng;
  895. }