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.
1289 lines
38 KiB
1289 lines
38 KiB
![]()
4 years ago
|
//========= 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
|