diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/tf/tf_logic_halloween_2014.cpp | |
| download | archived-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.cpp | 1288 |
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 |