diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/tf/tf_hud_freezepanel.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/client/tf/tf_hud_freezepanel.cpp')
| -rw-r--r-- | game/client/tf/tf_hud_freezepanel.cpp | 1370 |
1 files changed, 1370 insertions, 0 deletions
diff --git a/game/client/tf/tf_hud_freezepanel.cpp b/game/client/tf/tf_hud_freezepanel.cpp new file mode 100644 index 0000000..bac4f91 --- /dev/null +++ b/game/client/tf/tf_hud_freezepanel.cpp @@ -0,0 +1,1370 @@ +//========= 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" ); +} |