summaryrefslogtreecommitdiff
path: root/game/shared/tf/tf_logic_halloween_2014.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/tf/tf_logic_halloween_2014.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/shared/tf/tf_logic_halloween_2014.cpp')
-rw-r--r--game/shared/tf/tf_logic_halloween_2014.cpp1288
1 files changed, 1288 insertions, 0 deletions
diff --git a/game/shared/tf/tf_logic_halloween_2014.cpp b/game/shared/tf/tf_logic_halloween_2014.cpp
new file mode 100644
index 0000000..be980b2
--- /dev/null
+++ b/game/shared/tf/tf_logic_halloween_2014.cpp
@@ -0,0 +1,1288 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Entities for use in the Robot Destruction TF2 game mode.
+//
+//=========================================================================//
+
+#include "cbase.h"
+#include "tf_logic_halloween_2014.h"
+#include "tf_shareddefs.h"
+#include "tf_gamerules.h"
+
+#ifdef GAME_DLL
+ #include "particle_parse.h"
+ #include "halloween/tf_weapon_spellbook.h"
+ #include "tf_weapon_sniperrifle.h"
+ #include "ai_activity.h"
+ #include "halloween/halloween_base_boss.h"
+ #include "halloween/tf_weapon_spellbook.h"
+ #include "engine/IEngineSound.h"
+ #include "tf_props.h"
+#endif
+
+IMPLEMENT_AUTO_LIST( IMinigameAutoList );
+
+#ifdef GAME_DLL
+extern ConVar tf_teleporter_fov_time;
+extern ConVar tf_teleporter_fov_start;
+
+ConVar tf_fortune_teller_warning_time( "tf_fortune_teller_warning_time", "2", FCVAR_CHEAT, "Warning time (in second) before fortune teller tells a fortune." );
+ConVar tf_fortune_teller_interval_time( "tf_fortune_teller_interval_time", "120", FCVAR_CHEAT, "Time until the next fortune teller event (in second)." );
+ConVar tf_fortune_teller_fortune_duration( "tf_fortune_teller_fortune_duration", "30", FCVAR_CHEAT, "Duration of the fortune time." );
+
+ConVar tf_minigame_suddendeath_time( "tf_minigame_suddendeath_time", "-1", FCVAR_CHEAT, "Override Sudden Death Time." );
+
+BEGIN_DATADESC( CTFMiniGame )
+
+ DEFINE_KEYFIELD( m_iszYourTeamScoreSound, FIELD_STRING, "your_team_score_sound" ),
+ DEFINE_KEYFIELD( m_iszEnemyTeamScoreSound, FIELD_STRING, "enemy_team_score_sound" ),
+ DEFINE_KEYFIELD( m_iszHudResFile, FIELD_STRING, "hud_res_file" ),
+ DEFINE_KEYFIELD( m_pszTeamSpawnPoint[ TF_TEAM_RED ], FIELD_STRING, "RedSpawn" ),
+ DEFINE_KEYFIELD( m_pszTeamSpawnPoint[ TF_TEAM_BLUE ], FIELD_STRING, "BlueSpawn" ),
+ DEFINE_KEYFIELD( m_bMinigameAllowedInRamdomPool, FIELD_BOOLEAN, "InRandomPool" ),
+ DEFINE_KEYFIELD( m_nMaxScoreForMiniGame, FIELD_INTEGER, "MaxScore" ),
+ DEFINE_KEYFIELD( m_eScoringType, FIELD_INTEGER, "ScoreType" ),
+ DEFINE_KEYFIELD( m_flSuddenDeathTime, FIELD_FLOAT, "SuddenDeathTime" ),
+
+ DEFINE_OUTPUT( m_OnRedHitMaxScore, "OnRedHitMaxScore" ),
+ DEFINE_OUTPUT( m_OnBlueHitMaxScore, "OnBlueHitMaxScore" ),
+ DEFINE_OUTPUT( m_OnTeleportToMinigame, "OnTeleportToMinigame" ),
+ DEFINE_OUTPUT( m_OnReturnFromMinigame, "OnReturnFromMinigame" ),
+ DEFINE_OUTPUT( m_OnAllRedDead, "OnAllRedDead" ),
+ DEFINE_OUTPUT( m_OnAllBlueDead, "OnAllBlueDead" ),
+ DEFINE_OUTPUT( m_OnSuddenDeathStart, "OnSuddenDeathStart" ),
+
+ DEFINE_INPUTFUNC( FIELD_INTEGER, "ScoreTeamRed", InputScoreTeamRed ),
+ DEFINE_INPUTFUNC( FIELD_INTEGER, "ScoreTeamBlue", InputScoreTeamBlue ),
+ DEFINE_INPUTFUNC( FIELD_STRING, "ChangeHudResFile", InputChangeHudResFile ),
+
+END_DATADESC()
+#endif
+
+LINK_ENTITY_TO_CLASS( tf_base_minigame, CTFMiniGame );
+IMPLEMENT_NETWORKCLASS_ALIASED( TFMiniGame, DT_TFMinigame )
+
+BEGIN_NETWORK_TABLE_NOBASE( CTFMiniGame, DT_TFMinigame )
+#ifdef CLIENT_DLL
+ RecvPropArray3( RECVINFO_ARRAY( m_nMinigameTeamScore ), RecvPropInt( RECVINFO( m_nMinigameTeamScore[0] ) ) ),
+ RecvPropInt( RECVINFO( m_nMaxScoreForMiniGame ) ),
+ RecvPropString( RECVINFO( m_pszHudResFile ) ),
+ RecvPropInt( RECVINFO( m_eScoringType ) ),
+#else
+ SendPropArray3( SENDINFO_ARRAY3( m_nMinigameTeamScore ), SendPropInt( SENDINFO_ARRAY( m_nMinigameTeamScore ), -1, SPROP_UNSIGNED | SPROP_VARINT ) ),
+ SendPropInt( SENDINFO( m_nMaxScoreForMiniGame ), -1, SPROP_VARINT | SPROP_UNSIGNED ),
+ SendPropString( SENDINFO( m_pszHudResFile ) ),
+ SendPropInt( SENDINFO( m_eScoringType ), -1, SPROP_VARINT | SPROP_UNSIGNED ),
+#endif
+END_NETWORK_TABLE()
+
+#define NO_TEAM_ADVANTAGE -1
+
+
+CTFMiniGame::CTFMiniGame()
+{
+ m_nMaxScoreForMiniGame = 0;
+ m_nMinigameTeamScore.Set( TF_TEAM_RED, 0 );
+ m_nMinigameTeamScore.Set( TF_TEAM_BLUE, 0 );
+#ifdef GAME_DLL
+ m_bMinigameAllowedInRamdomPool = true;
+ m_bIsActive = false;
+ m_pszTeamSpawnPoint[ TF_TEAM_RED ] = NULL;
+ m_pszTeamSpawnPoint[ TF_TEAM_BLUE ] = NULL;
+ m_eMinigameType = MINIGAME_GENERIC;
+ m_iszYourTeamScoreSound = NULL_STRING;
+ m_iszEnemyTeamScoreSound = NULL_STRING;
+ m_flSuddenDeathTime = -1.0f; // No sudden death.
+ m_iAdvantagedTeam = NO_TEAM_ADVANTAGE;
+
+ ListenForGameEvent( "player_death" );
+ ListenForGameEvent( "player_turned_to_ghost" );
+ ListenForGameEvent( "player_disconnect" );
+ ListenForGameEvent( "player_team" );
+ ListenForGameEvent( "player_spawn" );
+#endif
+}
+
+#ifdef GAME_DLL
+void CTFMiniGame::Spawn()
+{
+ Precache();
+
+ BaseClass::Spawn();
+ V_strncpy( m_pszHudResFile.GetForModify(), STRING( m_iszHudResFile ), MAX_PATH );
+}
+
+void CTFMiniGame::Precache()
+{
+ BaseClass::Precache();
+
+ if ( m_iszYourTeamScoreSound.ToCStr() )
+ {
+ PrecacheScriptSound( m_iszYourTeamScoreSound.ToCStr() );
+ }
+ if ( m_iszEnemyTeamScoreSound.ToCStr() )
+ {
+ PrecacheScriptSound( m_iszEnemyTeamScoreSound.ToCStr() );
+ }
+}
+
+void CTFMiniGame::FireGameEvent( IGameEvent * pEvent )
+{
+ // Only look for dead players when the round is running or else we'll get
+ // victories while in the spawn room as we respawn
+ if ( ( TFGameRules() && TFGameRules()->State_Get() != GR_STATE_RND_RUNNING ) || !m_bIsActive )
+ return;
+
+ const char *pszEventName = pEvent->GetName();
+
+ if ( !V_strcmp( pszEventName, "player_turned_to_ghost" )
+ || !V_strcmp( pszEventName, "player_disconnect" )
+ || !V_strcmp( pszEventName, "player_team" )
+ || !V_strcmp( pszEventName, "player_death" )
+ || !V_strcmp( pszEventName, "player_spawn" ) )
+ {
+ bool bCanWin = true;
+ // Blue gets the chance to win first
+ UpdateDeadPlayers( TF_TEAM_RED, m_OnBlueHitMaxScore, m_OnAllRedDead, bCanWin );
+ UpdateDeadPlayers( TF_TEAM_BLUE, m_OnRedHitMaxScore, m_OnAllBlueDead, bCanWin );
+ }
+}
+
+void CTFMiniGame::TeleportAllPlayers()
+{
+ // remove all projectiles and objects before we go to minigame
+ TFGameRules()->RemoveAllProjectilesAndBuildings();
+
+ CUtlVector< CTFPlayer* > vecTeleportedPlayers;
+ TFGameRules()->TeleportPlayersToTargetEntities( TF_TEAM_RED, m_pszTeamSpawnPoint[ TF_TEAM_RED ], &vecTeleportedPlayers );
+ TFGameRules()->TeleportPlayersToTargetEntities( TF_TEAM_BLUE, m_pszTeamSpawnPoint[ TF_TEAM_BLUE ], &vecTeleportedPlayers );
+
+ FOR_EACH_VEC( vecTeleportedPlayers, i )
+ {
+ OnTeleportPlayerToMinigame( vecTeleportedPlayers[i] );
+ }
+ m_iAdvantagedTeam = NO_TEAM_ADVANTAGE; // reset advantage
+ m_bIsActive = true;
+
+ m_OnTeleportToMinigame.FireOutput( this, this );
+
+ if ( tf_minigame_suddendeath_time.GetFloat() != -1 )
+ {
+ m_flSuddenDeathTime = tf_minigame_suddendeath_time.GetFloat();
+ DevMsg( "Setting m_flSuddenDeathTime to %f\n", m_flSuddenDeathTime );
+ }
+
+ // If we've got a sudden death start time, trigger a think function callback.
+ if ( m_flSuddenDeathTime >= 0.0f )
+ {
+ SetContextThink( &CTFHalloweenMinigame::SuddenDeathTimeStartThink, gpGlobals->curtime + m_flSuddenDeathTime, "SuddenDeathTimeStart" );
+ }
+}
+
+void CTFMiniGame::OnTeleportPlayerToMinigame( CTFPlayer *pPlayer )
+{
+ // Do a zoom effect
+ pPlayer->SetFOV( pPlayer, tf_teleporter_fov_start.GetInt() );
+ pPlayer->SetFOV( pPlayer, 0, 1.f, tf_teleporter_fov_start.GetInt() );
+
+ // Screen flash
+ color32 fadeColor = {255,255,255,100};
+ UTIL_ScreenFade( pPlayer, fadeColor, 0.25, 0.4, FFADE_IN );
+}
+
+void CTFMiniGame::ReturnAllPlayers()
+{
+ // Send everyone back
+ CUtlVector< CTFPlayer * > vecPlayers;
+ CollectPlayers( &vecPlayers, TEAM_ANY, false );
+ FOR_EACH_VEC( vecPlayers, i )
+ {
+ vecPlayers[ i ]->ForceRespawn();
+ }
+
+ m_nMinigameTeamScore.Set( TF_TEAM_RED, 0 );
+ m_nMinigameTeamScore.Set( TF_TEAM_BLUE, 0 );
+
+ m_bIsActive = false;
+
+ m_OnReturnFromMinigame.FireOutput( this, this );
+}
+
+void CTFMiniGame::ScorePointsForTeam( int nTeamNum, int nPoints )
+{
+ // Don't tally more points if a team already hit the max
+ if ( !m_bIsActive || ( m_nMinigameTeamScore.Get( TF_TEAM_RED ) == m_nMaxScoreForMiniGame ) || ( m_nMinigameTeamScore.Get( TF_TEAM_BLUE ) == m_nMaxScoreForMiniGame ) )
+ {
+ return;
+ }
+
+ // Are we playing sudden death right now?
+ bool bInSuddenDeath = ( m_flSuddenDeathTime == 0.0f );
+
+ // Increment score for the appropriate team
+ auto& nTeamPoints = m_nMinigameTeamScore.GetForModify( nTeamNum );
+ nTeamPoints += nPoints;
+ nTeamPoints = clamp( nTeamPoints, 0, m_nMaxScoreForMiniGame );
+
+ // If they went to the max score or we're in sudden death, fire winning output.
+ if ( ( nTeamPoints == m_nMaxScoreForMiniGame ) || bInSuddenDeath )
+ {
+ auto& eventMaxScoreHit = ( nTeamNum == TF_TEAM_RED ) ? m_OnRedHitMaxScore : m_OnBlueHitMaxScore;
+ eventMaxScoreHit.FireOutput( this, this );
+
+ CUtlVector<CTFPlayer *> vecPlayers;
+ CollectPlayers( &vecPlayers, nTeamNum );
+
+ for ( auto pPlayer : vecPlayers )
+ {
+ HatAndMiscEconEntities_OnOwnerKillEaterEventNoParter( pPlayer, kKillEaterEvent_Halloween_MinigamesWon );
+
+ IGameEvent *pEvent = gameeventmanager->CreateEvent( "minigame_won" );
+ if ( pEvent )
+ {
+ pEvent->SetInt( "player", pPlayer->GetUserID() );
+ pEvent->SetInt( "game", GetMinigameType() );
+ gameeventmanager->FireEvent( pEvent, true );
+ }
+ }
+ }
+
+ // Can not be specified, and we dont want to do anything in that case
+ if ( m_iszYourTeamScoreSound.ToCStr() && *m_iszYourTeamScoreSound.ToCStr()
+ && m_iszEnemyTeamScoreSound.ToCStr() && *m_iszEnemyTeamScoreSound.ToCStr() )
+ {
+ // Get everyone
+ CUtlVector< CTFPlayer* > vecPlayer;
+ CollectPlayers( &vecPlayer );
+
+ // Play a sound based on if the scoring team is the player's team
+ for( CTFPlayer *pPlayer : vecPlayer )
+ {
+ EmitSound_t params;
+ float soundlen = 0;
+ params.m_flSoundTime = 0;
+ params.m_pSoundName = NULL;
+ params.m_pflSoundDuration = &soundlen;
+ params.m_pSoundName = pPlayer->GetTeamNumber() == nTeamNum ? m_iszYourTeamScoreSound.ToCStr() : m_iszEnemyTeamScoreSound.ToCStr();
+ params.m_nPitch = RemapValClamped( nTeamPoints, m_nMaxScoreForMiniGame * 0.75, m_nMaxScoreForMiniGame, 100, 120 );
+ params.m_nFlags |= SND_CHANGE_PITCH;
+ params.m_flVolume = 0.25f; // Pretty quiet
+ params.m_nFlags |= SND_CHANGE_VOL;
+
+ // Play in the player's ears
+ CSingleUserRecipientFilter filter( pPlayer );
+ filter.MakeReliable();
+ pPlayer->StopSound( params.m_pSoundName );
+ pPlayer->EmitSound( filter, pPlayer->entindex(), params );
+ }
+ }
+}
+
+void CTFMiniGame::InputScoreTeamRed( inputdata_t &inputdata )
+{
+ ScorePointsForTeam( TF_TEAM_RED, inputdata.value.Int() );
+ InternalHandleInputScore( inputdata );
+}
+
+void CTFMiniGame::InputScoreTeamBlue( inputdata_t &inputdata )
+{
+ ScorePointsForTeam( TF_TEAM_BLUE, inputdata.value.Int() );
+ InternalHandleInputScore( inputdata );
+}
+
+void CTFMiniGame::InputChangeHudResFile( inputdata_t &inputdata )
+{
+ const char *resFile = inputdata.value.String();
+
+ Assert( resFile && resFile[ 0 ] );
+ if ( resFile && resFile[ 0 ] )
+ {
+ V_strncpy( m_pszHudResFile.GetForModify(), resFile, MAX_PATH );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find spawn point entity for specified team
+//-----------------------------------------------------------------------------
+const char *CTFMiniGame::GetTeamSpawnPointName( int nTeamNum ) const
+{
+ if ( !IsValidTFTeam( nTeamNum ) )
+ return NULL;
+
+ return m_pszTeamSpawnPoint[ nTeamNum ];
+}
+
+void CTFMiniGame::UpdateDeadPlayers( int nTeam, COutputEvent& eventWin, COutputEvent& eventAllDead, bool& bCanWin )
+{
+ // Update the score for a team
+ CUtlVector< CTFPlayer * > vecPlayers;
+ CollectPlayers( &vecPlayers, nTeam, true );
+ int nNumDead = 0;
+ FOR_EACH_VEC( vecPlayers, i )
+ {
+ // Tally the number dead/ghosts
+ if ( vecPlayers[i]->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) || vecPlayers[i]->IsDead() )
+ ++nNumDead;
+ }
+
+ // Only update the score if this is the SCORING_TYPE_PLAYERS_ALIVE mode
+ if ( m_eScoringType == SCORING_TYPE_PLAYERS_ALIVE )
+ {
+ auto& nScore = m_nMinigameTeamScore.GetForModify( nTeam );
+ nScore = vecPlayers.Count() - nNumDead;
+ }
+
+ // Everyone is dead
+ if ( nNumDead == vecPlayers.Count() && !vecPlayers.IsEmpty() )
+ {
+ m_bIsActive = false;
+
+ // If everyone is dead, and we're allowed to win, fire the win event
+ if ( bCanWin && m_eScoringType == SCORING_TYPE_PLAYERS_ALIVE )
+ {
+ eventWin.FireOutput( this, this );
+ bCanWin = false;
+ }
+
+ // Fire the team dead event
+ eventAllDead.FireOutput( this, this );
+
+ CUtlVector<CTFPlayer *> vecEnemyPlayers;
+ CollectPlayers( &vecEnemyPlayers, GetEnemyTeam( nTeam ) );
+
+ for ( auto pPlayer : vecEnemyPlayers )
+ {
+ HatAndMiscEconEntities_OnOwnerKillEaterEventNoParter( pPlayer, kKillEaterEvent_Halloween_MinigamesWon );
+
+ IGameEvent *pEvent = gameeventmanager->CreateEvent( "minigame_won" );
+ if ( pEvent )
+ {
+ pEvent->SetInt( "player", pPlayer->GetUserID() );
+ pEvent->SetInt( "game", GetMinigameType() );
+ gameeventmanager->FireEvent( pEvent, true );
+ }
+ }
+ }
+}
+
+// "SuddenDeathTimeStart"
+void CTFMiniGame::SuddenDeathTimeStartThink()
+{
+ // If we're active, fire the sudden death time start event.
+ if ( m_bIsActive )
+ {
+ m_flSuddenDeathTime = 0.0f; // In sudden death!
+ m_OnSuddenDeathStart.FireOutput( this, this );
+ }
+}
+
+#else
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFMiniGame::GetScoreForTeam( int nTeamNum ) const
+{
+ if ( !IsValidTFTeam( nTeamNum ) )
+ return 0;
+
+ return m_nMinigameTeamScore[ nTeamNum ];
+}
+#endif // GAME_DLL
+
+
+#ifdef GAME_DLL
+BEGIN_DATADESC( CTFHalloweenMinigame )
+
+ DEFINE_KEYFIELD( m_eMinigameType, FIELD_INTEGER, "MinigameType" ),
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "KartWinAnimationRed", InputKartWinAnimationRed ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "KartWinAnimationBlue", InputKartWinAnimationBlue ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "KartLoseAnimationRed", InputKartLoseAnimationRed ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "KartLoseAnimationBlue", InputKartLoseAnimationBlue ),
+ DEFINE_INPUTFUNC( FIELD_STRING, "EnableSpawnBoss", InputEnableSpawnBoss ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "DisableSpawnBoss", InputDisableSpawnBoss ),
+
+END_DATADESC()
+#endif
+
+LINK_ENTITY_TO_CLASS( tf_halloween_minigame, CTFHalloweenMinigame );
+IMPLEMENT_NETWORKCLASS_ALIASED( TFHalloweenMinigame, DT_TFHalloweenMinigame )
+
+BEGIN_NETWORK_TABLE( CTFHalloweenMinigame, DT_TFHalloweenMinigame )
+END_NETWORK_TABLE()
+
+#ifdef GAME_DLL
+CTFHalloweenMinigame::CTFHalloweenMinigame()
+{
+ m_hBossSpawnPoint = NULL;
+ ListenForGameEvent( "pumpkin_lord_killed" );
+}
+
+
+void CTFHalloweenMinigame::Spawn()
+{
+ BaseClass::Spawn();
+}
+
+
+void CTFHalloweenMinigame::FireGameEvent( IGameEvent * event )
+{
+ BaseClass::FireGameEvent( event );
+
+ if ( FStrEq( event->GetName(), "pumpkin_lord_killed" ) )
+ {
+ if ( m_hBossSpawnPoint && ( !m_hHalloweenBoss || m_hHalloweenBoss->IsMarkedForDeletion() ) )
+ {
+ m_hHalloweenBoss = CHalloweenBaseBoss::SpawnBossAtPos( HALLOWEEN_BOSS_HHH, m_hBossSpawnPoint->GetAbsOrigin() );
+ }
+ }
+}
+
+void CTFHalloweenMinigame::InternalHandleInputScore( inputdata_t &inputdata )
+{
+ CPropSoccerBall *pSoccerBall = dynamic_cast< CPropSoccerBall* >( inputdata.pActivator );
+ if ( pSoccerBall )
+ {
+ CTFPlayer *pTFPlayer = pSoccerBall->GetLastToucher();
+ if ( pTFPlayer && TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
+ {
+ pTFPlayer->AwardAchievement( ACHIEVEMENT_TF_HALLOWEEN_DOOMSDAY_SCORE_GOALS );
+ }
+ }
+}
+
+void CTFHalloweenMinigame::TeleportAllPlayers()
+{
+ CUtlVector< CTFPlayer * > vecPlayers;
+ CollectPlayers( &vecPlayers, TEAM_ANY, false );
+
+ FOR_EACH_VEC( vecPlayers, i )
+ {
+ CTFPlayer *pPlayer = vecPlayers[i];
+ // Only do these effects if the player is alive
+ if ( !pPlayer->IsAlive() )
+ continue;
+
+ // Fade to white
+ color32 fadeColor = {255,255,255,255};
+ UTIL_ScreenFade( pPlayer, fadeColor, 2.f, 0.5f, FFADE_OUT | FFADE_PURGE );
+
+ // Do a zoom in effect
+ pPlayer->SetFOV( pPlayer, 10.f, 2.5f, 0.f );
+ // Rumble like something important happened
+ UTIL_ScreenShake(pPlayer->GetAbsOrigin(), 100.f, 150, 4.f, 0.f, SHAKE_START, true );
+ }
+
+ // Play a sound for all players
+ TFGameRules()->BroadcastSound( 255, "Halloween.hellride" );
+
+ SetContextThink( &CTFHalloweenMinigame::TeleportAllPlayersThink, gpGlobals->curtime + 2.0f, "TeleportToHell" );
+}
+
+void CTFHalloweenMinigame::TeleportAllPlayersThink()
+{
+ RemoveAll2013HalloweenTeleportSpellsInMidFlight();
+
+ CUtlVector< CTFPlayer * > vecPlayers;
+ CollectPlayers( &vecPlayers, TEAM_ANY, false );
+
+ BaseClass::TeleportAllPlayers();
+
+ FOR_EACH_VEC( vecPlayers, i )
+ {
+ CTFPlayer *pPlayer = vecPlayers[i];
+
+ pPlayer->CancelEurekaTeleport();
+
+ // Fade from white
+ color32 fadeColor = {255,255,255,255};
+ UTIL_ScreenFade( pPlayer, fadeColor, 1.f, 0.2f, FFADE_IN );
+ }
+
+ // Set this flag. Lets us check elsewhere if it's hell time
+ // HACK: Should we be doing this for 2014?!?
+ if ( TFGameRules() )
+ {
+ TFGameRules()->SetPlayersInHell( true );
+ }
+}
+
+void CTFHalloweenMinigame::OnTeleportPlayerToMinigame( CTFPlayer *pPlayer )
+{
+ BaseClass::OnTeleportPlayerToMinigame( pPlayer );
+
+ pPlayer->m_Shared.AddCond( TF_COND_HALLOWEEN_IN_HELL );
+ pPlayer->m_Shared.AddCond( TF_COND_HALLOWEEN_KART );
+
+ const float flCageTime = 3.f;
+ pPlayer->m_Shared.AddCond( TF_COND_HALLOWEEN_KART_CAGE, flCageTime );
+ pPlayer->m_Shared.AddCond( TF_COND_FREEZE_INPUT, flCageTime );
+
+ pPlayer->SetAbsVelocity( vec3_origin );
+
+ pPlayer->EmitSound( "BumperCar.Spawn" );
+
+ // if its set
+ if ( m_iAdvantagedTeam != NO_TEAM_ADVANTAGE )
+ {
+ if ( pPlayer->GetTeamNumber() != m_iAdvantagedTeam )
+ {
+ pPlayer->AddKartDamage( 66 );
+ }
+ }
+}
+
+void CTFHalloweenMinigame::ReturnAllPlayers()
+{
+ // Set this flag. Lets us check elsewhere if it's hell time
+ // HACK: Should we be doing this for 2014?!?
+ if ( TFGameRules() )
+ {
+ TFGameRules()->SetPlayersInHell( false );
+ }
+
+ BaseClass::ReturnAllPlayers();
+}
+
+void CTFHalloweenMinigame::InputKartWinAnimationRed( inputdata_t &inputdata )
+{
+ CUtlVector< CTFPlayer* > players;
+ CollectPlayers( &players, TF_TEAM_RED, true );
+ for ( int i=0; i<players.Count(); ++i )
+ {
+ if ( players[i]->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
+ {
+ players[i]->DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE, ACT_KART_GESTURE_POSITIVE );
+ }
+ }
+}
+
+void CTFHalloweenMinigame::InputKartWinAnimationBlue( inputdata_t &inputdata )
+{
+ CUtlVector< CTFPlayer* > players;
+ CollectPlayers( &players, TF_TEAM_BLUE, true );
+ for ( int i=0; i<players.Count(); ++i )
+ {
+ if ( players[i]->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
+ {
+ players[i]->DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE, ACT_KART_GESTURE_POSITIVE );
+ }
+ }
+}
+
+void CTFHalloweenMinigame::InputKartLoseAnimationRed( inputdata_t &inputdata )
+{
+ CUtlVector< CTFPlayer* > players;
+ CollectPlayers( &players, TF_TEAM_RED, true );
+ for ( int i=0; i<players.Count(); ++i )
+ {
+ if ( players[i]->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
+ {
+ players[i]->DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE, ACT_KART_GESTURE_NEGATIVE );
+ }
+ }
+}
+
+void CTFHalloweenMinigame::InputKartLoseAnimationBlue( inputdata_t &inputdata )
+{
+ CUtlVector< CTFPlayer* > players;
+ CollectPlayers( &players, TF_TEAM_BLUE, true );
+ for ( int i=0; i<players.Count(); ++i )
+ {
+ if ( players[i]->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
+ {
+ players[i]->DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE, ACT_KART_GESTURE_NEGATIVE );
+ }
+ }
+}
+
+void CTFHalloweenMinigame::InputEnableSpawnBoss( inputdata_t &inputdata )
+{
+ if ( !m_hBossSpawnPoint )
+ {
+ const char *pszSpawnPoint = inputdata.value.String();
+ if ( pszSpawnPoint )
+ {
+ m_hBossSpawnPoint = gEntList.FindEntityByName( NULL, pszSpawnPoint );
+ }
+ }
+
+ if ( m_hBossSpawnPoint )
+ {
+ m_hHalloweenBoss = CHalloweenBaseBoss::SpawnBossAtPos( HALLOWEEN_BOSS_HHH, m_hBossSpawnPoint->GetAbsOrigin() );
+ }
+}
+
+void CTFHalloweenMinigame::InputDisableSpawnBoss( inputdata_t &inputdata )
+{
+ m_hBossSpawnPoint = NULL;
+}
+
+#endif
+
+#ifdef GAME_DLL
+BEGIN_DATADESC( CTFHalloweenMinigame_FallingPlatforms )
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "ChoosePlatform", InputChoosePlatform ),
+
+ DEFINE_OUTPUT( m_OutputSafePlatform, "OutputSafePlatform" ),
+ DEFINE_OUTPUT( m_OutputRemovePlatform, "OutputRemovePlatform" ),
+
+END_DATADESC()
+#endif
+
+LINK_ENTITY_TO_CLASS( tf_halloween_minigame_falling_platforms, CTFHalloweenMinigame_FallingPlatforms );
+IMPLEMENT_NETWORKCLASS_ALIASED( TFHalloweenMinigame_FallingPlatforms, DT_TFHalloweenMinigame_FallingPlatforms )
+
+BEGIN_NETWORK_TABLE( CTFHalloweenMinigame_FallingPlatforms, DT_TFHalloweenMinigame_FallingPlatforms )
+END_NETWORK_TABLE()
+
+#ifdef GAME_DLL
+CTFHalloweenMinigame_FallingPlatforms::CTFHalloweenMinigame_FallingPlatforms()
+{
+ // Corners
+ CUtlVector<int> vecFirstSet;
+ vecFirstSet.AddToTail( 1 );
+ vecFirstSet.AddToTail( 3 );
+ vecFirstSet.AddToTail( 7 );
+ vecFirstSet.AddToTail( 9 );
+ vecFirstSet.Shuffle();
+
+ // On Axis
+ CUtlVector<int> vecSecondSet;
+ vecSecondSet.AddToTail( 2 );
+ vecSecondSet.AddToTail( 4 );
+ vecSecondSet.AddToTail( 6 );
+ vecSecondSet.AddToTail( 8 );
+ vecSecondSet.Shuffle();
+
+ m_vecRemainingPlatforms.AddVectorToTail( vecFirstSet );
+ m_vecRemainingPlatforms.AddVectorToTail( vecSecondSet );
+
+ m_vecRemainingPlatforms.AddToTail( 5 ); // The center
+}
+
+void CTFHalloweenMinigame_FallingPlatforms::InputChoosePlatform( inputdata_t &inputdata )
+{
+ variant_t nVal;
+ // If there's more than 1 platforms remaining, mark another to never come back
+ if ( m_vecRemainingPlatforms.Count() > 1 )
+ {
+ nVal.SetInt( m_vecRemainingPlatforms.Head() );
+ m_vecRemainingPlatforms.Remove( 0 );
+ m_OutputRemovePlatform.FireOutput( nVal, this, this );
+ }
+
+ // The which one is supposed to be the safe platform
+ int nIndex = RandomInt( 0, m_vecRemainingPlatforms.Count() - 1 );
+ int nSafePlatform = m_vecRemainingPlatforms[ nIndex ];
+ nVal.SetInt( nSafePlatform );
+ m_OutputSafePlatform.FireOutput( nVal, this, this );
+}
+
+void CTFHalloweenMinigame_FallingPlatforms::FireGameEvent( IGameEvent * pEvent )
+{
+ const char *pszEventName = pEvent->GetName();
+
+ // In the falling platforms game, we dont want ghosts to get stuck in platforms.
+ // This hack sets the collision of ghosts to collide with triggers, but not with
+ // other players. This should allow us to use the relative teleport trigger on the
+ // ghosts to prevent stuckage.
+ if ( m_bIsActive && !V_strcmp( pszEventName, "player_turned_to_ghost" ) )
+ {
+ CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByUserId( pEvent->GetInt( "userid" ) ) );
+ if ( pPlayer )
+ {
+ pPlayer->SetSolid( SOLID_BBOX );
+ pPlayer->SetSolidFlags( FSOLID_NOT_STANDABLE );
+ pPlayer->SetCollisionGroup( COLLISION_GROUP_DEBRIS_TRIGGER ); // Dont run into other players
+ }
+ }
+
+ BaseClass::FireGameEvent( pEvent );
+}
+
+#endif
+
+
+
+#ifdef GAME_DLL
+BEGIN_DATADESC( CTFMinigameLogic )
+
+ DEFINE_INPUTFUNC( FIELD_INTEGER, "TeleportToMinigame", InputTeleportToMinigame ),
+ DEFINE_INPUTFUNC( FIELD_STRING, "SetAdvantageTeam", InputSetAdvantageTeam ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "TeleportToRandomMinigame", InputTeleportToRandomMinigame ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "ReturnFromMinigame", InputReturnFromMinigame ),
+
+END_DATADESC()
+#endif
+
+LINK_ENTITY_TO_CLASS( tf_logic_minigames, CTFMinigameLogic );
+IMPLEMENT_NETWORKCLASS_ALIASED( TFMinigameLogic, DT_TFMinigameLogic )
+
+BEGIN_NETWORK_TABLE_NOBASE( CTFMinigameLogic, DT_TFMinigameLogic )
+#ifdef CLIENT_DLL
+ RecvPropEHandle( RECVINFO( m_hActiveMinigame ) ),
+#else
+ SendPropEHandle( SENDINFO( m_hActiveMinigame ) ),
+#endif
+END_NETWORK_TABLE()
+
+CTFMinigameLogic* CTFMinigameLogic::m_sMinigameLogic = NULL;
+
+CTFMinigameLogic::CTFMinigameLogic()
+{
+ m_hActiveMinigame = NULL;
+ m_sMinigameLogic = this;
+#ifdef GAME_DLL
+ m_iAdvantagedTeam = NO_TEAM_ADVANTAGE;
+#endif
+}
+
+CTFMinigameLogic::~CTFMinigameLogic()
+{
+ if ( m_sMinigameLogic == this )
+ {
+ m_sMinigameLogic = NULL;
+ }
+}
+
+#ifdef GAME_DLL
+
+void CTFMinigameLogic::TeleportToMinigame( int nMiniGameIndex )
+{
+ Assert( m_hActiveMinigame == NULL );
+ CTFMiniGame *pMinigame = static_cast< CTFMiniGame* >( IMinigameAutoList::AutoList()[ nMiniGameIndex ] );
+
+ if ( pMinigame )
+ {
+ m_hActiveMinigame = pMinigame;
+ m_hActiveMinigame->SetAdvantagedTeam( m_iAdvantagedTeam );
+ m_hActiveMinigame->TeleportAllPlayers( );
+ m_iAdvantagedTeam = NO_TEAM_ADVANTAGE;
+ }
+}
+
+void CTFMinigameLogic::ReturnFromMinigame()
+{
+ if ( m_hActiveMinigame )
+ {
+ m_hActiveMinigame->ReturnAllPlayers();
+ }
+
+ m_hActiveMinigame = NULL;
+}
+
+
+void CTFMinigameLogic::InputTeleportToMinigame( inputdata_t &inputdata )
+{
+ int nInput = inputdata.value.Int();
+
+ if ( nInput >= 0 && nInput < IMinigameAutoList::AutoList().Count() )
+ {
+ TeleportToMinigame( nInput );
+ }
+}
+
+void CTFMinigameLogic::InputSetAdvantageTeam( inputdata_t &inputdata )
+{
+ m_iAdvantagedTeam = FStrEq( inputdata.value.String(), "red" ) ? TF_TEAM_RED : TF_TEAM_BLUE;
+}
+
+void CTFMinigameLogic::InputReturnFromMinigame( inputdata_t &inputdata )
+{
+ ReturnFromMinigame();
+}
+
+void CTFMinigameLogic::InputTeleportToRandomMinigame( inputdata_t &inputdata )
+{
+ static int nLastChosenIndex = -1;
+ CUtlVector< int > m_vecRandomableMiniGames;
+ FOR_EACH_VEC( IMinigameAutoList::AutoList(), i )
+ {
+ CTFMiniGame *pMinigame = static_cast< CTFMiniGame* >( IMinigameAutoList::AutoList()[ i ] );
+ if ( pMinigame->AllowedInRandom() && nLastChosenIndex != i )
+ {
+ m_vecRandomableMiniGames.AddToTail( i );
+ }
+ }
+
+ if ( !m_vecRandomableMiniGames.IsEmpty() )
+ {
+ int nChosenIndex = m_vecRandomableMiniGames[ RandomInt( 0, m_vecRandomableMiniGames.Count() - 1 ) ];
+ nLastChosenIndex = nChosenIndex;
+ TeleportToMinigame( nChosenIndex );
+ }
+}
+
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CConditionFortuneTellerEffect
+#ifdef GAME_DLL
+ : public CGameEventListener
+#endif
+{
+public:
+
+ CConditionFortuneTellerEffect( const char* pszActivateSound, ETFCond eCond )
+ : m_pszActivateSound( pszActivateSound )
+ , m_eCondition( eCond )
+ , m_bUseTimer( false )
+ {}
+
+ void OnActivateEffect( bool bUseTimer )
+ {
+#ifdef GAME_DLL
+ m_bUseTimer = bUseTimer;
+
+ float flConditionDuration = m_bUseTimer ? tf_fortune_teller_fortune_duration.GetFloat() : PERMANENT_CONDITION;
+
+ CUtlVector< CTFPlayer* > vecPlayers;
+ CollectPlayers<CTFPlayer>( &vecPlayers, TEAM_ANY, true );
+ for ( CTFPlayer *pPlayer : vecPlayers )
+ {
+ // Permanently add condition. We'll remove it when we're done
+ pPlayer->m_Shared.AddCond( m_eCondition, flConditionDuration );
+ }
+
+ ListenForGameEvent( "player_spawn" );
+#endif
+ }
+
+ void OnDeactivateEffect()
+ {
+#ifdef GAME_DLL
+ m_bUseTimer = false;
+ StopListeningForAllEvents();
+
+ CUtlVector< CTFPlayer* > vecPlayers;
+ CollectPlayers<CTFPlayer>( &vecPlayers, TEAM_ANY, true );
+
+ for ( CTFPlayer *pPlayer : vecPlayers )
+ {
+ // We're done. Remove this condition
+ pPlayer->m_Shared.RemoveCond( m_eCondition );
+ }
+#endif
+ }
+
+#ifdef GAME_DLL
+ virtual void FireGameEvent( IGameEvent * event ) OVERRIDE
+ {
+ // don't do anything when players are in hell
+ if ( TFGameRules() && TFGameRules()->ArePlayersInHell() )
+ return;
+
+ float flConditionDuration = m_bUseTimer ? tf_fortune_teller_fortune_duration.GetFloat() : PERMANENT_CONDITION;
+
+ // Add the condition to anyone who spawns in
+ if ( FStrEq( event->GetName(), "player_spawn" ) )
+ {
+ const int nUserID = event->GetInt( "userid" );
+ CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByUserId( nUserID ) );
+ if( pPlayer )
+ {
+ pPlayer->m_Shared.AddCond( m_eCondition, flConditionDuration );
+ }
+ }
+ }
+#endif
+
+ const char *GetActivationSound() const
+ {
+ return m_pszActivateSound;
+ }
+
+ const char *m_pszActivateSound;
+ ETFCond m_eCondition;
+ bool m_bUseTimer;
+};
+
+static CConditionFortuneTellerEffect g_FortuneTellerEffect_BalloonHead = { "Announcer.SD_Event_BigHeadCurse", TF_COND_BALLOON_HEAD }; // FIXME: need 2014 sound link
+static CConditionFortuneTellerEffect g_FortuneTellerEffect_MeleeOnly = { "Announcer.SD_Event_NoGunsCurse", TF_COND_MELEE_ONLY };
+static CConditionFortuneTellerEffect g_FortuneTellerEffect_SwimmingCurse = { "Announcer.SD_Event_SwimmingCurse", TF_COND_SWIMMING_CURSE }; // FIXME: need 2014 sound link
+
+static CConditionFortuneTellerEffect *g_GlobalFortuneTellerEffects[] =
+{
+ &g_FortuneTellerEffect_BalloonHead,
+ &g_FortuneTellerEffect_MeleeOnly,
+ &g_FortuneTellerEffect_SwimmingCurse,
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+static const char *s_pszFortuneTellerSpinSound = "Halloween.WheelofFateQuiet";
+
+// Data Description
+BEGIN_DATADESC( CTFHalloweenFortuneTeller )
+#ifdef GAME_DLL
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "EnableFortuneTelling",InputEnableFortuneTelling ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "DisableFortuneTelling",InputDisableFortuneTelling ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "StartFortuneTelling", InputStartFortuneTelling ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "EndFortuneTelling", InputEndFortuneTelling ),
+
+ DEFINE_OUTPUT( m_OnFortuneWarning, "OnFortuneWarning" ),
+ DEFINE_OUTPUT( m_OnFortuneTold, "OnFortuneTold" ),
+ DEFINE_OUTPUT( m_OnFortuneCurse, "OnFortuneCurse" ),
+ DEFINE_OUTPUT( m_OnFortuneEnd, "OnFortuneEnd" ),
+
+ DEFINE_KEYFIELD( m_iszRedTeleport, FIELD_STRING, "red_teleport" ),
+ DEFINE_KEYFIELD( m_iszBlueTeleport, FIELD_STRING, "blue_teleport" ),
+
+#endif // GAME_DLLs
+END_DATADESC()
+
+
+LINK_ENTITY_TO_CLASS( halloween_fortune_teller, CTFHalloweenFortuneTeller );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFHalloweenFortuneTeller::CTFHalloweenFortuneTeller()
+{
+#ifdef GAME_DLL
+ m_bUseTimer = false;
+ m_bWasUsingTimer = false;
+#endif // GAME_DLL
+}
+
+CTFHalloweenFortuneTeller::~CTFHalloweenFortuneTeller()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFHalloweenFortuneTeller::Precache()
+{
+ PrecacheScriptSound( s_pszFortuneTellerSpinSound );
+
+ for ( const auto *pEffect : g_GlobalFortuneTellerEffects )
+ {
+ PrecacheScriptSound( pEffect->GetActivationSound() );
+ }
+
+ PrecacheModel( "models/bots/merasmus/merasmas_misfortune_teller.mdl" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFHalloweenFortuneTeller::Spawn()
+{
+ Precache();
+
+ SetThink( NULL );
+
+ SetModel( "models/bots/merasmus/merasmas_misfortune_teller.mdl" );
+
+ ResetSequence( LookupSequence( "ref" ) );
+
+#ifdef GAME_DLL
+ ResetTimer();
+
+ ListenForGameEvent( "sentry_on_go_active" );
+#endif // GAME_DLLFireGameEvent
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFHalloweenFortuneTeller::UpdateOnRemove()
+{
+#ifdef GAME_DLL
+ if ( m_pActiveFortune )
+ {
+ m_pActiveFortune->OnDeactivateEffect();
+ m_pActiveFortune = NULL;
+ }
+#endif // GAME_DLL
+
+ BaseClass::UpdateOnRemove();
+}
+
+
+#ifdef GAME_DLL
+
+void CTFHalloweenFortuneTeller::InputEnableFortuneTelling( inputdata_t & )
+{
+ m_bWasUsingTimer = m_bUseTimer;
+ m_bUseTimer = true;
+
+ if ( m_pActiveFortune )
+ {
+ m_pActiveFortune->OnDeactivateEffect();
+ m_pActiveFortune = NULL;
+ }
+
+ if ( !m_bWasUsingTimer )
+ UpdateFortuneTellerTime();
+}
+
+void CTFHalloweenFortuneTeller::InputDisableFortuneTelling( inputdata_t & )
+{
+ m_bWasUsingTimer = m_bUseTimer;
+ m_bUseTimer = false;
+
+ if ( m_pActiveFortune )
+ {
+ m_pActiveFortune->OnDeactivateEffect();
+ m_pActiveFortune = NULL;
+ }
+
+ // keep track of when we pause
+ if ( m_bWasUsingTimer )
+ PauseTimer();
+}
+
+void CTFHalloweenFortuneTeller::InputStartFortuneTelling( inputdata_t & )
+{
+ // don't manually call this while using timer
+ Assert( !m_bUseTimer );
+ StartFortuneTell();
+}
+
+void CTFHalloweenFortuneTeller::InputEndFortuneTelling( inputdata_t & )
+{
+ // don't manually call this while using timer
+ Assert( !m_bUseTimer );
+ EndFortuneTell();
+}
+
+void CTFHalloweenFortuneTeller::FireGameEvent( IGameEvent* pEvent )
+{
+ const char *pszEventName = pEvent->GetName();
+
+ if ( FStrEq( pszEventName, "sentry_on_go_active" ) )
+ {
+ // While curses are active, no ammo in sentries
+ if ( m_pActiveFortune )
+ {
+ TFGameRules()->RemoveAllSentriesAmmo();
+ return;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFHalloweenFortuneTeller::UpdateFortuneTellerTime()
+{
+ // unpause time, compute new start time
+ m_flStartTime = gpGlobals->curtime - ( m_flPauseTime - m_flStartTime );
+
+ const float flWarningTime = tf_fortune_teller_interval_time.GetFloat() - tf_fortune_teller_warning_time.GetFloat();
+ float flTimePast = gpGlobals->curtime - m_flStartTime;
+ // warning time
+ if ( flTimePast < flWarningTime )
+ {
+ float flTimeBeforeEvent = flWarningTime - flTimePast;
+ SetContextThink( &CTFHalloweenFortuneTeller::StartFortuneWarning, gpGlobals->curtime + flTimeBeforeEvent, "StartFortuneWarning" );
+ }
+ else
+ {
+ float flTimeBeforeEvent = tf_fortune_teller_interval_time.GetFloat() - flTimePast;
+ SetContextThink( &CTFHalloweenFortuneTeller::StartFortuneTell, gpGlobals->curtime + flTimeBeforeEvent, "StartFortuneTell" );
+ }
+}
+
+void CTFHalloweenFortuneTeller::PauseTimer()
+{
+ m_flPauseTime = gpGlobals->curtime;
+
+ // Cancel any fortunes in flight
+ SetContextThink( NULL, 0, "StartFortuneWarning" );
+ SetContextThink( NULL, 0, "StartFortuneTell" );
+ SetContextThink( NULL, 0, "TellFortune" );
+ SetContextThink( NULL, 0, "EndFortuneTell" );
+}
+
+
+void CTFHalloweenFortuneTeller::ResetTimer()
+{
+ m_flStartTime = m_flPauseTime = gpGlobals->curtime;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFHalloweenFortuneTeller::StartFortuneWarning()
+{
+ // disable nagging
+ TFGameRules()->StopDoomsdayTicketsTimer();
+
+ m_OnFortuneWarning.FireOutput( this, this );
+ SetContextThink( &CTFHalloweenFortuneTeller::StartFortuneTell, gpGlobals->curtime + tf_fortune_teller_warning_time.GetFloat(), "StartFortuneTell" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFHalloweenFortuneTeller::StartFortuneTell()
+{
+ // Common effects.
+ m_OnFortuneTold.FireOutput( this, this );
+
+ // Broadcast the spin sound for the team-wide
+ TFGameRules()->BroadcastSound( 255, s_pszFortuneTellerSpinSound );
+
+ // Set when to actually perform the fortune telling
+ SetContextThink( &CTFHalloweenFortuneTeller::TellFortune, gpGlobals->curtime + 6.1f, "TellFortune" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFHalloweenFortuneTeller::EndFortuneTell()
+{
+ // resume nagging
+ TFGameRules()->StartDoomsdayTicketsTimer();
+
+ // Cancel any fortunes in flight
+ SetContextThink( NULL, 0, "TellFortune" );
+
+ m_OnFortuneEnd.FireOutput( this, this );
+
+ if ( m_pActiveFortune )
+ {
+ m_pActiveFortune->OnDeactivateEffect();
+ m_pActiveFortune = NULL;
+ }
+
+ if ( m_bUseTimer )
+ {
+ // restart fortune teller time
+ ResetTimer();
+ UpdateFortuneTellerTime();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFHalloweenFortuneTeller::TellFortune()
+{
+ if ( m_pActiveFortune )
+ {
+ m_pActiveFortune->OnDeactivateEffect();
+ m_pActiveFortune = NULL;
+ }
+
+ m_pActiveFortune = g_GlobalFortuneTellerEffects[ RandomInt( 0, ARRAYSIZE( g_GlobalFortuneTellerEffects ) - 1 ) ];
+ if ( !m_pActiveFortune )
+ return;
+
+ // prevent stickies trap before the dance off
+ TFGameRules()->RemoveAllProjectiles();
+ TFGameRules()->RemoveAllSentriesAmmo();
+
+ // Teleport RED
+ CUtlVector< CTFPlayer* > vecRedPlayers;
+ CollectPlayers<CTFPlayer>( &vecRedPlayers, TF_TEAM_RED, true );
+ TFGameRules()->TeleportPlayersToTargetEntities( TF_TEAM_RED, m_iszRedTeleport.ToCStr(), &vecRedPlayers );
+ // Teleport BLUE
+ CUtlVector< CTFPlayer* > vecBluePlayers;
+ CollectPlayers<CTFPlayer>( &vecBluePlayers, TF_TEAM_BLUE, true );
+ TFGameRules()->TeleportPlayersToTargetEntities( TF_TEAM_BLUE, m_iszBlueTeleport.ToCStr(), &vecBluePlayers );
+
+ CUtlVector< CTFPlayer* > vecPlayers;
+ CollectPlayers<CTFPlayer>( &vecPlayers, TEAM_ANY, true );
+ // Effects
+ for( CTFPlayer * pPlayer : vecPlayers )
+ {
+ if ( pPlayer->m_Shared.IsCarryingObject() && pPlayer->m_Shared.GetCarriedObject() != NULL)
+ {
+ pPlayer->m_Shared.GetCarriedObject()->DetonateObject();
+ }
+
+ // Do a zoom effect
+ pPlayer->SetFOV( pPlayer, tf_teleporter_fov_start.GetInt() );
+ pPlayer->SetFOV( pPlayer, 0, 1.f, tf_teleporter_fov_start.GetInt() );
+
+ // Screen flash
+ color32 fadeColor = {255,255,255,100};
+ UTIL_ScreenFade( pPlayer, fadeColor, 0.25, 0.4, FFADE_IN );
+
+ // Plays music and makes it so Taunt() always performs a thriller
+ pPlayer->m_Shared.AddCond( TF_COND_HALLOWEEN_THRILLER, 6.f );
+ }
+
+ // Speak after the 1st thriller
+ SetContextThink( &CTFHalloweenFortuneTeller::SpeakThink, gpGlobals->curtime + 3.f, "SpeakThink" );
+ // Apply the effect after the 2nd thriller
+ SetContextThink( &CTFHalloweenFortuneTeller::ApplyFortuneEffect, gpGlobals->curtime + 6.f, "FortuneActivate" );
+
+ const float flDanceTime = 0.5f;
+ const float flDanceDuration = 2.75f;
+
+ // Queue up the thrillers
+ SetContextThink( &CTFHalloweenFortuneTeller::DanceThink, gpGlobals->curtime + flDanceTime, "DanceThink1" );
+ SetContextThink( &CTFHalloweenFortuneTeller::DanceThink, gpGlobals->curtime + flDanceTime + flDanceDuration, "DanceThink2" );
+}
+
+void CTFHalloweenFortuneTeller::ApplyFortuneEffect()
+{
+ m_OnFortuneCurse.FireOutput( this, this );
+
+ // Apply the actual effects.
+ if ( m_pActiveFortune )
+ m_pActiveFortune->OnActivateEffect( m_bUseTimer );
+
+ if ( m_bUseTimer )
+ {
+ SetContextThink( &CTFHalloweenFortuneTeller::EndFortuneTell, gpGlobals->curtime + tf_fortune_teller_fortune_duration.GetFloat(), "EndFortuneTell" );
+ }
+}
+
+void CTFHalloweenFortuneTeller::SpeakThink()
+{
+ float flSoundDuration = 0.0f;
+
+ if ( m_pActiveFortune )
+ {
+ // Speak
+ const char *pszActivationSound = m_pActiveFortune->GetActivationSound();
+ TFGameRules()->BroadcastSound( 255, pszActivationSound );
+ // Do speaking anim
+ SetSequence( LookupSequence( "jaw_talking" ) );
+ //flSoundDuration = enginesound->GetSoundDuration( pszActivationSound );
+ }
+
+ // Tell ourselves to stop speaking after awhile
+ SetContextThink( &CTFHalloweenFortuneTeller::StopTalkingAnim, gpGlobals->curtime + flSoundDuration, "StopTalkingAnim" );
+}
+
+void CTFHalloweenFortuneTeller::StopTalkingAnim()
+{
+ SetSequence( LookupSequence( "ref" ) );
+}
+
+void CTFHalloweenFortuneTeller::DanceThink()
+{
+ CUtlVector< CTFPlayer* > vecPlayers;
+ CollectPlayers<CTFPlayer>( &vecPlayers, TEAM_ANY, true );
+
+ // No mere mortal can resist the magic of Merasmus
+ for( CTFPlayer * pPlayer : vecPlayers )
+ {
+ pPlayer->Taunt();
+ }
+}
+
+#endif // GAME_DLLs