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.

1288 lines
38 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Entities for use in the Robot Destruction TF2 game mode.
  4. //
  5. //=========================================================================//
  6. #include "cbase.h"
  7. #include "tf_logic_halloween_2014.h"
  8. #include "tf_shareddefs.h"
  9. #include "tf_gamerules.h"
  10. #ifdef GAME_DLL
  11. #include "particle_parse.h"
  12. #include "halloween/tf_weapon_spellbook.h"
  13. #include "tf_weapon_sniperrifle.h"
  14. #include "ai_activity.h"
  15. #include "halloween/halloween_base_boss.h"
  16. #include "halloween/tf_weapon_spellbook.h"
  17. #include "engine/IEngineSound.h"
  18. #include "tf_props.h"
  19. #endif
  20. IMPLEMENT_AUTO_LIST( IMinigameAutoList );
  21. #ifdef GAME_DLL
  22. extern ConVar tf_teleporter_fov_time;
  23. extern ConVar tf_teleporter_fov_start;
  24. ConVar tf_fortune_teller_warning_time( "tf_fortune_teller_warning_time", "2", FCVAR_CHEAT, "Warning time (in second) before fortune teller tells a fortune." );
  25. ConVar tf_fortune_teller_interval_time( "tf_fortune_teller_interval_time", "120", FCVAR_CHEAT, "Time until the next fortune teller event (in second)." );
  26. ConVar tf_fortune_teller_fortune_duration( "tf_fortune_teller_fortune_duration", "30", FCVAR_CHEAT, "Duration of the fortune time." );
  27. ConVar tf_minigame_suddendeath_time( "tf_minigame_suddendeath_time", "-1", FCVAR_CHEAT, "Override Sudden Death Time." );
  28. BEGIN_DATADESC( CTFMiniGame )
  29. DEFINE_KEYFIELD( m_iszYourTeamScoreSound, FIELD_STRING, "your_team_score_sound" ),
  30. DEFINE_KEYFIELD( m_iszEnemyTeamScoreSound, FIELD_STRING, "enemy_team_score_sound" ),
  31. DEFINE_KEYFIELD( m_iszHudResFile, FIELD_STRING, "hud_res_file" ),
  32. DEFINE_KEYFIELD( m_pszTeamSpawnPoint[ TF_TEAM_RED ], FIELD_STRING, "RedSpawn" ),
  33. DEFINE_KEYFIELD( m_pszTeamSpawnPoint[ TF_TEAM_BLUE ], FIELD_STRING, "BlueSpawn" ),
  34. DEFINE_KEYFIELD( m_bMinigameAllowedInRamdomPool, FIELD_BOOLEAN, "InRandomPool" ),
  35. DEFINE_KEYFIELD( m_nMaxScoreForMiniGame, FIELD_INTEGER, "MaxScore" ),
  36. DEFINE_KEYFIELD( m_eScoringType, FIELD_INTEGER, "ScoreType" ),
  37. DEFINE_KEYFIELD( m_flSuddenDeathTime, FIELD_FLOAT, "SuddenDeathTime" ),
  38. DEFINE_OUTPUT( m_OnRedHitMaxScore, "OnRedHitMaxScore" ),
  39. DEFINE_OUTPUT( m_OnBlueHitMaxScore, "OnBlueHitMaxScore" ),
  40. DEFINE_OUTPUT( m_OnTeleportToMinigame, "OnTeleportToMinigame" ),
  41. DEFINE_OUTPUT( m_OnReturnFromMinigame, "OnReturnFromMinigame" ),
  42. DEFINE_OUTPUT( m_OnAllRedDead, "OnAllRedDead" ),
  43. DEFINE_OUTPUT( m_OnAllBlueDead, "OnAllBlueDead" ),
  44. DEFINE_OUTPUT( m_OnSuddenDeathStart, "OnSuddenDeathStart" ),
  45. DEFINE_INPUTFUNC( FIELD_INTEGER, "ScoreTeamRed", InputScoreTeamRed ),
  46. DEFINE_INPUTFUNC( FIELD_INTEGER, "ScoreTeamBlue", InputScoreTeamBlue ),
  47. DEFINE_INPUTFUNC( FIELD_STRING, "ChangeHudResFile", InputChangeHudResFile ),
  48. END_DATADESC()
  49. #endif
  50. LINK_ENTITY_TO_CLASS( tf_base_minigame, CTFMiniGame );
  51. IMPLEMENT_NETWORKCLASS_ALIASED( TFMiniGame, DT_TFMinigame )
  52. BEGIN_NETWORK_TABLE_NOBASE( CTFMiniGame, DT_TFMinigame )
  53. #ifdef CLIENT_DLL
  54. RecvPropArray3( RECVINFO_ARRAY( m_nMinigameTeamScore ), RecvPropInt( RECVINFO( m_nMinigameTeamScore[0] ) ) ),
  55. RecvPropInt( RECVINFO( m_nMaxScoreForMiniGame ) ),
  56. RecvPropString( RECVINFO( m_pszHudResFile ) ),
  57. RecvPropInt( RECVINFO( m_eScoringType ) ),
  58. #else
  59. SendPropArray3( SENDINFO_ARRAY3( m_nMinigameTeamScore ), SendPropInt( SENDINFO_ARRAY( m_nMinigameTeamScore ), -1, SPROP_UNSIGNED | SPROP_VARINT ) ),
  60. SendPropInt( SENDINFO( m_nMaxScoreForMiniGame ), -1, SPROP_VARINT | SPROP_UNSIGNED ),
  61. SendPropString( SENDINFO( m_pszHudResFile ) ),
  62. SendPropInt( SENDINFO( m_eScoringType ), -1, SPROP_VARINT | SPROP_UNSIGNED ),
  63. #endif
  64. END_NETWORK_TABLE()
  65. #define NO_TEAM_ADVANTAGE -1
  66. CTFMiniGame::CTFMiniGame()
  67. {
  68. m_nMaxScoreForMiniGame = 0;
  69. m_nMinigameTeamScore.Set( TF_TEAM_RED, 0 );
  70. m_nMinigameTeamScore.Set( TF_TEAM_BLUE, 0 );
  71. #ifdef GAME_DLL
  72. m_bMinigameAllowedInRamdomPool = true;
  73. m_bIsActive = false;
  74. m_pszTeamSpawnPoint[ TF_TEAM_RED ] = NULL;
  75. m_pszTeamSpawnPoint[ TF_TEAM_BLUE ] = NULL;
  76. m_eMinigameType = MINIGAME_GENERIC;
  77. m_iszYourTeamScoreSound = NULL_STRING;
  78. m_iszEnemyTeamScoreSound = NULL_STRING;
  79. m_flSuddenDeathTime = -1.0f; // No sudden death.
  80. m_iAdvantagedTeam = NO_TEAM_ADVANTAGE;
  81. ListenForGameEvent( "player_death" );
  82. ListenForGameEvent( "player_turned_to_ghost" );
  83. ListenForGameEvent( "player_disconnect" );
  84. ListenForGameEvent( "player_team" );
  85. ListenForGameEvent( "player_spawn" );
  86. #endif
  87. }
  88. #ifdef GAME_DLL
  89. void CTFMiniGame::Spawn()
  90. {
  91. Precache();
  92. BaseClass::Spawn();
  93. V_strncpy( m_pszHudResFile.GetForModify(), STRING( m_iszHudResFile ), MAX_PATH );
  94. }
  95. void CTFMiniGame::Precache()
  96. {
  97. BaseClass::Precache();
  98. if ( m_iszYourTeamScoreSound.ToCStr() )
  99. {
  100. PrecacheScriptSound( m_iszYourTeamScoreSound.ToCStr() );
  101. }
  102. if ( m_iszEnemyTeamScoreSound.ToCStr() )
  103. {
  104. PrecacheScriptSound( m_iszEnemyTeamScoreSound.ToCStr() );
  105. }
  106. }
  107. void CTFMiniGame::FireGameEvent( IGameEvent * pEvent )
  108. {
  109. // Only look for dead players when the round is running or else we'll get
  110. // victories while in the spawn room as we respawn
  111. if ( ( TFGameRules() && TFGameRules()->State_Get() != GR_STATE_RND_RUNNING ) || !m_bIsActive )
  112. return;
  113. const char *pszEventName = pEvent->GetName();
  114. if ( !V_strcmp( pszEventName, "player_turned_to_ghost" )
  115. || !V_strcmp( pszEventName, "player_disconnect" )
  116. || !V_strcmp( pszEventName, "player_team" )
  117. || !V_strcmp( pszEventName, "player_death" )
  118. || !V_strcmp( pszEventName, "player_spawn" ) )
  119. {
  120. bool bCanWin = true;
  121. // Blue gets the chance to win first
  122. UpdateDeadPlayers( TF_TEAM_RED, m_OnBlueHitMaxScore, m_OnAllRedDead, bCanWin );
  123. UpdateDeadPlayers( TF_TEAM_BLUE, m_OnRedHitMaxScore, m_OnAllBlueDead, bCanWin );
  124. }
  125. }
  126. void CTFMiniGame::TeleportAllPlayers()
  127. {
  128. // remove all projectiles and objects before we go to minigame
  129. TFGameRules()->RemoveAllProjectilesAndBuildings();
  130. CUtlVector< CTFPlayer* > vecTeleportedPlayers;
  131. TFGameRules()->TeleportPlayersToTargetEntities( TF_TEAM_RED, m_pszTeamSpawnPoint[ TF_TEAM_RED ], &vecTeleportedPlayers );
  132. TFGameRules()->TeleportPlayersToTargetEntities( TF_TEAM_BLUE, m_pszTeamSpawnPoint[ TF_TEAM_BLUE ], &vecTeleportedPlayers );
  133. FOR_EACH_VEC( vecTeleportedPlayers, i )
  134. {
  135. OnTeleportPlayerToMinigame( vecTeleportedPlayers[i] );
  136. }
  137. m_iAdvantagedTeam = NO_TEAM_ADVANTAGE; // reset advantage
  138. m_bIsActive = true;
  139. m_OnTeleportToMinigame.FireOutput( this, this );
  140. if ( tf_minigame_suddendeath_time.GetFloat() != -1 )
  141. {
  142. m_flSuddenDeathTime = tf_minigame_suddendeath_time.GetFloat();
  143. DevMsg( "Setting m_flSuddenDeathTime to %f\n", m_flSuddenDeathTime );
  144. }
  145. // If we've got a sudden death start time, trigger a think function callback.
  146. if ( m_flSuddenDeathTime >= 0.0f )
  147. {
  148. SetContextThink( &CTFHalloweenMinigame::SuddenDeathTimeStartThink, gpGlobals->curtime + m_flSuddenDeathTime, "SuddenDeathTimeStart" );
  149. }
  150. }
  151. void CTFMiniGame::OnTeleportPlayerToMinigame( CTFPlayer *pPlayer )
  152. {
  153. // Do a zoom effect
  154. pPlayer->SetFOV( pPlayer, tf_teleporter_fov_start.GetInt() );
  155. pPlayer->SetFOV( pPlayer, 0, 1.f, tf_teleporter_fov_start.GetInt() );
  156. // Screen flash
  157. color32 fadeColor = {255,255,255,100};
  158. UTIL_ScreenFade( pPlayer, fadeColor, 0.25, 0.4, FFADE_IN );
  159. }
  160. void CTFMiniGame::ReturnAllPlayers()
  161. {
  162. // Send everyone back
  163. CUtlVector< CTFPlayer * > vecPlayers;
  164. CollectPlayers( &vecPlayers, TEAM_ANY, false );
  165. FOR_EACH_VEC( vecPlayers, i )
  166. {
  167. vecPlayers[ i ]->ForceRespawn();
  168. }
  169. m_nMinigameTeamScore.Set( TF_TEAM_RED, 0 );
  170. m_nMinigameTeamScore.Set( TF_TEAM_BLUE, 0 );
  171. m_bIsActive = false;
  172. m_OnReturnFromMinigame.FireOutput( this, this );
  173. }
  174. void CTFMiniGame::ScorePointsForTeam( int nTeamNum, int nPoints )
  175. {
  176. // Don't tally more points if a team already hit the max
  177. if ( !m_bIsActive || ( m_nMinigameTeamScore.Get( TF_TEAM_RED ) == m_nMaxScoreForMiniGame ) || ( m_nMinigameTeamScore.Get( TF_TEAM_BLUE ) == m_nMaxScoreForMiniGame ) )
  178. {
  179. return;
  180. }
  181. // Are we playing sudden death right now?
  182. bool bInSuddenDeath = ( m_flSuddenDeathTime == 0.0f );
  183. // Increment score for the appropriate team
  184. auto& nTeamPoints = m_nMinigameTeamScore.GetForModify( nTeamNum );
  185. nTeamPoints += nPoints;
  186. nTeamPoints = clamp( nTeamPoints, 0, m_nMaxScoreForMiniGame );
  187. // If they went to the max score or we're in sudden death, fire winning output.
  188. if ( ( nTeamPoints == m_nMaxScoreForMiniGame ) || bInSuddenDeath )
  189. {
  190. auto& eventMaxScoreHit = ( nTeamNum == TF_TEAM_RED ) ? m_OnRedHitMaxScore : m_OnBlueHitMaxScore;
  191. eventMaxScoreHit.FireOutput( this, this );
  192. CUtlVector<CTFPlayer *> vecPlayers;
  193. CollectPlayers( &vecPlayers, nTeamNum );
  194. for ( auto pPlayer : vecPlayers )
  195. {
  196. HatAndMiscEconEntities_OnOwnerKillEaterEventNoParter( pPlayer, kKillEaterEvent_Halloween_MinigamesWon );
  197. IGameEvent *pEvent = gameeventmanager->CreateEvent( "minigame_won" );
  198. if ( pEvent )
  199. {
  200. pEvent->SetInt( "player", pPlayer->GetUserID() );
  201. pEvent->SetInt( "game", GetMinigameType() );
  202. gameeventmanager->FireEvent( pEvent, true );
  203. }
  204. }
  205. }
  206. // Can not be specified, and we dont want to do anything in that case
  207. if ( m_iszYourTeamScoreSound.ToCStr() && *m_iszYourTeamScoreSound.ToCStr()
  208. && m_iszEnemyTeamScoreSound.ToCStr() && *m_iszEnemyTeamScoreSound.ToCStr() )
  209. {
  210. // Get everyone
  211. CUtlVector< CTFPlayer* > vecPlayer;
  212. CollectPlayers( &vecPlayer );
  213. // Play a sound based on if the scoring team is the player's team
  214. for( CTFPlayer *pPlayer : vecPlayer )
  215. {
  216. EmitSound_t params;
  217. float soundlen = 0;
  218. params.m_flSoundTime = 0;
  219. params.m_pSoundName = NULL;
  220. params.m_pflSoundDuration = &soundlen;
  221. params.m_pSoundName = pPlayer->GetTeamNumber() == nTeamNum ? m_iszYourTeamScoreSound.ToCStr() : m_iszEnemyTeamScoreSound.ToCStr();
  222. params.m_nPitch = RemapValClamped( nTeamPoints, m_nMaxScoreForMiniGame * 0.75, m_nMaxScoreForMiniGame, 100, 120 );
  223. params.m_nFlags |= SND_CHANGE_PITCH;
  224. params.m_flVolume = 0.25f; // Pretty quiet
  225. params.m_nFlags |= SND_CHANGE_VOL;
  226. // Play in the player's ears
  227. CSingleUserRecipientFilter filter( pPlayer );
  228. filter.MakeReliable();
  229. pPlayer->StopSound( params.m_pSoundName );
  230. pPlayer->EmitSound( filter, pPlayer->entindex(), params );
  231. }
  232. }
  233. }
  234. void CTFMiniGame::InputScoreTeamRed( inputdata_t &inputdata )
  235. {
  236. ScorePointsForTeam( TF_TEAM_RED, inputdata.value.Int() );
  237. InternalHandleInputScore( inputdata );
  238. }
  239. void CTFMiniGame::InputScoreTeamBlue( inputdata_t &inputdata )
  240. {
  241. ScorePointsForTeam( TF_TEAM_BLUE, inputdata.value.Int() );
  242. InternalHandleInputScore( inputdata );
  243. }
  244. void CTFMiniGame::InputChangeHudResFile( inputdata_t &inputdata )
  245. {
  246. const char *resFile = inputdata.value.String();
  247. Assert( resFile && resFile[ 0 ] );
  248. if ( resFile && resFile[ 0 ] )
  249. {
  250. V_strncpy( m_pszHudResFile.GetForModify(), resFile, MAX_PATH );
  251. }
  252. }
  253. //-----------------------------------------------------------------------------
  254. // Purpose: Find spawn point entity for specified team
  255. //-----------------------------------------------------------------------------
  256. const char *CTFMiniGame::GetTeamSpawnPointName( int nTeamNum ) const
  257. {
  258. if ( !IsValidTFTeam( nTeamNum ) )
  259. return NULL;
  260. return m_pszTeamSpawnPoint[ nTeamNum ];
  261. }
  262. void CTFMiniGame::UpdateDeadPlayers( int nTeam, COutputEvent& eventWin, COutputEvent& eventAllDead, bool& bCanWin )
  263. {
  264. // Update the score for a team
  265. CUtlVector< CTFPlayer * > vecPlayers;
  266. CollectPlayers( &vecPlayers, nTeam, true );
  267. int nNumDead = 0;
  268. FOR_EACH_VEC( vecPlayers, i )
  269. {
  270. // Tally the number dead/ghosts
  271. if ( vecPlayers[i]->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) || vecPlayers[i]->IsDead() )
  272. ++nNumDead;
  273. }
  274. // Only update the score if this is the SCORING_TYPE_PLAYERS_ALIVE mode
  275. if ( m_eScoringType == SCORING_TYPE_PLAYERS_ALIVE )
  276. {
  277. auto& nScore = m_nMinigameTeamScore.GetForModify( nTeam );
  278. nScore = vecPlayers.Count() - nNumDead;
  279. }
  280. // Everyone is dead
  281. if ( nNumDead == vecPlayers.Count() && !vecPlayers.IsEmpty() )
  282. {
  283. m_bIsActive = false;
  284. // If everyone is dead, and we're allowed to win, fire the win event
  285. if ( bCanWin && m_eScoringType == SCORING_TYPE_PLAYERS_ALIVE )
  286. {
  287. eventWin.FireOutput( this, this );
  288. bCanWin = false;
  289. }
  290. // Fire the team dead event
  291. eventAllDead.FireOutput( this, this );
  292. CUtlVector<CTFPlayer *> vecEnemyPlayers;
  293. CollectPlayers( &vecEnemyPlayers, GetEnemyTeam( nTeam ) );
  294. for ( auto pPlayer : vecEnemyPlayers )
  295. {
  296. HatAndMiscEconEntities_OnOwnerKillEaterEventNoParter( pPlayer, kKillEaterEvent_Halloween_MinigamesWon );
  297. IGameEvent *pEvent = gameeventmanager->CreateEvent( "minigame_won" );
  298. if ( pEvent )
  299. {
  300. pEvent->SetInt( "player", pPlayer->GetUserID() );
  301. pEvent->SetInt( "game", GetMinigameType() );
  302. gameeventmanager->FireEvent( pEvent, true );
  303. }
  304. }
  305. }
  306. }
  307. // "SuddenDeathTimeStart"
  308. void CTFMiniGame::SuddenDeathTimeStartThink()
  309. {
  310. // If we're active, fire the sudden death time start event.
  311. if ( m_bIsActive )
  312. {
  313. m_flSuddenDeathTime = 0.0f; // In sudden death!
  314. m_OnSuddenDeathStart.FireOutput( this, this );
  315. }
  316. }
  317. #else
  318. //-----------------------------------------------------------------------------
  319. // Purpose:
  320. //-----------------------------------------------------------------------------
  321. int CTFMiniGame::GetScoreForTeam( int nTeamNum ) const
  322. {
  323. if ( !IsValidTFTeam( nTeamNum ) )
  324. return 0;
  325. return m_nMinigameTeamScore[ nTeamNum ];
  326. }
  327. #endif // GAME_DLL
  328. #ifdef GAME_DLL
  329. BEGIN_DATADESC( CTFHalloweenMinigame )
  330. DEFINE_KEYFIELD( m_eMinigameType, FIELD_INTEGER, "MinigameType" ),
  331. DEFINE_INPUTFUNC( FIELD_VOID, "KartWinAnimationRed", InputKartWinAnimationRed ),
  332. DEFINE_INPUTFUNC( FIELD_VOID, "KartWinAnimationBlue", InputKartWinAnimationBlue ),
  333. DEFINE_INPUTFUNC( FIELD_VOID, "KartLoseAnimationRed", InputKartLoseAnimationRed ),
  334. DEFINE_INPUTFUNC( FIELD_VOID, "KartLoseAnimationBlue", InputKartLoseAnimationBlue ),
  335. DEFINE_INPUTFUNC( FIELD_STRING, "EnableSpawnBoss", InputEnableSpawnBoss ),
  336. DEFINE_INPUTFUNC( FIELD_VOID, "DisableSpawnBoss", InputDisableSpawnBoss ),
  337. END_DATADESC()
  338. #endif
  339. LINK_ENTITY_TO_CLASS( tf_halloween_minigame, CTFHalloweenMinigame );
  340. IMPLEMENT_NETWORKCLASS_ALIASED( TFHalloweenMinigame, DT_TFHalloweenMinigame )
  341. BEGIN_NETWORK_TABLE( CTFHalloweenMinigame, DT_TFHalloweenMinigame )
  342. END_NETWORK_TABLE()
  343. #ifdef GAME_DLL
  344. CTFHalloweenMinigame::CTFHalloweenMinigame()
  345. {
  346. m_hBossSpawnPoint = NULL;
  347. ListenForGameEvent( "pumpkin_lord_killed" );
  348. }
  349. void CTFHalloweenMinigame::Spawn()
  350. {
  351. BaseClass::Spawn();
  352. }
  353. void CTFHalloweenMinigame::FireGameEvent( IGameEvent * event )
  354. {
  355. BaseClass::FireGameEvent( event );
  356. if ( FStrEq( event->GetName(), "pumpkin_lord_killed" ) )
  357. {
  358. if ( m_hBossSpawnPoint && ( !m_hHalloweenBoss || m_hHalloweenBoss->IsMarkedForDeletion() ) )
  359. {
  360. m_hHalloweenBoss = CHalloweenBaseBoss::SpawnBossAtPos( HALLOWEEN_BOSS_HHH, m_hBossSpawnPoint->GetAbsOrigin() );
  361. }
  362. }
  363. }
  364. void CTFHalloweenMinigame::InternalHandleInputScore( inputdata_t &inputdata )
  365. {
  366. CPropSoccerBall *pSoccerBall = dynamic_cast< CPropSoccerBall* >( inputdata.pActivator );
  367. if ( pSoccerBall )
  368. {
  369. CTFPlayer *pTFPlayer = pSoccerBall->GetLastToucher();
  370. if ( pTFPlayer && TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
  371. {
  372. pTFPlayer->AwardAchievement( ACHIEVEMENT_TF_HALLOWEEN_DOOMSDAY_SCORE_GOALS );
  373. }
  374. }
  375. }
  376. void CTFHalloweenMinigame::TeleportAllPlayers()
  377. {
  378. CUtlVector< CTFPlayer * > vecPlayers;
  379. CollectPlayers( &vecPlayers, TEAM_ANY, false );
  380. FOR_EACH_VEC( vecPlayers, i )
  381. {
  382. CTFPlayer *pPlayer = vecPlayers[i];
  383. // Only do these effects if the player is alive
  384. if ( !pPlayer->IsAlive() )
  385. continue;
  386. // Fade to white
  387. color32 fadeColor = {255,255,255,255};
  388. UTIL_ScreenFade( pPlayer, fadeColor, 2.f, 0.5f, FFADE_OUT | FFADE_PURGE );
  389. // Do a zoom in effect
  390. pPlayer->SetFOV( pPlayer, 10.f, 2.5f, 0.f );
  391. // Rumble like something important happened
  392. UTIL_ScreenShake(pPlayer->GetAbsOrigin(), 100.f, 150, 4.f, 0.f, SHAKE_START, true );
  393. }
  394. // Play a sound for all players
  395. TFGameRules()->BroadcastSound( 255, "Halloween.hellride" );
  396. SetContextThink( &CTFHalloweenMinigame::TeleportAllPlayersThink, gpGlobals->curtime + 2.0f, "TeleportToHell" );
  397. }
  398. void CTFHalloweenMinigame::TeleportAllPlayersThink()
  399. {
  400. RemoveAll2013HalloweenTeleportSpellsInMidFlight();
  401. CUtlVector< CTFPlayer * > vecPlayers;
  402. CollectPlayers( &vecPlayers, TEAM_ANY, false );
  403. BaseClass::TeleportAllPlayers();
  404. FOR_EACH_VEC( vecPlayers, i )
  405. {
  406. CTFPlayer *pPlayer = vecPlayers[i];
  407. pPlayer->CancelEurekaTeleport();
  408. // Fade from white
  409. color32 fadeColor = {255,255,255,255};
  410. UTIL_ScreenFade( pPlayer, fadeColor, 1.f, 0.2f, FFADE_IN );
  411. }
  412. // Set this flag. Lets us check elsewhere if it's hell time
  413. // HACK: Should we be doing this for 2014?!?
  414. if ( TFGameRules() )
  415. {
  416. TFGameRules()->SetPlayersInHell( true );
  417. }
  418. }
  419. void CTFHalloweenMinigame::OnTeleportPlayerToMinigame( CTFPlayer *pPlayer )
  420. {
  421. BaseClass::OnTeleportPlayerToMinigame( pPlayer );
  422. pPlayer->m_Shared.AddCond( TF_COND_HALLOWEEN_IN_HELL );
  423. pPlayer->m_Shared.AddCond( TF_COND_HALLOWEEN_KART );
  424. const float flCageTime = 3.f;
  425. pPlayer->m_Shared.AddCond( TF_COND_HALLOWEEN_KART_CAGE, flCageTime );
  426. pPlayer->m_Shared.AddCond( TF_COND_FREEZE_INPUT, flCageTime );
  427. pPlayer->SetAbsVelocity( vec3_origin );
  428. pPlayer->EmitSound( "BumperCar.Spawn" );
  429. // if its set
  430. if ( m_iAdvantagedTeam != NO_TEAM_ADVANTAGE )
  431. {
  432. if ( pPlayer->GetTeamNumber() != m_iAdvantagedTeam )
  433. {
  434. pPlayer->AddKartDamage( 66 );
  435. }
  436. }
  437. }
  438. void CTFHalloweenMinigame::ReturnAllPlayers()
  439. {
  440. // Set this flag. Lets us check elsewhere if it's hell time
  441. // HACK: Should we be doing this for 2014?!?
  442. if ( TFGameRules() )
  443. {
  444. TFGameRules()->SetPlayersInHell( false );
  445. }
  446. BaseClass::ReturnAllPlayers();
  447. }
  448. void CTFHalloweenMinigame::InputKartWinAnimationRed( inputdata_t &inputdata )
  449. {
  450. CUtlVector< CTFPlayer* > players;
  451. CollectPlayers( &players, TF_TEAM_RED, true );
  452. for ( int i=0; i<players.Count(); ++i )
  453. {
  454. if ( players[i]->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  455. {
  456. players[i]->DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE, ACT_KART_GESTURE_POSITIVE );
  457. }
  458. }
  459. }
  460. void CTFHalloweenMinigame::InputKartWinAnimationBlue( inputdata_t &inputdata )
  461. {
  462. CUtlVector< CTFPlayer* > players;
  463. CollectPlayers( &players, TF_TEAM_BLUE, true );
  464. for ( int i=0; i<players.Count(); ++i )
  465. {
  466. if ( players[i]->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  467. {
  468. players[i]->DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE, ACT_KART_GESTURE_POSITIVE );
  469. }
  470. }
  471. }
  472. void CTFHalloweenMinigame::InputKartLoseAnimationRed( inputdata_t &inputdata )
  473. {
  474. CUtlVector< CTFPlayer* > players;
  475. CollectPlayers( &players, TF_TEAM_RED, true );
  476. for ( int i=0; i<players.Count(); ++i )
  477. {
  478. if ( players[i]->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  479. {
  480. players[i]->DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE, ACT_KART_GESTURE_NEGATIVE );
  481. }
  482. }
  483. }
  484. void CTFHalloweenMinigame::InputKartLoseAnimationBlue( inputdata_t &inputdata )
  485. {
  486. CUtlVector< CTFPlayer* > players;
  487. CollectPlayers( &players, TF_TEAM_BLUE, true );
  488. for ( int i=0; i<players.Count(); ++i )
  489. {
  490. if ( players[i]->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  491. {
  492. players[i]->DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE, ACT_KART_GESTURE_NEGATIVE );
  493. }
  494. }
  495. }
  496. void CTFHalloweenMinigame::InputEnableSpawnBoss( inputdata_t &inputdata )
  497. {
  498. if ( !m_hBossSpawnPoint )
  499. {
  500. const char *pszSpawnPoint = inputdata.value.String();
  501. if ( pszSpawnPoint )
  502. {
  503. m_hBossSpawnPoint = gEntList.FindEntityByName( NULL, pszSpawnPoint );
  504. }
  505. }
  506. if ( m_hBossSpawnPoint )
  507. {
  508. m_hHalloweenBoss = CHalloweenBaseBoss::SpawnBossAtPos( HALLOWEEN_BOSS_HHH, m_hBossSpawnPoint->GetAbsOrigin() );
  509. }
  510. }
  511. void CTFHalloweenMinigame::InputDisableSpawnBoss( inputdata_t &inputdata )
  512. {
  513. m_hBossSpawnPoint = NULL;
  514. }
  515. #endif
  516. #ifdef GAME_DLL
  517. BEGIN_DATADESC( CTFHalloweenMinigame_FallingPlatforms )
  518. DEFINE_INPUTFUNC( FIELD_VOID, "ChoosePlatform", InputChoosePlatform ),
  519. DEFINE_OUTPUT( m_OutputSafePlatform, "OutputSafePlatform" ),
  520. DEFINE_OUTPUT( m_OutputRemovePlatform, "OutputRemovePlatform" ),
  521. END_DATADESC()
  522. #endif
  523. LINK_ENTITY_TO_CLASS( tf_halloween_minigame_falling_platforms, CTFHalloweenMinigame_FallingPlatforms );
  524. IMPLEMENT_NETWORKCLASS_ALIASED( TFHalloweenMinigame_FallingPlatforms, DT_TFHalloweenMinigame_FallingPlatforms )
  525. BEGIN_NETWORK_TABLE( CTFHalloweenMinigame_FallingPlatforms, DT_TFHalloweenMinigame_FallingPlatforms )
  526. END_NETWORK_TABLE()
  527. #ifdef GAME_DLL
  528. CTFHalloweenMinigame_FallingPlatforms::CTFHalloweenMinigame_FallingPlatforms()
  529. {
  530. // Corners
  531. CUtlVector<int> vecFirstSet;
  532. vecFirstSet.AddToTail( 1 );
  533. vecFirstSet.AddToTail( 3 );
  534. vecFirstSet.AddToTail( 7 );
  535. vecFirstSet.AddToTail( 9 );
  536. vecFirstSet.Shuffle();
  537. // On Axis
  538. CUtlVector<int> vecSecondSet;
  539. vecSecondSet.AddToTail( 2 );
  540. vecSecondSet.AddToTail( 4 );
  541. vecSecondSet.AddToTail( 6 );
  542. vecSecondSet.AddToTail( 8 );
  543. vecSecondSet.Shuffle();
  544. m_vecRemainingPlatforms.AddVectorToTail( vecFirstSet );
  545. m_vecRemainingPlatforms.AddVectorToTail( vecSecondSet );
  546. m_vecRemainingPlatforms.AddToTail( 5 ); // The center
  547. }
  548. void CTFHalloweenMinigame_FallingPlatforms::InputChoosePlatform( inputdata_t &inputdata )
  549. {
  550. variant_t nVal;
  551. // If there's more than 1 platforms remaining, mark another to never come back
  552. if ( m_vecRemainingPlatforms.Count() > 1 )
  553. {
  554. nVal.SetInt( m_vecRemainingPlatforms.Head() );
  555. m_vecRemainingPlatforms.Remove( 0 );
  556. m_OutputRemovePlatform.FireOutput( nVal, this, this );
  557. }
  558. // The which one is supposed to be the safe platform
  559. int nIndex = RandomInt( 0, m_vecRemainingPlatforms.Count() - 1 );
  560. int nSafePlatform = m_vecRemainingPlatforms[ nIndex ];
  561. nVal.SetInt( nSafePlatform );
  562. m_OutputSafePlatform.FireOutput( nVal, this, this );
  563. }
  564. void CTFHalloweenMinigame_FallingPlatforms::FireGameEvent( IGameEvent * pEvent )
  565. {
  566. const char *pszEventName = pEvent->GetName();
  567. // In the falling platforms game, we dont want ghosts to get stuck in platforms.
  568. // This hack sets the collision of ghosts to collide with triggers, but not with
  569. // other players. This should allow us to use the relative teleport trigger on the
  570. // ghosts to prevent stuckage.
  571. if ( m_bIsActive && !V_strcmp( pszEventName, "player_turned_to_ghost" ) )
  572. {
  573. CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByUserId( pEvent->GetInt( "userid" ) ) );
  574. if ( pPlayer )
  575. {
  576. pPlayer->SetSolid( SOLID_BBOX );
  577. pPlayer->SetSolidFlags( FSOLID_NOT_STANDABLE );
  578. pPlayer->SetCollisionGroup( COLLISION_GROUP_DEBRIS_TRIGGER ); // Dont run into other players
  579. }
  580. }
  581. BaseClass::FireGameEvent( pEvent );
  582. }
  583. #endif
  584. #ifdef GAME_DLL
  585. BEGIN_DATADESC( CTFMinigameLogic )
  586. DEFINE_INPUTFUNC( FIELD_INTEGER, "TeleportToMinigame", InputTeleportToMinigame ),
  587. DEFINE_INPUTFUNC( FIELD_STRING, "SetAdvantageTeam", InputSetAdvantageTeam ),
  588. DEFINE_INPUTFUNC( FIELD_VOID, "TeleportToRandomMinigame", InputTeleportToRandomMinigame ),
  589. DEFINE_INPUTFUNC( FIELD_VOID, "ReturnFromMinigame", InputReturnFromMinigame ),
  590. END_DATADESC()
  591. #endif
  592. LINK_ENTITY_TO_CLASS( tf_logic_minigames, CTFMinigameLogic );
  593. IMPLEMENT_NETWORKCLASS_ALIASED( TFMinigameLogic, DT_TFMinigameLogic )
  594. BEGIN_NETWORK_TABLE_NOBASE( CTFMinigameLogic, DT_TFMinigameLogic )
  595. #ifdef CLIENT_DLL
  596. RecvPropEHandle( RECVINFO( m_hActiveMinigame ) ),
  597. #else
  598. SendPropEHandle( SENDINFO( m_hActiveMinigame ) ),
  599. #endif
  600. END_NETWORK_TABLE()
  601. CTFMinigameLogic* CTFMinigameLogic::m_sMinigameLogic = NULL;
  602. CTFMinigameLogic::CTFMinigameLogic()
  603. {
  604. m_hActiveMinigame = NULL;
  605. m_sMinigameLogic = this;
  606. #ifdef GAME_DLL
  607. m_iAdvantagedTeam = NO_TEAM_ADVANTAGE;
  608. #endif
  609. }
  610. CTFMinigameLogic::~CTFMinigameLogic()
  611. {
  612. if ( m_sMinigameLogic == this )
  613. {
  614. m_sMinigameLogic = NULL;
  615. }
  616. }
  617. #ifdef GAME_DLL
  618. void CTFMinigameLogic::TeleportToMinigame( int nMiniGameIndex )
  619. {
  620. Assert( m_hActiveMinigame == NULL );
  621. CTFMiniGame *pMinigame = static_cast< CTFMiniGame* >( IMinigameAutoList::AutoList()[ nMiniGameIndex ] );
  622. if ( pMinigame )
  623. {
  624. m_hActiveMinigame = pMinigame;
  625. m_hActiveMinigame->SetAdvantagedTeam( m_iAdvantagedTeam );
  626. m_hActiveMinigame->TeleportAllPlayers( );
  627. m_iAdvantagedTeam = NO_TEAM_ADVANTAGE;
  628. }
  629. }
  630. void CTFMinigameLogic::ReturnFromMinigame()
  631. {
  632. if ( m_hActiveMinigame )
  633. {
  634. m_hActiveMinigame->ReturnAllPlayers();
  635. }
  636. m_hActiveMinigame = NULL;
  637. }
  638. void CTFMinigameLogic::InputTeleportToMinigame( inputdata_t &inputdata )
  639. {
  640. int nInput = inputdata.value.Int();
  641. if ( nInput >= 0 && nInput < IMinigameAutoList::AutoList().Count() )
  642. {
  643. TeleportToMinigame( nInput );
  644. }
  645. }
  646. void CTFMinigameLogic::InputSetAdvantageTeam( inputdata_t &inputdata )
  647. {
  648. m_iAdvantagedTeam = FStrEq( inputdata.value.String(), "red" ) ? TF_TEAM_RED : TF_TEAM_BLUE;
  649. }
  650. void CTFMinigameLogic::InputReturnFromMinigame( inputdata_t &inputdata )
  651. {
  652. ReturnFromMinigame();
  653. }
  654. void CTFMinigameLogic::InputTeleportToRandomMinigame( inputdata_t &inputdata )
  655. {
  656. static int nLastChosenIndex = -1;
  657. CUtlVector< int > m_vecRandomableMiniGames;
  658. FOR_EACH_VEC( IMinigameAutoList::AutoList(), i )
  659. {
  660. CTFMiniGame *pMinigame = static_cast< CTFMiniGame* >( IMinigameAutoList::AutoList()[ i ] );
  661. if ( pMinigame->AllowedInRandom() && nLastChosenIndex != i )
  662. {
  663. m_vecRandomableMiniGames.AddToTail( i );
  664. }
  665. }
  666. if ( !m_vecRandomableMiniGames.IsEmpty() )
  667. {
  668. int nChosenIndex = m_vecRandomableMiniGames[ RandomInt( 0, m_vecRandomableMiniGames.Count() - 1 ) ];
  669. nLastChosenIndex = nChosenIndex;
  670. TeleportToMinigame( nChosenIndex );
  671. }
  672. }
  673. #endif
  674. //-----------------------------------------------------------------------------
  675. // Purpose:
  676. //-----------------------------------------------------------------------------
  677. class CConditionFortuneTellerEffect
  678. #ifdef GAME_DLL
  679. : public CGameEventListener
  680. #endif
  681. {
  682. public:
  683. CConditionFortuneTellerEffect( const char* pszActivateSound, ETFCond eCond )
  684. : m_pszActivateSound( pszActivateSound )
  685. , m_eCondition( eCond )
  686. , m_bUseTimer( false )
  687. {}
  688. void OnActivateEffect( bool bUseTimer )
  689. {
  690. #ifdef GAME_DLL
  691. m_bUseTimer = bUseTimer;
  692. float flConditionDuration = m_bUseTimer ? tf_fortune_teller_fortune_duration.GetFloat() : PERMANENT_CONDITION;
  693. CUtlVector< CTFPlayer* > vecPlayers;
  694. CollectPlayers<CTFPlayer>( &vecPlayers, TEAM_ANY, true );
  695. for ( CTFPlayer *pPlayer : vecPlayers )
  696. {
  697. // Permanently add condition. We'll remove it when we're done
  698. pPlayer->m_Shared.AddCond( m_eCondition, flConditionDuration );
  699. }
  700. ListenForGameEvent( "player_spawn" );
  701. #endif
  702. }
  703. void OnDeactivateEffect()
  704. {
  705. #ifdef GAME_DLL
  706. m_bUseTimer = false;
  707. StopListeningForAllEvents();
  708. CUtlVector< CTFPlayer* > vecPlayers;
  709. CollectPlayers<CTFPlayer>( &vecPlayers, TEAM_ANY, true );
  710. for ( CTFPlayer *pPlayer : vecPlayers )
  711. {
  712. // We're done. Remove this condition
  713. pPlayer->m_Shared.RemoveCond( m_eCondition );
  714. }
  715. #endif
  716. }
  717. #ifdef GAME_DLL
  718. virtual void FireGameEvent( IGameEvent * event ) OVERRIDE
  719. {
  720. // don't do anything when players are in hell
  721. if ( TFGameRules() && TFGameRules()->ArePlayersInHell() )
  722. return;
  723. float flConditionDuration = m_bUseTimer ? tf_fortune_teller_fortune_duration.GetFloat() : PERMANENT_CONDITION;
  724. // Add the condition to anyone who spawns in
  725. if ( FStrEq( event->GetName(), "player_spawn" ) )
  726. {
  727. const int nUserID = event->GetInt( "userid" );
  728. CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByUserId( nUserID ) );
  729. if( pPlayer )
  730. {
  731. pPlayer->m_Shared.AddCond( m_eCondition, flConditionDuration );
  732. }
  733. }
  734. }
  735. #endif
  736. const char *GetActivationSound() const
  737. {
  738. return m_pszActivateSound;
  739. }
  740. const char *m_pszActivateSound;
  741. ETFCond m_eCondition;
  742. bool m_bUseTimer;
  743. };
  744. static CConditionFortuneTellerEffect g_FortuneTellerEffect_BalloonHead = { "Announcer.SD_Event_BigHeadCurse", TF_COND_BALLOON_HEAD }; // FIXME: need 2014 sound link
  745. static CConditionFortuneTellerEffect g_FortuneTellerEffect_MeleeOnly = { "Announcer.SD_Event_NoGunsCurse", TF_COND_MELEE_ONLY };
  746. static CConditionFortuneTellerEffect g_FortuneTellerEffect_SwimmingCurse = { "Announcer.SD_Event_SwimmingCurse", TF_COND_SWIMMING_CURSE }; // FIXME: need 2014 sound link
  747. static CConditionFortuneTellerEffect *g_GlobalFortuneTellerEffects[] =
  748. {
  749. &g_FortuneTellerEffect_BalloonHead,
  750. &g_FortuneTellerEffect_MeleeOnly,
  751. &g_FortuneTellerEffect_SwimmingCurse,
  752. };
  753. //-----------------------------------------------------------------------------
  754. // Purpose:
  755. //-----------------------------------------------------------------------------
  756. static const char *s_pszFortuneTellerSpinSound = "Halloween.WheelofFateQuiet";
  757. // Data Description
  758. BEGIN_DATADESC( CTFHalloweenFortuneTeller )
  759. #ifdef GAME_DLL
  760. DEFINE_INPUTFUNC( FIELD_VOID, "EnableFortuneTelling",InputEnableFortuneTelling ),
  761. DEFINE_INPUTFUNC( FIELD_VOID, "DisableFortuneTelling",InputDisableFortuneTelling ),
  762. DEFINE_INPUTFUNC( FIELD_VOID, "StartFortuneTelling", InputStartFortuneTelling ),
  763. DEFINE_INPUTFUNC( FIELD_VOID, "EndFortuneTelling", InputEndFortuneTelling ),
  764. DEFINE_OUTPUT( m_OnFortuneWarning, "OnFortuneWarning" ),
  765. DEFINE_OUTPUT( m_OnFortuneTold, "OnFortuneTold" ),
  766. DEFINE_OUTPUT( m_OnFortuneCurse, "OnFortuneCurse" ),
  767. DEFINE_OUTPUT( m_OnFortuneEnd, "OnFortuneEnd" ),
  768. DEFINE_KEYFIELD( m_iszRedTeleport, FIELD_STRING, "red_teleport" ),
  769. DEFINE_KEYFIELD( m_iszBlueTeleport, FIELD_STRING, "blue_teleport" ),
  770. #endif // GAME_DLLs
  771. END_DATADESC()
  772. LINK_ENTITY_TO_CLASS( halloween_fortune_teller, CTFHalloweenFortuneTeller );
  773. //-----------------------------------------------------------------------------
  774. // Purpose:
  775. //-----------------------------------------------------------------------------
  776. CTFHalloweenFortuneTeller::CTFHalloweenFortuneTeller()
  777. {
  778. #ifdef GAME_DLL
  779. m_bUseTimer = false;
  780. m_bWasUsingTimer = false;
  781. #endif // GAME_DLL
  782. }
  783. CTFHalloweenFortuneTeller::~CTFHalloweenFortuneTeller()
  784. {
  785. }
  786. //-----------------------------------------------------------------------------
  787. // Purpose:
  788. //-----------------------------------------------------------------------------
  789. void CTFHalloweenFortuneTeller::Precache()
  790. {
  791. PrecacheScriptSound( s_pszFortuneTellerSpinSound );
  792. for ( const auto *pEffect : g_GlobalFortuneTellerEffects )
  793. {
  794. PrecacheScriptSound( pEffect->GetActivationSound() );
  795. }
  796. PrecacheModel( "models/bots/merasmus/merasmas_misfortune_teller.mdl" );
  797. }
  798. //-----------------------------------------------------------------------------
  799. // Purpose:
  800. //-----------------------------------------------------------------------------
  801. void CTFHalloweenFortuneTeller::Spawn()
  802. {
  803. Precache();
  804. SetThink( NULL );
  805. SetModel( "models/bots/merasmus/merasmas_misfortune_teller.mdl" );
  806. ResetSequence( LookupSequence( "ref" ) );
  807. #ifdef GAME_DLL
  808. ResetTimer();
  809. ListenForGameEvent( "sentry_on_go_active" );
  810. #endif // GAME_DLLFireGameEvent
  811. }
  812. //-----------------------------------------------------------------------------
  813. // Purpose:
  814. //-----------------------------------------------------------------------------
  815. void CTFHalloweenFortuneTeller::UpdateOnRemove()
  816. {
  817. #ifdef GAME_DLL
  818. if ( m_pActiveFortune )
  819. {
  820. m_pActiveFortune->OnDeactivateEffect();
  821. m_pActiveFortune = NULL;
  822. }
  823. #endif // GAME_DLL
  824. BaseClass::UpdateOnRemove();
  825. }
  826. #ifdef GAME_DLL
  827. void CTFHalloweenFortuneTeller::InputEnableFortuneTelling( inputdata_t & )
  828. {
  829. m_bWasUsingTimer = m_bUseTimer;
  830. m_bUseTimer = true;
  831. if ( m_pActiveFortune )
  832. {
  833. m_pActiveFortune->OnDeactivateEffect();
  834. m_pActiveFortune = NULL;
  835. }
  836. if ( !m_bWasUsingTimer )
  837. UpdateFortuneTellerTime();
  838. }
  839. void CTFHalloweenFortuneTeller::InputDisableFortuneTelling( inputdata_t & )
  840. {
  841. m_bWasUsingTimer = m_bUseTimer;
  842. m_bUseTimer = false;
  843. if ( m_pActiveFortune )
  844. {
  845. m_pActiveFortune->OnDeactivateEffect();
  846. m_pActiveFortune = NULL;
  847. }
  848. // keep track of when we pause
  849. if ( m_bWasUsingTimer )
  850. PauseTimer();
  851. }
  852. void CTFHalloweenFortuneTeller::InputStartFortuneTelling( inputdata_t & )
  853. {
  854. // don't manually call this while using timer
  855. Assert( !m_bUseTimer );
  856. StartFortuneTell();
  857. }
  858. void CTFHalloweenFortuneTeller::InputEndFortuneTelling( inputdata_t & )
  859. {
  860. // don't manually call this while using timer
  861. Assert( !m_bUseTimer );
  862. EndFortuneTell();
  863. }
  864. void CTFHalloweenFortuneTeller::FireGameEvent( IGameEvent* pEvent )
  865. {
  866. const char *pszEventName = pEvent->GetName();
  867. if ( FStrEq( pszEventName, "sentry_on_go_active" ) )
  868. {
  869. // While curses are active, no ammo in sentries
  870. if ( m_pActiveFortune )
  871. {
  872. TFGameRules()->RemoveAllSentriesAmmo();
  873. return;
  874. }
  875. }
  876. }
  877. //-----------------------------------------------------------------------------
  878. // Purpose:
  879. //-----------------------------------------------------------------------------
  880. void CTFHalloweenFortuneTeller::UpdateFortuneTellerTime()
  881. {
  882. // unpause time, compute new start time
  883. m_flStartTime = gpGlobals->curtime - ( m_flPauseTime - m_flStartTime );
  884. const float flWarningTime = tf_fortune_teller_interval_time.GetFloat() - tf_fortune_teller_warning_time.GetFloat();
  885. float flTimePast = gpGlobals->curtime - m_flStartTime;
  886. // warning time
  887. if ( flTimePast < flWarningTime )
  888. {
  889. float flTimeBeforeEvent = flWarningTime - flTimePast;
  890. SetContextThink( &CTFHalloweenFortuneTeller::StartFortuneWarning, gpGlobals->curtime + flTimeBeforeEvent, "StartFortuneWarning" );
  891. }
  892. else
  893. {
  894. float flTimeBeforeEvent = tf_fortune_teller_interval_time.GetFloat() - flTimePast;
  895. SetContextThink( &CTFHalloweenFortuneTeller::StartFortuneTell, gpGlobals->curtime + flTimeBeforeEvent, "StartFortuneTell" );
  896. }
  897. }
  898. void CTFHalloweenFortuneTeller::PauseTimer()
  899. {
  900. m_flPauseTime = gpGlobals->curtime;
  901. // Cancel any fortunes in flight
  902. SetContextThink( NULL, 0, "StartFortuneWarning" );
  903. SetContextThink( NULL, 0, "StartFortuneTell" );
  904. SetContextThink( NULL, 0, "TellFortune" );
  905. SetContextThink( NULL, 0, "EndFortuneTell" );
  906. }
  907. void CTFHalloweenFortuneTeller::ResetTimer()
  908. {
  909. m_flStartTime = m_flPauseTime = gpGlobals->curtime;
  910. }
  911. //-----------------------------------------------------------------------------
  912. // Purpose:
  913. //-----------------------------------------------------------------------------
  914. void CTFHalloweenFortuneTeller::StartFortuneWarning()
  915. {
  916. // disable nagging
  917. TFGameRules()->StopDoomsdayTicketsTimer();
  918. m_OnFortuneWarning.FireOutput( this, this );
  919. SetContextThink( &CTFHalloweenFortuneTeller::StartFortuneTell, gpGlobals->curtime + tf_fortune_teller_warning_time.GetFloat(), "StartFortuneTell" );
  920. }
  921. //-----------------------------------------------------------------------------
  922. // Purpose:
  923. //-----------------------------------------------------------------------------
  924. void CTFHalloweenFortuneTeller::StartFortuneTell()
  925. {
  926. // Common effects.
  927. m_OnFortuneTold.FireOutput( this, this );
  928. // Broadcast the spin sound for the team-wide
  929. TFGameRules()->BroadcastSound( 255, s_pszFortuneTellerSpinSound );
  930. // Set when to actually perform the fortune telling
  931. SetContextThink( &CTFHalloweenFortuneTeller::TellFortune, gpGlobals->curtime + 6.1f, "TellFortune" );
  932. }
  933. //-----------------------------------------------------------------------------
  934. // Purpose:
  935. //-----------------------------------------------------------------------------
  936. void CTFHalloweenFortuneTeller::EndFortuneTell()
  937. {
  938. // resume nagging
  939. TFGameRules()->StartDoomsdayTicketsTimer();
  940. // Cancel any fortunes in flight
  941. SetContextThink( NULL, 0, "TellFortune" );
  942. m_OnFortuneEnd.FireOutput( this, this );
  943. if ( m_pActiveFortune )
  944. {
  945. m_pActiveFortune->OnDeactivateEffect();
  946. m_pActiveFortune = NULL;
  947. }
  948. if ( m_bUseTimer )
  949. {
  950. // restart fortune teller time
  951. ResetTimer();
  952. UpdateFortuneTellerTime();
  953. }
  954. }
  955. //-----------------------------------------------------------------------------
  956. // Purpose:
  957. //-----------------------------------------------------------------------------
  958. void CTFHalloweenFortuneTeller::TellFortune()
  959. {
  960. if ( m_pActiveFortune )
  961. {
  962. m_pActiveFortune->OnDeactivateEffect();
  963. m_pActiveFortune = NULL;
  964. }
  965. m_pActiveFortune = g_GlobalFortuneTellerEffects[ RandomInt( 0, ARRAYSIZE( g_GlobalFortuneTellerEffects ) - 1 ) ];
  966. if ( !m_pActiveFortune )
  967. return;
  968. // prevent stickies trap before the dance off
  969. TFGameRules()->RemoveAllProjectiles();
  970. TFGameRules()->RemoveAllSentriesAmmo();
  971. // Teleport RED
  972. CUtlVector< CTFPlayer* > vecRedPlayers;
  973. CollectPlayers<CTFPlayer>( &vecRedPlayers, TF_TEAM_RED, true );
  974. TFGameRules()->TeleportPlayersToTargetEntities( TF_TEAM_RED, m_iszRedTeleport.ToCStr(), &vecRedPlayers );
  975. // Teleport BLUE
  976. CUtlVector< CTFPlayer* > vecBluePlayers;
  977. CollectPlayers<CTFPlayer>( &vecBluePlayers, TF_TEAM_BLUE, true );
  978. TFGameRules()->TeleportPlayersToTargetEntities( TF_TEAM_BLUE, m_iszBlueTeleport.ToCStr(), &vecBluePlayers );
  979. CUtlVector< CTFPlayer* > vecPlayers;
  980. CollectPlayers<CTFPlayer>( &vecPlayers, TEAM_ANY, true );
  981. // Effects
  982. for( CTFPlayer * pPlayer : vecPlayers )
  983. {
  984. if ( pPlayer->m_Shared.IsCarryingObject() && pPlayer->m_Shared.GetCarriedObject() != NULL)
  985. {
  986. pPlayer->m_Shared.GetCarriedObject()->DetonateObject();
  987. }
  988. // Do a zoom effect
  989. pPlayer->SetFOV( pPlayer, tf_teleporter_fov_start.GetInt() );
  990. pPlayer->SetFOV( pPlayer, 0, 1.f, tf_teleporter_fov_start.GetInt() );
  991. // Screen flash
  992. color32 fadeColor = {255,255,255,100};
  993. UTIL_ScreenFade( pPlayer, fadeColor, 0.25, 0.4, FFADE_IN );
  994. // Plays music and makes it so Taunt() always performs a thriller
  995. pPlayer->m_Shared.AddCond( TF_COND_HALLOWEEN_THRILLER, 6.f );
  996. }
  997. // Speak after the 1st thriller
  998. SetContextThink( &CTFHalloweenFortuneTeller::SpeakThink, gpGlobals->curtime + 3.f, "SpeakThink" );
  999. // Apply the effect after the 2nd thriller
  1000. SetContextThink( &CTFHalloweenFortuneTeller::ApplyFortuneEffect, gpGlobals->curtime + 6.f, "FortuneActivate" );
  1001. const float flDanceTime = 0.5f;
  1002. const float flDanceDuration = 2.75f;
  1003. // Queue up the thrillers
  1004. SetContextThink( &CTFHalloweenFortuneTeller::DanceThink, gpGlobals->curtime + flDanceTime, "DanceThink1" );
  1005. SetContextThink( &CTFHalloweenFortuneTeller::DanceThink, gpGlobals->curtime + flDanceTime + flDanceDuration, "DanceThink2" );
  1006. }
  1007. void CTFHalloweenFortuneTeller::ApplyFortuneEffect()
  1008. {
  1009. m_OnFortuneCurse.FireOutput( this, this );
  1010. // Apply the actual effects.
  1011. if ( m_pActiveFortune )
  1012. m_pActiveFortune->OnActivateEffect( m_bUseTimer );
  1013. if ( m_bUseTimer )
  1014. {
  1015. SetContextThink( &CTFHalloweenFortuneTeller::EndFortuneTell, gpGlobals->curtime + tf_fortune_teller_fortune_duration.GetFloat(), "EndFortuneTell" );
  1016. }
  1017. }
  1018. void CTFHalloweenFortuneTeller::SpeakThink()
  1019. {
  1020. float flSoundDuration = 0.0f;
  1021. if ( m_pActiveFortune )
  1022. {
  1023. // Speak
  1024. const char *pszActivationSound = m_pActiveFortune->GetActivationSound();
  1025. TFGameRules()->BroadcastSound( 255, pszActivationSound );
  1026. // Do speaking anim
  1027. SetSequence( LookupSequence( "jaw_talking" ) );
  1028. //flSoundDuration = enginesound->GetSoundDuration( pszActivationSound );
  1029. }
  1030. // Tell ourselves to stop speaking after awhile
  1031. SetContextThink( &CTFHalloweenFortuneTeller::StopTalkingAnim, gpGlobals->curtime + flSoundDuration, "StopTalkingAnim" );
  1032. }
  1033. void CTFHalloweenFortuneTeller::StopTalkingAnim()
  1034. {
  1035. SetSequence( LookupSequence( "ref" ) );
  1036. }
  1037. void CTFHalloweenFortuneTeller::DanceThink()
  1038. {
  1039. CUtlVector< CTFPlayer* > vecPlayers;
  1040. CollectPlayers<CTFPlayer>( &vecPlayers, TEAM_ANY, true );
  1041. // No mere mortal can resist the magic of Merasmus
  1042. for( CTFPlayer * pPlayer : vecPlayers )
  1043. {
  1044. pPlayer->Taunt();
  1045. }
  1046. }
  1047. #endif // GAME_DLLs