|
|
//========= 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
|