Counter Strike : Global Offensive Source Code
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.

1381 lines
31 KiB

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