//========= Copyright © 1996-2006, Valve Corporation, All rights reserved. ============// // // Purpose: CTF Flag. // //=============================================================================// #include "cbase.h" #include "entity_capture_flag.h" #include "tf_gamerules.h" #include "tf_shareddefs.h" #ifdef CLIENT_DLL #include #include #include #include #include "hudelement.h" #include "iclientmode.h" #include "hud_numericdisplay.h" #include "tf_imagepanel.h" #include "c_tf_player.h" #include "c_tf_team.h" #include "tf_hud_objectivestatus.h" #include "view.h" extern CUtlVector g_Flags; ConVar cl_flag_return_size( "cl_flag_return_size", "20", FCVAR_CHEAT ); #else #include "tf_player.h" #include "tf_team.h" #include "tf_objective_resource.h" #include "tf_gamestats.h" #include "func_respawnroom.h" #include "datacache/imdlcache.h" extern ConVar tf_flag_caps_per_round; ConVar cl_flag_return_height( "cl_flag_return_height", "82", FCVAR_CHEAT ); #endif #ifdef CLIENT_DLL static void RecvProxy_IsDisabled( const CRecvProxyData *pData, void *pStruct, void *pOut ) { CCaptureFlag *pFlag = (CCaptureFlag *) pStruct; bool bIsDisabled = ( pData->m_Value.m_Int > 0 ); if ( pFlag ) { pFlag->SetDisabled( bIsDisabled ); } } #endif //============================================================================= // // CTF Flag tables. // IMPLEMENT_NETWORKCLASS_ALIASED( CaptureFlag, DT_CaptureFlag ) BEGIN_NETWORK_TABLE( CCaptureFlag, DT_CaptureFlag ) #ifdef GAME_DLL SendPropBool( SENDINFO( m_bDisabled ) ), SendPropInt( SENDINFO( m_nGameType ), 5, SPROP_UNSIGNED ), SendPropInt( SENDINFO( m_nFlagStatus ), 3, SPROP_UNSIGNED ), SendPropTime( SENDINFO( m_flResetTime ) ), SendPropTime( SENDINFO( m_flNeutralTime ) ), SendPropTime( SENDINFO( m_flMaxResetTime ) ), SendPropEHandle( SENDINFO( m_hPrevOwner ) ), #else RecvPropInt( RECVINFO( m_bDisabled ), 0, RecvProxy_IsDisabled ), RecvPropInt( RECVINFO( m_nGameType ) ), RecvPropInt( RECVINFO( m_nFlagStatus ) ), RecvPropTime( RECVINFO( m_flResetTime ) ), RecvPropTime( RECVINFO( m_flNeutralTime ) ), RecvPropTime( RECVINFO( m_flMaxResetTime ) ), RecvPropEHandle( RECVINFO( m_hPrevOwner ) ), #endif END_NETWORK_TABLE() BEGIN_DATADESC( CCaptureFlag ) // Keyfields. DEFINE_KEYFIELD( m_nGameType, FIELD_INTEGER, "GameType" ), #ifdef GAME_DLL // Inputs. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), DEFINE_INPUTFUNC( FIELD_VOID, "RoundActivate", InputRoundActivate ), // Outputs. DEFINE_OUTPUT( m_outputOnReturn, "OnReturn" ), DEFINE_OUTPUT( m_outputOnPickUp, "OnPickUp" ), DEFINE_OUTPUT( m_outputOnDrop, "OnDrop" ), DEFINE_OUTPUT( m_outputOnCapture, "OnCapture" ), #endif END_DATADESC(); LINK_ENTITY_TO_CLASS( item_teamflag, CCaptureFlag ); //============================================================================= // // CTF Flag functions. // CCaptureFlag::CCaptureFlag() { #ifdef CLIENT_DLL m_pGlowTrailEffect = NULL; m_pPaperTrailEffect = NULL; #else m_hReturnIcon = NULL; #endif UseClientSideAnimation(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- unsigned int CCaptureFlag::GetItemID( void ) { return TF_ITEM_CAPTURE_FLAG; } //----------------------------------------------------------------------------- // Purpose: Precache the model and sounds. //----------------------------------------------------------------------------- void CCaptureFlag::Precache( void ) { PrecacheModel( TF_FLAG_MODEL ); PrecacheScriptSound( TF_CTF_FLAGSPAWN ); PrecacheScriptSound( TF_CTF_ENEMY_STOLEN ); PrecacheScriptSound( TF_CTF_ENEMY_DROPPED ); PrecacheScriptSound( TF_CTF_ENEMY_CAPTURED ); PrecacheScriptSound( TF_CTF_ENEMY_RETURNED ); PrecacheScriptSound( TF_CTF_TEAM_STOLEN ); PrecacheScriptSound( TF_CTF_TEAM_DROPPED ); PrecacheScriptSound( TF_CTF_TEAM_CAPTURED ); PrecacheScriptSound( TF_CTF_TEAM_RETURNED ); PrecacheScriptSound( TF_AD_CAPTURED_SOUND ); PrecacheScriptSound( TF_AD_ENEMY_STOLEN ); PrecacheScriptSound( TF_AD_ENEMY_DROPPED ); PrecacheScriptSound( TF_AD_ENEMY_CAPTURED ); PrecacheScriptSound( TF_AD_ENEMY_RETURNED ); PrecacheScriptSound( TF_AD_TEAM_STOLEN ); PrecacheScriptSound( TF_AD_TEAM_DROPPED ); PrecacheScriptSound( TF_AD_TEAM_CAPTURED ); PrecacheScriptSound( TF_AD_TEAM_RETURNED ); PrecacheScriptSound( TF_INVADE_ENEMY_STOLEN ); PrecacheScriptSound( TF_INVADE_ENEMY_DROPPED ); PrecacheScriptSound( TF_INVADE_ENEMY_CAPTURED ); PrecacheScriptSound( TF_INVADE_TEAM_STOLEN ); PrecacheScriptSound( TF_INVADE_TEAM_DROPPED ); PrecacheScriptSound( TF_INVADE_TEAM_CAPTURED ); PrecacheScriptSound( TF_INVADE_FLAG_RETURNED ); PrecacheParticleSystem( "player_intel_trail_blue" ); PrecacheParticleSystem( "player_intel_trail_red" ); PrecacheParticleSystem( "player_intel_papertrail" ); } #ifndef GAME_DLL //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCaptureFlag::OnPreDataChanged( DataUpdateType_t updateType ) { m_nOldFlagStatus = m_nFlagStatus; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCaptureFlag::OnDataChanged( DataUpdateType_t updateType ) { if ( m_nOldFlagStatus != m_nFlagStatus ) { IGameEvent *pEvent = gameeventmanager->CreateEvent( "flagstatus_update" ); if ( pEvent ) { gameeventmanager->FireEventClientSide( pEvent ); } } } #endif //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCaptureFlag::Spawn( void ) { // Precache the model and sounds. Set the flag model. Precache(); SetModel( TF_FLAG_MODEL ); // Set the flag solid and the size for touching. SetSolid( SOLID_BBOX ); SetSolidFlags( FSOLID_NOT_SOLID | FSOLID_TRIGGER ); SetSize( vec3_origin, vec3_origin ); // Bloat the box for player pickup CollisionProp()->UseTriggerBounds( true, 24 ); // use the initial dynamic prop "m_bStartDisabled" setting to set our own m_bDisabled flag #ifdef GAME_DLL m_bDisabled = m_bStartDisabled; m_bStartDisabled = false; // Don't allow the intelligence to fade. m_flFadeScale = 0.0f; #else m_bDisabled = false; #endif // Base class spawn. BaseClass::Spawn(); #ifdef GAME_DLL // Save the starting position, so we can reset the flag later if need be. m_vecResetPos = GetAbsOrigin(); m_vecResetAng = GetAbsAngles(); SetFlagStatus( TF_FLAGINFO_NONE ); ResetFlagReturnTime(); ResetFlagNeutralTime(); m_bAllowOwnerPickup = true; m_hPrevOwner = NULL; m_bCaptured = false; #else // add this element if it isn't already in the list if ( g_Flags.Find( entindex() ) == -1 ) { g_Flags.AddToTail( entindex() ); } #endif if ( m_bDisabled ) { SetDisabled( true ); } else { SetDisabled( false ); } } #ifdef GAME_DLL //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCaptureFlag::Activate( void ) { BaseClass::Activate(); m_iOriginalTeam = GetTeamNumber(); m_nSkin = ( GetTeamNumber() == TEAM_UNASSIGNED ) ? 2 : (GetTeamNumber() - 2); } #endif //----------------------------------------------------------------------------- // Purpose: Reset the flag position state. //----------------------------------------------------------------------------- void CCaptureFlag::Reset( void ) { #ifdef GAME_DLL // Set the flag position. SetAbsOrigin( m_vecResetPos ); SetAbsAngles( m_vecResetAng ); // No longer dropped, if it was. SetFlagStatus( TF_FLAGINFO_NONE ); ResetFlagReturnTime(); ResetFlagNeutralTime(); m_bAllowOwnerPickup = true; m_hPrevOwner = NULL; if ( m_nGameType == TF_FLAGTYPE_INVADE ) { ChangeTeam( m_iOriginalTeam ); m_nSkin = ( GetTeamNumber() == TEAM_UNASSIGNED ) ? 2 : (GetTeamNumber() - 2); } SetMoveType( MOVETYPE_NONE ); #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCaptureFlag::ResetMessage( void ) { #ifdef GAME_DLL if ( m_nGameType == TF_FLAGTYPE_CTF ) { for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam ) { if ( iTeam == GetTeamNumber() ) { CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_CTF_ENEMY_RETURNED ); TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_YOUR_FLAG_RETURNED ); } else { CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_CTF_TEAM_RETURNED ); TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_ENEMY_FLAG_RETURNED ); } } // Returned sound CPASAttenuationFilter filter( this, TF_CTF_FLAGSPAWN ); EmitSound( filter, entindex(), TF_CTF_FLAGSPAWN ); } else if ( m_nGameType == TF_FLAGTYPE_ATTACK_DEFEND ) { for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam ) { if ( iTeam == GetTeamNumber() ) { TFTeamMgr()->TeamCenterPrint( iTeam, "#TF_AD_FlagReturned" ); CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_AD_TEAM_RETURNED ); } else { TFTeamMgr()->TeamCenterPrint( iTeam, "#TF_AD_FlagReturned" ); CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_AD_ENEMY_RETURNED ); } } } else if ( m_nGameType == TF_FLAGTYPE_INVADE ) { for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam ) { TFTeamMgr()->TeamCenterPrint( iTeam, "#TF_Invade_FlagReturned" ); CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_INVADE_FLAG_RETURNED ); } } // Output. m_outputOnReturn.FireOutput( this, this ); if ( m_hReturnIcon.Get() ) { UTIL_Remove( m_hReturnIcon ); m_hReturnIcon = NULL; } #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCaptureFlag::FlagTouch( CBaseEntity *pOther ) { // Is the flag enabled? if ( IsDisabled() ) { return; } // The the touch from a live player. if ( !pOther->IsPlayer() || !pOther->IsAlive() ) { return; } #ifdef GAME_DLL // Don't let the person who threw this flag pick it up until it hits the ground. // This way we can throw the flag to people, but not touch it as soon as we throw it ourselves if( m_hPrevOwner.Get() && m_hPrevOwner.Get() == pOther && m_bAllowOwnerPickup == false ) { return; } #endif // Does my team own this flag? If so, no touch. if ( m_nGameType == TF_FLAGTYPE_CTF && pOther->GetTeamNumber() == GetTeamNumber() ) { return; } if ( ( m_nGameType == TF_FLAGTYPE_ATTACK_DEFEND || m_nGameType == TF_FLAGTYPE_TERRITORY_CONTROL ) && pOther->GetTeamNumber() != GetTeamNumber() ) { return; } if ( m_nGameType == TF_FLAGTYPE_INVADE && GetTeamNumber() != TEAM_UNASSIGNED ) { if ( pOther->GetTeamNumber() != GetTeamNumber() ) { return; } } // Can't pickup flags during WaitingForPlayers if ( TFGameRules()->IsInWaitingForPlayers() ) return; // Get the touching player. CTFPlayer *pPlayer = ToTFPlayer( pOther ); if ( !pPlayer ) { return; } // Is the touching player about to teleport? if ( pPlayer->m_Shared.InCond( TF_COND_SELECTED_TO_TELEPORT ) ) return; // Don't let invulnerable players pickup flags if ( pPlayer->m_Shared.InCond( TF_COND_INVULNERABLE ) ) return; #ifdef GAME_DLL if ( PointInRespawnRoom(pPlayer,pPlayer->WorldSpaceCenter()) ) return; #endif // Pick up the flag. PickUp( pPlayer, true ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCaptureFlag::PickUp( CTFPlayer *pPlayer, bool bInvisible ) { // Is the flag enabled? if ( IsDisabled() ) return; if ( !TFGameRules()->FlagsMayBeCapped() ) return; #ifdef GAME_DLL if ( !m_bAllowOwnerPickup ) { if ( m_hPrevOwner.Get() && m_hPrevOwner.Get() == pPlayer ) { return; } } #endif // Call into the base class pickup. BaseClass::PickUp( pPlayer, false ); pPlayer->TeamFortress_SetSpeed(); #ifdef GAME_DLL // Update the parent to set the correct place on the model to attach the flag. int iAttachment = pPlayer->LookupAttachment( "flag" ); if( iAttachment != -1 ) { SetParent( pPlayer, iAttachment ); SetLocalOrigin( vec3_origin ); SetLocalAngles( vec3_angle ); } // Remove the player's disguse if they're a spy if ( pPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_SPY ) { if ( pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) || pPlayer->m_Shared.InCond( TF_COND_DISGUISING )) { pPlayer->m_Shared.RemoveDisguise(); } } // Remove the touch function. SetTouch( NULL ); m_hPrevOwner = pPlayer; m_bAllowOwnerPickup = true; if ( m_nGameType == TF_FLAGTYPE_CTF ) { for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam ) { if ( iTeam != pPlayer->GetTeamNumber() ) { CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_CTF_ENEMY_STOLEN ); TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_YOUR_FLAG_TAKEN ); } else { CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_CTF_TEAM_STOLEN ); // exclude the guy who just picked it up filter.RemoveRecipient( pPlayer ); TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_ENEMY_FLAG_TAKEN ); } } } else if ( m_nGameType == TF_FLAGTYPE_ATTACK_DEFEND ) { // Handle messages to the screen. TFTeamMgr()->PlayerCenterPrint( pPlayer, "#TF_AD_TakeFlagToPoint" ); for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam ) { if ( iTeam != pPlayer->GetTeamNumber() ) { CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_AD_ENEMY_STOLEN ); } else { CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_AD_TEAM_STOLEN ); } } } else if ( m_nGameType == TF_FLAGTYPE_INVADE ) { // Handle messages to the screen. TFTeamMgr()->PlayerCenterPrint( pPlayer, "#TF_Invade_PlayerPickup" ); TFTeamMgr()->PlayerTeamCenterPrint( pPlayer, "#TF_Invade_PlayerTeamPickup" ); for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam ) { if ( iTeam != pPlayer->GetTeamNumber() ) { TFTeamMgr()->TeamCenterPrint( iTeam, "#TF_Invade_OtherTeamPickup" ); CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_INVADE_ENEMY_STOLEN ); } else { CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_INVADE_TEAM_STOLEN ); } } // set the flag's team to match the player's team ChangeTeam( pPlayer->GetTeamNumber() ); m_nSkin = ( GetTeamNumber() == TEAM_UNASSIGNED ) ? 2 : (GetTeamNumber() - 2); } SetFlagStatus( TF_FLAGINFO_STOLEN ); ResetFlagReturnTime(); ResetFlagNeutralTime(); IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_flag_event" ); if ( event ) { event->SetInt( "player", pPlayer->entindex() ); event->SetInt( "eventtype", TF_FLAGEVENT_PICKUP ); event->SetInt( "priority", 8 ); gameeventmanager->FireEvent( event ); } pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_FLAGPICKUP ); // Output. m_outputOnPickUp.FireOutput( this, this ); if ( m_hReturnIcon.Get() ) { UTIL_Remove( m_hReturnIcon ); m_hReturnIcon = NULL; } #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCaptureFlag::Capture( CTFPlayer *pPlayer, int nCapturePoint ) { // Is the flag enabled? if ( IsDisabled() ) return; #ifdef GAME_DLL if ( m_nGameType == TF_FLAGTYPE_CTF ) { bool bNotify = true; // don't play any sounds if this is going to win the round for one of the teams (victory sound will be played instead) if ( tf_flag_caps_per_round.GetInt() > 0 ) { int nCaps = TFTeamMgr()->GetFlagCaptures( pPlayer->GetTeamNumber() ); if ( ( nCaps >= 0 ) && ( tf_flag_caps_per_round.GetInt() - nCaps <= 1 ) ) { // this cap is going to win, so don't play a sound bNotify = false; } } if ( bNotify ) { for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam ) { if ( iTeam != pPlayer->GetTeamNumber() ) { CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_CTF_ENEMY_CAPTURED ); TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_YOUR_FLAG_CAPTURED ); } else { CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_CTF_TEAM_CAPTURED ); TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_ENEMY_FLAG_CAPTURED ); } } } // Reward the player CTF_GameStats.Event_PlayerCapturedPoint( pPlayer ); // Reward the team if ( tf_flag_caps_per_round.GetInt() > 0 ) { TFTeamMgr()->IncrementFlagCaptures( pPlayer->GetTeamNumber() ); } else { TFTeamMgr()->AddTeamScore( pPlayer->GetTeamNumber(), TF_CTF_CAPTURED_TEAM_FRAGS ); } } else if ( m_nGameType == TF_FLAGTYPE_ATTACK_DEFEND ) { char szNumber[64]; Q_snprintf( szNumber, sizeof(szNumber), "%d", nCapturePoint ); // Handle messages to the screen. TFTeamMgr()->PlayerCenterPrint( pPlayer, "#TF_AD_YouSecuredPoint", szNumber ); TFTeamMgr()->PlayerTeamCenterPrint( pPlayer, "TF_AD_AttackersSecuredPoint", szNumber ); for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam ) { if ( iTeam != pPlayer->GetTeamNumber() ) { TFTeamMgr()->TeamCenterPrint( iTeam, "#TF_AD_AttackersSecuredPoint" ); CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_AD_ENEMY_CAPTURED ); } else { CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_AD_TEAM_CAPTURED ); } } // Capture sound CBroadcastRecipientFilter filter; EmitSound( filter, entindex(), TF_AD_CAPTURED_SOUND ); // Reward the player pPlayer->IncrementFragCount( TF_AD_CAPTURED_FRAGS ); // TFTODO:: Reward the team } else if ( m_nGameType == TF_FLAGTYPE_INVADE ) { // Handle messages to the screen. TFTeamMgr()->PlayerCenterPrint( pPlayer, "#TF_Invade_PlayerCapture" ); TFTeamMgr()->PlayerTeamCenterPrint( pPlayer, "#TF_Invade_PlayerTeamCapture" ); for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam ) { if ( iTeam != pPlayer->GetTeamNumber() ) { TFTeamMgr()->TeamCenterPrint( iTeam, "#TF_Invade_OtherTeamCapture" ); CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_INVADE_ENEMY_CAPTURED ); } else { CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_INVADE_TEAM_CAPTURED ); } } // Reward the player pPlayer->IncrementFragCount( TF_INVADE_CAPTURED_FRAGS ); // Reward the team TFTeamMgr()->AddTeamScore( pPlayer->GetTeamNumber(), TF_INVADE_CAPTURED_TEAM_FRAGS ); } IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_flag_event" ); if ( event ) { event->SetInt( "player", pPlayer->entindex() ); event->SetInt( "eventtype", TF_FLAGEVENT_CAPTURE ); event->SetInt( "priority", 9 ); gameeventmanager->FireEvent( event ); } SetFlagStatus( TF_FLAGINFO_NONE ); ResetFlagReturnTime(); ResetFlagNeutralTime(); // Reset the flag. BaseClass::Drop( pPlayer, true ); Reset(); pPlayer->TeamFortress_SetSpeed(); pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_FLAGCAPTURED ); // Output. m_outputOnCapture.FireOutput( this, this ); m_bCaptured = true; SetNextThink( gpGlobals->curtime + TF_FLAG_THINK_TIME ); if ( TFGameRules()->InStalemate() ) { // whoever capped the flag is the winner, give them enough caps to win CTFTeam *pTeam = pPlayer->GetTFTeam(); if ( !pTeam ) return; // if we still need more caps to trigger a win, give them to us if ( pTeam->GetFlagCaptures() < tf_flag_caps_per_round.GetInt() ) { pTeam->SetFlagCaptures( tf_flag_caps_per_round.GetInt() ); } } #endif } //----------------------------------------------------------------------------- // Purpose: A player drops the flag. //----------------------------------------------------------------------------- void CCaptureFlag::Drop( CTFPlayer *pPlayer, bool bVisible, bool bThrown /*= false*/, bool bMessage /*= true*/ ) { // Is the flag enabled? if ( IsDisabled() ) return; // Call into the base class drop. BaseClass::Drop( pPlayer, bVisible ); pPlayer->TeamFortress_SetSpeed(); #ifdef GAME_DLL if ( bThrown ) { m_bAllowOwnerPickup = false; m_flOwnerPickupTime = gpGlobals->curtime + TF_FLAG_OWNER_PICKUP_TIME; } Vector vecStart = GetAbsOrigin(); Vector vecEnd = vecStart; vecEnd.z -= 8000.0f; trace_t trace; UTIL_TraceHull( vecStart, vecEnd, WorldAlignMins(), WorldAlignMaxs(), MASK_SOLID, this, COLLISION_GROUP_DEBRIS, &trace ); SetAbsOrigin( trace.endpos ); if ( m_nGameType == TF_FLAGTYPE_CTF ) { if ( bMessage ) { for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam ) { if ( iTeam != pPlayer->GetTeamNumber() ) { CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_CTF_ENEMY_DROPPED ); TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_YOUR_FLAG_DROPPED ); } else { CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_CTF_TEAM_DROPPED ); TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_ENEMY_FLAG_DROPPED ); } } } SetFlagReturnIn( TF_CTF_RESET_TIME ); } else if ( m_nGameType == TF_FLAGTYPE_INVADE ) { if ( bMessage ) { // Handle messages to the screen. TFTeamMgr()->PlayerCenterPrint( pPlayer, "#TF_Invade_PlayerFlagDrop" ); TFTeamMgr()->PlayerTeamCenterPrint( pPlayer, "#TF_Invade_FlagDrop" ); for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam ) { if ( iTeam != pPlayer->GetTeamNumber() ) { TFTeamMgr()->TeamCenterPrint( iTeam, "#TF_Invade_FlagDrop" ); CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_INVADE_ENEMY_DROPPED ); } else { CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_INVADE_TEAM_DROPPED ); } } } SetFlagReturnIn( TF_INVADE_RESET_TIME ); SetFlagNeutralIn( TF_INVADE_NEUTRAL_TIME ); } else if ( m_nGameType == TF_FLAGTYPE_ATTACK_DEFEND ) { if ( bMessage ) { for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam ) { if ( iTeam != pPlayer->GetTeamNumber() ) { CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_AD_ENEMY_DROPPED ); } else { CTeamRecipientFilter filter( iTeam, true ); EmitSound( filter, entindex(), TF_AD_TEAM_DROPPED ); } } } SetFlagReturnIn( TF_AD_RESET_TIME ); } // Reset the flag's angles. SetAbsAngles( m_vecResetAng ); // Reset the touch function. SetTouch( &CCaptureFlag::FlagTouch ); SetFlagStatus( TF_FLAGINFO_DROPPED ); // Output. m_outputOnDrop.FireOutput( this, this ); m_hReturnIcon = CBaseEntity::Create( "item_teamflag_return_icon", GetAbsOrigin() + Vector(0,0,cl_flag_return_height.GetFloat()), vec3_angle, this ); #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CCaptureFlag::IsDropped( void ) { return ( m_nFlagStatus == TF_FLAGINFO_DROPPED ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CCaptureFlag::IsHome( void ) { return ( m_nFlagStatus == TF_FLAGINFO_NONE ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CCaptureFlag::IsStolen( void ) { return ( m_nFlagStatus == TF_FLAGINFO_STOLEN ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CCaptureFlag::IsDisabled( void ) { return m_bDisabled; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCaptureFlag::SetDisabled( bool bDisabled ) { m_bDisabled = bDisabled; if ( bDisabled ) { AddEffects( EF_NODRAW ); SetTouch( NULL ); SetThink( NULL ); } else { RemoveEffects( EF_NODRAW ); SetTouch( &CCaptureFlag::FlagTouch ); SetThink( &CCaptureFlag::Think ); SetNextThink( gpGlobals->curtime ); } } //----------------------------------------------------------------------------------------------- // GAME DLL Functions //----------------------------------------------------------------------------------------------- #ifdef GAME_DLL //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCaptureFlag::Think( void ) { // Is the flag enabled? if ( IsDisabled() ) return; if ( !TFGameRules()->FlagsMayBeCapped() ) { SetNextThink( gpGlobals->curtime + TF_FLAG_THINK_TIME ); return; } if ( m_bCaptured ) { m_bCaptured = false; SetTouch( &CCaptureFlag::FlagTouch ); } if ( IsDropped() ) { if ( !m_bAllowOwnerPickup ) { if ( m_flOwnerPickupTime && gpGlobals->curtime > m_flOwnerPickupTime ) { m_bAllowOwnerPickup = true; } } if ( m_nGameType == TF_FLAGTYPE_INVADE ) { if ( m_flResetTime && gpGlobals->curtime > m_flResetTime ) { Reset(); ResetMessage(); } else if ( m_flNeutralTime && gpGlobals->curtime > m_flNeutralTime ) { for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam ) { TFTeamMgr()->TeamCenterPrint( iTeam, "#TF_Invade_FlagNeutral" ); } // reset the team to the original team setting (when it spawned) ChangeTeam( m_iOriginalTeam ); m_nSkin = ( GetTeamNumber() == TEAM_UNASSIGNED ) ? 2 : (GetTeamNumber() - 2); ResetFlagNeutralTime(); } } else { if ( m_flResetTime && gpGlobals->curtime > m_flResetTime ) { Reset(); ResetMessage(); } } } SetNextThink( gpGlobals->curtime + TF_FLAG_THINK_TIME ); } //----------------------------------------------------------------------------- // Purpose: Sets the flag status //----------------------------------------------------------------------------- void CCaptureFlag::SetFlagStatus( int iStatus ) { MDLCACHE_CRITICAL_SECTION(); m_nFlagStatus = iStatus; switch ( m_nFlagStatus ) { case TF_FLAGINFO_NONE: case TF_FLAGINFO_DROPPED: ResetSequence( LookupSequence("spin") ); // set spin animation if it's not being held break; case TF_FLAGINFO_STOLEN: ResetSequence( LookupSequence("idle") ); // set idle animation if it is being held break; default: AssertOnce( false ); // invalid stats break; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCaptureFlag::InputEnable( inputdata_t &inputdata ) { SetDisabled( false ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCaptureFlag::InputDisable( inputdata_t &inputdata ) { SetDisabled( true ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCaptureFlag::InputRoundActivate( inputdata_t &inputdata ) { CTFPlayer *pPlayer = ToTFPlayer( m_hPrevOwner.Get() ); // If the player has a capture flag, drop it. if ( pPlayer && pPlayer->HasItem() && ( pPlayer->GetItem() == this ) ) { Drop( pPlayer, true, false, false ); } Reset(); } //----------------------------------------------------------------------------- // Purpose: Always transmitted to clients //----------------------------------------------------------------------------- int CCaptureFlag::UpdateTransmitState() { // ALWAYS transmit to all clients. return SetTransmitState( FL_EDICT_ALWAYS ); } #else float CCaptureFlag::GetReturnProgress() { float flEventTime = max( m_flResetTime.m_Value, m_flNeutralTime.m_Value ); return ( 1.0 - ( ( flEventTime - gpGlobals->curtime ) / m_flMaxResetTime ) ); } void CCaptureFlag::Simulate( void ) { BaseClass::Simulate(); ManageTrailEffects(); } void CCaptureFlag::ManageTrailEffects( void ) { if ( m_nFlagStatus == TF_FLAGINFO_STOLEN ) { if ( m_pGlowTrailEffect == NULL ) { char *pEffectName = NULL; if ( GetPrevOwner() && GetPrevOwner() != CBasePlayer::GetLocalPlayer() ) { switch( GetPrevOwner()->GetTeamNumber() ) { case TF_TEAM_BLUE: pEffectName = "player_intel_trail_blue"; break; case TF_TEAM_RED: pEffectName = "player_intel_trail_red"; break; default: pEffectName = "player_intel_trail_blue"; break; } m_pGlowTrailEffect = ParticleProp()->Create( pEffectName, PATTACH_ABSORIGIN_FOLLOW ); } } if ( GetPrevOwner() ) { CTFPlayer *pPlayer = ToTFPlayer( GetPrevOwner() ); if ( pPlayer ) { if ( pPlayer->GetAbsVelocity().Length() >= pPlayer->MaxSpeed() * 0.5f ) { if ( m_pPaperTrailEffect == NULL ) { m_pPaperTrailEffect = ParticleProp()->Create( "player_intel_papertrail", PATTACH_ABSORIGIN_FOLLOW ); } } else { if ( m_pPaperTrailEffect ) { ParticleProp()->StopEmission( m_pPaperTrailEffect ); m_pPaperTrailEffect = NULL; } } } } } else { if ( m_pGlowTrailEffect ) { ParticleProp()->StopEmission( m_pGlowTrailEffect ); m_pGlowTrailEffect = NULL; } if ( m_pPaperTrailEffect ) { ParticleProp()->StopEmission( m_pPaperTrailEffect ); m_pPaperTrailEffect = NULL; } } } #endif LINK_ENTITY_TO_CLASS( item_teamflag_return_icon, CCaptureFlagReturnIcon ); IMPLEMENT_NETWORKCLASS_ALIASED( CaptureFlagReturnIcon, DT_CaptureFlagReturnIcon ) BEGIN_NETWORK_TABLE( CCaptureFlagReturnIcon, DT_CaptureFlagReturnIcon ) END_NETWORK_TABLE() CCaptureFlagReturnIcon::CCaptureFlagReturnIcon() { #ifdef CLIENT_DLL m_pReturnProgressMaterial_Empty = NULL; m_pReturnProgressMaterial_Full = NULL; #endif } #ifdef GAME_DLL void CCaptureFlagReturnIcon::Spawn( void ) { BaseClass::Spawn(); UTIL_SetSize( this, Vector(-8,-8,-8), Vector(8,8,8) ); CollisionProp()->SetCollisionBounds( Vector( -50, -50, -50 ), Vector( 50, 50, 50 ) ); } int CCaptureFlagReturnIcon::UpdateTransmitState( void ) { return SetTransmitState( FL_EDICT_PVSCHECK ); } #endif #ifdef CLIENT_DLL typedef struct { float maxProgress; float vert1x; float vert1y; float vert2x; float vert2y; int swipe_dir_x; int swipe_dir_y; } progress_segment_t; // This defines the properties of the 8 circle segments // in the circular progress bar. progress_segment_t Segments[8] = { { 0.125, 0.5, 0.0, 1.0, 0.0, 1, 0 }, { 0.25, 1.0, 0.0, 1.0, 0.5, 0, 1 }, { 0.375, 1.0, 0.5, 1.0, 1.0, 0, 1 }, { 0.50, 1.0, 1.0, 0.5, 1.0, -1, 0 }, { 0.625, 0.5, 1.0, 0.0, 1.0, -1, 0 }, { 0.75, 0.0, 1.0, 0.0, 0.5, 0, -1 }, { 0.875, 0.0, 0.5, 0.0, 0.0, 0, -1 }, { 1.0, 0.0, 0.0, 0.5, 0.0, 1, 0 }, }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- RenderGroup_t CCaptureFlagReturnIcon::GetRenderGroup( void ) { return RENDER_GROUP_TRANSLUCENT_ENTITY; } void CCaptureFlagReturnIcon::GetRenderBounds( Vector& theMins, Vector& theMaxs ) { theMins.Init( -20, -20, -20 ); theMaxs.Init( 20, 20, 20 ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CCaptureFlagReturnIcon::DrawModel( int flags ) { int nRetVal = BaseClass::DrawModel( flags ); DrawReturnProgressBar(); return nRetVal; } //----------------------------------------------------------------------------- // Purpose: Draw progress bar above the flag indicating when it will return //----------------------------------------------------------------------------- void CCaptureFlagReturnIcon::DrawReturnProgressBar( void ) { CCaptureFlag *pFlag = dynamic_cast< CCaptureFlag * > ( GetOwnerEntity() ); if ( !pFlag ) return; // Don't draw if this flag is not going to reset if ( pFlag->GetMaxResetTime() <= 0 ) return; if ( !TFGameRules()->FlagsMayBeCapped() ) return; if ( !m_pReturnProgressMaterial_Full ) { m_pReturnProgressMaterial_Full = materials->FindMaterial( "VGUI/flagtime_full", TEXTURE_GROUP_VGUI ); } if ( !m_pReturnProgressMaterial_Empty ) { m_pReturnProgressMaterial_Empty = materials->FindMaterial( "VGUI/flagtime_empty", TEXTURE_GROUP_VGUI ); } if ( !m_pReturnProgressMaterial_Full || !m_pReturnProgressMaterial_Empty ) { return; } CMatRenderContextPtr pRenderContext( materials ); Vector vOrigin = GetAbsOrigin(); QAngle vAngle = vec3_angle; // Align it towards the viewer Vector vUp = CurrentViewUp(); Vector vRight = CurrentViewRight(); if ( fabs( vRight.z ) > 0.95 ) // don't draw it edge-on return; vRight.z = 0; VectorNormalize( vRight ); float flSize = cl_flag_return_size.GetFloat(); unsigned char ubColor[4]; ubColor[3] = 255; switch( pFlag->GetTeamNumber() ) { case TF_TEAM_RED: ubColor[0] = 255; ubColor[1] = 0; ubColor[2] = 0; break; case TF_TEAM_BLUE: ubColor[0] = 0; ubColor[1] = 0; ubColor[2] = 255; break; default: ubColor[0] = 100; ubColor[1] = 100; ubColor[2] = 100; break; } // First we draw a quad of a complete icon, background CMeshBuilder meshBuilder; pRenderContext->Bind( m_pReturnProgressMaterial_Empty ); IMesh *pMesh = pRenderContext->GetDynamicMesh(); meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); meshBuilder.Color4ubv( ubColor ); meshBuilder.TexCoord2f( 0,0,0 ); meshBuilder.Position3fv( (vOrigin + (vRight * -flSize) + (vUp * flSize)).Base() ); meshBuilder.AdvanceVertex(); meshBuilder.Color4ubv( ubColor ); meshBuilder.TexCoord2f( 0,1,0 ); meshBuilder.Position3fv( (vOrigin + (vRight * flSize) + (vUp * flSize)).Base() ); meshBuilder.AdvanceVertex(); meshBuilder.Color4ubv( ubColor ); meshBuilder.TexCoord2f( 0,1,1 ); meshBuilder.Position3fv( (vOrigin + (vRight * flSize) + (vUp * -flSize)).Base() ); meshBuilder.AdvanceVertex(); meshBuilder.Color4ubv( ubColor ); meshBuilder.TexCoord2f( 0,0,1 ); meshBuilder.Position3fv( (vOrigin + (vRight * -flSize) + (vUp * -flSize)).Base() ); meshBuilder.AdvanceVertex(); meshBuilder.End(); pMesh->Draw(); float flProgress = pFlag->GetReturnProgress(); pRenderContext->Bind( m_pReturnProgressMaterial_Full ); pMesh = pRenderContext->GetDynamicMesh(); vRight *= flSize * 2; vUp *= flSize * -2; // Next we're drawing the circular progress bar, in 8 segments // For each segment, we calculate the vertex position that will draw // the slice. int i; for ( i=0;i<8;i++ ) { if ( flProgress < Segments[i].maxProgress ) { CMeshBuilder meshBuilder_Full; meshBuilder_Full.Begin( pMesh, MATERIAL_TRIANGLES, 3 ); // vert 0 is ( 0.5, 0.5 ) meshBuilder_Full.Color4ubv( ubColor ); meshBuilder_Full.TexCoord2f( 0, 0.5, 0.5 ); meshBuilder_Full.Position3fv( vOrigin.Base() ); meshBuilder_Full.AdvanceVertex(); // Internal progress is the progress through this particular slice float internalProgress = RemapVal( flProgress, Segments[i].maxProgress - 0.125, Segments[i].maxProgress, 0.0, 1.0 ); internalProgress = clamp( internalProgress, 0.0, 1.0 ); // Calculate the x,y of the moving vertex based on internal progress float swipe_x = Segments[i].vert2x - ( 1.0 - internalProgress ) * 0.5 * Segments[i].swipe_dir_x; float swipe_y = Segments[i].vert2y - ( 1.0 - internalProgress ) * 0.5 * Segments[i].swipe_dir_y; // vert 1 is calculated from progress meshBuilder_Full.Color4ubv( ubColor ); meshBuilder_Full.TexCoord2f( 0, swipe_x, swipe_y ); meshBuilder_Full.Position3fv( (vOrigin + (vRight * ( swipe_x - 0.5 ) ) + (vUp *( swipe_y - 0.5 ) ) ).Base() ); meshBuilder_Full.AdvanceVertex(); // vert 2 is ( Segments[i].vert1x, Segments[i].vert1y ) meshBuilder_Full.Color4ubv( ubColor ); meshBuilder_Full.TexCoord2f( 0, Segments[i].vert2x, Segments[i].vert2y ); meshBuilder_Full.Position3fv( (vOrigin + (vRight * ( Segments[i].vert2x - 0.5 ) ) + (vUp *( Segments[i].vert2y - 0.5 ) ) ).Base() ); meshBuilder_Full.AdvanceVertex(); meshBuilder_Full.End(); pMesh->Draw(); } } } #endif