//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include "hud.h" #include "hudelement.h" #include "hud_macros.h" #include "hud_numericdisplay.h" #include "iclientmode.h" #include "c_tf_player.h" #include "VGuiMatSurface/IMatSystemSurface.h" #include "materialsystem/imaterial.h" #include "materialsystem/imesh.h" #include "materialsystem/imaterialvar.h" #include "client_virtualreality.h" #include "sourcevr/isourcevirtualreality.h" #include #include #include #include //for screenfade #include "ivieweffects.h" #include "shake.h" #include "view_scene.h" #include "tf_weapon_sniperrifle.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // Purpose: Figure out where the sniper scope should be drawn. //----------------------------------------------------------------------------- void WhereToDrawSniperScope ( int *pX, int *pY, int screenWide, int screenTall ) { C_BasePlayer* pPlayer = C_BasePlayer::GetLocalPlayer(); if ( pPlayer != NULL ) { // These are the correct values to use, but they lag the high-speed view data... Vector vecStart = pPlayer->Weapon_ShootPosition(); Vector vecAimDirection = pPlayer->GetAutoaimVector( 1.0f ); // ...so in some aim modes, they get zapped by something completely up-to-date. g_ClientVirtualReality.OverrideWeaponHudAimVectors ( &vecStart, &vecAimDirection ); Vector vAimPoint; // Here we just put the aim point a set distance from the viewer - same depth as the HUD. float fVrHudDistance = g_ClientVirtualReality.GetHUDDistance(); vAimPoint = vecStart + vecAimDirection * fVrHudDistance; Vector screen; screen.Init(); ScreenTransform(vAimPoint, screen); // screen[0][1] are in range (-1, 1) float x = 0.5 * ( 1.0f + screen[0] ) * screenWide + 0.5; float y = 0.5 * ( 1.0f - screen[1] ) * screenTall + 0.5; *pX = (int)( x + 0.5f ); *pY = (int)( y + 0.5f ); } } //----------------------------------------------------------------------------- // Purpose: Draws the sniper chargeup meter //----------------------------------------------------------------------------- class CHudScopeCharge : public vgui::Panel, public CHudElement { DECLARE_CLASS_SIMPLE( CHudScopeCharge, vgui::Panel ); public: CHudScopeCharge( const char *pElementName ); virtual ~CHudScopeCharge( void ); void Init( void ); protected: virtual void ApplySchemeSettings(vgui::IScheme *scheme); virtual void Paint( void ); private: int m_iChargeupTexture; int m_iChargeupTextureWidth; CPanelAnimationVarAliasType( float, m_iChargeup_xpos, "chargeup_xpos", "0", "proportional_float" ); CPanelAnimationVarAliasType( float, m_iChargeup_ypos, "chargeup_ypos", "0", "proportional_float" ); CPanelAnimationVarAliasType( float, m_iChargeup_wide, "chargeup_wide", "0", "proportional_float" ); CPanelAnimationVarAliasType( float, m_iChargeup_tall, "chargeup_tall", "0", "proportional_float" ); bool m_bJarateMode; }; DECLARE_HUDELEMENT_DEPTH( CHudScopeCharge, 100 ); using namespace vgui; //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- CHudScopeCharge::CHudScopeCharge( const char *pElementName ) : CHudElement(pElementName), BaseClass(NULL, "HudScopeCharge") { vgui::Panel *pParent = g_pClientMode->GetViewport(); SetParent( pParent ); SetHiddenBits( HIDEHUD_PLAYERDEAD ); m_bJarateMode = false; m_iChargeupTexture = -1; } CHudScopeCharge::~CHudScopeCharge( void ) { if ( vgui::surface() && m_iChargeupTexture != -1 ) { vgui::surface()->DestroyTextureID( m_iChargeupTexture ); m_iChargeupTexture = -1; } } //----------------------------------------------------------------------------- // Purpose: standard hud element init function //----------------------------------------------------------------------------- void CHudScopeCharge::Init( void ) { if ( m_iChargeupTexture == -1 ) { m_iChargeupTexture = vgui::surface()->CreateNewTextureID(); vgui::surface()->DrawSetTextureFile(m_iChargeupTexture, "HUD/sniperscope_numbers", true, false); } // Get the texture size int ignored; surface()->DrawGetTextureSize( m_iChargeupTexture, m_iChargeupTextureWidth, ignored ); } //----------------------------------------------------------------------------- // Purpose: sets scheme colors //----------------------------------------------------------------------------- void CHudScopeCharge::ApplySchemeSettings( vgui::IScheme *scheme ) { BaseClass::ApplySchemeSettings(scheme); SetPaintBackgroundEnabled(false); SetPaintBorderEnabled(false); if ( UseVR() ) { // Force it to go direct to the framebuffer. SetForceStereoRenderToFrameBuffer( true ); } } //----------------------------------------------------------------------------- // Purpose: draws the zoom effect //----------------------------------------------------------------------------- void CHudScopeCharge::Paint( void ) { C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( GetSpectatorTarget() != 0 && GetSpectatorMode() == OBS_MODE_IN_EYE ) { pPlayer = (C_TFPlayer *)UTIL_PlayerByIndex( GetSpectatorTarget() ); } if ( !pPlayer ) return; if ( !pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) ) return; // Make sure the current weapon is a sniper rifle CTFSniperRifle *pWeapon = assert_cast(pPlayer->GetActiveTFWeapon()); if ( !pWeapon ) return; if ( pWeapon->IsJarateRifle() && !m_bJarateMode ) { vgui::surface()->DrawSetTextureFile(m_iChargeupTexture, "HUD/sniperscope_numbers_jar", true, false); m_bJarateMode = true; } else if ( !pWeapon->IsJarateRifle() && m_bJarateMode ) { vgui::surface()->DrawSetTextureFile(m_iChargeupTexture, "HUD/sniperscope_numbers", true, false); m_bJarateMode = false; } // Actual charge value is set through a material proxy in the sniper rifle class int wide, tall; GetSize( wide, tall ); int x = 0; int y = 0; bool bDisableClipping = false; if ( UseVR() ) { int vx, vy, vw, vh; vgui::surface()->GetFullscreenViewport( vx, vy, vw, vh ); int screenWide = vw; int screenTall = vh; bDisableClipping = true; WhereToDrawSniperScope ( &x, &y, screenWide, screenTall ); // The origin is wherever the property file put it, so (0,0) means "the right place, if the scope is in the middle of the screen" // So offset from there. But additional fun - the rendering coordinates are in the UI space, not the actual screen space. int UiScreenWide, UiScreenTall; GetHudSize(UiScreenWide, UiScreenTall); x -= ( UiScreenWide / 2 ); y -= ( UiScreenTall / 2 ); } if( bDisableClipping ) g_pMatSystemSurface->DisableClipping( true ); vgui::surface()->DrawSetColor(255,255,255,255); vgui::surface()->DrawSetTexture(m_iChargeupTexture); vgui::surface()->DrawTexturedRect( x, y, x+wide, y+tall ); if( bDisableClipping ) g_pMatSystemSurface->DisableClipping( false ); } //----------------------------------------------------------------------------- // Purpose: Draws the zoom screen //----------------------------------------------------------------------------- class CHudScope : public vgui::Panel, public CHudElement { DECLARE_CLASS_SIMPLE( CHudScope, vgui::Panel ); public: CHudScope( const char *pElementName ); virtual ~CHudScope( void ); void Init( void ); protected: virtual void ApplySchemeSettings(vgui::IScheme *scheme); virtual void Paint( void ); virtual bool ShouldDraw( void ); private: int m_iScopeTexture[4]; int m_iScopeTextureAlt[4]; bool m_bAltScopeMode; }; DECLARE_HUDELEMENT_DEPTH( CHudScope, 100 ); using namespace vgui; //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- CHudScope::CHudScope( const char *pElementName ) : CHudElement(pElementName), BaseClass(NULL, "HudScope") { vgui::Panel *pParent = g_pClientMode->GetViewport(); SetParent( pParent ); SetHiddenBits( HIDEHUD_PLAYERDEAD ); for ( int i = 0; i < ARRAYSIZE( m_iScopeTexture ); i++ ) { m_iScopeTexture[ i ] = -1; } for ( int i = 0; i < ARRAYSIZE( m_iScopeTextureAlt ); i++ ) { m_iScopeTextureAlt[i] = -1; } m_bAltScopeMode = false; } CHudScope::~CHudScope( void ) { if ( vgui::surface() ) { for ( int i = 0; i < ARRAYSIZE( m_iScopeTexture ); i++ ) { if ( m_iScopeTexture[ i ] != -1 ) { vgui::surface()->DestroyTextureID( m_iScopeTexture[ i ] ); m_iScopeTexture[ i ] = -1; } } for ( int i = 0; i < ARRAYSIZE( m_iScopeTextureAlt ); i++ ) { if ( m_iScopeTextureAlt[i] != -1 ) { vgui::surface()->DestroyTextureID( m_iScopeTextureAlt[i] ); m_iScopeTextureAlt[i] = -1; } } } } //----------------------------------------------------------------------------- // Purpose: standard hud element init function //----------------------------------------------------------------------------- void CHudScope::Init( void ) { for ( int i = 0; i < ARRAYSIZE( m_iScopeTexture ); i++ ) { if ( m_iScopeTexture[ i ] == -1 ) { m_iScopeTexture[ i ] = vgui::surface()->CreateNewTextureID(); } } vgui::surface()->DrawSetTextureFile( m_iScopeTexture[0], "HUD/scope_sniper_ul", true, false ); vgui::surface()->DrawSetTextureFile( m_iScopeTexture[1], "HUD/scope_sniper_ur", true, false ); vgui::surface()->DrawSetTextureFile( m_iScopeTexture[2], "HUD/scope_sniper_lr", true, false ); vgui::surface()->DrawSetTextureFile( m_iScopeTexture[3], "HUD/scope_sniper_ll", true, false ); for ( int i = 0; i < ARRAYSIZE( m_iScopeTextureAlt ); i++ ) { if ( m_iScopeTextureAlt[i] == -1 ) { m_iScopeTextureAlt[i] = vgui::surface()->CreateNewTextureID(); } } vgui::surface()->DrawSetTextureFile( m_iScopeTextureAlt[0], "HUD/scope_sniper_alt_ul", true, false ); vgui::surface()->DrawSetTextureFile( m_iScopeTextureAlt[1], "HUD/scope_sniper_alt_ur", true, false ); vgui::surface()->DrawSetTextureFile( m_iScopeTextureAlt[2], "HUD/scope_sniper_alt_lr", true, false ); vgui::surface()->DrawSetTextureFile( m_iScopeTextureAlt[3], "HUD/scope_sniper_alt_ll", true, false ); // remove ourselves from the global group so the scoreboard doesn't hide us UnregisterForRenderGroup( "global" ); } //----------------------------------------------------------------------------- // Purpose: sets scheme colors //----------------------------------------------------------------------------- void CHudScope::ApplySchemeSettings( vgui::IScheme *scheme ) { BaseClass::ApplySchemeSettings(scheme); SetPaintBackgroundEnabled(false); SetPaintBorderEnabled(false); if ( UseVR() ) { // Make it fill the screen. int iViewportWidth, iViewportHeight; g_pSourceVR->GetViewportBounds( ISourceVirtualReality::VREye_Left, NULL, NULL, &iViewportWidth, &iViewportHeight ); SetSize ( iViewportWidth, iViewportHeight ); SetBounds ( 0, 0, iViewportWidth, iViewportHeight ); // Force it to go direct to the framebuffer. SetForceStereoRenderToFrameBuffer( true ); } else { int screenWide, screenTall; GetHudSize(screenWide, screenTall); SetBounds(0, 0, screenWide, screenTall); } // Move behind the spectator GUI, so we can be visible at the same time. SetZPos( -1 ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CHudScope::ShouldDraw( void ) { C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( GetSpectatorTarget() != 0 && GetSpectatorMode() == OBS_MODE_IN_EYE ) { pPlayer = (C_TFPlayer *)UTIL_PlayerByIndex( GetSpectatorTarget() ); } if ( !pPlayer || !pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) ) return false; if ( pPlayer->GetActiveTFWeapon() ) { m_bAltScopeMode = ( pPlayer->GetActiveTFWeapon()->GetWeaponID() == TF_WEAPON_SNIPERRIFLE_CLASSIC ); } return CHudElement::ShouldDraw(); } //----------------------------------------------------------------------------- // Purpose: draws the zoom effect //----------------------------------------------------------------------------- void CHudScope::Paint( void ) { // We need to update the refraction texture so the scope can refract it UpdateRefractTexture(); int screenWide; int screenTall; GetHudSize(screenWide, screenTall); // calculate the bounds in which we should draw the scope int xMid = screenWide / 2; int yMid = screenTall / 2; // width of the drawn scope. in widescreen, we draw the sides with primitives int wide, tall; if( screenWide > screenTall ) { wide = ( screenTall * 4 ) / 3; tall = screenTall; } else { wide = screenWide; tall = ( screenWide * 3 ) / 4; } bool bDisableClipping = false; if ( UseVR() ) { int vx, vy, vw, vh; vgui::surface()->GetFullscreenViewport( vx, vy, vw, vh ); screenWide = vw; screenTall = vh; // This is actually awful - the scope is drawn in HUD-space, which is this completely // artifical 640*480 space that we invent for VR and which doesn't even have square pixels. Ugh. // Hacked good enough to ship. TODO: don't hack it. float fMagnification = 1.0f / g_ClientVirtualReality.GetZoomedModeMagnification(); wide = (int)( fMagnification * (float)screenWide ); tall = ( wide * 3 ) / 4; bDisableClipping = true; WhereToDrawSniperScope ( &xMid, &yMid, screenWide, screenTall ); } int xLeft = xMid - wide/2; int xRight = xMid + wide/2; int yTop = yMid - tall/2; int yBottom = yMid + tall/2; float uv1 = 0.5f / 256.0f, uv2 = 1.0f - uv1; vgui::Vertex_t vert[4]; Vector2D uv11( uv1, uv1 ); Vector2D uv12( uv1, uv2 ); Vector2D uv21( uv2, uv1 ); Vector2D uv22( uv2, uv2 ); vgui::surface()->DrawSetColor(0,0,0,255); if( bDisableClipping ) g_pMatSystemSurface->DisableClipping( true ); //upper left vgui::surface()->DrawSetTexture( m_bAltScopeMode ? m_iScopeTextureAlt[0] : m_iScopeTexture[0] ); vert[0].Init( Vector2D( xLeft, yTop ), uv11 ); vert[1].Init( Vector2D( xMid, yTop ), uv21 ); vert[2].Init( Vector2D( xMid, yMid ), uv22 ); vert[3].Init( Vector2D( xLeft, yMid ), uv12 ); vgui::surface()->DrawTexturedPolygon( 4, vert ); // top right if ( m_bAltScopeMode ) { vgui::surface()->DrawSetTexture( m_iScopeTextureAlt[1] ); vert[0].Init( Vector2D( xMid, yTop ), uv11 ); vert[1].Init( Vector2D( xRight, yTop ), uv21 ); vert[2].Init( Vector2D( xRight, yMid ), uv22 ); vert[3].Init( Vector2D( xMid, yMid ), uv12 ); vgui::surface()->DrawTexturedPolygon( 4, vert ); } else { vgui::surface()->DrawSetTexture( m_iScopeTexture[1] ); vert[0].Init( Vector2D( xMid - 1, yTop ), uv11 ); vert[1].Init( Vector2D( xRight, yTop ), uv21 ); vert[2].Init( Vector2D( xRight, yMid + 1 ), uv22 ); vert[3].Init( Vector2D( xMid - 1, yMid + 1 ), uv12 ); vgui::surface()->DrawTexturedPolygon( 4, vert ); } // bottom right vgui::surface()->DrawSetTexture( m_bAltScopeMode ? m_iScopeTextureAlt[2] : m_iScopeTexture[2] ); vert[0].Init( Vector2D( xMid, yMid ), uv11 ); vert[1].Init( Vector2D( xRight, yMid ), uv21 ); vert[2].Init( Vector2D( xRight, yBottom ), uv22 ); vert[3].Init( Vector2D( xMid, yBottom ), uv12 ); vgui::surface()->DrawTexturedPolygon( 4, vert ); // bottom left vgui::surface()->DrawSetTexture( m_bAltScopeMode ? m_iScopeTextureAlt[3] : m_iScopeTexture[3] ); vert[0].Init( Vector2D( xLeft, yMid ), uv11 ); vert[1].Init( Vector2D( xMid, yMid ), uv21 ); vert[2].Init( Vector2D( xMid, yBottom ), uv22 ); vert[3].Init( Vector2D( xLeft, yBottom), uv12 ); vgui::surface()->DrawTexturedPolygon( 4, vert ); if ( xLeft > 0 ) { // Left block vgui::surface()->DrawFilledRect( 0, 0, xLeft, screenTall ); } if ( screenWide > xRight ) { // Right block vgui::surface()->DrawFilledRect( xRight, 0, screenWide, screenTall ); } if ( yTop > 0 ) { // top block vgui::surface()->DrawFilledRect( 0, 0, screenWide, yTop ); } if ( screenTall > yBottom ) { // bottom block vgui::surface()->DrawFilledRect( 0, yBottom, screenWide, screenTall ); } if( bDisableClipping ) g_pMatSystemSurface->DisableClipping( false ); }