Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1367 lines
37 KiB

//========= 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." );