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.

2455 lines
81 KiB

  1. //========= Copyright � 1996-2008, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: See header file
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "hud_locator_target.h"
  9. #include "iclientmode.h"
  10. #include <vgui/ILocalize.h>
  11. #include <vgui/ISurface.h>
  12. #include <vgui/IVGui.h>
  13. #include <vgui_controls/EditablePanel.h>
  14. #include <vgui_controls/Controls.h>
  15. #include <vgui_controls/Label.h>
  16. #include <vgui/IInput.h>
  17. #include <vgui/IScheme.h>
  18. #include "iinput.h"
  19. #include "view.h"
  20. #include "hud.h"
  21. #include "hudelement.h"
  22. #include "vgui_int.h"
  23. #include "ienginevgui.h"
  24. #include "inputsystem/iinputsystem.h"
  25. // memdbgon must be the last include file in a .cpp file!!!
  26. #include "tier0/memdbgon.h"
  27. #define ICON_SIZE 0.04f // Icons are ScreenWidth() * ICON_SIZE wide.
  28. #define ICON_GAP 5 // Number of pixels between the icon and the text
  29. #define OFFSCREEN_ICON_POSITION_RADIUS 100
  30. #define CAPTION_FONT_HANDLE ( ( IsLocatorSplitscreen() ) ? ( m_hCaptionFont_ss ) : ( m_hCaptionFont ) )
  31. #define BUTTON_FONT_HANDLE ( g_pInputSystem->IsSteamControllerActive()?( m_hButtonFontSC ):( m_hButtonFont ) )
  32. #define ICON_DIST_TOO_FAR (60.0f * 12.0f)
  33. #define MIN_ICON_ALPHA 0.5
  34. #define MAX_ICON_ALPHA 1
  35. ConVar locator_icon_min_size_non_ss( "locator_icon_min_size_non_ss", "1.0", FCVAR_NONE, "Minimum scale of the icon on the screen" );
  36. ConVar locator_icon_max_size_non_ss( "locator_icon_max_size_non_ss", "2", FCVAR_NONE, "Minimum scale of the icon on the screen" );
  37. #define MIN_ICON_SCALE ( ( IsLocatorSplitscreen() && !ss_verticalsplit.GetBool() ) ? ( locator_icon_min_size_non_ss.GetFloat() * 0.78 ) : ( locator_icon_min_size_non_ss.GetFloat() ) )
  38. #define MAX_ICON_SCALE ( ( IsLocatorSplitscreen() && !ss_verticalsplit.GetBool() ) ? ( locator_icon_max_size_non_ss.GetFloat() * 0.78 ) : ( locator_icon_max_size_non_ss.GetFloat() ) )
  39. #define LOCATOR_OCCLUSION_TEST_RATE 0.25f
  40. enum
  41. {
  42. DRAW_ARROW_NO = 0,
  43. DRAW_ARROW_UP,
  44. DRAW_ARROW_DOWN,
  45. DRAW_ARROW_LEFT,
  46. DRAW_ARROW_RIGHT
  47. };
  48. ConVar locator_fade_time( "locator_fade_time", "0.3", FCVAR_NONE, "Number of seconds it takes for a lesson to fully fade in/out." );
  49. ConVar locator_lerp_speed( "locator_lerp_speed", "5.0f", FCVAR_NONE, "Speed that static lessons move along the Y axis." );
  50. ConVar locator_lerp_rest( "locator_lerp_rest", "0.25f", FCVAR_NONE, "Number of seconds before moving from the center." );
  51. ConVar locator_lerp_time( "locator_lerp_time", "1.75f", FCVAR_NONE, "Number of seconds to lerp before reaching final destination" );
  52. ConVar locator_pulse_time( "locator_pulse_time", "1.0f", FCVAR_NONE, "Number of seconds to pulse after changing icon or position" );
  53. ConVar locator_start_at_crosshair( "locator_start_at_crosshair", "1", FCVAR_NONE, "Start position at the crosshair instead of the top middle of the screen." );
  54. ConVar locator_topdown_style( "locator_topdown_style", "0", FCVAR_NONE, "Topdown games set this to handle distance and offscreen location differently." );
  55. ConVar locator_background_style( "locator_background_style", "2", FCVAR_NONE, "Setting this to 1 will show rectangle backgrounds behind the items word-bubble pointers." );
  56. ConVar locator_background_color( "locator_background_color", "16 16 16 100", FCVAR_NONE, "The default color for the background." );
  57. ConVar locator_background_border_color( "locator_background_border_color", "16 16 16 0", FCVAR_NONE, "The default color for the border." );
  58. ConVar locator_background_thickness_x( "locator_background_thickness_x", "8", FCVAR_NONE, "How many pixels the background borders the left and right." );
  59. ConVar locator_background_thickness_y( "locator_background_thickness_y", "0", FCVAR_NONE, "How many pixels the background borders the top and bottom." );
  60. ConVar locator_background_shift_x( "locator_background_shift_x", "3", FCVAR_NONE, "How many pixels the background is shifted right." );
  61. ConVar locator_background_shift_y( "locator_background_shift_y", "1", FCVAR_NONE, "How many pixels the background is shifted down." );
  62. ConVar locator_background_border_thickness( "locator_background_border_thickness", "2", FCVAR_NONE, "How many pixels the background borders the left and right." );
  63. ConVar locator_target_offset_x( "locator_target_offset_x", "-17", FCVAR_NONE, "How many pixels to offset the locator from the target position." );
  64. ConVar locator_target_offset_y( "locator_target_offset_y", "-64", FCVAR_NONE, "How many pixels to offset the locator from the target position." );
  65. ConVar locator_screen_pos_y( "locator_screen_pos_y", "0.35", FCVAR_NONE, "Percentage of the lower half of the screen that the locator will draw at when at its reseting position on the hud." );
  66. ConVar locator_split_maxwide_percent( "locator_split_maxwide_percent", "0.80f", FCVAR_CHEAT );
  67. ConVar locator_split_len( "locator_split_len", "0.5f", FCVAR_CHEAT );
  68. // This maps a controller origin to a localized string (like "GameUI_Icons_SC_L_Trigger"). That string
  69. // will then remap to a character (like 'L'), which will correspond to a character inside the SC button font file.
  70. static const char *g_SteamControllerOriginStrings[k_EControllerActionOrigin_Count] =
  71. {
  72. "SC_None", // k_EControllerActionOrigin_None,
  73. "SC_A_Button", // k_EControllerActionOrigin_A,
  74. "SC_B_Button", // k_EControllerActionOrigin_B,
  75. "SC_X_Button", // k_EControllerActionOrigin_X,
  76. "SC_Y_Button", // k_EControllerActionOrigin_Y,
  77. "SC_L_Shoulder", // k_EControllerActionOrigin_LeftBumper,
  78. "SC_R_Shoulder", // k_EControllerActionOrigin_RightBumper,
  79. "SC_L_Grip", // k_EControllerActionOrigin_LeftGrip,
  80. "SC_R_Grip", // k_EControllerActionOrigin_RightGrip,
  81. "SC_Start_Button", // k_EControllerActionOrigin_Start,
  82. "SC_Back_Button", // k_EControllerActionOrigin_Back,
  83. "SC_Left_Pad_Touch", // k_EControllerActionOrigin_LeftPad_Touch,
  84. "SC_Left_Pad_Swipe", // k_EControllerActionOrigin_LeftPad_Swipe,
  85. "SC_Left_Pad_Click", // k_EControllerActionOrigin_LeftPad_Click,
  86. "SC_Left_Pad_DPad_N", // k_EControllerActionOrigin_LeftPad_DPadNorth,
  87. "SC_Left_Pad_DPad_S", // k_EControllerActionOrigin_LeftPad_DPadSouth,
  88. "SC_Left_Pad_DPad_W", // k_EControllerActionOrigin_LeftPad_DPadWest,
  89. "SC_Left_Pad_DPad_E", // k_EControllerActionOrigin_LeftPad_DPadEast,
  90. "SC_Right_Pad_Touch", // k_EControllerActionOrigin_RightPad_Touch,
  91. "SC_Right_Pad_Swipe", // k_EControllerActionOrigin_RightPad_Swipe,
  92. "SC_Right_Pad_Click", // k_EControllerActionOrigin_RightPad_Click,
  93. "SC_Right_Pad_DPad_N", // k_EControllerActionOrigin_RightPad_DPadNorth, // include dpad ones on right pad?
  94. "SC_Right_Pad_DPad_S", // k_EControllerActionOrigin_RightPad_DPadSouth,
  95. "SC_Right_Pad_DPad_W", // k_EControllerActionOrigin_RightPad_DPadWest,
  96. "SC_Right_Pad_DPad_E", // k_EControllerActionOrigin_RightPad_DPadEast,
  97. "SC_L_Trigger_Pull", // k_EControllerActionOrigin_LeftTrigger_Pull,
  98. "SC_L_Trigger_Click", // k_EControllerActionOrigin_LeftTrigger_Click,
  99. "SC_R_Trigger_Pull", // k_EControllerActionOrigin_RightTrigger_Pull,
  100. "SC_R_Trigger_Pull", // k_EControllerActionOrigin_RightTrigger_Click,
  101. "SC_L_Stick_Move", // k_EControllerActionOrigin_LeftStick_Move,
  102. "SC_L_Stick_Click", // k_EControllerActionOrigin_LeftStick_Click,
  103. "SC_Gyro_Move", // k_EControllerActionOrigin_Gyro_Move,
  104. "SC_Gyro_Pitch", // k_EControllerActionOrigin_Gyro_Pitch,
  105. "SC_Gyro_Yaw", // k_EControllerActionOrigin_Gyro_Yaw,
  106. "SC_Gyro_Roll" // k_EControllerActionOrigin_Gyro_Roll,
  107. };
  108. #ifdef DEBUG
  109. ConVar sc_debug_origins( "sc_debug_origins", "0", FCVAR_ARCHIVE, "Debugging" );
  110. #endif
  111. bool IsLocatorSplitscreen( void )
  112. {
  113. if ( !VGui_IsSplitScreen() )
  114. return false;
  115. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  116. int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
  117. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer( nSlot );
  118. if ( pPlayer )
  119. {
  120. return ( pPlayer->GetSplitScreenPlayers().Count() > 0 || pPlayer->IsSplitScreenPlayer() );
  121. }
  122. return true;
  123. }
  124. //Precahce the effects
  125. PRECACHE_REGISTER_BEGIN( GLOBAL, PrecacheLocatorTarget )
  126. PRECACHE( MATERIAL, "vgui/hud/icon_arrow_left" )
  127. PRECACHE( MATERIAL, "vgui/hud/icon_arrow_right" )
  128. PRECACHE( MATERIAL, "vgui/hud/icon_arrow_up" )
  129. PRECACHE( MATERIAL, "vgui/hud/icon_arrow_down" )
  130. PRECACHE( MATERIAL, "vgui/hud/icon_arrow_plain" )
  131. PRECACHE_REGISTER_END()
  132. //------------------------------------
  133. CLocatorTarget::CLocatorTarget( void )
  134. {
  135. Deactivate( true );
  136. }
  137. //------------------------------------
  138. void CLocatorTarget::Activate( int serialNumber )
  139. {
  140. m_serialNumber = serialNumber;
  141. m_frameLastUpdated = gpGlobals->framecount;
  142. m_isActive = true;
  143. m_bVisible = true;
  144. m_bOnscreen = true;
  145. m_alpha = 0;
  146. m_fadeStart = gpGlobals->curtime;
  147. m_bIconNoTarget = false;
  148. m_offsetX = m_offsetY = 0;
  149. int iStartX = ScreenWidth() / 2;
  150. int iStartY = ScreenHeight() / 4;
  151. if ( locator_start_at_crosshair.GetBool() )
  152. {
  153. // We want to start lessons at the players crosshair, cause that's where they're looking!
  154. vgui::input()->GetCursorPos( iStartX, iStartY );
  155. }
  156. m_lastXPos = iStartX;
  157. m_lastYPos = iStartY;
  158. m_bDrawArrow = false;
  159. m_fDrawArrowAngle = 0.0f;
  160. m_lerpStart = gpGlobals->curtime;
  161. m_pulseStart = gpGlobals->curtime;
  162. m_declutterIndex = 0;
  163. m_lastDeclutterIndex = 0;
  164. AddIconEffects( LOCATOR_ICON_FX_FADE_IN );
  165. }
  166. //------------------------------------
  167. void CLocatorTarget::Deactivate( bool bNoFade )
  168. {
  169. if ( ( engine && engine->IsPaused() ) || ( enginevgui && enginevgui->IsGameUIVisible() ) )
  170. {
  171. bNoFade = true;
  172. }
  173. if ( bNoFade || m_alpha == 0 ||
  174. ( m_bOccluded && !( m_iEffectsFlags & LOCATOR_ICON_FX_FORCE_CAPTION ) ) ||
  175. ( !m_bOnscreen && ( m_iEffectsFlags & LOCATOR_ICON_FX_NO_OFFSCREEN ) ) )
  176. {
  177. m_bOriginInScreenspace = false;
  178. m_serialNumber = -1;
  179. m_isActive = false;
  180. m_frameLastUpdated = 0;
  181. m_pIcon_onscreen = NULL;
  182. m_pIcon_offscreen = NULL;
  183. m_bDrawControllerButton = false;
  184. m_bDrawControllerButtonOffscreen = false;
  185. m_iEffectsFlags = LOCATOR_ICON_FX_NONE;
  186. m_rgbaIconColor = Color( 255, 255, 255, 255 );
  187. m_captionWide = 0;
  188. m_pchDrawBindingName = NULL;
  189. m_pchDrawBindingNameOffscreen = NULL;
  190. m_widthScale_onscreen = 1.0f;
  191. m_bOccluded = false;
  192. m_alpha = 0;
  193. m_bIsDrawing = false;
  194. m_bVisible = false;
  195. m_szVguiTargetName = "";
  196. m_szVguiTargetLookup = "";
  197. m_hVguiTarget = NULL;
  198. m_nVguiTargetEdge = vgui::Label::a_northwest;
  199. m_szBinding = "";
  200. m_iBindingTick = 0;
  201. m_flNextBindingTick = 0.0f;
  202. m_flNextOcclusionTest = 0.0f;
  203. m_iBindingChoicesCount = 0;
  204. m_wszCaption.RemoveAll();
  205. m_wszCaption.AddToTail( (wchar_t)0 );
  206. m_bWasControllerLast = m_bWasSteamControllerLast = false;
  207. }
  208. else if ( !( m_iEffectsFlags & LOCATOR_ICON_FX_FADE_OUT ) )
  209. {
  210. // Determine home much time it would have spent fading to reach the current alpha
  211. float flAssumedFadeTime;
  212. flAssumedFadeTime = ( 1.0f - static_cast<float>( m_alpha ) / 255.0f ) * locator_fade_time.GetFloat();
  213. // Set the fade
  214. m_fadeStart = gpGlobals->curtime - flAssumedFadeTime;
  215. AddIconEffects( LOCATOR_ICON_FX_FADE_OUT );
  216. RemoveIconEffects( LOCATOR_ICON_FX_FADE_IN );
  217. }
  218. }
  219. //------------------------------------
  220. void CLocatorTarget::Update()
  221. {
  222. m_frameLastUpdated = gpGlobals->framecount;
  223. if ( m_bVisible && ( m_iEffectsFlags & LOCATOR_ICON_FX_FADE_OUT ) )
  224. {
  225. // Determine home much time it would have spent fading to reach the current alpha
  226. float flAssumedFadeTime;
  227. flAssumedFadeTime = ( 1.0f - static_cast<float>( m_alpha ) / 255.0f ) * locator_fade_time.GetFloat();
  228. // Set the fade
  229. m_fadeStart = gpGlobals->curtime - flAssumedFadeTime;
  230. AddIconEffects( LOCATOR_ICON_FX_FADE_OUT );
  231. RemoveIconEffects( LOCATOR_ICON_FX_FADE_OUT );
  232. }
  233. }
  234. int CLocatorTarget::GetIconX( void )
  235. {
  236. return m_iconX + ( IsOnScreen() ? locator_target_offset_x.GetInt()+m_offsetX : 0 );
  237. }
  238. int CLocatorTarget::GetIconY( void )
  239. {
  240. return m_iconY + ( IsOnScreen() ? locator_target_offset_y.GetInt()+m_offsetY : 0 );
  241. }
  242. int CLocatorTarget::GetIconCenterX( void )
  243. {
  244. return m_centerX + locator_target_offset_x.GetInt() + m_offsetX;
  245. }
  246. int CLocatorTarget::GetIconCenterY( void )
  247. {
  248. return m_centerY + locator_target_offset_y.GetInt() + m_offsetY;
  249. }
  250. void CLocatorTarget::SetVisible( bool bVisible )
  251. {
  252. if ( m_bVisible == bVisible )
  253. {
  254. // They are already the same
  255. return;
  256. }
  257. m_bVisible = bVisible;
  258. if ( bVisible )
  259. {
  260. // Determine home much time it would have spent fading to reach the current alpha
  261. float flAssumedFadeTime;
  262. flAssumedFadeTime = ( static_cast<float>( m_alpha ) / 255.0f ) * locator_fade_time.GetFloat();
  263. // Set the fade
  264. m_fadeStart = gpGlobals->curtime - flAssumedFadeTime;
  265. AddIconEffects( LOCATOR_ICON_FX_FADE_IN );
  266. RemoveIconEffects( LOCATOR_ICON_FX_FADE_OUT );
  267. }
  268. else
  269. {
  270. // Determine home much time it would have spent fading to reach the current alpha
  271. float flAssumedFadeTime;
  272. flAssumedFadeTime = ( 1.0f - static_cast<float>( m_alpha ) / 255.0f ) * locator_fade_time.GetFloat();
  273. // Set the fade
  274. m_fadeStart = gpGlobals->curtime - flAssumedFadeTime;
  275. AddIconEffects( LOCATOR_ICON_FX_FADE_OUT );
  276. RemoveIconEffects( LOCATOR_ICON_FX_FADE_IN );
  277. }
  278. }
  279. bool CLocatorTarget::IsVisible( void )
  280. {
  281. return m_bVisible;
  282. }
  283. void CLocatorTarget::SetCaptionText( const char *pszText, const char *pszParam )
  284. {
  285. wchar_t outbuf[ 256 ];
  286. outbuf[ 0 ] = L'\0';
  287. if ( pszParam && pszParam[ 0 ] != '\0' )
  288. {
  289. wchar_t wszParamBuff[ 128 ];
  290. wchar_t *pLocalizedParam = NULL;
  291. if ( pszParam[ 0 ] == '#' )
  292. {
  293. pLocalizedParam = g_pVGuiLocalize->Find( pszParam );
  294. }
  295. if ( !pLocalizedParam )
  296. {
  297. g_pVGuiLocalize->ConvertANSIToUnicode( pszParam, wszParamBuff, sizeof( wszParamBuff ) );
  298. pLocalizedParam = wszParamBuff;
  299. }
  300. wchar_t wszTextBuff[ 128 ];
  301. wchar_t *pLocalizedText = NULL;
  302. if ( pszText[ 0 ] == '#' )
  303. {
  304. pLocalizedText = g_pVGuiLocalize->Find( pszText );
  305. }
  306. if ( !pLocalizedText )
  307. {
  308. g_pVGuiLocalize->ConvertANSIToUnicode( pszText, wszTextBuff, sizeof( wszTextBuff ) );
  309. pLocalizedText = wszTextBuff;
  310. }
  311. wchar_t buf[ 256 ];
  312. g_pVGuiLocalize->ConstructString( buf, sizeof(buf), pLocalizedText, 1, pLocalizedParam );
  313. UTIL_ReplaceKeyBindings( buf, sizeof( buf ), outbuf, sizeof( outbuf ) );
  314. }
  315. else
  316. {
  317. wchar_t wszTextBuff[ 128 ];
  318. wchar_t *pLocalizedText = NULL;
  319. if ( pszText[ 0 ] == '#' )
  320. {
  321. pLocalizedText = g_pVGuiLocalize->Find( pszText );
  322. }
  323. if ( !pLocalizedText )
  324. {
  325. g_pVGuiLocalize->ConvertANSIToUnicode( pszText, wszTextBuff, sizeof( wszTextBuff ) );
  326. pLocalizedText = wszTextBuff;
  327. }
  328. wchar_t buf[ 256 ];
  329. Q_wcsncpy( buf, pLocalizedText, sizeof( buf ) );
  330. UTIL_ReplaceKeyBindings( buf, sizeof(buf), outbuf, sizeof( outbuf ) );
  331. }
  332. int len = wcslen( outbuf ) + 1;
  333. m_wszCaption.RemoveAll();
  334. m_wszCaption.EnsureCount( len );
  335. Q_wcsncpy( m_wszCaption.Base(), outbuf, len * sizeof( wchar_t ) );
  336. }
  337. void CLocatorTarget::SetCaptionColor( const char *pszCaptionColor )
  338. {
  339. int r,g,b;
  340. r = g = b = 0;
  341. CSplitString colorValues( pszCaptionColor, "," );
  342. if( colorValues.Count() == 3 )
  343. {
  344. r = atoi( colorValues[0] );
  345. g = atoi( colorValues[1] );
  346. b = atoi( colorValues[2] );
  347. m_captionColor.SetColor( r,g,b, 255 );
  348. }
  349. else
  350. {
  351. DevWarning( "caption_color format incorrect. RRR,GGG,BBB expected.\n");
  352. }
  353. }
  354. bool CLocatorTarget::IsStatic()
  355. {
  356. return ( ( m_iEffectsFlags & LOCATOR_ICON_FX_STATIC ) || IsPresenting() );
  357. }
  358. bool CLocatorTarget::IsPresenting()
  359. {
  360. return ( gpGlobals->curtime - m_lerpStart < locator_lerp_rest.GetFloat() );
  361. }
  362. void CLocatorTarget::StartTimedLerp()
  363. {
  364. if ( gpGlobals->curtime - m_lerpStart > locator_lerp_rest.GetFloat() )
  365. {
  366. m_lerpStart = gpGlobals->curtime - locator_lerp_rest.GetFloat();
  367. }
  368. }
  369. void CLocatorTarget::StartPresent()
  370. {
  371. m_lerpStart = gpGlobals->curtime;
  372. }
  373. void CLocatorTarget::EndPresent()
  374. {
  375. if ( gpGlobals->curtime - m_lerpStart < locator_lerp_rest.GetFloat() )
  376. {
  377. m_lerpStart = gpGlobals->curtime - locator_lerp_rest.GetFloat();
  378. }
  379. }
  380. void CLocatorTarget::UpdateVguiTarget( void )
  381. {
  382. const char *pchVguiTargetName = m_szVguiTargetName.String();
  383. if ( !pchVguiTargetName || pchVguiTargetName[ 0 ] == '\0' )
  384. {
  385. m_hVguiTarget = NULL;
  386. return;
  387. }
  388. // Get the appropriate token based on the binding
  389. if ( m_iBindingChoicesCount > 0 )
  390. {
  391. int nTagetToken = m_iBindChoicesOriginalToken[ m_iBindingTick % m_iBindingChoicesCount ];
  392. for ( int nToken = 0; nToken < nTagetToken && pchVguiTargetName; ++nToken )
  393. {
  394. pchVguiTargetName = strchr( pchVguiTargetName, ';' );
  395. if ( pchVguiTargetName )
  396. {
  397. pchVguiTargetName++;
  398. }
  399. }
  400. if ( !pchVguiTargetName || pchVguiTargetName[ 0 ] == '\0' )
  401. {
  402. // There wasn't enough tokens, just use the first
  403. pchVguiTargetName = m_szVguiTargetName.String();
  404. }
  405. }
  406. m_hVguiTarget = GetClientMode()->GetPanelFromViewport( pchVguiTargetName );
  407. }
  408. void CLocatorTarget::SetVguiTargetName( const char *pchVguiTargetName )
  409. {
  410. if ( Q_strcmp( m_szVguiTargetName.String(), pchVguiTargetName ) == 0 )
  411. return;
  412. m_szVguiTargetName = pchVguiTargetName;
  413. UpdateVguiTarget();
  414. }
  415. void CLocatorTarget::SetVguiTargetLookup( const char *pchVguiTargetLookup )
  416. {
  417. m_szVguiTargetLookup = pchVguiTargetLookup;
  418. }
  419. void CLocatorTarget::SetVguiTargetEdge( int nVguiEdge )
  420. {
  421. m_nVguiTargetEdge = nVguiEdge;
  422. }
  423. vgui::Panel *CLocatorTarget::GetVguiTarget( void )
  424. {
  425. return (vgui::Panel *)m_hVguiTarget.Get();
  426. }
  427. //------------------------------------
  428. void CLocatorTarget::SetOnscreenIconTextureName( const char *pszTexture )
  429. {
  430. if ( Q_strcmp( m_szOnscreenTexture.String(), pszTexture ) == 0 )
  431. return;
  432. m_szOnscreenTexture = pszTexture;
  433. m_pIcon_onscreen = NULL; // Dirty the onscreen icon so that the Locator will look up the new icon by name.
  434. m_pulseStart = gpGlobals->curtime;
  435. }
  436. //------------------------------------
  437. void CLocatorTarget::SetOffscreenIconTextureName( const char *pszTexture )
  438. {
  439. if ( Q_strcmp( m_szOffscreenTexture.String(), pszTexture ) == 0 )
  440. return;
  441. m_szOffscreenTexture = pszTexture;
  442. m_pIcon_offscreen = NULL; // Ditto
  443. m_pulseStart = gpGlobals->curtime;
  444. }
  445. //------------------------------------
  446. void CLocatorTarget::SetBinding( const char *pszBinding )
  447. {
  448. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  449. int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
  450. BindingLookupOption_t nBindingLookupFlags = BINDINGLOOKUP_ALL;
  451. if ( !IsGameConsole() )
  452. {
  453. // Only show joystick binds if it's enabled and non-joystick if it's disabled
  454. if ( g_pInputSystem->IsSteamControllerActive() )
  455. nBindingLookupFlags = BINDINGLOOKUP_STEAMCONTROLLER_ONLY;
  456. else
  457. nBindingLookupFlags = input->ControllerModeActive() ? BINDINGLOOKUP_JOYSTICK_ONLY : BINDINGLOOKUP_KEYBOARD_ONLY;
  458. }
  459. bool bIsControllerNow = ( nBindingLookupFlags != 0 );
  460. #ifdef DEBUG
  461. if ( !sc_debug_origins.GetBool() && (m_bWasControllerLast == bIsControllerNow && m_bWasSteamControllerLast == g_pInputSystem->IsSteamControllerActive()) )
  462. #else
  463. if ( m_bWasControllerLast == bIsControllerNow || m_bWasSteamControllerLast == g_pInputSystem->IsSteamControllerActive() )
  464. #endif
  465. {
  466. // We haven't toggled joystick enabled recently, so if it's the same bind, bail
  467. if ( Q_strcmp( m_szBinding.String(), pszBinding ) == 0 )
  468. {
  469. return;
  470. }
  471. }
  472. m_bWasControllerLast = bIsControllerNow;
  473. m_bWasSteamControllerLast = g_pInputSystem->IsSteamControllerActive();
  474. m_szBinding = pszBinding;
  475. m_pIcon_onscreen = NULL; // Dirty the onscreen icon so that the Locator will look up the new icon by name.
  476. m_pIcon_offscreen = NULL; // ditto.
  477. m_flNextBindingTick = gpGlobals->curtime + 0.75f;
  478. // Get a list of all the keys bound to these actions
  479. m_iBindingChoicesCount = 0;
  480. // Tokenize the binding name (could be more than one binding)
  481. int nOriginalToken = 0;
  482. const char *pchToken = m_szBinding.String();
  483. char szToken[ 128 ];
  484. pchToken = nexttoken( szToken, pchToken, ';' );
  485. // Get our steam controller handles ready
  486. uint64 nSteamControllerHandles[STEAM_CONTROLLER_MAX_COUNT];
  487. int nSteamControllerCount = 0;
  488. if ( nBindingLookupFlags == BINDINGLOOKUP_STEAMCONTROLLER_ONLY )
  489. {
  490. if ( steamapicontext && steamapicontext->SteamController() )
  491. {
  492. nSteamControllerCount = steamapicontext->SteamController()->GetConnectedControllers( nSteamControllerHandles );
  493. }
  494. }
  495. // Msg(" m_bWasControllerLast : %s\n", m_bWasControllerLast ? "TRUE" : "FALSE" );
  496. // Msg(" m_bWasSteamControllerLast: %s\n", m_bWasSteamControllerLast ? "TRUE" : "FALSE" );
  497. // Msg(" nSteamControllerCount : %d\n", nSteamControllerCount );
  498. while ( pchToken )
  499. {
  500. if ( nBindingLookupFlags == BINDINGLOOKUP_STEAMCONTROLLER_ONLY && nSteamControllerCount > 0 )
  501. {
  502. // What to do if they have multiple controllers connected?
  503. uint64 nController = nSteamControllerHandles[0];
  504. const char *pszSearchToken = szToken;
  505. if ( pszSearchToken && pszSearchToken[0] == '+' )
  506. {
  507. pszSearchToken++;
  508. }
  509. const ControllerActionSetHandle_t handleActionSet = steamapicontext->SteamController()->GetActionSetHandle( "GameControls" );
  510. // Get the handle for the game action matching the command.
  511. ControllerDigitalActionHandle_t hDigitalAction = steamapicontext->SteamController()->GetDigitalActionHandle( pszSearchToken );
  512. if ( hDigitalAction )
  513. {
  514. EControllerActionOrigin eOrigins[STEAM_CONTROLLER_MAX_ORIGINS];
  515. memset( eOrigins, k_EControllerActionOrigin_None, sizeof( eOrigins ) );
  516. steamapicontext->SteamController()->GetDigitalActionOrigins( nController, handleActionSet, hDigitalAction, eOrigins );
  517. SetSteamControllerBindingToOrigin( eOrigins, nOriginalToken, pszSearchToken );
  518. }
  519. else
  520. {
  521. ControllerAnalogActionHandle_t hAnalogAction = steamapicontext->SteamController()->GetAnalogActionHandle( pszSearchToken );
  522. if ( hAnalogAction )
  523. {
  524. EControllerActionOrigin eOrigins[STEAM_CONTROLLER_MAX_ORIGINS];
  525. memset( eOrigins, k_EControllerActionOrigin_None, sizeof( eOrigins ) );
  526. steamapicontext->SteamController()->GetDigitalActionOrigins( nController, handleActionSet, hAnalogAction, eOrigins );
  527. SetSteamControllerBindingToOrigin( eOrigins, nOriginalToken, pszSearchToken );
  528. }
  529. }
  530. }
  531. else
  532. {
  533. // Get the first parameter
  534. int iTokenBindingCount = 0;
  535. const char *pchBinding = engine->Key_LookupBindingEx( szToken, nSlot, iTokenBindingCount, nBindingLookupFlags );
  536. while ( m_iBindingChoicesCount < MAX_LOCATOR_BINDINGS_SHOWN && pchBinding )
  537. {
  538. m_pchBindingChoices[ m_iBindingChoicesCount ] = pchBinding;
  539. m_iBindChoicesOriginalToken[ m_iBindingChoicesCount ] = nOriginalToken;
  540. ++m_iBindingChoicesCount;
  541. ++iTokenBindingCount;
  542. pchBinding = engine->Key_LookupBindingEx( szToken, nSlot, iTokenBindingCount, nBindingLookupFlags );
  543. }
  544. }
  545. nOriginalToken++;
  546. pchToken = nexttoken( szToken, pchToken, ';' );
  547. }
  548. //Msg(" m_iBindingChoicesCount : %d\n", m_iBindingChoicesCount );
  549. if ( m_bWasSteamControllerLast && !m_iBindingChoicesCount )
  550. {
  551. // This is a hack until we can get origins in other game action sets.
  552. // By setting this back to false, it'll force us to keep looking for origins until the
  553. // game action set has switched from the main menu controls back to the FPS Controls.
  554. m_bWasSteamControllerLast = false;
  555. }
  556. m_pulseStart = gpGlobals->curtime;
  557. }
  558. #ifdef DEBUG
  559. char *g_szControllerOrigins[] =
  560. {
  561. "k_EControllerActionOrigin_None",
  562. "k_EControllerActionOrigin_A",
  563. "k_EControllerActionOrigin_B",
  564. "k_EControllerActionOrigin_X",
  565. "k_EControllerActionOrigin_Y",
  566. "k_EControllerActionOrigin_LeftBumper",
  567. "k_EControllerActionOrigin_RightBumper",
  568. "k_EControllerActionOrigin_LeftGrip",
  569. "k_EControllerActionOrigin_RightGrip",
  570. "k_EControllerActionOrigin_Start",
  571. "k_EControllerActionOrigin_Back",
  572. "k_EControllerActionOrigin_LeftPad_Touch",
  573. "k_EControllerActionOrigin_LeftPad_Swipe",
  574. "k_EControllerActionOrigin_LeftPad_Click",
  575. "k_EControllerActionOrigin_LeftPad_DPadNorth",
  576. "k_EControllerActionOrigin_LeftPad_DPadSouth",
  577. "k_EControllerActionOrigin_LeftPad_DPadWest",
  578. "k_EControllerActionOrigin_LeftPad_DPadEast",
  579. "k_EControllerActionOrigin_RightPad_Touch",
  580. "k_EControllerActionOrigin_RightPad_Swipe",
  581. "k_EControllerActionOrigin_RightPad_Click",
  582. "k_EControllerActionOrigin_RightPad_DPadNorth", // include dpad ones on right pad?
  583. "k_EControllerActionOrigin_RightPad_DPadSouth",
  584. "k_EControllerActionOrigin_RightPad_DPadWest",
  585. "k_EControllerActionOrigin_RightPad_DPadEast",
  586. "k_EControllerActionOrigin_LeftTrigger_Pull",
  587. "k_EControllerActionOrigin_LeftTrigger_Click",
  588. "k_EControllerActionOrigin_RightTrigger_Pull",
  589. "k_EControllerActionOrigin_RightTrigger_Click",
  590. "k_EControllerActionOrigin_LeftStick_Move",
  591. "k_EControllerActionOrigin_LeftStick_Click",
  592. "k_EControllerActionOrigin_Gyro_Move",
  593. "k_EControllerActionOrigin_Gyro_Pitch",
  594. "k_EControllerActionOrigin_Gyro_Yaw",
  595. "k_EControllerActionOrigin_Gyro_Roll",
  596. };
  597. #endif
  598. //------------------------------------
  599. void CLocatorTarget::SetSteamControllerBindingToOrigin( EControllerActionOrigin *pOrigins, int nOriginalToken, const char *pszActionName )
  600. {
  601. #ifdef DEBUG
  602. if ( sc_debug_origins.GetBool() )
  603. {
  604. bool bFound = false;
  605. for( int i = 0; i < STEAM_CONTROLLER_MAX_ORIGINS; i++ )
  606. {
  607. if ( pOrigins[i] == k_EControllerActionOrigin_None )
  608. break;
  609. if ( !bFound )
  610. {
  611. bFound = true;
  612. Msg("ORIGINS FOR %s\n", pszActionName);
  613. }
  614. Msg(" (%d) %s\n", pOrigins[i], g_szControllerOrigins[ pOrigins[i] ] );
  615. }
  616. }
  617. #endif
  618. for( int i = 0; i < STEAM_CONTROLLER_MAX_ORIGINS; i++ )
  619. {
  620. if ( pOrigins[i] == k_EControllerActionOrigin_None )
  621. break;
  622. m_pchBindingChoices[ m_iBindingChoicesCount ] = g_SteamControllerOriginStrings[ pOrigins[i] ];
  623. m_iBindChoicesOriginalToken[ m_iBindingChoicesCount ] = nOriginalToken;
  624. m_iBindingChoicesCount++;
  625. if ( m_iBindingChoicesCount >= MAX_LOCATOR_BINDINGS_SHOWN )
  626. break;
  627. }
  628. }
  629. //-----------------------------------------------------------------------------
  630. // Purpose:
  631. //-----------------------------------------------------------------------------
  632. const char *CLocatorTarget::UseBindingImage( char *pchIconTextureName, size_t bufSize )
  633. {
  634. if ( m_iBindingChoicesCount <= 0 )
  635. {
  636. if ( IsGameConsole() )
  637. {
  638. Q_strncpy( pchIconTextureName, "icon_blank_wide", bufSize );
  639. }
  640. else
  641. {
  642. Q_strncpy( pchIconTextureName, "icon_key_wide", bufSize );
  643. return "#GameUI_Icons_NONE";
  644. }
  645. return NULL;
  646. }
  647. // Cycle through the list of binds at a rate of 2 per second
  648. const char *pchBinding = m_pchBindingChoices[ m_iBindingTick % m_iBindingChoicesCount ];
  649. // We counted at least one binding... this should not be NULL!
  650. Assert( pchBinding );
  651. if ( IsGameConsole() )
  652. {
  653. // Use a blank background for the button icons
  654. Q_strncpy( pchIconTextureName, "icon_blank", bufSize );
  655. return pchBinding;
  656. }
  657. // Steam controller overrides all actions now
  658. if ( g_pInputSystem->IsSteamControllerActive() )
  659. {
  660. // Use a blank background for the button icons
  661. Q_strncpy( pchIconTextureName, "icon_blank", bufSize );
  662. return pchBinding;
  663. }
  664. //icon_blank_wide
  665. /*
  666. if ( input->ControllerModeActive() &&
  667. ( Q_strcmp( pchBinding, "A_BUTTON" ) == 0 ||
  668. Q_strcmp( pchBinding, "B_BUTTON" ) == 0 ||
  669. Q_strcmp( pchBinding, "X_BUTTON" ) == 0 ||
  670. Q_strcmp( pchBinding, "Y_BUTTON" ) == 0 ||
  671. Q_strcmp( pchBinding, "L_SHOULDER" ) == 0 ||
  672. Q_strcmp( pchBinding, "R_SHOULDER" ) == 0 ||
  673. Q_strcmp( pchBinding, "L_TRIGGER" ) == 0 ||
  674. Q_strcmp( pchBinding, "R_TRIGGER" ) == 0 ||
  675. Q_strcmp( pchBinding, "BACK" ) == 0 ||
  676. Q_strcmp( pchBinding, "START" ) == 0 ||
  677. Q_strcmp( pchBinding, "STICK1" ) == 0 ||
  678. Q_strcmp( pchBinding, "STICK2" ) == 0 ||
  679. Q_strcmp( pchBinding, "UP" ) == 0 ||
  680. Q_strcmp( pchBinding, "DOWN" ) == 0 ||
  681. Q_strcmp( pchBinding, "LEFT" ) == 0 ||
  682. Q_strcmp( pchBinding, "RIGHT" ) == 0 ) )
  683. {
  684. // Use a blank background for the button icons
  685. Q_strncpy( pchIconTextureName, "icon_blank", bufSize );
  686. return pchBinding;
  687. }
  688. */
  689. if ( Q_strcmp( pchBinding, "MOUSE1" ) == 0 )
  690. {
  691. Q_strncpy( pchIconTextureName, "icon_mouseLeft", bufSize );
  692. return NULL;
  693. }
  694. else if ( Q_strcmp( pchBinding, "MOUSE2" ) == 0 )
  695. {
  696. Q_strncpy( pchIconTextureName, "icon_mouseRight", bufSize );
  697. return NULL;
  698. }
  699. else if ( Q_strcmp( pchBinding, "MOUSE3" ) == 0 )
  700. {
  701. Q_strncpy( pchIconTextureName, "icon_mouseThree", bufSize );
  702. return NULL;
  703. }
  704. else if ( Q_strcmp( pchBinding, "MWHEELUP" ) == 0 )
  705. {
  706. Q_strncpy( pchIconTextureName, "icon_mouseWheel_up", bufSize );
  707. return NULL;
  708. }
  709. else if ( Q_strcmp( pchBinding, "MWHEELDOWN" ) == 0 )
  710. {
  711. Q_strncpy( pchIconTextureName, "icon_mouseWheel_down", bufSize );
  712. return NULL;
  713. }
  714. else if ( Q_strcmp( pchBinding, "UPARROW" ) == 0 )
  715. {
  716. Q_strncpy( pchIconTextureName, "icon_key_up", bufSize );
  717. return NULL;
  718. }
  719. else if ( Q_strcmp( pchBinding, "LEFTARROW" ) == 0 )
  720. {
  721. Q_strncpy( pchIconTextureName, "icon_key_left", bufSize );
  722. return NULL;
  723. }
  724. else if ( Q_strcmp( pchBinding, "DOWNARROW" ) == 0 )
  725. {
  726. Q_strncpy( pchIconTextureName, "icon_key_down", bufSize );
  727. return NULL;
  728. }
  729. else if ( Q_strcmp( pchBinding, "RIGHTARROW" ) == 0 )
  730. {
  731. Q_strncpy( pchIconTextureName, "icon_key_right", bufSize );
  732. return NULL;
  733. }
  734. else if ( Q_strcmp( pchBinding, "SEMICOLON" ) == 0 ||
  735. Q_strcmp( pchBinding, "INS" ) == 0 ||
  736. Q_strcmp( pchBinding, "DEL" ) == 0 ||
  737. Q_strcmp( pchBinding, "HOME" ) == 0 ||
  738. Q_strcmp( pchBinding, "END" ) == 0 ||
  739. Q_strcmp( pchBinding, "PGUP" ) == 0 ||
  740. Q_strcmp( pchBinding, "PGDN" ) == 0 ||
  741. Q_strcmp( pchBinding, "PAUSE" ) == 0 ||
  742. Q_strcmp( pchBinding, "F10" ) == 0 ||
  743. Q_strcmp( pchBinding, "F11" ) == 0 ||
  744. Q_strcmp( pchBinding, "F12" ) == 0 )
  745. {
  746. Q_strncpy( pchIconTextureName, "icon_key_generic", bufSize );
  747. return pchBinding;
  748. }
  749. else if ( Q_strlen( pchBinding ) <= 2 )
  750. {
  751. Q_strncpy( pchIconTextureName, "icon_key_generic", bufSize );
  752. return pchBinding;
  753. }
  754. else if ( Q_strlen( pchBinding ) <= 6 )
  755. {
  756. Q_strncpy( pchIconTextureName, "icon_key_wide", bufSize );
  757. return pchBinding;
  758. }
  759. else
  760. {
  761. Q_strncpy( pchIconTextureName, "icon_key_wide", bufSize );
  762. return pchBinding;
  763. }
  764. return pchBinding;
  765. }
  766. //-----------------------------------------------------------------------------
  767. int CLocatorTarget::GetIconWidth( void )
  768. {
  769. return m_wide;
  770. }
  771. //-----------------------------------------------------------------------------
  772. int CLocatorTarget::GetIconHeight( void )
  773. {
  774. return m_tall;
  775. }
  776. //-----------------------------------------------------------------------------
  777. // Purpose:
  778. //-----------------------------------------------------------------------------
  779. class CLocatorPanel : public vgui::EditablePanel
  780. {
  781. DECLARE_CLASS_SIMPLE( CLocatorPanel, vgui::EditablePanel );
  782. public:
  783. CLocatorPanel( vgui::Panel *parent, const char *name );
  784. ~CLocatorPanel( void );
  785. virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
  786. virtual void PerformLayout( void );
  787. virtual void OnTick( void );
  788. virtual void PaintBackground( void );
  789. virtual void Paint( void );
  790. void ValidateTexture( int *pTextureID, const char *pszTextureName );
  791. bool ValidateTargetTextures( CLocatorTarget *pTarget );
  792. bool IconsAreIntersecting( CLocatorTarget &first, CLocatorTarget &second, int iTolerance );
  793. virtual void PaintTarget( CLocatorTarget *pTarget );
  794. void DrawPointerBackground( CLocatorTarget *pTarget, int nPointerX, int nPointerY, int nWide, int nTall, bool bPointer );
  795. void DrawStaticIcon( CLocatorTarget *pTarget );
  796. void DrawDynamicIcon( CLocatorTarget *pTarget, bool bDrawCaption, bool bDrawSimpleArrow );
  797. void DrawIndicatorArrow( int x, int y, int iconWide, int iconTall, int textWidth, float fAngle );
  798. void DrawTargetCaption( CLocatorTarget *pTarget, int x, int y, bool bDrawMultiline );
  799. int GetScreenWidthForCaption( const wchar_t *pString, vgui::HFont hFont );
  800. void DrawBindingName( CLocatorTarget *pTarget, const char *pchBindingName, int x, int y, bool bController );
  801. void ComputeTargetIconPosition( CLocatorTarget *pTarget, bool bSetPosition );
  802. void CalculateOcclusion( CLocatorTarget *pTarget );
  803. void DrawSimpleArrow( int x, int y, int iconWide, int iconTall );
  804. void GetIconPositionForOffscreenTarget( const Vector &vecDelta, float flDist, int *pXPos, int *pYPos );
  805. CLocatorTarget *GetPointerForHandle( int hTarget );
  806. int AddTarget();
  807. void RemoveTarget( int hTarget );
  808. void GetTargetPosition( const Vector &vecDelta, float flRadius, float *xpos, float *ypos, float *flRotation );
  809. void DeactivateAllTargets();
  810. void CollectGarbage();
  811. // Animation
  812. void AnimateIconSize( int flags, int *wide, int *tall, float fPulseStart, float flDistFromPlayer );
  813. void AnimateIconPosition( int flags, int *x, int *y );
  814. void AnimateIconAlpha( int flags, int *alpha, float fadeStart );
  815. private:
  816. CPanelAnimationVar( vgui::HFont, m_hCaptionFont, "font", "InstructorTitle" );
  817. CPanelAnimationVar( vgui::HFont, m_hCaptionFont_ss, "font", "InstructorTitle_ss" );
  818. CPanelAnimationVar( vgui::HFont, m_hButtonFont, "font", "InstructorButtons" );
  819. #ifdef TERROR
  820. CPanelAnimationVar( vgui::HFont, m_hButtonFont_ss, "font", "GameUIButtons" );
  821. CPanelAnimationVar( vgui::HFont, m_hKeysFont, "font", "MenuSubTitle" );
  822. #else
  823. CPanelAnimationVar( vgui::HFont, m_hButtonFont_ss, "font", "InstructorButtons_ss" );
  824. CPanelAnimationVar( vgui::HFont, m_hKeysFont, "font", "InstructorKeyBindings" );
  825. CPanelAnimationVar( vgui::HFont, m_hKeysFontSmall, "font", "InstructorKeyBindingsSmall" );
  826. #endif
  827. CPanelAnimationVar( vgui::HFont, m_hButtonFontSC, "font", "InstructorButtonsSteamController" );
  828. CPanelAnimationVar( int, m_iShouldWrapStaticLocators, "WrapStaticLocators", "0" );
  829. static int m_serializer; // Used to issue unique serial numbers to targets, for use as handles
  830. int m_textureID_ArrowRight;
  831. int m_textureID_ArrowLeft;
  832. int m_textureID_ArrowUp;
  833. int m_textureID_ArrowDown;
  834. int m_textureID_SimpleArrow;
  835. int m_staticIconPosition;// Helps us stack static icons
  836. CLocatorTarget m_targets[MAX_LOCATOR_TARGETS];
  837. };
  838. //-----------------------------------------------------------------------------
  839. // Local variables
  840. //-----------------------------------------------------------------------------
  841. static CLocatorPanel *s_pLocatorPanel[ MAX_SPLITSCREEN_PLAYERS ];
  842. inline CLocatorPanel *GetPlayerLocatorPanel()
  843. {
  844. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  845. if ( !engine->IsLocalPlayerResolvable() )
  846. return NULL;
  847. int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
  848. Assert( !( nSlot < 0 || nSlot >= ARRAYSIZE( s_pLocatorPanel ) ) );
  849. if ( nSlot < 0 || nSlot >= ARRAYSIZE( s_pLocatorPanel ) )
  850. return NULL;
  851. Assert( nSlot >= 0 && nSlot < MAX_SPLITSCREEN_PLAYERS );
  852. return s_pLocatorPanel[ nSlot ];
  853. }
  854. //-----------------------------------------------------------------------------
  855. // Static variable initialization
  856. //-----------------------------------------------------------------------------
  857. int CLocatorPanel::m_serializer = 1000; // Serial numbers start at 1000
  858. //-----------------------------------------------------------------------------
  859. // This is the interface function that other systems use to send us targets
  860. //-----------------------------------------------------------------------------
  861. int Locator_AddTarget()
  862. {
  863. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  864. int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
  865. if ( nSlot < 0 || nSlot >= ARRAYSIZE( s_pLocatorPanel ) )
  866. return -1;
  867. if( s_pLocatorPanel[ nSlot ] == NULL )
  868. {
  869. // Locator has not been used yet. Construct it.
  870. CLocatorPanel *pLocator = new CLocatorPanel( GetClientMode()->GetViewport(), "LocatorPanel" );
  871. vgui::SETUP_PANEL(pLocator);
  872. pLocator->SetBounds( 0, 0, ScreenWidth(), ScreenHeight() );
  873. pLocator->SetPos( 0, 0 );
  874. pLocator->SetVisible( true );
  875. vgui::ivgui()->AddTickSignal( pLocator->GetVPanel() );
  876. }
  877. Assert( s_pLocatorPanel[ nSlot ] != NULL );
  878. return s_pLocatorPanel[ nSlot ] ? s_pLocatorPanel[ nSlot ]->AddTarget() : -1;
  879. }
  880. //-----------------------------------------------------------------------------
  881. //-----------------------------------------------------------------------------
  882. void Locator_RemoveTarget( int hTarget )
  883. {
  884. if ( CLocatorPanel *pPanel = GetPlayerLocatorPanel() )
  885. {
  886. pPanel->RemoveTarget( hTarget );
  887. }
  888. }
  889. //-----------------------------------------------------------------------------
  890. //-----------------------------------------------------------------------------
  891. CLocatorTarget *Locator_GetTargetFromHandle( int hTarget )
  892. {
  893. if ( CLocatorPanel *pPanel = GetPlayerLocatorPanel() )
  894. return pPanel->GetPointerForHandle( hTarget );
  895. else
  896. return NULL;
  897. }
  898. void Locator_ComputeTargetIconPositionFromHandle( int hTarget )
  899. {
  900. if ( CLocatorPanel *pPanel = GetPlayerLocatorPanel() )
  901. {
  902. if ( CLocatorTarget *pTarget = pPanel->GetPointerForHandle( hTarget ) )
  903. {
  904. if( !( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_STATIC ) )
  905. {
  906. // It's not presenting in the middle of the screen, so figure out it's position
  907. pPanel->ComputeTargetIconPosition( pTarget, !pTarget->IsPresenting() );
  908. pPanel->CalculateOcclusion( pTarget );
  909. }
  910. }
  911. }
  912. }
  913. //-----------------------------------------------------------------------------
  914. // Purpose:
  915. //-----------------------------------------------------------------------------
  916. CLocatorPanel::CLocatorPanel( Panel *parent, const char *name ) : EditablePanel(parent,name)
  917. {
  918. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  919. int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
  920. Assert( s_pLocatorPanel[ nSlot ] == NULL );
  921. DeactivateAllTargets();
  922. s_pLocatorPanel[ nSlot ] = this;
  923. m_textureID_ArrowRight = -1;
  924. m_textureID_ArrowLeft = -1;
  925. m_textureID_ArrowUp = -1;
  926. m_textureID_ArrowDown = -1;
  927. m_textureID_SimpleArrow = -1;
  928. SetScheme( "clientscheme" );
  929. }
  930. //-----------------------------------------------------------------------------
  931. // Purpose:
  932. //-----------------------------------------------------------------------------
  933. CLocatorPanel::~CLocatorPanel( void )
  934. {
  935. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  936. int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
  937. Assert( s_pLocatorPanel[ nSlot ] == this );
  938. s_pLocatorPanel[ nSlot ] = NULL;
  939. }
  940. //-----------------------------------------------------------------------------
  941. // Purpose: Applies scheme settings
  942. //-----------------------------------------------------------------------------
  943. void CLocatorPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
  944. {
  945. BaseClass::ApplySchemeSettings( pScheme );
  946. LoadControlSettings( "resource/UI/Locator.res" );
  947. }
  948. //-----------------------------------------------------------------------------
  949. // Purpose:
  950. //-----------------------------------------------------------------------------
  951. void CLocatorPanel::PerformLayout( void )
  952. {
  953. BaseClass::PerformLayout();
  954. vgui::Panel *pPanel = FindChildByName( "LocatorBG" );
  955. if ( pPanel )
  956. {
  957. pPanel->SetPos( (GetWide() - pPanel->GetWide()) * 0.5, (GetTall() - pPanel->GetTall()) * 0.5 );
  958. }
  959. }
  960. //-----------------------------------------------------------------------------
  961. // Purpose: Given an offscreen target position, compute the 'compass position'
  962. // so that we can draw an icon on the imaginary circle around the crosshair
  963. // that indicates which way the player should turn to bring the target into view.
  964. //-----------------------------------------------------------------------------
  965. void CLocatorPanel::GetTargetPosition( const Vector &vecDelta, float flRadius, float *xpos, float *ypos, float *flRotation )
  966. {
  967. int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
  968. // Player Data
  969. Vector playerPosition = MainViewOrigin(nSlot);
  970. QAngle playerAngles = MainViewAngles(nSlot);
  971. Vector forward, right, up(0,0,1);
  972. AngleVectors (playerAngles, &forward, NULL, NULL );
  973. forward.z = 0;
  974. VectorNormalize(forward);
  975. CrossProduct( up, forward, right );
  976. float front = DotProduct(vecDelta, forward);
  977. float side = DotProduct(vecDelta, right);
  978. *xpos = flRadius * -side;
  979. *ypos = flRadius * -front;
  980. // Get the rotation (yaw)
  981. *flRotation = atan2(*xpos,*ypos) + M_PI;
  982. *flRotation *= 180 / M_PI;
  983. float yawRadians = -(*flRotation) * M_PI / 180.0f;
  984. float ca = cos( yawRadians );
  985. float sa = sin( yawRadians );
  986. // Rotate it around the circle, squash Y to make an oval rather than a circle
  987. *xpos = (int)((ScreenWidth() / 2) + (flRadius * sa));
  988. *ypos = (int)((ScreenHeight() / 2) - (flRadius * 0.6f * ca));
  989. }
  990. //-----------------------------------------------------------------------------
  991. //-----------------------------------------------------------------------------
  992. void CLocatorPanel::DeactivateAllTargets()
  993. {
  994. for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ )
  995. {
  996. m_targets[ i ].Deactivate( true );
  997. }
  998. }
  999. //-----------------------------------------------------------------------------
  1000. // Purpose: Deactivate any target that has not been updated within several frames
  1001. //-----------------------------------------------------------------------------
  1002. void CLocatorPanel::CollectGarbage()
  1003. {
  1004. for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ )
  1005. {
  1006. if( m_targets[ i ].m_isActive )
  1007. {
  1008. if( gpGlobals->framecount - m_targets[ i ].m_frameLastUpdated > 20 )
  1009. {
  1010. m_targets[ i ].Deactivate();
  1011. }
  1012. }
  1013. }
  1014. }
  1015. //-----------------------------------------------------------------------------
  1016. // Purpose: Provide simple animation by modifying the width and height of the
  1017. // icon before it is drawn.
  1018. //-----------------------------------------------------------------------------
  1019. void CLocatorPanel::AnimateIconSize( int flags, int *wide, int *tall, float fPulseStart, float flDistFromPlayer )
  1020. {
  1021. static ConVarRef ss_verticalsplit( "ss_verticalsplit" );
  1022. float flScale = MIN_ICON_SCALE;
  1023. float scaleDelta = MAX_ICON_SCALE - MIN_ICON_SCALE;
  1024. float newWide = *wide;
  1025. float newTall = *tall;
  1026. if( flags & LOCATOR_ICON_FX_PULSE_SLOW || gpGlobals->curtime - fPulseStart < locator_pulse_time.GetFloat() )
  1027. {
  1028. flScale += scaleDelta * fabs( sin( ( gpGlobals->curtime - fPulseStart ) * M_PI ) );
  1029. }
  1030. else if( flags & LOCATOR_ICON_FX_PULSE_FAST )
  1031. {
  1032. flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 2 * M_PI ) );
  1033. }
  1034. else if( flags & LOCATOR_ICON_FX_PULSE_URGENT )
  1035. {
  1036. flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 4 * M_PI ) );
  1037. }
  1038. if ( newWide > newTall )
  1039. {
  1040. // Get scale to make width change by only the standard height amount of pixels
  1041. int iHeightDelta = (int)(newTall * flScale - newTall);
  1042. flScale = ( newWide + iHeightDelta ) / newWide;
  1043. }
  1044. newWide = newWide * flScale;
  1045. newTall = newTall * flScale;
  1046. if ( flags & LOCATOR_ICON_FX_SCALE_BY_DIST )
  1047. {
  1048. const float flMaxDist = 1800.0f;
  1049. const float flMinDist = 64.0f;
  1050. const float flMaxIconScaler = 4.0f;
  1051. const float flMinIconScaler = IsLocatorSplitscreen() ? 2.0f : 1.0f;
  1052. float flInterp = 1.0f - clamp( (flDistFromPlayer - flMinDist) / (flMaxDist - flMinDist), 0.0f, 1.0f );
  1053. float flScaler = (((flMaxIconScaler - flMinIconScaler) * (flInterp*flInterp) ) + flMinIconScaler) * 0.45f;
  1054. newTall *= flScaler;
  1055. newWide *= flScaler;
  1056. //Msg( "SCALER = %f, dist = %f, wide = %f, tall = %f\n", flScaler, flDistFromPlayer, newWide, newTall );
  1057. }
  1058. if ( flags & LOCATOR_ICON_FX_SCALE_LARGE )
  1059. {
  1060. newTall *= 3.0f;
  1061. newWide *= 3.0f;
  1062. }
  1063. *wide = newWide;
  1064. *tall = newTall;
  1065. }
  1066. //-----------------------------------------------------------------------------
  1067. // Purpose: Modify the alpha of the icon before it is drawn.
  1068. //-----------------------------------------------------------------------------
  1069. void CLocatorPanel::AnimateIconAlpha( int flags, int *alpha, float fadeStart )
  1070. {
  1071. float flScale = MIN_ICON_ALPHA;
  1072. float scaleDelta = MAX_ICON_ALPHA - MIN_ICON_ALPHA;
  1073. if( flags & LOCATOR_ICON_FX_ALPHA_SLOW )
  1074. {
  1075. flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 3 ) );
  1076. }
  1077. else if( flags & LOCATOR_ICON_FX_ALPHA_FAST )
  1078. {
  1079. flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 7 ) );
  1080. }
  1081. else if( flags & LOCATOR_ICON_FX_ALPHA_URGENT )
  1082. {
  1083. flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 10 ) );
  1084. }
  1085. else
  1086. {
  1087. flScale = MAX_ICON_ALPHA;
  1088. }
  1089. if ( flags & LOCATOR_ICON_FX_FADE_OUT )
  1090. {
  1091. flScale *= MAX( 0.0f, ( locator_fade_time.GetFloat() - ( gpGlobals->curtime - fadeStart ) ) / locator_fade_time.GetFloat() );
  1092. }
  1093. else if ( flags & LOCATOR_ICON_FX_FADE_IN )
  1094. {
  1095. flScale *= MAX_ICON_ALPHA - MAX( 0.0f, ( locator_fade_time.GetFloat() - ( gpGlobals->curtime - fadeStart ) ) / locator_fade_time.GetFloat() );
  1096. }
  1097. *alpha = static_cast<int>( 255.0f * flScale );
  1098. }
  1099. //-----------------------------------------------------------------------------
  1100. // Purpose:
  1101. //-----------------------------------------------------------------------------
  1102. void CLocatorPanel::AnimateIconPosition( int flags, int *x, int *y )
  1103. {
  1104. int newX = *x;
  1105. int newY = *y;
  1106. if( flags & LOCATOR_ICON_FX_SHAKE_NARROW )
  1107. {
  1108. newX += RandomInt( -2, 2 );
  1109. newY += RandomInt( -2, 2 );
  1110. }
  1111. else if( flags & LOCATOR_ICON_FX_SHAKE_WIDE )
  1112. {
  1113. newX += RandomInt( -5, 5 );
  1114. newY += RandomInt( -5, 5 );
  1115. }
  1116. *x = newX;
  1117. *y = newY;
  1118. }
  1119. //-----------------------------------------------------------------------------
  1120. // Purpose:
  1121. //-----------------------------------------------------------------------------
  1122. void CLocatorPanel::OnTick( void )
  1123. {
  1124. }
  1125. //-----------------------------------------------------------------------------
  1126. // Purpose:
  1127. //-----------------------------------------------------------------------------
  1128. void CLocatorPanel::PaintBackground( void )
  1129. {
  1130. return;
  1131. }
  1132. //-----------------------------------------------------------------------------
  1133. // Purpose:
  1134. //-----------------------------------------------------------------------------
  1135. void CLocatorPanel::Paint( void )
  1136. {
  1137. ValidateTexture( &m_textureID_ArrowLeft, "vgui/hud/icon_arrow_left" );
  1138. ValidateTexture( &m_textureID_ArrowRight, "vgui/hud/icon_arrow_right" );
  1139. ValidateTexture( &m_textureID_ArrowUp, "vgui/hud/icon_arrow_up" );
  1140. ValidateTexture( &m_textureID_ArrowDown, "vgui/hud/icon_arrow_down" );
  1141. ValidateTexture( &m_textureID_SimpleArrow, "vgui/hud/icon_arrow_plain" );
  1142. // reset the static icon position. This is the y position at which the first
  1143. // static icon will be drawn. This value will be incremented by the height of
  1144. // each static icon drawn, which forces the next static icon to be drawn below
  1145. // the previous one, creating a little fixed, vertical stack below the crosshair.
  1146. m_staticIconPosition = (ScreenHeight()/2) * locator_screen_pos_y.GetFloat();
  1147. // Time now to draw the 'dynamic' icons, the icons which help players locate things
  1148. // in actual world space.
  1149. //----------
  1150. // Batch 1
  1151. // Go through all of the active locator targets and compute where to draw the icons
  1152. // that represent each of them. This builds a poor man's draw list by updating the
  1153. // m_iconX, m_iconY members of each locator target.
  1154. CUtlVectorFixed< CLocatorTarget *, MAX_LOCATOR_TARGETS > vecValid;
  1155. for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ )
  1156. {
  1157. CLocatorTarget *pLocatorTarget = &(m_targets[ i ]);
  1158. // Reset drawing state for this frame... set back to true when it's finally draws
  1159. pLocatorTarget->m_bIsDrawing = false;
  1160. if ( ( !pLocatorTarget->m_bVisible && !pLocatorTarget->m_alpha ) || !pLocatorTarget->m_isActive )
  1161. {
  1162. // Don't want to be visible and have finished fading
  1163. continue;
  1164. }
  1165. vecValid.AddToTail( pLocatorTarget );
  1166. // This prevents an error that if a locator was fading as the map transitioned
  1167. pLocatorTarget->m_fadeStart = fpmin( pLocatorTarget->m_fadeStart, gpGlobals->curtime );
  1168. if( !( pLocatorTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_STATIC ) )
  1169. {
  1170. // It's not presenting in the middle of the screen, so figure out it's position
  1171. ComputeTargetIconPosition( pLocatorTarget, !pLocatorTarget->IsPresenting() );
  1172. CalculateOcclusion( pLocatorTarget );
  1173. pLocatorTarget->m_lastDeclutterIndex = pLocatorTarget->m_declutterIndex;
  1174. pLocatorTarget->m_declutterIndex = 0;
  1175. }
  1176. }
  1177. //----------
  1178. // Batch 2
  1179. // Now that we know where each icon _wants_ to be drawn, we grovel through them and
  1180. // push apart any icons that are too close to one another. This helps to unclutter
  1181. // the display and ensure the maximum number of legible icons and captions. Obviously
  1182. // this process changes where some icons will be drawn. Bubble sort, but tiny data set.
  1183. int iTolerance = 1.25 * (ScreenWidth() * ICON_SIZE);
  1184. int iterations = 0;// Count iterations, don't go infinite in the event of some weird case.
  1185. bool bStillUncluttering = true;
  1186. static int MAX_UNCLUTTER_ITERATIONS = 10;
  1187. while( iterations < MAX_UNCLUTTER_ITERATIONS && bStillUncluttering )
  1188. {
  1189. iterations++;
  1190. bStillUncluttering = false;
  1191. for( int i = 0 ; i < vecValid.Count() ; ++i )
  1192. {
  1193. CLocatorTarget *pLocatorTarget1 = vecValid[ i ];
  1194. for( int j = i + 1 ; j < vecValid.Count() ; ++j )
  1195. {
  1196. CLocatorTarget *pLocatorTarget2 = vecValid[ j ];
  1197. // Don't attempt to declutter icons if one or both is attempting to fade out
  1198. bool bLocatorsFullyActive = !((pLocatorTarget1->GetIconEffectsFlags()|pLocatorTarget2->GetIconEffectsFlags()) & LOCATOR_ICON_FX_FADE_OUT);
  1199. if ( bLocatorsFullyActive && IconsAreIntersecting( *pLocatorTarget1, *pLocatorTarget2, iTolerance ) )
  1200. {
  1201. // Unclutter. Lift whichever icon is highest a bit higher
  1202. if( pLocatorTarget1->m_iconY < pLocatorTarget2->m_iconY )
  1203. {
  1204. pLocatorTarget1->m_iconY = pLocatorTarget2->m_iconY - iTolerance;
  1205. pLocatorTarget1->m_centerY = pLocatorTarget2->m_centerY - iTolerance;
  1206. pLocatorTarget1->m_declutterIndex -= 1;
  1207. }
  1208. else
  1209. {
  1210. pLocatorTarget2->m_iconY = pLocatorTarget1->m_iconY - iTolerance;
  1211. pLocatorTarget2->m_centerY = pLocatorTarget1->m_centerY - iTolerance;
  1212. pLocatorTarget2->m_declutterIndex -= 1;
  1213. }
  1214. bStillUncluttering = true;
  1215. }
  1216. }
  1217. }
  1218. }
  1219. if( iterations == MAX_UNCLUTTER_ITERATIONS )
  1220. {
  1221. DevWarning( "Game instructor hit MAX_UNCLUTTER_ITERATIONS!\n");
  1222. }
  1223. float flLocatorLerpRest = locator_lerp_rest.GetFloat();
  1224. float flLocatorLerpTime = locator_lerp_time.GetFloat();
  1225. //----------
  1226. // Batch 3
  1227. // Draw each of the icons.
  1228. for( int i = 0 ; i < vecValid.Count() ; i++ )
  1229. {
  1230. CLocatorTarget *pLocatorTarget = vecValid[ i ];
  1231. // Back to lerping for these guys
  1232. if ( pLocatorTarget->m_lastDeclutterIndex != pLocatorTarget->m_declutterIndex )
  1233. {
  1234. // It wants to be popped to another position... do it smoothly
  1235. pLocatorTarget->StartTimedLerp();
  1236. }
  1237. // Lerp to the desired position
  1238. float flLerpTime = gpGlobals->curtime - pLocatorTarget->m_lerpStart;
  1239. if ( flLerpTime >= flLocatorLerpRest && flLerpTime < flLocatorLerpRest + flLocatorLerpTime )
  1240. {
  1241. // Lerp slow to fast
  1242. float fInterp = 1.0f - ( ( flLocatorLerpTime - ( flLerpTime - flLocatorLerpRest ) ) / flLocatorLerpTime );
  1243. // Get our desired position
  1244. float iconX = pLocatorTarget->m_iconX;
  1245. float iconY = pLocatorTarget->m_iconY;
  1246. // Get the distance we need to go to reach it
  1247. float diffX = fabsf( pLocatorTarget->m_iconX - pLocatorTarget->m_lastXPos );
  1248. float diffY = fabsf( pLocatorTarget->m_iconY - pLocatorTarget->m_lastYPos );
  1249. // Go from our current position toward the desired position as quick as the interp allows
  1250. pLocatorTarget->m_iconX = static_cast<int>( Approach( iconX, pLocatorTarget->m_lastXPos, diffX * fInterp ) );
  1251. pLocatorTarget->m_iconY = static_cast<int>( Approach( iconY, pLocatorTarget->m_lastYPos, diffY * fInterp ) );
  1252. // Get how much our position changed and apply it to the center values
  1253. int iOffsetX = pLocatorTarget->m_iconX - iconX;
  1254. int iOffsetY = pLocatorTarget->m_iconY - iconY;
  1255. pLocatorTarget->m_centerX += iOffsetX;
  1256. pLocatorTarget->m_centerY += iOffsetY;
  1257. if ( iOffsetX < 3 && iOffsetY < 3 )
  1258. {
  1259. // Near our target! Stop lerping!
  1260. flLerpTime = flLocatorLerpRest + flLocatorLerpTime;
  1261. }
  1262. }
  1263. PaintTarget( pLocatorTarget );
  1264. }
  1265. CollectGarbage();
  1266. }
  1267. //-----------------------------------------------------------------------------
  1268. // Purpose: A helper function to save on typing. Make sure our texture ID's
  1269. // stay valid.
  1270. //-----------------------------------------------------------------------------
  1271. void CLocatorPanel::ValidateTexture( int *pTextureID, const char *pszTextureName )
  1272. {
  1273. if( *pTextureID == -1 )
  1274. {
  1275. *pTextureID = vgui::surface()->CreateNewTextureID();
  1276. vgui::surface()->DrawSetTextureFile( *pTextureID, pszTextureName, true, false );
  1277. }
  1278. }
  1279. //-----------------------------------------------------------------------------
  1280. // Purpose: Called every frame before painting the targets. Ensures that the
  1281. // target's textures are properly cached.
  1282. //-----------------------------------------------------------------------------
  1283. bool CLocatorPanel::ValidateTargetTextures( CLocatorTarget *pTarget )
  1284. {
  1285. bool bBindingTick = false;
  1286. if ( gpGlobals->curtime >= pTarget->m_flNextBindingTick )
  1287. {
  1288. if ( pTarget->m_iBindingChoicesCount > 1 )
  1289. {
  1290. bBindingTick = true;
  1291. pTarget->m_iBindingTick++;
  1292. }
  1293. pTarget->m_flNextBindingTick = gpGlobals->curtime + 1.25f;
  1294. pTarget->UpdateVguiTarget();
  1295. }
  1296. bool bUsesBinding = ( Q_stricmp( pTarget->GetOnscreenIconTextureName(), "use_binding" ) == 0 );
  1297. if( !pTarget->m_pIcon_onscreen || !pTarget->m_pIcon_offscreen || ( bUsesBinding && bBindingTick ) )
  1298. {
  1299. char szIconTextureName[ 256 ];
  1300. if ( bUsesBinding )
  1301. {
  1302. const char *pchDrawBindingName = pTarget->UseBindingImage( szIconTextureName, sizeof( szIconTextureName ) );
  1303. pTarget->m_bDrawControllerButton = ( Q_strcmp( szIconTextureName, "icon_blank" ) == 0 ) || ( Q_strcmp( szIconTextureName, "icon_blank_wide" ) == 0 );
  1304. pTarget->DrawBindingName( pchDrawBindingName );
  1305. }
  1306. else
  1307. {
  1308. pTarget->m_bDrawControllerButton = false;
  1309. V_strcpy_safe( szIconTextureName, pTarget->GetOnscreenIconTextureName() );
  1310. pTarget->DrawBindingName( NULL );
  1311. }
  1312. // This target's texture ID is dirty, meaning the target is about to be drawn
  1313. // for the first time, or about to be drawn for the first time since a texture
  1314. // was changed.
  1315. if ( Q_strlen(szIconTextureName) == 0 )
  1316. {
  1317. if ( !pTarget->m_bIconNoTarget )
  1318. DevWarning("Locator Target has no onscreen texture name!\n");
  1319. return false;
  1320. }
  1321. else
  1322. {
  1323. pTarget->m_pIcon_onscreen = HudIcons().GetIcon( szIconTextureName );
  1324. if ( pTarget->m_pIcon_onscreen )
  1325. {
  1326. pTarget->m_widthScale_onscreen = static_cast< float >( pTarget->m_pIcon_onscreen->Width() ) / pTarget->m_pIcon_onscreen->Height();
  1327. }
  1328. else
  1329. {
  1330. pTarget->m_widthScale_onscreen = 1.0f;
  1331. }
  1332. }
  1333. if ( Q_stricmp( pTarget->GetOffscreenIconTextureName() , "use_binding" ) == 0 )
  1334. {
  1335. const char *pchDrawBindingName = pTarget->UseBindingImage( szIconTextureName, sizeof( szIconTextureName ) );
  1336. pTarget->m_bDrawControllerButtonOffscreen = ( Q_strcmp( szIconTextureName, "icon_blank" ) == 0 );
  1337. pTarget->DrawBindingNameOffscreen( pchDrawBindingName );
  1338. }
  1339. else
  1340. {
  1341. pTarget->m_bDrawControllerButtonOffscreen = false;
  1342. V_strcpy_safe( szIconTextureName, pTarget->GetOffscreenIconTextureName() );
  1343. pTarget->DrawBindingNameOffscreen( NULL );
  1344. }
  1345. if( Q_strlen(szIconTextureName) == 0 )
  1346. {
  1347. if( !pTarget->m_pIcon_onscreen )
  1348. {
  1349. DevWarning("Locator Target has no offscreen texture name and can't fall back!\n");
  1350. }
  1351. else
  1352. {
  1353. // The onscreen texture is valid, so default behavior is to use that.
  1354. pTarget->m_pIcon_offscreen = pTarget->m_pIcon_onscreen;
  1355. const char *pchDrawBindingName = pTarget->DrawBindingName();
  1356. pTarget->DrawBindingNameOffscreen( pchDrawBindingName );
  1357. }
  1358. }
  1359. else
  1360. {
  1361. pTarget->m_pIcon_offscreen = HudIcons().GetIcon( szIconTextureName );
  1362. }
  1363. return true;
  1364. }
  1365. return false;
  1366. }
  1367. //-----------------------------------------------------------------------------
  1368. // Purpose: Compute where on the screen to draw the icon for this target.
  1369. //-----------------------------------------------------------------------------
  1370. void CLocatorPanel::ComputeTargetIconPosition( CLocatorTarget *pTarget, bool bSetPosition )
  1371. {
  1372. int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
  1373. int iconX;
  1374. int iconY;
  1375. // Measure the delta and the dist from this player to this target.
  1376. Vector vecTarget = pTarget->m_vecOrigin;
  1377. Vector vecDelta = vecTarget - MainViewOrigin(nSlot);
  1378. if ( pTarget->m_bOriginInScreenspace )
  1379. {
  1380. // Coordinates are already in screenspace
  1381. pTarget->m_distFromPlayer = 0.0f;
  1382. iconX = vecTarget.x * ScreenWidth();
  1383. iconY = vecTarget.y * ScreenHeight();
  1384. pTarget->m_targetX = iconX;
  1385. pTarget->m_targetY = iconY;
  1386. }
  1387. else
  1388. {
  1389. pTarget->m_distFromPlayer = VectorNormalize( vecDelta );
  1390. if ( GetVectorInScreenSpace( vecTarget, iconX, iconY ) )
  1391. {
  1392. // NOTE: GetVectorInScreenSpace returns false in an edge case where the
  1393. // target is very far off screen... just us the old values
  1394. pTarget->m_targetX = iconX;
  1395. pTarget->m_targetY = iconY;
  1396. }
  1397. }
  1398. pTarget->m_bDrawArrow = false;
  1399. float fTitleSafeInset = ScreenWidth() * 0.075f;
  1400. if ( iconX < fTitleSafeInset || iconX > ScreenWidth() - fTitleSafeInset || iconY < fTitleSafeInset || iconY > ScreenHeight() - fTitleSafeInset )
  1401. {
  1402. // It's off the screen left or right.
  1403. if ( pTarget->m_bOnscreen && !( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_NO_OFFSCREEN ) )
  1404. {
  1405. // Back to lerping
  1406. pTarget->StartTimedLerp();
  1407. pTarget->m_pulseStart = gpGlobals->curtime;
  1408. }
  1409. if ( bSetPosition )
  1410. {
  1411. pTarget->m_bOnscreen = false;
  1412. }
  1413. GetIconPositionForOffscreenTarget( vecDelta, pTarget->m_distFromPlayer, &iconX, &iconY );
  1414. pTarget->m_bDrawArrow = true;
  1415. // Figure out the arrow angle
  1416. Vector vOffsetNormal = pTarget->m_vecOrigin - MainViewOrigin(nSlot);
  1417. VectorNormalize( vOffsetNormal );
  1418. float fRightDot = MainViewRight(nSlot).Dot( vOffsetNormal );
  1419. float fUpDot = MainViewUp(nSlot).Dot( vOffsetNormal );
  1420. pTarget->m_fDrawArrowAngle = RemapVal( fUpDot, -1.0f, 1.0f, 0.0f, 180.0f ) * ( fRightDot > 0 ? 1.0f : -1.0f );
  1421. }
  1422. else
  1423. {
  1424. if ( !pTarget->m_bOnscreen && !( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_NO_OFFSCREEN ) )
  1425. {
  1426. // Back to lerping
  1427. pTarget->StartTimedLerp();
  1428. pTarget->m_pulseStart = gpGlobals->curtime;
  1429. }
  1430. pTarget->m_bOnscreen = true;
  1431. }
  1432. if ( bSetPosition )
  1433. {
  1434. int tall = ScreenWidth() * ICON_SIZE;
  1435. int wide = tall * pTarget->m_widthScale_onscreen;
  1436. // Animate the icon
  1437. AnimateIconSize( pTarget->GetIconEffectsFlags(), &wide, &tall, pTarget->m_pulseStart, pTarget->m_distFromPlayer );
  1438. AnimateIconPosition( pTarget->GetIconEffectsFlags(), &iconX, &iconY );
  1439. AnimateIconAlpha( pTarget->GetIconEffectsFlags(), &pTarget->m_alpha, pTarget->m_fadeStart );
  1440. if( !(pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_SCALE_BY_DIST) && !(pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_SCALE_LARGE) && pTarget->m_distFromPlayer > ICON_DIST_TOO_FAR && !locator_topdown_style.GetBool() )
  1441. {
  1442. // Make the icon smaller
  1443. wide = wide >> 1;
  1444. tall = tall >> 1;
  1445. }
  1446. pTarget->m_centerX = iconX;
  1447. pTarget->m_centerY = iconY;
  1448. pTarget->m_iconX = pTarget->m_centerX - ( wide >> 1 );
  1449. pTarget->m_iconY = pTarget->m_centerY - ( tall >> 1 );
  1450. pTarget->m_wide = wide;
  1451. pTarget->m_tall = tall;
  1452. }
  1453. }
  1454. void CLocatorPanel::CalculateOcclusion( CLocatorTarget *pTarget )
  1455. {
  1456. int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
  1457. if ( gpGlobals->curtime >= pTarget->m_flNextOcclusionTest )
  1458. {
  1459. pTarget->m_flNextOcclusionTest = gpGlobals->curtime + LOCATOR_OCCLUSION_TEST_RATE;
  1460. // Assume the target is not occluded.
  1461. pTarget->m_bOccluded = false;
  1462. if ( pTarget->m_bOriginInScreenspace )
  1463. return;
  1464. trace_t tr;
  1465. UTIL_TraceLine( pTarget->m_vecOrigin, MainViewOrigin(nSlot), (CONTENTS_SOLID|CONTENTS_MOVEABLE), NULL, COLLISION_GROUP_NONE, &tr );
  1466. if ( tr.fraction < 1.0f )
  1467. {
  1468. pTarget->m_bOccluded = true;
  1469. }
  1470. }
  1471. }
  1472. //-----------------------------------------------------------------------------
  1473. // Purpose: This is not valid until after you have computed the onscreen
  1474. // icon position for each target!
  1475. //-----------------------------------------------------------------------------
  1476. bool CLocatorPanel::IconsAreIntersecting( CLocatorTarget &first, CLocatorTarget &second, int iTolerance )
  1477. {
  1478. if( first.m_bOnscreen != second.m_bOnscreen )
  1479. {
  1480. // We only declutter onscreen icons against other onscreen icons and vice-versa.
  1481. return false;
  1482. }
  1483. if( first.IsStatic() || second.IsStatic() )
  1484. {
  1485. // Static icons don't count.
  1486. return false;
  1487. }
  1488. if( abs(first.GetIconY() - second.GetIconY()) < iTolerance )
  1489. {
  1490. // OK, we need the Y-check first. Now we have to see if these icons and their captions overlap.
  1491. int firstWide = iTolerance + first.m_captionWide;
  1492. int secondWide = iTolerance + second.m_captionWide;
  1493. if( abs(first.GetIconX() - second.GetIconX()) < (firstWide + secondWide) / 2 )
  1494. {
  1495. return true;
  1496. }
  1497. }
  1498. return false;
  1499. }
  1500. //-----------------------------------------------------------------------------
  1501. // Purpose: Draw this target on the locator.
  1502. //
  1503. // IF onscreen and visible, draw no icon, draw no arrows
  1504. // IF onscreen and occluded, draw icon transparently, draw no arrows
  1505. // IF offscreen, draw icon, draw an arrow indicating the direction to the target
  1506. //-----------------------------------------------------------------------------
  1507. void CLocatorPanel::PaintTarget( CLocatorTarget *pTarget )
  1508. {
  1509. bool bNewTexture = ValidateTargetTextures( pTarget );
  1510. if ( bNewTexture )
  1511. {
  1512. // Refigure the width/height for the new texture
  1513. int tall = ScreenWidth() * ICON_SIZE;
  1514. int wide = tall * pTarget->m_widthScale_onscreen;
  1515. AnimateIconSize( pTarget->GetIconEffectsFlags(), &wide, &tall, pTarget->m_pulseStart, pTarget->m_distFromPlayer );
  1516. pTarget->m_wide = wide;
  1517. pTarget->m_tall = tall;
  1518. }
  1519. // A static icon just draws with other static icons in a stack under the crosshair.
  1520. // Once displayed, they do not move. The are often used for notifiers.
  1521. if( pTarget->IsStatic() )
  1522. {
  1523. DrawStaticIcon( pTarget );
  1524. return;
  1525. }
  1526. if ( !pTarget->m_bOnscreen && ( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_NO_OFFSCREEN ) )
  1527. {
  1528. // Doesn't draw when offscreen... reset it's alpha so it has to fade in again
  1529. pTarget->m_fadeStart = gpGlobals->curtime;
  1530. pTarget->m_alpha = 0;
  1531. }
  1532. else
  1533. {
  1534. // Save these coordinates for later lerping
  1535. pTarget->m_lastXPos = pTarget->m_iconX;
  1536. pTarget->m_lastYPos = pTarget->m_iconY;
  1537. // Draw when it's on screen or allowed to draw offscreen
  1538. DrawDynamicIcon( pTarget, pTarget->HasCaptionText(), pTarget->m_bOnscreen );
  1539. }
  1540. }
  1541. //-----------------------------------------------------------------------------
  1542. // Purpose: Draws the caption-like background with word-bubble style pointer
  1543. //-----------------------------------------------------------------------------
  1544. void CLocatorPanel::DrawPointerBackground( CLocatorTarget *pTarget, int nPointerX, int nPointerY, int nWide, int nTall, bool bPointer )
  1545. {
  1546. if ( locator_background_style.GetInt() == 0 || pTarget->m_alpha == 0 || pTarget->m_bIconNoTarget )
  1547. return;
  1548. if ( locator_background_style.GetInt() == 2 && !pTarget->m_bOnscreen )
  1549. return;
  1550. int nPosX = pTarget->GetIconX() + locator_background_shift_x.GetInt() - locator_background_thickness_x.GetInt() / 2;
  1551. int nPosY = pTarget->GetIconY() + locator_background_shift_y.GetInt() - locator_background_thickness_y.GetInt() / 2;
  1552. int nBackgroundWide = nWide + locator_background_thickness_x.GetInt();
  1553. int nBackgroundTall = nTall + locator_background_thickness_y.GetInt();
  1554. nPointerX = clamp( nPointerX, -0.5f * ScreenWidth(), ScreenWidth() * 1.5f );
  1555. nPointerY = clamp( nPointerY, -0.5f * ScreenHeight(), ScreenHeight() * 1.5f );
  1556. float fAlpha = static_cast<float>( pTarget->m_alpha ) / 255.0f;
  1557. Color rgbaBackground = locator_background_color.GetColor();
  1558. rgbaBackground[ 3 ] *= fAlpha;
  1559. Color rgbaBorder = locator_background_border_color.GetColor();
  1560. rgbaBorder[ 3 ] *= fAlpha;
  1561. vgui::surface()->DrawWordBubble( nPosX, nPosY, nPosX + nBackgroundWide, nPosY + nBackgroundTall, locator_background_border_thickness.GetInt(),
  1562. rgbaBackground, rgbaBorder, bPointer, nPointerX, nPointerY, ScreenWidth() * ICON_SIZE );
  1563. }
  1564. //-----------------------------------------------------------------------------
  1565. // Purpose: Draw an icon with the group of static icons.
  1566. //-----------------------------------------------------------------------------
  1567. void CLocatorPanel::DrawStaticIcon( CLocatorTarget *pTarget )
  1568. {
  1569. float flApparentZ = 15.0f;
  1570. vgui::surface()->DrawSetApparentDepth( flApparentZ );
  1571. int centerX = ScreenWidth() / 2;
  1572. int centerY = ScreenHeight() / 2;
  1573. centerY += m_staticIconPosition;
  1574. int iconTall = ScreenWidth() * ICON_SIZE;
  1575. int iconWide = iconTall * pTarget->m_widthScale_onscreen;
  1576. pTarget->m_centerX = centerX;
  1577. pTarget->m_centerY = centerY;
  1578. // Animate the icon
  1579. AnimateIconSize( pTarget->GetIconEffectsFlags(), &iconWide, &iconTall, pTarget->m_pulseStart, pTarget->m_distFromPlayer );
  1580. AnimateIconPosition( pTarget->GetIconEffectsFlags(), &centerX, &centerY );
  1581. AnimateIconAlpha( pTarget->GetIconEffectsFlags(), &pTarget->m_alpha, pTarget->m_fadeStart );
  1582. // Figure out the caption width
  1583. pTarget->m_captionWide = GetScreenWidthForCaption( pTarget->GetCaptionText(), CAPTION_FONT_HANDLE );
  1584. bool bDrawMultilineCaption = false;
  1585. if ( m_iShouldWrapStaticLocators > 0 ) // conditionalized in locator.res
  1586. {
  1587. if ( pTarget->m_captionWide > ( ScreenWidth() * locator_split_maxwide_percent.GetFloat() ) )
  1588. {
  1589. // we will double-line this
  1590. pTarget->m_captionWide = pTarget->m_captionWide * locator_split_len.GetFloat();
  1591. bDrawMultilineCaption = true;
  1592. }
  1593. }
  1594. int totalWide = iconWide + ICON_GAP + pTarget->m_captionWide;
  1595. pTarget->m_iconX = centerX - totalWide * 0.5f;
  1596. pTarget->m_iconY = centerY - ( iconTall >> 1 );
  1597. // Lerp by speed on the Y axis
  1598. float iconY = pTarget->m_iconY;
  1599. float diffY = fabsf( pTarget->m_iconY - pTarget->m_lastYPos );
  1600. float flLerpSpeed = gpGlobals->frametime * locator_lerp_speed.GetFloat();
  1601. pTarget->m_iconY = static_cast<int>( Approach( iconY, pTarget->m_lastYPos, MAX( 3.0f, flLerpSpeed * diffY ) ) );
  1602. pTarget->m_centerY += ( pTarget->m_iconY - iconY );
  1603. pTarget->m_lastXPos = pTarget->m_iconX;
  1604. pTarget->m_lastYPos = pTarget->m_iconY;
  1605. pTarget->m_bIsDrawing = true;
  1606. vgui::Panel *pVguiTarget = pTarget->GetVguiTarget();
  1607. if ( pVguiTarget )
  1608. {
  1609. int nPanelX, nPanelY;
  1610. nPanelX = 0;
  1611. nPanelY = 0;
  1612. vgui::Label::Alignment nVguiTargetEdge = (vgui::Label::Alignment)pTarget->GetVguiTargetEdge();
  1613. int nWide = pVguiTarget->GetWide();
  1614. int nTall = pVguiTarget->GetTall();
  1615. const char *pchLookup = pTarget->GetVguiTargetLookup();
  1616. if ( pchLookup[ 0 ] != '\0' )
  1617. {
  1618. bool bLookupSuccess = false;
  1619. bLookupSuccess = pVguiTarget->LookupElementBounds( pchLookup, nPanelX, nPanelY, nWide, nTall );
  1620. Assert( bLookupSuccess );
  1621. }
  1622. if ( nVguiTargetEdge == vgui::Label::a_north ||
  1623. nVguiTargetEdge == vgui::Label::a_center ||
  1624. nVguiTargetEdge == vgui::Label::a_south )
  1625. {
  1626. nPanelX += nWide / 2;
  1627. }
  1628. else if ( nVguiTargetEdge == vgui::Label::a_northeast ||
  1629. nVguiTargetEdge == vgui::Label::a_east ||
  1630. nVguiTargetEdge == vgui::Label::a_southeast )
  1631. {
  1632. nPanelX += nWide;
  1633. }
  1634. if ( nVguiTargetEdge == vgui::Label::a_west ||
  1635. nVguiTargetEdge == vgui::Label::a_center ||
  1636. nVguiTargetEdge == vgui::Label::a_east )
  1637. {
  1638. nPanelY += nTall / 2;
  1639. }
  1640. else if ( nVguiTargetEdge == vgui::Label::a_southwest ||
  1641. nVguiTargetEdge == vgui::Label::a_south ||
  1642. nVguiTargetEdge == vgui::Label::a_southeast )
  1643. {
  1644. nPanelY += nTall;
  1645. }
  1646. pVguiTarget->LocalToScreen( nPanelX, nPanelY );
  1647. DrawPointerBackground( pTarget, nPanelX, nPanelY, totalWide, iconTall, true );
  1648. }
  1649. else
  1650. {
  1651. DrawPointerBackground( pTarget, pTarget->m_centerX, pTarget->m_centerY, totalWide, iconTall, false );
  1652. }
  1653. if ( pTarget->m_pIcon_onscreen )
  1654. {
  1655. if ( !pTarget->m_bDrawControllerButton )
  1656. {
  1657. // Don't draw the icon if we're on 360 and have a binding to draw
  1658. Color colorIcon = pTarget->GetIconColor();
  1659. colorIcon[ 3 ] = pTarget->m_alpha * static_cast< float >( colorIcon[ 3 ] ) / 255.0f;
  1660. pTarget->m_pIcon_onscreen->DrawSelf( pTarget->GetIconX(), pTarget->GetIconY(), iconWide, iconTall, colorIcon, flApparentZ );
  1661. }
  1662. }
  1663. DrawTargetCaption( pTarget, pTarget->GetIconX() + iconWide + ICON_GAP, pTarget->GetIconCenterY(), bDrawMultilineCaption );
  1664. if ( pTarget->DrawBindingName() )
  1665. {
  1666. DrawBindingName( pTarget, pTarget->DrawBindingName(), pTarget->GetIconX() + (iconWide>>1), pTarget->GetIconY() + (iconTall>>1), pTarget->m_bDrawControllerButton );
  1667. }
  1668. // Draw the arrow.
  1669. if ( pTarget->m_bDrawArrow )
  1670. {
  1671. int iArrowSize = ScreenWidth() * ICON_SIZE; // Always square width
  1672. DrawIndicatorArrow( pTarget->GetIconX(), pTarget->GetIconY(), iArrowSize, iArrowSize, pTarget->m_captionWide + ICON_GAP, pTarget->m_fDrawArrowAngle );
  1673. }
  1674. pTarget->m_bOnscreen = true;
  1675. // Move the static icon position so the next static icon drawn this frame below this one.
  1676. m_staticIconPosition += iconTall + (iconTall>>2);
  1677. // Move down a little more if this one was multi-line
  1678. if ( bDrawMultilineCaption )
  1679. {
  1680. m_staticIconPosition += (iconTall>>2);
  1681. }
  1682. vgui::surface()->DrawClearApparentDepth();
  1683. }
  1684. //-----------------------------------------------------------------------------
  1685. // Purpose: Position and animate this target's icon on the screen. Based on
  1686. // options, draw the indicator arrows (arrows that point to the
  1687. // direction the player should turn to see the icon), text caption,
  1688. // and the 'simple' arrow which just points down to indicate the
  1689. // item the icon represents.
  1690. //-----------------------------------------------------------------------------
  1691. void CLocatorPanel::DrawDynamicIcon( CLocatorTarget *pTarget, bool bDrawCaption, bool bDrawSimpleArrow )
  1692. {
  1693. int alpha = pTarget->m_alpha;
  1694. if( pTarget->m_bOccluded && !( (pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_FORCE_CAPTION) || locator_topdown_style.GetBool() ) )
  1695. {
  1696. return;
  1697. }
  1698. // Use the distance to target for stereo depth
  1699. Vector vOffsetNormal = pTarget->m_vecOrigin - MainViewOrigin( GET_ACTIVE_SPLITSCREEN_SLOT() );
  1700. float flApparentZ = vOffsetNormal.Length() * 0.75f; // Set the depth to 75% of the distance to center of object
  1701. vgui::surface()->DrawSetApparentDepth( flApparentZ );
  1702. // Draw the icon!
  1703. vgui::surface()->DrawSetColor( 255, 255, 255, alpha );
  1704. int iWide = pTarget->m_wide;
  1705. if ( !pTarget->m_bOnscreen )
  1706. {
  1707. // Width is always square for offscreen icons
  1708. iWide /= pTarget->m_widthScale_onscreen;
  1709. }
  1710. // Figure out the caption width
  1711. pTarget->m_captionWide = GetScreenWidthForCaption( pTarget->GetCaptionText(), CAPTION_FONT_HANDLE );
  1712. bool bDrawMultilineCaption = false;
  1713. if ( m_iShouldWrapStaticLocators > 0 ) // conditionalized in locator.res
  1714. {
  1715. if ( pTarget->m_captionWide > ( ScreenWidth() * locator_split_maxwide_percent.GetFloat() ) )
  1716. {
  1717. // we will double-line this
  1718. pTarget->m_captionWide = pTarget->m_captionWide * locator_split_len.GetFloat();
  1719. bDrawMultilineCaption = true;
  1720. }
  1721. }
  1722. int totalWide = iWide;
  1723. bool bShouldDrawCaption = ( (pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_FORCE_CAPTION) || (!pTarget->m_bOccluded && pTarget->m_distFromPlayer <= ICON_DIST_TOO_FAR) || locator_topdown_style.GetBool() );
  1724. if( pTarget->m_bOnscreen && bDrawCaption && bShouldDrawCaption )
  1725. {
  1726. totalWide += ( ICON_GAP + pTarget->m_captionWide );
  1727. }
  1728. pTarget->m_bIsDrawing = true;
  1729. int nTargetX, nTargetY;
  1730. vgui::Panel *pVguiTarget = pTarget->GetVguiTarget();
  1731. if ( pVguiTarget )
  1732. {
  1733. nTargetX = 0;
  1734. nTargetY = 0;
  1735. vgui::Label::Alignment nVguiTargetEdge = (vgui::Label::Alignment)pTarget->GetVguiTargetEdge();
  1736. int nWide = pVguiTarget->GetWide();
  1737. int nTall = pVguiTarget->GetTall();
  1738. const char *pchLookup = pTarget->GetVguiTargetLookup();
  1739. if ( pchLookup[ 0 ] != '\0' )
  1740. {
  1741. bool bLookupSuccess = false;
  1742. bLookupSuccess = pVguiTarget->LookupElementBounds( pchLookup, nTargetX, nTargetY, nWide, nTall );
  1743. Assert( bLookupSuccess );
  1744. }
  1745. if ( nVguiTargetEdge == vgui::Label::a_north ||
  1746. nVguiTargetEdge == vgui::Label::a_center ||
  1747. nVguiTargetEdge == vgui::Label::a_south )
  1748. {
  1749. nTargetX += nWide / 2;
  1750. }
  1751. else if ( nVguiTargetEdge== vgui::Label::a_northeast ||
  1752. nVguiTargetEdge == vgui::Label::a_east ||
  1753. nVguiTargetEdge == vgui::Label::a_southeast )
  1754. {
  1755. nTargetX += nWide;
  1756. }
  1757. if ( nVguiTargetEdge == vgui::Label::a_west ||
  1758. nVguiTargetEdge == vgui::Label::a_center ||
  1759. nVguiTargetEdge == vgui::Label::a_east )
  1760. {
  1761. nTargetY += nTall / 2;
  1762. }
  1763. else if ( nVguiTargetEdge == vgui::Label::a_southwest ||
  1764. nVguiTargetEdge == vgui::Label::a_south ||
  1765. nVguiTargetEdge == vgui::Label::a_southeast )
  1766. {
  1767. nTargetY += nTall;
  1768. }
  1769. pVguiTarget->LocalToScreen( nTargetX, nTargetY );
  1770. }
  1771. else if ( !pTarget->m_bOnscreen )
  1772. {
  1773. nTargetX = pTarget->m_targetX;
  1774. nTargetY = pTarget->m_targetY;
  1775. }
  1776. else
  1777. {
  1778. nTargetX = pTarget->m_centerX;
  1779. nTargetY = pTarget->m_centerY;
  1780. }
  1781. if ( pTarget->m_bOnscreen )
  1782. {
  1783. DrawPointerBackground( pTarget, nTargetX, nTargetY, totalWide, pTarget->m_tall, true );
  1784. }
  1785. else
  1786. {
  1787. // Offscreen we need to point the pointer toward out offscreen target
  1788. DrawPointerBackground( pTarget, nTargetX, nTargetY, totalWide, pTarget->m_tall, true );
  1789. }
  1790. if( pTarget->m_bOnscreen && pTarget->m_pIcon_onscreen )
  1791. {
  1792. if ( !pTarget->m_bDrawControllerButton )
  1793. {
  1794. Color colorIcon = pTarget->GetIconColor();
  1795. colorIcon[ 3 ] = pTarget->m_alpha * static_cast< float >( colorIcon[ 3 ] ) / 255.0f;
  1796. pTarget->m_pIcon_onscreen->DrawSelf( pTarget->GetIconX(), pTarget->GetIconY(), iWide, pTarget->m_tall, colorIcon, flApparentZ );
  1797. //Msg( "WIDE = %i\n", iWide );
  1798. }
  1799. }
  1800. else if ( pTarget->m_pIcon_offscreen )
  1801. {
  1802. if ( !pTarget->m_bDrawControllerButtonOffscreen )
  1803. {
  1804. Color colorIcon = pTarget->GetIconColor();
  1805. colorIcon[ 3 ] = pTarget->m_alpha * static_cast< float >( colorIcon[ 3 ] ) / 255.0f;
  1806. pTarget->m_pIcon_offscreen->DrawSelf( pTarget->GetIconX(), pTarget->GetIconY(), iWide, pTarget->m_tall, colorIcon, flApparentZ );
  1807. }
  1808. }
  1809. if( !pTarget->m_bOnscreen )
  1810. {
  1811. if ( pTarget->DrawBindingNameOffscreen() )
  1812. {
  1813. DrawBindingName( pTarget, pTarget->DrawBindingName(), pTarget->GetIconX() + (iWide>>1), pTarget->GetIconY() + (pTarget->m_tall>>1), pTarget->m_bDrawControllerButtonOffscreen );
  1814. }
  1815. if ( pTarget->m_bDrawArrow && !pTarget->m_bIconNoTarget )
  1816. {
  1817. if ( locator_background_style.GetInt() == 0 || locator_background_style.GetInt() == 2 )
  1818. {
  1819. // Draw the arrow.
  1820. DrawIndicatorArrow( pTarget->GetIconX(), pTarget->GetIconY(), iWide, pTarget->m_tall, 0, pTarget->m_fDrawArrowAngle );
  1821. }
  1822. }
  1823. }
  1824. else if( bShouldDrawCaption )
  1825. {
  1826. if( bDrawCaption )
  1827. {
  1828. DrawTargetCaption( pTarget, pTarget->GetIconCenterX() + ICON_GAP + ScreenWidth() * ICON_SIZE * pTarget->m_widthScale_onscreen * 0.5, pTarget->GetIconCenterY(), bDrawMultilineCaption );
  1829. }
  1830. if ( pTarget->DrawBindingName() )
  1831. {
  1832. DrawBindingName( pTarget, pTarget->DrawBindingName(), pTarget->GetIconX() + (iWide>>1), pTarget->GetIconY() + (pTarget->m_tall>>1), pTarget->m_bDrawControllerButton );
  1833. }
  1834. }
  1835. vgui::surface()->DrawClearApparentDepth();
  1836. }
  1837. //-----------------------------------------------------------------------------
  1838. // Purpose: Some targets have text captions. Draw the text.
  1839. //-----------------------------------------------------------------------------
  1840. void CLocatorPanel::DrawTargetCaption( CLocatorTarget *pTarget, int x, int y, bool bDrawMultiline )
  1841. {
  1842. // Draw the caption
  1843. vgui::surface()->DrawSetTextFont( CAPTION_FONT_HANDLE );
  1844. int fontTall = vgui::surface()->GetFontTall( CAPTION_FONT_HANDLE );
  1845. int iCaptionWidth = GetScreenWidthForCaption( pTarget->GetCaptionText(), CAPTION_FONT_HANDLE );
  1846. if ( bDrawMultiline )
  1847. {
  1848. iCaptionWidth *= locator_split_len.GetFloat();
  1849. }
  1850. // Only draw drop shadow on PC because it looks crappy on a TV
  1851. if ( !IsGameConsole() )
  1852. {
  1853. // Draw black text (drop shadow)
  1854. vgui::surface()->DrawSetTextColor( 0,0,0, pTarget->m_alpha );
  1855. vgui::surface()->DrawSetTextPos( x, y - (fontTall >>1) );
  1856. vgui::surface()->DrawUnicodeString( pTarget->GetCaptionText() );
  1857. }
  1858. // Draw text
  1859. vgui::surface()->DrawSetTextColor( pTarget->m_captionColor.r(),pTarget->m_captionColor.g(),pTarget->m_captionColor.b(), pTarget->m_alpha );
  1860. if ( !bDrawMultiline )
  1861. {
  1862. vgui::surface()->DrawSetTextPos( x - 1, y - (fontTall >>1) - 1 );
  1863. vgui::surface()->DrawUnicodeString( pTarget->GetCaptionText() );
  1864. }
  1865. else
  1866. {
  1867. int charX = x-1;
  1868. int charY = y - ( fontTall >> 1 ) - 1;
  1869. int iWidth = 0;
  1870. const wchar_t *pString = pTarget->GetCaptionText();
  1871. int len = Q_wcslen( pString );
  1872. for ( int iChar = 0; iChar < len; ++ iChar )
  1873. {
  1874. int charW = vgui::surface()->GetCharacterWidth( CAPTION_FONT_HANDLE, pString[ iChar ] );
  1875. iWidth += charW;
  1876. if ( iWidth > pTarget->m_captionWide && pString[iChar] == L' ' )
  1877. {
  1878. charY += fontTall;
  1879. charX = x-1;
  1880. iWidth = 0;
  1881. }
  1882. vgui::surface()->DrawSetTextPos( charX, charY );
  1883. vgui::surface()->DrawUnicodeChar( pString[iChar] );
  1884. charX += charW;
  1885. }
  1886. }
  1887. }
  1888. //-----------------------------------------------------------------------------
  1889. // Purpose: Figure out how wide (pixels) a string will be if rendered with this font
  1890. //
  1891. //-----------------------------------------------------------------------------
  1892. int CLocatorPanel::GetScreenWidthForCaption( const wchar_t *pString, vgui::HFont hFont )
  1893. {
  1894. int iWidth = 0;
  1895. for ( int iChar = 0; iChar < Q_wcslen( pString ); ++ iChar )
  1896. {
  1897. iWidth += vgui::surface()->GetCharacterWidth( hFont, pString[ iChar ] );
  1898. }
  1899. return iWidth;
  1900. }
  1901. //-----------------------------------------------------------------------------
  1902. // Purpose: Some targets' captions contain information about key bindings that
  1903. // should be displayed to the player. Do so.
  1904. //-----------------------------------------------------------------------------
  1905. void CLocatorPanel::DrawBindingName( CLocatorTarget *pTarget, const char *pchBindingName, int x, int y, bool bController )
  1906. {
  1907. if ( !bController && !IsGameConsole() )
  1908. {
  1909. char szBinding[ 256 ];
  1910. V_strcpy_safe( szBinding, pchBindingName ? pchBindingName : "" );
  1911. if ( Q_strcmp( szBinding, "SEMICOLON" ) == 0 )
  1912. {
  1913. V_strcpy_safe( szBinding, ";" );
  1914. }
  1915. else if ( Q_strcmp( szBinding, "ESCAPE" ) == 0 )
  1916. {
  1917. V_strcpy_safe( szBinding, "ESC" );
  1918. }
  1919. else if ( Q_strlen( szBinding ) == 1 && szBinding[ 0 ] >= 'a' && szBinding[ 0 ] <= 'z' )
  1920. {
  1921. // Make single letters uppercase
  1922. szBinding[ 0 ] += ( 'A' - 'a' );
  1923. }
  1924. // Draw the caption
  1925. vgui::HFont &KeysFont = ( Q_strlen( szBinding ) <= 5 ? m_hKeysFont : m_hKeysFontSmall );
  1926. vgui::surface()->DrawSetTextFont( KeysFont );
  1927. int fontTall = vgui::surface()->GetFontTall( KeysFont );
  1928. wchar_t wszCaption[ 64 ];
  1929. g_pVGuiLocalize->ConstructString( wszCaption, sizeof(wchar_t)*64, szBinding, NULL );
  1930. int iWidth = GetScreenWidthForCaption( wszCaption, KeysFont );
  1931. // Draw black text
  1932. vgui::surface()->DrawSetTextColor( 0,0,0, pTarget->m_alpha );
  1933. vgui::surface()->DrawSetTextPos( x - (iWidth>>1) - 1, y - (fontTall >>1) - 1 );
  1934. vgui::surface()->DrawUnicodeString( wszCaption );
  1935. }
  1936. else
  1937. {
  1938. // Draw the caption
  1939. wchar_t wszCaption[ 64 ];
  1940. vgui::surface()->DrawSetTextFont( BUTTON_FONT_HANDLE );
  1941. int fontTall = vgui::surface()->GetFontTall( BUTTON_FONT_HANDLE );
  1942. char szBinding[ 256 ];
  1943. // Turn localized string into icon character
  1944. Q_snprintf( szBinding, sizeof( szBinding ), "#GameUI_Icons_%s", pchBindingName );
  1945. g_pVGuiLocalize->ConstructString( wszCaption, sizeof( wszCaption ), g_pVGuiLocalize->Find( szBinding ), 0 );
  1946. g_pVGuiLocalize->ConvertUnicodeToANSI( wszCaption, szBinding, sizeof( szBinding ) );
  1947. int iWidth = GetScreenWidthForCaption( wszCaption, BUTTON_FONT_HANDLE );
  1948. // Draw the button
  1949. vgui::surface()->DrawSetTextColor( 255,255,255, pTarget->m_alpha );
  1950. vgui::surface()->DrawSetTextPos( x - (iWidth>>1), y - (fontTall >>1) );
  1951. vgui::surface()->DrawUnicodeString( wszCaption );
  1952. }
  1953. }
  1954. //-----------------------------------------------------------------------------
  1955. // Purpose: Draw an arrow to indicate that a target is offscreen
  1956. //
  1957. // iconWide is sent to this function so that the arrow knows how to straddle
  1958. // the icon that it is being drawn near.
  1959. //-----------------------------------------------------------------------------
  1960. void CLocatorPanel::DrawIndicatorArrow( int x, int y, int iconWide, int iconTall, int textWidth, float fAngle )
  1961. {
  1962. float fWide = iconWide;
  1963. float fTall = iconTall;
  1964. // Rotated version of the bitmap!
  1965. // Rotate about the center of the bitmap
  1966. vgui::Vertex_t verts[4];
  1967. Vector2D center( x + (fWide * 0.5f), y + (fTall * 0.5f) );
  1968. // Choose a basis...
  1969. float yawRadians = -fAngle * M_PI / 180.0f;
  1970. Vector2D axis[2];
  1971. axis[0].x = cos(yawRadians);
  1972. axis[0].y = sin(yawRadians);
  1973. axis[1].x = -axis[0].y;
  1974. axis[1].y = axis[0].x;
  1975. center += Vector2D( axis[ 1 ].x * fTall, axis[ 1 ].y * fTall );
  1976. verts[0].m_TexCoord.Init( 0, 0 );
  1977. Vector2DMA( center, -0.5f * fWide, axis[0], verts[0].m_Position );
  1978. Vector2DMA( verts[0].m_Position, -0.5f * fTall, axis[1], verts[0].m_Position );
  1979. verts[1].m_TexCoord.Init( 1, 0 );
  1980. Vector2DMA( verts[0].m_Position, fWide, axis[0], verts[1].m_Position );
  1981. verts[2].m_TexCoord.Init( 1, 1 );
  1982. Vector2DMA( verts[1].m_Position, fTall, axis[1], verts[2].m_Position );
  1983. verts[3].m_TexCoord.Init( 0, 1 );
  1984. Vector2DMA( verts[0].m_Position, fTall, axis[1], verts[3].m_Position );
  1985. vgui::surface()->DrawSetTexture( m_textureID_ArrowDown );
  1986. vgui::surface()->DrawTexturedPolygon( 4, verts );
  1987. }
  1988. //-----------------------------------------------------------------------------
  1989. // Purpose: Draws a very simple arrow that points down.
  1990. //-----------------------------------------------------------------------------
  1991. void CLocatorPanel::DrawSimpleArrow( int x, int y, int iconWide, int iconTall )
  1992. {
  1993. vgui::surface()->DrawSetTexture( m_textureID_SimpleArrow );
  1994. y += iconTall;
  1995. vgui::surface()->DrawTexturedRect( x, y, x + iconWide, y + iconTall );
  1996. }
  1997. //-----------------------------------------------------------------------------
  1998. //-----------------------------------------------------------------------------
  1999. void CLocatorPanel::GetIconPositionForOffscreenTarget( const Vector &vecDelta, float flDist, int *pXPos, int *pYPos )
  2000. {
  2001. float xpos = 0, ypos = 0;
  2002. float flRotation = 0;
  2003. float flRadius = YRES(OFFSCREEN_ICON_POSITION_RADIUS);
  2004. if ( locator_topdown_style.GetBool() )
  2005. {
  2006. flRadius *= clamp( flDist / 600.0f, 1.75f, 3.0f );
  2007. }
  2008. GetTargetPosition( vecDelta, flRadius, &xpos, &ypos, &flRotation );
  2009. *pXPos = xpos;
  2010. *pYPos = ypos;
  2011. }
  2012. //-----------------------------------------------------------------------------
  2013. // Purpose: Given a handle, return the pointer to the proper locator target.
  2014. //-----------------------------------------------------------------------------
  2015. CLocatorTarget *CLocatorPanel::GetPointerForHandle( int hTarget )
  2016. {
  2017. for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ )
  2018. {
  2019. if( m_targets[ i ].m_isActive && m_targets[ i ].m_serialNumber == hTarget )
  2020. {
  2021. return &m_targets[ i ];
  2022. }
  2023. }
  2024. return NULL;
  2025. }
  2026. //-----------------------------------------------------------------------------
  2027. //-----------------------------------------------------------------------------
  2028. int CLocatorPanel::AddTarget()
  2029. {
  2030. for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ )
  2031. {
  2032. if( !m_targets[ i ].m_isActive )
  2033. {
  2034. m_targets[ i ].Activate( m_serializer );
  2035. m_serializer++;
  2036. return m_targets[ i ].m_serialNumber;
  2037. }
  2038. }
  2039. DevWarning( "Locator Panel has no free targets!\n" );
  2040. return -1;
  2041. }
  2042. //-----------------------------------------------------------------------------
  2043. //-----------------------------------------------------------------------------
  2044. void CLocatorPanel::RemoveTarget( int hTarget )
  2045. {
  2046. CLocatorTarget *pTarget = GetPointerForHandle( hTarget );
  2047. if( pTarget )
  2048. {
  2049. pTarget->Deactivate();
  2050. }
  2051. }