|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "tf_hud_freezepanel.h"
#include "vgui_controls/AnimationController.h"
#include "iclientmode.h"
#include "c_tf_player.h"
#include "c_tf_playerresource.h"
#include <vgui_controls/Label.h>
#include <vgui/ILocalize.h>
#include <vgui/ISurface.h>
#include <vgui/IInput.h>
#include "c_baseobject.h"
#include "fmtstr.h"
#include "tf_gamerules.h"
#include "tf_hud_statpanel.h"
#include "view.h"
#include "ivieweffects.h"
#include "viewrender.h"
#include "c_obj_sentrygun.h"
#include "NextBot/C_NextBot.h"
#include "halloween/c_headless_hatman.h"
#include "halloween/c_eyeball_boss.h"
#include "halloween/c_merasmus.h"
#include "tf_wardata.h"
#if defined( REPLAY_ENABLED )
#include "replay/ireplaysystem.h"
#include "replay/ireplaymanager.h"
#include "replay/replay.h"
#include "replay/screenshot.h"
#include "replay/ireplayscreenshotmanager.h"
#include "replay/vgui/replayreminderpanel.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
DECLARE_HUDELEMENT_DEPTH( CTFFreezePanel, 1 );
#define CALLOUT_WIDE (XRES(100))
#define CALLOUT_TALL (XRES(50))
extern float g_flFreezeFlash;
#define FREEZECAM_SCREENSHOT_STRING "is looking good!"
extern ConVar hud_freezecamhide;
bool IsTakingAFreezecamScreenshot( void ) { // Don't draw in freezecam, or when the game's not running
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); bool bInFreezeCam = ( pPlayer && pPlayer->GetObserverMode() == OBS_MODE_FREEZECAM );
if ( bInFreezeCam == true && engine->IsTakingScreenshot() ) return true;
CTFFreezePanel *pFreezePanel = CTFFreezePanel::Instance(); if ( pFreezePanel ) { if ( pFreezePanel->IsHoldingAfterScreenShot() ) return true; }
return false; }
DECLARE_BUILD_FACTORY( CTFFreezePanelHealth );
//-----------------------------------------------------------------------------
CTFFreezePanel *CTFFreezePanel::s_pFreezePanel = NULL;
//-----------------------------------------------------------------------------
CTFFreezePanel *CTFFreezePanel::Instance() { return s_pFreezePanel; }
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CTFFreezePanel::CTFFreezePanel( const char *pElementName ) : EditablePanel( NULL, "FreezePanel" ), CHudElement( pElementName ) { AssertMsg( !s_pFreezePanel, "There can be only one." ); s_pFreezePanel = this;
vgui::Panel *pParent = g_pClientMode->GetViewport(); SetParent( pParent ); SetVisible( false ); SetScheme( "ClientScheme" );
m_iKillerIndex = 0; m_iShowNemesisPanel = SHOW_NO_NEMESIS; m_iYBase = -1; m_flShowCalloutsAt = 0;
m_iBasePanelOriginalX = -1; m_iBasePanelOriginalY = -1;
m_pItemPanel = new CItemModelPanel( this, "itempanel" ) ; m_iItemPanelOriginalX = -1; m_iItemPanelOriginalY = -1;
#if defined( REPLAY_ENABLED )
m_pSaveReplayPanel = GET_HUDELEMENT( CReplayReminderPanel ); // Use the HUD's instance
#endif
m_strCurrentFreezeCamResFile = GetResFilename(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFFreezePanel::Reset() { Hide();
if ( m_pKillerHealth ) { m_pKillerHealth->Reset(); } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFFreezePanel::Init() { // listen for events
ListenForGameEvent( "show_freezepanel" ); ListenForGameEvent( "hide_freezepanel" ); ListenForGameEvent( "freezecam_started" ); ListenForGameEvent( "player_death" ); ListenForGameEvent( "teamplay_win_panel" ); ListenForGameEvent( "training_complete" ); Hide();
CHudElement::Init(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFFreezePanel::SendTauntAcknowledgement( const char *pszCommand, int iGibs ) { C_TFPlayer *pKiller = ToTFPlayer( UTIL_PlayerByIndex( GetSpectatorTarget() ) ); if ( pKiller && pKiller->m_Shared.InCond( TF_COND_TAUNTING ) ) { CTFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( pPlayer ) { KeyValues *kv = new KeyValues( "FreezeCamTaunt" ); kv->SetInt( "achiever", pKiller->GetUserID() ); kv->SetString( "command", pszCommand ); kv->SetInt( "gibs", iGibs ); engine->ServerCmdKeyValues( kv ); } } }
//-----------------------------------------------------------------------------
// Purpose: Applies scheme settings
//-----------------------------------------------------------------------------
void CTFFreezePanel::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme );
Assert( !m_strCurrentFreezeCamResFile.IsEmpty() ); LoadControlSettings( m_strCurrentFreezeCamResFile.String() );
m_pBasePanel = dynamic_cast<EditablePanel *>( FindChildByName("FreezePanelBase") );
Assert( m_pBasePanel );
if ( m_pBasePanel ) { m_pFreezeLabel = dynamic_cast<Label *>( m_pBasePanel->FindChildByName("FreezeLabel") ); m_pKillerLabel = dynamic_cast<Label *>( m_pBasePanel->FindChildByName("FreezeLabelKiller") ); m_pFreezePanelBG = dynamic_cast<CTFImagePanel *>( m_pBasePanel->FindChildByName( "FreezePanelBG" ) ); m_pNemesisSubPanel = dynamic_cast<EditablePanel *>( m_pBasePanel->FindChildByName( "NemesisSubPanel" ) ); m_pKillerHealth = dynamic_cast<CTFFreezePanelHealth *>( m_pBasePanel->FindChildByName( "FreezePanelHealth" ) ); m_pAvatar = dynamic_cast<CAvatarImagePanel *>( m_pBasePanel->FindChildByName("AvatarImage") ); m_pFreezeTeamIcon = dynamic_cast<CTFImagePanel *>( m_pBasePanel->FindChildByName( "FreezeTeamIcon" ) );
if ( m_pAvatar ) { m_pAvatar->SetShouldScaleImage( true ); m_pAvatar->SetShouldDrawFriendIcon( false ); } } m_pScreenshotPanel = dynamic_cast<EditablePanel *>( FindChildByName( "ScreenshotPanel" ) ); Assert( m_pScreenshotPanel );
// Move killer panels when the win panel is up
int xp,yp; GetPos( xp, yp ); m_iYBase = yp;
int w = 0 , h = 0; if ( m_pBasePanel ) m_pBasePanel->GetBounds( m_iBasePanelOriginalX, m_iBasePanelOriginalY, w, h ); m_pItemPanel->GetBounds( m_iItemPanelOriginalX, m_iItemPanelOriginalY, w, h ); if ( m_pKillerLabel ) { m_pKillerLabel->GetPos( m_iKillerOriginalX, m_iKillerOriginalY ); } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFFreezePanel::FireGameEvent( IGameEvent * event ) { const char *pEventName = event->GetName();
if ( Q_strcmp( "player_death", pEventName ) == 0 ) { // see if the local player died
int iPlayerIndexVictim = engine->GetPlayerForUserID( event->GetInt( "userid" ) ); C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( pLocalPlayer && iPlayerIndexVictim == pLocalPlayer->entindex() ) { // the local player is dead, see if this is a new nemesis or a revenge
if (event->GetInt( "death_flags" ) & TF_DEATH_DOMINATION ) { m_iShowNemesisPanel = SHOW_NEW_NEMESIS; } else if ( event->GetInt( "death_flags" ) & TF_DEATH_REVENGE ) { m_iShowNemesisPanel = SHOW_REVENGE; } else { m_iShowNemesisPanel = SHOW_NO_NEMESIS; } } } else if ( Q_strcmp( "hide_freezepanel", pEventName ) == 0 ) { Hide(); } else if ( Q_strcmp( "freezecam_started", pEventName ) == 0 ) { ShowCalloutsIn( 1.0 ); ShowSnapshotPanelIn( 1.25 );
#if defined( REPLAY_ENABLED )
// If Replay is enabled on the server, show the replay download reminder. If GetPendingReplay()
// returns NULL, we know we've already saved the replay.
CReplay *pCurLifeReplay = ( g_pReplayManager ) ? g_pReplayManager->GetReplayForCurrentLife() : NULL; if ( g_pReplay->IsRecording() && ( pCurLifeReplay && !pCurLifeReplay->m_bRequestedByUser && !pCurLifeReplay->m_bSaved ) ) { ShowSaveReplayPanelIn( 1.25 ); }
// Save the freezeframe for the replay browser
if ( g_pReplay->IsRecording() ) { // Capture the freezecam in half a second
CaptureScreenshotParams_t params; V_memset( ¶ms, 0, sizeof( params ) ); params.m_flDelay = 0.0f; params.m_bIgnoreMinTimeBetweenScreenshots = true; g_pReplayScreenshotManager->CaptureScreenshot( params ); } #endif
CTFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( pPlayer ) { SendTauntAcknowledgement( "freezecam_taunt" ); } } else if ( Q_strcmp( "teamplay_win_panel", pEventName ) == 0 ) { Hide(); } else if ( Q_strcmp( "training_complete", pEventName ) == 0 ) { Hide(); } else if ( Q_strcmp( "show_freezepanel", pEventName ) == 0 ) { // Get the entity who killed us
m_iKillerIndex = event->GetInt( "killer" ); C_BaseEntity *pKiller = ClientEntityList().GetBaseEntity( m_iKillerIndex ); CTFPlayer *pTFPlayerKiller = NULL; if ( pKiller ) { if ( pKiller->IsPlayer() ) { pTFPlayerKiller = ToTFPlayer( pKiller ); } else if ( pKiller->IsBaseObject() ) { C_BaseObject *pObj = assert_cast<C_BaseObject *>( pKiller ); pTFPlayerKiller = pObj->GetOwner(); } }
// Do we need to invalidate a new res file?
const char *pszNewResFile = GetResFilename( pTFPlayerKiller ); if ( V_stricmp( m_strCurrentFreezeCamResFile.String(), pszNewResFile ) != 0 ) { m_strCurrentFreezeCamResFile = pszNewResFile; InvalidateLayout( true, true ); }
if ( !g_TF_PR ) { if ( m_pNemesisSubPanel ) m_pNemesisSubPanel->SetDialogVariable( "nemesisname", NULL ); return; }
Show();
ShowSnapshotPanel( false ); ShowSaveReplayPanel( false ); m_bHoldingAfterScreenshot = false;
if ( m_iBasePanelOriginalX > -1 && m_iBasePanelOriginalY > -1 ) { m_pBasePanel->SetPos( m_iBasePanelOriginalX, m_iBasePanelOriginalY ); } if ( m_iItemPanelOriginalX > -1 && m_iItemPanelOriginalY > -1 ) { m_pItemPanel->SetPos( m_iItemPanelOriginalX, m_iItemPanelOriginalY ); }
int xp,yp; GetPos( xp, yp ); if ( TFGameRules()->RoundHasBeenWon() ) { SetPos( xp, m_iYBase - YRES(50) ); } else { SetPos( xp, m_iYBase ); }
if ( pKiller ) { int iMaxBuffedHealth = 0;
if ( pTFPlayerKiller ) { iMaxBuffedHealth = pTFPlayerKiller->m_Shared.GetMaxBuffedHealth(); }
int iKillerHealth = pKiller->GetHealth(); if ( !pKiller->IsAlive() ) { iKillerHealth = 0; }
m_pKillerHealth->SetBuilding( pKiller->IsBaseObject() ); m_pKillerHealth->SetHealth( iKillerHealth, pKiller->GetMaxHealth(), iMaxBuffedHealth );
if ( m_pItemPanel ) { m_pItemPanel->SetVisible( false ); }
if ( pKiller->IsPlayer() ) { C_TFPlayer *pVictim = C_TFPlayer::GetLocalTFPlayer();
//If this was just a regular kill but this guy is our nemesis then just show it.
if ( pVictim && pTFPlayerKiller->m_Shared.IsPlayerDominated( pVictim->entindex() ) ) { if ( !pKiller->IsAlive() ) { m_pFreezeLabel->SetText( "#FreezePanel_Nemesis_Dead" ); } else { m_pFreezeLabel->SetText( "#FreezePanel_Nemesis" ); } } else { if ( !pKiller->IsAlive() ) { m_pFreezeLabel->SetText( "#FreezePanel_Killer_Dead" ); } else { m_pFreezeLabel->SetText( "#FreezePanel_Killer" ); } }
m_pBasePanel->SetDialogVariable( "killername", g_PR->GetPlayerName( m_iKillerIndex ) );
if ( m_pAvatar ) { m_pAvatar->SetPlayer( (C_BasePlayer*)pKiller ); }
// If our killer is using a powerup, show the details of that powerup
if ( pTFPlayerKiller && pTFPlayerKiller->m_Shared.IsCarryingRune() ) { static CSchemaItemDefHandle rgPowerupItems [] = { CSchemaItemDefHandle( "Powerup Strength" ) , CSchemaItemDefHandle( "Powerup Haste" ) , CSchemaItemDefHandle( "Powerup Regen" ) , CSchemaItemDefHandle( "Powerup Resist" ) , CSchemaItemDefHandle( "Powerup Vampire" ) , CSchemaItemDefHandle( "Powerup Reflect" ) , CSchemaItemDefHandle( "Powerup Precision" ) , CSchemaItemDefHandle( "Powerup Agility" ) , CSchemaItemDefHandle( "Powerup Knockout" ) , CSchemaItemDefHandle( "Powerup King" ) , CSchemaItemDefHandle( "Powerup Plague" ) , CSchemaItemDefHandle( "Powerup Supernova" ) };
COMPILE_TIME_ASSERT( ARRAYSIZE( rgPowerupItems ) == RUNE_TYPES_MAX );
// Get the item
const CSchemaItemDefHandle& itemDef = rgPowerupItems[pTFPlayerKiller->m_Shared.GetCarryingRuneType()];
// Create a fake, temp item to show the powerup
CEconItemView item; item.SetItemDefIndex( itemDef->GetDefinitionIndex() ); item.SetItemQuality( AE_UNIQUE ); // Unique by default
item.SetItemLevel( 0 ); // Hide this?
item.SetInitialized( true ); item.SetItemOriginOverride( kEconItemOrigin_Invalid );
m_pItemPanel->SetDialogVariable( "killername", g_PR->GetPlayerName( m_iKillerIndex ) ); m_pItemPanel->SetItem( &item ); m_pItemPanel->SetVisible( true ); } else { // If our killer is using an item, display its stats.
CTFWeaponBase *pWeapon = pTFPlayerKiller ? pTFPlayerKiller->GetActiveTFWeapon() : NULL; bool bShowItem = false; if ( pWeapon ) { bShowItem = pWeapon->GetAttributeContainer()->GetItem()->GetItemQuality() != AE_NORMAL; if ( bShowItem ) { CTFStatPanel *pStatPanel = GET_HUDELEMENT( CTFStatPanel ); if ( pStatPanel && pStatPanel->IsVisible() ) { // Stat panel overrides.
bShowItem = false; } } }
if ( bShowItem ) { Label* pItemLabel = m_pItemPanel->FindControl<Label>( "ItemLabel" );
if ( pItemLabel ) { // Change the label text depending on if they're holding someone else's item
CBasePlayer *pOriginalOwner = GetPlayerByAccountID( pWeapon->GetAttributeContainer()->GetItem()->GetAccountID() ); bool bOriginalOwner = pOriginalOwner == pKiller; pItemLabel->SetText( bOriginalOwner ? "#FreezePanel_Item" : "#FreezePanel_ItemOtherOwner" ); m_pItemPanel->SetDialogVariable( "ownername", bOriginalOwner ? g_PR->GetPlayerName( pOriginalOwner->entindex() ) : "" ); }
m_pItemPanel->SetDialogVariable( "killername", g_PR->GetPlayerName( m_iKillerIndex ) ); m_pItemPanel->SetItem( pWeapon->GetAttributeContainer()->GetItem() ); m_pItemPanel->SetVisible( true ); } } if ( m_pItemPanel && m_pItemPanel->IsVisible() ) { int x, y; m_pItemPanel->GetPos( x, y ); m_pItemPanel->SetPos( x, ScreenHeight() - YRES( 12 ) - m_pItemPanel->GetTall() ); } } else if ( pKiller->IsBaseObject() ) { C_BaseObject *pObj = assert_cast<C_BaseObject *>( pKiller ); //Assert( pTFPlayerKiller && "Why does this object not have an owner?" );
if ( pTFPlayerKiller ) { m_iKillerIndex = pTFPlayerKiller->entindex();
m_pBasePanel->SetDialogVariable( "killername", g_PR->GetPlayerName( m_iKillerIndex ) );
if ( m_pAvatar ) { m_pAvatar->SetPlayer( pTFPlayerKiller ); m_pAvatar->SetVisible( true ); }
pKiller = pTFPlayerKiller; } else { if ( m_pAvatar ) { m_pAvatar->SetVisible( false ); } }
if ( m_pFreezeLabel ) { if ( pKiller && !pKiller->IsAlive() ) { m_pFreezeLabel->SetText( "#FreezePanel_KillerObject_Dead" ); } else { m_pFreezeLabel->SetText( "#FreezePanel_KillerObject" ); } } const char *pszStatusName = pObj->GetStatusName(); wchar_t *wszLocalized = g_pVGuiLocalize->Find( pszStatusName );
if ( !wszLocalized ) { m_pBasePanel->SetDialogVariable( "objectkiller", pszStatusName ); } else { m_pBasePanel->SetDialogVariable( "objectkiller", wszLocalized ); } } else if ( dynamic_cast< C_HeadlessHatman * >( pKiller ) != NULL ) { m_pBasePanel->SetDialogVariable( "killername", g_pVGuiLocalize->Find( "#TF_HALLOWEEN_BOSS_DEATHCAM_NAME" ) );
if ( m_pAvatar ) { m_pAvatar->SetVisible( false ); } } else if ( dynamic_cast< C_EyeballBoss * >( pKiller ) != NULL ) { m_pBasePanel->SetDialogVariable( "killername", g_pVGuiLocalize->Find( "#TF_HALLOWEEN_EYEBALL_BOSS_DEATHCAM_NAME" ) );
if ( m_pAvatar ) { m_pAvatar->SetVisible( false ); } } else if ( dynamic_cast< C_Merasmus * >( pKiller ) != NULL ) { m_pBasePanel->SetDialogVariable( "killername", g_pVGuiLocalize->Find( "#TF_HALLOWEEN_MERASMUS_DEATHCAM_NAME" ) );
if ( m_pAvatar ) { m_pAvatar->SetVisible( false ); } } else if ( m_pFreezeLabel ) { if ( !pKiller->IsAlive() ) { m_pFreezeLabel->SetText( "#FreezePanel_Killer_Dead" ); } else { m_pFreezeLabel->SetText( "#FreezePanel_Killer" ); } } if ( m_pFreezePanelBG ) { // use the killer's team for the background color
m_pFreezePanelBG->SetImage( pKiller->GetTeamNumber() == TF_TEAM_BLUE ? "../hud/color_panel_blu" : "../hud/color_panel_red" ); }
if ( m_pAvatar ) { int iAvX, iAvY; m_pAvatar->GetPos( iAvX, iAvY ); if ( m_pAvatar->IsVisible() && m_pAvatar->IsValid() ) { m_pKillerLabel->SetPos( iAvX + m_pAvatar->GetWide() + XRES(2), m_iKillerOriginalY ); } else { m_pKillerLabel->SetPos( iAvX, m_iKillerOriginalY ); } } } // see if we should show nemesis panel
bool bAdvice = false; const wchar_t *pchNemesisText = NULL; switch ( m_iShowNemesisPanel ) { case SHOW_NO_NEMESIS: { C_TFPlayer *pVictim = C_TFPlayer::GetLocalTFPlayer(); CTFPlayer *pTFKiller = ToTFPlayer( pKiller ); //If this was just a regular kill but this guy is our nemesis then just show it.
if ( pTFKiller && pTFKiller->m_Shared.IsPlayerDominated( pVictim->entindex() ) ) { pchNemesisText = g_pVGuiLocalize->Find( "#TF_FreezeNemesis" ); } // UNDONE: We're not shipping this for now
/*else if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && pTFKiller && pTFKiller->GetTeamNumber() == TF_TEAM_PVE_INVADERS )
{ const wchar_t *pwchHint = g_pVGuiLocalize->Find( VarArgs( "#TF_PVE_FreezePanelHint_%s", pTFKiller->GetPlayerClass()->GetClassIconName() ) ); if ( pwchHint && pwchHint[ 0 ] != L'\0' ) { pchNemesisText = pwchHint; bAdvice = true; } }*/ } break; case SHOW_NEW_NEMESIS: { C_TFPlayer *pVictim = C_TFPlayer::GetLocalTFPlayer(); CTFPlayer *pTFKiller = ToTFPlayer( pKiller ); // check to see if killer is still the nemesis of victim; victim may have managed to kill him after victim's
// death by grenade or some such, extracting revenge and clearing nemesis condition
if ( pTFKiller && pTFKiller->m_Shared.IsPlayerDominated( pVictim->entindex() ) ) { pchNemesisText = g_pVGuiLocalize->Find( "#TF_NewNemesis" ); } } break; case SHOW_REVENGE: pchNemesisText = g_pVGuiLocalize->Find( "#TF_GotRevenge" ); break; default: Assert( false ); // invalid value
break; }
if ( m_pNemesisSubPanel ) { if ( !bAdvice ) { m_pNemesisSubPanel->SetDialogVariable( "nemesisname", pchNemesisText ); m_pNemesisSubPanel->SetControlVisible( "NemesisLabel2", false ); } else { m_pNemesisSubPanel->SetDialogVariable( "nemesisname", "" ); m_pNemesisSubPanel->SetControlVisible( "NemesisLabel2", true ); m_pNemesisSubPanel->SetDialogVariable( "nemesisadvice", pchNemesisText ); } }
ShowNemesisPanel( pchNemesisText != NULL ); m_iShowNemesisPanel = SHOW_NO_NEMESIS; } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CTFFreezePanel::GetResFilename( C_TFPlayer *pTFPlayer /*= NULL*/ ) const { return "resource/UI/FreezePanel_Basic.res"; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFFreezePanel::ShowCalloutsIn( float flTime ) { m_flShowCalloutsAt = gpGlobals->curtime + flTime; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFFreezePanelCallout *CTFFreezePanel::TestAndAddCallout( Vector &origin, Vector &vMins, Vector &vMaxs, CUtlVector<Vector> *vecCalloutsTL, CUtlVector<Vector> *vecCalloutsBR, Vector &vecFreezeTL, Vector &vecFreezeBR, Vector &vecStatTL, Vector &vecStatBR, int *iX, int *iY ) { // This is the offset from the topleft of the callout to the arrow tip
const int iXOffset = XRES(25); const int iYOffset = YRES(50);
//if ( engine->IsBoxInViewCluster( vMins + origin, vMaxs + origin) && !engine->CullBox( vMins + origin, vMaxs + origin ) )
{ if ( GetVectorInHudSpace( origin, *iX, *iY ) ) // TODO: GetVectorInHudSpace or GetVectorInScreenSpace?
{ *iX -= iXOffset; *iY -= iYOffset; int iRight = *iX + CALLOUT_WIDE; int iBottom = *iY + CALLOUT_TALL; if ( *iX > 0 && *iY > 0 && (iRight < ScreenWidth()) && (iBottom < (ScreenHeight()-YRES(40))) ) { // Make sure it wouldn't be over the top of the freezepanel or statpanel
Vector vecCalloutTL( *iX, *iY, 0 ); Vector vecCalloutBR( iRight, iBottom, 1 ); if ( !QuickBoxIntersectTest( vecCalloutTL, vecCalloutBR, vecFreezeTL, vecFreezeBR ) && !QuickBoxIntersectTest( vecCalloutTL, vecCalloutBR, vecStatTL, vecStatBR ) ) { // Make sure it doesn't intersect any other callouts
bool bClear = true; for ( int iCall = 0; iCall < vecCalloutsTL->Count(); iCall++ ) { if ( QuickBoxIntersectTest( vecCalloutTL, vecCalloutBR, vecCalloutsTL->Element(iCall), vecCalloutsBR->Element(iCall) ) ) { bClear = false; break; } }
if ( bClear ) { // Verify that we have LOS to the gib
trace_t tr; UTIL_TraceLine( origin, MainViewOrigin(), MASK_OPAQUE, NULL, COLLISION_GROUP_NONE, &tr ); bClear = ( tr.fraction >= 1.0f ); }
if ( bClear ) { CTFFreezePanelCallout *pCallout = new CTFFreezePanelCallout( g_pClientMode->GetViewport(), "FreezePanelCallout" ); m_pCalloutPanels.AddToTail( vgui::SETUP_PANEL(pCallout) ); vecCalloutsTL->AddToTail( vecCalloutTL ); vecCalloutsBR->AddToTail( vecCalloutBR ); pCallout->SetVisible( true ); pCallout->SetBounds( *iX, *iY, CALLOUT_WIDE, CALLOUT_TALL ); return pCallout; } } } } }
return NULL; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFFreezePanel::UpdateCallout( void ) { CTFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( !pPlayer ) return;
// Abort early if we have no gibs or ragdoll
CUtlVector<EHANDLE> *pGibList = pPlayer->GetSpawnedGibs(); IRagdoll *pRagdoll = pPlayer->GetRepresentativeRagdoll(); if ( (!pGibList || pGibList->Count() == 0) && !pRagdoll ) return;
if ( m_pFreezePanelBG == NULL ) return;
// Precalc the vectors of the freezepanel & statpanel
int iX, iY; m_pFreezePanelBG->GetPos( iX, iY ); Vector vecFreezeTL( iX, iY, 0 ); Vector vecFreezeBR( iX + m_pFreezePanelBG->GetWide(), iY + m_pFreezePanelBG->GetTall(), 1 );
CUtlVector<Vector> vecCalloutsTL; CUtlVector<Vector> vecCalloutsBR;
Vector vecStatTL(0,0,0); Vector vecStatBR(0,0,1); CTFStatPanel *pStatPanel = GET_HUDELEMENT( CTFStatPanel ); if ( pStatPanel && pStatPanel->IsVisible() ) { pStatPanel->GetPos( iX, iY ); vecStatTL.x = iX; vecStatTL.y = iY; vecStatBR.x = vecStatTL.x + pStatPanel->GetWide(); vecStatBR.y = vecStatTL.y + pStatPanel->GetTall(); }
Vector vMins, vMaxs;
// Check gibs
if ( pGibList && pGibList->Count() ) { int iCount = 0; for ( int i = 0; i < pGibList->Count(); i++ ) { CBaseEntity *pGib = pGibList->Element(i); if ( pGib ) { Vector origin = pGib->GetRenderOrigin(); IPhysicsObject *pPhysicsObject = pGib->VPhysicsGetObject(); if( pPhysicsObject ) { Vector vecMassCenter = pPhysicsObject->GetMassCenterLocalSpace(); pGib->CollisionProp()->CollisionToWorldSpace( vecMassCenter, &origin ); } pGib->GetRenderBounds( vMins, vMaxs );
// Try and add the callout
CTFFreezePanelCallout *pCallout = TestAndAddCallout( origin, vMins, vMaxs, &vecCalloutsTL, &vecCalloutsBR, vecFreezeTL, vecFreezeBR, vecStatTL, vecStatBR, &iX, &iY ); if ( pCallout ) { pCallout->UpdateForGib( i, iCount ); iCount++; } } }
C_ObjectSentrygun *pSentry = dynamic_cast<C_ObjectSentrygun*>( ClientEntityList().GetEnt( GetSpectatorTarget() ) ); if ( pSentry ) { // A sentry was the killer...check and see if the builder is on screen.
CTFPlayer *pBuilder = pSentry->GetBuilder(); if ( pBuilder && GetVectorInHudSpace( pBuilder->GetRenderOrigin(), iX, iY ) ) // TODO: GetVectorInHudSpace or GetVectorInScreenSpace?
{ KeyValues *kv = new KeyValues( "FreezeCamTaunt" ); kv->SetInt( "achiever", pBuilder->GetUserID() ); kv->SetString( "command", "freezecam_tauntsentry" ); engine->ServerCmdKeyValues( kv ); } }
// Tell the server that we saw some gibs onscreen
if ( iCount > 0 ) { SendTauntAcknowledgement( "freezecam_tauntgibs", iCount ); } }
// Check for a ragdoll as well. Dying characters that ragdoll can also drop wearable items as gibs
if ( pRagdoll ) { Vector origin = pRagdoll->GetRagdollOrigin(); pRagdoll->GetRagdollBounds( vMins, vMaxs );
// Try and add the callout
CTFFreezePanelCallout *pCallout = TestAndAddCallout( origin, vMins, vMaxs, &vecCalloutsTL, &vecCalloutsBR, vecFreezeTL, vecFreezeBR, vecStatTL, vecStatBR, &iX, &iY ); if ( pCallout ) { pCallout->UpdateForRagdoll(); }
// even if the callout failed, check that our ragdoll is onscreen and our killer is taunting us (for an achievement)
if ( GetVectorInHudSpace( origin, iX, iY ) ) // TODO: GetVectorInHudSpace or GetVectorInScreenSpace?
{ SendTauntAcknowledgement( "freezecam_tauntrag" ); } } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFFreezePanel::Show() { m_flShowCalloutsAt = 0; SetVisible( true ); }
void CTFFreezePanel::DeleteCalloutPanels() { for ( int i = m_pCalloutPanels.Count()-1; i >= 0; i-- ) { m_pCalloutPanels[i]->MarkForDeletion(); } m_pCalloutPanels.RemoveAll(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFFreezePanel::Hide() { SetVisible( false ); m_bHoldingAfterScreenshot = false;
// Delete all our callout panels
DeleteCalloutPanels();
#if defined( REPLAY_ENABLED )
// Explicitly set the replay reminder's visibility, which is not parented
// to the freeze panel.
if ( m_pSaveReplayPanel ) { m_pSaveReplayPanel->SetVisible( false ); } #endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFFreezePanel::ShouldDraw( void ) { return ( IsVisible() ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFFreezePanel::OnThink( void ) { BaseClass::OnThink();
if ( m_pItemPanel && m_pItemPanel->IsVisible() ) { CTFStatPanel *pStatPanel = GET_HUDELEMENT( CTFStatPanel ); if ( pStatPanel && pStatPanel->IsVisible() ) { m_pItemPanel->SetVisible( false ); } }
if ( m_flShowCalloutsAt && m_flShowCalloutsAt < gpGlobals->curtime ) { if ( ShouldDraw() ) { UpdateCallout(); } m_flShowCalloutsAt = 0; }
if ( m_flShowSnapshotReminderAt && m_flShowSnapshotReminderAt < gpGlobals->curtime ) { if ( ShouldDraw() ) { // For now don't do this in Steam Controller mode, because there's no easy way for a SC user to deal with this
if ( !::input->IsSteamControllerActive() ) { ShowSnapshotPanel( true ); } } m_flShowSnapshotReminderAt = 0; }
if ( m_flShowReplayReminderAt && m_flShowReplayReminderAt < gpGlobals->curtime ) { if ( ShouldDraw() ) { // For now don't do this in Steam Controller mode, because there's no easy way for a SC user to deal with this
if ( !::input->IsSteamControllerActive() ) { ShowSaveReplayPanel( true ); } } m_flShowReplayReminderAt = 0; } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFFreezePanel::ShowSnapshotPanelIn( float flTime ) { #if defined (_X360 )
return; #endif
m_flShowSnapshotReminderAt = gpGlobals->curtime + flTime; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFFreezePanel::ShowSaveReplayPanelIn( float flTime ) { #if defined (_X360 )
return; #endif
m_flShowReplayReminderAt = gpGlobals->curtime + flTime; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFFreezePanel::ShowSnapshotPanel( bool bShow ) { if ( !m_pScreenshotPanel ) return;
const char *key = engine->Key_LookupBinding( "screenshot" );
if ( key == NULL || FStrEq( key, "(null)" ) ) { bShow = false; key = " "; }
if ( bShow ) { char szKey[16]; Q_snprintf( szKey, sizeof(szKey), "%s", key ); wchar_t wKey[16]; wchar_t wLabel[256];
g_pVGuiLocalize->ConvertANSIToUnicode(szKey, wKey, sizeof(wKey)); g_pVGuiLocalize->ConstructString_safe( wLabel, g_pVGuiLocalize->Find("#TF_freezecam_snapshot" ), 1, wKey );
m_pScreenshotPanel->SetDialogVariable( "text", wLabel );
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "HudSnapShotReminderIn" ); }
m_pScreenshotPanel->SetVisible( bShow ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFFreezePanel::ShowSaveReplayPanel( bool bShow ) { #if defined( REPLAY_ENABLED )
// Make sure ptr's ok
if ( !m_pSaveReplayPanel ) return;
// Don't do this for Steam Controller users
if ( ::input->IsSteamControllerActive() ) return;
// Make sure we're recording
if ( !g_pReplay->IsRecording() ) return; // Start animation if necessary
if ( bShow ) { g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_pSaveReplayPanel->GetParent(), "HudReplayReminderIn2" ); }
// Setup visibility
m_pSaveReplayPanel->SetVisible( bShow ); #endif
}
const char *CTFFreezePanel::GetFilesafePlayerName( const char *pszOldName ) { if ( !pszOldName ) return "";
static char szSafeName[ MAX_PLAYER_NAME_LENGTH ]; int nSafeNameBufSize = sizeof( szSafeName ); int nNewPos = 0; for( const char *p = pszOldName; *p != 0 && nNewPos < nSafeNameBufSize-1; p++ ) { if( *p == '.' ) { szSafeName[ nNewPos ] = '-'; } else if( *p == '/' ) { szSafeName[ nNewPos ] = '-'; } else if( *p == '\\' ) { szSafeName[ nNewPos ] = '-'; } else if( *p == ':' ) { szSafeName[ nNewPos ] = '-'; } else { szSafeName[ nNewPos ] = *p; }
nNewPos++; }
szSafeName[ nNewPos ] = 0;
return szSafeName; }
int CTFFreezePanel::HudElementKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding ) { if ( ShouldDraw() && pszCurrentBinding ) { if ( FStrEq( pszCurrentBinding, "screenshot" ) || FStrEq( pszCurrentBinding, "jpeg" ) ) { // move the target id to the corner
if ( m_pBasePanel && m_bShouldScreenshotMovePanelToCorner ) { int w, h; m_pBasePanel->GetSize( w, h );
if ( m_pItemPanel && m_pItemPanel->IsVisible() ) { int iw,ih; m_pItemPanel->GetSize( iw, ih ); m_pItemPanel->SetPos( ScreenWidth() - iw, ScreenHeight() - ih ); m_pBasePanel->SetPos( ScreenWidth() - w, ScreenHeight() - ih - h ); } else { m_pBasePanel->SetPos( ScreenWidth() - w, ScreenHeight() - h ); } }
// Get the local player.
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); if ( pPlayer ) { //Do effects
g_flFreezeFlash = gpGlobals->curtime + 0.75f; pPlayer->EmitSound( "Camera.SnapShot" );
//Extend Freezecam by a couple more seconds.
engine->ClientCmd( "extendfreeze" ); view->FreezeFrame( 3.0f );
//Hide the reminder panel
m_flShowSnapshotReminderAt = 0; ShowSnapshotPanel( false );
// Hide replay reminder panel
m_flShowReplayReminderAt = 0; ShowSaveReplayPanel( false );
m_bHoldingAfterScreenshot = true;
// Hide everything?
if ( hud_freezecamhide.GetBool() ) { SetVisible( false ); DeleteCalloutPanels(); }
//Set the screenshot name
if ( m_iKillerIndex <= MAX_PLAYERS ) { const char *pszKillerName = g_PR->GetPlayerName( m_iKillerIndex );
if ( pszKillerName ) { ConVarRef cl_screenshotname( "cl_screenshotname" );
if ( cl_screenshotname.IsValid() ) { char szScreenShotName[512];
Q_snprintf( szScreenShotName, sizeof( szScreenShotName ), "%s %s", GetFilesafePlayerName( pszKillerName ), FREEZECAM_SCREENSHOT_STRING );
cl_screenshotname.SetValue( szScreenShotName ); } }
C_TFPlayer *pKiller = ToTFPlayer( UTIL_PlayerByIndex( m_iKillerIndex ) ); if ( pKiller ) { CSteamID steamID; if ( pKiller->GetSteamID( &steamID ) ) { ConVarRef cl_screenshotusertag( "cl_screenshotusertag" ); if ( cl_screenshotusertag.IsValid() ) { cl_screenshotusertag.SetValue( (int)steamID.GetAccountID() ); } } } } } } #if defined( REPLAY_ENABLED )
else if ( FStrEq (pszCurrentBinding, "save_replay" ) ) { m_flShowReplayReminderAt = 0; ShowSaveReplayPanel( false ); } #endif
}
return 0; }
//-----------------------------------------------------------------------------
// Purpose: Shows or hides the nemesis part of the panel
//-----------------------------------------------------------------------------
void CTFFreezePanel::ShowNemesisPanel( bool bShow ) { if ( !m_pNemesisSubPanel ) return;
m_pNemesisSubPanel->SetVisible( bShow );
if ( bShow ) { vgui::Label *pLabel = dynamic_cast< vgui::Label *>( m_pNemesisSubPanel->FindChildByName( "NemesisLabel" ) ); vgui::Label *pLabel2 = dynamic_cast< vgui::Label *>( m_pNemesisSubPanel->FindChildByName( "NemesisLabel2" ) ); vgui::Panel *pBG = m_pNemesisSubPanel->FindChildByName( "NemesisPanelBG" ); vgui::ImagePanel *pIcon = dynamic_cast< vgui::ImagePanel *>( m_pNemesisSubPanel->FindChildByName( "NemesisIcon" ) );
// check that our Nemesis panel and resize it to the length of the string (the right side is pinned and doesn't move)
if ( pLabel && pLabel2 && pBG && pIcon ) { int nDiffX, nDiffY; int wide, tall;
if ( !pLabel2->IsVisible() ) { pLabel->GetContentSize( wide, tall ); nDiffX = wide - pLabel->GetWide(); nDiffY = tall - pLabel->GetTall(); } else { pLabel2->GetContentSize( wide, tall ); nDiffX = wide - pLabel2->GetWide(); nDiffY = tall - pLabel2->GetTall(); }
if ( nDiffX != 0 || nDiffY != 0 ) { int x, y, w, t;
// move the icon
pIcon->GetBounds( x, y, w, t ); pIcon->SetBounds( x - nDiffX, y - nDiffY, w, t );
pLabel->GetBounds( x, y, w, t ); pLabel->SetBounds( x - nDiffX, y - nDiffY, w + nDiffX, t + nDiffY );
pLabel2->GetBounds( x, y, w, t ); pLabel2->SetBounds( x - nDiffX, y - nDiffY, w + nDiffX, t + nDiffY );
// move/resize the background
pBG->GetBounds( x, y, w, t ); pBG->SetBounds( x - nDiffX, y - nDiffY, w + nDiffX, t + nDiffY );
m_pNemesisSubPanel->GetBounds( x, y, w, t ); m_pNemesisSubPanel->SetBounds( x, y - nDiffY, w, t + nDiffY ); } } } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFFreezePanelCallout::CTFFreezePanelCallout( Panel *parent, const char *name ) : EditablePanel(parent,name) { m_pGibLabel = NULL; }
//-----------------------------------------------------------------------------
// Purpose: Applies scheme settings
//-----------------------------------------------------------------------------
void CTFFreezePanelCallout::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme );
LoadControlSettings( "resource/UI/FreezePanelCallout.res" );
m_pGibLabel = dynamic_cast<Label *>( FindChildByName("CalloutLabel") ); }
const char *pszCalloutGibNames[] = { "#Callout_Head", "#Callout_Foot", "#Callout_Hand", "#Callout_Torso", NULL, // Random
}; const char *pszCalloutRandomGibNames[] = { "#Callout_Organ2", "#Callout_Organ3", "#Callout_Organ4", "#Callout_Organ5", "#Callout_Organ6", };
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFFreezePanelCallout::UpdateForGib( int iGib, int iCount ) { if ( !m_pGibLabel ) return;
if ( iGib < ARRAYSIZE(pszCalloutGibNames) ) { if ( pszCalloutGibNames[iGib] ) { m_pGibLabel->SetText( pszCalloutGibNames[iGib] ); } else { m_pGibLabel->SetText( pszCalloutRandomGibNames[ RandomInt(0,ARRAYSIZE(pszCalloutRandomGibNames)-1) ] ); } } else { if ( iCount > 1 ) { m_pGibLabel->SetText( "#FreezePanel_Callout3" ); } else if ( iCount == 1 ) { m_pGibLabel->SetText( "#FreezePanel_Callout2" ); } else { m_pGibLabel->SetText( "#FreezePanel_Callout" ); } } #ifndef _X360
int wide, tall; m_pGibLabel->GetContentSize( wide, tall );
// is the text wider than the label?
if ( wide > m_pGibLabel->GetWide() ) { int nDiff = wide - m_pGibLabel->GetWide(); int x, y, w, t;
// make the label wider
m_pGibLabel->GetBounds( x, y, w, t ); m_pGibLabel->SetBounds( x, y, w + nDiff, t );
vgui::Panel *pBackground = FindChildByName( "CalloutBG" ); if ( pBackground ) { // also adjust the background image
pBackground->GetBounds( x, y, w, t ); pBackground->SetBounds( x, y, w + nDiff, t ); }
// make ourselves bigger to accommodate the wider children
GetBounds( x, y, w, t ); SetBounds( x, y, w + nDiff, t );
// check that we haven't run off the right side of the screen
if ( x + GetWide() > ScreenWidth() ) { // push ourselves to the left to fit on the screen
nDiff = ( x + GetWide() ) - ScreenWidth(); SetPos( x - nDiff, y );
// push the arrow to the right to offset moving ourselves to the left
vgui::ImagePanel *pArrow = dynamic_cast<ImagePanel *>( FindChildByName( "ArrowIcon" ) ); if ( pArrow ) { pArrow->GetBounds( x, y, w, t ); pArrow->SetBounds( x + nDiff, y, w, t ); } } } #endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFFreezePanelCallout::UpdateForRagdoll( void ) { if ( !m_pGibLabel ) return;
m_pGibLabel->SetText( "#Callout_Ragdoll" ); }
|