//========= Copyright 1996-2005, 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.h> #include <cl_dll/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, "Show player's health in map overview.\n" ); ConVar overview_names ( "overview_names", "1", FCVAR_ARCHIVE, "Show player's names in map overview.\n" ); ConVar overview_tracks( "overview_tracks", "1", FCVAR_ARCHIVE, "Show player's tracks in map overview.\n" ); ConVar overview_locked( "overview_locked", "1", FCVAR_ARCHIVE, "Locks map angle, doesn't follow view angle.\n" ); ConVar overview_alpha( "overview_alpha", "1.0", FCVAR_ARCHIVE, "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 || engine->Cmd_Argc() < 2 ) return; float zoom = Q_atof( engine->Cmd_Argv( 1 ) ); float time = 0; if ( engine->Cmd_Argc() >= 3 ) time = Q_atof( engine->Cmd_Argv( 2 ) ); if ( engine->Cmd_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 ( engine->Cmd_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(engine->Cmd_Argv( 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 gameeventmanager->AddListener( this, "game_newmap", false ); gameeventmanager->AddListener( this, "round_start", false ); gameeventmanager->AddListener( this, "player_connect", false ); gameeventmanager->AddListener( this, "player_info", false ); gameeventmanager->AddListener( this, "player_team", false ); gameeventmanager->AddListener( this, "player_spawn", false ); gameeventmanager->AddListener( this, "player_death", false ); gameeventmanager->AddListener( this, "player_disconnect", false ); } 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; gameeventmanager->RemoveListener(this); //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 ]; localize()->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( vgui::filesystem(), 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(); } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 5821 | Knut Wikstrom |
Added Valve Source code. This is NOT to be commited to other than new code from Valve. |