Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1288 lines
38 KiB

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