diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/client/game_controls/MapOverview.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/client/game_controls/MapOverview.cpp')
| -rw-r--r-- | mp/src/game/client/game_controls/MapOverview.cpp | 1340 |
1 files changed, 1340 insertions, 0 deletions
diff --git a/mp/src/game/client/game_controls/MapOverview.cpp b/mp/src/game/client/game_controls/MapOverview.cpp new file mode 100644 index 00000000..9f335b26 --- /dev/null +++ b/mp/src/game/client/game_controls/MapOverview.cpp @@ -0,0 +1,1340 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: MapOverview.cpp: implementation of the CMapOverview class.
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "mapoverview.h"
+#include <vgui/ISurface.h>
+#include <vgui/ILocalize.h>
+#include <filesystem.h>
+#include <KeyValues.h>
+#include <convar.h>
+#include "mathlib/mathlib.h"
+#include <game/client/iviewport.h>
+#include <igameresources.h>
+#include "gamevars_shared.h"
+#include "spectatorgui.h"
+#include "c_playerresource.h"
+#include "view.h"
+
+#include "clientmode.h"
+#include <vgui_controls/AnimationController.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+ConVar overview_health( "overview_health", "1", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Show player's health in map overview.\n" );
+ConVar overview_names ( "overview_names", "1", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Show player's names in map overview.\n" );
+ConVar overview_tracks( "overview_tracks", "1", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Show player's tracks in map overview.\n" );
+ConVar overview_locked( "overview_locked", "1", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Locks map angle, doesn't follow view angle.\n" );
+ConVar overview_alpha( "overview_alpha", "1.0", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Overview map translucency.\n" );
+
+IMapOverviewPanel *g_pMapOverview = NULL; // we assume only one overview is created
+
+static int AdjustValue( int curValue, int targetValue, int amount )
+{
+ if ( curValue > targetValue )
+ {
+ curValue -= amount;
+
+ if ( curValue < targetValue )
+ curValue = targetValue;
+ }
+ else if ( curValue < targetValue )
+ {
+ curValue += amount;
+
+ if ( curValue > targetValue )
+ curValue = targetValue;
+ }
+
+ return curValue;
+}
+
+CON_COMMAND( overview_zoom, "Sets overview map zoom: <zoom> [<time>] [rel]" )
+{
+ if ( !g_pMapOverview || args.ArgC() < 2 )
+ return;
+
+ float zoom = Q_atof( args[ 1 ] );
+
+ float time = 0;
+
+ if ( args.ArgC() >= 3 )
+ time = Q_atof( args[ 2 ] );
+
+ if ( args.ArgC() == 4 )
+ zoom *= g_pMapOverview->GetZoom();
+
+ // We are going to store their zoom pick as the resultant overview size that it sees. This way, the value will remain
+ // correct even on a different map that has a different intrinsic zoom.
+ float desiredViewSize = 0.0f;
+ desiredViewSize = (zoom * OVERVIEW_MAP_SIZE * g_pMapOverview->GetFullZoom()) / g_pMapOverview->GetMapScale();
+ g_pMapOverview->SetPlayerPreferredViewSize( desiredViewSize );
+
+ if( !g_pMapOverview->AllowConCommandsWhileAlive() )
+ {
+ C_BasePlayer *localPlayer = CBasePlayer::GetLocalPlayer();
+ if( localPlayer && CBasePlayer::GetLocalPlayer()->IsAlive() )
+ return;// Not allowed to execute commands while alive
+ else if( localPlayer && localPlayer->GetObserverMode() == OBS_MODE_DEATHCAM )
+ return;// In the death cam spiral counts as alive
+ }
+
+ g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( g_pMapOverview->GetAsPanel(), "zoom", zoom, 0.0, time, vgui::AnimationController::INTERPOLATOR_LINEAR );
+}
+
+CON_COMMAND( overview_mode, "Sets overview map mode off,small,large: <0|1|2>" )
+{
+ if ( !g_pMapOverview )
+ return;
+
+ int mode;
+
+ if ( args.ArgC() < 2 )
+ {
+ // toggle modes
+ mode = g_pMapOverview->GetMode() + 1;
+
+ if ( mode > CMapOverview::MAP_MODE_FULL )
+ mode = CMapOverview::MAP_MODE_OFF;
+ }
+ else
+ {
+ // set specific mode
+ mode = Q_atoi( args[ 1 ] );
+ }
+
+ if( mode != CMapOverview::MAP_MODE_RADAR )
+ g_pMapOverview->SetPlayerPreferredMode( mode );
+
+ if( !g_pMapOverview->AllowConCommandsWhileAlive() )
+ {
+ C_BasePlayer *localPlayer = CBasePlayer::GetLocalPlayer();
+ if( localPlayer && CBasePlayer::GetLocalPlayer()->IsAlive() )
+ return;// Not allowed to execute commands while alive
+ else if( localPlayer && localPlayer->GetObserverMode() == OBS_MODE_DEATHCAM )
+ return;// In the death cam spiral counts as alive
+ }
+
+ g_pMapOverview->SetMode( mode );
+}
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+
+using namespace vgui;
+
+CMapOverview::CMapOverview( const char *pElementName ) : BaseClass( NULL, pElementName ), CHudElement( pElementName )
+{
+ SetParent( g_pClientMode->GetViewport()->GetVPanel() );
+
+ SetBounds( 0,0, 256, 256 );
+ SetBgColor( Color( 0,0,0,100 ) );
+ SetPaintBackgroundEnabled( true );
+ ShowPanel( false );
+
+ // Make sure we actually have the font...
+ vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
+
+ m_hIconFont = pScheme->GetFont( "DefaultSmall" );
+
+ m_nMapTextureID = -1;
+ m_MapKeyValues = NULL;
+
+ m_MapOrigin = Vector( 0, 0, 0 );
+ m_fMapScale = 1.0f;
+ m_bFollowAngle = false;
+ SetMode( MAP_MODE_OFF );
+
+ m_fZoom = 3.0f;
+ m_MapCenter = Vector2D( 512, 512 );
+ m_ViewOrigin = Vector2D( 512, 512 );
+ m_fViewAngle = 0;
+ m_fTrailUpdateInterval = 1.0f;
+
+ m_bShowNames = true;
+ m_bShowHealth = true;
+ m_bShowTrails = true;
+
+ m_flChangeSpeed = 1000;
+ m_flIconSize = 64.0f;
+
+ m_ObjectCounterID = 1;
+
+ Reset();
+
+ Q_memset( m_Players, 0, sizeof(m_Players) );
+
+ InitTeamColorsAndIcons();
+
+ g_pMapOverview = this; // for cvars access etc
+}
+
+void CMapOverview::Init( void )
+{
+ // register for events as client listener
+ ListenForGameEvent( "game_newmap" );
+ ListenForGameEvent( "round_start" );
+ ListenForGameEvent( "player_connect" );
+ ListenForGameEvent( "player_info" );
+ ListenForGameEvent( "player_team" );
+ ListenForGameEvent( "player_spawn" );
+ ListenForGameEvent( "player_death" );
+ ListenForGameEvent( "player_disconnect" );
+}
+
+void CMapOverview::InitTeamColorsAndIcons()
+{
+ Q_memset( m_TeamIcons, 0, sizeof(m_TeamIcons) );
+ Q_memset( m_TeamColors, 0, sizeof(m_TeamColors) );
+ Q_memset( m_ObjectIcons, 0, sizeof(m_ObjectIcons) );
+
+ m_TextureIDs.RemoveAll();
+}
+
+int CMapOverview::AddIconTexture(const char *filename)
+{
+ int index = m_TextureIDs.Find( filename );
+
+ if ( m_TextureIDs.IsValidIndex( index ) )
+ {
+ // already known, return texture ID
+ return m_TextureIDs.Element(index);
+ }
+
+ index = surface()->CreateNewTextureID();
+ surface()->DrawSetTextureFile( index , filename, true, false);
+
+ m_TextureIDs.Insert( filename, index );
+
+ return index;
+}
+
+void CMapOverview::ApplySchemeSettings(vgui::IScheme *scheme)
+{
+ BaseClass::ApplySchemeSettings( scheme );
+
+ SetBgColor( Color( 0,0,0,100 ) );
+ SetPaintBackgroundEnabled( true );
+}
+
+CMapOverview::~CMapOverview()
+{
+ if ( m_MapKeyValues )
+ m_MapKeyValues->deleteThis();
+
+ g_pMapOverview = NULL;
+
+ //TODO release Textures ? clear lists
+}
+
+void CMapOverview::UpdatePlayers()
+{
+ if ( !g_PR )
+ return;
+
+ // first disable all players health
+ for ( int i=0; i<MAX_PLAYERS; i++ )
+ {
+ m_Players[i].health = 0;
+ m_Players[i].team = TEAM_SPECTATOR;
+ }
+
+ for ( int i = 1; i<= gpGlobals->maxClients; i++)
+ {
+ // update from global player resources
+ if ( g_PR && g_PR->IsConnected(i) )
+ {
+ MapPlayer_t *player = &m_Players[i-1];
+
+ player->health = g_PR->GetHealth( i );
+
+ if ( !g_PR->IsAlive( i ) )
+ {
+ player->health = 0;
+ }
+
+ if ( player->team != g_PR->GetTeam( i ) )
+ {
+ player->team = g_PR->GetTeam( i );
+ player->icon = m_TeamIcons[ GetIconNumberFromTeamNumber(player->team) ];
+ player->color = m_TeamColors[ GetIconNumberFromTeamNumber(player->team) ];
+ }
+ }
+
+ C_BasePlayer *pPlayer = UTIL_PlayerByIndex( i );
+
+ if ( !pPlayer )
+ continue;
+
+ // don't update if player is dormant
+ if ( pPlayer->IsDormant() )
+ continue;
+
+ // update position of active players in our PVS
+ Vector position = pPlayer->EyePosition();
+ QAngle angles = pPlayer->EyeAngles();
+
+ SetPlayerPositions( i-1, position, angles );
+ }
+}
+
+void CMapOverview::UpdatePlayerTrails()
+{
+ if ( m_fNextTrailUpdate > m_fWorldTime )
+ return;
+
+ m_fNextTrailUpdate = m_fWorldTime + 1.0f; // update once a second
+
+ for (int i=0; i<MAX_PLAYERS; i++)
+ {
+ MapPlayer_t *p = &m_Players[i];
+
+ // no trails for spectators or dead players
+ if ( (p->team <= TEAM_SPECTATOR) || (p->health <= 0) )
+ {
+ continue;
+ }
+
+ // move old trail points
+ for ( int j=MAX_TRAIL_LENGTH-1; j>0; j--)
+ {
+ p->trail[j]=p->trail[j-1];
+ }
+
+ p->trail[0] = WorldToMap ( p->position );
+ }
+}
+
+void CMapOverview::UpdateFollowEntity()
+{
+ if ( m_nFollowEntity != 0 )
+ {
+ C_BaseEntity *ent = ClientEntityList().GetEnt( m_nFollowEntity );
+
+ if ( ent )
+ {
+ Vector position = MainViewOrigin(); // Use MainViewOrigin so SourceTV works in 3rd person
+ QAngle angle = ent->EyeAngles();
+
+ if ( m_nFollowEntity <= MAX_PLAYERS )
+ {
+ SetPlayerPositions( m_nFollowEntity-1, position, angle );
+ }
+
+ SetCenter( WorldToMap(position) );
+ SetAngle( angle[YAW] );
+ }
+ }
+ else
+ {
+ SetCenter( Vector2D(OVERVIEW_MAP_SIZE/2,OVERVIEW_MAP_SIZE/2) );
+ SetAngle( 0 );
+ }
+}
+
+void CMapOverview::Paint()
+{
+ UpdateSizeAndPosition();
+
+ UpdateFollowEntity();
+
+ UpdateObjects();
+
+ UpdatePlayers();
+
+ UpdatePlayerTrails();
+
+ DrawMapTexture();
+
+ DrawMapPlayerTrails();
+
+ DrawObjects();
+
+ DrawMapPlayers();
+
+ DrawCamera();
+
+ BaseClass::Paint();
+}
+
+bool CMapOverview::CanPlayerBeSeen(MapPlayer_t *player)
+{
+ C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer();
+
+ if ( !localPlayer || !player )
+ return false;
+
+ // don't draw ourself
+ if ( localPlayer->GetUserID() == (player->userid) )
+ return false;
+
+ // Invalid guy.
+ if( player->position == Vector(0,0,0) )
+ return false;
+
+ // if local player is on spectator team, he can see everyone
+ if ( localPlayer->GetTeamNumber() <= TEAM_SPECTATOR )
+ return true;
+
+ // we never track unassigned or real spectators
+ if ( player->team <= TEAM_SPECTATOR )
+ return false;
+
+ // if observer is an active player, check mp_forcecamera:
+
+ if ( mp_forcecamera.GetInt() == OBS_ALLOW_NONE )
+ return false;
+
+ if ( mp_forcecamera.GetInt() == OBS_ALLOW_TEAM )
+ {
+ // true if both players are on the same team
+ return (localPlayer->GetTeamNumber() == player->team );
+ }
+
+ // by default we can see all players
+ return true;
+}
+
+/// allows mods to restrict health
+/// Note: index is 0-based
+bool CMapOverview::CanPlayerHealthBeSeen(MapPlayer_t *player)
+{
+ C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer();
+
+ if ( !localPlayer )
+ return false;
+
+ // real spectators can see everything
+ if ( localPlayer->GetTeamNumber() <= TEAM_SPECTATOR )
+ return true;
+
+ if ( mp_forcecamera.GetInt() != OBS_ALLOW_ALL )
+ {
+ // if forcecamera is on, only show health for teammates
+ return ( localPlayer->GetTeamNumber() == player->team );
+ }
+
+ return true;
+}
+
+// usually name rule is same as health rule
+bool CMapOverview::CanPlayerNameBeSeen(MapPlayer_t *player)
+{
+ return CanPlayerHealthBeSeen( player );
+}
+
+void CMapOverview::SetPlayerPositions(int index, const Vector &position, const QAngle &angle)
+{
+ MapPlayer_t *p = &m_Players[index];
+
+ p->angle = angle;
+ p->position = position;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: shows/hides the buy menu
+//-----------------------------------------------------------------------------
+void CMapOverview::ShowPanel(bool bShow)
+{
+ SetVisible( bShow );
+}
+
+void CMapOverview::OnThink( void )
+{
+ if ( NeedsUpdate() )
+ {
+ Update();
+ m_fNextUpdateTime = gpGlobals->curtime + 0.2f; // update 5 times a second
+ }
+}
+
+bool CMapOverview::NeedsUpdate( void )
+{
+ return m_fNextUpdateTime < gpGlobals->curtime;
+}
+
+void CMapOverview::Update( void )
+{
+ // update settings
+ m_bShowNames = overview_names.GetBool() && ( GetMode() != MAP_MODE_RADAR );
+ m_bShowHealth = overview_health.GetBool() && ( GetMode() != MAP_MODE_RADAR );
+ m_bFollowAngle = ( GetMode() != MAP_MODE_RADAR && !overview_locked.GetBool() ) || ( GetMode() == MAP_MODE_RADAR && !IsRadarLocked() );
+ m_fTrailUpdateInterval = overview_tracks.GetInt() && ( GetMode() != MAP_MODE_RADAR );
+
+ m_fWorldTime = gpGlobals->curtime;
+
+ C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
+
+ if ( !pPlayer )
+ return;
+
+ int specmode = GetSpectatorMode();
+
+ if ( specmode == OBS_MODE_IN_EYE || specmode == OBS_MODE_CHASE )
+ {
+ // follow target
+ SetFollowEntity( GetSpectatorTarget() );
+ }
+ else
+ {
+ // follow ourself otherwise
+ SetFollowEntity( pPlayer->entindex() );
+ }
+}
+
+void CMapOverview::Reset( void )
+{
+ m_fNextUpdateTime = 0;
+}
+
+void CMapOverview::SetData(KeyValues *data)
+{
+ m_fZoom = data->GetFloat( "zoom", m_fZoom );
+ m_nFollowEntity = data->GetInt( "entity", m_nFollowEntity );
+}
+
+
+CMapOverview::MapPlayer_t* CMapOverview::GetPlayerByUserID( int userID )
+{
+ for (int i=0; i<MAX_PLAYERS; i++)
+ {
+ MapPlayer_t *player = &m_Players[i];
+
+ if ( player->userid == userID )
+ return player;
+ }
+
+ return NULL;
+}
+
+bool CMapOverview::IsInPanel(Vector2D &pos)
+{
+ int x,y,w,t;
+
+ GetBounds( x,y,w,t );
+
+ return ( pos.x >= 0 && pos.x < w && pos.y >= 0 && pos.y < t );
+}
+
+void CMapOverview::DrawMapTexture()
+{
+ // now draw a box around the outside of this panel
+ int x0, y0, x1, y1;
+ int wide, tall;
+
+ GetSize(wide, tall);
+ x0 = 0; y0 = 0; x1 = wide - 2; y1 = tall - 2 ;
+
+ if ( m_nMapTextureID < 0 )
+ return;
+
+ Vertex_t points[4] =
+ {
+ Vertex_t( MapToPanel ( Vector2D(0,0) ), Vector2D(0,0) ),
+ Vertex_t( MapToPanel ( Vector2D(OVERVIEW_MAP_SIZE-1,0) ), Vector2D(1,0) ),
+ Vertex_t( MapToPanel ( Vector2D(OVERVIEW_MAP_SIZE-1,OVERVIEW_MAP_SIZE-1) ), Vector2D(1,1) ),
+ Vertex_t( MapToPanel ( Vector2D(0,OVERVIEW_MAP_SIZE-1) ), Vector2D(0,1) )
+ };
+
+ int alpha = 255.0f * overview_alpha.GetFloat(); clamp( alpha, 1, 255 );
+
+ surface()->DrawSetColor( 255,255,255, alpha );
+ surface()->DrawSetTexture( m_nMapTextureID );
+ surface()->DrawTexturedPolygon( 4, points );
+}
+
+void CMapOverview::DrawMapPlayerTrails()
+{
+ if ( m_fTrailUpdateInterval <= 0 )
+ return; // turned off
+
+ for (int i=0; i<MAX_PLAYERS; i++)
+ {
+ MapPlayer_t *player = &m_Players[i];
+
+ if ( !CanPlayerBeSeen(player) )
+ continue;
+
+ player->trail[0] = WorldToMap ( player->position );
+
+ for ( int i=0; i<(MAX_TRAIL_LENGTH-1); i++)
+ {
+ if ( player->trail[i+1].x == 0 && player->trail[i+1].y == 0 )
+ break;
+
+ Vector2D pos1 = MapToPanel( player->trail[i] );
+ Vector2D pos2 = MapToPanel( player->trail[i+1] );
+
+ int intensity = 255 - float(255.0f * i) / MAX_TRAIL_LENGTH;
+
+ Vector2D dist = pos1 - pos2;
+
+ // don't draw too long lines, player probably teleported
+ if ( dist.LengthSqr() < (128*128) )
+ {
+ surface()->DrawSetColor( player->color[0], player->color[1], player->color[2], intensity );
+ surface()->DrawLine( pos1.x, pos1.y, pos2.x, pos2.y );
+ }
+ }
+ }
+}
+
+void CMapOverview::DrawObjects( )
+{
+ surface()->DrawSetTextFont( m_hIconFont );
+
+ for (int i=0; i<m_Objects.Count(); i++)
+ {
+ MapObject_t *obj = &m_Objects[i];
+
+ const char *text = NULL;
+
+ if ( Q_strlen(obj->name) > 0 )
+ text = obj->name;
+
+ float flAngle = obj->angle[YAW];
+
+ if ( obj->flags & MAP_OBJECT_ALIGN_TO_MAP && m_bRotateMap )
+ {
+ if ( m_bRotateMap )
+ flAngle = 90;
+ else
+ flAngle = 0;
+ }
+
+ MapObject_t tempObj = *obj;
+ tempObj.angle[YAW] = flAngle;
+ tempObj.text = text;
+ tempObj.statusColor = obj->color;
+
+ // draw icon
+ if ( !DrawIcon( &tempObj ) )
+ continue;
+ }
+}
+
+bool CMapOverview::DrawIcon( MapObject_t *obj )
+{
+ int textureID = obj->icon;
+ Vector pos = obj->position;
+ float scale = obj->size;
+ float angle = obj->angle[YAW];
+ const char *text = obj->text;
+ Color *textColor = &obj->color;
+ float status = obj->status;
+ Color *statusColor = &obj->statusColor;
+
+ Vector offset; offset.z = 0;
+
+ Vector2D pospanel = WorldToMap( pos );
+ pospanel = MapToPanel( pospanel );
+
+ if ( !IsInPanel( pospanel ) )
+ return false; // player is not within overview panel
+
+ offset.x = -scale; offset.y = scale;
+ VectorYawRotate( offset, angle, offset );
+ Vector2D pos1 = WorldToMap( pos + offset );
+
+ offset.x = scale; offset.y = scale;
+ VectorYawRotate( offset, angle, offset );
+ Vector2D pos2 = WorldToMap( pos + offset );
+
+ offset.x = scale; offset.y = -scale;
+ VectorYawRotate( offset, angle, offset );
+ Vector2D pos3 = WorldToMap( pos + offset );
+
+ offset.x = -scale; offset.y = -scale;
+ VectorYawRotate( offset, angle, offset );
+ Vector2D pos4 = WorldToMap( pos + offset );
+
+ Vertex_t points[4] =
+ {
+ Vertex_t( MapToPanel ( pos1 ), Vector2D(0,0) ),
+ Vertex_t( MapToPanel ( pos2 ), Vector2D(1,0) ),
+ Vertex_t( MapToPanel ( pos3 ), Vector2D(1,1) ),
+ Vertex_t( MapToPanel ( pos4 ), Vector2D(0,1) )
+ };
+
+ surface()->DrawSetColor( 255, 255, 255, 255 );
+ surface()->DrawSetTexture( textureID );
+ surface()->DrawTexturedPolygon( 4, points );
+
+ int d = GetPixelOffset( scale);
+
+ pospanel.y += d + 4;
+
+ if ( status >=0.0f && status <= 1.0f && statusColor )
+ {
+ // health bar is 50x3 pixels
+ surface()->DrawSetColor( 0,0,0,255 );
+ surface()->DrawFilledRect( pospanel.x-d, pospanel.y-1, pospanel.x+d, pospanel.y+1 );
+
+ int length = (float)(d*2)*status;
+ surface()->DrawSetColor( statusColor->r(), statusColor->g(), statusColor->b(), 255 );
+ surface()->DrawFilledRect( pospanel.x-d, pospanel.y-1, pospanel.x-d+length, pospanel.y+1 );
+
+ pospanel.y += 3;
+ }
+
+ if ( text && textColor )
+ {
+ wchar_t iconText[ MAX_PLAYER_NAME_LENGTH*2 ];
+
+ g_pVGuiLocalize->ConvertANSIToUnicode( text, iconText, sizeof( iconText ) );
+
+ int wide, tall;
+ surface()->GetTextSize( m_hIconFont, iconText, wide, tall );
+
+ int x = pospanel.x-(wide/2);
+ int y = pospanel.y;
+
+ // draw black shadow text
+ surface()->DrawSetTextColor( 0, 0, 0, 255 );
+ surface()->DrawSetTextPos( x+1, y );
+ surface()->DrawPrintText( iconText, wcslen(iconText) );
+
+ // draw name in color
+ surface()->DrawSetTextColor( textColor->r(), textColor->g(), textColor->b(), 255 );
+ surface()->DrawSetTextPos( x, y );
+ surface()->DrawPrintText( iconText, wcslen(iconText) );
+ }
+
+ return true;
+}
+
+int CMapOverview::GetPixelOffset( float height )
+{
+ Vector2D pos2 = WorldToMap( Vector( height,0,0) );
+ pos2 = MapToPanel( pos2 );
+
+ Vector2D pos3 = WorldToMap( Vector(0,0,0) );
+ pos3 = MapToPanel( pos3 );
+
+ int a = pos2.y-pos3.y;
+ int b = pos2.x-pos3.x;
+
+ return (int)sqrt((float)(a*a+b*b)); // number of panel pixels for "scale" units in world
+}
+
+void CMapOverview::DrawMapPlayers()
+{
+ surface()->DrawSetTextFont( m_hIconFont );
+
+ Color colorGreen( 0, 255, 0, 255 ); // health bar color
+
+ for (int i=0; i<MAX_PLAYERS; i++)
+ {
+ MapPlayer_t *player = &m_Players[i];
+
+ if ( !CanPlayerBeSeen( player ) )
+ continue;
+
+ // don't draw dead players / spectators
+ if ( player->health <= 0 )
+ continue;
+
+ float status = -1;
+ const char *name = NULL;
+
+ if ( m_bShowNames && CanPlayerNameBeSeen( player ) )
+ name = player->name;
+
+ if ( m_bShowHealth && CanPlayerHealthBeSeen( player ) )
+ status = player->health/100.0f;
+
+ // convert from PlayerObject_t
+ MapObject_t tempObj;
+ memset( &tempObj, 0, sizeof(MapObject_t) );
+ tempObj.icon = player->icon;
+ tempObj.position = player->position;
+ tempObj.size = m_flIconSize;
+ tempObj.angle = player->angle;
+ tempObj.text = name;
+ tempObj.color = player->color;
+ tempObj.status = status;
+ tempObj.statusColor = colorGreen;
+
+ DrawIcon( &tempObj );
+ }
+}
+
+Vector2D CMapOverview::WorldToMap( const Vector &worldpos )
+{
+ Vector2D offset( worldpos.x - m_MapOrigin.x, worldpos.y - m_MapOrigin.y);
+
+ offset.x /= m_fMapScale;
+ offset.y /= -m_fMapScale;
+
+ return offset;
+}
+
+float CMapOverview::GetViewAngle( void )
+{
+ float viewAngle = m_fViewAngle - 90.0f;
+
+ if ( !m_bFollowAngle )
+ {
+ // We don't use fViewAngle. We just show straight at all times.
+ if ( m_bRotateMap )
+ viewAngle = 90.0f;
+ else
+ viewAngle = 0.0f;
+ }
+
+ return viewAngle;
+}
+
+Vector2D CMapOverview::MapToPanel( const Vector2D &mappos )
+{
+ int pwidth, pheight;
+ Vector2D panelpos;
+ float viewAngle = GetViewAngle();
+
+ GetSize(pwidth, pheight);
+
+ Vector offset;
+ offset.x = mappos.x - m_MapCenter.x;
+ offset.y = mappos.y - m_MapCenter.y;
+ offset.z = 0;
+
+ VectorYawRotate( offset, viewAngle, offset );
+
+ // find the actual zoom from the animationvar m_fZoom and the map zoom scale
+ float fScale = (m_fZoom * m_fFullZoom) / OVERVIEW_MAP_SIZE;
+
+ offset.x *= fScale;
+ offset.y *= fScale;
+
+ panelpos.x = (pwidth * 0.5f) + (pheight * offset.x);
+ panelpos.y = (pheight * 0.5f) + (pheight * offset.y);
+
+ return panelpos;
+}
+
+void CMapOverview::SetTime( float time )
+{
+ m_fWorldTime = time;
+}
+
+void CMapOverview::SetMap(const char * levelname)
+{
+ // Reset players and objects, even if the map is the same as the previous one
+ m_Objects.RemoveAll();
+
+ m_fNextTrailUpdate = 0;// Set to 0 for immediate update. Our WorldTime var hasn't been updated to 0 for the new map yet
+ m_fWorldTime = 0;// In release, we occasionally race and get this bug again if we gt a paint before an update. Reset this before the old value gets in to the timer.
+ // Please note, UpdatePlayerTrails comes from PAINT, not UPDATE.
+
+ InitTeamColorsAndIcons();
+
+ // load new KeyValues
+ if ( m_MapKeyValues && Q_strcmp( levelname, m_MapKeyValues->GetName() ) == 0 )
+ {
+ return; // map didn't change
+ }
+
+ if ( m_MapKeyValues )
+ m_MapKeyValues->deleteThis();
+
+ m_MapKeyValues = new KeyValues( levelname );
+
+ char tempfile[MAX_PATH];
+ Q_snprintf( tempfile, sizeof( tempfile ), "resource/overviews/%s.txt", levelname );
+
+ if ( !m_MapKeyValues->LoadFromFile( g_pFullFileSystem, tempfile, "GAME" ) )
+ {
+ DevMsg( 1, "Error! CMapOverview::SetMap: couldn't load file %s.\n", tempfile );
+ m_nMapTextureID = -1;
+ m_MapOrigin.x = 0;
+ m_MapOrigin.y = 0;
+ m_fMapScale = 1;
+ m_bRotateMap = false;
+ m_fFullZoom = 1;
+ return;
+ }
+
+ // TODO release old texture ?
+
+ m_nMapTextureID = surface()->CreateNewTextureID();
+
+ //if we have not uploaded yet, lets go ahead and do so
+ surface()->DrawSetTextureFile( m_nMapTextureID, m_MapKeyValues->GetString("material"), true, false);
+
+ int wide, tall;
+
+ surface()->DrawGetTextureSize( m_nMapTextureID, wide, tall );
+
+ if ( wide != tall )
+ {
+ DevMsg( 1, "Error! CMapOverview::SetMap: map image must be a square.\n" );
+ m_nMapTextureID = -1;
+ return;
+ }
+
+ m_MapOrigin.x = m_MapKeyValues->GetInt("pos_x");
+ m_MapOrigin.y = m_MapKeyValues->GetInt("pos_y");
+ m_fMapScale = m_MapKeyValues->GetFloat("scale", 1.0f);
+ m_bRotateMap = m_MapKeyValues->GetInt("rotate")!=0;
+ m_fFullZoom = m_MapKeyValues->GetFloat("zoom", 1.0f );
+}
+
+void CMapOverview::ResetRound()
+{
+ for (int i=0; i<MAX_PLAYERS; i++)
+ {
+ MapPlayer_t *p = &m_Players[i];
+
+ if ( p->team > TEAM_SPECTATOR )
+ {
+ p->health = 100;
+ }
+
+ Q_memset( p->trail, 0, sizeof(p->trail) );
+
+ p->position = Vector( 0, 0, 0 );
+ }
+
+ m_Objects.RemoveAll();
+}
+
+void CMapOverview::OnMousePressed( MouseCode code )
+{
+
+}
+
+void CMapOverview::DrawCamera()
+{
+ // draw a red center point
+ surface()->DrawSetColor( 255,0,0,255 );
+ Vector2D center = MapToPanel( m_ViewOrigin );
+ surface()->DrawFilledRect( center.x-2, center.y-2, center.x+2, center.y+2);
+}
+
+void CMapOverview::FireGameEvent( IGameEvent *event )
+{
+ const char * type = event->GetName();
+
+ if ( Q_strcmp(type, "game_newmap") == 0 )
+ {
+ SetMap( event->GetString("mapname") );
+ ResetRound();
+ }
+
+ else if ( Q_strcmp(type, "round_start") == 0 )
+ {
+ ResetRound();
+ }
+
+ else if ( Q_strcmp(type,"player_connect") == 0 )
+ {
+ int index = event->GetInt("index"); // = entity index - 1
+
+ if ( index < 0 || index >= MAX_PLAYERS )
+ return;
+
+ MapPlayer_t *player = &m_Players[index];
+
+ player->index = index;
+ player->userid = event->GetInt("userid");
+ Q_strncpy( player->name, event->GetString("name","unknown"), sizeof(player->name) );
+
+ // Reset settings
+ Q_memset( player->trail, 0, sizeof(player->trail) );
+ player->team = TEAM_UNASSIGNED;
+ player->health = 0;
+ }
+
+ else if ( Q_strcmp(type,"player_info") == 0 )
+ {
+ int index = event->GetInt("index"); // = entity index - 1
+
+ if ( index < 0 || index >= MAX_PLAYERS )
+ return;
+
+ MapPlayer_t *player = &m_Players[index];
+
+ player->index = index;
+ player->userid = event->GetInt("userid");
+ Q_strncpy( player->name, event->GetString("name","unknown"), sizeof(player->name) );
+ }
+
+ else if ( Q_strcmp(type,"player_team") == 0 )
+ {
+ MapPlayer_t *player = GetPlayerByUserID( event->GetInt("userid") );
+
+ if ( !player )
+ return;
+
+ player->team = event->GetInt("team");
+ player->icon = m_TeamIcons[ GetIconNumberFromTeamNumber(player->team) ];
+ player->color = m_TeamColors[ GetIconNumberFromTeamNumber(player->team) ];
+ }
+
+ else if ( Q_strcmp(type,"player_death") == 0 )
+ {
+ MapPlayer_t *player = GetPlayerByUserID( event->GetInt("userid") );
+
+ if ( !player )
+ return;
+
+ player->health = 0;
+ Q_memset( player->trail, 0, sizeof(player->trail) ); // clear trails
+ }
+
+ else if ( Q_strcmp(type,"player_spawn") == 0 )
+ {
+ MapPlayer_t *player = GetPlayerByUserID( event->GetInt("userid") );
+
+ if ( !player )
+ return;
+
+ player->health = 100;
+ Q_memset( player->trail, 0, sizeof(player->trail) ); // clear trails
+ }
+
+ else if ( Q_strcmp(type,"player_disconnect") == 0 )
+ {
+ MapPlayer_t *player = GetPlayerByUserID( event->GetInt("userid") );
+
+ if ( !player )
+ return;
+
+ Q_memset( player, 0, sizeof(MapPlayer_t) ); // clear player field
+ }
+}
+
+void CMapOverview::SetMode(int mode)
+{
+ m_flChangeSpeed = 0; // change size instantly
+
+ if ( mode == MAP_MODE_OFF )
+ {
+ ShowPanel( false );
+
+ g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "MapOff" );
+ }
+ else if ( mode == MAP_MODE_INSET )
+ {
+ if( m_nMapTextureID == -1 )
+ {
+ SetMode( MAP_MODE_OFF );
+ return;
+ }
+
+ if ( m_nMode != MAP_MODE_OFF )
+ m_flChangeSpeed = 1000; // zoom effect
+
+ C_BasePlayer *pPlayer = CBasePlayer::GetLocalPlayer();
+
+ if ( pPlayer )
+ SetFollowEntity( pPlayer->entindex() );
+
+ ShowPanel( true );
+
+ if ( mode != m_nMode && RunHudAnimations() )
+ {
+ g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "MapZoomToSmall" );
+ }
+ }
+ else if ( mode == MAP_MODE_FULL )
+ {
+ if( m_nMapTextureID == -1 )
+ {
+ SetMode( MAP_MODE_OFF );
+ return;
+ }
+
+ if ( m_nMode != MAP_MODE_OFF )
+ m_flChangeSpeed = 1000; // zoom effect
+
+ SetFollowEntity( 0 );
+
+ ShowPanel( true );
+
+ if ( mode != m_nMode && RunHudAnimations() )
+ {
+ g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "MapZoomToLarge" );
+ }
+ }
+
+ // finally set mode
+ m_nMode = mode;
+
+ UpdateSizeAndPosition();
+}
+
+bool CMapOverview::ShouldDraw( void )
+{
+ return ( m_nMode != MAP_MODE_OFF ) && CHudElement::ShouldDraw();
+}
+
+void CMapOverview::UpdateSizeAndPosition()
+{
+ if ( g_pSpectatorGUI && g_pSpectatorGUI->IsVisible() )
+ {
+ int iScreenWide, iScreenTall;
+ GetHudSize( iScreenWide, iScreenTall );
+
+ int iTopBarHeight = g_pSpectatorGUI->GetTopBarHeight();
+ int iBottomBarHeight = g_pSpectatorGUI->GetBottomBarHeight();
+
+ iScreenTall -= ( iTopBarHeight + iBottomBarHeight );
+
+ int x,y,w,h;
+ GetBounds( x,y,w,h );
+
+ if ( y < iTopBarHeight )
+ y = iTopBarHeight;
+
+ SetBounds( x,y,w,MIN(h,iScreenTall) );
+ }
+}
+
+void CMapOverview::SetCenter(const Vector2D &mappos)
+{
+ int width, height;
+
+ GetSize( width, height);
+
+ m_ViewOrigin = mappos;
+ m_MapCenter = mappos;
+
+ float fTwiceZoom = m_fZoom * m_fFullZoom * 2;
+
+ width = height = OVERVIEW_MAP_SIZE / (fTwiceZoom);
+
+ if( GetMode() != MAP_MODE_RADAR )
+ {
+ if ( m_MapCenter.x < width )
+ m_MapCenter.x = width;
+
+ if ( m_MapCenter.x > (OVERVIEW_MAP_SIZE-width) )
+ m_MapCenter.x = (OVERVIEW_MAP_SIZE-width);
+
+ if ( m_MapCenter.y < height )
+ m_MapCenter.y = height;
+
+ if ( m_MapCenter.y > (OVERVIEW_MAP_SIZE-height) )
+ m_MapCenter.y = (OVERVIEW_MAP_SIZE-height);
+
+ //center if in full map mode
+ if ( m_fZoom <= 1.0 )
+ {
+ m_MapCenter.x = OVERVIEW_MAP_SIZE/2;
+ m_MapCenter.y = OVERVIEW_MAP_SIZE/2;
+ }
+ }
+
+}
+
+void CMapOverview::SetFollowAngle(bool state)
+{
+ m_bFollowAngle = state;
+}
+
+void CMapOverview::SetFollowEntity(int entindex)
+{
+ m_nFollowEntity = entindex;
+}
+
+float CMapOverview::GetZoom( void )
+{
+ return m_fZoom;
+}
+
+int CMapOverview::GetMode( void )
+{
+ return m_nMode;
+}
+
+void CMapOverview::SetAngle(float angle)
+{
+ m_fViewAngle = angle;
+}
+
+void CMapOverview::ShowPlayerNames(bool state)
+{
+ m_bShowNames = state;
+}
+
+
+void CMapOverview::ShowPlayerHealth(bool state)
+{
+ m_bShowHealth = state;
+}
+
+void CMapOverview::ShowPlayerTracks(float seconds)
+{
+ m_fTrailUpdateInterval = seconds;
+}
+
+bool CMapOverview::SetTeamColor(int team, Color color)
+{
+ if ( team < 0 || team>= MAX_TEAMS )
+ return false;
+
+ m_TeamColors[team] = color;
+
+ return true;
+}
+
+CMapOverview::MapObject_t* CMapOverview::FindObjectByID(int objectID)
+{
+ for ( int i = 0; i < m_Objects.Count(); i++ )
+ {
+ if ( m_Objects[i].objectID == objectID )
+ return &m_Objects[i];
+ }
+
+ return NULL;
+}
+
+int CMapOverview::AddObject( const char *icon, int entity, float timeToLive )
+{
+ MapObject_t obj; Q_memset( &obj, 0, sizeof(obj) );
+
+ obj.objectID = m_ObjectCounterID++;
+ obj.index = entity;
+ obj.icon = AddIconTexture( icon );
+ obj.size = m_flIconSize;
+ obj.status = -1;
+
+ if ( timeToLive > 0 )
+ obj.endtime = gpGlobals->curtime + timeToLive;
+ else
+ obj.endtime = -1;
+
+ m_Objects.AddToTail( obj );
+
+ return obj.objectID;
+}
+
+void CMapOverview::SetObjectText( int objectID, const char *text, Color color )
+{
+ MapObject_t* obj = FindObjectByID( objectID );
+
+ if ( !obj )
+ return;
+
+ if ( text )
+ {
+ Q_strncpy( obj->name, text, sizeof(obj->name) );
+ }
+ else
+ {
+ Q_memset( obj->name, 0, sizeof(obj->name) );
+ }
+
+ obj->color = color;
+}
+
+void CMapOverview::SetObjectStatus( int objectID, float status, Color color )
+{
+ MapObject_t* obj = FindObjectByID( objectID );
+
+ if ( !obj )
+ return;
+
+ obj->status = status;
+ obj->statusColor = color;
+}
+
+void CMapOverview::SetObjectIcon( int objectID, const char *icon, float size )
+{
+ MapObject_t* obj = FindObjectByID( objectID );
+
+ if ( !obj )
+ return;
+
+ obj->icon = AddIconTexture( icon );
+ obj->size = size;
+}
+
+void CMapOverview::SetObjectPosition( int objectID, const Vector &position, const QAngle &angle )
+{
+ MapObject_t* obj = FindObjectByID( objectID );
+
+ if ( !obj )
+ return;
+
+ obj->angle = angle;
+ obj->position = position;
+}
+
+void CMapOverview::AddObjectFlags( int objectID, int flags )
+{
+ MapObject_t* obj = FindObjectByID( objectID );
+
+ if ( !obj )
+ return;
+
+ obj->flags |= flags;
+}
+
+void CMapOverview::SetObjectFlags( int objectID, int flags )
+{
+ MapObject_t* obj = FindObjectByID( objectID );
+
+ if ( !obj )
+ return;
+
+ obj->flags = flags;
+}
+
+void CMapOverview::RemoveObjectByIndex( int index )
+{
+ for ( int i = 0; i < m_Objects.Count(); i++ )
+ {
+ if ( m_Objects[i].index == index )
+ {
+ m_Objects.Remove( i );
+ return;
+ }
+ }
+}
+
+void CMapOverview::RemoveObject( int objectID )
+{
+ for ( int i = 0; i < m_Objects.Count(); i++ )
+ {
+ if ( m_Objects[i].objectID == objectID )
+ {
+ m_Objects.Remove( i );
+ return;
+ }
+ }
+}
+
+void CMapOverview::UpdateObjects()
+{
+ for ( int i = 0; i < m_Objects.Count(); i++ )
+ {
+ MapObject_t *obj = &m_Objects[i];
+
+ if ( obj->endtime > 0 && obj->endtime < gpGlobals->curtime )
+ {
+ m_Objects.Remove( i );
+ i--;
+ continue;
+ }
+
+ if ( obj->index <= 0 )
+ continue;
+
+ C_BaseEntity *entity = ClientEntityList().GetEnt( obj->index );
+
+ if ( !entity )
+ continue;
+
+ obj->position = entity->GetAbsOrigin();
+ obj->angle = entity->GetAbsAngles();
+ }
+}
\ No newline at end of file |