Moderator elections are being held. See Valve Developer Community:Moderator elections for more details.
Users who would like to run for moderator must be autoconfirmed and have at least 100 edits. Users can check their own edit count at Special:Preferences.
Users who would like to run for moderator must be autoconfirmed and have at least 100 edits. Users can check their own edit count at Special:Preferences.
The Message template has been deleted. A list of pages that transclude it are at Valve Developer Community:Message transclusions.
Template:Hud locator target
hud_locator_target.h
//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. =======
//
// Purpose: Add entities to this system, and the Locator will maintain an arrow
// on the HUD that points to the entities when they are offscreen.
//
//=============================================================================
#ifndef L4D_HUD_LOCATOR_H
#define L4D_HUD_LOCATOR_H
#ifdef _WIN32
#pragma once
#endif
#include "vgui_controls/PHandle.h"
#define MAX_LOCATOR_BINDINGS_SHOWN 8
#define MAX_LOCATOR_TARGETS 10
#define LOCATOR_FLAGS_NONE 0x00000000
#define LOCATOR_ICON_FX_NONE 0x00000000
#define LOCATOR_ICON_FX_PULSE_SLOW 0x00000001
#define LOCATOR_ICON_FX_PULSE_FAST 0x00000002
#define LOCATOR_ICON_FX_PULSE_URGENT 0x00000004
#define LOCATOR_ICON_FX_ALPHA_SLOW 0x00000008
#define LOCATOR_ICON_FX_ALPHA_FAST 0x00000010
#define LOCATOR_ICON_FX_ALPHA_URGENT 0x00000020
#define LOCATOR_ICON_FX_SHAKE_NARROW 0x00000040
#define LOCATOR_ICON_FX_SHAKE_WIDE 0x00000080
#define LOCATOR_ICON_FX_STATIC 0x00000100 // This icon draws at a fixed location on the HUD.
#define LOCATOR_ICON_FX_NO_OFFSCREEN 0x00000200
#define LOCATOR_ICON_FX_FORCE_CAPTION 0x00000400 // Always draw the caption, even when the icon is occluded.
#define LOCATOR_ICON_FX_FADE_OUT 0x00000800 // Set when deactivated so it can smoothly vanish
#define LOCATOR_ICON_FX_FADE_IN 0x00001000 // Set when activated so it can smoothly appear
#include "tier1/UtlSymbol.h"
// See comments in UtlSymbol on why this is useful
DECLARE_PRIVATE_SYMBOLTYPE( CGameInstructorSymbol );
//-----------------------------------------------------------------------------
// This class represents a single target to be tracked by the locator
//-----------------------------------------------------------------------------
class CLocatorTarget
{
public:
bool m_bOriginInScreenspace;
Vector m_vecOrigin; // The location in the world to draw on the locator
// ONLY the locator panel should fiddle with these fields.
bool m_isActive;
int m_serialNumber;
int m_frameLastUpdated;
bool m_bOnscreen;
bool m_bOccluded;
bool m_bVisible;
bool m_bIsDrawing;
float m_distFromPlayer;
CHudTexture *m_pIcon_onscreen;
CHudTexture *m_pIcon_offscreen;
int m_iBindingTick;
float m_flNextBindingTick;
float m_flNextOcclusionTest;
int m_iBindingChoicesCount;
const char *(m_pchBindingChoices[ MAX_LOCATOR_BINDINGS_SHOWN ]);
int m_iBindChoicesOriginalToken[ MAX_LOCATOR_BINDINGS_SHOWN ];
// Fields for drawing
int m_targetX; // screen X position of the actual target
int m_targetY; // screen Y position of the actual target
int m_iconX; // screen X position (top)
int m_iconY; // screen Y position (left)
int m_centerX; // screen X position (center)
int m_centerY; // screen Y position (center)
int m_wide; // draw width of icon (may be different from frame to frame as the icon's size animates, for instance)
int m_tall; // draw height of icon '' ''
float m_widthScale_onscreen; // for icons that are wider than standard
int m_alpha; //
float m_fadeStart; // time stamp when fade out started
float m_lerpStart; // time stamp when lerping started
float m_pulseStart; // time stamp when pulsing started
int m_declutterIndex; // sort order from the declutterer
int m_lastDeclutterIndex; // last sort order from the declutterer
int m_drawArrowDirection; // Whether to draw an arrow indicating this target is off-screen, also tells us which arrow to draw (left, up, etc.)
int m_captionWide; // How wide (pixels) my caption is.
bool m_bDrawControllerButton;
bool m_bDrawControllerButtonOffscreen;
int m_offsetX; // User-specified X offset which is applied in screenspace
int m_offsetY; // User-specified Y offset which is applied in screenspace
// Fields for interpolating icon position
float m_flTimeLerpDone; // How much time left before this icon arrives where it is supposed to be.
int m_lastXPos; // screen X position last frame
int m_lastYPos; // '' Y
CLocatorTarget( void );
void Activate( int serialNumber );
void Deactivate( bool bNoFade = false );
void Update();
int GetIconX( void );
int GetIconY( void );
int GetIconCenterX( void );
int GetIconCenterY( void );
int GetIconWidth( void );
int GetIconHeight( void );
void AddIconEffects( int add ) { m_iEffectsFlags |= add; }
void RemoveIconEffects( int remove ) { m_iEffectsFlags &= ~remove; }
int GetIconEffectsFlags() { return m_iEffectsFlags; }
void SetCaptionColor( Color col ) { m_captionColor = col; }
void SetCaptionColor( const char *pszCaptionColor );
bool IsStatic();
bool IsPresenting();
void StartTimedLerp();
void StartPresent();
void EndPresent();
void UpdateVguiTarget( void );
vgui::Panel *GetVguiTarget( void );
void SetVguiTargetName( const char *pchVguiTargetName );
const char *GetVguiTargetName( void ) { return m_szVguiTargetName.String(); }
void SetVguiTargetLookup( const char *pchVguiTargetLookup );
const char *GetVguiTargetLookup( void ) { return m_szVguiTargetLookup.String(); }
void SetVguiTargetEdge( int nVguiEdge );
int GetVguiTargetEdge( void ) const { return m_nVguiTargetEdge; }
void SetOnscreenIconTextureName( const char *pszTexture );
void SetOffscreenIconTextureName( const char *pszTexture );
void SetBinding( const char *pszBinding );
const char *UseBindingImage( char *pchIconTextureName, size_t bufSize );
const char *GetOnscreenIconTextureName() { return m_szOnscreenTexture.String(); }
const char *GetOffscreenIconTextureName() { return m_szOffscreenTexture.String(); }
const char *GetBinding() { return m_szBinding.String(); }
void SetVisible( bool bVisible );
bool IsVisible( void );
void SetCaptionText( const char *pszText, const char *pszParam );
const wchar_t *GetCaptionText( void ) { return (const wchar_t *)m_wszCaption.Base(); }
bool HasCaptionText( void ) { return m_wszCaption.Count() > 1; }
void DrawBindingName( const char *pchDrawName ) { m_pchDrawBindingName = pchDrawName; }
void DrawBindingNameOffscreen( const char *pchDrawName ) { m_pchDrawBindingNameOffscreen = pchDrawName; }
const char *DrawBindingName( void ) { return m_pchDrawBindingName; }
const char *DrawBindingNameOffscreen( void ) { return m_pchDrawBindingNameOffscreen; }
bool IsOnScreen() { return m_bOnscreen; }
bool IsOccluded() { return m_bOccluded; }
private:
CGameInstructorSymbol m_szVguiTargetName;
CGameInstructorSymbol m_szVguiTargetLookup;
vgui::DHANDLE<vgui::Panel> m_hVguiTarget;
int m_nVguiTargetEdge;
CGameInstructorSymbol m_szOnscreenTexture;
CGameInstructorSymbol m_szOffscreenTexture;
CGameInstructorSymbol m_szBinding;
bool m_bWasControllerLast;
const char *m_pchDrawBindingName;
const char *m_pchDrawBindingNameOffscreen;
int m_iEffectsFlags;
CUtlVector< wchar_t > m_wszCaption;
public:
Color m_captionColor;
};
extern int Locator_AddTarget();
extern void Locator_RemoveTarget( int hTarget );
CLocatorTarget *Locator_GetTargetFromHandle( int hTarget );
void Locator_ComputeTargetIconPositionFromHandle( int hTarget );
#endif // L4D_HUD_LOCATOR_H
hud_locator_target.cpp
//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============//
//
// Purpose: See header file
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "hud_locator_target.h"
#include "iclientmode.h"
#include <vgui/ILocalize.h>
#include <vgui/ISurface.h>
#include <vgui/IVGUI.h>
#include <vgui_controls/EditablePanel.h>
#include <vgui_controls/Controls.h>
#include <vgui_controls/Label.h>
#include <vgui/IInput.h>
#include <vgui/IScheme.h>
#include "iinput.h"
#include "view.h"
#include "hud.h"
#include "hudelement.h"
#include "vgui_int.h"
#include "hud_macros.h"
#include "iclientmode.h"
#ifdef INSOURCE
#include "in_player_shared.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define ICON_SIZE 0.04f // Icons are ScreenWidth() * ICON_SIZE wide.
#define ICON_GAP 5 // Number of pixels between the icon and the text
#define OFFSCREEN_ICON_POSITION_RADIUS 100
#define BUTTON_FONT_HANDLE m_hCaptionFont
#define ICON_DIST_TOO_FAR (60.0f * 12.0f)
#define MIN_ICON_ALPHA 0.5
#define MAX_ICON_ALPHA 1
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" );
ConVar locator_icon_max_size_non_ss( "locator_icon_max_size_non_ss", "1.5", FCVAR_NONE, "Maximum scale of the icon on the screen" );
#define MIN_ICON_SCALE locator_icon_min_size_non_ss.GetFloat()
#define MAX_ICON_SCALE locator_icon_max_size_non_ss.GetFloat()
#define LOCATOR_OCCLUSION_TEST_RATE 0.25f
enum
{
DRAW_ARROW_NO = 0,
DRAW_ARROW_UP,
DRAW_ARROW_DOWN,
DRAW_ARROW_LEFT,
DRAW_ARROW_RIGHT
};
ConVar locator_fade_time( "locator_fade_time", "0.3", FCVAR_NONE, "Number of seconds it takes for a lesson to fully fade in/out." );
ConVar locator_lerp_speed( "locator_lerp_speed", "5.0f", FCVAR_NONE, "Speed that static lessons move along the Y axis." );
ConVar locator_lerp_rest( "locator_lerp_rest", "2.25f", FCVAR_NONE, "Number of seconds before moving from the center." );
ConVar locator_lerp_time( "locator_lerp_time", "1.75f", FCVAR_NONE, "Number of seconds to lerp before reaching final destination" );
ConVar locator_pulse_time( "locator_pulse_time", "1.0f", FCVAR_NONE, "Number of seconds to pulse after changing icon or position" );
ConVar locator_start_at_crosshair( "locator_start_at_crosshair", "0", FCVAR_NONE, "Start position at the crosshair instead of the top middle of the screen." );
ConVar locator_topdown_style( "locator_topdown_style", "0", FCVAR_NONE, "Topdown games set this to handle distance and offscreen location differently." );
ConVar locator_background_style( "locator_background_style", "0", FCVAR_NONE, "Setting this to 1 will show rectangle backgrounds behind the items word-bubble pointers." );
ConVar locator_background_color( "locator_background_color", "255 255 255 5", FCVAR_NONE, "The default color for the background." );
ConVar locator_background_border_color( "locator_background_border_color", "255 255 255 15", FCVAR_NONE, "The default color for the border." );
ConVar locator_background_thickness_x( "locator_background_thickness_x", "8", FCVAR_NONE, "How many pixels the background borders the left and right." );
ConVar locator_background_thickness_y( "locator_background_thickness_y", "0", FCVAR_NONE, "How many pixels the background borders the top and bottom." );
ConVar locator_background_shift_x( "locator_background_shift_x", "3", FCVAR_NONE, "How many pixels the background is shifted right." );
ConVar locator_background_shift_y( "locator_background_shift_y", "1", FCVAR_NONE, "How many pixels the background is shifted down." );
ConVar locator_background_border_thickness( "locator_background_border_thickness", "3", FCVAR_NONE, "How many pixels the background borders the left and right." );
ConVar locator_target_offset_x( "locator_target_offset_x", "0", FCVAR_NONE, "How many pixels to offset the locator from the target position." );
ConVar locator_target_offset_y( "locator_target_offset_y", "0", FCVAR_NONE, "How many pixels to offset the locator from the target position." );
ConVar locator_text_drop_shadow( "locator_text_drop_shadow", "1", FCVAR_NONE, "If enabled, a drop shadow is drawn behind caption text. PC only." );
ConVar locator_text_glow( "locator_text_glow", "0", FCVAR_NONE, "If enabled, a glow is drawn behind caption text" );
ConVar locator_text_glow_color( "locator_text_glow_color", "255 255 255 255", FCVAR_NONE, "Color of text glow" );
ConVar locator_split_maxwide_percent( "locator_split_maxwide_percent", "0.80f", FCVAR_CHEAT );
ConVar locator_split_len( "locator_split_len", "0.5f", FCVAR_CHEAT );
//------------------------------------
CLocatorTarget::CLocatorTarget( void )
{
Deactivate( true );
PrecacheMaterial("vgui/hud/icon_arrow_left");
PrecacheMaterial("vgui/hud/icon_arrow_right");
PrecacheMaterial("vgui/hud/icon_arrow_up");
PrecacheMaterial("vgui/hud/icon_arrow_down");
PrecacheMaterial("vgui/hud/icon_arrow_plain");
}
//------------------------------------
void CLocatorTarget::Activate( int serialNumber )
{
m_serialNumber = serialNumber;
m_frameLastUpdated = gpGlobals->framecount;
m_isActive = true;
m_bVisible = true;
m_bOnscreen = true;
m_alpha = 0;
m_fadeStart = gpGlobals->curtime;
m_offsetX = m_offsetY = 0;
int iStartX = ScreenWidth() / 2;
int iStartY = ScreenHeight() / 4;
// We want to start lessons at the players crosshair, cause that's where they're looking!
if ( locator_start_at_crosshair.GetBool() )
vgui::input()->GetCursorPos( iStartX, iStartY );
m_lastXPos = iStartX;
m_lastYPos = iStartY;
m_drawArrowDirection = DRAW_ARROW_NO;
m_lerpStart = gpGlobals->curtime;
m_pulseStart = gpGlobals->curtime;
m_declutterIndex = 0;
m_lastDeclutterIndex = 0;
AddIconEffects(LOCATOR_ICON_FX_FADE_IN);
}
//------------------------------------
void CLocatorTarget::Deactivate( bool bNoFade )
{
if ( bNoFade || m_alpha == 0 ||
( m_bOccluded && !( m_iEffectsFlags & LOCATOR_ICON_FX_FORCE_CAPTION ) ) ||
( !m_bOnscreen && ( m_iEffectsFlags & LOCATOR_ICON_FX_NO_OFFSCREEN ) ) )
{
m_bOriginInScreenspace = false;
m_serialNumber = -1;
m_isActive = false;
m_frameLastUpdated = 0;
m_pIcon_onscreen = NULL;
m_pIcon_offscreen = NULL;
m_bDrawControllerButton = false;
m_bDrawControllerButtonOffscreen = false;
m_iEffectsFlags = LOCATOR_ICON_FX_NONE;
m_captionWide = 0;
m_pchDrawBindingName = NULL;
m_pchDrawBindingNameOffscreen = NULL;
m_widthScale_onscreen = 1.0f;
m_bOccluded = false;
m_alpha = 0;
m_bIsDrawing = false;
m_bVisible = false;
m_szVguiTargetName = "";
m_szVguiTargetLookup = "";
m_hVguiTarget = NULL;
m_nVguiTargetEdge = vgui::Label::a_northwest;
m_szBinding = "";
m_iBindingTick = 0;
m_flNextBindingTick = 0.0f;
m_flNextOcclusionTest = 0.0f;
m_iBindingChoicesCount = 0;
m_wszCaption.RemoveAll();
m_wszCaption.AddToTail( (wchar_t)0 );
}
else if ( !( m_iEffectsFlags & LOCATOR_ICON_FX_FADE_OUT ) )
{
// Determine home much time it would have spent fading to reach the current alpha
float flAssumedFadeTime;
flAssumedFadeTime = ( 1.0f - static_cast<float>( m_alpha ) / 255.0f ) * locator_fade_time.GetFloat();
// Set the fade
m_fadeStart = gpGlobals->curtime - flAssumedFadeTime;
AddIconEffects( LOCATOR_ICON_FX_FADE_OUT );
RemoveIconEffects( LOCATOR_ICON_FX_FADE_IN );
}
}
//------------------------------------
void CLocatorTarget::Update()
{
m_frameLastUpdated = gpGlobals->framecount;
if ( m_bVisible && ( m_iEffectsFlags & LOCATOR_ICON_FX_FADE_OUT ) )
{
// Determine home much time it would have spent fading to reach the current alpha
float flAssumedFadeTime;
flAssumedFadeTime = ( 1.0f - static_cast<float>( m_alpha ) / 255.0f ) * locator_fade_time.GetFloat();
// Set the fade
m_fadeStart = gpGlobals->curtime - flAssumedFadeTime;
AddIconEffects( LOCATOR_ICON_FX_FADE_OUT );
RemoveIconEffects( LOCATOR_ICON_FX_FADE_OUT );
}
}
int CLocatorTarget::GetIconX( void )
{
return m_iconX + ( IsOnScreen() ? locator_target_offset_x.GetInt()+m_offsetX : 0 );
}
int CLocatorTarget::GetIconY( void )
{
return m_iconY + ( IsOnScreen() ? locator_target_offset_y.GetInt()+m_offsetY : 0 );
}
int CLocatorTarget::GetIconCenterX( void )
{
return m_centerX + locator_target_offset_x.GetInt() + m_offsetX;
}
int CLocatorTarget::GetIconCenterY( void )
{
return m_centerY + locator_target_offset_y.GetInt() + m_offsetY;
}
void CLocatorTarget::SetVisible( bool bVisible )
{
// They are already the same
if ( m_bVisible == bVisible )
return;
m_bVisible = bVisible;
if ( bVisible )
{
// Determine home much time it would have spent fading to reach the current alpha
float flAssumedFadeTime;
flAssumedFadeTime = ( static_cast<float>( m_alpha ) / 255.0f ) * locator_fade_time.GetFloat();
// Set the fade
m_fadeStart = gpGlobals->curtime - flAssumedFadeTime;
AddIconEffects( LOCATOR_ICON_FX_FADE_IN );
RemoveIconEffects( LOCATOR_ICON_FX_FADE_OUT );
}
else
{
// Determine home much time it would have spent fading to reach the current alpha
float flAssumedFadeTime;
flAssumedFadeTime = ( 1.0f - static_cast<float>( m_alpha ) / 255.0f ) * locator_fade_time.GetFloat();
// Set the fade
m_fadeStart = gpGlobals->curtime - flAssumedFadeTime;
AddIconEffects( LOCATOR_ICON_FX_FADE_OUT );
RemoveIconEffects( LOCATOR_ICON_FX_FADE_IN );
}
}
bool CLocatorTarget::IsVisible( void )
{
return m_bVisible;
}
void CLocatorTarget::SetCaptionText( const char *pszText, const char *pszParam )
{
wchar_t outbuf[ 256 ];
outbuf[ 0 ] = L'\0';
if ( pszParam && pszParam[ 0 ] != '\0' )
{
wchar_t wszParamBuff[ 128 ];
wchar_t *pLocalizedParam = NULL;
if ( pszParam[ 0 ] == '#' )
{
pLocalizedParam = g_pVGuiLocalize->Find( pszParam );
}
if ( !pLocalizedParam )
{
g_pVGuiLocalize->ConvertANSIToUnicode( pszParam, wszParamBuff, sizeof( wszParamBuff ) );
pLocalizedParam = wszParamBuff;
}
wchar_t wszTextBuff[ 128 ];
wchar_t *pLocalizedText = NULL;
if ( pszText[ 0 ] == '#' )
pLocalizedText = g_pVGuiLocalize->Find( pszText );
if ( !pLocalizedText )
{
g_pVGuiLocalize->ConvertANSIToUnicode( pszText, wszTextBuff, sizeof( wszTextBuff ) );
pLocalizedText = wszTextBuff;
}
wchar_t buf[ 256 ];
g_pVGuiLocalize->ConstructString( buf, sizeof(buf), pLocalizedText, 1, pLocalizedParam );
UTIL_ReplaceKeyBindings( buf, sizeof( buf ), outbuf, sizeof( outbuf ) );
}
else
{
wchar_t wszTextBuff[ 128 ];
wchar_t *pLocalizedText = NULL;
if ( pszText[ 0 ] == '#' )
{
pLocalizedText = g_pVGuiLocalize->Find( pszText );
}
if ( !pLocalizedText )
{
g_pVGuiLocalize->ConvertANSIToUnicode( pszText, wszTextBuff, sizeof( wszTextBuff ) );
pLocalizedText = wszTextBuff;
}
wchar_t buf[ 256 ];
Q_wcsncpy( buf, pLocalizedText, sizeof( buf ) );
UTIL_ReplaceKeyBindings( buf, sizeof(buf), outbuf, sizeof( outbuf ) );
}
int len = wcslen( outbuf ) + 1;
m_wszCaption.RemoveAll();
m_wszCaption.EnsureCount( len );
Q_wcsncpy( m_wszCaption.Base(), outbuf, len * sizeof( wchar_t ) );
}
void CLocatorTarget::SetCaptionColor( const char *pszCaptionColor )
{
int r,g,b;
r = g = b = 0;
CSplitString colorValues( pszCaptionColor, "," );
if( colorValues.Count() == 3 )
{
r = atoi( colorValues[0] );
g = atoi( colorValues[1] );
b = atoi( colorValues[2] );
m_captionColor.SetColor( r,g,b, 255 );
}
else
{
DevWarning( "caption_color format incorrect. RRR,GGG,BBB expected.\n");
}
}
bool CLocatorTarget::IsStatic()
{
return ( ( m_iEffectsFlags & LOCATOR_ICON_FX_STATIC ) || IsPresenting() );
}
bool CLocatorTarget::IsPresenting()
{
return ( gpGlobals->curtime - m_lerpStart < locator_lerp_rest.GetFloat() );
}
void CLocatorTarget::StartTimedLerp()
{
if ( gpGlobals->curtime - m_lerpStart > locator_lerp_rest.GetFloat() )
{
m_lerpStart = gpGlobals->curtime - locator_lerp_rest.GetFloat();
}
}
void CLocatorTarget::StartPresent()
{
m_lerpStart = gpGlobals->curtime;
}
void CLocatorTarget::EndPresent()
{
if ( gpGlobals->curtime - m_lerpStart < locator_lerp_rest.GetFloat() )
{
m_lerpStart = gpGlobals->curtime - locator_lerp_rest.GetFloat();
}
}
void CLocatorTarget::UpdateVguiTarget( void )
{
const char *pchVguiTargetName = m_szVguiTargetName.String();
if ( !pchVguiTargetName || pchVguiTargetName[ 0 ] == '\0' )
{
m_hVguiTarget = NULL;
return;
}
// Get the appropriate token based on the binding
if ( m_iBindingChoicesCount > 0 )
{
int nTagetToken = m_iBindChoicesOriginalToken[ m_iBindingTick % m_iBindingChoicesCount ];
for ( int nToken = 0; nToken < nTagetToken && pchVguiTargetName; ++nToken )
{
pchVguiTargetName = strchr( pchVguiTargetName, ';' );
if ( pchVguiTargetName )
{
pchVguiTargetName++;
}
}
if ( !pchVguiTargetName || pchVguiTargetName[ 0 ] == '\0' )
{
// There wasn't enough tokens, just use the first
pchVguiTargetName = m_szVguiTargetName.String();
}
}
m_hVguiTarget = g_pClientMode->GetViewport();
}
void CLocatorTarget::SetVguiTargetName( const char *pchVguiTargetName )
{
if ( Q_strcmp( m_szVguiTargetName.String(), pchVguiTargetName ) == 0 )
return;
m_szVguiTargetName = pchVguiTargetName;
UpdateVguiTarget();
}
void CLocatorTarget::SetVguiTargetLookup( const char *pchVguiTargetLookup )
{
m_szVguiTargetLookup = pchVguiTargetLookup;
}
void CLocatorTarget::SetVguiTargetEdge( int nVguiEdge )
{
m_nVguiTargetEdge = nVguiEdge;
}
vgui::Panel *CLocatorTarget::GetVguiTarget( void )
{
return (vgui::Panel *)m_hVguiTarget.Get();
}
//------------------------------------
void CLocatorTarget::SetOnscreenIconTextureName( const char *pszTexture )
{
if ( Q_strcmp( m_szOnscreenTexture.String(), pszTexture ) == 0 )
return;
m_szOnscreenTexture = pszTexture;
m_pIcon_onscreen = NULL; // Dirty the onscreen icon so that the Locator will look up the new icon by name.
m_pulseStart = gpGlobals->curtime;
}
//------------------------------------
void CLocatorTarget::SetOffscreenIconTextureName( const char *pszTexture )
{
if ( Q_strcmp( m_szOffscreenTexture.String(), pszTexture ) == 0 )
return;
m_szOffscreenTexture = pszTexture;
m_pIcon_offscreen = NULL; // Ditto
m_pulseStart = gpGlobals->curtime;
}
//------------------------------------
void CLocatorTarget::SetBinding( const char *pszBinding )
{
int iAllowJoystick = -1;
/*if ( !IsX360() )
{
// Only show joystick binds if it's enabled and non-joystick if it's disabled
iAllowJoystick = input->ControllerModeActive();
}*/
bool bIsControllerNow = ( iAllowJoystick != 0 );
if ( m_bWasControllerLast == bIsControllerNow )
{
// We haven't toggled joystick enabled recently, so if it's the same bind, bail
if ( Q_strcmp( m_szBinding.String(), pszBinding ) == 0 )
return;
}
m_bWasControllerLast = bIsControllerNow;
m_szBinding = pszBinding;
m_pIcon_onscreen = NULL; // Dirty the onscreen icon so that the Locator will look up the new icon by name.
m_pIcon_offscreen = NULL; // ditto.
m_flNextBindingTick = gpGlobals->curtime + 0.75f;
// Get a list of all the keys bound to these actions
m_iBindingChoicesCount = 0;
// Tokenize the binding name (could be more than one binding)
int nOriginalToken = 0;
const char *pchToken = m_szBinding.String();
char szToken[ 128 ];
pchToken = nexttoken( szToken, pchToken, ';' );
while ( pchToken )
{
// Get the first parameter
int iTokenBindingCount = 0;
const char *pchBinding = engine->Key_LookupBindingExact( szToken );
while ( m_iBindingChoicesCount < MAX_LOCATOR_BINDINGS_SHOWN && pchBinding )
{
m_pchBindingChoices[ m_iBindingChoicesCount ] = pchBinding;
m_iBindChoicesOriginalToken[ m_iBindingChoicesCount ] = nOriginalToken;
++m_iBindingChoicesCount;
++iTokenBindingCount;
pchBinding = engine->Key_LookupBindingExact( szToken );
}
nOriginalToken++;
pchToken = nexttoken( szToken, pchToken, ';' );
}
m_pulseStart = gpGlobals->curtime;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CLocatorTarget::UseBindingImage( char *pchIconTextureName, size_t bufSize )
{
if ( m_iBindingChoicesCount <= 0 )
{
if ( IsX360() )
{
Q_strncpy( pchIconTextureName, "icon_blank", bufSize );
}
else
{
Q_strncpy( pchIconTextureName, "icon_key_wide", bufSize );
return "#GameUI_Icons_NONE";
}
return NULL;
}
// Cycle through the list of binds at a rate of 2 per second
const char *pchBinding = m_pchBindingChoices[ m_iBindingTick % m_iBindingChoicesCount ];
// We counted at least one binding... this should not be NULL!
Assert( pchBinding );
if ( IsX360() )
{
// Use a blank background for the button icons
Q_strncpy( pchIconTextureName, "icon_blank", bufSize );
return pchBinding;
}
/*if ( input->ControllerModeActive() &&
( Q_strcmp( pchBinding, "A_BUTTON" ) == 0 ||
Q_strcmp( pchBinding, "B_BUTTON" ) == 0 ||
Q_strcmp( pchBinding, "X_BUTTON" ) == 0 ||
Q_strcmp( pchBinding, "Y_BUTTON" ) == 0 ||
Q_strcmp( pchBinding, "L_SHOULDER" ) == 0 ||
Q_strcmp( pchBinding, "R_SHOULDER" ) == 0 ||
Q_strcmp( pchBinding, "L_TRIGGER" ) == 0 ||
Q_strcmp( pchBinding, "R_TRIGGER" ) == 0 ||
Q_strcmp( pchBinding, "BACK" ) == 0 ||
Q_strcmp( pchBinding, "START" ) == 0 ||
Q_strcmp( pchBinding, "STICK1" ) == 0 ||
Q_strcmp( pchBinding, "STICK2" ) == 0 ||
Q_strcmp( pchBinding, "UP" ) == 0 ||
Q_strcmp( pchBinding, "DOWN" ) == 0 ||
Q_strcmp( pchBinding, "LEFT" ) == 0 ||
Q_strcmp( pchBinding, "RIGHT" ) == 0 ) )
{
// Use a blank background for the button icons
Q_strncpy( pchIconTextureName, "icon_blank", bufSize );
return pchBinding;
}*/
if ( Q_strcmp( pchBinding, "MOUSE1" ) == 0 )
{
Q_strncpy( pchIconTextureName, "icon_mouseLeft", bufSize );
return NULL;
}
else if ( Q_strcmp( pchBinding, "MOUSE2" ) == 0 )
{
Q_strncpy( pchIconTextureName, "icon_mouseRight", bufSize );
return NULL;
}
else if ( Q_strcmp( pchBinding, "MOUSE3" ) == 0 )
{
Q_strncpy( pchIconTextureName, "icon_mouseThree", bufSize );
return NULL;
}
else if ( Q_strcmp( pchBinding, "MWHEELUP" ) == 0 )
{
Q_strncpy( pchIconTextureName, "icon_mouseWheel_up", bufSize );
return NULL;
}
else if ( Q_strcmp( pchBinding, "MWHEELDOWN" ) == 0 )
{
Q_strncpy( pchIconTextureName, "icon_mouseWheel_down", bufSize );
return NULL;
}
else if ( Q_strcmp( pchBinding, "UPARROW" ) == 0 )
{
Q_strncpy( pchIconTextureName, "icon_key_up", bufSize );
return NULL;
}
else if ( Q_strcmp( pchBinding, "LEFTARROW" ) == 0 )
{
Q_strncpy( pchIconTextureName, "icon_key_left", bufSize );
return NULL;
}
else if ( Q_strcmp( pchBinding, "DOWNARROW" ) == 0 )
{
Q_strncpy( pchIconTextureName, "icon_key_down", bufSize );
return NULL;
}
else if ( Q_strcmp( pchBinding, "RIGHTARROW" ) == 0 )
{
Q_strncpy( pchIconTextureName, "icon_key_right", bufSize );
return NULL;
}
else if ( Q_strcmp( pchBinding, "SEMICOLON" ) == 0 ||
Q_strcmp( pchBinding, "INS" ) == 0 ||
Q_strcmp( pchBinding, "DEL" ) == 0 ||
Q_strcmp( pchBinding, "HOME" ) == 0 ||
Q_strcmp( pchBinding, "END" ) == 0 ||
Q_strcmp( pchBinding, "PGUP" ) == 0 ||
Q_strcmp( pchBinding, "PGDN" ) == 0 ||
Q_strcmp( pchBinding, "PAUSE" ) == 0 ||
Q_strcmp( pchBinding, "F10" ) == 0 ||
Q_strcmp( pchBinding, "F11" ) == 0 ||
Q_strcmp( pchBinding, "F12" ) == 0 )
{
Q_strncpy( pchIconTextureName, "icon_key_generic", bufSize );
return pchBinding;
}
else if ( Q_strlen( pchBinding ) <= 2 )
{
Q_strncpy( pchIconTextureName, "icon_key_generic", bufSize );
return pchBinding;
}
else if ( Q_strlen( pchBinding ) <= 6 )
{
Q_strncpy( pchIconTextureName, "icon_key_wide", bufSize );
return pchBinding;
}
else
{
Q_strncpy( pchIconTextureName, "icon_key_wide", bufSize );
return pchBinding;
}
return pchBinding;
}
//-----------------------------------------------------------------------------
int CLocatorTarget::GetIconWidth( void )
{
return m_wide;
}
//-----------------------------------------------------------------------------
int CLocatorTarget::GetIconHeight( void )
{
return m_tall;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CLocatorPanel : public vgui::EditablePanel
{
DECLARE_CLASS_SIMPLE( CLocatorPanel, vgui::EditablePanel );
public:
CLocatorPanel( vgui::Panel *parent, const char *name );
~CLocatorPanel( void );
virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
virtual void PerformLayout( void );
virtual void OnTick( void );
virtual void PaintBackground( void );
virtual void Paint( void );
void ValidateTexture( int *pTextureID, const char *pszTextureName );
bool ValidateTargetTextures( CLocatorTarget *pTarget );
bool IconsAreIntersecting( CLocatorTarget &first, CLocatorTarget &second, int iTolerance );
virtual void PaintTarget( CLocatorTarget *pTarget );
void DrawPointerBackground( CLocatorTarget *pTarget, int nPointerX, int nPointerY, int nWide, int nTall, bool bPointer );
void DrawStaticIcon( CLocatorTarget *pTarget );
void DrawDynamicIcon( CLocatorTarget *pTarget, bool bDrawCaption, bool bDrawSimpleArrow );
void DrawIndicatorArrow( int x, int y, int iconWide, int iconTall, int textWidth, int direction );
void DrawTargetCaption( CLocatorTarget *pTarget, int x, int y, bool bDrawMultiline );
int GetScreenWidthForCaption( const wchar_t *pString, vgui::HFont hFont );
void DrawBindingName( CLocatorTarget *pTarget, const char *pchBindingName, int x, int y, bool bController );
void ComputeTargetIconPosition( CLocatorTarget *pTarget, bool bSetPosition );
void CalculateOcclusion( CLocatorTarget *pTarget );
void DrawSimpleArrow( int x, int y, int iconWide, int iconTall );
void GetIconPositionForOffscreenTarget( const Vector &vecDelta, float flDist, int *pXPos, int *pYPos );
CLocatorTarget *GetPointerForHandle( int hTarget );
int AddTarget();
void RemoveTarget( int hTarget );
void GetTargetPosition( const Vector &vecDelta, float flRadius, float *xpos, float *ypos, float *flRotation );
void DeactivateAllTargets();
void CollectGarbage();
// Animation
void AnimateIconSize( int flags, int *wide, int *tall, float fPulseStart );
void AnimateIconPosition( int flags, int *x, int *y );
void AnimateIconAlpha( int flags, int *alpha, float fadeStart );
private:
CPanelAnimationVar( vgui::HFont, m_hCaptionFont, "font", "InstructorTitle" );
CPanelAnimationVar( vgui::HFont, m_hCaptionFont_ss, "font", "InstructorTitle_ss" );
CPanelAnimationVar( vgui::HFont, m_hCaptionGlowFont, "font", "InstructorTitleGlow" );
CPanelAnimationVar( vgui::HFont, m_hCaptionGlowFont_ss, "font", "InstructorTitleGlow_ss" );
CPanelAnimationVar( vgui::HFont, m_hButtonFont, "font", "InstructorButtons" );
CPanelAnimationVar( vgui::HFont, m_hButtonFont_ss, "font", "InstructorButtons_ss" );
CPanelAnimationVar( vgui::HFont, m_hKeysFont, "font", "InstructorKeyBindings" );
CPanelAnimationVar( int, m_iShouldWrapStaticLocators, "WrapStaticLocators", "0" );
static int m_serializer; // Used to issue unique serial numbers to targets, for use as handles
int m_textureID_ArrowRight;
int m_textureID_ArrowLeft;
int m_textureID_ArrowUp;
int m_textureID_ArrowDown;
int m_textureID_SimpleArrow;
int m_staticIconPosition;// Helps us stack static icons
CLocatorTarget m_targets[MAX_LOCATOR_TARGETS];
};
//-----------------------------------------------------------------------------
// Local variables
//-----------------------------------------------------------------------------
static CLocatorPanel *s_pLocatorPanel;
inline CLocatorPanel * GetPlayerLocatorPanel()
{
//if ( !engine->IsLocalPlayerResolvable() )
//return NULL;
Assert( s_pLocatorPanel );
return s_pLocatorPanel;
}
//-----------------------------------------------------------------------------
// Static variable initialization
//-----------------------------------------------------------------------------
int CLocatorPanel::m_serializer = 1000; // Serial numbers start at 1000
//-----------------------------------------------------------------------------
// This is the interface function that other systems use to send us targets
//-----------------------------------------------------------------------------
int Locator_AddTarget()
{
if( s_pLocatorPanel == NULL )
{
// Locator has not been used yet. Construct it.
CLocatorPanel *pLocator = new CLocatorPanel( g_pClientMode->GetViewport(), "LocatorPanel" );
vgui::SETUP_PANEL(pLocator);
pLocator->SetBounds( 0, 0, ScreenWidth(), ScreenHeight() );
pLocator->SetPos( 0, 0 );
pLocator->SetVisible( true );
vgui::ivgui()->AddTickSignal( pLocator->GetVPanel() );
}
Assert( s_pLocatorPanel != NULL );
return s_pLocatorPanel ? s_pLocatorPanel->AddTarget() : -1;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void Locator_RemoveTarget( int hTarget )
{
if ( CLocatorPanel *pPanel = GetPlayerLocatorPanel() )
pPanel->RemoveTarget( hTarget );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CLocatorTarget *Locator_GetTargetFromHandle( int hTarget )
{
if ( CLocatorPanel *pPanel = GetPlayerLocatorPanel() )
return pPanel->GetPointerForHandle( hTarget );
else
return NULL;
}
void Locator_ComputeTargetIconPositionFromHandle( int hTarget )
{
if ( CLocatorPanel *pPanel = GetPlayerLocatorPanel() )
{
if ( CLocatorTarget *pTarget = pPanel->GetPointerForHandle( hTarget ) )
{
if( !( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_STATIC ) )
{
// It's not presenting in the middle of the screen, so figure out it's position
pPanel->ComputeTargetIconPosition( pTarget, !pTarget->IsPresenting() );
pPanel->CalculateOcclusion( pTarget );
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CLocatorPanel::CLocatorPanel( Panel *parent, const char *name ) : EditablePanel(parent,name)
{
Assert( s_pLocatorPanel == NULL );
DeactivateAllTargets();
s_pLocatorPanel = this;
m_textureID_ArrowRight = -1;
m_textureID_ArrowLeft = -1;
m_textureID_ArrowUp = -1;
m_textureID_ArrowDown = -1;
m_textureID_SimpleArrow = -1;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CLocatorPanel::~CLocatorPanel( void )
{
Assert( s_pLocatorPanel == this );
s_pLocatorPanel = NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Applies scheme settings
//-----------------------------------------------------------------------------
void CLocatorPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
LoadControlSettings("resource/UI/Locator.res");
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CLocatorPanel::PerformLayout( void )
{
BaseClass::PerformLayout();
vgui::Panel *pPanel = FindChildByName( "LocatorBG" );
if ( pPanel )
pPanel->SetPos( (GetWide() - pPanel->GetWide()) * 0.5, (GetTall() - pPanel->GetTall()) * 0.5 );
}
//-----------------------------------------------------------------------------
// Purpose: Given an offscreen target position, compute the 'compass position'
// so that we can draw an icon on the imaginary circle around the crosshair
// that indicates which way the player should turn to bring the target into view.
//-----------------------------------------------------------------------------
void CLocatorPanel::GetTargetPosition( const Vector &vecDelta, float flRadius, float *xpos, float *ypos, float *flRotation )
{
// Player Data
Vector playerPosition = MainViewOrigin();
QAngle playerAngles = MainViewAngles();
Vector forward, right, up(0,0,1);
AngleVectors (playerAngles, &forward, NULL, NULL );
forward.z = 0;
VectorNormalize(forward);
CrossProduct( up, forward, right );
float front = DotProduct(vecDelta, forward);
float side = DotProduct(vecDelta, right);
*xpos = flRadius * -side;
*ypos = flRadius * -front;
// Get the rotation (yaw)
*flRotation = atan2(*xpos,*ypos) + M_PI;
*flRotation *= 180 / M_PI;
float yawRadians = -(*flRotation) * M_PI / 180.0f;
float ca = cos( yawRadians );
float sa = sin( yawRadians );
// Rotate it around the circle, squash Y to make an oval rather than a circle
*xpos = (int)((ScreenWidth() / 2) + (flRadius * sa));
*ypos = (int)((ScreenHeight() / 2) - (flRadius * 0.6f * ca));
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CLocatorPanel::DeactivateAllTargets()
{
for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ )
m_targets[ i ].Deactivate( true );
}
//-----------------------------------------------------------------------------
// Purpose: Deactivate any target that has not been updated within several frames
//-----------------------------------------------------------------------------
void CLocatorPanel::CollectGarbage()
{
for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ )
{
if( m_targets[ i ].m_isActive )
{
if( gpGlobals->framecount - m_targets[ i ].m_frameLastUpdated > 20 )
m_targets[ i ].Deactivate();
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Provide simple animation by modifying the width and height of the
// icon before it is drawn.
//-----------------------------------------------------------------------------
void CLocatorPanel::AnimateIconSize( int flags, int *wide, int *tall, float fPulseStart )
{
float flScale = MIN_ICON_SCALE;
float scaleDelta = MAX_ICON_SCALE - MIN_ICON_SCALE;
float newWide = *wide;
float newTall = *tall;
if( flags & LOCATOR_ICON_FX_PULSE_SLOW || gpGlobals->curtime - fPulseStart < locator_pulse_time.GetFloat() )
{
flScale += scaleDelta * fabs( sin( ( gpGlobals->curtime - fPulseStart ) * M_PI ) );
}
else if( flags & LOCATOR_ICON_FX_PULSE_FAST )
{
flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 2 * M_PI ) );
}
else if( flags & LOCATOR_ICON_FX_PULSE_URGENT )
{
flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 4 * M_PI ) );
}
if ( newWide > newTall )
{
// Get scale to make width change by only the standard height amount of pixels
int iHeightDelta = (int)(newTall * flScale - newTall);
flScale = ( newWide + iHeightDelta ) / newWide;
}
newWide = newWide * flScale;
newTall = newTall * flScale;
*wide = newWide;
*tall = newTall;
}
//-----------------------------------------------------------------------------
// Purpose: Modify the alpha of the icon before it is drawn.
//-----------------------------------------------------------------------------
void CLocatorPanel::AnimateIconAlpha( int flags, int *alpha, float fadeStart )
{
float flScale = MIN_ICON_ALPHA;
float scaleDelta = MAX_ICON_ALPHA - MIN_ICON_ALPHA;
if( flags & LOCATOR_ICON_FX_ALPHA_SLOW )
{
flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 3 ) );
}
else if( flags & LOCATOR_ICON_FX_ALPHA_FAST )
{
flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 7 ) );
}
else if( flags & LOCATOR_ICON_FX_ALPHA_URGENT )
{
flScale += scaleDelta * fabs( sin( gpGlobals->curtime * 10 ) );
}
else
{
flScale = MAX_ICON_ALPHA;
}
if ( flags & LOCATOR_ICON_FX_FADE_OUT )
{
flScale *= MAX( 0.0f, ( locator_fade_time.GetFloat() - ( gpGlobals->curtime - fadeStart ) ) / locator_fade_time.GetFloat() );
}
else if ( flags & LOCATOR_ICON_FX_FADE_IN )
{
flScale *= MAX_ICON_ALPHA - MAX( 0.0f, ( locator_fade_time.GetFloat() - ( gpGlobals->curtime - fadeStart ) ) / locator_fade_time.GetFloat() );
}
*alpha = static_cast<int>( 255.0f * flScale );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CLocatorPanel::AnimateIconPosition( int flags, int *x, int *y )
{
int newX = *x;
int newY = *y;
if( flags & LOCATOR_ICON_FX_SHAKE_NARROW )
{
newX += RandomInt( -2, 2 );
newY += RandomInt( -2, 2 );
}
else if( flags & LOCATOR_ICON_FX_SHAKE_WIDE )
{
newX += RandomInt( -5, 5 );
newY += RandomInt( -5, 5 );
}
*x = newX;
*y = newY;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CLocatorPanel::OnTick( void )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CLocatorPanel::PaintBackground( void )
{
return;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CLocatorPanel::Paint( void )
{
ValidateTexture( &m_textureID_ArrowLeft, "vgui/hud/icon_arrow_left" );
ValidateTexture( &m_textureID_ArrowRight, "vgui/hud/icon_arrow_right" );
ValidateTexture( &m_textureID_ArrowUp, "vgui/hud/icon_arrow_up" );
ValidateTexture( &m_textureID_ArrowDown, "vgui/hud/icon_arrow_down" );
ValidateTexture( &m_textureID_SimpleArrow, "vgui/hud/icon_arrow_plain" );
// reset the static icon position. This is the y position at which the first
// static icon will be drawn. This value will be incremented by the height of
// each static icon drawn, which forces the next static icon to be drawn below
// the previous one, creating a little fixed, vertical stack below the crosshair.
m_staticIconPosition = ScreenHeight() / 6;
// Time now to draw the 'dynamic' icons, the icons which help players locate things
// in actual world space.
//----------
// Batch 1
// Go through all of the active locator targets and compute where to draw the icons
// that represent each of them. This builds a poor man's draw list by updating the
// m_iconX, m_iconY members of each locator target.
CUtlVectorFixed< CLocatorTarget *, MAX_LOCATOR_TARGETS > vecValid;
for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ )
{
CLocatorTarget *pLocatorTarget = &(m_targets[ i ]);
// Reset drawing state for this frame... set back to true when it's finally draws
pLocatorTarget->m_bIsDrawing = false;
if ( ( !pLocatorTarget->m_bVisible && !pLocatorTarget->m_alpha ) || !pLocatorTarget->m_isActive )
{
// Don't want to be visible and have finished fading
continue;
}
vecValid.AddToTail( pLocatorTarget );
// This prevents an error that if a locator was fading as the map transitioned
pLocatorTarget->m_fadeStart = fpmin( pLocatorTarget->m_fadeStart, gpGlobals->curtime );
if( !( pLocatorTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_STATIC ) )
{
// It's not presenting in the middle of the screen, so figure out it's position
ComputeTargetIconPosition( pLocatorTarget, !pLocatorTarget->IsPresenting() );
CalculateOcclusion( pLocatorTarget );
pLocatorTarget->m_lastDeclutterIndex = pLocatorTarget->m_declutterIndex;
pLocatorTarget->m_declutterIndex = 0;
}
}
//----------
// Batch 2
// Now that we know where each icon _wants_ to be drawn, we grovel through them and
// push apart any icons that are too close to one another. This helps to unclutter
// the display and ensure the maximum number of legible icons and captions. Obviously
// this process changes where some icons will be drawn. Bubble sort, but tiny data set.
int iTolerance = 1.25 * (ScreenWidth() * ICON_SIZE);
int iterations = 0;// Count iterations, don't go infinite in the event of some weird case.
bool bStillUncluttering = true;
static int MAX_UNCLUTTER_ITERATIONS = 10;
while( iterations < MAX_UNCLUTTER_ITERATIONS && bStillUncluttering )
{
iterations++;
bStillUncluttering = false;
for( int i = 0 ; i < vecValid.Count() ; ++i )
{
CLocatorTarget *pLocatorTarget1 = vecValid[ i ];
for( int j = i + 1 ; j < vecValid.Count() ; ++j )
{
CLocatorTarget *pLocatorTarget2 = vecValid[ j ];
// Don't attempt to declutter icons if one or both is attempting to fade out
bool bLocatorsFullyActive = !((pLocatorTarget1->GetIconEffectsFlags()|pLocatorTarget2->GetIconEffectsFlags()) & LOCATOR_ICON_FX_FADE_OUT);
if ( bLocatorsFullyActive && IconsAreIntersecting( *pLocatorTarget1, *pLocatorTarget2, iTolerance ) )
{
// Unclutter. Lift whichever icon is highest a bit higher
if( pLocatorTarget1->m_iconY < pLocatorTarget2->m_iconY )
{
pLocatorTarget1->m_iconY = pLocatorTarget2->m_iconY - iTolerance;
pLocatorTarget1->m_centerY = pLocatorTarget2->m_centerY - iTolerance;
pLocatorTarget1->m_declutterIndex -= 1;
}
else
{
pLocatorTarget2->m_iconY = pLocatorTarget1->m_iconY - iTolerance;
pLocatorTarget2->m_centerY = pLocatorTarget1->m_centerY - iTolerance;
pLocatorTarget2->m_declutterIndex -= 1;
}
bStillUncluttering = true;
}
}
}
}
if( iterations == MAX_UNCLUTTER_ITERATIONS )
{
DevWarning( "Game instructor hit MAX_UNCLUTTER_ITERATIONS!\n");
}
float flLocatorLerpRest = locator_lerp_rest.GetFloat();
float flLocatorLerpTime = locator_lerp_time.GetFloat();
//----------
// Batch 3
// Draw each of the icons.
for( int i = 0 ; i < vecValid.Count() ; i++ )
{
CLocatorTarget *pLocatorTarget = vecValid[ i ];
// Back to lerping for these guys
if ( pLocatorTarget->m_lastDeclutterIndex != pLocatorTarget->m_declutterIndex )
{
// It wants to be popped to another position... do it smoothly
pLocatorTarget->StartTimedLerp();
}
// Lerp to the desired position
float flLerpTime = gpGlobals->curtime - pLocatorTarget->m_lerpStart;
if ( flLerpTime >= flLocatorLerpRest && flLerpTime < flLocatorLerpRest + flLocatorLerpTime )
{
// Lerp slow to fast
float fInterp = 1.0f - ( ( flLocatorLerpTime - ( flLerpTime - flLocatorLerpRest ) ) / flLocatorLerpTime );
// Get our desired position
float iconX = pLocatorTarget->m_iconX;
float iconY = pLocatorTarget->m_iconY;
// Get the distance we need to go to reach it
float diffX = fabsf( pLocatorTarget->m_iconX - pLocatorTarget->m_lastXPos );
float diffY = fabsf( pLocatorTarget->m_iconY - pLocatorTarget->m_lastYPos );
// Go from our current position toward the desired position as quick as the interp allows
pLocatorTarget->m_iconX = static_cast<int>( Approach( iconX, pLocatorTarget->m_lastXPos, diffX * fInterp ) );
pLocatorTarget->m_iconY = static_cast<int>( Approach( iconY, pLocatorTarget->m_lastYPos, diffY * fInterp ) );
// Get how much our position changed and apply it to the center values
int iOffsetX = pLocatorTarget->m_iconX - iconX;
int iOffsetY = pLocatorTarget->m_iconY - iconY;
pLocatorTarget->m_centerX += iOffsetX;
pLocatorTarget->m_centerY += iOffsetY;
if ( iOffsetX < 3 && iOffsetY < 3 )
{
// Near our target! Stop lerping!
flLerpTime = flLocatorLerpRest + flLocatorLerpTime;
}
}
PaintTarget( pLocatorTarget );
}
CollectGarbage();
}
//-----------------------------------------------------------------------------
// Purpose: A helper function to save on typing. Make sure our texture ID's
// stay valid.
//-----------------------------------------------------------------------------
void CLocatorPanel::ValidateTexture( int *pTextureID, const char *pszTextureName )
{
if( *pTextureID == -1 )
{
*pTextureID = vgui::surface()->CreateNewTextureID();
vgui::surface()->DrawSetTextureFile( *pTextureID, pszTextureName, true, false );
}
}
//-----------------------------------------------------------------------------
// Purpose: Called every frame before painting the targets. Ensures that the
// target's textures are properly cached.
//-----------------------------------------------------------------------------
bool CLocatorPanel::ValidateTargetTextures( CLocatorTarget *pTarget )
{
bool bBindingTick = false;
if ( gpGlobals->curtime >= pTarget->m_flNextBindingTick )
{
if ( pTarget->m_iBindingChoicesCount > 1 )
{
bBindingTick = true;
pTarget->m_iBindingTick++;
}
pTarget->m_flNextBindingTick = gpGlobals->curtime + 0.75f;
pTarget->UpdateVguiTarget();
}
bool bUsesBinding = ( Q_stricmp( pTarget->GetOnscreenIconTextureName(), "use_binding" ) == 0 );
if( !pTarget->m_pIcon_onscreen || !pTarget->m_pIcon_offscreen || ( bUsesBinding && bBindingTick ) )
{
char szIconTextureName[ 256 ];
if ( bUsesBinding )
{
const char *pchDrawBindingName = pTarget->UseBindingImage( szIconTextureName, sizeof( szIconTextureName ) );
pTarget->m_bDrawControllerButton = ( Q_strcmp( szIconTextureName, "icon_blank" ) == 0 );
pTarget->DrawBindingName( pchDrawBindingName );
}
else
{
pTarget->m_bDrawControllerButton = false;
Q_strcpy( szIconTextureName, pTarget->GetOnscreenIconTextureName() );
pTarget->DrawBindingName( NULL );
}
// This target's texture ID is dirty, meaning the target is about to be drawn
// for the first time, or about to be drawn for the first time since a texture
// was changed.
if ( Q_strlen(szIconTextureName) == 0 )
{
DevWarning("Locator Target has no onscreen texture name!\n");
return false;
}
else
{
pTarget->m_pIcon_onscreen = HudIcons().GetIcon( szIconTextureName );
if ( pTarget->m_pIcon_onscreen )
{
pTarget->m_widthScale_onscreen = static_cast< float >( pTarget->m_pIcon_onscreen->Width() ) / pTarget->m_pIcon_onscreen->Height();
}
else
{
pTarget->m_widthScale_onscreen = 1.0f;
}
}
if ( Q_stricmp( pTarget->GetOffscreenIconTextureName() , "use_binding" ) == 0 )
{
const char *pchDrawBindingName = pTarget->UseBindingImage( szIconTextureName, sizeof( szIconTextureName ) );
pTarget->m_bDrawControllerButtonOffscreen = ( Q_strcmp( szIconTextureName, "icon_blank" ) == 0 );
pTarget->DrawBindingNameOffscreen( pchDrawBindingName );
}
else
{
pTarget->m_bDrawControllerButtonOffscreen = false;
Q_strcpy( szIconTextureName, pTarget->GetOffscreenIconTextureName() );
pTarget->DrawBindingNameOffscreen( NULL );
}
if( Q_strlen(szIconTextureName) == 0 )
{
if( !pTarget->m_pIcon_onscreen )
{
DevWarning("Locator Target has no offscreen texture name and can't fall back!\n");
}
else
{
// The onscreen texture is valid, so default behavior is to use that.
pTarget->m_pIcon_offscreen = pTarget->m_pIcon_onscreen;
const char *pchDrawBindingName = pTarget->DrawBindingName();
pTarget->DrawBindingNameOffscreen( pchDrawBindingName );
}
}
else
{
pTarget->m_pIcon_offscreen = HudIcons().GetIcon( szIconTextureName );
}
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Compute where on the screen to draw the icon for this target.
//-----------------------------------------------------------------------------
void CLocatorPanel::ComputeTargetIconPosition( CLocatorTarget *pTarget, bool bSetPosition )
{
int iconX;
int iconY;
// Measure the delta and the dist from this player to this target.
Vector vecTarget = pTarget->m_vecOrigin;
Vector vecDelta = vecTarget - MainViewOrigin();
if ( pTarget->m_bOriginInScreenspace )
{
// Coordinates are already in screenspace
pTarget->m_distFromPlayer = 0.0f;
iconX = vecTarget.x * ScreenWidth();
iconY = vecTarget.y * ScreenHeight();
pTarget->m_targetX = iconX;
pTarget->m_targetY = iconY;
}
else
{
pTarget->m_distFromPlayer = VectorNormalize( vecDelta );
if ( GetVectorInScreenSpace( vecTarget, iconX, iconY ) )
{
// NOTE: GetVectorInScreenSpace returns false in an edge case where the
// target is very far off screen... just us the old values
pTarget->m_targetX = iconX;
pTarget->m_targetY = iconY;
}
}
pTarget->m_drawArrowDirection = DRAW_ARROW_NO;
float fTitleSafeInset = ScreenWidth() * 0.075f;
if( iconX < fTitleSafeInset || iconX > ScreenWidth() - fTitleSafeInset )
{
// It's off the screen left or right.
if ( pTarget->m_bOnscreen && !( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_NO_OFFSCREEN ) )
{
// Back to lerping
pTarget->StartTimedLerp();
pTarget->m_pulseStart = gpGlobals->curtime;
}
if ( bSetPosition )
{
pTarget->m_bOnscreen = false;
}
GetIconPositionForOffscreenTarget( vecDelta, pTarget->m_distFromPlayer, &iconX, &iconY );
Vector vCenter = pTarget->m_vecOrigin;
if( MainViewRight().Dot( vCenter - MainViewOrigin() ) > 0 )
pTarget->m_drawArrowDirection = DRAW_ARROW_RIGHT;
else
pTarget->m_drawArrowDirection = DRAW_ARROW_LEFT;
}
else if( iconY < fTitleSafeInset || iconY > ScreenHeight() - fTitleSafeInset )
{
// It's off the screen up or down.
if ( pTarget->m_bOnscreen && !( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_NO_OFFSCREEN ) )
{
// Back to lerping
pTarget->StartTimedLerp();
pTarget->m_pulseStart = gpGlobals->curtime;
}
if ( bSetPosition )
{
pTarget->m_bOnscreen = false;
}
GetIconPositionForOffscreenTarget( vecDelta, pTarget->m_distFromPlayer, &iconX, &iconY );
Vector vCenter = pTarget->m_vecOrigin;
if( MainViewUp().Dot( vCenter - MainViewOrigin() ) > 0 )
pTarget->m_drawArrowDirection = DRAW_ARROW_UP;
else
pTarget->m_drawArrowDirection = DRAW_ARROW_DOWN;
}
else
{
if ( !pTarget->m_bOnscreen && !( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_NO_OFFSCREEN ) )
{
// Back to lerping
pTarget->StartTimedLerp();
pTarget->m_pulseStart = gpGlobals->curtime;
}
pTarget->m_bOnscreen = true;
}
if ( bSetPosition )
{
int tall = ScreenWidth() * ICON_SIZE;
int wide = tall * pTarget->m_widthScale_onscreen;
// Animate the icon
AnimateIconSize( pTarget->GetIconEffectsFlags(), &wide, &tall, pTarget->m_pulseStart );
AnimateIconPosition( pTarget->GetIconEffectsFlags(), &iconX, &iconY );
AnimateIconAlpha( pTarget->GetIconEffectsFlags(), &pTarget->m_alpha, pTarget->m_fadeStart );
if( pTarget->m_distFromPlayer > ICON_DIST_TOO_FAR && !locator_topdown_style.GetBool() )
{
// Make the icon smaller
wide = wide >> 1;
tall = tall >> 1;
}
pTarget->m_centerX = iconX;
pTarget->m_centerY = iconY;
pTarget->m_iconX = pTarget->m_centerX - ( wide >> 1 );
pTarget->m_iconY = pTarget->m_centerY - ( tall >> 1 );
pTarget->m_wide = wide;
pTarget->m_tall = tall;
}
}
void CLocatorPanel::CalculateOcclusion( CLocatorTarget *pTarget )
{
if ( gpGlobals->curtime >= pTarget->m_flNextOcclusionTest )
{
pTarget->m_flNextOcclusionTest = gpGlobals->curtime + LOCATOR_OCCLUSION_TEST_RATE;
// Assume the target is not occluded.
pTarget->m_bOccluded = false;
if ( pTarget->m_bOriginInScreenspace )
return;
trace_t tr;
UTIL_TraceLine( pTarget->m_vecOrigin, MainViewOrigin(), (CONTENTS_SOLID|CONTENTS_MOVEABLE), NULL, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction < 1.0f )
{
pTarget->m_bOccluded = true;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: This is not valid until after you have computed the onscreen
// icon position for each target!
//-----------------------------------------------------------------------------
bool CLocatorPanel::IconsAreIntersecting( CLocatorTarget &first, CLocatorTarget &second, int iTolerance )
{
if( first.m_bOnscreen != second.m_bOnscreen )
{
// We only declutter onscreen icons against other onscreen icons and vice-versa.
return false;
}
if( first.IsStatic() || second.IsStatic() )
{
// Static icons don't count.
return false;
}
if( abs(first.GetIconY() - second.GetIconY()) < iTolerance )
{
// OK, we need the Y-check first. Now we have to see if these icons and their captions overlap.
int firstWide = iTolerance + first.m_captionWide;
int secondWide = iTolerance + second.m_captionWide;
if( abs(first.GetIconX() - second.GetIconX()) < (firstWide + secondWide) / 2 )
{
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Draw this target on the locator.
//
// IF onscreen and visible, draw no icon, draw no arrows
// IF onscreen and occluded, draw icon transparently, draw no arrows
// IF offscreen, draw icon, draw an arrow indicating the direction to the target
//-----------------------------------------------------------------------------
void CLocatorPanel::PaintTarget( CLocatorTarget *pTarget )
{
bool bNewTexture = ValidateTargetTextures( pTarget );
if ( bNewTexture )
{
// Refigure the width/height for the new texture
int tall = ScreenWidth() * ICON_SIZE;
int wide = tall * pTarget->m_widthScale_onscreen;
AnimateIconSize( pTarget->GetIconEffectsFlags(), &wide, &tall, pTarget->m_pulseStart );
pTarget->m_wide = wide;
pTarget->m_tall = tall;
}
// A static icon just draws with other static icons in a stack under the crosshair.
// Once displayed, they do not move. The are often used for notifiers.
if( pTarget->IsStatic() )
{
DrawStaticIcon( pTarget );
return;
}
if ( !pTarget->m_bOnscreen && ( pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_NO_OFFSCREEN ) )
{
// Doesn't draw when offscreen... reset it's alpha so it has to fade in again
pTarget->m_fadeStart = gpGlobals->curtime;
pTarget->m_alpha = 0;
}
else
{
// Save these coordinates for later lerping
pTarget->m_lastXPos = pTarget->m_iconX;
pTarget->m_lastYPos = pTarget->m_iconY;
// Draw when it's on screen or allowed to draw offscreen
DrawDynamicIcon( pTarget, pTarget->HasCaptionText(), pTarget->m_bOnscreen );
}
}
//-----------------------------------------------------------------------------
// Purpose: Draws the caption-like background with word-bubble style pointer
//-----------------------------------------------------------------------------
void CLocatorPanel::DrawPointerBackground( CLocatorTarget *pTarget, int nPointerX, int nPointerY, int nWide, int nTall, bool bPointer )
{
if ( locator_background_style.GetInt() == 0 || pTarget->m_alpha == 0 )
return;
int nPosX = pTarget->GetIconX() + locator_background_shift_x.GetInt() - locator_background_thickness_x.GetInt() / 2;
int nPosY = pTarget->GetIconY() + locator_background_shift_y.GetInt() - locator_background_thickness_y.GetInt() / 2;
int nBackgroundWide = nWide + locator_background_thickness_x.GetInt();
int nBackgroundTall = nTall + locator_background_thickness_y.GetInt();
nPointerX = clamp( nPointerX, -0.5f * ScreenWidth(), ScreenWidth() * 1.5f );
nPointerY = clamp( nPointerY, -0.5f * ScreenHeight(), ScreenHeight() * 1.5f );
float fAlpha = static_cast<float>( pTarget->m_alpha ) / 255.0f;
Color rgbaBackground = locator_background_color.GetColor();
rgbaBackground[ 3 ] *= fAlpha;
Color rgbaBorder = locator_background_border_color.GetColor();
rgbaBorder[ 3 ] *= fAlpha;
DevMsg("[TODO] vgui::surface()->DrawWordBubble \n");
//vgui::surface()->DrawWordBubble( nPosX, nPosY, nPosX + nBackgroundWide, nPosY + nBackgroundTall, locator_background_border_thickness.GetInt(), rgbaBackground, rgbaBorder, bPointer, nPointerX, nPointerY, ScreenWidth() * ICON_SIZE );
}
//-----------------------------------------------------------------------------
// Purpose: Draw an icon with the group of static icons.
//-----------------------------------------------------------------------------
void CLocatorPanel::DrawStaticIcon( CLocatorTarget *pTarget )
{
int centerX = ScreenWidth() / 2;
int centerY = ScreenHeight() / 2;
centerY += m_staticIconPosition;
int iconTall = ScreenWidth() * ICON_SIZE;
int iconWide = iconTall * pTarget->m_widthScale_onscreen;
pTarget->m_centerX = centerX;
pTarget->m_centerY = centerY;
// Animate the icon
AnimateIconSize( pTarget->GetIconEffectsFlags(), &iconWide, &iconTall, pTarget->m_pulseStart );
AnimateIconPosition( pTarget->GetIconEffectsFlags(), ¢erX, ¢erY );
AnimateIconAlpha( pTarget->GetIconEffectsFlags(), &pTarget->m_alpha, pTarget->m_fadeStart );
// Figure out the caption width
pTarget->m_captionWide = GetScreenWidthForCaption( pTarget->GetCaptionText(), m_hCaptionFont );
bool bDrawMultilineCaption = false;
if ( m_iShouldWrapStaticLocators > 0 ) // conditionalized in locator.res
{
if ( pTarget->m_captionWide > ( ScreenWidth() * locator_split_maxwide_percent.GetFloat() ) )
{
// we will double-line this
pTarget->m_captionWide = pTarget->m_captionWide * locator_split_len.GetFloat();
bDrawMultilineCaption = true;
}
}
int totalWide = iconWide + ICON_GAP + pTarget->m_captionWide;
pTarget->m_iconX = centerX - totalWide * 0.5f;
pTarget->m_iconY = centerY - ( iconTall >> 1 );
// Lerp by speed on the Y axis
float iconY = pTarget->m_iconY;
float diffY = fabsf( pTarget->m_iconY - pTarget->m_lastYPos );
float flLerpSpeed = gpGlobals->frametime * locator_lerp_speed.GetFloat();
pTarget->m_iconY = static_cast<int>( Approach( iconY, pTarget->m_lastYPos, MAX( 3.0f, flLerpSpeed * diffY ) ) );
pTarget->m_centerY += ( pTarget->m_iconY - iconY );
pTarget->m_lastXPos = pTarget->m_iconX;
pTarget->m_lastYPos = pTarget->m_iconY;
pTarget->m_bIsDrawing = true;
vgui::Panel *pVguiTarget = pTarget->GetVguiTarget();
if ( pVguiTarget )
{
int nPanelX, nPanelY;
nPanelX = 0;
nPanelY = 0;
vgui::Label::Alignment nVguiTargetEdge = (vgui::Label::Alignment)pTarget->GetVguiTargetEdge();
int nWide = pVguiTarget->GetWide();
int nTall = pVguiTarget->GetTall();
const char *pchLookup = pTarget->GetVguiTargetLookup();
/*if ( pchLookup[ 0 ] != '\0' )
{
bool bLookupSuccess = false;
bLookupSuccess = pVguiTarget->LookupElementBounds( pchLookup, nPanelX, nPanelY, nWide, nTall );
Assert( bLookupSuccess );
}*/
if ( nVguiTargetEdge == vgui::Label::a_north ||
nVguiTargetEdge == vgui::Label::a_center ||
nVguiTargetEdge == vgui::Label::a_south )
{
nPanelX += nWide / 2;
}
else if ( nVguiTargetEdge == vgui::Label::a_northeast ||
nVguiTargetEdge == vgui::Label::a_east ||
nVguiTargetEdge == vgui::Label::a_southeast )
{
nPanelX += nWide;
}
if ( nVguiTargetEdge == vgui::Label::a_west ||
nVguiTargetEdge == vgui::Label::a_center ||
nVguiTargetEdge == vgui::Label::a_east )
{
nPanelY += nTall / 2;
}
else if ( nVguiTargetEdge == vgui::Label::a_southwest ||
nVguiTargetEdge == vgui::Label::a_south ||
nVguiTargetEdge == vgui::Label::a_southeast )
{
nPanelY += nTall;
}
pVguiTarget->LocalToScreen( nPanelX, nPanelY );
DrawPointerBackground( pTarget, nPanelX, nPanelY, totalWide, iconTall, true );
}
else
{
DrawPointerBackground( pTarget, pTarget->m_centerX, pTarget->m_centerY, totalWide, iconTall, false );
}
if ( pTarget->m_pIcon_onscreen )
{
if ( !pTarget->m_bDrawControllerButton )
{
// Don't draw the icon if we're on 360 and have a binding to draw
pTarget->m_pIcon_onscreen->DrawSelf( pTarget->GetIconX(), pTarget->GetIconY(), iconWide, iconTall, Color( 255, 255, 255, pTarget->m_alpha ) );
}
}
DrawTargetCaption( pTarget, pTarget->GetIconX() + iconWide + ICON_GAP, pTarget->GetIconCenterY(), bDrawMultilineCaption );
if ( pTarget->DrawBindingName() )
{
DrawBindingName( pTarget, pTarget->DrawBindingName(), pTarget->GetIconX() + (iconWide>>1), pTarget->GetIconY() + (iconTall>>1), pTarget->m_bDrawControllerButton );
}
// Draw the arrow.
int iArrowSize = ScreenWidth() * ICON_SIZE; // Always square width
DrawIndicatorArrow( pTarget->GetIconX(), pTarget->GetIconY(), iArrowSize, iArrowSize, pTarget->m_captionWide + ICON_GAP, pTarget->m_drawArrowDirection );
pTarget->m_bOnscreen = true;
// Move the static icon position so the next static icon drawn this frame below this one.
m_staticIconPosition += iconTall + (iconTall>>2);
// Move down a little more if this one was multi-line
if ( bDrawMultilineCaption )
{
m_staticIconPosition += (iconTall>>2);
}
return;
}
//-----------------------------------------------------------------------------
// Purpose: Position and animate this target's icon on the screen. Based on
// options, draw the indicator arrows (arrows that point to the
// direction the player should turn to see the icon), text caption,
// and the 'simple' arrow which just points down to indicate the
// item the icon represents.
//-----------------------------------------------------------------------------
void CLocatorPanel::DrawDynamicIcon( CLocatorTarget *pTarget, bool bDrawCaption, bool bDrawSimpleArrow )
{
int alpha = pTarget->m_alpha;
if( pTarget->m_bOccluded && !( (pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_FORCE_CAPTION) || locator_topdown_style.GetBool() ) )
{
return;
}
// Draw the icon!
vgui::surface()->DrawSetColor( 255, 255, 255, alpha );
int iWide = pTarget->m_wide;
if ( !pTarget->m_bOnscreen )
{
// Width is always square for offscreen icons
iWide /= pTarget->m_widthScale_onscreen;
}
// Figure out the caption width
pTarget->m_captionWide = GetScreenWidthForCaption( pTarget->GetCaptionText(), m_hCaptionFont );
bool bDrawMultilineCaption = false;
if ( m_iShouldWrapStaticLocators > 0 ) // conditionalized in locator.res
{
if ( pTarget->m_captionWide > ( ScreenWidth() * locator_split_maxwide_percent.GetFloat() ) )
{
// we will double-line this
pTarget->m_captionWide = pTarget->m_captionWide * locator_split_len.GetFloat();
bDrawMultilineCaption = true;
}
}
int totalWide = iWide;
bool bShouldDrawCaption = ( (pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_FORCE_CAPTION) || (!pTarget->m_bOccluded && pTarget->m_distFromPlayer <= ICON_DIST_TOO_FAR) || locator_topdown_style.GetBool() );
if( pTarget->m_bOnscreen && bDrawCaption && bShouldDrawCaption )
{
totalWide += ( ICON_GAP + pTarget->m_captionWide );
}
pTarget->m_bIsDrawing = true;
int nTargetX, nTargetY;
vgui::Panel *pVguiTarget = pTarget->GetVguiTarget();
if ( pVguiTarget )
{
nTargetX = 0;
nTargetY = 0;
vgui::Label::Alignment nVguiTargetEdge = (vgui::Label::Alignment)pTarget->GetVguiTargetEdge();
int nWide = pVguiTarget->GetWide();
int nTall = pVguiTarget->GetTall();
const char *pchLookup = pTarget->GetVguiTargetLookup();
/*if ( pchLookup[ 0 ] != '\0' )
{
bool bLookupSuccess = false;
bLookupSuccess = pVguiTarget->LookupElementBounds( pchLookup, nTargetX, nTargetY, nWide, nTall );
Assert( bLookupSuccess );
}*/
if ( nVguiTargetEdge == vgui::Label::a_north ||
nVguiTargetEdge == vgui::Label::a_center ||
nVguiTargetEdge == vgui::Label::a_south )
{
nTargetX += nWide / 2;
}
else if ( nVguiTargetEdge== vgui::Label::a_northeast ||
nVguiTargetEdge == vgui::Label::a_east ||
nVguiTargetEdge == vgui::Label::a_southeast )
{
nTargetX += nWide;
}
if ( nVguiTargetEdge == vgui::Label::a_west ||
nVguiTargetEdge == vgui::Label::a_center ||
nVguiTargetEdge == vgui::Label::a_east )
{
nTargetY += nTall / 2;
}
else if ( nVguiTargetEdge == vgui::Label::a_southwest ||
nVguiTargetEdge == vgui::Label::a_south ||
nVguiTargetEdge == vgui::Label::a_southeast )
{
nTargetY += nTall;
}
pVguiTarget->LocalToScreen( nTargetX, nTargetY );
}
else if ( !pTarget->m_bOnscreen )
{
nTargetX = pTarget->m_targetX;
nTargetY = pTarget->m_targetY;
}
else
{
nTargetX = pTarget->m_centerX;
nTargetY = pTarget->m_centerY;
}
if ( pTarget->m_bOnscreen )
{
DrawPointerBackground( pTarget, nTargetX, nTargetY, totalWide, pTarget->m_tall, true );
}
else
{
// Offscreen we need to point the pointer toward out offscreen target
DrawPointerBackground( pTarget, nTargetX, nTargetY, totalWide, pTarget->m_tall, true );
}
if( pTarget->m_bOnscreen && pTarget->m_pIcon_onscreen )
{
if ( !pTarget->m_bDrawControllerButton )
{
pTarget->m_pIcon_onscreen->DrawSelf( pTarget->GetIconX(), pTarget->GetIconY(), iWide, pTarget->m_tall, Color( 255, 255, 255, alpha ) );
}
}
else if ( pTarget->m_pIcon_offscreen )
{
if ( !pTarget->m_bDrawControllerButtonOffscreen )
{
pTarget->m_pIcon_offscreen->DrawSelf( pTarget->GetIconX(), pTarget->GetIconY(), iWide, pTarget->m_tall, Color( 255, 255, 255, alpha ) );
}
}
if( !pTarget->m_bOnscreen )
{
if ( pTarget->DrawBindingNameOffscreen() )
{
DrawBindingName( pTarget, pTarget->DrawBindingName(), pTarget->GetIconX() + (iWide>>1), pTarget->GetIconY() + (pTarget->m_tall>>1), pTarget->m_bDrawControllerButtonOffscreen );
}
if ( locator_background_style.GetInt() == 0 )
{
// Draw the arrow.
DrawIndicatorArrow( pTarget->GetIconX(), pTarget->GetIconY(), iWide, pTarget->m_tall, 0, pTarget->m_drawArrowDirection );
}
}
else if( bShouldDrawCaption )
{
if( bDrawCaption )
{
//ScreenWidth() * * pTarget->m_widthScale_onscreen
DrawTargetCaption( pTarget, pTarget->GetIconCenterX() + ICON_GAP + pTarget->GetIconWidth() * 0.5, pTarget->GetIconCenterY(), bDrawMultilineCaption );
}
if ( pTarget->DrawBindingName() )
{
DrawBindingName( pTarget, pTarget->DrawBindingName(), pTarget->GetIconX() + (iWide>>1), pTarget->GetIconY() + (pTarget->m_tall>>1), pTarget->m_bDrawControllerButton );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Some targets have text captions. Draw the text.
//-----------------------------------------------------------------------------
void CLocatorPanel::DrawTargetCaption( CLocatorTarget *pTarget, int x, int y, bool bDrawMultiline )
{
// Draw the caption
vgui::surface()->DrawSetTextFont( m_hCaptionFont );
int fontTall = vgui::surface()->GetFontTall( m_hCaptionFont );
int iCaptionWidth = GetScreenWidthForCaption( pTarget->GetCaptionText(), m_hCaptionFont );
if ( bDrawMultiline )
{
iCaptionWidth *= locator_split_len.GetFloat();
}
if ( locator_text_glow.GetBool() )
{
vgui::surface()->DrawSetTextFont( m_hCaptionGlowFont );
Color glowColor = locator_text_glow_color.GetColor();
vgui::surface()->DrawSetTextColor( glowColor.r(), glowColor.g(), glowColor.b(), ( glowColor.a() / 255.0f ) * pTarget->m_alpha );
vgui::surface()->DrawSetTextPos( x - 1, y - (fontTall >>1) - 1 );
vgui::surface()->DrawUnicodeString( pTarget->GetCaptionText() );
vgui::surface()->DrawSetTextFont( m_hCaptionFont );
}
// Only draw drop shadow on PC because it looks crappy on a TV
if ( !IsConsole() && locator_text_drop_shadow.GetBool() )
{
// Draw black text (drop shadow)
vgui::surface()->DrawSetTextColor( 0,0,0, pTarget->m_alpha );
vgui::surface()->DrawSetTextPos( x, y - (fontTall >>1) );
vgui::surface()->DrawUnicodeString( pTarget->GetCaptionText() );
}
// Draw text
vgui::surface()->DrawSetTextColor( pTarget->m_captionColor.r(),pTarget->m_captionColor.g(),pTarget->m_captionColor.b(), pTarget->m_alpha );
if ( !bDrawMultiline )
{
vgui::surface()->DrawSetTextPos( x - 1, y - (fontTall >>1) - 1 );
vgui::surface()->DrawUnicodeString( pTarget->GetCaptionText() );
}
else
{
int charX = x-1;
int charY = y - ( fontTall >> 1 ) - 1;
int iWidth = 0;
const wchar_t *pString = pTarget->GetCaptionText();
int len = Q_wcslen( pString );
for ( int iChar = 0; iChar < len; ++ iChar )
{
int charW = vgui::surface()->GetCharacterWidth( m_hCaptionFont, pString[ iChar ] );
iWidth += charW;
if ( iWidth > pTarget->m_captionWide && pString[iChar] == L' ' )
{
charY += fontTall;
charX = x-1;
iWidth = 0;
}
vgui::surface()->DrawSetTextPos( charX, charY );
vgui::surface()->DrawUnicodeChar( pString[iChar] );
charX += charW;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Figure out how wide (pixels) a string will be if rendered with this font
//
//-----------------------------------------------------------------------------
int CLocatorPanel::GetScreenWidthForCaption( const wchar_t *pString, vgui::HFont hFont )
{
int iWidth = 0;
for ( int iChar = 0; iChar < Q_wcslen( pString ); ++ iChar )
{
iWidth += vgui::surface()->GetCharacterWidth( hFont, pString[ iChar ] );
}
return iWidth;
}
//-----------------------------------------------------------------------------
// Purpose: Some targets' captions contain information about key bindings that
// should be displayed to the player. Do so.
//-----------------------------------------------------------------------------
void CLocatorPanel::DrawBindingName( CLocatorTarget *pTarget, const char *pchBindingName, int x, int y, bool bController )
{
if ( !bController && !IsConsole() )
{
// Draw the caption
vgui::surface()->DrawSetTextFont( m_hKeysFont );
int fontTall = vgui::surface()->GetFontTall( m_hKeysFont );
char szBinding[ 256 ];
Q_strcpy( szBinding, pchBindingName ? pchBindingName : "" );
if ( Q_strcmp( szBinding, "SEMICOLON" ) == 0 )
{
Q_strcpy( szBinding, ";" );
}
else if ( Q_strlen( szBinding ) == 1 && szBinding[ 0 ] >= 'a' && szBinding[ 0 ] <= 'z' )
{
// Make single letters uppercase
szBinding[ 0 ] += ( 'A' - 'a' );
}
wchar_t wszCaption[ 64 ];
g_pVGuiLocalize->ConstructString( wszCaption, sizeof(wchar_t)*64, szBinding, NULL );
int iWidth = GetScreenWidthForCaption( wszCaption, m_hKeysFont );
// Draw black text
vgui::surface()->DrawSetTextColor( 0,0,0, pTarget->m_alpha );
vgui::surface()->DrawSetTextPos( x - (iWidth>>1) - 1, y - (fontTall >>1) - 1 );
vgui::surface()->DrawUnicodeString( wszCaption );
}
else
{
// Draw the caption
wchar_t wszCaption[ 64 ];
vgui::surface()->DrawSetTextFont( BUTTON_FONT_HANDLE );
int fontTall = vgui::surface()->GetFontTall( BUTTON_FONT_HANDLE );
char szBinding[ 256 ];
// Turn localized string into icon character
Q_snprintf( szBinding, sizeof( szBinding ), "#GameUI_Icons_%s", pchBindingName );
g_pVGuiLocalize->ConstructString( wszCaption, sizeof( wszCaption ), g_pVGuiLocalize->Find( szBinding ), 0 );
g_pVGuiLocalize->ConvertUnicodeToANSI( wszCaption, szBinding, sizeof( szBinding ) );
int iWidth = GetScreenWidthForCaption( wszCaption, BUTTON_FONT_HANDLE );
int iLargeIconShift = MAX( 0, iWidth - ( ScreenWidth() * ICON_SIZE + ICON_GAP + ICON_GAP ) );
// Draw the button
vgui::surface()->DrawSetTextColor( 255,255,255, pTarget->m_alpha );
vgui::surface()->DrawSetTextPos( x - (iWidth>>1) - iLargeIconShift, y - (fontTall >>1) );
vgui::surface()->DrawUnicodeString( wszCaption );
}
}
//-----------------------------------------------------------------------------
// Purpose: Draw an arrow to indicate that a target is offscreen
//
// iconWide is sent to this function so that the arrow knows how to straddle
// the icon that it is being drawn near.
//-----------------------------------------------------------------------------
void CLocatorPanel::DrawIndicatorArrow( int x, int y, int iconWide, int iconTall, int textWidth, int direction )
{
int wide = iconWide;
int tall = iconTall;
//tall = wide = ScreenWidth() * ICON_SIZE;
switch( direction )
{
case DRAW_ARROW_LEFT:
vgui::surface()->DrawSetTexture( m_textureID_ArrowLeft );
x -= wide;
y += iconTall / 2 - tall / 2;
vgui::surface()->DrawTexturedRect( x, y, x + wide, y + tall );
break;
case DRAW_ARROW_RIGHT:
vgui::surface()->DrawSetTexture( m_textureID_ArrowRight );
x += iconWide + textWidth;
y += iconTall / 2 - tall / 2;
vgui::surface()->DrawTexturedRect( x, y, x + wide, y + tall );
break;
case DRAW_ARROW_UP:
vgui::surface()->DrawSetTexture( m_textureID_ArrowUp );
x += iconWide / 2 - wide / 2;
y -= tall;
vgui::surface()->DrawTexturedRect( x, y, x + wide, y + tall );
break;
case DRAW_ARROW_DOWN:
vgui::surface()->DrawSetTexture( m_textureID_ArrowDown );
x += iconWide / 2 - wide / 2;
y += iconTall;
vgui::surface()->DrawTexturedRect( x, y, x + wide, y + tall );
break;
default:
// Do not draw.
break;
}
}
//-----------------------------------------------------------------------------
// Purpose: Draws a very simple arrow that points down.
//-----------------------------------------------------------------------------
void CLocatorPanel::DrawSimpleArrow( int x, int y, int iconWide, int iconTall )
{
vgui::surface()->DrawSetTexture( m_textureID_SimpleArrow );
y += iconTall;
vgui::surface()->DrawTexturedRect( x, y, x + iconWide, y + iconTall );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CLocatorPanel::GetIconPositionForOffscreenTarget( const Vector &vecDelta, float flDist, int *pXPos, int *pYPos )
{
float xpos, ypos;
float flRotation;
float flRadius = YRES(OFFSCREEN_ICON_POSITION_RADIUS);
if ( locator_topdown_style.GetBool() )
{
flRadius *= clamp( flDist / 600.0f, 1.75f, 3.0f );
}
GetTargetPosition( vecDelta, flRadius, &xpos, &ypos, &flRotation );
*pXPos = xpos;
*pYPos = ypos;
}
//-----------------------------------------------------------------------------
// Purpose: Given a handle, return the pointer to the proper locator target.
//-----------------------------------------------------------------------------
CLocatorTarget *CLocatorPanel::GetPointerForHandle( int hTarget )
{
for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ )
{
if( m_targets[ i ].m_isActive && m_targets[ i ].m_serialNumber == hTarget )
{
return &m_targets[ i ];
}
}
return NULL;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CLocatorPanel::AddTarget()
{
for( int i = 0 ; i < MAX_LOCATOR_TARGETS ; i++ )
{
if( !m_targets[ i ].m_isActive )
{
m_targets[ i ].Activate( m_serializer );
m_serializer++;
return m_targets[ i ].m_serialNumber;
}
}
DevWarning( "Locator Panel has no free targets!\n" );
return -1;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CLocatorPanel::RemoveTarget( int hTarget )
{
CLocatorTarget *pTarget = GetPointerForHandle( hTarget );
if( pTarget )
{
pTarget->Deactivate();
}
}