diff options
Diffstat (limited to 'game/client/tf2/hud_minimap.cpp')
| -rw-r--r-- | game/client/tf2/hud_minimap.cpp | 1367 |
1 files changed, 1367 insertions, 0 deletions
diff --git a/game/client/tf2/hud_minimap.cpp b/game/client/tf2/hud_minimap.cpp new file mode 100644 index 0000000..dded27a --- /dev/null +++ b/game/client/tf2/hud_minimap.cpp @@ -0,0 +1,1367 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "hud_minimap.h" +#include <vgui_controls/Controls.h> +#include <vgui/ISurface.h> +#include <vgui/IVGui.h> +#include <vgui/IInput.h> +#include <vgui_controls/AnimationController.h> +#include "vgui_bitmapimage.h" +#include "clientmode_tfbase.h" +#include "clientmode_tfnormal.h" +#include "hud.h" +#include "hud_commander_statuspanel.h" +#include "view.h" +#include "filesystem.h" +#include "imessagechars.h" +#include "hud_macros.h" +#include "c_tfteam.h" +#include "c_info_act.h" +#include "engine/IEngineSound.h" +#include "iinput.h" +#include "in_buttons.h" +#include "c_basetfplayer.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +static ConVar minimap_visible( "minimap_visible", "1", 0, "Draw minimap?" ); +ConVar minimap_zoomtime( "minimap_zoomtime", "0.4", 0, "How long it takes to resize the minimap." ); + + +static ConVar current_team( "current_team", "-1", 0 ); + +// Start out new maps at this zoom level +#define DEFAULT_ZOOM_LEVEL 0 + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Instantiate a temporary trace (position based, or entity based) +//----------------------------------------------------------------------------- +void MinimapCreateTempTrace( const char* pMetaClassName, int sortOrder, const Vector &vecPosition ) +{ + MinimapInitData_t initData; + initData.m_vecPosition = vecPosition; + + PanelMetaClassMgr()->CreatePanelMetaClass( + pMetaClassName, sortOrder, &initData, CMinimapPanel::MinimapRootPanel() ); +} + +void MinimapCreateTempTrace( const char* pMetaClassName, int sortOrder, C_BaseEntity *pEntity, const Vector &vecOffset ) +{ + MinimapInitData_t initData; + initData.m_pEntity = pEntity; + initData.m_vecPosition = vecOffset; + + PanelMetaClassMgr()->CreatePanelMetaClass( + pMetaClassName, sortOrder, &initData, CMinimapPanel::MinimapRootPanel() ); +} + +//----------------------------------------------------------------------------- +// dummy root panel for all minimap traces +//----------------------------------------------------------------------------- +class CMinimapRootPanel : public Panel +{ + typedef Panel BaseClass; + +public: + CMinimapRootPanel( Panel *pParent = NULL ) + : BaseClass( pParent,"CMinimapRootPanel" ) + { + SetPaintBackgroundEnabled( false ); + SetPaintEnabled( false ); + SetAutoDelete( false ); + } +}; + +class CTextHelpPanel : public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CTextHelpPanel, vgui::Panel ) + +public: + + CTextHelpPanel(); + + virtual void Paint(); + virtual void PaintBackground(); + + void SetImage( BitmapImage *image ); + + virtual void ApplySettings( KeyValues *inResourceData ) + { + BaseClass::ApplySettings( inResourceData ); + } + +private: + + BitmapImage *m_pImage; + + CPanelAnimationVar( Color, m_OverlayColor, "OverlayColor", "White" ); + CPanelAnimationVar( Color, m_BorderColor, "BorderColor", "BrightFg" ); + CPanelAnimationVar( Color, m_BackgroundColor, "BackgroundColor", "Black" ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTextHelpPanel::CTextHelpPanel() +: BaseClass( NULL, "HudMinimapTextHelpPanel" ) +{ + Panel *pParent = g_pClientMode->GetViewport(); + SetParent( pParent ); + + SetVisible( false ); + SetZPos( 1 ); + + m_pImage = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTextHelpPanel::PaintBackground() +{ + // Get alpha from image + if ( m_pImage ) + { + Color bg = m_BackgroundColor; + int r, g, b, a; + m_pImage->GetColor( r, g, b, a ); + bg[3] = a; + SetBgColor( bg ); + + BaseClass::PaintBackground(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTextHelpPanel::Paint() +{ + BaseClass::Paint(); + + if ( !m_pImage ) + return; + + m_pImage->SetColor( m_OverlayColor ); + m_pImage->DoPaint( GetVPanel() ); + + int w, h; + GetSize( w, h ); + + surface()->DrawSetColor( m_BorderColor ); + surface()->DrawOutlinedRect( 0, 0, w, h ); + surface()->DrawOutlinedRect( 1, 1, w-1, h-1 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *image - +// x - +// y - +// w - +// h - +//----------------------------------------------------------------------------- +void CTextHelpPanel::SetImage( BitmapImage *image ) +{ + m_pImage = image; +} + +//----------------------------------------------------------------------------- +// globals +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// All traces are children of this panel +//----------------------------------------------------------------------------- +Panel *CMinimapPanel::MinimapRootPanel() +{ + static CMinimapRootPanel s_MinimapRootPanel; + return &s_MinimapRootPanel; +} + +CMinimapPanel *CMinimapPanel::MinimapPanel() +{ + ClientModeTFBase *pBasemode = ( ClientModeTFBase * )g_pClientMode; + if ( !pBasemode ) + return NULL; + + return pBasemode->GetMinimap(); +} + +DECLARE_HUDELEMENT( CMinimapPanel ); +DECLARE_HUD_MESSAGE( CMinimapPanel, MinimapPulse ); + +//----------------------------------------------------------------------------- +// Purpose: Placeholder for small overview map with viewport rectangle/selector +//----------------------------------------------------------------------------- +CMinimapPanel::CMinimapPanel( const char *pElementName ) +: CHudElement( pElementName ), BaseClass( NULL, "HudMinimap" ) +{ + Panel *pParent = g_pClientMode->GetViewport(); + SetParent( pParent ); + + SetAutoDelete( false ); + for ( int i = 0; i < MAX_ACT_TEAMS; i++ ) + { + m_pBackground[ i ] = 0; + } + memset( m_rgOverlays, 0, sizeof( m_rgOverlays ) ); + + m_flExpansionFrac = 0.0f; + + // Minimap zoom + m_bMinimapZoomed = false; + m_nZoomLevel = DEFAULT_ZOOM_LEVEL; + + m_vecCurrentOrigin.Init(); + m_vecMapCenter.Init(); + m_flMapAspectRatio = 1.0f; + m_flViewportAspectRatio = 1.0f; + m_flAspectAdjustment = 1.0f; + m_flNormalizedXScale = 1.0f; + m_flNormalizedYScale = 1.0f; + m_flNormalizedXOffset = 0.0f; + m_flNormalizedYOffset = 0.0f; + + m_nCurrentAct = ACT_NONE_SPECIFIED; + + m_pTextPanel = new CTextHelpPanel(); + m_pBackgroundPanel = new Panel( NULL, "BackgroundPanel" ); + + // We're gonna manage the lifetime of the text panel + // since we change it's hierarchical connections from time to time + m_pTextPanel->SetAutoDelete( false ); + m_pBackgroundPanel->SetAutoDelete( false ); + SetAutoDelete( false ); + + m_pClient = NULL; + + m_flZoomAdjust = 1.0f; + m_flPrevZoomAmount = 0.01f; + + SetZPos( 10 ); + + ivgui()->AddTickSignal( GetVPanel() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CMinimapPanel::~CMinimapPanel( void ) +{ + delete m_pTextPanel; + delete m_pBackgroundPanel; + + for ( int i = 0; i < MAX_ACT_TEAMS; i++ ) + { + delete m_pBackground[ i ]; + } + + ShutdownOverlays(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *scheme - +//----------------------------------------------------------------------------- +void CMinimapPanel::ApplySchemeSettings( IScheme *scheme ) +{ + BaseClass::ApplySchemeSettings( scheme ); + SetPaintBackgroundEnabled( false ); +} + + +//----------------------------------------------------------------------------- +// initialization +//----------------------------------------------------------------------------- +void CMinimapPanel::Init( IMinimapClient* pClient ) +{ + m_pClient = pClient; +} + +//----------------------------------------------------------------------------- +// Call this when the minimap panel is going to be drawn... +//----------------------------------------------------------------------------- +void CMinimapPanel::Activate() +{ + // The panel is a view into the minimap root panel + MinimapRootPanel()->SetParent( this ); + + Panel *pParent = g_pClientMode->GetViewport(); + + if ( pParent && m_pBackgroundPanel ) + { + m_pBackgroundPanel->SetParent( pParent ); + m_pBackgroundPanel->SetBounds( XRES( 0 ), YRES( 0 ), XRES( 640 ), YRES( 480 ) ); + m_pBackgroundPanel->SetVisible( false ); + m_pBackgroundPanel->SetZPos( 0 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : w - +// h - +//----------------------------------------------------------------------------- +void CMinimapPanel::OnSizeChanged( int w, int h ) +{ + BaseClass::OnSizeChanged( w, h ); + + MinimapRootPanel()->SetSize( w, h ); + + // Make sure icons are snapped to current window size + InvokeOnTickOnChildren( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CMinimapPanel::GetAdjustedZoom( void ) +{ + return m_flZoomAmount * m_flZoomAdjust; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CMinimapPanel::GetTrueZoom() +{ + return m_flZoomAmount; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : center - +// scale - +//----------------------------------------------------------------------------- +void CMinimapPanel::GetMapOriginAndScale( Vector& origin, float& scale ) +{ + origin = m_vecCurrentOrigin; + scale = GetAdjustedZoom(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : clip - +// pos - +// outx - +// outy - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CMinimapPanel::WorldToMinimap( MinimapPosType_t posType, const Vector& pos, float& outx, float& outy ) +{ + Vector origin; + float zoomscale; + + GetMapOriginAndScale( origin, zoomscale ); + + return InternalWorldToMinimap( posType, pos, origin, zoomscale, outx, outy ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : x - +// y - +//----------------------------------------------------------------------------- +void CMinimapPanel::AdjustNormalizedPositionForAspectRatio( float& x, float& y ) +{ + x = m_flNormalizedXOffset + x * m_flNormalizedXScale; + y = m_flNormalizedYOffset + y * m_flNormalizedYScale; +} + +//----------------------------------------------------------------------------- +// Converts a world-space position to a coordinate in minimap panel space +//----------------------------------------------------------------------------- +bool CMinimapPanel::InternalWorldToMinimap( MinimapPosType_t posType, const Vector &pos, const Vector& origin, float zoomscale, float& outx, float& outy ) +{ + int wide, tall; + MinimapRootPanel()->GetSize( wide, tall ); + + Vector worldmins, worldmaxs; + MapData().GetMapBounds( worldmins, worldmaxs ); + Vector worldsize = worldmaxs - worldmins; + + Vector test = ( pos - origin ); + + float xfraction = 0.0f; + + if ( worldsize.x > 0 ) + { + xfraction = (test.x - worldmins.x) / (worldmaxs.x - worldmins.x); + } + + float yfraction = 0.0f; + + if ( worldsize.y > 0 ) + { + yfraction = (test.y - worldmins.y) / (worldmaxs.y - worldmins.y); + } + + xfraction = ( xfraction - 0.5f ) * zoomscale + 0.5f; + yfraction = ( yfraction - 0.5f ) * zoomscale + 0.5f; + + yfraction = 1.0f - yfraction; + + // Adjust in case not all of map can be shown + AdjustNormalizedPositionForAspectRatio( xfraction, yfraction ); + + // Normalize? + bool inside = true; + switch ( posType ) + { + case MINIMAP_CLIP: + { + // Clip the vector from minimap center to object + // to the minimap bounds and put the object on the edge + Vector2D delta( xfraction - 0.5f, yfraction - 0.5f ); + Vector2D fdelta( fabs(delta.x), fabs(delta.y) ); + if (fdelta.x > fdelta.y) + { + // It's more horizontal than vertical.. + if (fdelta.x >= 0.5f) + { + float flRatio = delta.y / delta.x; + xfraction = clamp(xfraction, 0, 1); + yfraction = (xfraction - 0.5f) * flRatio + 0.5f; + } + } + else + { + if (fdelta.y >= 0.5f) + { + // It's more vertical than horizontal + float flRatio = delta.x / delta.y; + yfraction = clamp(yfraction, 0, 1); + xfraction = (yfraction - 0.5f) * flRatio + 0.5f; + } + } + } + break; + + case MINIMAP_CLAMP: + { + // Clamp the position to lie within the minimap + xfraction = clamp(xfraction, 0, 1); + yfraction = clamp(yfraction, 0, 1); + } + break; + + case MINIMAP_NOCLIP: + { + // See if it's off screen + if ( xfraction < 0.0 || xfraction > 1.0 || + yfraction < 0.0 || yfraction > 1.0 ) + { + inside = false; + } + } + break; + } + + outx = xfraction * wide; + outy = yfraction * tall; + + return inside; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *mapname - +//----------------------------------------------------------------------------- +void CMinimapPanel::LevelInit( const char *mapname ) +{ + SetBackgroundMaterials( MapData().m_Minimap.m_szBackgroundMaterial ); + + m_nZoomLevel = DEFAULT_ZOOM_LEVEL; + + HOOK_HUD_MESSAGE( CMinimapPanel, MinimapPulse ); +} + +//----------------------------------------------------------------------------- +// Purpose: Play a pulse on the minimap +//----------------------------------------------------------------------------- +void CMinimapPanel::MsgFunc_MinimapPulse( bf_read &msg ) +{ + Vector vecPosition; + msg.ReadBitVec3Coord( vecPosition ); + C_TFTeam *pTeam = (C_TFTeam *)GetLocalTeam(); + if ( pTeam ) + { + pTeam->NotifyBaseUnderAttack( vecPosition, false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMinimapPanel::LevelShutdown( void ) +{ +} + +//----------------------------------------------------------------------------- +// Sets the background material +//----------------------------------------------------------------------------- +void CMinimapPanel::SetBackgroundMaterials( const char *pMaterialName ) +{ + int i; + for ( i = 0; i < MAX_ACT_TEAMS; i++ ) + { + delete m_pBackground[ i ]; + m_pBackground[ i ] = NULL; + } + + if ( pMaterialName[ 0 ] ) + { + for ( i = 0; i < MAX_ACT_TEAMS; i++ ) + { + char teammaterial[ 512 ]; + + Q_snprintf( teammaterial, sizeof( teammaterial ), "%s_team%i", + pMaterialName, i + 1 ); + + // If a _team# version exists, use that, otherwise, use the default + if ( g_pFullFileSystem->FileExists( VarArgs( "materials/%s.vmt", teammaterial ) ) ) + { + m_pBackground[ i ] = new BitmapImage( GetVPanel(), teammaterial ); + } + else + { + m_pBackground[ i ] = new BitmapImage( GetVPanel(), pMaterialName ); + } + } + } + + if ( m_pTextPanel ) + { + m_pTextPanel->SetImage( NULL ); + } + + ShutdownOverlays(); + InitOverlays( pMaterialName ); +} + +//----------------------------------------------------------------------------- +// Called when the mouse is hit +//----------------------------------------------------------------------------- +void CMinimapPanel::OnMousePressed(MouseCode code) +{ + if ((code == MOUSE_LEFT) && m_pClient) + { + // Convert mouse position to world position + int x, y; + vgui::input()->GetCursorPos( x, y ); + + int w, h; + GetSize( w, h ); + + Vector worldPos; + worldPos.x = (float) x / (float) w; + worldPos.y = 1.0f - (float) y / (float) h; + worldPos.z = 0; // z isn't used + + Vector worldMins, worldMaxs; + MapData().GetMapBounds( worldMins, worldMaxs ); + worldPos *= (worldMaxs - worldMins); + worldPos += worldMins; + + m_pClient->MinimapClicked( worldPos ); + } +} + +void CMinimapPanel::SetBackgroundViewport( float minx, float miny, float maxx, float maxy, bool includedetails ) +{ + int i; + int x, y, w, h; + + x = 0; + y = 0; + w = GetWide(); + h = GetTall(); + + if ( minx < 0.0f || maxx > 1.0f || + miny < 0.0f || maxy > 1.0f ) + { + float x0 = 0.0f; + float y0 = 0.0f; + float x1 = 1.0f; + float y1 = 1.0f; + + float xrange = maxx - minx; + float yrange = maxy - miny; + + if ( minx < 0.0f ) + { + x0 = -minx / xrange; + //xrange += minx; + maxx -= minx; + minx = 0.0f; + } + if ( maxx > 1.0f ) + { + x1 = 1.0f - ( maxx - 1.0f ) / xrange; + maxx = 1.0f; + } + if ( miny < 0.0f ) + { + y0 = -miny / yrange; + //yrange += miny; + maxy -= miny; + miny = 0.0f; + } + if ( maxy > 1.0f ) + { + y1 = 1.0f - ( maxy - 1.0f ) / yrange; + maxy = 1.0f; + } + + x = x0 * w; + y = y0 * h; + w = x1 * w; + h = y1 * h; + } + + for ( i = 0; i < MAX_ACT_TEAMS; i++ ) + { + if ( m_pBackground[ i ] ) + { + m_pBackground[ i ]->SetPos( x, y ); + m_pBackground[ i ]->SetRenderSize( w, h ); + m_pBackground[ i ]->SetViewport( true, minx, miny, maxx, maxy ); + } + } + + if ( includedetails ) + { + int t; + for ( t = 0; t < MAX_ACT_TEAMS; t++ ) + { + for ( i = 0; i < MAX_ACTS; i++ ) + { + Overlays *p = &m_rgOverlays[ t ][ i ]; + if ( !p->m_bInUse ) + continue; + + if ( !p->m_pOverlay ) + continue; + + p->m_pOverlay->SetPos( x, y ); + p->m_pOverlay->SetRenderSize( w, h ); + p->m_pOverlay->SetViewport( true, minx, miny, maxx, maxy ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMinimapPanel::PaintActOverlays( int teamIndex, int alpha ) +{ + Assert( teamIndex >= 0 && teamIndex < MAX_ACT_TEAMS ); + + bool textshowing = false; + + int i = GetCurrentActNumber(); + if ( i != ACT_NONE_SPECIFIED ) + { + i = clamp( i, 0, MAX_ACTS - 1 ); + Overlays *p = &m_rgOverlays[ teamIndex ][ i ]; + if ( p->m_bInUse ) + { + int r, g, b, a; + + if ( p->m_pOverlay ) + { + p->m_pOverlay->GetColor( r, g, b, a ); + Color clr( r, g, b, alpha ); + p->m_pOverlay->SetColor( clr ); + p->m_pOverlay->DoPaint( NULL, 0, (float)alpha/255.0f ); + } + + if ( p->m_pText && m_pTextPanel ) + { + p->m_pText->GetColor( r, g, b, a ); + Color clr( r, g, b, alpha ); + p->m_pText->SetColor( clr ); + + m_pTextPanel->SetImage( p->m_pText ); + + clr = m_BackgroundColor; + clr[3] = alpha; + m_pBackgroundPanel->SetBgColor( clr ); + + textshowing = true; + } + } + } + + if ( !textshowing ) + { + m_pTextPanel->SetVisible( false ); + m_pTextPanel->SetImage( NULL ); + m_pBackgroundPanel->SetVisible( false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMinimapPanel::OnThink() +{ + BaseClass::OnThink(); + + C_BaseTFPlayer *local = C_BaseTFPlayer::GetLocalPlayer(); + if ( local ) + { + if ( local->m_TFLocal.m_bForceMapOverview && m_bMinimapZoomed != local->m_TFLocal.m_bForceMapOverview ) + { + SetMinimapZoom( local->m_TFLocal.m_bForceMapOverview ); + } + } + + if ( !IsVisible() ) + return; + + if ( m_flZoomAmount != m_flPrevZoomAmount ) + { + m_flPrevZoomAmount = m_flZoomAmount; + ComputeMapOrigin( m_vecCurrentOrigin ); + InvokeOnTickOnChildren( this ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMinimapPanel::Paint() +{ + if ( gHUD.IsHidden( HIDEHUD_MISCSTATUS ) ) + { + // Remove the minimap zoom if the hud's hidden + SetMinimapZoom( false ); + return; + } + + C_BasePlayer *local = C_BasePlayer::GetLocalPlayer(); + int team = 0; + if ( local ) + { + team = local->GetTeamNumber(); + } + + int w, h; + GetSize( w, h ); + + int alpha; + bool shouldDrawDetails = ShouldDrawZoomDetails( alpha ); + + if ( m_pTextPanel && m_pBackgroundPanel ) + { + m_pTextPanel->SetVisible( shouldDrawDetails ); + m_pBackgroundPanel->SetVisible( shouldDrawDetails ); + } + + // DEBUGGING: Allow cvar override + if ( current_team.GetInt() >= 0 ) + { + team = current_team.GetInt(); + } + + // Can can be 0 through MAX_ACT_TEAMS + team = clamp( team, 0, MAX_ACT_TEAMS ); + + // Array index is 0 to MAX_ACT_TEAMS - 1 where a team of zero means no team and won't be indexed + // due to logic that checks team > 0 + int teamIndex = clamp( team - 1, 0, MAX_ACT_TEAMS - 1 ); + + if ( m_pBackground[ teamIndex ] ) + { + if ( shouldDrawDetails ) + { + Color clr = m_BackgroundColor; + clr[3] *= alpha / 255.0f; + surface()->DrawSetColor( clr ); + surface()->DrawFilledRect( 0, 0, w, h ); + } + + float offsetx, offsety; + + // Need to translate m_vecCurrentOrigin into minimap space + InternalWorldToMinimap( + MINIMAP_NOCLIP, + m_vecCurrentOrigin, + -m_vecMapCenter, + 1, + offsetx, offsety ); + + // Scale to 0.0f to 1.0f + offsetx /= (float)w; + offsety /= (float)h; + + float minx, maxx, miny, maxy; + + float xscale = 1.0f; + float yscale = 1.0f; + float startx = 0.0f; + float starty = 0.0f; + + Assert( m_flAspectAdjustment > 0.0f ); + + // Note, the scale sense is inverted here + float invaspect = 1.0f / m_flAspectAdjustment; + + if ( m_flAspectAdjustment < 1.0f ) + { + xscale = invaspect; + startx = ( 1.0f - xscale ) * 0.5f; + } + else + { + yscale = m_flAspectAdjustment; + starty = ( 1.0f - yscale ) * 0.5f; + } + + float halfzoom = ( 1.0f / GetAdjustedZoom() ) * 0.5f; + + // zoom scale is already normalized, so just take half in one direction, half the other + minx = startx + xscale * ( offsetx - halfzoom ); + miny = starty + yscale * ( offsety - halfzoom ); + maxx = startx + xscale * ( offsetx + halfzoom ); + maxy = starty + yscale * ( offsety + halfzoom ); + + SetBackgroundViewport( minx, miny, maxx, maxy, shouldDrawDetails ); + + m_pBackground[ teamIndex ]->DoPaint( NULL ); + + if ( shouldDrawDetails && ( team > 0 ) ) + { + PaintActOverlays( teamIndex, alpha ); + } + } + else + { + Color clr = m_BackgroundColor; + clr[3] *= alpha * 0.9f / 255.0f; + + surface()->DrawSetColor( clr ); + surface()->DrawFilledRect( 0, 0, w, h ); + } + + // Dumb place to do this + if ( !shouldDrawDetails && CurrentActIsAWaitingAct() ) + { + char *cmsg = "Wait for game start..."; + int width, height; + messagechars->GetStringLength( g_hFontTrebuchet24, &width, &height, cmsg ); + messagechars->DrawString( g_hFontTrebuchet24, XRES(16), ScreenHeight() - (height * 6), 255, 255, 245, 255, cmsg, IMessageChars::MESSAGESTRINGID_NONE ); + } + + Color border = m_BorderColor; + border[3] = border[3] * ( alpha * 0.9f ) / 255.0f; + + //border = vgui::Color( 255, 0, 120, 255 ); + + surface()->DrawSetColor( border ); + surface()->DrawOutlinedRect( 0, 0, w, h ); + surface()->DrawOutlinedRect( 1, 1, w-1, h-1 ); +} + +void CMinimapPanel::InvokeOnTickOnChildren( vgui::Panel *parent ) +{ + if ( !parent ) + return; + + int c = parent->GetChildCount(); + int i; + for ( i = 0; i < c; i++ ) + { + vgui::Panel *child = parent->GetChild( i ); + child->OnTick(); + InvokeOnTickOnChildren( child ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMinimapPanel::OnTick() +{ + // See if the act's changed. If it has, bring up the act overlays. + if ( m_nCurrentAct != GetCurrentActNumber() ) + { + // g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "MinimapActChanged" ); + // SetMinimapZoom( true ); + m_nCurrentAct = GetCurrentActNumber(); + } + + // Cache these only once per frame if in a valid game + if ( C_BasePlayer::GetLocalPlayer() && minimap_visible.GetBool() ) + { + SetVisible( true ); + ComputeMapOrigin( m_vecCurrentOrigin ); + } + else + { + SetVisible( false ); + } + + InvokeOnTickOnChildren( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : alpha - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CMinimapPanel::ShouldDrawZoomDetails( int& alpha ) +{ + alpha = (int)m_flDetailsAlpha; + alpha = clamp( alpha, 0, 255 ); + + if ( !alpha ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : center - +//----------------------------------------------------------------------------- +void CMinimapPanel::ComputeMapOrigin( Vector& origin ) +{ + Vector worldmins, worldmaxs, worldsize; + MapData().GetMapBounds( worldmins, worldmaxs ); + VectorSubtract( worldmaxs, worldmins, worldsize ); + + // Cache true map center + m_vecMapCenter = ( worldmins + worldmaxs ) * 0.5f; + + C_BasePlayer* pPlayer = C_BasePlayer::GetLocalPlayer(); + + Vector playerOrigin; + + if( !pPlayer ) + { + playerOrigin = m_vecMapCenter; + } + else + { + playerOrigin = pPlayer->GetAbsOrigin(); + } + + origin.Init(); + + playerOrigin.z = m_vecMapCenter.z = 0.0f; + + Vector delta = playerOrigin - m_vecMapCenter; + + // Map center pointer is biased toward player origin as we become zoomed in to 1.0x to 2.5x and toward true world center as we zoom all the way out + VectorScale( delta, m_flCenterOnPlayer, origin ); + + int vw, vh; + GetSize( vw, vh ); + + m_flMapAspectRatio = 1.0f; + m_flViewportAspectRatio = 1.0f; + m_flAspectAdjustment = 1.0f; + + if ( vh > 0 ) + { + m_flViewportAspectRatio = ( float )vw / ( float )vh; + } + + if ( worldsize.y > 0 ) + { + m_flMapAspectRatio = worldsize.x / worldsize.y; + } + + if ( m_flViewportAspectRatio > 0 ) + { + m_flAspectAdjustment = m_flMapAspectRatio / m_flViewportAspectRatio; + } + + float fittedworldunitsperpixel; + float zoomedworldunitsperpixel; + float zooomedoutworldunitsperpixel; + float actualworldunitsperpixel; + + if ( m_flAspectAdjustment > 1.0f ) + { + // World height fits exactly in minimap height at zoom 1x + fittedworldunitsperpixel = worldsize.y / (float)( vh ); + + // At higher zoom we get less world units per pixel + zoomedworldunitsperpixel = fittedworldunitsperpixel / m_flZoomAmount; + + // at fully zoomed back view, world width fits window width instead + zooomedoutworldunitsperpixel = worldsize.x / (float)( vw ); + + // As we center more on player, we move away from zoomed out units and toward zoomed units per pixel + actualworldunitsperpixel = zooomedoutworldunitsperpixel + m_flCenterOnPlayer * ( zoomedworldunitsperpixel - zooomedoutworldunitsperpixel ); + + m_flZoomAdjust = (1-m_flCenterOnPlayer) + m_flCenterOnPlayer * ( 1/m_flViewportAspectRatio ); + } + else + { + // World width fits exactly in minimap width at zoom 1x + fittedworldunitsperpixel = worldsize.x / (float)( vw ); + + // At higher zoom we get less world units per pixel + zoomedworldunitsperpixel = fittedworldunitsperpixel / m_flZoomAmount; + + // at fully zoomed back view, world height fits window height instead + zooomedoutworldunitsperpixel = worldsize.y / (float)( vh ); + + // As we center more on player, we move away from zoomed out units and toward zoomed units per pixel + actualworldunitsperpixel = zooomedoutworldunitsperpixel + m_flCenterOnPlayer * ( zoomedworldunitsperpixel - zooomedoutworldunitsperpixel ); + + m_flZoomAdjust = (1-m_flCenterOnPlayer) + m_flCenterOnPlayer * ( 1/m_flAspectAdjustment ); + } + + Vector preOrigin = origin; + + float inset_pixels = m_flInsetPixels; + + float viewport_width_world_units = ( float )( vw - 2 * inset_pixels ) * actualworldunitsperpixel; + float viewport_height_world_units = ( float )( vh - 2 * inset_pixels ) * actualworldunitsperpixel; + + // Insets apply when centering on player + m_flWorldSpaceInsets[ 0 ] = MIN( m_vecMapCenter.x, worldmins.x + m_flCenterOnPlayer * ( viewport_width_world_units ) * 0.5f ); + m_flWorldSpaceInsets[ 1 ] = MIN( m_vecMapCenter.y, worldmins.y + m_flCenterOnPlayer * ( viewport_height_world_units ) * 0.5f ); + m_flWorldSpaceInsets[ 2 ] = MAX( m_vecMapCenter.x, worldmaxs.x - m_flCenterOnPlayer * ( viewport_width_world_units ) * 0.5f ); + m_flWorldSpaceInsets[ 3 ] = MAX( m_vecMapCenter.y, worldmaxs.y - m_flCenterOnPlayer * ( viewport_height_world_units ) * 0.5f ); + + // Assuming origin is at center of view, compute world space left, top, right, bottom + m_flWorldSpaceBounds[ 0 ] = m_vecMapCenter.x + origin.x - viewport_width_world_units * 0.5f; + m_flWorldSpaceBounds[ 1 ] = m_vecMapCenter.y + origin.y - viewport_height_world_units * 0.5f; + m_flWorldSpaceBounds[ 2 ] = m_vecMapCenter.x + origin.x + viewport_width_world_units * 0.5f; + m_flWorldSpaceBounds[ 3 ] = m_vecMapCenter.y + origin.y + viewport_height_world_units * 0.5f; + + // Clip these bounds + m_flClippedWorldSpaceBounds[ 0 ] = MAX( worldmins.x, m_flWorldSpaceBounds[ 0 ] ); + m_flClippedWorldSpaceBounds[ 1 ] = MAX( worldmins.y, m_flWorldSpaceBounds[ 1 ] ); + m_flClippedWorldSpaceBounds[ 2 ] = MIN( worldmaxs.x, m_flWorldSpaceBounds[ 2 ] ); + m_flClippedWorldSpaceBounds[ 3 ] = MIN( worldmaxs.y, m_flWorldSpaceBounds[ 3 ] ); + + // Clip origin to inserts + origin.x = clamp( origin.x, m_flWorldSpaceInsets[ 0 ] - m_vecMapCenter.x, m_flWorldSpaceInsets[ 2 ] - m_vecMapCenter.x ); + origin.y = clamp( origin.y, m_flWorldSpaceInsets[ 1 ] - m_vecMapCenter.y, m_flWorldSpaceInsets[ 3 ] - m_vecMapCenter.y ); + + /* + engine->Con_NPrintf( 1, "map bounds left %i top %i right %i bottom %i", + (int)worldmins.x, + (int)worldmins.y, + (int)worldmaxs.x, + (int)worldmaxs.y ); + + engine->Con_NPrintf( 2, "world space bounds left %i top %i right %i bottom %i", + (int)m_flWorldSpaceBounds[ 0 ], + (int)m_flWorldSpaceBounds[ 1 ], + (int)m_flWorldSpaceBounds[ 2 ], + (int)m_flWorldSpaceBounds[ 3 ] ); + + engine->Con_NPrintf( 3, "world space insets left %i top %i right %i bottom %i", + (int)m_flWorldSpaceInsets[ 0 ], + (int)m_flWorldSpaceInsets[ 1 ], + (int)m_flWorldSpaceInsets[ 2 ], + (int)m_flWorldSpaceInsets[ 3 ] ); + + engine->Con_NPrintf( 4, "world space clipping left %i top %i right %i bottom %i", + (int)m_flClippedWorldSpaceBounds[ 0 ], + (int)m_flClippedWorldSpaceBounds[ 1 ], + (int)m_flClippedWorldSpaceBounds[ 2 ], + (int)m_flClippedWorldSpaceBounds[ 3 ] ); + + + engine->Con_NPrintf( 5, "world center %i %i", + (int)m_vecMapCenter.x, (int)m_vecMapCenter.y ); + + engine->Con_NPrintf( 6, "player origin %i %i", + (int)playerOrigin.x, (int)playerOrigin.y ); + + engine->Con_NPrintf( 7, "desired map center %i %i", + (int)( m_vecMapCenter.x + preOrigin.x ), (int)( preOrigin.y + m_vecMapCenter.x ) ); + + engine->Con_NPrintf( 8, "actual map center %i %i", + (int)( m_vecMapCenter.x + origin.x ), (int)( origin.y + m_vecMapCenter.y ) ); + + engine->Con_NPrintf( 9, "viewport (%ix%i) aspect %.2f world (%ix%i) aspect %f", + vw, vh, m_flViewportAspectRatio, (int)worldsize.x, (int)worldsize.y, m_flMapAspectRatio ); + + engine->Con_NPrintf( 10, "zoom %.3f zoom adjust %.3f", + m_flZoomAmount, m_flZoomAdjust ); + + engine->Con_NPrintf( 11, "viewport %i x %i", + (int)viewport_width_world_units, (int)viewport_height_world_units ); + */ + + // Assume 100% scale and no x or y offset to make up for aspect ration diff + m_flNormalizedXScale = 1.0f; + m_flNormalizedYScale = 1.0f; + m_flNormalizedXOffset = 0.0f; + m_flNormalizedYOffset = 0.0f; + + if ( m_flAspectAdjustment < 1.0f ) + { + m_flNormalizedXScale = m_flAspectAdjustment; + // Offset 0->1 version of x value + m_flNormalizedXOffset = ( 1.0f - m_flNormalizedXScale ) * 0.5f; + } + else + { + m_flNormalizedYScale = 1.0f / m_flAspectAdjustment; + // Offset 0->1 version of y value + m_flNormalizedYOffset = ( 1.0f - m_flNormalizedYScale ) * 0.5f; + } +} + +void CMinimapPanel::InitOverlays( const char *materialrootname ) +{ + if ( !materialrootname || !materialrootname[0] ) + return; + + int t; + for ( t = 0; t < MAX_ACT_TEAMS; t++ ) + { + char teamnum[ 2 ]; + Q_snprintf( teamnum, sizeof( teamnum ), "%01i", t + 1 ); + + int i; + for ( i = 0; i < MAX_ACTS; i++ ) + { + char actnum[ 3 ]; + + char filename[ 512 ]; + + Q_snprintf( actnum, sizeof( actnum ), "%02i", i ); + + Overlays *p = &m_rgOverlays[ t ][ i ]; + Assert( p && !p->m_bInUse ); + + Q_snprintf( filename, sizeof( filename ), "%s_act%s_overlay_team%s", + materialrootname, actnum, teamnum ); + + // Check if file exists + if ( g_pFullFileSystem->FileExists( VarArgs( "materials/%s.vmt", filename ) ) ) + { + p->m_pOverlay = new BitmapImage( GetVPanel(), filename ); + p->m_bInUse = true; + } + else + { + // Try it without the team number + Q_snprintf( filename, sizeof( filename ), "%s_act%s_overlay", + materialrootname, actnum ); + + if ( g_pFullFileSystem->FileExists( VarArgs( "materials/%s.vmt", filename ) ) ) + { + p->m_pOverlay = new BitmapImage( GetVPanel(), filename ); + p->m_bInUse = true; + } + } + + Q_snprintf( filename, sizeof( filename ), "%s_act%s_text_team%s", + materialrootname, actnum, teamnum ); + + // Check if file exists + if ( g_pFullFileSystem->FileExists( VarArgs( "materials/%s.vmt", filename ) ) ) + { + p->m_pText = new BitmapImage( GetTextPaintPanel()->GetVPanel(), filename ); + p->m_bInUse = true; + } + else + { + // Try it without the team number + Q_snprintf( filename, sizeof( filename ), "%s_act%s_text", + materialrootname, actnum ); + + // Check if file exists + if ( g_pFullFileSystem->FileExists( VarArgs( "materials/%s.vmt", filename ) ) ) + { + p->m_pText = new BitmapImage( GetTextPaintPanel()->GetVPanel(), filename ); + p->m_bInUse = true; + } + } + } + } +} + +void CMinimapPanel::ShutdownOverlays( void ) +{ + int t; + for ( t = 0; t < MAX_ACT_TEAMS; t++ ) + { + + int i; + for ( i = 0; i < MAX_ACTS; i++ ) + { + Overlays *p = &m_rgOverlays[ t ][ i ]; + Assert( p ); + if ( !p->m_bInUse ) + continue; + + delete p->m_pOverlay; + delete p->m_pText; + p->m_bInUse = false; + p->m_pOverlay = NULL; + p->m_pText = NULL; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Panel +//----------------------------------------------------------------------------- +Panel *CMinimapPanel::GetTextPaintPanel( void ) +{ + ClientModeTFBase *basemode = ( ClientModeTFBase * )g_pClientMode; + if ( !basemode ) + { + Assert( 0 ); + return NULL; + } + + return basemode->GetMinimapParent(); +} + +void CMinimapPanel::ZoomIn( void ) +{ + if ( m_flDetailsAlpha > 0 ) + { + if ( m_nZoomLevel != 0 ) + { + // g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( + // "MinimapZoomLevel0" ); + } + + // Full window + m_nZoomLevel = 0; + } + else + { + m_nZoomLevel = ( m_nZoomLevel + 1 ) % ( NUM_WIDTHS ); + m_nZoomLevel = clamp( m_nZoomLevel, 0, NUM_WIDTHS - 1 ); + + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( + m_nZoomLevel == 0 ? + "MinimapZoomLevel0" : + "MinimapZoomLevel1" ); + } +} + +void CMinimapPanel::Zoom_Minimap_f( void ) +{ + ClientModeTFBase *basemode = ( ClientModeTFBase * )g_pClientMode; + if ( !basemode ) + return; + + CMinimapPanel *minimap = basemode->GetMinimap(); + if ( !minimap ) + return; + + minimap->ZoomIn(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMinimapPanel::ToggleMinimap( void ) +{ + int iKeybits = ::input->GetButtonBits( 0 ); + bool hitting_button = ( iKeybits & (IN_ATTACK | IN_ATTACK2 | IN_JUMP) ) ? true : false; + if ( hitting_button && !m_bMinimapZoomed ) + { + CLocalPlayerFilter filter; + C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "ClientModeTFNormal.ToggleMinimap" ); + } + + SetMinimapZoom( !m_bMinimapZoomed ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Toggle_Minimap_f( void ) +{ + ClientModeTFBase *basemode = ( ClientModeTFBase * )g_pClientMode; + if ( !basemode ) + return; + + CMinimapPanel *minimap = basemode->GetMinimap(); + if ( !minimap ) + return; + minimap->ToggleMinimap(); +} + +static ConCommand minimap( "minimap", Toggle_Minimap_f, "Toggle size of the tf2 minimap." ); + +//----------------------------------------------------------------------------- +// Purpose: Set the state of the minimap's zoom +//----------------------------------------------------------------------------- +void CMinimapPanel::SetMinimapZoom( bool bZoom ) +{ + C_BaseTFPlayer *local = C_BaseTFPlayer::GetLocalPlayer(); + if ( local && local->m_TFLocal.m_bForceMapOverview ) + { + bZoom = true; + } + + bool changed = bZoom != m_bMinimapZoomed; + m_bMinimapZoomed = bZoom; + if ( bZoom ) + { + m_nZoomLevel = 0; + } + + if ( changed ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( + m_bMinimapZoomed ? + "MinimapZoomToFullScreen" : + "MinimapZoomToCorner"); + + CLocalPlayerFilter filter; + C_BaseEntity::EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, m_bMinimapZoomed ? "Minimap.ZoomIn" : "Minimap.ZoomOut" ); + + } +} + +//----------------------------------------------------------------------------- +// Purpose: Get at input data before it's used +//----------------------------------------------------------------------------- +void CMinimapPanel::ProcessInput() +{ + int iKeybits = ::input->GetButtonBits( 0 ); + + bool hitting_button = ( iKeybits & (IN_ATTACK | IN_ATTACK2 | IN_JUMP) ) ? true : false; + + // While the minimap's zoomed, + if ( m_bMinimapZoomed && hitting_button ) + { + SetMinimapZoom( false ); + ::input->ClearInputButton( (IN_ATTACK | IN_ATTACK2 | IN_JUMP) ); + } + + CHudElement::ProcessInput(); +} + +static ConCommand zoom_minimap( "zoom_minimap", CMinimapPanel::Zoom_Minimap_f, "Zoom in on minimap." ); + + + + |