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.

2324 lines
66 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "cstrikespectatorgui.h"
  9. #include "hud.h"
  10. #include "cs_shareddefs.h"
  11. #include <vgui/ILocalize.h>
  12. #include <vgui/ISurface.h>
  13. #include <filesystem.h>
  14. #include "cs_gamerules.h"
  15. #include "c_team.h"
  16. #include "c_cs_playerresource.h"
  17. #include "c_plantedc4.h"
  18. #include "c_cs_hostage.h"
  19. #include "vtf/vtf.h"
  20. #include "clientmode.h"
  21. #include <vgui_controls/AnimationController.h>
  22. #include "voice_status.h"
  23. #include "hud_radar.h"
  24. using namespace vgui;
  25. DECLARE_HUDELEMENT( CCSMapOverview )
  26. extern ConVar overview_health;
  27. extern ConVar overview_names;
  28. extern ConVar overview_tracks;
  29. extern ConVar overview_locked;
  30. extern ConVar overview_alpha;
  31. extern ConVar cl_radaralpha;
  32. ConVar cl_radar_locked( "cl_radar_locked", "0", FCVAR_ARCHIVE, "Lock the angle of the radar display?" );
  33. void PreferredOverviewModeChanged( IConVar *pConVar, const char *oldString, float flOldValue )
  34. {
  35. ConVarRef var( pConVar );
  36. char cmd[32];
  37. V_snprintf( cmd, sizeof( cmd ), "overview_mode %d\n", var.GetInt() );
  38. engine->ClientCmd( cmd );
  39. }
  40. ConVar overview_preferred_mode( "overview_preferred_mode", "1", FCVAR_ARCHIVE, "Preferred overview mode", PreferredOverviewModeChanged );
  41. ConVar overview_preferred_view_size( "overview_preferred_view_size", "600", FCVAR_ARCHIVE, "Preferred overview view size" );
  42. #define HOSTAGE_RESCUE_DURATION (2.5f)
  43. #define BOMB_FADE_DURATION (2.5f)
  44. #define DEATH_ICON_FADE (7.5f)
  45. #define DEATH_ICON_DURATION (10.0f)
  46. #define LAST_SEEN_ICON_DURATION (4.0f)
  47. #define DIFFERENCE_THRESHOLD (200.0f)
  48. // To make your own green radar file from the map overview file, turn this on, and include vtf.lib
  49. #define no_GENERATE_RADAR_FILE
  50. //-----------------------------------------------------------------------------
  51. // Purpose: Constructor
  52. //-----------------------------------------------------------------------------
  53. CCSSpectatorGUI::CCSSpectatorGUI(IViewPort *pViewPort) : CSpectatorGUI(pViewPort)
  54. {
  55. m_pCTLabel = NULL;
  56. m_pCTScore = NULL;
  57. m_pTerLabel = NULL;
  58. m_pTerScore = NULL;
  59. m_pTimer = NULL;
  60. m_pTimerLabel = NULL;
  61. m_pDivider = NULL;
  62. m_pExtraInfo = NULL;
  63. m_modifiedWidths = false;
  64. m_scoreWidth = 0;
  65. m_extraInfoWidth = 0;
  66. }
  67. //-----------------------------------------------------------------------------
  68. // Purpose:
  69. //-----------------------------------------------------------------------------
  70. void CCSSpectatorGUI::ApplySchemeSettings(vgui::IScheme *pScheme)
  71. {
  72. BaseClass::ApplySchemeSettings( pScheme );
  73. // Grab some control pointers
  74. m_pCTLabel = dynamic_cast<Label *>(FindChildByName("CTScoreLabel"));
  75. m_pCTScore = dynamic_cast<Label *>(FindChildByName("CTScoreValue"));
  76. m_pTerLabel = dynamic_cast<Label *>(FindChildByName("TerScoreLabel"));
  77. m_pTerScore = dynamic_cast<Label *>(FindChildByName("TerScoreValue"));
  78. m_pTimer = dynamic_cast<Label *>(FindChildByName("timerclock"));
  79. m_pTimerLabel = dynamic_cast<Label *>(FindChildByName("timerlabel"));
  80. m_pDivider = dynamic_cast<Panel *>(FindChildByName("DividerBar"));
  81. m_pExtraInfo = dynamic_cast<Label *>(FindChildByName("extrainfo"));
  82. }
  83. //-----------------------------------------------------------------------------
  84. // Purpose: Resets the list of players
  85. //-----------------------------------------------------------------------------
  86. void CCSSpectatorGUI::UpdateSpectatorPlayerList()
  87. {
  88. C_Team *cts = GetGlobalTeam( TEAM_CT );
  89. if ( cts )
  90. {
  91. wchar_t frags[ 10 ];
  92. _snwprintf( frags, ARRAYSIZE( frags ), L"%i", cts->Get_Score() );
  93. SetLabelText( "CTScoreValue", frags );
  94. }
  95. C_Team *ts = GetGlobalTeam( TEAM_TERRORIST );
  96. if ( ts )
  97. {
  98. wchar_t frags[ 10 ];
  99. _snwprintf( frags, ARRAYSIZE( frags ), L"%i", ts->Get_Score() );
  100. SetLabelText( "TERScoreValue", frags );
  101. }
  102. }
  103. bool CCSSpectatorGUI::NeedsUpdate( void )
  104. {
  105. C_CSPlayer *player = C_CSPlayer::GetLocalCSPlayer();
  106. if ( !player )
  107. return false;
  108. if ( m_nLastAccount != player->GetAccount() )
  109. return true;
  110. if ( m_nLastTime != (int)CSGameRules()->GetRoundRemainingTime() )
  111. return true;
  112. if ( m_nLastSpecMode != player->GetObserverMode() )
  113. return true;
  114. if ( m_nLastSpecTarget != player->GetObserverTarget() )
  115. return true;
  116. return BaseClass::NeedsUpdate();
  117. }
  118. //=============================================================================
  119. // HPE_BEGIN:
  120. // [smessick]
  121. //=============================================================================
  122. void CCSSpectatorGUI::ShowPanel( bool bShow )
  123. {
  124. BaseClass::ShowPanel( bShow );
  125. if ( bShow )
  126. {
  127. // Resend the overview command.
  128. char cmd[32];
  129. V_snprintf( cmd, sizeof( cmd ), "overview_mode %d\n", overview_preferred_mode.GetInt() );
  130. engine->ClientCmd( cmd );
  131. }
  132. }
  133. //=============================================================================
  134. // HPE_END
  135. //=============================================================================
  136. //-----------------------------------------------------------------------------
  137. // Purpose: Updates the timer label if one exists
  138. //-----------------------------------------------------------------------------
  139. void CCSSpectatorGUI::UpdateTimer()
  140. {
  141. // these could be NULL if players modified the UI
  142. if ( !ControlsPresent() )
  143. return;
  144. Color timerColor = m_pTimer->GetFgColor();
  145. if( g_PlantedC4s.Count() > 0 )
  146. {
  147. m_pTimer->SetText( "\\" ); // bomb icon
  148. m_pTimerLabel->SetVisible( false );
  149. if( g_PlantedC4s[0]->m_flNextGlow > gpGlobals->curtime + 0.1f )
  150. timerColor[3] = 80;
  151. else
  152. timerColor[3] = 255;
  153. m_pTimer->SetFgColor( timerColor );
  154. return;
  155. }
  156. timerColor[3] = 255;
  157. m_pTimer->SetFgColor( timerColor );
  158. m_pTimer->SetText( "e" ); // clock icon
  159. m_nLastTime = (int)( CSGameRules()->GetRoundRemainingTime() );
  160. if ( m_nLastTime < 0 )
  161. m_nLastTime = 0;
  162. wchar_t szText[ 63 ];
  163. _snwprintf ( szText, ARRAYSIZE( szText ), L"%d:%02d", (m_nLastTime / 60), (m_nLastTime % 60) );
  164. szText[62] = 0;
  165. SetLabelText("timerlabel", szText );
  166. m_pTimerLabel->SetVisible( true );
  167. }
  168. void CCSSpectatorGUI::UpdateAccount()
  169. {
  170. C_CSPlayer *player = C_CSPlayer::GetLocalCSPlayer();
  171. if ( !player )
  172. return;
  173. m_nLastAccount = player->GetAccount();
  174. if ( (player->GetTeamNumber() == TEAM_TERRORIST) || (player->GetTeamNumber() == TEAM_CT) )
  175. {
  176. wchar_t szText[ 63 ];
  177. _snwprintf ( szText, ARRAYSIZE( szText ), L"$%i", m_nLastAccount );
  178. szText[62] = 0;
  179. SetLabelText( "extrainfo", szText );
  180. }
  181. }
  182. /*bool CCSSpectatorGUI::CanSpectateTeam( int iTeam )
  183. {
  184. bool bRetVal = true;
  185. int iTeamOnly = 0;// TODO = gCSViewPortInterface->GetForceCamera();
  186. // if we're not a spectator or HLTV and iTeamOnly is set
  187. if ( C_BasePlayer::GetLocalPlayer()->GetTeamNumber() // && !gEngfuncs.IsSpectateOnly()
  188. && iTeamOnly )
  189. {
  190. // then we want to force the same team
  191. if ( C_BasePlayer::GetLocalPlayer()->GetTeamNumber() != iTeam )
  192. {
  193. bRetVal = false;
  194. }
  195. }
  196. return bRetVal;
  197. }*/
  198. void CCSSpectatorGUI::Update()
  199. {
  200. BaseClass::Update();
  201. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  202. if( pLocalPlayer )
  203. {
  204. m_nLastSpecMode = pLocalPlayer->GetObserverMode();
  205. m_nLastSpecTarget = pLocalPlayer->GetObserverTarget();
  206. }
  207. UpdateTimer();
  208. UpdateAccount();
  209. UpdateSpectatorPlayerList();
  210. if ( pLocalPlayer )
  211. {
  212. ResizeControls();
  213. }
  214. }
  215. //-----------------------------------------------------------------------------
  216. // Purpose: Save off widths for sizing calculations
  217. //-----------------------------------------------------------------------------
  218. void CCSSpectatorGUI::StoreWidths( void )
  219. {
  220. if ( !ControlsPresent() )
  221. return;
  222. if ( !m_modifiedWidths )
  223. {
  224. m_scoreWidth = m_pCTScore->GetWide();
  225. int terScoreWidth = m_pTerScore->GetWide();
  226. m_extraInfoWidth = m_pExtraInfo->GetWide();
  227. if ( m_scoreWidth != terScoreWidth )
  228. {
  229. m_pTerScore = NULL; // We're working with a modified res file. Don't muck things up playing with positioning.
  230. }
  231. }
  232. }
  233. //-----------------------------------------------------------------------------
  234. // Purpose: Resize controls so scores & map names are not cut off
  235. //-----------------------------------------------------------------------------
  236. void CCSSpectatorGUI::ResizeControls( void )
  237. {
  238. if ( !ControlsPresent() )
  239. return;
  240. int x1, y1, w1, t1;
  241. int x2, y2, w2, t2;
  242. StoreWidths();
  243. // ensure scores are wide enough
  244. int wCT, hCT, wTer, hTer;
  245. m_pCTScore->GetBounds( x1, y1, w1, t1 );
  246. m_pCTScore->GetContentSize( wCT, hCT );
  247. m_pTerScore->GetBounds( x2, y2, w2, t2 );
  248. m_pTerScore->GetContentSize( wTer, hTer );
  249. int desiredScoreWidth = m_scoreWidth;
  250. desiredScoreWidth = MAX( desiredScoreWidth, wCT );
  251. desiredScoreWidth = MAX( desiredScoreWidth, wTer );
  252. int diff = desiredScoreWidth - w1;
  253. if ( diff != 0 )
  254. {
  255. m_pCTScore->GetBounds( x1, y1, w1, t1 );
  256. m_pCTScore->SetBounds( x1 - diff, y1, w1 + diff, t1 );
  257. m_pTerScore->GetBounds( x1, y1, w1, t1 );
  258. m_pTerScore->SetBounds( x1 - diff, y1, w1 + diff, t1 );
  259. m_pCTLabel->GetPos( x1, y1 );
  260. m_pCTLabel->SetPos( x1 - diff, y1 );
  261. m_pTerLabel->GetPos( x1, y1 );
  262. m_pTerLabel->SetPos( x1 - diff, y1 );
  263. m_modifiedWidths = true;
  264. }
  265. // ensure extra info is wide enough
  266. int wExtra, hExtra;
  267. m_pExtraInfo->GetBounds( x1, y1, w1, t1 );
  268. m_pExtraInfo->GetContentSize( wExtra, hExtra );
  269. int desiredExtraWidth = m_extraInfoWidth;
  270. desiredExtraWidth = MAX( desiredExtraWidth, wExtra );
  271. diff = desiredExtraWidth - w1;
  272. if ( diff != 0 )
  273. {
  274. m_pExtraInfo->GetBounds( x1, y1, w1, t1 );
  275. m_pExtraInfo->SetBounds( x1 - diff, y1, w1 + diff, t1 );
  276. m_pTimer->GetPos( x1, y1 );
  277. m_pTimer->SetPos( x1 - diff, y1 );
  278. m_pTimerLabel->GetPos( x1, y1 );
  279. m_pTimerLabel->SetPos( x1 - diff, y1 );
  280. m_pDivider->GetPos( x1, y1 );
  281. m_pDivider->SetPos( x1 - diff, y1 );
  282. m_pCTScore->GetPos( x1, y1 );
  283. m_pCTScore->SetPos( x1 - diff, y1 );
  284. m_pCTLabel->GetPos( x1, y1 );
  285. m_pCTLabel->SetPos( x1 - diff, y1 );
  286. m_pTerScore->GetPos( x1, y1 );
  287. m_pTerScore->SetPos( x1 - diff, y1 );
  288. m_pTerLabel->GetPos( x1, y1 );
  289. m_pTerLabel->SetPos( x1 - diff, y1 );
  290. m_modifiedWidths = true;
  291. }
  292. }
  293. //-----------------------------------------------------------------------------
  294. // Purpose: Verify controls are present to resize
  295. //-----------------------------------------------------------------------------
  296. bool CCSSpectatorGUI::ControlsPresent( void ) const
  297. {
  298. return ( m_pCTLabel != NULL &&
  299. m_pCTScore != NULL &&
  300. m_pTerLabel != NULL &&
  301. m_pTerScore != NULL &&
  302. m_pTimer != NULL &&
  303. m_pTimerLabel != NULL &&
  304. m_pDivider != NULL &&
  305. m_pExtraInfo != NULL );
  306. }
  307. //-----------------------------------------------------------------------------
  308. //-----------------------------------------------------------------------------
  309. //-----------------------------------------------------------------------------
  310. static int AdjustValue( int curValue, int targetValue, int amount )
  311. {
  312. if ( curValue > targetValue )
  313. {
  314. curValue -= amount;
  315. if ( curValue < targetValue )
  316. curValue = targetValue;
  317. }
  318. else if ( curValue < targetValue )
  319. {
  320. curValue += amount;
  321. if ( curValue > targetValue )
  322. curValue = targetValue;
  323. }
  324. return curValue;
  325. }
  326. void CCSMapOverview::InitTeamColorsAndIcons()
  327. {
  328. BaseClass::InitTeamColorsAndIcons();
  329. Q_memset( m_TeamIconsSelf, 0, sizeof(m_TeamIconsSelf) );
  330. Q_memset( m_TeamIconsDead, 0, sizeof(m_TeamIconsDead) );
  331. Q_memset( m_TeamIconsOffscreen, 0, sizeof(m_TeamIconsOffscreen) );
  332. Q_memset( m_TeamIconsDeadOffscreen, 0, sizeof(m_TeamIconsDeadOffscreen) );
  333. m_bombIconPlanted = -1;
  334. m_bombIconDropped = -1;
  335. m_bombIconCarried = -1;
  336. m_bombRingPlanted = -1;
  337. m_bombRingDropped = -1;
  338. m_bombRingCarried = -1;
  339. m_bombRingCarriedOffscreen = -1;
  340. m_radioFlash = -1;
  341. m_radioFlashOffscreen = -1;
  342. m_radarTint = -1;
  343. m_hostageFollowing = -1;
  344. m_hostageFollowingOffscreen = -1;
  345. m_playerFacing = -1;
  346. m_cameraIconFirst = -1;
  347. m_cameraIconThird = -1;
  348. m_cameraIconFree = -1;
  349. m_hostageRescueIcon = -1;
  350. m_bombSiteIconA = -1;
  351. m_bombSiteIconB = -1;
  352. //setup team red
  353. m_TeamColors[MAP_ICON_T] = COLOR_RED;
  354. m_TeamIcons[MAP_ICON_T] = AddIconTexture( "sprites/player_red_small" );
  355. m_TeamIconsSelf[MAP_ICON_T] = AddIconTexture( "sprites/player_red_self" );
  356. m_TeamIconsDead[MAP_ICON_T] = AddIconTexture( "sprites/player_red_dead" );
  357. m_TeamIconsOffscreen[MAP_ICON_T] = AddIconTexture( "sprites/player_red_offscreen" );
  358. m_TeamIconsDeadOffscreen[MAP_ICON_T] = AddIconTexture( "sprites/player_red_dead_offscreen" );
  359. // setup team blue
  360. m_TeamColors[MAP_ICON_CT] = COLOR_BLUE;
  361. m_TeamIcons[MAP_ICON_CT] = AddIconTexture( "sprites/player_blue_small" );
  362. m_TeamIconsSelf[MAP_ICON_CT] = AddIconTexture( "sprites/player_blue_self" );
  363. m_TeamIconsDead[MAP_ICON_CT] = AddIconTexture( "sprites/player_blue_dead" );
  364. m_TeamIconsOffscreen[MAP_ICON_CT] = AddIconTexture( "sprites/player_blue_offscreen" );
  365. m_TeamIconsDeadOffscreen[MAP_ICON_CT] = AddIconTexture( "sprites/player_blue_dead_offscreen" );
  366. // setup team other
  367. m_TeamColors[MAP_ICON_HOSTAGE] = COLOR_GREY;
  368. m_TeamIcons[MAP_ICON_HOSTAGE] = AddIconTexture( "sprites/player_hostage_small" );
  369. m_TeamIconsSelf[MAP_ICON_HOSTAGE] = -1;
  370. m_TeamIconsDead[MAP_ICON_HOSTAGE] = AddIconTexture( "sprites/player_hostage_dead" );
  371. m_TeamIconsOffscreen[MAP_ICON_HOSTAGE] = AddIconTexture( "sprites/player_hostage_offscreen" );
  372. m_TeamIconsDeadOffscreen[MAP_ICON_HOSTAGE] = AddIconTexture( "sprites/player_hostage_dead_offscreen" );
  373. m_bombIconPlanted = AddIconTexture( "sprites/bomb_planted" );
  374. m_bombIconDropped = AddIconTexture( "sprites/bomb_dropped" );
  375. m_bombIconCarried = AddIconTexture( "sprites/bomb_carried" );
  376. m_bombRingPlanted = AddIconTexture( "sprites/bomb_planted_ring" );
  377. m_bombRingDropped = AddIconTexture( "sprites/bomb_dropped_ring" );
  378. m_bombRingCarried = AddIconTexture( "sprites/bomb_carried_ring" );
  379. m_bombRingCarriedOffscreen = AddIconTexture( "sprites/bomb_carried_ring_offscreen" );
  380. m_hostageFollowing = AddIconTexture( "sprites/hostage_following" );
  381. m_hostageFollowingOffscreen = AddIconTexture( "sprites/hostage_following_offscreen" );
  382. m_playerFacing = AddIconTexture( "sprites/player_tick" );
  383. m_cameraIconFirst = AddIconTexture( "sprites/spectator_eye" );
  384. m_cameraIconThird = AddIconTexture( "sprites/spectator_3rdcam" );
  385. m_cameraIconFree = AddIconTexture( "sprites/spectator_freecam" );
  386. m_hostageRescueIcon = AddIconTexture( "sprites/objective_rescue" );;
  387. m_bombSiteIconA = AddIconTexture( "sprites/objective_site_a" );;
  388. m_bombSiteIconB = AddIconTexture( "sprites/objective_site_b" );;
  389. m_radioFlash = AddIconTexture("sprites/player_radio_ring");
  390. m_radioFlashOffscreen = AddIconTexture("sprites/player_radio_ring_offscreen");
  391. m_radarTint = AddIconTexture("sprites/radar_trans");
  392. }
  393. //-----------------------------------------------------------------------------
  394. void CCSMapOverview::ApplySchemeSettings(vgui::IScheme *scheme)
  395. {
  396. BaseClass::ApplySchemeSettings( scheme );
  397. m_hIconFont = scheme->GetFont( "DefaultSmall", true );
  398. }
  399. //-----------------------------------------------------------------------------
  400. void CCSMapOverview::Update( void )
  401. {
  402. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  403. if ( !pPlayer )
  404. return;
  405. int team = pPlayer->GetTeamNumber();
  406. // if dead with fadetoblack on, we can't show anything
  407. if ( mp_fadetoblack.GetBool() && team > TEAM_SPECTATOR && !pPlayer->IsAlive() )
  408. {
  409. SetMode( MAP_MODE_OFF );
  410. return;
  411. }
  412. bool inRadarMode = (GetMode() == MAP_MODE_RADAR);
  413. int specmode = pPlayer->GetObserverMode();
  414. // if alive, we can only be in radar mode
  415. if( !inRadarMode && pPlayer->IsAlive())
  416. {
  417. SetMode( MAP_MODE_RADAR );
  418. inRadarMode = true;
  419. }
  420. if( inRadarMode )
  421. {
  422. if( specmode > OBS_MODE_DEATHCAM )
  423. {
  424. // If fully dead, we don't want to be radar any more
  425. SetMode( m_playerPreferredMode );
  426. m_flChangeSpeed = 0;
  427. }
  428. else
  429. {
  430. SetFollowEntity(pPlayer->entindex());
  431. UpdatePlayers();
  432. }
  433. }
  434. BaseClass::Update();
  435. if ( GetSpectatorMode() == OBS_MODE_CHASE )
  436. {
  437. // Follow the local player in chase cam, so the map rotates using the local player's angles
  438. SetFollowEntity( pPlayer->entindex() );
  439. }
  440. }
  441. //-----------------------------------------------------------------------------
  442. CCSMapOverview::CSMapPlayer_t* CCSMapOverview::GetCSInfoForPlayerIndex( int index )
  443. {
  444. if ( index < 0 || index >= MAX_PLAYERS )
  445. return NULL;
  446. return &m_PlayersCSInfo[ index ];
  447. }
  448. //-----------------------------------------------------------------------------
  449. CCSMapOverview::CSMapPlayer_t* CCSMapOverview::GetCSInfoForPlayer(MapPlayer_t *player)
  450. {
  451. if( player == NULL )
  452. return NULL;
  453. for( int i = 0; i < MAX_PLAYERS; i++ )
  454. {
  455. if( &m_Players[i] == player )
  456. return &m_PlayersCSInfo[i];
  457. }
  458. return NULL;
  459. }
  460. //-----------------------------------------------------------------------------
  461. CCSMapOverview::CSMapPlayer_t* CCSMapOverview::GetCSInfoForHostage(MapPlayer_t *hostage)
  462. {
  463. if( hostage == NULL )
  464. return NULL;
  465. for( int i = 0; i < MAX_HOSTAGES; i++ )
  466. {
  467. if( &m_Hostages[i] == hostage )
  468. return &m_HostagesCSInfo[i];
  469. }
  470. return NULL;
  471. }
  472. //-----------------------------------------------------------------------------
  473. #define TIME_SPOTS_STAY_SEEN (0.5f)
  474. #define TIME_UNTIL_ENEMY_SEEN (0.5f)
  475. // rules that define if you can see a player on the overview or not
  476. bool CCSMapOverview::CanPlayerBeSeen( MapPlayer_t *player )
  477. {
  478. C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer();
  479. if (!localPlayer || !player )
  480. return false;
  481. CSMapPlayer_t *csPlayer = GetCSInfoForPlayer(player);
  482. if ( !csPlayer )
  483. return false;
  484. if( GetMode() == MAP_MODE_RADAR )
  485. {
  486. // This level will be for all the RadarMode thinking. Base class will be the old way for the other modes.
  487. float now = gpGlobals->curtime;
  488. if( player->position == Vector(0,0,0) )
  489. return false; // Invalid guy.
  490. // draw special icons if within time
  491. if ( csPlayer->overrideExpirationTime != -1 && csPlayer->overrideExpirationTime > gpGlobals->curtime )
  492. return true;
  493. // otherwise, not dead people
  494. if( player->health <= 0 )
  495. return false;
  496. if( localPlayer->GetTeamNumber() == player->team )
  497. return true;// always yes for teammates.
  498. // and a living enemy needs to have been seen recently, and have been for a while
  499. if( csPlayer->timeLastSeen != -1
  500. && ( now - csPlayer->timeLastSeen < TIME_SPOTS_STAY_SEEN )
  501. && ( now - csPlayer->timeFirstSeen > TIME_UNTIL_ENEMY_SEEN )
  502. )
  503. return true;
  504. return false;
  505. }
  506. else if( player->health <= 0 )
  507. {
  508. // Have to be under the overriden icon time to draw when dead.
  509. if ( csPlayer->overrideExpirationTime == -1 || csPlayer->overrideExpirationTime <= gpGlobals->curtime )
  510. return false;
  511. }
  512. return BaseClass::CanPlayerBeSeen(player);
  513. }
  514. bool CCSMapOverview::CanHostageBeSeen( MapPlayer_t *hostage )
  515. {
  516. C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer();
  517. if ( !localPlayer || !hostage )
  518. return false;
  519. CSMapPlayer_t *csHostage = GetCSInfoForHostage(hostage);
  520. if ( !csHostage )
  521. return false;
  522. if( GetMode() == MAP_MODE_RADAR )
  523. {
  524. // This level will be for all the RadarMode thinking. Base class will be the old way for the other modes.
  525. float now = gpGlobals->curtime;
  526. if( hostage->position == Vector(0,0,0) )
  527. return false; // Invalid guy.
  528. // draw special icons if within time
  529. if ( csHostage->overrideExpirationTime != -1 && csHostage->overrideExpirationTime > gpGlobals->curtime )
  530. return true;
  531. // otherwise, not dead people
  532. if( hostage->health <= 0 )
  533. return false;
  534. if( localPlayer->GetTeamNumber() == hostage->team )
  535. return true;// always yes for teammates.
  536. // and a living enemy needs to have been seen recently
  537. if( csHostage->timeLastSeen != -1 && now - csHostage->timeLastSeen < TIME_SPOTS_STAY_SEEN )
  538. return true;
  539. return false;
  540. }
  541. else if( hostage->health <= 0 )
  542. {
  543. // Have to be under the overriden icon time to draw when dead.
  544. if ( csHostage->overrideExpirationTime == -1 || csHostage->overrideExpirationTime <= gpGlobals->curtime )
  545. return false;
  546. }
  547. return BaseClass::CanPlayerBeSeen(hostage);
  548. }
  549. CCSMapOverview::CCSMapOverview( const char *pElementName ) : BaseClass( pElementName )
  550. {
  551. m_nRadarMapTextureID = -1;
  552. g_pMapOverview = this; // for cvars access etc
  553. // restore non-radar modes
  554. switch ( overview_preferred_mode.GetInt() )
  555. {
  556. case MAP_MODE_INSET:
  557. m_playerPreferredMode = MAP_MODE_INSET;
  558. break;
  559. case MAP_MODE_FULL:
  560. m_playerPreferredMode = MAP_MODE_FULL;
  561. break;
  562. default:
  563. m_playerPreferredMode = MAP_MODE_OFF;
  564. break;
  565. }
  566. }
  567. void CCSMapOverview::Init( void )
  568. {
  569. BaseClass::Init();
  570. // register for events as client listener
  571. ListenForGameEvent( "hostage_killed" );
  572. ListenForGameEvent( "hostage_rescued" );
  573. ListenForGameEvent( "bomb_defused" );
  574. ListenForGameEvent( "bomb_exploded" );
  575. }
  576. CCSMapOverview::~CCSMapOverview()
  577. {
  578. g_pMapOverview = NULL;
  579. //TODO release Textures ? clear lists
  580. }
  581. void CCSMapOverview::UpdatePlayers()
  582. {
  583. if( !m_goalIconsLoaded )
  584. UpdateGoalIcons();
  585. UpdateHostages();// Update before players so players can spot them
  586. UpdateBomb();// Before players so player can properly spot where it is in this update
  587. UpdateFlashes();
  588. C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources();
  589. if ( !pCSPR )
  590. return;
  591. float now = gpGlobals->curtime;
  592. CBasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer();
  593. if( localPlayer == NULL )
  594. return;
  595. MapPlayer_t *localMapPlayer = GetPlayerByUserID(localPlayer->GetUserID());
  596. if( localMapPlayer == NULL )
  597. return;
  598. for ( int i = 1; i<= gpGlobals->maxClients; i++)
  599. {
  600. MapPlayer_t *player = &m_Players[i-1];
  601. CSMapPlayer_t *playerCS = GetCSInfoForPlayerIndex(i-1);
  602. if ( !playerCS )
  603. continue;
  604. // update from global player resources
  605. if ( pCSPR->IsConnected(i) )
  606. {
  607. player->health = pCSPR->GetHealth( i );
  608. if ( !pCSPR->IsAlive( i ) )
  609. {
  610. // Safety actually happens after a TKPunish.
  611. player->health = 0;
  612. playerCS->isDead = true;
  613. }
  614. if ( player->team != pCSPR->GetTeam( i ) )
  615. {
  616. player->team = pCSPR->GetTeam( i );
  617. if( player == localMapPlayer )
  618. player->icon = m_TeamIconsSelf[ GetIconNumberFromTeamNumber(player->team) ];
  619. else
  620. player->icon = m_TeamIcons[ GetIconNumberFromTeamNumber(player->team) ];
  621. player->color = m_TeamColors[ GetIconNumberFromTeamNumber(player->team) ];
  622. }
  623. }
  624. Vector position = player->position;
  625. QAngle angles = player->angle;
  626. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  627. if ( pPlayer && !pPlayer->IsDormant() )
  628. {
  629. // update position of active players in our PVS
  630. position = pPlayer->EyePosition();
  631. angles = pPlayer->EyeAngles();
  632. SetPlayerPositions( i-1, position, angles );
  633. }
  634. }
  635. if ( GetMode() == MAP_MODE_RADAR )
  636. {
  637. // Check for teammates spotting the bomb
  638. if ( m_bomb.state != CSMapBomb_t::BOMB_INVALID && pCSPR->IsBombSpotted() )
  639. {
  640. SetBombSeen( true );
  641. }
  642. // Check for teammates spotting enemy players
  643. for ( int i = 1; i<= gpGlobals->maxClients; ++i )
  644. {
  645. if ( !pCSPR->IsConnected(i) )
  646. continue;
  647. if ( !pCSPR->IsAlive(i) )
  648. continue;
  649. if ( pCSPR->GetTeam(i) == localMapPlayer->team )
  650. continue;
  651. if ( pCSPR->IsPlayerSpotted(i) )
  652. {
  653. SetPlayerSeen( i-1 );
  654. MapPlayer_t *baseEnemy = &m_Players[i-1];
  655. if( baseEnemy->health > 0 )
  656. {
  657. // They were just seen, so if they are alive get rid of their "last known" icon
  658. CSMapPlayer_t *csEnemy = GetCSInfoForPlayerIndex(i-1);
  659. if ( csEnemy )
  660. {
  661. csEnemy->overrideIcon = -1;
  662. csEnemy->overrideIconOffscreen = -1;
  663. csEnemy->overridePosition = Vector(0, 0, 0);
  664. csEnemy->overrideAngle = QAngle(0, 0, 0);
  665. csEnemy->overrideFadeTime = -1;
  666. csEnemy->overrideExpirationTime = -1;
  667. }
  668. }
  669. }
  670. }
  671. for( int i = 1; i<= gpGlobals->maxClients; i++ )
  672. {
  673. MapPlayer_t *player = &m_Players[i-1];
  674. CSMapPlayer_t *playerCS = GetCSInfoForPlayerIndex(i-1);
  675. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  676. if ( !pPlayer || !playerCS )
  677. continue;
  678. float timeSinceLastSeen = now - playerCS->timeLastSeen;
  679. if( timeSinceLastSeen < 0.25f )
  680. continue;
  681. if( player->health <= 0 )
  682. continue;// We don't need to spot dead guys, since they always show
  683. if( player->team == localMapPlayer->team )
  684. continue;// We don't need to spot our own guys
  685. // Now that everyone has had a say on people they can see for us, go through and handle baddies that can no longer be seen.
  686. if( playerCS->timeLastSeen != now && player->health > 0 )
  687. {
  688. // We are not seen now, but if we were seen recently (and for long enough),
  689. // put up a "last known" icon and clear timelastseen
  690. // if they are alive. Death icon is more important, which is why the health check above.
  691. if( timeSinceLastSeen < 0.5f && ( playerCS->timeLastSeen != -1 ) )
  692. {
  693. if( now - playerCS->timeFirstSeen > TIME_UNTIL_ENEMY_SEEN )
  694. {
  695. playerCS->overrideIcon = m_TeamIcons[ GetIconNumberFromTeamNumber(player->team) ];;
  696. playerCS->overrideIconOffscreen = m_TeamIconsOffscreen[ GetIconNumberFromTeamNumber(player->team) ];
  697. playerCS->overridePosition = player->position;
  698. playerCS->overrideFadeTime = -1;
  699. playerCS->overrideExpirationTime = now + LAST_SEEN_ICON_DURATION;
  700. playerCS->overrideAngle = player->angle;
  701. }
  702. playerCS->timeLastSeen = -1;
  703. playerCS->timeFirstSeen = -1;
  704. }
  705. }
  706. }
  707. }
  708. }
  709. void CCSMapOverview::UpdateHostages()
  710. {
  711. C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources();
  712. if ( !pCSPR )
  713. return;
  714. for( int i=0; i < MAX_HOSTAGES; i++ )
  715. {
  716. if( pCSPR->IsHostageAlive( i ) )
  717. {
  718. MapPlayer_t *hostage = GetHostageByEntityID( pCSPR->GetHostageEntityID(i) );
  719. if( hostage == NULL )
  720. hostage = &m_Hostages[i];// Don't have entry yet, so need one. This'll only happen once, at start of map
  721. CSMapPlayer_t *hostageCS = GetCSInfoForHostage(hostage);
  722. if ( !hostageCS )
  723. return;
  724. if( !hostageCS->isDead )
  725. {
  726. hostage->index = pCSPR->GetHostageEntityID(i);
  727. hostage->position = pCSPR->GetHostagePosition( i );
  728. hostage->health = 100; // Hostages don't have health available from pCSPR.
  729. hostage->angle = QAngle(0, 0, 0); // No facing, like no health
  730. hostage->team = TEAM_CT; // CT in terms of who sees them
  731. hostage->icon = m_TeamIcons[ MAP_ICON_HOSTAGE ]; // But hostage for icon.
  732. hostage->color = m_TeamColors[ MAP_ICON_HOSTAGE ];
  733. hostageCS->isHostage = true;
  734. // engine->Con_NPrintf( i + 15, "ID:%d Pos:(%.0f,%.0f,%.0f)", hostage->index, hostage->position.x, hostage->position.y, hostage->position.z );
  735. }
  736. else
  737. {
  738. // engine->Con_NPrintf( i + 15, "Mostly Dead" );
  739. }
  740. }
  741. else
  742. {
  743. // engine->Con_NPrintf( i + 15, "Dead" );
  744. }
  745. }
  746. }
  747. void CCSMapOverview::UpdateBomb()
  748. {
  749. if( m_bomb.state == CSMapBomb_s::BOMB_GONE )
  750. return;// no more updates until map restart
  751. float now = gpGlobals->curtime;
  752. // First, decide if it has been too long since the bomb has been seen to clear visibility timers.
  753. if( now - m_bomb.timeLastSeen >= TIME_SPOTS_STAY_SEEN && m_bomb.timeFirstSeen != -1 )
  754. {
  755. SetBombSeen( false );
  756. }
  757. C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources();
  758. if ( !pCSPR )
  759. return;
  760. float biggestRadius = 0, smallestRadius = 0;
  761. if ( g_PlantedC4s.Count() > 0 )
  762. {
  763. // bomb is planted
  764. C_PlantedC4 *pC4 = g_PlantedC4s[0];
  765. if( pC4->IsBombActive() )
  766. {
  767. m_bomb.position = pC4->GetAbsOrigin();
  768. m_bomb.state = CSMapBomb_t::BOMB_PLANTED;
  769. m_bomb.ringTravelTime = 3.0f;
  770. smallestRadius = m_flIconSize;
  771. biggestRadius = m_flIconSize * 15.0f;
  772. }
  773. else
  774. {
  775. // Defused or exploded
  776. m_bomb.state = CSMapBomb_t::BOMB_GONE;
  777. }
  778. }
  779. else if ( pCSPR->HasC4( 0 ) )
  780. {
  781. // bomb dropped
  782. Vector pos = pCSPR->GetC4Postion();
  783. if ( pos.x != 0 || pos.y != 0 || pos.z != 0 )
  784. {
  785. m_bomb.position = pos;
  786. m_bomb.state = CSMapBomb_t::BOMB_DROPPED;
  787. m_bomb.ringTravelTime = 6.0f;
  788. smallestRadius = m_flIconSize;
  789. biggestRadius = m_flIconSize * 10.0f;
  790. }
  791. else
  792. {
  793. m_bomb.state = CSMapBomb_t::BOMB_INVALID;
  794. //Not a bomb map. Man, what a weird system instead of IsBombMap. If nobody has it, and it isn't on the ground, then it isn't a bomb map.
  795. }
  796. }
  797. else
  798. {
  799. for( int i = 1; i<= gpGlobals->maxClients; i++ )
  800. {
  801. if( pCSPR->HasC4(i) )
  802. {
  803. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  804. if( pPlayer == NULL || pPlayer->IsDormant() )
  805. {
  806. // Dormant or no player means we are relying on RadarUpdate messages so we can trust the MapOverview position.
  807. MapPlayer_t *player = &m_Players[i-1];
  808. m_bomb.position = player->position;
  809. }
  810. else
  811. {
  812. // Update players is about to put this Real Data in the player sturct, and we don't want the bomb pos lagged one update behind
  813. m_bomb.position = pPlayer->GetAbsOrigin();
  814. }
  815. m_bomb.state = CSMapBomb_t::BOMB_CARRIED;
  816. m_bomb.ringTravelTime = 0;
  817. smallestRadius = m_flIconSize * 1.2f;
  818. biggestRadius = m_flIconSize * 1.2f;
  819. break;
  820. }
  821. }
  822. }
  823. int alpha = GetMasterAlpha();
  824. if( m_bomb.currentRingRadius == m_bomb.maxRingRadius || m_bomb.ringTravelTime == 0 )
  825. {
  826. m_bomb.currentRingRadius = smallestRadius;
  827. m_bomb.maxRingRadius = biggestRadius;
  828. m_bomb.currentRingAlpha = alpha;
  829. }
  830. else
  831. {
  832. m_bomb.currentRingRadius += (m_bomb.maxRingRadius - m_flIconSize) * gpGlobals->frametime / m_bomb.ringTravelTime;
  833. m_bomb.currentRingRadius = MIN( m_bomb.currentRingRadius, m_bomb.maxRingRadius );
  834. m_bomb.currentRingAlpha = (alpha - 55) * ((m_bomb.maxRingRadius - m_bomb.currentRingRadius) / (m_bomb.maxRingRadius - m_flIconSize)) + 55;
  835. }
  836. }
  837. bool CCSMapOverview::ShouldDraw( void )
  838. {
  839. int alpha = GetMasterAlpha();
  840. if( alpha == 0 )
  841. return false;// we have been set to fully transparent
  842. //=============================================================================
  843. // HPE_BEGIN:
  844. // [smessick] Turn off large map display when in freezecam.
  845. //=============================================================================
  846. if ( IsInFreezeCam() && GetMode() == MAP_MODE_FULL )
  847. {
  848. return false;
  849. }
  850. //=============================================================================
  851. // HPE_END
  852. //=============================================================================
  853. float now = gpGlobals->curtime;
  854. if( GetMode() == MAP_MODE_RADAR )
  855. {
  856. if ( (GET_HUDELEMENT( CHudRadar ))->ShouldDraw() == false )
  857. {
  858. return false;
  859. }
  860. // We have to be alive and not blind to draw in this mode.
  861. C_CSPlayer *pCSPlayer = C_CSPlayer::GetLocalCSPlayer();
  862. if( !pCSPlayer || pCSPlayer->GetObserverMode() == OBS_MODE_DEATHCAM )
  863. {
  864. return false;
  865. }
  866. else if (pCSPlayer->m_flFlashBangTime > now)
  867. {
  868. return false;
  869. }
  870. }
  871. return BaseClass::ShouldDraw();
  872. }
  873. CCSMapOverview::MapPlayer_t* CCSMapOverview::GetHostageByEntityID( int entityID )
  874. {
  875. for (int i=0; i<MAX_HOSTAGES; i++)
  876. {
  877. if ( m_Hostages[i].index == entityID )
  878. return &m_Hostages[i];
  879. }
  880. return NULL;
  881. }
  882. CCSMapOverview::MapPlayer_t* CCSMapOverview::GetPlayerByEntityID( int entityID )
  883. {
  884. C_BasePlayer *realPlayer = UTIL_PlayerByIndex(entityID);
  885. if( realPlayer == NULL )
  886. return NULL;
  887. for (int i=0; i<MAX_PLAYERS; i++)
  888. {
  889. MapPlayer_t *player = &m_Players[i];
  890. if ( player->userid == realPlayer->GetUserID() )
  891. return player;
  892. }
  893. return NULL;
  894. }
  895. #define BORDER_WIDTH 4
  896. bool CCSMapOverview::AdjustPointToPanel(Vector2D *pos)
  897. {
  898. if( pos == NULL )
  899. return false;
  900. int mapInset = GetBorderSize();// This gives us the amount inside the panel that the background is drawing.
  901. if( mapInset != 0 )
  902. mapInset += BORDER_WIDTH; // And this gives us the border inside the map edge to give us room for offscreen icons.
  903. int x,y,w,t;
  904. //MapTpPanel has already offset the x and y. That's why we use 0 for left and top.
  905. GetBounds( x,y,w,t );
  906. bool madeChange = false;
  907. if( pos->x < mapInset )
  908. {
  909. pos->x = mapInset;
  910. madeChange = true;
  911. }
  912. if( pos->x > w - mapInset )
  913. {
  914. pos->x = w - mapInset;
  915. madeChange = true;
  916. }
  917. if( pos->y < mapInset )
  918. {
  919. pos->y = mapInset;
  920. madeChange = true;
  921. }
  922. if( pos->y > t - mapInset )
  923. {
  924. pos->y = t - mapInset;
  925. madeChange = true;
  926. }
  927. return madeChange;
  928. }
  929. void CCSMapOverview::DrawMapTexture()
  930. {
  931. int alpha = GetMasterAlpha();
  932. if( GetMode() == MAP_MODE_FULL )
  933. SetBgColor( Color(0,0,0,0) );// no background in big mode
  934. else
  935. SetBgColor( Color(0,0,0,alpha * 0.5) );
  936. int textureIDToUse = m_nMapTextureID;
  937. bool foundRadarVersion = false;
  938. if( m_nRadarMapTextureID != -1 && GetMode() == MAP_MODE_RADAR )
  939. {
  940. textureIDToUse = m_nRadarMapTextureID;
  941. foundRadarVersion = true;
  942. }
  943. int mapInset = GetBorderSize();
  944. int pwidth, pheight;
  945. GetSize(pwidth, pheight);
  946. if ( textureIDToUse > 0 )
  947. {
  948. // We are drawing to the whole panel with a little border
  949. Vector2D panelTL = Vector2D( mapInset, mapInset );
  950. Vector2D panelTR = Vector2D( pwidth - mapInset, mapInset );
  951. Vector2D panelBR = Vector2D( pwidth - mapInset, pheight - mapInset );
  952. Vector2D panelBL = Vector2D( mapInset, pheight - mapInset );
  953. // So where are those four points on the great big map?
  954. Vector2D textureTL = PanelToMap( panelTL );// The top left corner of the display is where on the master map?
  955. textureTL /= OVERVIEW_MAP_SIZE;// Texture Vec2D is 0 to 1
  956. Vector2D textureTR = PanelToMap( panelTR );
  957. textureTR /= OVERVIEW_MAP_SIZE;
  958. Vector2D textureBR = PanelToMap( panelBR );
  959. textureBR /= OVERVIEW_MAP_SIZE;
  960. Vector2D textureBL = PanelToMap( panelBL );
  961. textureBL /= OVERVIEW_MAP_SIZE;
  962. Vertex_t points[4] =
  963. {
  964. // To draw a textured polygon, the first column is where you want to draw (to), and the second is what you want to draw (from).
  965. // We want to draw to the panel (pulled in for a border), and we want to draw the part of the map texture that should be seen.
  966. // First column is in panel coords, second column is in 0-1 texture coords
  967. Vertex_t( panelTL, textureTL ),
  968. Vertex_t( panelTR, textureTR ),
  969. Vertex_t( panelBR, textureBR ),
  970. Vertex_t( panelBL, textureBL )
  971. };
  972. surface()->DrawSetColor( 255, 255, 255, alpha );
  973. surface()->DrawSetTexture( textureIDToUse );
  974. surface()->DrawTexturedPolygon( 4, points );
  975. }
  976. // If we didn't find the greenscale version of the map, then at least do a tint.
  977. if( !foundRadarVersion && GetMode() == MAP_MODE_RADAR )
  978. {
  979. surface()->DrawSetColor( 0,255,0, alpha / 4 );
  980. surface()->DrawFilledRect( mapInset, mapInset, m_vSize.x - 1 - mapInset, m_vSize.y - 1 - mapInset );
  981. }
  982. }
  983. void CCSMapOverview::DrawBomb()
  984. {
  985. if( m_bomb.state == CSMapBomb_t::BOMB_INVALID )
  986. return;
  987. CBasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer();
  988. if( localPlayer == NULL )
  989. return;
  990. MapPlayer_t *localMapPlayer = GetPlayerByUserID(localPlayer->GetUserID());
  991. if( localMapPlayer == NULL )
  992. return;
  993. float now = gpGlobals->curtime;
  994. if( localMapPlayer->team == TEAM_CT )
  995. {
  996. // CT's only get to see it if...
  997. if( localMapPlayer->health <= 0 )
  998. {
  999. if ( mp_forcecamera.GetInt() != OBS_ALLOW_ALL )
  1000. return;// They're dead and spectating isn't restricted
  1001. }
  1002. else if( (m_bomb.timeLastSeen == -1)
  1003. || ( now - m_bomb.timeLastSeen >= TIME_SPOTS_STAY_SEEN )
  1004. || ( now - m_bomb.timeFirstSeen < TIME_UNTIL_ENEMY_SEEN )
  1005. )
  1006. {
  1007. return;// It's in view
  1008. }
  1009. }
  1010. // else if you aren't CT you can always see it
  1011. int bombIcon;
  1012. int bombRing;
  1013. int bombRingOffscreen;
  1014. switch(m_bomb.state)
  1015. {
  1016. case CSMapBomb_t::BOMB_DROPPED:
  1017. {
  1018. bombIcon = m_bombIconDropped;
  1019. bombRing = m_bombRingDropped;
  1020. bombRingOffscreen = m_bombRingDropped;
  1021. break;
  1022. }
  1023. case CSMapBomb_t::BOMB_CARRIED:
  1024. {
  1025. bombIcon = m_bombIconCarried;
  1026. bombRing = m_bombRingCarried;
  1027. bombRingOffscreen = m_bombRingCarriedOffscreen;
  1028. break;
  1029. }
  1030. case CSMapBomb_t::BOMB_PLANTED:
  1031. {
  1032. bombIcon = m_bombIconPlanted;
  1033. bombRing = m_bombRingPlanted;
  1034. bombRingOffscreen = m_bombRingPlanted;
  1035. break;
  1036. }
  1037. case CSMapBomb_t::BOMB_GONE:
  1038. {
  1039. bombIcon = m_bombIconPlanted;
  1040. bombRing = m_bombRingPlanted;
  1041. bombRingOffscreen = m_bombRingPlanted;
  1042. break;
  1043. }
  1044. default:
  1045. return;
  1046. }
  1047. int alpha = 255;
  1048. if( m_bomb.timeGone != -1 && m_bomb.timeFade <= gpGlobals->curtime )
  1049. alpha *= 1 - ( (float)(gpGlobals->curtime - m_bomb.timeFade) / (float)(m_bomb.timeGone - m_bomb.timeFade) );
  1050. if( m_bomb.state != CSMapBomb_t::BOMB_GONE )
  1051. DrawIconCS(bombRing, bombRingOffscreen, m_bomb.position, m_bomb.currentRingRadius, 0, m_bomb.currentRingAlpha);
  1052. DrawIconCS(bombIcon, bombIcon, m_bomb.position, m_flIconSize, 0, alpha);
  1053. }
  1054. bool CCSMapOverview::DrawIconCS( int textureID, int offscreenTextureID, Vector pos, float scale, float angle, int alpha, bool allowRotation, const char *text, Color *textColor, float status, Color *statusColor )
  1055. {
  1056. if( GetMode() == MAP_MODE_RADAR && cl_radaralpha.GetInt() == 0 )
  1057. return false;
  1058. if( alpha <= 0 )
  1059. return false;
  1060. Vector2D pospanel = WorldToMap( pos );
  1061. pospanel = MapToPanel( pospanel );
  1062. int idToUse = textureID;
  1063. float angleToUse = angle;
  1064. Vector2D oldPos = pospanel;
  1065. Vector2D adjustment(0,0);
  1066. if( AdjustPointToPanel( &pospanel ) )
  1067. {
  1068. if( offscreenTextureID == -1 )
  1069. return false; //Doesn't want to draw if off screen.
  1070. // Move it in to on panel, and change the icon.
  1071. idToUse = offscreenTextureID;
  1072. // And point towards the original spot
  1073. adjustment = Vector2D(pospanel.x - oldPos.x, pospanel.y - oldPos.y);
  1074. QAngle adjustmentAngles;
  1075. Vector adjustment3D(adjustment.x, -adjustment.y, 0); // Y gets flipped in WorldToMap
  1076. VectorAngles(adjustment3D, adjustmentAngles) ;
  1077. if( allowRotation )
  1078. {
  1079. // Some icons don't want to rotate even when off radar
  1080. angleToUse = adjustmentAngles[YAW];
  1081. // And the angle needs to be in world space, not panel space.
  1082. if( m_bFollowAngle )
  1083. {
  1084. angleToUse += m_fViewAngle;
  1085. }
  1086. else
  1087. {
  1088. if ( m_bRotateMap )
  1089. angleToUse += 180.0f;
  1090. else
  1091. angleToUse += 90.0f;
  1092. }
  1093. }
  1094. // Don't draw names for icons that are offscreen (bunches up and looks bad)
  1095. text = NULL;
  1096. }
  1097. int d = GetPixelOffset( scale );
  1098. Vector offset;
  1099. offset.x = -scale; offset.y = scale;
  1100. VectorYawRotate( offset, angleToUse, offset );
  1101. Vector2D pos1 = WorldToMap( pos + offset );
  1102. Vector2D pos1Panel = MapToPanel(pos1);
  1103. pos1Panel.x += adjustment.x;
  1104. pos1Panel.y += adjustment.y;
  1105. offset.x = scale; offset.y = scale;
  1106. VectorYawRotate( offset, angleToUse, offset );
  1107. Vector2D pos2 = WorldToMap( pos + offset );
  1108. Vector2D pos2Panel = MapToPanel(pos2);
  1109. pos2Panel.x += adjustment.x;
  1110. pos2Panel.y += adjustment.y;
  1111. offset.x = scale; offset.y = -scale;
  1112. VectorYawRotate( offset, angleToUse, offset );
  1113. Vector2D pos3 = WorldToMap( pos + offset );
  1114. Vector2D pos3Panel = MapToPanel(pos3);
  1115. pos3Panel.x += adjustment.x;
  1116. pos3Panel.y += adjustment.y;
  1117. offset.x = -scale; offset.y = -scale;
  1118. VectorYawRotate( offset, angleToUse, offset );
  1119. Vector2D pos4 = WorldToMap( pos + offset );
  1120. Vector2D pos4Panel = MapToPanel(pos4);
  1121. pos4Panel.x += adjustment.x;
  1122. pos4Panel.y += adjustment.y;
  1123. Vertex_t points[4] =
  1124. {
  1125. Vertex_t( pos1Panel, Vector2D(0,0) ),
  1126. Vertex_t( pos2Panel, Vector2D(1,0) ),
  1127. Vertex_t( pos3Panel, Vector2D(1,1) ),
  1128. Vertex_t( pos4Panel, Vector2D(0,1) )
  1129. };
  1130. surface()->DrawSetColor( 255, 255, 255, alpha );
  1131. surface()->DrawSetTexture( idToUse );
  1132. surface()->DrawTexturedPolygon( 4, points );
  1133. pospanel.y += d + 4;
  1134. if ( status >=0.0f && status <= 1.0f && statusColor )
  1135. {
  1136. // health bar is 50x3 pixels
  1137. surface()->DrawSetColor( 0,0,0,255 );
  1138. surface()->DrawFilledRect( pospanel.x-d, pospanel.y-1, pospanel.x+d, pospanel.y+1 );
  1139. int length = (float)(d*2)*status;
  1140. surface()->DrawSetColor( statusColor->r(), statusColor->g(), statusColor->b(), 255 );
  1141. surface()->DrawFilledRect( pospanel.x-d, pospanel.y-1, pospanel.x-d+length, pospanel.y+1 );
  1142. pospanel.y += 3;
  1143. }
  1144. if ( text && textColor )
  1145. {
  1146. wchar_t iconText[ MAX_PLAYER_NAME_LENGTH*2 ];
  1147. g_pVGuiLocalize->ConvertANSIToUnicode( text, iconText, sizeof( iconText ) );
  1148. int wide, tall;
  1149. surface()->GetTextSize( m_hIconFont, iconText, wide, tall );
  1150. int x = pospanel.x-(wide/2);
  1151. int y = pospanel.y;
  1152. // draw black shadow text
  1153. surface()->DrawSetTextColor( 0, 0, 0, 255 );
  1154. surface()->DrawSetTextPos( x+1, y );
  1155. surface()->DrawPrintText( iconText, wcslen(iconText) );
  1156. // draw name in color
  1157. surface()->DrawSetTextColor( textColor->r(), textColor->g(), textColor->b(), 255 );
  1158. surface()->DrawSetTextPos( x, y );
  1159. surface()->DrawPrintText( iconText, wcslen(iconText) );
  1160. }
  1161. return true;
  1162. }
  1163. void CCSMapOverview::DrawMapPlayers()
  1164. {
  1165. DrawGoalIcons();
  1166. DrawHostages();
  1167. C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources();
  1168. surface()->DrawSetTextFont( m_hIconFont );
  1169. Color colorGreen( 0, 255, 0, 255 ); // health bar color
  1170. CBasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer();
  1171. for (int i=0; i < MAX_PLAYERS; i++)
  1172. {
  1173. int alpha = 255;
  1174. MapPlayer_t *player = &m_Players[i];
  1175. CSMapPlayer_t *playerCS = GetCSInfoForPlayerIndex(i);
  1176. if ( !playerCS )
  1177. continue;
  1178. if ( !CanPlayerBeSeen( player ) )
  1179. continue;
  1180. float status = -1;
  1181. const char *name = NULL;
  1182. if ( m_bShowNames && CanPlayerNameBeSeen( player ) )
  1183. name = player->name;
  1184. if ( m_bShowHealth && CanPlayerHealthBeSeen( player ) )
  1185. status = player->health/100.0f;
  1186. // Now draw them
  1187. if( playerCS->overrideExpirationTime > gpGlobals->curtime )// If dead, an X, if alive, an alpha'd normal icon
  1188. {
  1189. int alphaToUse = alpha;
  1190. if( playerCS->overrideFadeTime != -1 && playerCS->overrideFadeTime <= gpGlobals->curtime )
  1191. {
  1192. // Fade linearly from fade start to disappear
  1193. alphaToUse *= 1 - (float)(gpGlobals->curtime - playerCS->overrideFadeTime) / (float)(playerCS->overrideExpirationTime - playerCS->overrideFadeTime);
  1194. }
  1195. DrawIconCS( playerCS->overrideIcon, playerCS->overrideIconOffscreen, playerCS->overridePosition, m_flIconSize * 1.1f, GetViewAngle(), player->health > 0 ? alphaToUse / 2 : alphaToUse, true, name, &player->color, -1, &colorGreen );
  1196. if( player->health > 0 )
  1197. DrawIconCS( m_playerFacing, -1, playerCS->overridePosition, m_flIconSize * 1.1f, playerCS->overrideAngle[YAW], player->health > 0 ? alphaToUse / 2 : alphaToUse, true, name, &player->color, status, &colorGreen );
  1198. }
  1199. else
  1200. {
  1201. float zDifference = 0;
  1202. if( localPlayer )
  1203. {
  1204. if( (localPlayer->GetObserverMode() != OBS_MODE_NONE) && localPlayer->GetObserverTarget() )
  1205. zDifference = player->position.z - localPlayer->GetObserverTarget()->GetAbsOrigin().z;
  1206. else
  1207. zDifference = player->position.z - localPlayer->GetAbsOrigin().z;
  1208. }
  1209. float sizeForRing = m_flIconSize * 1.4f;
  1210. float sizeForPlayer = m_flIconSize * 1.1f; // The 1.1 is because the player dots are shrunken a little, so their facing pip can have some space to live
  1211. if ( zDifference > DIFFERENCE_THRESHOLD )
  1212. {
  1213. // A dot above is bigger and a little fuzzy now.
  1214. sizeForRing *= 1.4f;
  1215. sizeForPlayer *= 1.4f;
  1216. alpha *= 0.5f;
  1217. }
  1218. else if ( zDifference < -DIFFERENCE_THRESHOLD )
  1219. {
  1220. // A dot below is smaller.
  1221. sizeForRing *= 0.7f;
  1222. sizeForPlayer *= 0.7f;
  1223. }
  1224. bool showTalkRing = localPlayer && (localPlayer->GetTeamNumber() == player->team || localPlayer->GetTeamNumber() == TEAM_SPECTATOR);
  1225. if( showTalkRing && playerCS->currentFlashAlpha > 0 )// Flash type
  1226. {
  1227. // Make them flash a halo
  1228. DrawIconCS(m_radioFlash, m_radioFlashOffscreen, player->position, sizeForRing, player->angle[YAW], playerCS->currentFlashAlpha);
  1229. }
  1230. else if( showTalkRing && pCSPR->IsAlive( i + 1 ) && GetClientVoiceMgr()->IsPlayerSpeaking( i + 1) ) // Or solid on type
  1231. {
  1232. // Make them show a halo
  1233. DrawIconCS(m_radioFlash, m_radioFlashOffscreen, player->position, sizeForRing, player->angle[YAW], 255);
  1234. }
  1235. bool doingLocalPlayer = GetPlayerByUserID(localPlayer->GetUserID()) == player;
  1236. float angleForPlayer = GetViewAngle();
  1237. if( doingLocalPlayer )
  1238. {
  1239. sizeForPlayer *= 4.0f; // The self icon is really big since it has a camera view cone attached.
  1240. angleForPlayer = player->angle[YAW];// And, the self icon now rotates, natch.
  1241. }
  1242. int offscreenIcon = m_TeamIconsOffscreen[GetIconNumberFromTeamNumber(player->team)];
  1243. DrawIconCS( player->icon, offscreenIcon, player->position, sizeForPlayer, angleForPlayer, alpha, true, name, &player->color, status, &colorGreen );
  1244. if( !doingLocalPlayer )
  1245. {
  1246. // Draw the facing for everyone but the local player.
  1247. if( player->health > 0 )
  1248. DrawIconCS( m_playerFacing, -1, player->position, sizeForPlayer, player->angle[YAW], alpha, true, name, &player->color, status, &colorGreen );
  1249. }
  1250. }
  1251. }
  1252. DrawBomb();// After players so it can draw on top
  1253. }
  1254. void CCSMapOverview::DrawHostages()
  1255. {
  1256. C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources();
  1257. if ( !pCSPR )
  1258. return;
  1259. surface()->DrawSetTextFont( m_hIconFont );
  1260. Color colorGreen( 0, 255, 0, 255 ); // health bar color
  1261. CBasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer();
  1262. for (int i=0; i < MAX_HOSTAGES; i++)
  1263. {
  1264. int alpha = 255;
  1265. MapPlayer_t *hostage = GetHostageByEntityID( pCSPR->GetHostageEntityID(i) );
  1266. if( hostage == NULL )
  1267. continue;
  1268. CSMapPlayer_t *hostageCS = GetCSInfoForHostage(hostage);
  1269. if ( !hostageCS )
  1270. continue;
  1271. if ( !CanHostageBeSeen( hostage ) )
  1272. {
  1273. // engine->Con_NPrintf( i + 30, "Can't be seen." );
  1274. continue;
  1275. }
  1276. float status = -1;
  1277. const char *name = NULL;
  1278. if( hostageCS->overrideExpirationTime > gpGlobals->curtime )// If dead, an X, if alive, an alpha'd normal icon
  1279. {
  1280. // engine->Con_NPrintf( i + 30, "ID:%d Override Pos:(%.0f,%.0f,%.0f)", hostage->index, hostageCS->overridePosition.x, hostageCS->overridePosition.y, hostageCS->overridePosition.z );
  1281. int alphaToUse = alpha;
  1282. if( hostageCS->overrideFadeTime != -1 && hostageCS->overrideFadeTime <= gpGlobals->curtime )
  1283. {
  1284. // Fade linearly from fade start to disappear
  1285. alphaToUse *= 1 - (float)(gpGlobals->curtime - hostageCS->overrideFadeTime) / (float)(hostageCS->overrideExpirationTime - hostageCS->overrideFadeTime);
  1286. }
  1287. DrawIconCS( hostageCS->overrideIcon, hostageCS->overrideIconOffscreen, hostageCS->overridePosition, m_flIconSize, hostageCS->overrideAngle[YAW], hostage->health > 0 ? alphaToUse / 2 : alphaToUse, true, name, &hostage->color, status, &colorGreen );
  1288. }
  1289. else
  1290. {
  1291. if( localPlayer && localPlayer->GetTeamNumber() == hostage->team && hostageCS->currentFlashAlpha > 0 )
  1292. {
  1293. // Make them flash a halo
  1294. DrawIconCS(m_radioFlash, m_radioFlashOffscreen, hostage->position, m_flIconSize * 1.4f, hostage->angle[YAW], hostageCS->currentFlashAlpha);
  1295. }
  1296. // engine->Con_NPrintf( i + 30, "ID:%d Pos:(%.0f,%.0f,%.0f)", hostage->index, hostage->position.x, hostage->position.y, hostage->position.z );
  1297. int normalIcon, offscreenIcon;
  1298. float zDifference = 0;
  1299. if( localPlayer )
  1300. {
  1301. if( (localPlayer->GetObserverMode() != OBS_MODE_NONE) && localPlayer->GetObserverTarget() )
  1302. zDifference = hostage->position.z - localPlayer->GetObserverTarget()->GetAbsOrigin().z;
  1303. else
  1304. zDifference = hostage->position.z - localPlayer->GetAbsOrigin().z;
  1305. }
  1306. float sizeForHostage = m_flIconSize;
  1307. if( zDifference > DIFFERENCE_THRESHOLD )
  1308. {
  1309. // A dot above is bigger and a little fuzzy now.
  1310. sizeForHostage = m_flIconSize * 1.5f;
  1311. alpha *= 0.5f;
  1312. }
  1313. else if( zDifference < -DIFFERENCE_THRESHOLD )
  1314. {
  1315. // A dot below is smaller.
  1316. sizeForHostage = m_flIconSize * 0.6f;
  1317. }
  1318. normalIcon = hostage->icon;
  1319. offscreenIcon = m_TeamIconsOffscreen[ MAP_ICON_HOSTAGE ];
  1320. DrawIconCS( normalIcon, offscreenIcon, hostage->position, sizeForHostage, GetViewAngle(), alpha, true, name, &hostage->color, status, &colorGreen );
  1321. if( pCSPR->IsHostageFollowingSomeone( i ) )
  1322. {
  1323. // If they are following a CT, then give them a little extra symbol to show it.
  1324. DrawIconCS( m_hostageFollowing, m_hostageFollowingOffscreen, hostage->position, sizeForHostage, hostage->angle[YAW], alpha );
  1325. }
  1326. }
  1327. }
  1328. }
  1329. void CCSMapOverview::SetMap(const char * levelname)
  1330. {
  1331. BaseClass::SetMap(levelname);
  1332. int wide, tall;
  1333. surface()->DrawGetTextureSize( m_nMapTextureID, wide, tall );
  1334. if( wide == 0 && tall == 0 )
  1335. {
  1336. m_nMapTextureID = -1;
  1337. m_nRadarMapTextureID = -1;
  1338. return;// No map image, so no radar image
  1339. }
  1340. char radarFileName[MAX_PATH];
  1341. Q_snprintf(radarFileName, MAX_PATH, "%s_radar", m_MapKeyValues->GetString("material"));
  1342. m_nRadarMapTextureID = surface()->CreateNewTextureID();
  1343. surface()->DrawSetTextureFile(m_nRadarMapTextureID, radarFileName, true, false);
  1344. int radarWide = -1;
  1345. int radarTall = -1;
  1346. surface()->DrawGetTextureSize(m_nRadarMapTextureID, radarWide, radarTall);
  1347. bool radarTextureFound = false;
  1348. if( radarWide == wide && radarTall == tall )
  1349. {
  1350. // Unbelievable that these is no failure return from SetTextureFile, and not
  1351. // even a ValidTexture check on the ID. So I can check if it is different from
  1352. // the original. It'll be a 32x32 default if not present.
  1353. radarTextureFound = true;
  1354. }
  1355. if( !radarTextureFound )
  1356. {
  1357. if( !CreateRadarImage(m_MapKeyValues->GetString("material"), radarFileName) )
  1358. m_nRadarMapTextureID = -1;
  1359. }
  1360. ClearGoalIcons();
  1361. }
  1362. bool CCSMapOverview::CreateRadarImage(const char *mapName, const char * radarFileName)
  1363. {
  1364. #ifdef GENERATE_RADAR_FILE
  1365. char fullFileName[MAX_PATH];
  1366. Q_snprintf(fullFileName, MAX_PATH, "materials/%s.vtf", mapName);
  1367. char fullRadarFileName[MAX_PATH];
  1368. Q_snprintf(fullRadarFileName, MAX_PATH, "materials/%s.vtf", radarFileName);
  1369. // Not found, so try to make one
  1370. FileHandle_t fp;
  1371. fp = ::filesystem->Open( fullFileName, "rb" );
  1372. if( !fp )
  1373. {
  1374. return false;
  1375. }
  1376. ::filesystem->Seek( fp, 0, FILESYSTEM_SEEK_TAIL );
  1377. int srcVTFLength = ::filesystem->Tell( fp );
  1378. ::filesystem->Seek( fp, 0, FILESYSTEM_SEEK_HEAD );
  1379. CUtlBuffer buf;
  1380. buf.EnsureCapacity( srcVTFLength );
  1381. int overviewMapBytesRead = ::filesystem->Read( buf.Base(), srcVTFLength, fp );
  1382. ::filesystem->Close( fp );
  1383. buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );// Need to set these explicitly since ->Read goes straight to memory and skips them.
  1384. buf.SeekPut( CUtlBuffer::SEEK_HEAD, overviewMapBytesRead );
  1385. IVTFTexture *radarTexture = CreateVTFTexture();
  1386. if (radarTexture->Unserialize(buf))
  1387. {
  1388. ImageFormat oldImageFormat = radarTexture->Format();
  1389. radarTexture->ConvertImageFormat(IMAGE_FORMAT_RGBA8888, false);
  1390. unsigned char *imageData = radarTexture->ImageData(0,0,0);
  1391. int size = radarTexture->ComputeTotalSize(); // in bytes!
  1392. unsigned char *pEnd = imageData + size;
  1393. for( ; imageData < pEnd; imageData += 4 )
  1394. {
  1395. imageData[0] = 0; // R
  1396. imageData[2] = 0; // B
  1397. }
  1398. radarTexture->ConvertImageFormat(oldImageFormat, false);
  1399. buf.Clear();
  1400. radarTexture->Serialize(buf);
  1401. fp = ::filesystem->Open(fullRadarFileName, "wb");
  1402. ::filesystem->Write(buf.Base(), buf.TellPut(), fp);
  1403. ::filesystem->Close(fp);
  1404. DestroyVTFTexture(radarTexture);
  1405. buf.Purge();
  1406. // And need a vmt file to go with it.
  1407. char vmtFilename[MAX_PATH];
  1408. Q_snprintf(vmtFilename, MAX_PATH, "%s", fullRadarFileName);
  1409. char *extension = &vmtFilename[Q_strlen(vmtFilename) - 3];
  1410. *extension++ = 'v';
  1411. *extension++ = 'm';
  1412. *extension++ = 't';
  1413. *extension++ = '\0';
  1414. fp = ::filesystem->Open(vmtFilename, "wt");
  1415. ::filesystem->Write("\"UnlitGeneric\"\n", 15, fp);
  1416. ::filesystem->Write("{\n", 2, fp);
  1417. ::filesystem->Write("\t\"$translucent\" \"1\"\n", 20, fp);
  1418. ::filesystem->Write("\t\"$basetexture\" \"", 17, fp);
  1419. ::filesystem->Write(radarFileName, Q_strlen(radarFileName), fp);
  1420. ::filesystem->Write("\"\n", 2, fp);
  1421. ::filesystem->Write("\t\"$vertexalpha\" \"1\"\n", 20, fp);
  1422. ::filesystem->Write("\t\"$vertexcolor\" \"1\"\n", 20, fp);
  1423. ::filesystem->Write("\t\"$no_fullbright\" \"1\"\n", 22, fp);
  1424. ::filesystem->Write("\t\"$ignorez\" \"1\"\n", 16, fp);
  1425. ::filesystem->Write("}\n", 2, fp);
  1426. ::filesystem->Close(fp);
  1427. m_nRadarMapTextureID = surface()->CreateNewTextureID();
  1428. surface()->DrawSetTextureFile( m_nRadarMapTextureID, radarFileName, true, true);
  1429. return true;
  1430. }
  1431. #endif
  1432. return false;
  1433. }
  1434. void CCSMapOverview::ResetRound()
  1435. {
  1436. BaseClass::ResetRound();
  1437. for (int i=0; i<MAX_PLAYERS; i++)
  1438. {
  1439. CSMapPlayer_t *p = &m_PlayersCSInfo[i];
  1440. p->isDead = false;
  1441. p->overrideFadeTime = -1;
  1442. p->overrideExpirationTime = -1;
  1443. p->overrideIcon = -1;
  1444. p->overrideIconOffscreen = -1;
  1445. p->overridePosition = Vector( 0, 0, 0);
  1446. p->overrideAngle = QAngle(0, 0, 0);
  1447. p->timeLastSeen = -1;
  1448. p->timeFirstSeen = -1;
  1449. p->isHostage = false;
  1450. p->flashUntilTime = -1;
  1451. p->nextFlashPeakTime = -1;
  1452. p->currentFlashAlpha = 0;
  1453. }
  1454. for (int i=0; i<MAX_HOSTAGES; i++)
  1455. {
  1456. MapPlayer_t *basep = &m_Hostages[i];
  1457. CSMapPlayer_t *p = &m_HostagesCSInfo[i];
  1458. basep->health = 100;
  1459. Q_memset( basep->trail, 0, sizeof(basep->trail) );
  1460. basep->position = Vector( 0, 0, 0 );
  1461. basep->index = 0;
  1462. p->isDead = false;
  1463. p->overrideFadeTime = -1;
  1464. p->overrideExpirationTime = -1;
  1465. p->overrideIcon = -1;
  1466. p->overrideIconOffscreen = -1;
  1467. p->overridePosition = Vector( 0, 0, 0);
  1468. p->overrideAngle = QAngle(0, 0, 0);
  1469. p->timeLastSeen = -1;
  1470. p->timeFirstSeen = -1;
  1471. p->isHostage = false;
  1472. p->flashUntilTime = -1;
  1473. p->nextFlashPeakTime = -1;
  1474. p->currentFlashAlpha = 0;
  1475. }
  1476. m_bomb.position = Vector(0,0,0);
  1477. m_bomb.state = CSMapBomb_t::BOMB_INVALID;
  1478. m_bomb.timeLastSeen = -1;
  1479. m_bomb.timeFirstSeen = -1;
  1480. m_bomb.timeFade = -1;
  1481. m_bomb.timeGone = -1;
  1482. m_bomb.currentRingRadius = -1;
  1483. m_bomb.currentRingAlpha = -1;
  1484. m_bomb.maxRingRadius = -1;
  1485. m_bomb.ringTravelTime = -1;
  1486. m_goalIconsLoaded = false;
  1487. }
  1488. void CCSMapOverview::DrawCamera()
  1489. {
  1490. C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer();
  1491. if (!localPlayer)
  1492. return;
  1493. if( localPlayer->GetObserverMode() == OBS_MODE_ROAMING )
  1494. {
  1495. // Instead of the programmer-art red dot, we'll draw an icon for when our camera is roaming.
  1496. int alpha = 255;
  1497. DrawIconCS(m_cameraIconFree, m_cameraIconFree, localPlayer->GetAbsOrigin(), m_flIconSize * 3.0f, localPlayer->EyeAngles()[YAW], alpha);
  1498. }
  1499. else if( localPlayer->GetObserverMode() == OBS_MODE_IN_EYE )
  1500. {
  1501. if( localPlayer->GetObserverTarget() )
  1502. {
  1503. // Fade it if it is on top of a player dot. And don't rotate it.
  1504. int alpha = 255 * 0.5f;
  1505. DrawIconCS(m_cameraIconFirst, m_cameraIconFirst, localPlayer->GetObserverTarget()->GetAbsOrigin(), m_flIconSize * 1.5f, GetViewAngle(), alpha);
  1506. }
  1507. }
  1508. else if( localPlayer->GetObserverMode() == OBS_MODE_CHASE )
  1509. {
  1510. if( localPlayer->GetObserverTarget() )
  1511. {
  1512. // Or Draw the third-camera a little bigger. (Needs room to be off the dot being followed)
  1513. int alpha = 255;
  1514. DrawIconCS(m_cameraIconThird, m_cameraIconThird, localPlayer->GetObserverTarget()->GetAbsOrigin(), m_flIconSize * 3.0f, localPlayer->EyeAngles()[YAW], alpha);
  1515. }
  1516. }
  1517. }
  1518. void CCSMapOverview::FireGameEvent( IGameEvent *event )
  1519. {
  1520. const char * type = event->GetName();
  1521. if ( Q_strcmp(type,"hostage_killed") == 0 )
  1522. {
  1523. MapPlayer_t *hostage = GetHostageByEntityID( event->GetInt("hostage") );
  1524. // DevMsg("Hostage id %d just died.\n", event->GetInt("hostage"));
  1525. if ( !hostage )
  1526. return;
  1527. CSMapPlayer_t *hostageCS = GetCSInfoForHostage(hostage);
  1528. if ( !hostageCS )
  1529. return;
  1530. hostage->health = 0;
  1531. hostageCS->isDead = true;
  1532. Q_memset( hostage->trail, 0, sizeof(hostage->trail) ); // clear trails
  1533. hostageCS->overrideIcon = m_TeamIconsDead[MAP_ICON_HOSTAGE];
  1534. hostageCS->overrideIconOffscreen = hostageCS->overrideIcon;
  1535. hostageCS->overridePosition = hostage->position;
  1536. hostageCS->overrideAngle = hostage->angle;
  1537. hostageCS->overrideFadeTime = gpGlobals->curtime + DEATH_ICON_FADE;
  1538. hostageCS->overrideExpirationTime = gpGlobals->curtime + DEATH_ICON_DURATION;
  1539. }
  1540. else if ( Q_strcmp(type,"hostage_rescued") == 0 )
  1541. {
  1542. MapPlayer_t *hostage = GetHostageByEntityID( event->GetInt("hostage") );
  1543. // DevMsg("Hostage id %d just got rescued.\n", event->GetInt("hostage"));
  1544. if ( !hostage )
  1545. return;
  1546. CSMapPlayer_t *hostageCS = GetCSInfoForHostage(hostage);
  1547. if ( !hostageCS )
  1548. return;
  1549. hostage->health = 0;
  1550. hostageCS->isDead = true;
  1551. Q_memset( hostage->trail, 0, sizeof(hostage->trail) ); // clear trails
  1552. hostageCS->overrideIcon = hostage->icon;
  1553. hostageCS->overrideIconOffscreen = -1;
  1554. hostageCS->overridePosition = hostage->position;
  1555. hostageCS->overrideAngle = hostage->angle;
  1556. hostageCS->overrideFadeTime = gpGlobals->curtime;
  1557. hostageCS->overrideExpirationTime = gpGlobals->curtime + HOSTAGE_RESCUE_DURATION;
  1558. }
  1559. else if ( Q_strcmp(type,"bomb_defused") == 0 )
  1560. {
  1561. m_bomb.state = CSMapBomb_t::BOMB_GONE;
  1562. m_bomb.timeFade = gpGlobals->curtime;
  1563. m_bomb.timeGone = gpGlobals->curtime + BOMB_FADE_DURATION;
  1564. }
  1565. else if ( Q_strcmp(type,"bomb_exploded") == 0 )
  1566. {
  1567. m_bomb.state = CSMapBomb_t::BOMB_GONE;
  1568. m_bomb.timeFade = gpGlobals->curtime;
  1569. m_bomb.timeGone = gpGlobals->curtime + BOMB_FADE_DURATION;
  1570. }
  1571. else if ( Q_strcmp(type,"player_death") == 0 )
  1572. {
  1573. MapPlayer_t *player = GetPlayerByUserID( event->GetInt("userid") );
  1574. if ( !player )
  1575. return;
  1576. player->health = 0;
  1577. Q_memset( player->trail, 0, sizeof(player->trail) ); // clear trails
  1578. CSMapPlayer_t *playerCS = GetCSInfoForPlayer(player);
  1579. if ( !playerCS )
  1580. return;
  1581. playerCS->isDead = true;
  1582. playerCS->overrideIcon = m_TeamIconsDead[GetIconNumberFromTeamNumber(player->team)];
  1583. playerCS->overrideIconOffscreen = playerCS->overrideIcon;
  1584. playerCS->overridePosition = player->position;
  1585. playerCS->overrideAngle = player->angle;
  1586. playerCS->overrideFadeTime = gpGlobals->curtime + DEATH_ICON_FADE;
  1587. playerCS->overrideExpirationTime = gpGlobals->curtime + DEATH_ICON_DURATION;
  1588. }
  1589. else if ( Q_strcmp(type,"player_team") == 0 )
  1590. {
  1591. MapPlayer_t *player = GetPlayerByUserID( event->GetInt("userid") );
  1592. if ( !player )
  1593. return;
  1594. CBasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer();
  1595. if( localPlayer == NULL )
  1596. return;
  1597. MapPlayer_t *localMapPlayer = GetPlayerByUserID(localPlayer->GetUserID());
  1598. player->team = event->GetInt("team");
  1599. if( player == localMapPlayer )
  1600. player->icon = m_TeamIconsSelf[ GetIconNumberFromTeamNumber(player->team) ];
  1601. else
  1602. player->icon = m_TeamIcons[ GetIconNumberFromTeamNumber(player->team) ];
  1603. player->color = m_TeamColors[ GetIconNumberFromTeamNumber(player->team) ];
  1604. }
  1605. else
  1606. {
  1607. BaseClass::FireGameEvent(event);
  1608. }
  1609. }
  1610. void CCSMapOverview::SetMode(int mode)
  1611. {
  1612. if ( mode == MAP_MODE_RADAR )
  1613. {
  1614. m_flChangeSpeed = 0; // change size instantly
  1615. // We want the _output_ of the radar to be consistant, so we need to take the map scale in to account.
  1616. float desiredZoom = (DESIRED_RADAR_RESOLUTION * m_fMapScale) / (OVERVIEW_MAP_SIZE * m_fFullZoom);
  1617. g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( this, "zoom", desiredZoom, 0.0, 0, vgui::AnimationController::INTERPOLATOR_LINEAR );
  1618. if( CBasePlayer::GetLocalPlayer() )
  1619. SetFollowEntity( CBasePlayer::GetLocalPlayer()->entindex() );
  1620. SetPaintBackgroundType( 2 );// rounded corners
  1621. ShowPanel( true );
  1622. }
  1623. else if ( mode == MAP_MODE_INSET )
  1624. {
  1625. SetPaintBackgroundType( 2 );// rounded corners
  1626. float desiredZoom = (overview_preferred_view_size.GetFloat() * m_fMapScale) / (OVERVIEW_MAP_SIZE * m_fFullZoom);
  1627. g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( this, "zoom", desiredZoom, 0.0f, 0.2f, vgui::AnimationController::INTERPOLATOR_LINEAR );
  1628. }
  1629. else
  1630. {
  1631. SetPaintBackgroundType( 0 );// square corners
  1632. float desiredZoom = 1.0f;
  1633. g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( this, "zoom", desiredZoom, 0.0f, 0.2f, vgui::AnimationController::INTERPOLATOR_LINEAR );
  1634. }
  1635. BaseClass::SetMode(mode);
  1636. }
  1637. void CCSMapOverview::UpdateSizeAndPosition()
  1638. {
  1639. int x,y,w,h;
  1640. vgui::surface()->GetScreenSize( w, h );
  1641. switch( m_nMode )
  1642. {
  1643. case MAP_MODE_RADAR:
  1644. {
  1645. // To allow custom hud scripts to work, get our size from the HudRadar element that people already tweak.
  1646. int x, y, w, t;
  1647. (GET_HUDELEMENT( CHudRadar ))->GetBounds(x, y, w, t);
  1648. m_vPosition.x = x;
  1649. m_vPosition.y = y;
  1650. if ( g_pSpectatorGUI && g_pSpectatorGUI->IsVisible() )
  1651. {
  1652. m_vPosition.y += g_pSpectatorGUI->GetTopBarHeight();
  1653. }
  1654. m_vSize.x = w;
  1655. m_vSize.y = w;// Intentionally not 't'. We need to enforce square-ness to prevent people from seeing more of the map by fiddling their HudLayout
  1656. break;
  1657. }
  1658. case MAP_MODE_INSET:
  1659. {
  1660. m_vPosition.x = XRES(16);
  1661. m_vPosition.y = YRES(16);
  1662. if ( g_pSpectatorGUI && g_pSpectatorGUI->IsVisible() )
  1663. {
  1664. m_vPosition.y += g_pSpectatorGUI->GetTopBarHeight();
  1665. }
  1666. m_vSize.x = w/4;
  1667. m_vSize.y = m_vSize.x/1.333;
  1668. break;
  1669. }
  1670. case MAP_MODE_FULL:
  1671. default:
  1672. {
  1673. m_vSize.x = w;
  1674. m_vSize.y = h;
  1675. m_vPosition.x = 0;
  1676. m_vPosition.y = 0;
  1677. if ( g_pSpectatorGUI && g_pSpectatorGUI->IsVisible() )
  1678. {
  1679. m_vPosition.y += g_pSpectatorGUI->GetTopBarHeight();
  1680. m_vSize.y -= g_pSpectatorGUI->GetTopBarHeight();
  1681. m_vSize.y -= g_pSpectatorGUI->GetBottomBarHeight();
  1682. }
  1683. break;
  1684. }
  1685. }
  1686. GetBounds( x,y,w,h );
  1687. if ( m_flChangeSpeed > 0 )
  1688. {
  1689. // adjust slowly
  1690. int pixels = m_flChangeSpeed * gpGlobals->frametime;
  1691. x = AdjustValue( x, m_vPosition.x, pixels );
  1692. y = AdjustValue( y, m_vPosition.y, pixels );
  1693. w = AdjustValue( w, m_vSize.x, pixels );
  1694. h = AdjustValue( h, m_vSize.y, pixels );
  1695. }
  1696. else
  1697. {
  1698. // set instantly
  1699. x = m_vPosition.x;
  1700. y = m_vPosition.y;
  1701. w = m_vSize.x;
  1702. h = m_vSize.y;
  1703. }
  1704. SetBounds( x,y,w,h );
  1705. }
  1706. void CCSMapOverview::SetPlayerSeen( int index )
  1707. {
  1708. CSMapPlayer_t *pCS = GetCSInfoForPlayerIndex(index);
  1709. float now = gpGlobals->curtime;
  1710. if( pCS )
  1711. {
  1712. if( pCS->timeLastSeen == -1 )
  1713. pCS->timeFirstSeen = now;
  1714. pCS->timeLastSeen = now;
  1715. }
  1716. }
  1717. void CCSMapOverview::SetBombSeen( bool seen )
  1718. {
  1719. if( seen )
  1720. {
  1721. float now = gpGlobals->curtime;
  1722. if( m_bomb.timeLastSeen == -1 )
  1723. m_bomb.timeFirstSeen = now;
  1724. m_bomb.timeLastSeen = now;
  1725. }
  1726. else
  1727. {
  1728. m_bomb.timeFirstSeen = -1;
  1729. m_bomb.timeLastSeen = -1;
  1730. }
  1731. }
  1732. void CCSMapOverview::FlashEntity( int entityID )
  1733. {
  1734. MapPlayer_t *player = GetPlayerByEntityID(entityID);
  1735. if( player == NULL )
  1736. return;
  1737. CSMapPlayer_t *playerCS = GetCSInfoForPlayer(player);
  1738. if ( !playerCS )
  1739. return;
  1740. playerCS->flashUntilTime = gpGlobals->curtime + 2.0f;
  1741. playerCS->currentFlashAlpha = 255;
  1742. playerCS->nextFlashPeakTime = gpGlobals->curtime + 0.5f;
  1743. }
  1744. void CCSMapOverview::UpdateFlashes()
  1745. {
  1746. float now = gpGlobals->curtime;
  1747. for (int i=0; i<MAX_PLAYERS; i++)
  1748. {
  1749. CSMapPlayer_t *playerCS = GetCSInfoForPlayerIndex(i);
  1750. if( playerCS->flashUntilTime <= now )
  1751. {
  1752. // Flashing over.
  1753. playerCS->currentFlashAlpha = 0;
  1754. }
  1755. else
  1756. {
  1757. if( playerCS->nextFlashPeakTime <= now )
  1758. {
  1759. // Time for a peak
  1760. playerCS->currentFlashAlpha = 255;
  1761. playerCS->nextFlashPeakTime = now + 0.5f;
  1762. playerCS->nextFlashPeakTime = MIN( playerCS->nextFlashPeakTime, playerCS->flashUntilTime );
  1763. }
  1764. else
  1765. {
  1766. // Just fade away
  1767. playerCS->currentFlashAlpha -= ((playerCS->currentFlashAlpha * gpGlobals->frametime) / (playerCS->nextFlashPeakTime - now));
  1768. playerCS->currentFlashAlpha = MAX( 0, playerCS->currentFlashAlpha );
  1769. }
  1770. }
  1771. }
  1772. }
  1773. //-----------------------------------------------------------------------------
  1774. void CCSMapOverview::SetPlayerPreferredMode( int mode )
  1775. {
  1776. // A player has given an explicit overview_mode command, so we need to honor that when we are done being the radar.
  1777. m_playerPreferredMode = mode;
  1778. // save off non-radar preferred modes
  1779. switch ( mode )
  1780. {
  1781. case MAP_MODE_OFF:
  1782. overview_preferred_mode.SetValue( MAP_MODE_OFF );
  1783. break;
  1784. case MAP_MODE_INSET:
  1785. overview_preferred_mode.SetValue( MAP_MODE_INSET );
  1786. break;
  1787. case MAP_MODE_FULL:
  1788. overview_preferred_mode.SetValue( MAP_MODE_FULL );
  1789. break;
  1790. }
  1791. }
  1792. //-----------------------------------------------------------------------------
  1793. void CCSMapOverview::SetPlayerPreferredViewSize( float viewSize )
  1794. {
  1795. overview_preferred_view_size.SetValue( viewSize );
  1796. }
  1797. //-----------------------------------------------------------------------------
  1798. int CCSMapOverview::GetIconNumberFromTeamNumber( int teamNumber )
  1799. {
  1800. switch(teamNumber)
  1801. {
  1802. case TEAM_TERRORIST:
  1803. return MAP_ICON_T;
  1804. case TEAM_CT:
  1805. return MAP_ICON_CT;
  1806. default:
  1807. return MAP_ICON_HOSTAGE;
  1808. }
  1809. }
  1810. //-----------------------------------------------------------------------------
  1811. void CCSMapOverview::ClearGoalIcons()
  1812. {
  1813. m_goalIcons.RemoveAll();
  1814. }
  1815. //-----------------------------------------------------------------------------
  1816. void CCSMapOverview::UpdateGoalIcons()
  1817. {
  1818. // The goal entities don't exist on the client, so we have to get them from the CS Resource.
  1819. C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources();
  1820. if ( !pCSPR )
  1821. return;
  1822. Vector bombA = pCSPR->GetBombsiteAPosition();
  1823. if( bombA != vec3_origin )
  1824. {
  1825. CSMapGoal_t bombGoalA;
  1826. bombGoalA.position = bombA;
  1827. bombGoalA.iconToUse = m_bombSiteIconA;
  1828. m_goalIcons.AddToTail( bombGoalA );
  1829. m_goalIconsLoaded = true;
  1830. }
  1831. Vector bombB = pCSPR->GetBombsiteBPosition();
  1832. if( bombB != vec3_origin )
  1833. {
  1834. CSMapGoal_t bombGoalB;
  1835. bombGoalB.position = bombB;
  1836. bombGoalB.iconToUse = m_bombSiteIconB;
  1837. m_goalIcons.AddToTail( bombGoalB );
  1838. m_goalIconsLoaded = true;
  1839. }
  1840. for( int rescueIndex = 0; rescueIndex < MAX_HOSTAGE_RESCUES; rescueIndex++ )
  1841. {
  1842. Vector hostageI = pCSPR->GetHostageRescuePosition( rescueIndex );
  1843. if( hostageI != vec3_origin )
  1844. {
  1845. CSMapGoal_t hostageGoalI;
  1846. hostageGoalI.position = hostageI;
  1847. hostageGoalI.iconToUse = m_hostageRescueIcon;
  1848. m_goalIcons.AddToTail( hostageGoalI );
  1849. m_goalIconsLoaded = true;
  1850. }
  1851. }
  1852. }
  1853. //-----------------------------------------------------------------------------
  1854. void CCSMapOverview::DrawGoalIcons()
  1855. {
  1856. for( int iconIndex = 0; iconIndex < m_goalIcons.Count(); iconIndex++ )
  1857. {
  1858. // Goal icons are drawn without turning, but with edge adjustment.
  1859. CSMapGoal_t *currentIcon = &(m_goalIcons[iconIndex]);
  1860. int alpha = 255;
  1861. DrawIconCS(currentIcon->iconToUse, currentIcon->iconToUse, currentIcon->position, m_flIconSize, GetViewAngle(), alpha, false);
  1862. }
  1863. }
  1864. //-----------------------------------------------------------------------------
  1865. bool CCSMapOverview::IsRadarLocked()
  1866. {
  1867. return cl_radar_locked.GetBool();
  1868. }
  1869. //-----------------------------------------------------------------------------
  1870. int CCSMapOverview::GetMasterAlpha( void )
  1871. {
  1872. // The master alpha is the alpha that the map wants to draw at. The background will be at half that, and the icons
  1873. // will always be full. (The icons fade themselves for functional reasons like seen-recently.)
  1874. int alpha = 255;
  1875. if( GetMode() == MAP_MODE_RADAR )
  1876. alpha = cl_radaralpha.GetInt();
  1877. else
  1878. alpha = overview_alpha.GetFloat() * 255;
  1879. alpha = clamp( alpha, 0, 255 );
  1880. return alpha;
  1881. }
  1882. //-----------------------------------------------------------------------------
  1883. int CCSMapOverview::GetBorderSize( void )
  1884. {
  1885. switch( GetMode() )
  1886. {
  1887. case MAP_MODE_RADAR:
  1888. return 4;
  1889. case MAP_MODE_INSET:
  1890. return 4;
  1891. case MAP_MODE_FULL:
  1892. default:
  1893. return 0;
  1894. }
  1895. }
  1896. //-----------------------------------------------------------------------------
  1897. Vector2D CCSMapOverview::PanelToMap( const Vector2D &panelPos )
  1898. {
  1899. // This is the reversing of baseclass's MapToPanel
  1900. int pwidth, pheight;
  1901. GetSize(pwidth, pheight);
  1902. float viewAngle = GetViewAngle();
  1903. float fScale = (m_fZoom * m_fFullZoom) / OVERVIEW_MAP_SIZE;
  1904. Vector offset;
  1905. offset.x = (panelPos.x - (pwidth * 0.5f)) / pheight;
  1906. offset.y = (panelPos.y - (pheight * 0.5f)) / pheight;
  1907. offset.x /= fScale;
  1908. offset.y /= fScale;
  1909. VectorYawRotate( offset, -viewAngle, offset );
  1910. Vector2D mapPos;
  1911. mapPos.x = offset.x + m_MapCenter.x;
  1912. mapPos.y = offset.y + m_MapCenter.y;
  1913. return mapPos;
  1914. }