Team Fortress 2 Source Code as on 22/4/2020
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.

1339 lines
30 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: MapOverview.cpp: implementation of the CMapOverview class.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "mapoverview.h"
  9. #include <vgui/ISurface.h>
  10. #include <vgui/ILocalize.h>
  11. #include <filesystem.h>
  12. #include <KeyValues.h>
  13. #include <convar.h>
  14. #include "mathlib/mathlib.h"
  15. #include <game/client/iviewport.h>
  16. #include <igameresources.h>
  17. #include "gamevars_shared.h"
  18. #include "spectatorgui.h"
  19. #include "c_playerresource.h"
  20. #include "view.h"
  21. #include "clientmode.h"
  22. #include <vgui_controls/AnimationController.h>
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include "tier0/memdbgon.h"
  25. ConVar overview_health( "overview_health", "1", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Show player's health in map overview.\n" );
  26. ConVar overview_names ( "overview_names", "1", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Show player's names in map overview.\n" );
  27. ConVar overview_tracks( "overview_tracks", "1", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Show player's tracks in map overview.\n" );
  28. ConVar overview_locked( "overview_locked", "1", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Locks map angle, doesn't follow view angle.\n" );
  29. ConVar overview_alpha( "overview_alpha", "1.0", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Overview map translucency.\n" );
  30. IMapOverviewPanel *g_pMapOverview = NULL; // we assume only one overview is created
  31. static int AdjustValue( int curValue, int targetValue, int amount )
  32. {
  33. if ( curValue > targetValue )
  34. {
  35. curValue -= amount;
  36. if ( curValue < targetValue )
  37. curValue = targetValue;
  38. }
  39. else if ( curValue < targetValue )
  40. {
  41. curValue += amount;
  42. if ( curValue > targetValue )
  43. curValue = targetValue;
  44. }
  45. return curValue;
  46. }
  47. CON_COMMAND( overview_zoom, "Sets overview map zoom: <zoom> [<time>] [rel]" )
  48. {
  49. if ( !g_pMapOverview || args.ArgC() < 2 )
  50. return;
  51. float zoom = Q_atof( args[ 1 ] );
  52. float time = 0;
  53. if ( args.ArgC() >= 3 )
  54. time = Q_atof( args[ 2 ] );
  55. if ( args.ArgC() == 4 )
  56. zoom *= g_pMapOverview->GetZoom();
  57. // We are going to store their zoom pick as the resultant overview size that it sees. This way, the value will remain
  58. // correct even on a different map that has a different intrinsic zoom.
  59. float desiredViewSize = 0.0f;
  60. desiredViewSize = (zoom * OVERVIEW_MAP_SIZE * g_pMapOverview->GetFullZoom()) / g_pMapOverview->GetMapScale();
  61. g_pMapOverview->SetPlayerPreferredViewSize( desiredViewSize );
  62. if( !g_pMapOverview->AllowConCommandsWhileAlive() )
  63. {
  64. C_BasePlayer *localPlayer = CBasePlayer::GetLocalPlayer();
  65. if( localPlayer && CBasePlayer::GetLocalPlayer()->IsAlive() )
  66. return;// Not allowed to execute commands while alive
  67. else if( localPlayer && localPlayer->GetObserverMode() == OBS_MODE_DEATHCAM )
  68. return;// In the death cam spiral counts as alive
  69. }
  70. g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( g_pMapOverview->GetAsPanel(), "zoom", zoom, 0.0, time, vgui::AnimationController::INTERPOLATOR_LINEAR );
  71. }
  72. CON_COMMAND( overview_mode, "Sets overview map mode off,small,large: <0|1|2>" )
  73. {
  74. if ( !g_pMapOverview )
  75. return;
  76. int mode;
  77. if ( args.ArgC() < 2 )
  78. {
  79. // toggle modes
  80. mode = g_pMapOverview->GetMode() + 1;
  81. if ( mode > CMapOverview::MAP_MODE_FULL )
  82. mode = CMapOverview::MAP_MODE_OFF;
  83. }
  84. else
  85. {
  86. // set specific mode
  87. mode = Q_atoi( args[ 1 ] );
  88. }
  89. if( mode != CMapOverview::MAP_MODE_RADAR )
  90. g_pMapOverview->SetPlayerPreferredMode( mode );
  91. if( !g_pMapOverview->AllowConCommandsWhileAlive() )
  92. {
  93. C_BasePlayer *localPlayer = CBasePlayer::GetLocalPlayer();
  94. if( localPlayer && CBasePlayer::GetLocalPlayer()->IsAlive() )
  95. return;// Not allowed to execute commands while alive
  96. else if( localPlayer && localPlayer->GetObserverMode() == OBS_MODE_DEATHCAM )
  97. return;// In the death cam spiral counts as alive
  98. }
  99. g_pMapOverview->SetMode( mode );
  100. }
  101. //////////////////////////////////////////////////////////////////////
  102. // Construction/Destruction
  103. //////////////////////////////////////////////////////////////////////
  104. using namespace vgui;
  105. CMapOverview::CMapOverview( const char *pElementName ) : BaseClass( NULL, pElementName ), CHudElement( pElementName )
  106. {
  107. SetParent( g_pClientMode->GetViewport()->GetVPanel() );
  108. SetBounds( 0,0, 256, 256 );
  109. SetBgColor( Color( 0,0,0,100 ) );
  110. SetPaintBackgroundEnabled( true );
  111. ShowPanel( false );
  112. // Make sure we actually have the font...
  113. vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
  114. m_hIconFont = pScheme->GetFont( "DefaultSmall" );
  115. m_nMapTextureID = -1;
  116. m_MapKeyValues = NULL;
  117. m_MapOrigin = Vector( 0, 0, 0 );
  118. m_fMapScale = 1.0f;
  119. m_bFollowAngle = false;
  120. SetMode( MAP_MODE_OFF );
  121. m_fZoom = 3.0f;
  122. m_MapCenter = Vector2D( 512, 512 );
  123. m_ViewOrigin = Vector2D( 512, 512 );
  124. m_fViewAngle = 0;
  125. m_fTrailUpdateInterval = 1.0f;
  126. m_bShowNames = true;
  127. m_bShowHealth = true;
  128. m_bShowTrails = true;
  129. m_flChangeSpeed = 1000;
  130. m_flIconSize = 64.0f;
  131. m_ObjectCounterID = 1;
  132. Reset();
  133. Q_memset( m_Players, 0, sizeof(m_Players) );
  134. InitTeamColorsAndIcons();
  135. g_pMapOverview = this; // for cvars access etc
  136. }
  137. void CMapOverview::Init( void )
  138. {
  139. // register for events as client listener
  140. ListenForGameEvent( "game_newmap" );
  141. ListenForGameEvent( "round_start" );
  142. ListenForGameEvent( "player_connect_client" );
  143. ListenForGameEvent( "player_info" );
  144. ListenForGameEvent( "player_team" );
  145. ListenForGameEvent( "player_spawn" );
  146. ListenForGameEvent( "player_death" );
  147. ListenForGameEvent( "player_disconnect" );
  148. }
  149. void CMapOverview::InitTeamColorsAndIcons()
  150. {
  151. Q_memset( m_TeamIcons, 0, sizeof(m_TeamIcons) );
  152. Q_memset( m_TeamColors, 0, sizeof(m_TeamColors) );
  153. Q_memset( m_ObjectIcons, 0, sizeof(m_ObjectIcons) );
  154. m_TextureIDs.RemoveAll();
  155. }
  156. int CMapOverview::AddIconTexture(const char *filename)
  157. {
  158. int index = m_TextureIDs.Find( filename );
  159. if ( m_TextureIDs.IsValidIndex( index ) )
  160. {
  161. // already known, return texture ID
  162. return m_TextureIDs.Element(index);
  163. }
  164. index = surface()->CreateNewTextureID();
  165. surface()->DrawSetTextureFile( index , filename, true, false);
  166. m_TextureIDs.Insert( filename, index );
  167. return index;
  168. }
  169. void CMapOverview::ApplySchemeSettings(vgui::IScheme *scheme)
  170. {
  171. BaseClass::ApplySchemeSettings( scheme );
  172. SetBgColor( Color( 0,0,0,100 ) );
  173. SetPaintBackgroundEnabled( true );
  174. }
  175. CMapOverview::~CMapOverview()
  176. {
  177. if ( m_MapKeyValues )
  178. m_MapKeyValues->deleteThis();
  179. g_pMapOverview = NULL;
  180. //TODO release Textures ? clear lists
  181. }
  182. void CMapOverview::UpdatePlayers()
  183. {
  184. if ( !g_PR )
  185. return;
  186. // first disable all players health
  187. for ( int i=0; i<MAX_PLAYERS; i++ )
  188. {
  189. m_Players[i].health = 0;
  190. m_Players[i].team = TEAM_SPECTATOR;
  191. }
  192. for ( int i = 1; i<= gpGlobals->maxClients; i++)
  193. {
  194. // update from global player resources
  195. if ( g_PR && g_PR->IsConnected(i) )
  196. {
  197. MapPlayer_t *player = &m_Players[i-1];
  198. player->health = g_PR->GetHealth( i );
  199. if ( !g_PR->IsAlive( i ) )
  200. {
  201. player->health = 0;
  202. }
  203. if ( player->team != g_PR->GetTeam( i ) )
  204. {
  205. player->team = g_PR->GetTeam( i );
  206. player->icon = m_TeamIcons[ GetIconNumberFromTeamNumber(player->team) ];
  207. player->color = m_TeamColors[ GetIconNumberFromTeamNumber(player->team) ];
  208. }
  209. }
  210. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  211. if ( !pPlayer )
  212. continue;
  213. // don't update if player is dormant
  214. if ( pPlayer->IsDormant() )
  215. continue;
  216. // update position of active players in our PVS
  217. Vector position = pPlayer->EyePosition();
  218. QAngle angles = pPlayer->EyeAngles();
  219. SetPlayerPositions( i-1, position, angles );
  220. }
  221. }
  222. void CMapOverview::UpdatePlayerTrails()
  223. {
  224. if ( m_fNextTrailUpdate > m_fWorldTime )
  225. return;
  226. m_fNextTrailUpdate = m_fWorldTime + 1.0f; // update once a second
  227. for (int i=0; i<MAX_PLAYERS; i++)
  228. {
  229. MapPlayer_t *p = &m_Players[i];
  230. // no trails for spectators or dead players
  231. if ( (p->team <= TEAM_SPECTATOR) || (p->health <= 0) )
  232. {
  233. continue;
  234. }
  235. // move old trail points
  236. for ( int j=MAX_TRAIL_LENGTH-1; j>0; j--)
  237. {
  238. p->trail[j]=p->trail[j-1];
  239. }
  240. p->trail[0] = WorldToMap ( p->position );
  241. }
  242. }
  243. void CMapOverview::UpdateFollowEntity()
  244. {
  245. if ( m_nFollowEntity != 0 )
  246. {
  247. C_BaseEntity *ent = ClientEntityList().GetEnt( m_nFollowEntity );
  248. if ( ent )
  249. {
  250. Vector position = MainViewOrigin(); // Use MainViewOrigin so SourceTV works in 3rd person
  251. QAngle angle = ent->EyeAngles();
  252. if ( m_nFollowEntity <= MAX_PLAYERS )
  253. {
  254. SetPlayerPositions( m_nFollowEntity-1, position, angle );
  255. }
  256. SetCenter( WorldToMap(position) );
  257. SetAngle( angle[YAW] );
  258. }
  259. }
  260. else
  261. {
  262. SetCenter( Vector2D(OVERVIEW_MAP_SIZE/2,OVERVIEW_MAP_SIZE/2) );
  263. SetAngle( 0 );
  264. }
  265. }
  266. void CMapOverview::Paint()
  267. {
  268. UpdateSizeAndPosition();
  269. UpdateFollowEntity();
  270. UpdateObjects();
  271. UpdatePlayers();
  272. UpdatePlayerTrails();
  273. DrawMapTexture();
  274. DrawMapPlayerTrails();
  275. DrawObjects();
  276. DrawMapPlayers();
  277. DrawCamera();
  278. BaseClass::Paint();
  279. }
  280. bool CMapOverview::CanPlayerBeSeen(MapPlayer_t *player)
  281. {
  282. C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer();
  283. if ( !localPlayer || !player )
  284. return false;
  285. // don't draw ourself
  286. if ( localPlayer->GetUserID() == (player->userid) )
  287. return false;
  288. // Invalid guy.
  289. if( player->position == Vector(0,0,0) )
  290. return false;
  291. // if local player is on spectator team, he can see everyone
  292. if ( localPlayer->GetTeamNumber() <= TEAM_SPECTATOR )
  293. return true;
  294. // we never track unassigned or real spectators
  295. if ( player->team <= TEAM_SPECTATOR )
  296. return false;
  297. // if observer is an active player, check mp_forcecamera:
  298. if ( mp_forcecamera.GetInt() == OBS_ALLOW_NONE )
  299. return false;
  300. if ( mp_forcecamera.GetInt() == OBS_ALLOW_TEAM )
  301. {
  302. // true if both players are on the same team
  303. return (localPlayer->GetTeamNumber() == player->team );
  304. }
  305. // by default we can see all players
  306. return true;
  307. }
  308. /// allows mods to restrict health
  309. /// Note: index is 0-based
  310. bool CMapOverview::CanPlayerHealthBeSeen(MapPlayer_t *player)
  311. {
  312. C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer();
  313. if ( !localPlayer )
  314. return false;
  315. // real spectators can see everything
  316. if ( localPlayer->GetTeamNumber() <= TEAM_SPECTATOR )
  317. return true;
  318. if ( mp_forcecamera.GetInt() != OBS_ALLOW_ALL )
  319. {
  320. // if forcecamera is on, only show health for teammates
  321. return ( localPlayer->GetTeamNumber() == player->team );
  322. }
  323. return true;
  324. }
  325. // usually name rule is same as health rule
  326. bool CMapOverview::CanPlayerNameBeSeen(MapPlayer_t *player)
  327. {
  328. return CanPlayerHealthBeSeen( player );
  329. }
  330. void CMapOverview::SetPlayerPositions(int index, const Vector &position, const QAngle &angle)
  331. {
  332. MapPlayer_t *p = &m_Players[index];
  333. p->angle = angle;
  334. p->position = position;
  335. }
  336. //-----------------------------------------------------------------------------
  337. // Purpose: shows/hides the buy menu
  338. //-----------------------------------------------------------------------------
  339. void CMapOverview::ShowPanel(bool bShow)
  340. {
  341. SetVisible( bShow );
  342. }
  343. void CMapOverview::OnThink( void )
  344. {
  345. if ( NeedsUpdate() )
  346. {
  347. Update();
  348. m_fNextUpdateTime = gpGlobals->curtime + 0.2f; // update 5 times a second
  349. }
  350. }
  351. bool CMapOverview::NeedsUpdate( void )
  352. {
  353. return m_fNextUpdateTime < gpGlobals->curtime;
  354. }
  355. void CMapOverview::Update( void )
  356. {
  357. // update settings
  358. m_bShowNames = overview_names.GetBool() && ( GetMode() != MAP_MODE_RADAR );
  359. m_bShowHealth = overview_health.GetBool() && ( GetMode() != MAP_MODE_RADAR );
  360. m_bFollowAngle = ( GetMode() != MAP_MODE_RADAR && !overview_locked.GetBool() ) || ( GetMode() == MAP_MODE_RADAR && !IsRadarLocked() );
  361. m_fTrailUpdateInterval = overview_tracks.GetInt() && ( GetMode() != MAP_MODE_RADAR );
  362. m_fWorldTime = gpGlobals->curtime;
  363. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  364. if ( !pPlayer )
  365. return;
  366. int specmode = GetSpectatorMode();
  367. if ( specmode == OBS_MODE_IN_EYE || specmode == OBS_MODE_CHASE )
  368. {
  369. // follow target
  370. SetFollowEntity( GetSpectatorTarget() );
  371. }
  372. else
  373. {
  374. // follow ourself otherwise
  375. SetFollowEntity( pPlayer->entindex() );
  376. }
  377. }
  378. void CMapOverview::Reset( void )
  379. {
  380. m_fNextUpdateTime = 0;
  381. }
  382. void CMapOverview::SetData(KeyValues *data)
  383. {
  384. m_fZoom = data->GetFloat( "zoom", m_fZoom );
  385. m_nFollowEntity = data->GetInt( "entity", m_nFollowEntity );
  386. }
  387. CMapOverview::MapPlayer_t* CMapOverview::GetPlayerByUserID( int userID )
  388. {
  389. for (int i=0; i<MAX_PLAYERS; i++)
  390. {
  391. MapPlayer_t *player = &m_Players[i];
  392. if ( player->userid == userID )
  393. return player;
  394. }
  395. return NULL;
  396. }
  397. bool CMapOverview::IsInPanel(Vector2D &pos)
  398. {
  399. int x,y,w,t;
  400. GetBounds( x,y,w,t );
  401. return ( pos.x >= 0 && pos.x < w && pos.y >= 0 && pos.y < t );
  402. }
  403. void CMapOverview::DrawMapTexture()
  404. {
  405. // now draw a box around the outside of this panel
  406. int x0, y0, x1, y1;
  407. int wide, tall;
  408. GetSize(wide, tall);
  409. x0 = 0; y0 = 0; x1 = wide - 2; y1 = tall - 2 ;
  410. if ( m_nMapTextureID < 0 )
  411. return;
  412. Vertex_t points[4] =
  413. {
  414. Vertex_t( MapToPanel ( Vector2D(0,0) ), Vector2D(0,0) ),
  415. Vertex_t( MapToPanel ( Vector2D(OVERVIEW_MAP_SIZE-1,0) ), Vector2D(1,0) ),
  416. Vertex_t( MapToPanel ( Vector2D(OVERVIEW_MAP_SIZE-1,OVERVIEW_MAP_SIZE-1) ), Vector2D(1,1) ),
  417. Vertex_t( MapToPanel ( Vector2D(0,OVERVIEW_MAP_SIZE-1) ), Vector2D(0,1) )
  418. };
  419. int alpha = 255.0f * overview_alpha.GetFloat(); clamp( alpha, 1, 255 );
  420. surface()->DrawSetColor( 255,255,255, alpha );
  421. surface()->DrawSetTexture( m_nMapTextureID );
  422. surface()->DrawTexturedPolygon( 4, points );
  423. }
  424. void CMapOverview::DrawMapPlayerTrails()
  425. {
  426. if ( m_fTrailUpdateInterval <= 0 )
  427. return; // turned off
  428. for (int i=0; i<MAX_PLAYERS; i++)
  429. {
  430. MapPlayer_t *player = &m_Players[i];
  431. if ( !CanPlayerBeSeen(player) )
  432. continue;
  433. player->trail[0] = WorldToMap ( player->position );
  434. for ( int iTrail=0; iTrail<(MAX_TRAIL_LENGTH-1); iTrail++)
  435. {
  436. if ( player->trail[iTrail +1].x == 0 && player->trail[iTrail +1].y == 0 )
  437. break;
  438. Vector2D pos1 = MapToPanel( player->trail[iTrail] );
  439. Vector2D pos2 = MapToPanel( player->trail[iTrail +1] );
  440. int intensity = 255 - float(255.0f * iTrail ) / MAX_TRAIL_LENGTH;
  441. Vector2D dist = pos1 - pos2;
  442. // don't draw too long lines, player probably teleported
  443. if ( dist.LengthSqr() < (128*128) )
  444. {
  445. surface()->DrawSetColor( player->color[0], player->color[1], player->color[2], intensity );
  446. surface()->DrawLine( pos1.x, pos1.y, pos2.x, pos2.y );
  447. }
  448. }
  449. }
  450. }
  451. void CMapOverview::DrawObjects( )
  452. {
  453. surface()->DrawSetTextFont( m_hIconFont );
  454. for (int i=0; i<m_Objects.Count(); i++)
  455. {
  456. MapObject_t *obj = &m_Objects[i];
  457. const char *text = NULL;
  458. if ( Q_strlen(obj->name) > 0 )
  459. text = obj->name;
  460. float flAngle = obj->angle[YAW];
  461. if ( obj->flags & MAP_OBJECT_ALIGN_TO_MAP && m_bRotateMap )
  462. {
  463. if ( m_bRotateMap )
  464. flAngle = 90;
  465. else
  466. flAngle = 0;
  467. }
  468. MapObject_t tempObj = *obj;
  469. tempObj.angle[YAW] = flAngle;
  470. tempObj.text = text;
  471. tempObj.statusColor = obj->color;
  472. // draw icon
  473. if ( !DrawIcon( &tempObj ) )
  474. continue;
  475. }
  476. }
  477. bool CMapOverview::DrawIcon( MapObject_t *obj )
  478. {
  479. int textureID = obj->icon;
  480. Vector pos = obj->position;
  481. float scale = obj->size;
  482. float angle = obj->angle[YAW];
  483. const char *text = obj->text;
  484. Color *textColor = &obj->color;
  485. float status = obj->status;
  486. Color *statusColor = &obj->statusColor;
  487. Vector offset; offset.z = 0;
  488. Vector2D pospanel = WorldToMap( pos );
  489. pospanel = MapToPanel( pospanel );
  490. if ( !IsInPanel( pospanel ) )
  491. return false; // player is not within overview panel
  492. offset.x = -scale; offset.y = scale;
  493. VectorYawRotate( offset, angle, offset );
  494. Vector2D pos1 = WorldToMap( pos + offset );
  495. offset.x = scale; offset.y = scale;
  496. VectorYawRotate( offset, angle, offset );
  497. Vector2D pos2 = WorldToMap( pos + offset );
  498. offset.x = scale; offset.y = -scale;
  499. VectorYawRotate( offset, angle, offset );
  500. Vector2D pos3 = WorldToMap( pos + offset );
  501. offset.x = -scale; offset.y = -scale;
  502. VectorYawRotate( offset, angle, offset );
  503. Vector2D pos4 = WorldToMap( pos + offset );
  504. Vertex_t points[4] =
  505. {
  506. Vertex_t( MapToPanel ( pos1 ), Vector2D(0,0) ),
  507. Vertex_t( MapToPanel ( pos2 ), Vector2D(1,0) ),
  508. Vertex_t( MapToPanel ( pos3 ), Vector2D(1,1) ),
  509. Vertex_t( MapToPanel ( pos4 ), Vector2D(0,1) )
  510. };
  511. surface()->DrawSetColor( 255, 255, 255, 255 );
  512. surface()->DrawSetTexture( textureID );
  513. surface()->DrawTexturedPolygon( 4, points );
  514. int d = GetPixelOffset( scale);
  515. pospanel.y += d + 4;
  516. if ( status >=0.0f && status <= 1.0f && statusColor )
  517. {
  518. // health bar is 50x3 pixels
  519. surface()->DrawSetColor( 0,0,0,255 );
  520. surface()->DrawFilledRect( pospanel.x-d, pospanel.y-1, pospanel.x+d, pospanel.y+1 );
  521. int length = (float)(d*2)*status;
  522. surface()->DrawSetColor( statusColor->r(), statusColor->g(), statusColor->b(), 255 );
  523. surface()->DrawFilledRect( pospanel.x-d, pospanel.y-1, pospanel.x-d+length, pospanel.y+1 );
  524. pospanel.y += 3;
  525. }
  526. if ( text && textColor )
  527. {
  528. wchar_t iconText[ MAX_PLAYER_NAME_LENGTH*2 ];
  529. g_pVGuiLocalize->ConvertANSIToUnicode( text, iconText, sizeof( iconText ) );
  530. int wide, tall;
  531. surface()->GetTextSize( m_hIconFont, iconText, wide, tall );
  532. int x = pospanel.x-(wide/2);
  533. int y = pospanel.y;
  534. // draw black shadow text
  535. surface()->DrawSetTextColor( 0, 0, 0, 255 );
  536. surface()->DrawSetTextPos( x+1, y );
  537. surface()->DrawPrintText( iconText, wcslen(iconText) );
  538. // draw name in color
  539. surface()->DrawSetTextColor( textColor->r(), textColor->g(), textColor->b(), 255 );
  540. surface()->DrawSetTextPos( x, y );
  541. surface()->DrawPrintText( iconText, wcslen(iconText) );
  542. }
  543. return true;
  544. }
  545. int CMapOverview::GetPixelOffset( float height )
  546. {
  547. Vector2D pos2 = WorldToMap( Vector( height,0,0) );
  548. pos2 = MapToPanel( pos2 );
  549. Vector2D pos3 = WorldToMap( Vector(0,0,0) );
  550. pos3 = MapToPanel( pos3 );
  551. int a = pos2.y-pos3.y;
  552. int b = pos2.x-pos3.x;
  553. return (int)sqrt((float)(a*a+b*b)); // number of panel pixels for "scale" units in world
  554. }
  555. void CMapOverview::DrawMapPlayers()
  556. {
  557. surface()->DrawSetTextFont( m_hIconFont );
  558. Color colorGreen( 0, 255, 0, 255 ); // health bar color
  559. for (int i=0; i<MAX_PLAYERS; i++)
  560. {
  561. MapPlayer_t *player = &m_Players[i];
  562. if ( !CanPlayerBeSeen( player ) )
  563. continue;
  564. // don't draw dead players / spectators
  565. if ( player->health <= 0 )
  566. continue;
  567. float status = -1;
  568. const char *name = NULL;
  569. if ( m_bShowNames && CanPlayerNameBeSeen( player ) )
  570. name = player->name;
  571. if ( m_bShowHealth && CanPlayerHealthBeSeen( player ) )
  572. status = player->health/100.0f;
  573. // convert from PlayerObject_t
  574. MapObject_t tempObj;
  575. memset( &tempObj, 0, sizeof(MapObject_t) );
  576. tempObj.icon = player->icon;
  577. tempObj.position = player->position;
  578. tempObj.size = m_flIconSize;
  579. tempObj.angle = player->angle;
  580. tempObj.text = name;
  581. tempObj.color = player->color;
  582. tempObj.status = status;
  583. tempObj.statusColor = colorGreen;
  584. DrawIcon( &tempObj );
  585. }
  586. }
  587. Vector2D CMapOverview::WorldToMap( const Vector &worldpos )
  588. {
  589. Vector2D offset( worldpos.x - m_MapOrigin.x, worldpos.y - m_MapOrigin.y);
  590. offset.x /= m_fMapScale;
  591. offset.y /= -m_fMapScale;
  592. return offset;
  593. }
  594. float CMapOverview::GetViewAngle( void )
  595. {
  596. float viewAngle = m_fViewAngle - 90.0f;
  597. if ( !m_bFollowAngle )
  598. {
  599. // We don't use fViewAngle. We just show straight at all times.
  600. if ( m_bRotateMap )
  601. viewAngle = 90.0f;
  602. else
  603. viewAngle = 0.0f;
  604. }
  605. return viewAngle;
  606. }
  607. Vector2D CMapOverview::MapToPanel( const Vector2D &mappos )
  608. {
  609. int pwidth, pheight;
  610. Vector2D panelpos;
  611. float viewAngle = GetViewAngle();
  612. GetSize(pwidth, pheight);
  613. Vector offset;
  614. offset.x = mappos.x - m_MapCenter.x;
  615. offset.y = mappos.y - m_MapCenter.y;
  616. offset.z = 0;
  617. VectorYawRotate( offset, viewAngle, offset );
  618. // find the actual zoom from the animationvar m_fZoom and the map zoom scale
  619. float fScale = (m_fZoom * m_fFullZoom) / OVERVIEW_MAP_SIZE;
  620. offset.x *= fScale;
  621. offset.y *= fScale;
  622. panelpos.x = (pwidth * 0.5f) + (pheight * offset.x);
  623. panelpos.y = (pheight * 0.5f) + (pheight * offset.y);
  624. return panelpos;
  625. }
  626. void CMapOverview::SetTime( float time )
  627. {
  628. m_fWorldTime = time;
  629. }
  630. void CMapOverview::SetMap(const char * levelname)
  631. {
  632. // Reset players and objects, even if the map is the same as the previous one
  633. m_Objects.RemoveAll();
  634. m_fNextTrailUpdate = 0;// Set to 0 for immediate update. Our WorldTime var hasn't been updated to 0 for the new map yet
  635. 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.
  636. // Please note, UpdatePlayerTrails comes from PAINT, not UPDATE.
  637. InitTeamColorsAndIcons();
  638. // load new KeyValues
  639. if ( m_MapKeyValues && Q_strcmp( levelname, m_MapKeyValues->GetName() ) == 0 )
  640. {
  641. return; // map didn't change
  642. }
  643. if ( m_MapKeyValues )
  644. m_MapKeyValues->deleteThis();
  645. m_MapKeyValues = new KeyValues( levelname );
  646. char tempfile[MAX_PATH];
  647. Q_snprintf( tempfile, sizeof( tempfile ), "resource/overviews/%s.txt", levelname );
  648. if ( !m_MapKeyValues->LoadFromFile( g_pFullFileSystem, tempfile, "GAME" ) )
  649. {
  650. DevMsg( 1, "Error! CMapOverview::SetMap: couldn't load file %s.\n", tempfile );
  651. m_nMapTextureID = -1;
  652. m_MapOrigin.x = 0;
  653. m_MapOrigin.y = 0;
  654. m_fMapScale = 1;
  655. m_bRotateMap = false;
  656. m_fFullZoom = 1;
  657. return;
  658. }
  659. // TODO release old texture ?
  660. m_nMapTextureID = surface()->CreateNewTextureID();
  661. //if we have not uploaded yet, lets go ahead and do so
  662. surface()->DrawSetTextureFile( m_nMapTextureID, m_MapKeyValues->GetString("material"), true, false);
  663. int wide, tall;
  664. surface()->DrawGetTextureSize( m_nMapTextureID, wide, tall );
  665. if ( wide != tall )
  666. {
  667. DevMsg( 1, "Error! CMapOverview::SetMap: map image must be a square.\n" );
  668. m_nMapTextureID = -1;
  669. return;
  670. }
  671. m_MapOrigin.x = m_MapKeyValues->GetInt("pos_x");
  672. m_MapOrigin.y = m_MapKeyValues->GetInt("pos_y");
  673. m_fMapScale = m_MapKeyValues->GetFloat("scale", 1.0f);
  674. m_bRotateMap = m_MapKeyValues->GetInt("rotate")!=0;
  675. m_fFullZoom = m_MapKeyValues->GetFloat("zoom", 1.0f );
  676. }
  677. void CMapOverview::ResetRound()
  678. {
  679. for (int i=0; i<MAX_PLAYERS; i++)
  680. {
  681. MapPlayer_t *p = &m_Players[i];
  682. if ( p->team > TEAM_SPECTATOR )
  683. {
  684. p->health = 100;
  685. }
  686. Q_memset( p->trail, 0, sizeof(p->trail) );
  687. p->position = Vector( 0, 0, 0 );
  688. }
  689. m_Objects.RemoveAll();
  690. }
  691. void CMapOverview::OnMousePressed( MouseCode code )
  692. {
  693. }
  694. void CMapOverview::DrawCamera()
  695. {
  696. // draw a red center point
  697. surface()->DrawSetColor( 255,0,0,255 );
  698. Vector2D center = MapToPanel( m_ViewOrigin );
  699. surface()->DrawFilledRect( center.x-2, center.y-2, center.x+2, center.y+2);
  700. }
  701. void CMapOverview::FireGameEvent( IGameEvent *event )
  702. {
  703. const char * type = event->GetName();
  704. if ( Q_strcmp(type, "game_newmap") == 0 )
  705. {
  706. SetMap( event->GetString("mapname") );
  707. ResetRound();
  708. }
  709. else if ( Q_strcmp(type, "round_start") == 0 )
  710. {
  711. ResetRound();
  712. }
  713. else if ( Q_strcmp(type,"player_connect_client") == 0 )
  714. {
  715. int index = event->GetInt("index"); // = entity index - 1
  716. if ( index < 0 || index >= MAX_PLAYERS )
  717. return;
  718. MapPlayer_t *player = &m_Players[index];
  719. player->index = index;
  720. player->userid = event->GetInt("userid");
  721. Q_strncpy( player->name, event->GetString("name","unknown"), sizeof(player->name) );
  722. // Reset settings
  723. Q_memset( player->trail, 0, sizeof(player->trail) );
  724. player->team = TEAM_UNASSIGNED;
  725. player->health = 0;
  726. }
  727. else if ( Q_strcmp(type,"player_info") == 0 )
  728. {
  729. int index = event->GetInt("index"); // = entity index - 1
  730. if ( index < 0 || index >= MAX_PLAYERS )
  731. return;
  732. MapPlayer_t *player = &m_Players[index];
  733. player->index = index;
  734. player->userid = event->GetInt("userid");
  735. Q_strncpy( player->name, event->GetString("name","unknown"), sizeof(player->name) );
  736. }
  737. else if ( Q_strcmp(type,"player_team") == 0 )
  738. {
  739. MapPlayer_t *player = GetPlayerByUserID( event->GetInt("userid") );
  740. if ( !player )
  741. return;
  742. player->team = event->GetInt("team");
  743. player->icon = m_TeamIcons[ GetIconNumberFromTeamNumber(player->team) ];
  744. player->color = m_TeamColors[ GetIconNumberFromTeamNumber(player->team) ];
  745. }
  746. else if ( Q_strcmp(type,"player_death") == 0 )
  747. {
  748. MapPlayer_t *player = GetPlayerByUserID( event->GetInt("userid") );
  749. if ( !player )
  750. return;
  751. player->health = 0;
  752. Q_memset( player->trail, 0, sizeof(player->trail) ); // clear trails
  753. }
  754. else if ( Q_strcmp(type,"player_spawn") == 0 )
  755. {
  756. MapPlayer_t *player = GetPlayerByUserID( event->GetInt("userid") );
  757. if ( !player )
  758. return;
  759. player->health = 100;
  760. Q_memset( player->trail, 0, sizeof(player->trail) ); // clear trails
  761. }
  762. else if ( Q_strcmp(type,"player_disconnect") == 0 )
  763. {
  764. MapPlayer_t *player = GetPlayerByUserID( event->GetInt("userid") );
  765. if ( !player )
  766. return;
  767. Q_memset( player, 0, sizeof(MapPlayer_t) ); // clear player field
  768. }
  769. }
  770. void CMapOverview::SetMode(int mode)
  771. {
  772. m_flChangeSpeed = 0; // change size instantly
  773. if ( mode == MAP_MODE_OFF )
  774. {
  775. ShowPanel( false );
  776. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "MapOff" );
  777. }
  778. else if ( mode == MAP_MODE_INSET )
  779. {
  780. if( m_nMapTextureID == -1 )
  781. {
  782. SetMode( MAP_MODE_OFF );
  783. return;
  784. }
  785. if ( m_nMode != MAP_MODE_OFF )
  786. m_flChangeSpeed = 1000; // zoom effect
  787. C_BasePlayer *pPlayer = CBasePlayer::GetLocalPlayer();
  788. if ( pPlayer )
  789. SetFollowEntity( pPlayer->entindex() );
  790. ShowPanel( true );
  791. if ( mode != m_nMode && RunHudAnimations() )
  792. {
  793. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "MapZoomToSmall" );
  794. }
  795. }
  796. else if ( mode == MAP_MODE_FULL )
  797. {
  798. if( m_nMapTextureID == -1 )
  799. {
  800. SetMode( MAP_MODE_OFF );
  801. return;
  802. }
  803. if ( m_nMode != MAP_MODE_OFF )
  804. m_flChangeSpeed = 1000; // zoom effect
  805. SetFollowEntity( 0 );
  806. ShowPanel( true );
  807. if ( mode != m_nMode && RunHudAnimations() )
  808. {
  809. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "MapZoomToLarge" );
  810. }
  811. }
  812. // finally set mode
  813. m_nMode = mode;
  814. UpdateSizeAndPosition();
  815. }
  816. bool CMapOverview::ShouldDraw( void )
  817. {
  818. return ( m_nMode != MAP_MODE_OFF ) && CHudElement::ShouldDraw();
  819. }
  820. void CMapOverview::UpdateSizeAndPosition()
  821. {
  822. if ( g_pSpectatorGUI && g_pSpectatorGUI->IsVisible() )
  823. {
  824. int iScreenWide, iScreenTall;
  825. GetHudSize( iScreenWide, iScreenTall );
  826. int iTopBarHeight = g_pSpectatorGUI->GetTopBarHeight();
  827. int iBottomBarHeight = g_pSpectatorGUI->GetBottomBarHeight();
  828. iScreenTall -= ( iTopBarHeight + iBottomBarHeight );
  829. int x,y,w,h;
  830. GetBounds( x,y,w,h );
  831. if ( y < iTopBarHeight )
  832. y = iTopBarHeight;
  833. SetBounds( x,y,w,MIN(h,iScreenTall) );
  834. }
  835. }
  836. void CMapOverview::SetCenter(const Vector2D &mappos)
  837. {
  838. int width, height;
  839. GetSize( width, height);
  840. m_ViewOrigin = mappos;
  841. m_MapCenter = mappos;
  842. float fTwiceZoom = m_fZoom * m_fFullZoom * 2;
  843. width = height = OVERVIEW_MAP_SIZE / (fTwiceZoom);
  844. if( GetMode() != MAP_MODE_RADAR )
  845. {
  846. if ( m_MapCenter.x < width )
  847. m_MapCenter.x = width;
  848. if ( m_MapCenter.x > (OVERVIEW_MAP_SIZE-width) )
  849. m_MapCenter.x = (OVERVIEW_MAP_SIZE-width);
  850. if ( m_MapCenter.y < height )
  851. m_MapCenter.y = height;
  852. if ( m_MapCenter.y > (OVERVIEW_MAP_SIZE-height) )
  853. m_MapCenter.y = (OVERVIEW_MAP_SIZE-height);
  854. //center if in full map mode
  855. if ( m_fZoom <= 1.0 )
  856. {
  857. m_MapCenter.x = OVERVIEW_MAP_SIZE/2;
  858. m_MapCenter.y = OVERVIEW_MAP_SIZE/2;
  859. }
  860. }
  861. }
  862. void CMapOverview::SetFollowAngle(bool state)
  863. {
  864. m_bFollowAngle = state;
  865. }
  866. void CMapOverview::SetFollowEntity(int entindex)
  867. {
  868. m_nFollowEntity = entindex;
  869. }
  870. float CMapOverview::GetZoom( void )
  871. {
  872. return m_fZoom;
  873. }
  874. int CMapOverview::GetMode( void )
  875. {
  876. return m_nMode;
  877. }
  878. void CMapOverview::SetAngle(float angle)
  879. {
  880. m_fViewAngle = angle;
  881. }
  882. void CMapOverview::ShowPlayerNames(bool state)
  883. {
  884. m_bShowNames = state;
  885. }
  886. void CMapOverview::ShowPlayerHealth(bool state)
  887. {
  888. m_bShowHealth = state;
  889. }
  890. void CMapOverview::ShowPlayerTracks(float seconds)
  891. {
  892. m_fTrailUpdateInterval = seconds;
  893. }
  894. bool CMapOverview::SetTeamColor(int team, Color color)
  895. {
  896. if ( team < 0 || team>= MAX_TEAMS )
  897. return false;
  898. m_TeamColors[team] = color;
  899. return true;
  900. }
  901. CMapOverview::MapObject_t* CMapOverview::FindObjectByID(int objectID)
  902. {
  903. for ( int i = 0; i < m_Objects.Count(); i++ )
  904. {
  905. if ( m_Objects[i].objectID == objectID )
  906. return &m_Objects[i];
  907. }
  908. return NULL;
  909. }
  910. int CMapOverview::AddObject( const char *icon, int entity, float timeToLive )
  911. {
  912. MapObject_t obj; Q_memset( &obj, 0, sizeof(obj) );
  913. obj.objectID = m_ObjectCounterID++;
  914. obj.index = entity;
  915. obj.icon = AddIconTexture( icon );
  916. obj.size = m_flIconSize;
  917. obj.status = -1;
  918. if ( timeToLive > 0 )
  919. obj.endtime = gpGlobals->curtime + timeToLive;
  920. else
  921. obj.endtime = -1;
  922. m_Objects.AddToTail( obj );
  923. return obj.objectID;
  924. }
  925. void CMapOverview::SetObjectText( int objectID, const char *text, Color color )
  926. {
  927. MapObject_t* obj = FindObjectByID( objectID );
  928. if ( !obj )
  929. return;
  930. if ( text )
  931. {
  932. Q_strncpy( obj->name, text, sizeof(obj->name) );
  933. }
  934. else
  935. {
  936. Q_memset( obj->name, 0, sizeof(obj->name) );
  937. }
  938. obj->color = color;
  939. }
  940. void CMapOverview::SetObjectStatus( int objectID, float status, Color color )
  941. {
  942. MapObject_t* obj = FindObjectByID( objectID );
  943. if ( !obj )
  944. return;
  945. obj->status = status;
  946. obj->statusColor = color;
  947. }
  948. void CMapOverview::SetObjectIcon( int objectID, const char *icon, float size )
  949. {
  950. MapObject_t* obj = FindObjectByID( objectID );
  951. if ( !obj )
  952. return;
  953. obj->icon = AddIconTexture( icon );
  954. obj->size = size;
  955. }
  956. void CMapOverview::SetObjectPosition( int objectID, const Vector &position, const QAngle &angle )
  957. {
  958. MapObject_t* obj = FindObjectByID( objectID );
  959. if ( !obj )
  960. return;
  961. obj->angle = angle;
  962. obj->position = position;
  963. }
  964. void CMapOverview::AddObjectFlags( int objectID, int flags )
  965. {
  966. MapObject_t* obj = FindObjectByID( objectID );
  967. if ( !obj )
  968. return;
  969. obj->flags |= flags;
  970. }
  971. void CMapOverview::SetObjectFlags( int objectID, int flags )
  972. {
  973. MapObject_t* obj = FindObjectByID( objectID );
  974. if ( !obj )
  975. return;
  976. obj->flags = flags;
  977. }
  978. void CMapOverview::RemoveObjectByIndex( int index )
  979. {
  980. for ( int i = 0; i < m_Objects.Count(); i++ )
  981. {
  982. if ( m_Objects[i].index == index )
  983. {
  984. m_Objects.Remove( i );
  985. return;
  986. }
  987. }
  988. }
  989. void CMapOverview::RemoveObject( int objectID )
  990. {
  991. for ( int i = 0; i < m_Objects.Count(); i++ )
  992. {
  993. if ( m_Objects[i].objectID == objectID )
  994. {
  995. m_Objects.Remove( i );
  996. return;
  997. }
  998. }
  999. }
  1000. void CMapOverview::UpdateObjects()
  1001. {
  1002. for ( int i = 0; i < m_Objects.Count(); i++ )
  1003. {
  1004. MapObject_t *obj = &m_Objects[i];
  1005. if ( obj->endtime > 0 && obj->endtime < gpGlobals->curtime )
  1006. {
  1007. m_Objects.Remove( i );
  1008. i--;
  1009. continue;
  1010. }
  1011. if ( obj->index <= 0 )
  1012. continue;
  1013. C_BaseEntity *entity = ClientEntityList().GetEnt( obj->index );
  1014. if ( !entity )
  1015. continue;
  1016. obj->position = entity->GetAbsOrigin();
  1017. obj->angle = entity->GetAbsAngles();
  1018. }
  1019. }