Agregando el Game Instructor
El Game Instructor en breves palabras es un sistema ubicado en el Cliente enfocado para mostrar instrucciones sobre como jugar o realizar ciertas acciones mientras al mismo tiempo se juega. El mismo apareció por primera vez en el Branch de Alien Swarm (o hasta donde imagino, puedo estar equivocado) y actualmente es usado por los juegos de Valve .
En este articulo veremos a como incorporarlo dentro del Source SDK 2013 Multiplayer ( No he probado si funciona dentro del Branch para Singleplayer o en Source SDK 2007 ). El código que usaremos es proveniente del código fuente de Alien Swarm con algunas modificaciones para hacerlo funcionar dentro del Source SDK 2013.
Contents
Requisitos
- Un Mod basado en el Source SDK 2013
- Conocimientos en C++
CLocatorTarget
La clase CLocatorTarget de los archivos hud_locator_target.cpp/hud_locator_target.h es la encargada de mostrar los mensajes en la pantalla del jugador ( Cliente ) y ( en caso de dirigirse a un objeto, por ejemplo un botón ) de mover el mismo hacia la ubicación del objeto a usar ( De ahí proviene su nombre ). Así que para empezar necesitaremos incorporar los siguientes 2 archivos dentro del proyecto del Cliente:
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();
}
}
UtlSymbol
Ahora es necesario editar el archivo utlsymbol.h que se encuentra en public/tier1/utlsymbol.h, una vez que hayamos abierto el archivo eliminamos TODO su contenido y pegamos el nuevo código:
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Defines a symbol table
//
// $Header: $
// $NoKeywords: $
//===========================================================================//
#ifndef UTLSYMBOL_H
#define UTLSYMBOL_H
#ifdef _WIN32
#pragma once
#endif
#include "tier0/threadtools.h"
#include "tier1/utlrbtree.h"
#include "tier1/utlvector.h"
#include "tier1/utlbuffer.h"
#include "tier1/utllinkedlist.h"
#include "tier1/stringpool.h"
//-----------------------------------------------------------------------------
// forward declarations
//-----------------------------------------------------------------------------
class CUtlSymbolTable;
class CUtlSymbolTableMT;
//-----------------------------------------------------------------------------
// This is a symbol, which is a easier way of dealing with strings.
//-----------------------------------------------------------------------------
typedef unsigned short UtlSymId_t;
#define UTL_INVAL_SYMBOL ((UtlSymId_t)~0)
class CUtlSymbol
{
public:
// constructor, destructor
CUtlSymbol() : m_Id(UTL_INVAL_SYMBOL) {}
CUtlSymbol( UtlSymId_t id ) : m_Id(id) {}
CUtlSymbol( const char* pStr );
CUtlSymbol( CUtlSymbol const& sym ) : m_Id(sym.m_Id) {}
// operator=
CUtlSymbol& operator=( CUtlSymbol const& src ) { m_Id = src.m_Id; return *this; }
// operator==
bool operator==( CUtlSymbol const& src ) const { return m_Id == src.m_Id; }
bool operator==( const char* pStr ) const;
// Is valid?
bool IsValid() const { return m_Id != UTL_INVAL_SYMBOL; }
// Gets at the symbol
operator UtlSymId_t const() const { return m_Id; }
// Gets the string associated with the symbol
const char* String( ) const;
// Modules can choose to disable the static symbol table so to prevent accidental use of them.
static void DisableStaticSymbolTable();
protected:
UtlSymId_t m_Id;
// Initializes the symbol table
static void Initialize();
// returns the current symbol table
static CUtlSymbolTableMT* CurrTable();
// The standard global symbol table
static CUtlSymbolTableMT* s_pSymbolTable;
static bool s_bAllowStaticSymbolTable;
friend class CCleanupUtlSymbolTable;
};
//-----------------------------------------------------------------------------
// CUtlSymbolTable:
// description:
// This class defines a symbol table, which allows us to perform mappings
// of strings to symbols and back. The symbol class itself contains
// a static version of this class for creating global strings, but this
// class can also be instanced to create local symbol tables.
//
// This class stores the strings in a series of string pools. The first
// two bytes of each string are decorated with a hash to speed up
// comparisons.
//-----------------------------------------------------------------------------
class CUtlSymbolTable
{
public:
// constructor, destructor
CUtlSymbolTable( int growSize = 0, int initSize = 16, bool caseInsensitive = false );
~CUtlSymbolTable();
// Finds and/or creates a symbol based on the string
CUtlSymbol AddString( const char* pString );
// Finds the symbol for pString
CUtlSymbol Find( const char* pString ) const;
// Look up the string associated with a particular symbol
const char* String( CUtlSymbol id ) const;
// Remove all symbols in the table.
void RemoveAll();
int GetNumStrings( void ) const
{
return m_Lookup.Count();
}
// We store one of these at the beginning of every string to speed
// up comparisons.
typedef unsigned short hashDecoration_t;
protected:
class CStringPoolIndex
{
public:
inline CStringPoolIndex()
{
}
inline CStringPoolIndex( unsigned short iPool, unsigned short iOffset )
: m_iPool(iPool), m_iOffset(iOffset)
{}
inline bool operator==( const CStringPoolIndex &other ) const
{
return m_iPool == other.m_iPool && m_iOffset == other.m_iOffset;
}
unsigned short m_iPool; // Index into m_StringPools.
unsigned short m_iOffset; // Index into the string pool.
};
class CLess
{
public:
CLess( int ignored = 0 ) {} // permits default initialization to NULL in CUtlRBTree
bool operator!() const { return false; }
bool operator()( const CStringPoolIndex &left, const CStringPoolIndex &right ) const;
};
// Stores the symbol lookup
class CTree : public CUtlRBTree<CStringPoolIndex, unsigned short, CLess>
{
public:
CTree( int growSize, int initSize ) : CUtlRBTree<CStringPoolIndex, unsigned short, CLess>( growSize, initSize ) {}
friend class CUtlSymbolTable::CLess; // Needed to allow CLess to calculate pointer to symbol table
};
struct StringPool_t
{
int m_TotalLen; // How large is
int m_SpaceUsed;
char m_Data[1];
};
CTree m_Lookup;
bool m_bInsensitive;
mutable unsigned short m_nUserSearchStringHash;
mutable const char* m_pUserSearchString;
// stores the string data
CUtlVector<StringPool_t*> m_StringPools;
private:
int FindPoolWithSpace( int len ) const;
const char* StringFromIndex( const CStringPoolIndex &index ) const;
const char* DecoratedStringFromIndex( const CStringPoolIndex &index ) const;
friend class CLess;
friend class CSymbolHash;
};
class CUtlSymbolTableMT : public CUtlSymbolTable
{
public:
CUtlSymbolTableMT( int growSize = 0, int initSize = 32, bool caseInsensitive = false )
: CUtlSymbolTable( growSize, initSize, caseInsensitive )
{
}
CUtlSymbol AddString( const char* pString )
{
m_lock.LockForWrite();
CUtlSymbol result = CUtlSymbolTable::AddString( pString );
m_lock.UnlockWrite();
return result;
}
CUtlSymbol Find( const char* pString ) const
{
m_lock.LockForWrite();
CUtlSymbol result = CUtlSymbolTable::Find( pString );
m_lock.UnlockWrite();
return result;
}
const char* String( CUtlSymbol id ) const
{
m_lock.LockForRead();
const char *pszResult = CUtlSymbolTable::String( id );
m_lock.UnlockRead();
return pszResult;
}
private:
mutable CThreadSpinRWLock m_lock;
};
//-----------------------------------------------------------------------------
// CUtlFilenameSymbolTable:
// description:
// This class defines a symbol table of individual filenames, stored more
// efficiently than a standard symbol table. Internally filenames are broken
// up into file and path entries, and a file handle class allows convenient
// access to these.
//-----------------------------------------------------------------------------
// The handle is a CUtlSymbol for the dirname and the same for the filename, the accessor
// copies them into a static char buffer for return.
typedef void* FileNameHandle_t;
// Symbol table for more efficiently storing filenames by breaking paths and filenames apart.
// Refactored from BaseFileSystem.h
class CUtlFilenameSymbolTable
{
// Internal representation of a FileHandle_t
// If we get more than 64K filenames, we'll have to revisit...
// Right now CUtlSymbol is a short, so this packs into an int/void * pointer size...
struct FileNameHandleInternal_t
{
FileNameHandleInternal_t()
{
path = 0;
file = 0;
}
// Part before the final '/' character
unsigned short path;
// Part after the final '/', including extension
unsigned short file;
};
public:
FileNameHandle_t FindOrAddFileName( const char *pFileName );
FileNameHandle_t FindFileName( const char *pFileName );
int PathIndex(const FileNameHandle_t &handle) { return (( const FileNameHandleInternal_t * )&handle)->path; }
bool String( const FileNameHandle_t& handle, char *buf, int buflen );
void RemoveAll();
void SpewStrings();
bool SaveToBuffer( CUtlBuffer &buffer );
bool RestoreFromBuffer( CUtlBuffer &buffer );
private:
CCountedStringPool m_StringPool;
mutable CThreadSpinRWLock m_lock;
};
// This creates a simple class that includes the underlying CUtlSymbol
// as a private member and then instances a private symbol table to
// manage those symbols. Avoids the possibility of the code polluting the
// 'global'/default symbol table, while letting the code look like
// it's just using = and .String() to look at CUtlSymbol type objects
//
// NOTE: You can't pass these objects between .dlls in an interface (also true of CUtlSymbol of course)
//
#define DECLARE_PRIVATE_SYMBOLTYPE( typename ) \
class typename \
{ \
public: \
typename(); \
typename( const char* pStr ); \
typename& operator=( typename const& src ); \
bool operator==( typename const& src ) const; \
const char* String( ) const; \
private: \
CUtlSymbol m_SymbolId; \
};
// Put this in the .cpp file that uses the above typename
#define IMPLEMENT_PRIVATE_SYMBOLTYPE( typename ) \
static CUtlSymbolTable g_##typename##SymbolTable; \
typename::typename() \
{ \
m_SymbolId = UTL_INVAL_SYMBOL; \
} \
typename::typename( const char* pStr ) \
{ \
m_SymbolId = g_##typename##SymbolTable.AddString( pStr ); \
} \
typename& typename::operator=( typename const& src ) \
{ \
m_SymbolId = src.m_SymbolId; \
return *this; \
} \
bool typename::operator==( typename const& src ) const \
{ \
return ( m_SymbolId == src.m_SymbolId ); \
} \
const char* typename::String( ) const \
{ \
return g_##typename##SymbolTable.String( m_SymbolId ); \
}
#endif // UTLSYMBOL_H
Para ser honesto no entiendo mucho acerca de este archivo y para que sirven exactamente las actualizaciones hechas al mismo, sin embargo son necesarios para hud_locator_target.
C_GameInstructor y CBaseLesson
Ahora pasaremos al sistema real del Game Instructor, este se encarga de leer 2 archivos ( scripts/instructor_lessons.txt y scripts/mod_lessons.txt ) que son los responsables de tener las "lecciones por aprender" que básicamente son los eventos que el Game Instructor puede mostrar en la pantalla junto a las opciones que van desde el icono que aparecerá junto al mensaje hasta cuando se marcará como "lección aprendida" o "lección vista".
Se puede decir que es como un tipo de lenguaje que nos permite crear lecciones personalizadas que pueden "aprenderse" o sencillamente cerrarse cuando cierta condición se cumple dentro del código, algo complicado de entender al principio pero veremos una introducción de como crearlos más adelante, por ahora pasemos al código de este sistema:
CBaseLesson
En resumen es la clase encargada de contener toda la información acerca de cada lección que puede aparecer con el Game Instructor. Tendremos que agregar estos 2 archivos igual en el Cliente:
c_baselesson.h
//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============//
//
// Purpose: Client handler for instruction players how to play
//
//=============================================================================//
#ifndef _C_BASELESSON_H_
#define _C_BASELESSON_H_
#include "GameEventListener.h"
#include "hud_locator_target.h"
#define DECLARE_LESSON( _lessonClassName, _baseLessonClassName ) \
typedef _baseLessonClassName BaseClass;\
typedef _lessonClassName ThisClass;\
_lessonClassName( const char *pchName, bool bIsDefaultHolder, bool bIsOpenOpportunity )\
: _baseLessonClassName( pchName, bIsDefaultHolder, bIsOpenOpportunity )\
{\
Init();\
}
enum LessonInstanceType
{
LESSON_INSTANCE_MULTIPLE,
LESSON_INSTANCE_SINGLE_OPEN,
LESSON_INSTANCE_FIXED_REPLACE,
LESSON_INSTANCE_SINGLE_ACTIVE,
LESSON_INSTANCE_TYPE_TOTAL
};
// This is used to solve a problem where bots can take the place of a player, where on or the other don't have valid entities on the client at the same time
#define MAX_DELAYED_PLAYER_SWAPS 8
struct delayed_player_swap_t
{
CHandle<C_BaseEntity> *phHandleToChange;
int iNewUserID;
delayed_player_swap_t( void )
{
phHandleToChange = NULL;
iNewUserID = -1;
}
};
abstract_class CBaseLesson : public CGameEventListener
{
public:
CBaseLesson( const char *pchName, bool bIsDefaultHolder, bool bIsOpenOpportunity );
virtual ~CBaseLesson( void );
void AddPrerequisite( const char *pchLessonName );
const CGameInstructorSymbol& GetNameSymbol( void ) const { return m_stringName; }
const char * GetName( void ) const { return m_stringName.String(); }
int GetPriority( void ) const { return m_iPriority; }
const char * GetCloseReason( void ) const { return m_stringCloseReason.String(); }
void SetCloseReason( const char *pchReason ) { m_stringCloseReason = pchReason; }
CBaseLesson* GetRoot( void ) const { return m_pRoot; }
void SetRoot( CBaseLesson *pRoot );
const CUtlVector < CBaseLesson * >* GetChildren( void ) const { return &m_OpenOpportunities; }
float GetInitTime( void ) { return m_fInitTime; }
void SetStartTime( void ) { m_fStartTime = gpGlobals->curtime; }
void ResetStartTime( void ) { m_fStartTime = 0.0f; m_bHasPlayedSound = false; }
bool ShouldShowSpew( void );
bool NoPriority( void ) const;
bool IsDefaultHolder( void ) const { return m_bIsDefaultHolder; }
bool IsOpenOpportunity( void ) const { return m_bIsOpenOpportunity; }
bool IsLocked( void ) const;
bool CanOpenWhenDead( void ) const { return m_bCanOpenWhenDead; }
bool IsInstructing( void ) const { return ( m_fStartTime > 0.0f ); }
bool IsLearned( void ) const;
bool PrerequisitesHaveBeenMet( void ) const;
bool IsTimedOut( void );
int InstanceType( void ) const { return m_iInstanceType; }
const CGameInstructorSymbol& GetReplaceKeySymbol( void ) const { return m_stringReplaceKey; }
const char* GetReplaceKey( void ) const { return m_stringReplaceKey.String(); }
int GetFixedInstancesMax( void ) const { return m_iFixedInstancesMax; }
bool ShouldReplaceOnlyWhenStopped( void ) const { return m_bReplaceOnlyWhenStopped; }
void SetInstanceActive( bool bInstanceActive ) { m_bInstanceActive = bInstanceActive; }
bool IsInstanceActive( void ) const { return m_bInstanceActive; }
void ResetDisplaysAndSuccesses( void );
bool IncDisplayCount( void );
bool IncSuccessCount( void );
void SetDisplayCount( int iDisplayCount ) { m_iDisplayCount = iDisplayCount; }
void SetSuccessCount( int iSuccessCount ) { m_iSuccessCount = iSuccessCount; }
int GetDisplayCount( void ) const { return m_iDisplayCount; }
int GetSuccessCount( void ) const { return m_iSuccessCount; }
int GetDisplayLimit( void ) const { return m_iDisplayLimit; }
int GetSuccessLimit( void ) const { return m_iSuccessLimit; }
void Init( void ); // NOT virtual, each constructor calls their own
virtual void InitPrerequisites( void ) {};
virtual void Start( void ) = 0;
virtual void Stop( void ) = 0;
virtual void OnOpen( void ) {};
virtual void Update( void ) {};
virtual void UpdateInactive( void ) {};
virtual bool ShouldDisplay( void ) const { return true; }
virtual bool IsVisible( void ) const { return true; }
virtual bool WasDisplayed( void ) const { return m_bWasDisplayed ? true : false; }
virtual void SwapOutPlayers( int iOldUserID, int iNewUserID ) {}
virtual void TakePlaceOf( CBaseLesson *pLesson );
const char *GetGroup() { return m_szLessonGroup.String(); }
void SetEnabled( bool bEnabled ) { m_bDisabled = !bEnabled; }
protected:
void MarkSucceeded( void );
void CloseOpportunity( const char *pchReason );
bool DoDelayedPlayerSwaps( void ) const;
private:
CBaseLesson *m_pRoot;
CUtlVector < CBaseLesson * > m_OpenOpportunities;
CUtlVector < const CBaseLesson * > m_Prerequisites;
CGameInstructorSymbol m_stringCloseReason;
CGameInstructorSymbol m_stringName;
bool m_bInstanceActive : 1;
bool m_bSuccessCounted : 1;
bool m_bIsDefaultHolder : 1;
bool m_bIsOpenOpportunity : 1;
protected:
LessonInstanceType m_iInstanceType;
int m_iPriority;
CGameInstructorSymbol m_stringReplaceKey;
int m_iFixedInstancesMax;
bool m_bReplaceOnlyWhenStopped;
int m_iTeam;
bool m_bOnlyKeyboard;
bool m_bOnlyGamepad;
int m_iDisplayLimit;
int m_iDisplayCount;
bool m_bWasDisplayed;
int m_iSuccessLimit;
int m_iSuccessCount;
float m_fLockDuration;
float m_fTimeout;
float m_fInitTime;
float m_fStartTime;
float m_fLockTime;
float m_fUpdateInterval;
bool m_bHasPlayedSound;
CGameInstructorSymbol m_szStartSound;
CGameInstructorSymbol m_szLessonGroup;
bool m_bCanOpenWhenDead;
bool m_bBumpWithTimeoutWhenLearned;
bool m_bCanTimeoutWhileInactive;
bool m_bDisabled;
// Right now we can only queue up 4 swaps...
// this number can be increased if more entity handle scripted variables are added
mutable delayed_player_swap_t m_pDelayedPlayerSwap[ MAX_DELAYED_PLAYER_SWAPS ];
mutable int m_iNumDelayedPlayerSwaps;
public:
// Colors for console spew in verbose mode
static Color m_rgbaVerboseHeader;
static Color m_rgbaVerbosePlain;
static Color m_rgbaVerboseName;
static Color m_rgbaVerboseOpen;
static Color m_rgbaVerboseClose;
static Color m_rgbaVerboseSuccess;
static Color m_rgbaVerboseUpdate;
};
class CTextLesson : public CBaseLesson
{
public:
DECLARE_LESSON( CTextLesson, CBaseLesson );
void Init( void ); // NOT virtual, each constructor calls their own
virtual void Start( void );
virtual void Stop( void );
protected:
CGameInstructorSymbol m_szDisplayText;
CGameInstructorSymbol m_szDisplayParamText;
CGameInstructorSymbol m_szBinding;
CGameInstructorSymbol m_szGamepadBinding;
};
class CIconLesson : public CTextLesson
{
public:
DECLARE_LESSON( CIconLesson, CTextLesson );
void Init( void ); // NOT virtual, each constructor calls their own
virtual void Start( void );
virtual void Stop( void );
virtual void Update( void );
virtual void UpdateInactive( void );
virtual bool ShouldDisplay( void ) const;
virtual bool IsVisible( void ) const;
virtual void SwapOutPlayers( int iOldUserID, int iNewUserID );
virtual void TakePlaceOf( CBaseLesson *pLesson );
void SetLocatorBinding( CLocatorTarget * pLocatorTarget );
const char *GetCaptionColorString() { return m_szCaptionColor.String(); }
bool IsPresentComplete( void );
void PresentStart( void );
void PresentEnd( void );
private:
virtual void UpdateLocatorTarget( CLocatorTarget *pLocatorTarget, C_BaseEntity *pIconTarget );
protected:
CHandle<C_BaseEntity> m_hIconTarget;
CGameInstructorSymbol m_szVguiTargetName;
CGameInstructorSymbol m_szVguiTargetLookup;
int m_nVguiTargetEdge;
float m_flUpOffset;
float m_flRelativeUpOffset;
float m_fFixedPositionX;
float m_fFixedPositionY;
int m_hLocatorTarget;
int m_iFlags;
float m_fRange;
float m_fCurrentDistance;
float m_fOnScreenStartTime;
float m_fUpdateDistanceTime;
CGameInstructorSymbol m_szOnscreenIcon;
CGameInstructorSymbol m_szOffscreenIcon;
CGameInstructorSymbol m_szCaptionColor;
bool m_bFixedPosition;
bool m_bNoIconTarget;
bool m_bAllowNodrawTarget;
bool m_bVisible;
bool m_bShowWhenOccluded;
bool m_bNoOffscreen;
bool m_bForceCaption;
};
enum LessonAction
{
LESSON_ACTION_NONE,
LESSON_ACTION_SCOPE_IN,
LESSON_ACTION_SCOPE_OUT,
LESSON_ACTION_CLOSE,
LESSON_ACTION_SUCCESS,
LESSON_ACTION_LOCK,
LESSON_ACTION_PRESENT_COMPLETE,
LESSON_ACTION_PRESENT_START,
LESSON_ACTION_PRESENT_END,
LESSON_ACTION_REFERENCE_OPEN,
LESSON_ACTION_SET,
LESSON_ACTION_ADD,
LESSON_ACTION_SUBTRACT,
LESSON_ACTION_MULTIPLY,
LESSON_ACTION_IS,
LESSON_ACTION_LESS_THAN,
LESSON_ACTION_HAS_PREFIX,
LESSON_ACTION_HAS_BIT,
LESSON_ACTION_BIT_COUNT_IS,
LESSON_ACTION_BIT_COUNT_LESS_THAN,
LESSON_ACTION_GET_DISTANCE,
LESSON_ACTION_GET_ANGULAR_DISTANCE,
LESSON_ACTION_GET_PLAYER_DISPLAY_NAME,
LESSON_ACTION_CLASSNAME_IS,
LESSON_ACTION_MODELNAME_IS,
LESSON_ACTION_TEAM_IS,
LESSON_ACTION_HEALTH_LESS_THAN,
LESSON_ACTION_HEALTH_PERCENTAGE_LESS_THAN,
LESSON_ACTION_GET_ACTIVE_WEAPON,
LESSON_ACTION_WEAPON_IS,
LESSON_ACTION_WEAPON_HAS,
LESSON_ACTION_GET_ACTIVE_WEAPON_SLOT,
LESSON_ACTION_GET_WEAPON_SLOT,
LESSON_ACTION_GET_WEAPON_IN_SLOT,
LESSON_ACTION_CLIP_PERCENTAGE_LESS_THAN,
LESSON_ACTION_WEAPON_AMMO_LOW,
LESSON_ACTION_WEAPON_AMMO_FULL,
LESSON_ACTION_WEAPON_AMMO_EMPTY,
LESSON_ACTION_WEAPON_CAN_USE,
LESSON_ACTION_USE_TARGET_IS,
LESSON_ACTION_GET_USE_TARGET,
LESSON_ACTION_GET_POTENTIAL_USE_TARGET,
// Enum continued in Mod_LessonAction
LESSON_ACTION_MOD_START,
};
struct LessonElement_t
{
int iVariable;
int iParamVarIndex;
int iAction;
_fieldtypes paramType;
CGameInstructorSymbol szParam;
bool bNot : 1;
bool bOptionalParam : 1;
LessonElement_t( int p_iVariable, int p_iAction, bool p_bNot, bool p_bOptionalParam, const char *pchParam, int p_iParamVarIndex, _fieldtypes p_paramType )
{
iVariable = p_iVariable;
iAction = p_iAction;
bNot = p_bNot;
bOptionalParam = p_bOptionalParam;
szParam = pchParam;
iParamVarIndex = p_iParamVarIndex;
paramType = p_paramType;
}
LessonElement_t( const LessonElement_t &p_LessonElement )
{
iVariable = p_LessonElement.iVariable;
iAction = p_LessonElement.iAction;
bNot = p_LessonElement.bNot;
bOptionalParam = p_LessonElement.bOptionalParam;
szParam = p_LessonElement.szParam;
iParamVarIndex = p_LessonElement.iParamVarIndex;
paramType = p_LessonElement.paramType;
}
};
struct LessonEvent_t
{
CUtlVector< LessonElement_t > elements;
CGameInstructorSymbol szEventName;
};
class CScriptedIconLesson : public CIconLesson
{
public:
DECLARE_LESSON( CScriptedIconLesson, CIconLesson )
virtual ~CScriptedIconLesson( void );
static void PreReadLessonsFromFile( void );
static void Mod_PreReadLessonsFromFile( void );
void Init( void ); // NOT virtual, each constructor calls their own
virtual void InitPrerequisites( void );
virtual void OnOpen( void );
virtual void Update( void );
virtual void SwapOutPlayers( int iOldUserID, int iNewUserID );
virtual void FireGameEvent( IGameEvent *event );
virtual void ProcessOpenGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event );
virtual void ProcessCloseGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event );
virtual void ProcessSuccessGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event );
CUtlVector< LessonEvent_t >& GetOpenEvents( void ) { return m_OpenEvents; }
CUtlVector< LessonEvent_t >& GetCloseEvents( void ) { return m_CloseEvents; }
CUtlVector< LessonEvent_t >& GetSuccessEvents( void ) { return m_SuccessEvents; }
CUtlVector< LessonEvent_t >& GetOnOpenEvents( void ) { return m_OnOpenEvents; }
CUtlVector< LessonEvent_t >& GetUpdateEvents( void ) { return m_UpdateEvents; }
bool ProcessElements( IGameEvent *event, const CUtlVector< LessonElement_t > *pElements );
private:
void InitElementsFromKeys( CUtlVector< LessonElement_t > *pLessonElements, KeyValues *pKey );
void InitElementsFromElements( CUtlVector< LessonElement_t > *pLessonElements, const CUtlVector< LessonElement_t > *pLessonElements2 );
void InitFromKeys( KeyValues *pKey );
bool ProcessElement( IGameEvent *event, const LessonElement_t *pLessonElement, bool bInFailedScope );
bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, float &bVar, const CGameInstructorSymbol *pchParamName, float fParam );
bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, int &bVar, const CGameInstructorSymbol *pchParamName, float fParam );
bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, bool &bVar, const CGameInstructorSymbol *pchParamName, float fParam );
bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, EHANDLE &hVar, const CGameInstructorSymbol *pchParamName, float fParam, C_BaseEntity *pParam, const char *pchParam );
bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, CGameInstructorSymbol *pchVar, const CGameInstructorSymbol *pchParamName, const char *pchParam );
// Implemented per mod so they can have custom actions
bool Mod_ProcessElementAction( int iAction, bool bNot, const char *pchVarName, EHANDLE &hVar, const CGameInstructorSymbol *pchParamName, float fParam, C_BaseEntity *pParam, const char *pchParam, bool &bModHandled );
LessonEvent_t * AddOpenEvent( void );
LessonEvent_t * AddCloseEvent( void );
LessonEvent_t * AddSuccessEvent( void );
LessonEvent_t * AddOnOpenEvent( void );
LessonEvent_t * AddUpdateEvent( void );
private:
static CUtlDict< int, int > CScriptedIconLesson::LessonActionMap;
EHANDLE m_hLocalPlayer;
float m_fOutput;
CHandle<C_BaseEntity> m_hEntity1;
CHandle<C_BaseEntity> m_hEntity2;
CGameInstructorSymbol m_szString1;
CGameInstructorSymbol m_szString2;
int m_iInteger1;
int m_iInteger2;
float m_fFloat1;
float m_fFloat2;
CUtlVector< CGameInstructorSymbol > m_PrerequisiteNames;
CUtlVector< LessonEvent_t > m_OpenEvents;
CUtlVector< LessonEvent_t > m_CloseEvents;
CUtlVector< LessonEvent_t > m_SuccessEvents;
CUtlVector< LessonEvent_t > m_OnOpenEvents;
CUtlVector< LessonEvent_t > m_UpdateEvents;
float m_fUpdateEventTime;
CScriptedIconLesson *m_pDefaultHolder;
int m_iScopeDepth;
// Need this to get offsets to scripted variables
friend class LessonVariableInfo;
friend int LessonActionFromString( const char *pchName );
};
#endif // _C_BASELESSON_H_
c_baselesson.cpp
//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============//
//
// Purpose: Client handler implementations for instruction players how to play
//
//=============================================================================//
#include "cbase.h"
#include "c_baselesson.h"
#include "c_gameinstructor.h"
#include "hud_locator_target.h"
#include "c_world.h"
#include "iinput.h"
#include "ammodef.h"
#include "vprof.h"
#include "view.h"
#include "vstdlib/ikeyvaluessystem.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//=========================================================
// Configuración
//=========================================================
#define LESSON_PRIORITY_MAX 1000
#define LESSON_PRIORITY_NONE 0
#define LESSON_MIN_TIME_ON_SCREEN_TO_MARK_DISPLAYED 1.5f
#define LESSON_MIN_TIME_BEFORE_LOCK_ALLOWED 0.1f
#define LESSON_DISTANCE_UPDATE_RATE 0.25f
// See comments in UtlSymbol on why this is useful and how it works
IMPLEMENT_PRIVATE_SYMBOLTYPE( CGameInstructorSymbol );
extern ConVar gameinstructor_verbose;
extern ConVar gameinstructor_verbose_lesson;
extern ConVar gameinstructor_find_errors;
//
// CGameInstructorLesson
//
Color CBaseLesson::m_rgbaVerboseHeader = Color( 255, 128, 64, 255 );
Color CBaseLesson::m_rgbaVerbosePlain = Color( 64, 128, 255, 255 );
Color CBaseLesson::m_rgbaVerboseName = Color( 255, 255, 255, 255 );
Color CBaseLesson::m_rgbaVerboseOpen = Color( 0, 255, 0, 255 );
Color CBaseLesson::m_rgbaVerboseClose = Color( 255, 0, 0, 255 );
Color CBaseLesson::m_rgbaVerboseSuccess = Color( 255, 255, 0, 255 );
Color CBaseLesson::m_rgbaVerboseUpdate = Color( 255, 0, 255, 255 );
//=========================================================
// Constructor
//=========================================================
CBaseLesson::CBaseLesson( const char *pchName, bool bIsDefaultHolder, bool bIsOpenOpportunity )
{
COMPILE_TIME_ASSERT( sizeof( CGameInstructorSymbol ) == sizeof( CUtlSymbol ) );
m_stringName = pchName;
m_stringReplaceKey = "";
m_bIsDefaultHolder = bIsDefaultHolder;
m_bIsOpenOpportunity = bIsOpenOpportunity;
Init();
}
//=========================================================
// Destructor
//=========================================================
CBaseLesson::~CBaseLesson()
{
// Remove from root's children list
if ( m_pRoot )
m_pRoot->m_OpenOpportunities.FindAndRemove(this);
else
{
for ( int i = 0; i < m_OpenOpportunities.Count(); ++i )
{
// Remove from children if they are still around
CBaseLesson *pLesson = m_OpenOpportunities[ i ];
pLesson->m_pRoot = NULL;
}
}
}
//=========================================================
//=========================================================
void CBaseLesson::AddPrerequisite( const char *pchLessonName )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "\t%s: ", GetName() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Adding prereq " );
ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", pchLessonName );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" );
}
const CBaseLesson *pPrerequisite = GetGameInstructor().GetLesson( pchLessonName );
if ( !pPrerequisite )
{
DevWarning( "Prerequisite %s added by lesson %s doesn't exist!\n", pchLessonName, GetName() );
return;
}
m_Prerequisites.AddToTail(pPrerequisite);
}
//=========================================================
//=========================================================
void CBaseLesson::SetRoot( CBaseLesson *pRoot )
{
m_pRoot = pRoot;
if ( m_pRoot->m_OpenOpportunities.Find( this ) == -1 )
m_pRoot->m_OpenOpportunities.AddToTail( this );
}
//=========================================================
//=========================================================
bool CBaseLesson::ShouldShowSpew()
{
// @DEBUG
return true;
if ( gameinstructor_verbose_lesson.GetString()[ 0 ] == '\0' )
return false;
return ( Q_stristr( GetName(), gameinstructor_verbose_lesson.GetString() ) != NULL );
}
//=========================================================
//=========================================================
bool CBaseLesson::NoPriority() const
{
return ( m_iPriority == LESSON_PRIORITY_NONE );
}
//=========================================================
//=========================================================
bool CBaseLesson::IsLocked() const
{
if ( m_fLockDuration == 0.0f )
return false;
if ( !IsInstructing() || !IsVisible() )
return false;
float fLockTime = m_fLockTime;
if ( fLockTime == 0.0f )
fLockTime = m_fStartTime;
return ( gpGlobals->curtime > m_fStartTime + LESSON_MIN_TIME_BEFORE_LOCK_ALLOWED && gpGlobals->curtime < fLockTime + m_fLockDuration );
}
//=========================================================
//=========================================================
bool CBaseLesson::IsLearned() const
{
if ( m_iDisplayLimit > 0 && m_iDisplayCount >= m_iDisplayLimit )
return true;
if ( m_iSuccessLimit > 0 && m_iSuccessCount >= m_iSuccessLimit )
return true;
return false;
}
//=========================================================
//=========================================================
bool CBaseLesson::PrerequisitesHaveBeenMet() const
{
for ( int i = 0; i < m_Prerequisites.Count(); ++i )
{
if ( !m_Prerequisites[ i ]->IsLearned() )
{
// Failed a prereq
return false;
}
}
// All prereqs passed
return true;
}
//=========================================================
//=========================================================
bool CBaseLesson::IsTimedOut()
{
VPROF_BUDGET( "CBaseLesson::IsTimedOut", "GameInstructor" );
// Check for no timeout
if ( m_fTimeout == 0.0f )
return false;
float fStartTime = m_fStartTime;
if ( GetRoot()->IsLearned() )
{
if ( !m_bBumpWithTimeoutWhenLearned )
{
// Time out instantly if we've learned this and don't want to keep it open for priority bumping
return true;
}
else
{
// It'll never be active, so lets use timeout based on when it was initialized
fStartTime = m_fInitTime;
}
}
if ( !fStartTime )
{
if ( !m_bCanTimeoutWhileInactive )
{
return false;
}
// Not active, so lets use timeout based on when it was initialized
fStartTime = m_fInitTime;
}
bool bTimedOut = ( fStartTime + m_fTimeout < gpGlobals->curtime );
if ( bTimedOut )
SetCloseReason( "Timed out." );
return bTimedOut;
}
//=========================================================
//=========================================================
void CBaseLesson::ResetDisplaysAndSuccesses()
{
m_iDisplayCount = 0;
m_bSuccessCounted = false;
m_iSuccessCount = 0;
}
//=========================================================
//=========================================================
bool CBaseLesson::IncDisplayCount()
{
if ( m_iDisplayCount < m_iDisplayLimit )
{
m_iDisplayCount++;
return true;
}
return false;
}
//=========================================================
//=========================================================
bool CBaseLesson::IncSuccessCount()
{
if ( m_iSuccessCount < m_iSuccessLimit )
{
m_iSuccessCount++;
return true;
}
return false;
}
//=========================================================
//=========================================================
void CBaseLesson::Init()
{
m_pRoot = NULL;
m_bSuccessCounted = false;
SetCloseReason( "None given." );
m_iPriority = LESSON_PRIORITY_MAX; // Set to invalid value to ensure that it is actually set later on
m_iInstanceType = LESSON_INSTANCE_MULTIPLE;
m_iFixedInstancesMax = 1;
m_bReplaceOnlyWhenStopped = false;
m_iTeam = TEAM_ANY;
m_bOnlyKeyboard = false;
m_bOnlyGamepad = false;
m_iDisplayLimit = 0;
m_iDisplayCount = 0;
m_bWasDisplayed = false;
m_iSuccessLimit = 0;
m_iSuccessCount = 0;
m_fLockDuration = 0.0f;
m_bCanOpenWhenDead = false;
m_bBumpWithTimeoutWhenLearned = false;
m_bCanTimeoutWhileInactive = false;
m_fTimeout = 0.0f;
m_fInitTime = gpGlobals->curtime;
m_fStartTime = 0.0f;
m_fLockTime = 0.0f;
m_fUpdateInterval = 0.5;
m_bHasPlayedSound = false;
m_szStartSound = "Instructor.LessonStart";
m_szLessonGroup = "";
m_iNumDelayedPlayerSwaps = 0;
}
//=========================================================
//=========================================================
void CBaseLesson::TakePlaceOf( CBaseLesson *pLesson )
{
// Transfer over marked as displayed so a replaced lesson won't count as an extra display
m_bWasDisplayed = pLesson->m_bWasDisplayed;
pLesson->m_bWasDisplayed = false;
}
//=========================================================
//=========================================================
void CBaseLesson::MarkSucceeded()
{
if ( !m_bSuccessCounted )
{
GetGameInstructor().MarkSucceeded( GetName() );
m_bSuccessCounted = true;
}
}
//=========================================================
//=========================================================
void CBaseLesson::CloseOpportunity( const char *pchReason )
{
SetCloseReason(pchReason);
m_bIsOpenOpportunity = false;
}
//=========================================================
//=========================================================
bool CBaseLesson::DoDelayedPlayerSwaps() const
{
// A bot has swapped places with a player or player with a bot...
// At the time of the actual swap there was no client representation for the new player...
// So that swap was queued up and now we're going to make things right!
while ( m_iNumDelayedPlayerSwaps )
{
C_BasePlayer *pNewPlayer = UTIL_PlayerByUserId( m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps - 1 ].iNewUserID );
if ( !pNewPlayer )
{
// There is still no client representation of the new player, we'll have to try again later
if ( gameinstructor_verbose.GetInt() > 1 )
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tFailed delayed player swap!" );
return false;
}
if ( gameinstructor_verbose.GetInt() > 1 )
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tSuccessful delayed player swap!" );
m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps - 1 ].phHandleToChange->Set( pNewPlayer );
m_iNumDelayedPlayerSwaps--;
}
return true;
}
//
// CTextLesson
//
//=========================================================
//=========================================================
void CTextLesson::Init()
{
m_szDisplayText = "";
m_szDisplayParamText = "";
m_szBinding = "";
m_szGamepadBinding = "";
}
//=========================================================
//=========================================================
void CTextLesson::Start()
{
// TODO: Display some text
//m_szDisplayText
}
//=========================================================
//=========================================================
void CTextLesson::Stop()
{
// TODO: Clean up text
}
//
// CIconLesson
//
void CIconLesson::Init()
{
m_hIconTarget = NULL;
m_szVguiTargetName = "";
m_szVguiTargetLookup = "";
m_nVguiTargetEdge = 0;
m_hLocatorTarget = -1;
m_bFixedPosition = false;
m_bNoIconTarget = false;
m_bAllowNodrawTarget = false;
m_bVisible = true;
m_bShowWhenOccluded = true;
m_bNoOffscreen = false;
m_bForceCaption = false;
m_szOnscreenIcon = "";
m_szOffscreenIcon = "";
m_flUpOffset = 0.0f;
m_flRelativeUpOffset = 0.0f;
m_fFixedPositionX = 0.0f;
m_fFixedPositionY = 0.0f;
m_fRange = 0.0f;
m_fCurrentDistance = 0.0f;
m_fOnScreenStartTime = 0.0f;
m_fUpdateDistanceTime = 0.0f;
m_iFlags = LOCATOR_ICON_FX_NONE;
m_szCaptionColor = "255,255,255";// Default to white
}
//=========================================================
//=========================================================
void CIconLesson::Start()
{
if ( !DoDelayedPlayerSwaps() )
return;
// Display some text
C_BaseEntity *pIconTarget = m_hIconTarget.Get();
if ( !pIconTarget )
{
if ( !m_bNoIconTarget )
{
// Wanted one, but couldn't get it
CloseOpportunity( "Icon Target handle went invalid before the lesson started!" );
}
return;
}
else
{
if ( ( pIconTarget->IsEffectActive( EF_NODRAW ) || pIconTarget->IsDormant() ) && !m_bAllowNodrawTarget )
{
// We don't allow no draw entities
CloseOpportunity( "Icon Target is using effect NODRAW and allow_nodraw_target is false!" );
return;
}
}
CLocatorTarget *pLocatorTarget = NULL;
if ( m_hLocatorTarget != -1 )
{
// Lets try the handle that we've held on to
pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget );
if ( !pLocatorTarget )
{
// It's gone stale, get a new target
m_hLocatorTarget = Locator_AddTarget();
pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget );
}
}
else
{
// Get a new target
m_hLocatorTarget = Locator_AddTarget();
pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget );
}
if( m_hLocatorTarget == -1 || !pLocatorTarget )
{
CloseOpportunity( "Could not get a handle for new locator target. Too many targets in use!" );
return;
}
pLocatorTarget->AddIconEffects( m_iFlags );
pLocatorTarget->SetCaptionColor( GetCaptionColorString() );
UpdateLocatorTarget( pLocatorTarget, pIconTarget );
// Update occlusion data
Locator_ComputeTargetIconPositionFromHandle( m_hLocatorTarget );
}
//=========================================================
//=========================================================
void CIconLesson::Stop()
{
if ( !DoDelayedPlayerSwaps() )
return;
if ( m_hLocatorTarget != -1 )
Locator_RemoveTarget( m_hLocatorTarget );
m_fOnScreenStartTime = 0.0f;
}
//=========================================================
//=========================================================
void CIconLesson::Update()
{
if ( !DoDelayedPlayerSwaps() )
return;
C_BaseEntity *pIconTarget = m_hIconTarget.Get();
if ( !pIconTarget )
{
if ( !m_bNoIconTarget )
{
CloseOpportunity( "Lost our icon target handle returned NULL." );
}
return;
}
else
{
if ( ( pIconTarget->IsEffectActive( EF_NODRAW ) || pIconTarget->IsDormant() ) && !m_bAllowNodrawTarget )
{
// We don't allow no draw entities
CloseOpportunity( "Icon Target is using effect NODRAW and allow_nodraw_target is false!" );
return;
}
}
CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget );
if ( !pLocatorTarget )
{
// Temp instrumentation to catch a bug - possibly calling Update without having called Start?
Warning( "Problem in lesson %s: Locator_GetTargetFromHandle returned null for handle %d.\n IsInstanceActive: %s. IsInstructing: %s. IsLearned: %s\n",
GetName(), m_hLocatorTarget,
(IsInstanceActive() ? "yes" : "no"),
(IsInstructing() ? "yes" : "no"),
(IsLearned() ? "yes" : "no") );
CloseOpportunity( "Lost locator target handle." );
return;
}
UpdateLocatorTarget( pLocatorTarget, pIconTarget );
C_BasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer();
// Check if it has been onscreen long enough to count as being displayed
if ( m_fOnScreenStartTime == 0.0f )
{
if ( pLocatorTarget->IsOnScreen() && ( IsPresentComplete() || ( pLocatorTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_STATIC ) ) )
{
// Is either static or has finished presenting and is on screen
m_fOnScreenStartTime = gpGlobals->curtime;
}
}
else
{
if ( !pLocatorTarget->IsOnScreen() )
{
// Was visible before, but it isn't now
m_fOnScreenStartTime = 0.0f;
}
else if ( gpGlobals->curtime - m_fOnScreenStartTime >= LESSON_MIN_TIME_ON_SCREEN_TO_MARK_DISPLAYED )
{
// Lesson on screen long enough to be counted as displayed
m_bWasDisplayed = true;
}
}
if ( m_fUpdateDistanceTime < gpGlobals->curtime )
{
// Update it's distance from the local player
C_BaseEntity *pTarget = m_hIconTarget.Get();
if ( !pLocalPlayer || !pTarget || pLocalPlayer == pTarget )
{
m_fCurrentDistance = 0.0f;
}
else
{
m_fCurrentDistance = pLocalPlayer->EyePosition().DistTo( pTarget->WorldSpaceCenter() );
}
m_fUpdateDistanceTime = gpGlobals->curtime + LESSON_DISTANCE_UPDATE_RATE;
}
}
void CIconLesson::UpdateInactive()
{
if ( m_fUpdateDistanceTime < gpGlobals->curtime )
{
if ( !DoDelayedPlayerSwaps() )
{
return;
}
C_BaseEntity *pIconTarget = m_hIconTarget.Get();
if ( !pIconTarget )
{
if ( !m_bNoIconTarget )
{
CloseOpportunity( "Lost our icon target handle returned NULL." );
}
m_fCurrentDistance = 0.0f;
return;
}
else
{
if ( ( pIconTarget->IsEffectActive( EF_NODRAW ) || pIconTarget->IsDormant() ) && !m_bAllowNodrawTarget )
{
// We don't allow no draw entities
CloseOpportunity( "Icon Target is using effect NODRAW and allow_nodraw_target is false!" );
return;
}
}
// Update it's distance from the local player
C_BasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer();
if ( !pLocalPlayer || pLocalPlayer == pIconTarget )
{
m_fCurrentDistance = 0.0f;
}
else
{
m_fCurrentDistance = pLocalPlayer->EyePosition().DistTo( pIconTarget->WorldSpaceCenter() );
}
m_fUpdateDistanceTime = gpGlobals->curtime + LESSON_DISTANCE_UPDATE_RATE;
}
}
bool CIconLesson::ShouldDisplay() const
{
VPROF_BUDGET( "CIconLesson::ShouldDisplay", "GameInstructor" );
if ( !DoDelayedPlayerSwaps() )
{
return false;
}
if ( m_fRange > 0.0f && m_fCurrentDistance > m_fRange )
{
// Distance to target is more than the max range
return false;
}
if ( !m_bShowWhenOccluded && m_hLocatorTarget >= 0 )
{
CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget );
if ( pLocatorTarget && pLocatorTarget->IsOccluded() )
{
// Target is occluded and doesn't want to be shown when occluded
return false;
}
}
// Ok to display
return true;
}
bool CIconLesson::IsVisible() const
{
VPROF_BUDGET( "CIconLesson::IsVisible", "GameInstructor" );
if( m_hLocatorTarget == -1 )
{
// If it doesn't want a target, it's "visible" otherwise we'll have to call it invisible
return m_bNoIconTarget;
}
CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget );
if ( !pLocatorTarget )
{
return false;
}
return pLocatorTarget->IsVisible();
}
void CIconLesson::SwapOutPlayers( int iOldUserID, int iNewUserID )
{
BaseClass::SwapOutPlayers( iOldUserID, iNewUserID );
if ( m_bNoIconTarget )
return;
// Get the player pointers from the user IDs
C_BasePlayer *pOldPlayer = UTIL_PlayerByUserId( iOldUserID );
C_BasePlayer *pNewPlayer = UTIL_PlayerByUserId( iNewUserID );
if ( pOldPlayer == m_hIconTarget.Get() )
{
if ( pNewPlayer )
{
m_hIconTarget = pNewPlayer;
}
else
{
if ( m_iNumDelayedPlayerSwaps < MAX_DELAYED_PLAYER_SWAPS )
{
m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].phHandleToChange = &m_hIconTarget;
m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].iNewUserID = iNewUserID;
++m_iNumDelayedPlayerSwaps;
}
}
}
}
void CIconLesson::TakePlaceOf( CBaseLesson *pLesson )
{
BaseClass::TakePlaceOf( pLesson );
const CIconLesson *pIconLesson = dynamic_cast<const CIconLesson*>( pLesson );
if ( pIconLesson )
{
if ( pIconLesson->m_hLocatorTarget != -1 )
{
CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( pIconLesson->m_hLocatorTarget );
if ( pLocatorTarget )
{
// This one draw right to the hud... use it's icon target handle
m_hLocatorTarget = pIconLesson->m_hLocatorTarget;
}
}
m_fOnScreenStartTime = pIconLesson->m_fOnScreenStartTime;
}
}
void CIconLesson::SetLocatorBinding( CLocatorTarget * pLocatorTarget )
{
if ( IsX360() /*|| input->ControllerModeActive()*/ )
{
// Try to use gamepad bindings first
if ( m_szGamepadBinding.String()[ 0 ] != '\0' )
{
// Found gamepad binds!
pLocatorTarget->SetBinding( m_szGamepadBinding.String() );
}
else
{
// No gamepad binding, so fallback to the regular binding
pLocatorTarget->SetBinding( m_szBinding.String() );
}
}
else
{
// Always use the regular binding when the gamepad is disabled
pLocatorTarget->SetBinding( m_szBinding.String() );
}
}
bool CIconLesson::IsPresentComplete()
{
if ( m_hLocatorTarget == -1 )
return false;
CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget );
if ( !pLocatorTarget )
return false;
return !pLocatorTarget->IsPresenting();
}
void CIconLesson::PresentStart()
{
if ( m_hLocatorTarget == -1 )
return;
CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget );
if ( !pLocatorTarget )
return;
pLocatorTarget->StartPresent();
}
void CIconLesson::PresentEnd()
{
if ( m_hLocatorTarget == -1 )
return;
CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget );
if ( !pLocatorTarget )
return;
pLocatorTarget->EndPresent();
}
void CIconLesson::UpdateLocatorTarget( CLocatorTarget *pLocatorTarget, C_BaseEntity *pIconTarget )
{
if ( m_bFixedPosition )
{
pLocatorTarget->m_bOriginInScreenspace = true;
pLocatorTarget->m_vecOrigin.x = m_fFixedPositionX;
pLocatorTarget->m_vecOrigin.y = m_fFixedPositionY;
pLocatorTarget->SetVguiTargetName( m_szVguiTargetName.String() );
pLocatorTarget->SetVguiTargetLookup( m_szVguiTargetLookup.String() );
pLocatorTarget->SetVguiTargetEdge( m_nVguiTargetEdge );
}
else
{
pLocatorTarget->m_bOriginInScreenspace = false;
pLocatorTarget->m_vecOrigin = pIconTarget->EyePosition() + MainViewUp() * m_flRelativeUpOffset + Vector( 0.0f, 0.0f, m_flUpOffset );
pLocatorTarget->SetVguiTargetName( "" );
}
const char *pchDisplayParamText = m_szDisplayParamText.String();
#ifdef INFESTED_DLL
char szCustomName[ 256 ];
#endif
// Check if the parameter is the be the player display name
if ( Q_stricmp( pchDisplayParamText, "use_name" ) == 0 )
{
// Fix up the player display name
C_BasePlayer *pPlayer = ToBasePlayer( pIconTarget );
if ( pPlayer )
{
pchDisplayParamText = pPlayer->GetPlayerName();
}
else
{
bool bNoName = true;
#ifdef INFESTED_DLL
C_ASW_Marine *pMarine = dynamic_cast< C_ASW_Marine* >( pIconTarget );
if ( pMarine )
{
C_ASW_Marine_Resource *pMR = pMarine->GetMarineResource();
if ( pMR )
{
pMR->GetDisplayName( szCustomName, sizeof( szCustomName ) );
pchDisplayParamText = szCustomName;
bNoName = false;
}
}
#endif
if ( bNoName )
{
// It's not a player!
pchDisplayParamText = "";
}
}
}
pLocatorTarget->SetCaptionText( m_szDisplayText.String(), pchDisplayParamText );
SetLocatorBinding( pLocatorTarget );
pLocatorTarget->SetOnscreenIconTextureName( m_szOnscreenIcon.String() );
pLocatorTarget->SetOffscreenIconTextureName( m_szOffscreenIcon.String() );
pLocatorTarget->SetVisible( m_bVisible );
C_BasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer();
if( !m_bFixedPosition &&
( ( pLocalPlayer != NULL && pLocalPlayer == m_hIconTarget ) ||
GetClientWorldEntity() == m_hIconTarget ) )
{
// Mark this icon as a static icon that draws in a fixed
// location on the hud rather than tracking an object
// in 3D space.
pLocatorTarget->AddIconEffects( LOCATOR_ICON_FX_STATIC );
}
else
{
pLocatorTarget->AddIconEffects( LOCATOR_ICON_FX_NONE );
}
if ( m_bNoOffscreen )
{
pLocatorTarget->AddIconEffects( LOCATOR_ICON_FX_NO_OFFSCREEN );
}
else
{
pLocatorTarget->RemoveIconEffects( LOCATOR_ICON_FX_NO_OFFSCREEN );
}
if( m_bForceCaption || IsLocked() )
{
pLocatorTarget->AddIconEffects( LOCATOR_ICON_FX_FORCE_CAPTION );
}
else
{
pLocatorTarget->RemoveIconEffects( LOCATOR_ICON_FX_FORCE_CAPTION );
}
pLocatorTarget->Update();
if ( pLocatorTarget->m_bIsDrawing )
{
if ( !m_bHasPlayedSound )
{
GetGameInstructor().PlaySound( m_szStartSound.String() );
m_bHasPlayedSound = true;
}
}
}
//
// CScriptedIconLesson
//
// Linking variables to scriptable entries is done here!
// The first parameter correlates to the case insensitive string name read from scripts.
// This macro generates code that passes this consistent variable data in to other macros
#define LESSON_VARIABLE_FACTORY \
LESSON_VARIABLE_MACRO_EHANDLE( VOID, m_hLocalPlayer, EHANDLE ) \
\
LESSON_VARIABLE_MACRO_EHANDLE( LOCAL_PLAYER, m_hLocalPlayer, EHANDLE ) \
LESSON_VARIABLE_MACRO( OUTPUT, m_fOutput, float ) \
\
LESSON_VARIABLE_MACRO_EHANDLE( ENTITY1, m_hEntity1, EHANDLE ) \
LESSON_VARIABLE_MACRO_EHANDLE( ENTITY2, m_hEntity2, EHANDLE ) \
LESSON_VARIABLE_MACRO_STRING( STRING1, m_szString1, CGameInstructorSymbol ) \
LESSON_VARIABLE_MACRO_STRING( STRING2, m_szString2, CGameInstructorSymbol ) \
LESSON_VARIABLE_MACRO( INTEGER1, m_iInteger1, int ) \
LESSON_VARIABLE_MACRO( INTEGER2, m_iInteger2, int ) \
LESSON_VARIABLE_MACRO( FLOAT1, m_fFloat1, float ) \
LESSON_VARIABLE_MACRO( FLOAT2, m_fFloat2, float ) \
\
LESSON_VARIABLE_MACRO_EHANDLE( ICON_TARGET, m_hIconTarget, EHANDLE ) \
LESSON_VARIABLE_MACRO_STRING( VGUI_TARGET_NAME, m_szVguiTargetName, CGameInstructorSymbol ) \
LESSON_VARIABLE_MACRO_STRING( VGUI_TARGET_LOOKUP, m_szVguiTargetLookup, CGameInstructorSymbol ) \
LESSON_VARIABLE_MACRO( VGUI_TARGET_EDGE, m_nVguiTargetEdge, int ) \
LESSON_VARIABLE_MACRO( FIXED_POSITION_X, m_fFixedPositionX, float ) \
LESSON_VARIABLE_MACRO( FIXED_POSITION_Y, m_fFixedPositionY, float ) \
LESSON_VARIABLE_MACRO_BOOL( FIXED_POSITION, m_bFixedPosition, bool ) \
LESSON_VARIABLE_MACRO_BOOL( NO_ICON_TARGET, m_bNoIconTarget, bool ) \
LESSON_VARIABLE_MACRO_BOOL( ALLOW_NODRAW_TARGET, m_bAllowNodrawTarget, bool ) \
LESSON_VARIABLE_MACRO_BOOL( VISIBLE, m_bVisible, bool ) \
LESSON_VARIABLE_MACRO_BOOL( SHOW_WHEN_OCCLUDED, m_bShowWhenOccluded, bool ) \
LESSON_VARIABLE_MACRO_BOOL( NO_OFFSCREEN, m_bNoOffscreen, bool ) \
LESSON_VARIABLE_MACRO_BOOL( FORCE_CAPTION, m_bForceCaption, bool ) \
LESSON_VARIABLE_MACRO_STRING( ONSCREEN_ICON, m_szOnscreenIcon, CGameInstructorSymbol ) \
LESSON_VARIABLE_MACRO_STRING( OFFSCREEN_ICON, m_szOffscreenIcon, CGameInstructorSymbol ) \
LESSON_VARIABLE_MACRO( ICON_OFFSET, m_flUpOffset, float ) \
LESSON_VARIABLE_MACRO( ICON_RELATIVE_OFFSET, m_flRelativeUpOffset, float ) \
LESSON_VARIABLE_MACRO( RANGE, m_fRange, float ) \
\
LESSON_VARIABLE_MACRO( FLAGS, m_iFlags, int ) \
LESSON_VARIABLE_MACRO_STRING( CAPTION_COLOR, m_szCaptionColor, CGameInstructorSymbol ) \
LESSON_VARIABLE_MACRO_STRING( GROUP, m_szLessonGroup, CGameInstructorSymbol ) \
\
LESSON_VARIABLE_MACRO_STRING( CAPTION, m_szDisplayText, CGameInstructorSymbol ) \
LESSON_VARIABLE_MACRO_STRING( CAPTION_PARAM, m_szDisplayParamText, CGameInstructorSymbol ) \
LESSON_VARIABLE_MACRO_STRING( BINDING, m_szBinding, CGameInstructorSymbol ) \
LESSON_VARIABLE_MACRO_STRING( GAMEPAD_BINDING, m_szGamepadBinding, CGameInstructorSymbol ) \
\
LESSON_VARIABLE_MACRO( PRIORITY, m_iPriority, int ) \
LESSON_VARIABLE_MACRO_STRING( REPLACE_KEY, m_stringReplaceKey, CGameInstructorSymbol ) \
\
LESSON_VARIABLE_MACRO( LOCK_DURATION, m_fLockDuration, float ) \
LESSON_VARIABLE_MACRO_BOOL( CAN_OPEN_WHEN_DEAD, m_bCanOpenWhenDead, bool ) \
LESSON_VARIABLE_MACRO_BOOL( BUMP_WITH_TIMEOUT_WHEN_LEARNED, m_bBumpWithTimeoutWhenLearned, bool ) \
LESSON_VARIABLE_MACRO_BOOL( CAN_TIMEOUT_WHILE_INACTIVE, m_bCanTimeoutWhileInactive, bool ) \
LESSON_VARIABLE_MACRO( TIMEOUT, m_fTimeout, float ) \
LESSON_VARIABLE_MACRO( UPDATE_INTERVAL, m_fUpdateInterval, float ) \
LESSON_VARIABLE_MACRO_STRING( START_SOUND, m_szStartSound, CGameInstructorSymbol ) \
// Create keyvalues name symbol
#define LESSON_VARIABLE_SYMBOL( _varEnum, _varName, _varType ) static int g_n##_varEnum##Symbol;
#define LESSON_VARIABLE_INIT_SYMBOL( _varEnum, _varName, _varType ) g_n##_varEnum##Symbol = KeyValuesSystem()->GetSymbolForString( #_varEnum );
#define LESSON_SCRIPT_STRING_ADD_TO_MAP( _varEnum, _varName, _varType ) g_NameToTypeMap.Insert( #_varEnum, LESSON_VARIABLE_##_varEnum## );
// Create enum value
#define LESSON_VARIABLE_ENUM( _varEnum, _varName, _varType ) LESSON_VARIABLE_##_varEnum##,
// Init info call
#define LESSON_VARIABLE_INIT_INFO_CALL( _varEnum, _varName, _varType ) g_pLessonVariableInfo[ LESSON_VARIABLE_##_varEnum## ].Init_##_varEnum##();
// Init info
#define LESSON_VARIABLE_INIT_INFO( _varEnum, _varName, _varType ) \
void Init_##_varEnum##() \
{ \
iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \
varType = LessonParamTypeFromString( #_varType ); \
}
#define LESSON_VARIABLE_INIT_INFO_BOOL( _varEnum, _varName, _varType ) \
void Init_##_varEnum##() \
{ \
iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \
varType = FIELD_BOOLEAN; \
}
#define LESSON_VARIABLE_INIT_INFO_EHANDLE( _varEnum, _varName, _varType ) \
void Init_##_varEnum##() \
{ \
iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \
varType = FIELD_EHANDLE; \
}
#define LESSON_VARIABLE_INIT_INFO_STRING( _varEnum, _varName, _varType ) \
void Init_##_varEnum##() \
{ \
iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \
varType = FIELD_STRING; \
}
// Copy defaults into this scripted lesson into a new one
#define LESSON_VARIABLE_DEFAULT( _varEnum, _varName, _varType ) ( _varName = m_pDefaultHolder->_varName );
// Copy a variable from this scripted lesson into a new one
#define LESSON_VARIABLE_COPY( _varEnum, _varName, _varType ) ( pOpenLesson->_varName = _varName );
// Return the first param if pchName is the same as the second param
#define LESSON_SCRIPT_STRING( _type, _string ) \
if ( Q_stricmp( pchName, _string ) == 0 )\
{\
return _type;\
}
// Wrapper for using this macro in the factory
#define LESSON_SCRIPT_STRING_GENERAL( _varEnum, _varName, _varType ) LESSON_SCRIPT_STRING( LESSON_VARIABLE_##_varEnum##, #_varEnum )
// Process the element action on this variable
#define PROCESS_LESSON_ACTION( _varEnum, _varName, _varType ) \
case LESSON_VARIABLE_##_varEnum##:\
return ProcessElementAction( pLessonElement->iAction, pLessonElement->bNot, #_varName, _varName, &pLessonElement->szParam, eventParam_float );
#define PROCESS_LESSON_ACTION_EHANDLE( _varEnum, _varName, _varType ) \
case LESSON_VARIABLE_##_varEnum##:\
return ProcessElementAction( pLessonElement->iAction, pLessonElement->bNot, #_varName, _varName, &pLessonElement->szParam, eventParam_float, eventParam_BaseEntity, eventParam_string );
#define PROCESS_LESSON_ACTION_STRING( _varEnum, _varName, _varType ) \
case LESSON_VARIABLE_##_varEnum##:\
return ProcessElementAction( pLessonElement->iAction, pLessonElement->bNot, #_varName, &_varName, &pLessonElement->szParam, eventParam_string );
// Init the variable from the script (or a convar)
#define LESSON_VARIABLE_INIT( _varEnum, _varName, _varType ) \
else if ( g_n##_varEnum##Symbol == pSubKey->GetNameSymbol() ) \
{ \
const char *pchParam = pSubKey->GetString(); \
if ( pchParam && StringHasPrefix( pchParam, "convar " ) ) \
{ \
ConVarRef tempCVar( pchParam + Q_strlen( "convar " ) ); \
if ( tempCVar.IsValid() ) \
{ \
_varName = static_cast<_varType>( tempCVar.GetFloat() ); \
} \
else \
{ \
_varName = static_cast<_varType>( 0.0f ); \
} \
} \
else \
{ \
_varName = static_cast<_varType>( pSubKey->GetFloat() ); \
} \
}
#define LESSON_VARIABLE_INIT_BOOL( _varEnum, _varName, _varType ) \
else if ( Q_stricmp( #_varEnum, pSubKey->GetName() ) == 0 ) \
{ \
_varName = pSubKey->GetBool(); \
}
#define LESSON_VARIABLE_INIT_EHANDLE( _varEnum, _varName, _varType ) \
else if ( g_n##_varEnum##Symbol == pSubKey->GetNameSymbol() ) \
{ \
DevWarning( "Can't initialize an EHANDLE from the instructor lesson script." ); \
}
#define LESSON_VARIABLE_INIT_STRING( _varEnum, _varName, _varType ) \
else if ( g_n##_varEnum##Symbol == pSubKey->GetNameSymbol() ) \
{ \
const char *pchParam = pSubKey->GetString(); \
if ( pchParam && StringHasPrefix( pchParam, "convar " ) ) \
{ \
ConVarRef tempCVar( pchParam + Q_strlen( "convar " ) ); \
if ( tempCVar.IsValid() ) \
{ \
_varName = tempCVar.GetString(); \
} \
else \
{ \
_varName = ""; \
} \
} \
else \
{ \
_varName = pSubKey->GetString(); \
} \
}
// Gets a scripted variable by offset and casts it to the proper type
#define LESSON_VARIABLE_GET_FROM_OFFSET( _type, _offset ) *static_cast<_type*>( static_cast<void*>( static_cast<byte*>( static_cast<void*>( this ) ) + _offset ) )
// Enum of scripted variables
enum LessonVariable
{
// Run enum macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition)
#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_ENUM
#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_ENUM
#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_ENUM
#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_ENUM
LESSON_VARIABLE_FACTORY
#undef LESSON_VARIABLE_MACRO
#undef LESSON_VARIABLE_MACRO_BOOL
#undef LESSON_VARIABLE_MACRO_EHANDLE
#undef LESSON_VARIABLE_MACRO_STRING
LESSON_VARIABLE_TOTAL
};
// Declare the keyvalues symbols for the keynames
#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_SYMBOL
#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_SYMBOL
#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_SYMBOL
#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_SYMBOL
LESSON_VARIABLE_FACTORY
#undef LESSON_VARIABLE_MACRO
#undef LESSON_VARIABLE_MACRO_BOOL
#undef LESSON_VARIABLE_MACRO_EHANDLE
#undef LESSON_VARIABLE_MACRO_STRING
// String lookup prototypes
LessonVariable LessonVariableFromString( const char *pchName, bool bWarnOnInvalidNames = true );
_fieldtypes LessonParamTypeFromString( const char *pchName );
int LessonActionFromString( const char *pchName );
// This is used to get type info an variable offsets from the variable enumerated value
class LessonVariableInfo
{
public:
LessonVariableInfo()
: iOffset( 0 ), varType( FIELD_VOID )
{
}
// Run init info macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition)
#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_INIT_INFO
#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_INIT_INFO_BOOL
#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_INIT_INFO_EHANDLE
#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_INIT_INFO_STRING
LESSON_VARIABLE_FACTORY
#undef LESSON_VARIABLE_MACRO
#undef LESSON_VARIABLE_MACRO_BOOL
#undef LESSON_VARIABLE_MACRO_EHANDLE
#undef LESSON_VARIABLE_MACRO_STRING
public:
int iOffset;
_fieldtypes varType;
};
LessonVariableInfo g_pLessonVariableInfo[ LESSON_VARIABLE_TOTAL ];
const LessonVariableInfo *GetLessonVariableInfo( int iLessonVariable )
{
Assert( iLessonVariable >= 0 && iLessonVariable < LESSON_VARIABLE_TOTAL );
if ( g_pLessonVariableInfo[ 0 ].varType == FIELD_VOID )
{
// Run init info call macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition)
#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_INIT_INFO_CALL
#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_INIT_INFO_CALL
#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_INIT_INFO_CALL
#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_INIT_INFO_CALL
LESSON_VARIABLE_FACTORY
#undef LESSON_VARIABLE_MACRO
#undef LESSON_VARIABLE_MACRO_BOOL
#undef LESSON_VARIABLE_MACRO_EHANDLE
#undef LESSON_VARIABLE_MACRO_STRING
}
return &(g_pLessonVariableInfo[ iLessonVariable ]);
}
static CUtlDict< LessonVariable, int > g_NameToTypeMap;
static CUtlDict< fieldtype_t, int > g_TypeToParamTypeMap;
CUtlDict< int, int > CScriptedIconLesson::LessonActionMap;
CScriptedIconLesson::~CScriptedIconLesson()
{
if ( m_pDefaultHolder )
{
delete m_pDefaultHolder;
m_pDefaultHolder = NULL;
}
}
void CScriptedIconLesson::Init()
{
m_hLocalPlayer.Set( NULL );
m_fOutput = 0.0f;
m_hEntity1.Set( NULL );
m_hEntity2.Set( NULL );
m_szString1 = "";
m_szString2 = "";
m_iInteger1 = 0;
m_iInteger2 = 0;
m_fFloat1 = 0.0f;
m_fFloat2 = 0.0f;
m_fUpdateEventTime = 0.0f;
m_pDefaultHolder = NULL;
m_iScopeDepth = 0;
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Initializing scripted lesson " );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", GetName() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" );
}
if ( !IsDefaultHolder() )
{
if ( !IsOpenOpportunity() )
{
// Initialize from the key value file
InitFromKeys( GetGameInstructor().GetScriptKeys() );
if ( m_iPriority >= LESSON_PRIORITY_MAX )
{
DevWarning( "Priority level not set for lesson: %s\n", GetName() );
}
// We use this to remember variable defaults to be reset before each open attempt
m_pDefaultHolder = new CScriptedIconLesson( GetName(), true, false );
CScriptedIconLesson *pOpenLesson = m_pDefaultHolder;
// Run copy macros on all default scriptable variables (see: LESSON_VARIABLE_FACTORY definition)
#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_COPY
#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_COPY
#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_COPY
#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_COPY
LESSON_VARIABLE_FACTORY
#undef LESSON_VARIABLE_MACRO
#undef LESSON_VARIABLE_MACRO_BOOL
#undef LESSON_VARIABLE_MACRO_EHANDLE
#undef LESSON_VARIABLE_MACRO_STRING
// Listen for open events
for ( int iLessonEvent = 0; iLessonEvent < m_OpenEvents.Count(); ++iLessonEvent )
{
const LessonEvent_t *pLessonEvent = &(m_OpenEvents[ iLessonEvent ]);
ListenForGameEvent( pLessonEvent->szEventName.String() );
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tListen for open event " );
ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", pLessonEvent->szEventName.String());
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" );
}
}
// Listen for close events
for ( int iLessonEvent = 0; iLessonEvent < m_CloseEvents.Count(); ++iLessonEvent )
{
const LessonEvent_t *pLessonEvent = &(m_CloseEvents[ iLessonEvent ]);
ListenForGameEvent( pLessonEvent->szEventName.String() );
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tListen for close event " );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\"", pLessonEvent->szEventName.String());
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" );
}
}
// Listen for success events
for ( int iLessonEvent = 0; iLessonEvent < m_SuccessEvents.Count(); ++iLessonEvent )
{
const LessonEvent_t *pLessonEvent = &(m_SuccessEvents[ iLessonEvent ]);
ListenForGameEvent( pLessonEvent->szEventName.String());
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tListen for success event " );
ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\"%s\"", pLessonEvent->szEventName.String());
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" );
}
}
}
else
{
// This is an open lesson! Get the root for reference
const CScriptedIconLesson *pLesson = static_cast<const CScriptedIconLesson *>( GetGameInstructor().GetLesson( GetName() ) );
SetRoot( const_cast<CScriptedIconLesson *>( pLesson ) );
}
}
}
void CScriptedIconLesson::InitPrerequisites()
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Initializing prereqs for scripted lesson " );
ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", GetName() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" );
}
for ( int iPrerequisit = 0; iPrerequisit < m_PrerequisiteNames.Count(); ++iPrerequisit )
{
const char *pPrerequisiteName = m_PrerequisiteNames[ iPrerequisit ].String();
AddPrerequisite( pPrerequisiteName );
}
}
void CScriptedIconLesson::OnOpen()
{
VPROF_BUDGET( "CScriptedIconLesson::OnOpen", "GameInstructor" );
if ( !DoDelayedPlayerSwaps() )
{
return;
}
const CScriptedIconLesson *pLesson = static_cast<const CScriptedIconLesson *>( GetRoot() );
// Process all update events
for ( int iLessonEvent = 0; iLessonEvent < pLesson->m_OnOpenEvents.Count(); ++iLessonEvent )
{
const LessonEvent_t *pLessonEvent = &(pLesson->m_OnOpenEvents[ iLessonEvent ]);
if ( gameinstructor_verbose.GetInt() > 1 && ShouldShowSpew() )
{
ConColorMsg( Color( 255, 128, 64, 255 ), "GAME INSTRUCTOR: " );
ConColorMsg( Color( 64, 128, 255, 255 ), "OnOpen event " );
ConColorMsg( Color( 0, 255, 0, 255 ), "\"%s\"", pLessonEvent->szEventName.String());
ConColorMsg( Color( 64, 128, 255, 255 ), "received for lesson \"%s\"...\n", GetName() );
}
ProcessElements( NULL, &(pLessonEvent->elements) );
}
BaseClass::OnOpen();
}
void CScriptedIconLesson::Update()
{
VPROF_BUDGET( "CScriptedIconLesson::Update", "GameInstructor" );
if ( !DoDelayedPlayerSwaps() )
{
return;
}
const CScriptedIconLesson *pLesson = static_cast<const CScriptedIconLesson *>( GetRoot() );
if ( gpGlobals->curtime >= m_fUpdateEventTime )
{
bool bShowSpew = ( gameinstructor_verbose.GetInt() > 1 && ShouldShowSpew() );
int iVerbose = gameinstructor_verbose.GetInt();
if ( gameinstructor_verbose.GetInt() == 1 )
{
// Force the verbose level from 1 to 0 for update events
gameinstructor_verbose.SetValue( 0 );
}
// Process all update events
for ( int iLessonEvent = 0; iLessonEvent < pLesson->m_UpdateEvents.Count(); ++iLessonEvent )
{
const LessonEvent_t *pLessonEvent = &(pLesson->m_UpdateEvents[ iLessonEvent ]);
if ( bShowSpew )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Update event " );
ConColorMsg( CBaseLesson::m_rgbaVerboseUpdate, "\"%s\"", pLessonEvent->szEventName.String());
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "received for lesson \"%s\"...\n", GetName() );
}
ProcessElements( NULL, &(pLessonEvent->elements) );
}
gameinstructor_verbose.SetValue( iVerbose );
// Wait before doing update events again
m_fUpdateEventTime = gpGlobals->curtime + m_fUpdateInterval;
}
BaseClass::Update();
}
void CScriptedIconLesson::SwapOutPlayers( int iOldUserID, int iNewUserID )
{
BaseClass::SwapOutPlayers( iOldUserID, iNewUserID );
// Get the player pointers from the user IDs
C_BasePlayer *pOldPlayer = UTIL_PlayerByUserId( iOldUserID );
C_BasePlayer *pNewPlayer = UTIL_PlayerByUserId( iNewUserID );
if ( pOldPlayer == m_hEntity1.Get() )
{
if ( pNewPlayer )
{
m_hEntity1 = pNewPlayer;
}
else
{
if ( m_iNumDelayedPlayerSwaps < MAX_DELAYED_PLAYER_SWAPS )
{
m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].phHandleToChange = &m_hEntity1;
m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].iNewUserID = iNewUserID;
++m_iNumDelayedPlayerSwaps;
}
}
}
if ( pOldPlayer == m_hEntity2.Get() )
{
if ( pNewPlayer )
{
m_hEntity2 = pNewPlayer;
}
else
{
if ( m_iNumDelayedPlayerSwaps < MAX_DELAYED_PLAYER_SWAPS )
{
m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].phHandleToChange = &m_hEntity2;
m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].iNewUserID = iNewUserID;
++m_iNumDelayedPlayerSwaps;
}
}
}
}
void CScriptedIconLesson::FireGameEvent( IGameEvent *event )
{
VPROF_BUDGET( "CScriptedIconLesson::FireGameEvent", "GameInstructor" );
if ( m_bDisabled )
return;
if ( !DoDelayedPlayerSwaps() )
{
return;
}
if ( !C_BasePlayer::GetLocalPlayer() )
return;
// Check that this lesson is allowed for the current input device
if( m_bOnlyKeyboard /*&& input->ControllerModeActive()*/ )
return;
if( m_bOnlyGamepad /*&& !input->ControllerModeActive()*/ )
return;
// Check that this lesson is for the proper team
CBasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer();
if ( m_iTeam != TEAM_ANY && pLocalPlayer && pLocalPlayer->GetTeamNumber() != m_iTeam )
{
// This lesson is intended for a different team
return;
}
const char *name = event->GetName();
// Open events run on the root
ProcessOpenGameEvents( this, name, event );
// Close and success events run on the children
const CUtlVector < CBaseLesson * > *pChildren = GetChildren();
for ( int iChild = 0; iChild < pChildren->Count(); ++iChild )
{
CScriptedIconLesson *pScriptedChild = dynamic_cast<CScriptedIconLesson*>( (*pChildren)[ iChild ] );
pScriptedChild->ProcessCloseGameEvents( this, name, event );
pScriptedChild->ProcessSuccessGameEvents( this, name, event );
}
}
void CScriptedIconLesson::ProcessOpenGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event )
{
if ( pRootLesson->InstanceType() == LESSON_INSTANCE_SINGLE_OPEN && GetGameInstructor().IsLessonOfSameTypeOpen( this ) )
{
// We don't want more than one of this type, and there is already one open
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pRootLesson->GetName() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "open events NOT processed (there is already an open lesson of this type).\n" );
}
return;
}
for ( int iLessonEvent = 0; iLessonEvent < pRootLesson->m_OpenEvents.Count(); ++iLessonEvent )
{
const LessonEvent_t *pLessonEvent = &(pRootLesson->m_OpenEvents[ iLessonEvent ]);
if ( Q_strcmp( name, pLessonEvent->szEventName.String()) == 0 )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Open event " );
ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", pLessonEvent->szEventName.String());
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "received for lesson \"%s\"...\n", GetName() );
}
if ( m_pDefaultHolder )
{
// Run copy from default macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition)
#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_DEFAULT
#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_DEFAULT
#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_DEFAULT
#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_DEFAULT
LESSON_VARIABLE_FACTORY
#undef LESSON_VARIABLE_MACRO
#undef LESSON_VARIABLE_MACRO_BOOL
#undef LESSON_VARIABLE_MACRO_EHANDLE
#undef LESSON_VARIABLE_MACRO_STRING
}
if ( ProcessElements( event, &(pLessonEvent->elements) ) )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\tAll elements returned true. Opening!\n" );
}
MEM_ALLOC_CREDIT();
CScriptedIconLesson *pOpenLesson = new CScriptedIconLesson( GetName(), false, true );
// Run copy macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition)
#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_COPY
#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_COPY
#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_COPY
#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_COPY
LESSON_VARIABLE_FACTORY
#undef LESSON_VARIABLE_MACRO
#undef LESSON_VARIABLE_MACRO_BOOL
#undef LESSON_VARIABLE_MACRO_EHANDLE
#undef LESSON_VARIABLE_MACRO_STRING
if ( GetGameInstructor().OpenOpportunity( pOpenLesson ) )
{
pOpenLesson->OnOpen();
if ( pRootLesson->InstanceType() == LESSON_INSTANCE_SINGLE_OPEN )
{
// This one is open and we only want one! So, we're done.
// Other open events may be listening for the same events... skip them!
return;
}
}
}
}
}
}
void CScriptedIconLesson::ProcessCloseGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event )
{
for ( int iLessonEvent = 0; iLessonEvent < pRootLesson->m_CloseEvents.Count() && IsOpenOpportunity(); ++iLessonEvent )
{
const LessonEvent_t *pLessonEvent = &(pRootLesson->m_CloseEvents[ iLessonEvent ]);
if ( Q_strcmp( name, pLessonEvent->szEventName.String()) == 0 )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Close event " );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\"", pLessonEvent->szEventName.String());
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "received for lesson \"%s\"...\n", GetName() );
}
if ( ProcessElements( event, &(pLessonEvent->elements) ) )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tAll elements returned true. Closing!\n" );
}
CloseOpportunity( "Close event elements completed." );
}
}
}
}
void CScriptedIconLesson::ProcessSuccessGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event )
{
for ( int iLessonEvent = 0; iLessonEvent < pRootLesson->m_SuccessEvents.Count(); ++iLessonEvent )
{
const LessonEvent_t *pLessonEvent = &(pRootLesson->m_SuccessEvents[ iLessonEvent ]);
if ( Q_strcmp( name, pLessonEvent->szEventName.String()) == 0 )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Success event " );
ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\"%s\"", pLessonEvent->szEventName.String());
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "received for lesson \"%s\"...\n", GetName() );
}
if ( ProcessElements( event, &(pLessonEvent->elements) ) )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\tAll elements returned true. Succeeding!\n" );
}
MarkSucceeded();
}
}
}
}
LessonVariable LessonVariableFromString( const char *pchName, bool bWarnOnInvalidNames )
{
int slot = g_NameToTypeMap.Find( pchName );
if ( slot != g_NameToTypeMap.InvalidIndex() )
return g_NameToTypeMap[ slot ];
if ( bWarnOnInvalidNames )
{
AssertMsg( 0, "Invalid scripted lesson variable!" );
DevWarning( "Invalid scripted lesson variable: %s\n", pchName );
}
return LESSON_VARIABLE_TOTAL;
}
_fieldtypes LessonParamTypeFromString( const char *pchName )
{
int slot = g_TypeToParamTypeMap.Find( pchName );
if ( slot != g_TypeToParamTypeMap.InvalidIndex() )
return g_TypeToParamTypeMap[ slot ];
DevWarning( "Invalid scripted lesson variable/param type: %s\n", pchName );
return FIELD_VOID;
}
int LessonActionFromString( const char *pchName )
{
int slot = CScriptedIconLesson::LessonActionMap.Find( pchName );
if ( slot != CScriptedIconLesson::LessonActionMap.InvalidIndex() )
return CScriptedIconLesson::LessonActionMap[ slot ];
DevWarning( "Invalid scripted lesson action: %s\n", pchName );
return LESSON_ACTION_NONE;
}
void CScriptedIconLesson::InitElementsFromKeys( CUtlVector< LessonElement_t > *pLessonElements, KeyValues *pKey )
{
KeyValues *pSubKey = NULL;
for ( pSubKey = pKey->GetFirstSubKey(); pSubKey; pSubKey = pSubKey->GetNextKey() )
{
char szSubKeyName[ 256 ];
Q_strcpy( szSubKeyName, pSubKey->GetName() );
char *pchToken = strtok( szSubKeyName, " " );
LessonVariable iVariable = LessonVariableFromString( pchToken );
pchToken = strtok ( NULL, "" );
int iAction = LESSON_ACTION_NONE;
bool bNot = false;
bool bOptionalParam = false;
if ( !pchToken || pchToken[ 0 ] == '\0' )
{
DevWarning( "No action specified for variable: \"%s\"\n", pSubKey->GetName() );
}
else
{
if ( pchToken[ 0 ] == '?' )
{
pchToken++;
bOptionalParam = true;
}
if ( pchToken[ 0 ] == '!' )
{
pchToken++;
bNot = true;
}
iAction = LessonActionFromString( pchToken );
}
Q_strcpy( szSubKeyName, pSubKey->GetString() );
pchToken = strtok( szSubKeyName, " " );
_fieldtypes paramType = LessonParamTypeFromString( pchToken );
char *pchParam = "";
if ( paramType != FIELD_VOID )
{
pchToken = strtok ( NULL, "" );
pchParam = pchToken;
}
if ( !pchParam )
{
DevWarning( "No parameter specified for action: \"%s\"\n", pSubKey->GetName() );
}
else
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t\tElement \"%s %s\" added.\n", pSubKey->GetName(), pSubKey->GetString() );
}
// See if our param is a scripted var
LessonVariable iParamVarIndex = LessonVariableFromString( pchParam, false );
pLessonElements->AddToTail( LessonElement_t( iVariable, iAction, bNot, bOptionalParam, pchParam, iParamVarIndex, paramType ) );
}
}
}
void CScriptedIconLesson::InitElementsFromElements( CUtlVector< LessonElement_t > *pLessonElements, const CUtlVector< LessonElement_t > *pLessonElements2 )
{
for ( int i = 0; i < pLessonElements2->Count(); ++i )
{
pLessonElements->AddToTail( LessonElement_t( (*pLessonElements2)[ i ] ) );
}
}
void CScriptedIconLesson::InitFromKeys( KeyValues *pKey )
{
if ( !pKey )
return;
static int s_nInstanceTypeSymbol = KeyValuesSystem()->GetSymbolForString( "instance_type" );
static int s_nReplaceKeySymbol = KeyValuesSystem()->GetSymbolForString( "replace_key" );
static int s_nFixedInstancesMaxSymbol = KeyValuesSystem()->GetSymbolForString( "fixed_instances_max" );
static int s_nReplaceOnlyWhenStopped = KeyValuesSystem()->GetSymbolForString( "replace_only_when_stopped" );
static int s_nTeamSymbol = KeyValuesSystem()->GetSymbolForString( "team" );
static int s_nOnlyKeyboardSymbol = KeyValuesSystem()->GetSymbolForString( "only_keyboard" );
static int s_nOnlyGamepadSymbol = KeyValuesSystem()->GetSymbolForString( "only_gamepad" );
static int s_nDisplayLimitSymbol = KeyValuesSystem()->GetSymbolForString( "display_limit" );
static int s_nSuccessLimitSymbol = KeyValuesSystem()->GetSymbolForString( "success_limit" );
static int s_nPreReqSymbol = KeyValuesSystem()->GetSymbolForString( "prereq" );
static int s_nOpenSymbol = KeyValuesSystem()->GetSymbolForString( "open" );
static int s_nCloseSymbol = KeyValuesSystem()->GetSymbolForString( "close" );
static int s_nSuccessSymbol = KeyValuesSystem()->GetSymbolForString( "success" );
static int s_nOnOpenSymbol = KeyValuesSystem()->GetSymbolForString( "onopen" );
static int s_nUpdateSymbol = KeyValuesSystem()->GetSymbolForString( "update" );
KeyValues *pSubKey = NULL;
for ( pSubKey = pKey->GetFirstSubKey(); pSubKey; pSubKey = pSubKey->GetNextKey() )
{
if ( pSubKey->GetNameSymbol() == s_nInstanceTypeSymbol )
{
m_iInstanceType = LessonInstanceType( pSubKey->GetInt() );
}
else if ( pSubKey->GetNameSymbol() == s_nReplaceKeySymbol )
{
m_stringReplaceKey = pSubKey->GetString();
}
else if ( pSubKey->GetNameSymbol() == s_nFixedInstancesMaxSymbol )
{
m_iFixedInstancesMax = pSubKey->GetInt();
}
else if ( pSubKey->GetNameSymbol() == s_nReplaceOnlyWhenStopped )
{
m_bReplaceOnlyWhenStopped = pSubKey->GetBool();
}
else if ( pSubKey->GetNameSymbol() == s_nTeamSymbol )
{
m_iTeam = pSubKey->GetInt();
}
else if ( pSubKey->GetNameSymbol() == s_nOnlyKeyboardSymbol )
{
m_bOnlyKeyboard = pSubKey->GetBool();
}
else if ( pSubKey->GetNameSymbol() == s_nOnlyGamepadSymbol )
{
m_bOnlyGamepad = pSubKey->GetBool();
}
else if ( pSubKey->GetNameSymbol() == s_nDisplayLimitSymbol )
{
m_iDisplayLimit = pSubKey->GetInt();
}
else if ( pSubKey->GetNameSymbol() == s_nSuccessLimitSymbol )
{
m_iSuccessLimit = pSubKey->GetInt();
}
else if ( pSubKey->GetNameSymbol() == s_nPreReqSymbol )
{
CGameInstructorSymbol pName;
pName = pSubKey->GetString();
m_PrerequisiteNames.AddToTail( pName );
}
else if ( pSubKey->GetNameSymbol() == s_nOpenSymbol )
{
KeyValues *pEventKey = NULL;
for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() )
{
LessonEvent_t *pLessonEvent = AddOpenEvent();
pLessonEvent->szEventName = pEventKey->GetName();
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding open event " );
ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLessonEvent->szEventName.String());
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" );
}
InitElementsFromKeys( &(pLessonEvent->elements), pEventKey );
}
}
else if ( pSubKey->GetNameSymbol() == s_nCloseSymbol )
{
KeyValues *pEventKey = NULL;
for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() )
{
LessonEvent_t *pLessonEvent = AddCloseEvent();
pLessonEvent->szEventName = pEventKey->GetName();
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding close event " );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLessonEvent->szEventName.String());
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" );
}
InitElementsFromKeys( &(pLessonEvent->elements), pEventKey );
}
}
else if ( pSubKey->GetNameSymbol() == s_nSuccessSymbol )
{
KeyValues *pEventKey = NULL;
for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() )
{
LessonEvent_t *pLessonEvent = AddSuccessEvent();
pLessonEvent->szEventName = pEventKey->GetName();
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding success event " );
ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\"%s\" ", pLessonEvent->szEventName.String());
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" );
}
InitElementsFromKeys( &(pLessonEvent->elements), pEventKey );
}
}
else if ( pSubKey->GetNameSymbol() == s_nOnOpenSymbol )
{
KeyValues *pEventKey = NULL;
for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() )
{
LessonEvent_t *pLessonEvent = AddOnOpenEvent();
pLessonEvent->szEventName = pEventKey->GetName();
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding onopen event " );
ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLessonEvent->szEventName.String());
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" );
}
InitElementsFromKeys( &(pLessonEvent->elements), pEventKey );
}
}
else if ( pSubKey->GetNameSymbol() == s_nUpdateSymbol )
{
KeyValues *pEventKey = NULL;
for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() )
{
LessonEvent_t *pLessonEvent = AddUpdateEvent();
pLessonEvent->szEventName = pEventKey->GetName();
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding update event " );
ConColorMsg( CBaseLesson::m_rgbaVerboseUpdate, "\"%s\" ", pLessonEvent->szEventName.String());
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" );
}
InitElementsFromKeys( &(pLessonEvent->elements), pEventKey );
}
}
// Run intialize from key macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition)
#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_INIT
#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_INIT_BOOL
#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_INIT_EHANDLE
#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_INIT_STRING
LESSON_VARIABLE_FACTORY
#undef LESSON_VARIABLE_MACRO
#undef LESSON_VARIABLE_MACRO_BOOL
#undef LESSON_VARIABLE_MACRO_EHANDLE
#undef LESSON_VARIABLE_MACRO_STRING
}
}
bool CScriptedIconLesson::ProcessElements( IGameEvent *event, const CUtlVector< LessonElement_t > *pElements )
{
VPROF_BUDGET( "CScriptedIconLesson::ProcessElements", "GameInstructor" );
m_hLocalPlayer = GetGameInstructor().GetLocalPlayer();
bool bSuccess = true;
int nContinueScope = -1;
m_iScopeDepth = 0;
if ( gameinstructor_find_errors.GetBool() )
{
// Just run them all to check for errors!
for ( int iElement = 0; iElement < pElements->Count(); ++iElement )
{
ProcessElement( event, &((*pElements)[ iElement ] ), false );
}
return false;
}
// Process each element until a step fails
for ( int iElement = 0; iElement < pElements->Count(); ++iElement )
{
if ( nContinueScope == m_iScopeDepth )
{
nContinueScope = -1;
}
if ( !ProcessElement( event, &((*pElements)[ iElement ]), nContinueScope != -1 ) )
{
// This element failed
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tPrevious element returned false.\n" );
}
nContinueScope = m_iScopeDepth - 1;
if ( nContinueScope < 0 )
{
// No outer scope to worry about, we're done
bSuccess = false;
break;
}
}
}
return bSuccess;
}
bool CScriptedIconLesson::ProcessElement( IGameEvent *event, const LessonElement_t *pLessonElement, bool bInFailedScope )
{
VPROF_BUDGET( "CScriptedIconLesson::ProcessElement", "GameInstructor" );
if ( pLessonElement->iAction == LESSON_ACTION_SCOPE_IN )
{
// Special case for closing (we don't need variables for this)
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tScopeIn()\n" );
}
m_iScopeDepth++;
return true;
}
else if ( pLessonElement->iAction == LESSON_ACTION_SCOPE_OUT )
{
// Special case for closing (we don't need variables for this)
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tScopeOut()\n" );
}
m_iScopeDepth--;
return true;
}
if ( bInFailedScope )
{
// Only scope upkeep is done when we're in a failing scope... bail!
return true;
}
if ( pLessonElement->iAction == LESSON_ACTION_CLOSE )
{
// Special case for closing (we don't need variables for this)
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tCloseOpportunity()\n" );
}
CloseOpportunity( "Close action." );
return true;
}
else if ( pLessonElement->iAction == LESSON_ACTION_SUCCESS )
{
// Special case for succeeding (we don't need variables for this)
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tMarkSucceeded()\n" );
}
MarkSucceeded();
return true;
}
else if ( pLessonElement->iAction == LESSON_ACTION_LOCK )
{
// Special case for setting the starting point for the lesson to stay locked from (we don't need variables for this)
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tm_fLockTime = gpGlobals->curtime\n" );
}
m_fLockTime = gpGlobals->curtime;
return true;
}
else if ( pLessonElement->iAction == LESSON_ACTION_PRESENT_COMPLETE )
{
// Special case for checking presentation status (we don't need variables for this)
bool bPresentComplete = IsPresentComplete();
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tIsPresentComplete() " );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%s ", ( bPresentComplete ) ? ( "true" ) : ( "false" ) );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( pLessonElement->bNot ) ? ( "!= true\n" ) : ( "== true\n" ) );
}
return ( pLessonElement->bNot ) ? ( !bPresentComplete ) : ( bPresentComplete );
}
else if ( pLessonElement->iAction == LESSON_ACTION_PRESENT_START )
{
// Special case for setting presentation status (we don't need variables for this)
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tPresentStart()\n" );
}
PresentStart();
return true;
}
else if ( pLessonElement->iAction == LESSON_ACTION_PRESENT_END )
{
// Special case for setting presentation status (we don't need variables for this)
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tPresentEnd()\n" );
}
PresentEnd();
return true;
}
// These values temporarily hold the parameter's value
const char *pParamName = pLessonElement->szParam.String();
float eventParam_float = 0.0f;
char eventParam_string[ 256 ];
eventParam_string[ 0 ] = '\0';
C_BaseEntity *eventParam_BaseEntity = NULL;
// Get the value from the event parameter based on its type
switch ( pLessonElement->paramType )
{
case FIELD_FLOAT:
if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL )
{
// The parameter is a scripted var
const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex );
switch ( pInfo->varType )
{
case FIELD_FLOAT:
eventParam_float = LESSON_VARIABLE_GET_FROM_OFFSET( float, pInfo->iOffset );
break;
case FIELD_INTEGER:
eventParam_float = static_cast<float>( LESSON_VARIABLE_GET_FROM_OFFSET( int, pInfo->iOffset ) );
break;
case FIELD_BOOLEAN:
eventParam_float = static_cast<float>( LESSON_VARIABLE_GET_FROM_OFFSET( bool, pInfo->iOffset ) );
break;
case FIELD_STRING:
eventParam_float = static_cast<float>( atoi( &LESSON_VARIABLE_GET_FROM_OFFSET( CGameInstructorSymbol, pInfo->iOffset )->String() ) );
break;
case FIELD_EHANDLE:
case FIELD_FUNCTION:
DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" );
break;
}
}
else if ( event && !(event->IsEmpty( pParamName )) )
{
eventParam_float = event->GetFloat( pParamName );
}
else if ( pLessonElement->bOptionalParam )
{
// We don't want to interpret this and not finding the param is still ok
return true;
}
else if ( ( pParamName[ 0 ] >= '0' && pParamName[ 0 ] <= '9' ) || pParamName[ 0 ] == '-' || pParamName[ 0 ] == '.' )
{
// This param doesn't exist, try parsing the string
eventParam_float = Q_atof( pParamName );
}
else
{
DevWarning( "Invalid event field name and not a float \"%s\".\n", pParamName );
return false;
}
break;
case FIELD_INTEGER:
if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL )
{
// The parameter is a scripted var
const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex );
switch ( pInfo->varType )
{
case FIELD_FLOAT:
eventParam_float = static_cast<int>( LESSON_VARIABLE_GET_FROM_OFFSET( float, pInfo->iOffset ) );
break;
case FIELD_INTEGER:
eventParam_float = LESSON_VARIABLE_GET_FROM_OFFSET( int, pInfo->iOffset );
break;
case FIELD_BOOLEAN:
eventParam_float = static_cast<int>( LESSON_VARIABLE_GET_FROM_OFFSET( bool, pInfo->iOffset ) );
break;
case FIELD_STRING:
eventParam_float = atof( &LESSON_VARIABLE_GET_FROM_OFFSET( CGameInstructorSymbol, pInfo->iOffset )->String() );
break;
case FIELD_EHANDLE:
case FIELD_FUNCTION:
DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" );
break;
}
}
else if ( event && !(event->IsEmpty( pParamName )) )
{
eventParam_float = static_cast<float>( event->GetInt( pParamName ) );
}
else if ( pLessonElement->bOptionalParam )
{
// We don't want to interpret this and not finding the param is still ok
return true;
}
else if ( ( pParamName[ 0 ] >= '0' && pParamName[ 0 ] <= '9' ) || pParamName[ 0 ] == '-' )
{
// This param doesn't exist, try parsing the string
eventParam_float = static_cast<float>( Q_atoi( pParamName ) );
}
else
{
DevWarning( "Invalid event field name and not an integer \"%s\".\n", pParamName );
return false;
}
break;
case FIELD_STRING:
if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL )
{
// The parameter is a scripted var
const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex );
switch ( pInfo->varType )
{
case FIELD_STRING:
Q_strncpy( eventParam_string, &LESSON_VARIABLE_GET_FROM_OFFSET( CGameInstructorSymbol, pInfo->iOffset )->String(), sizeof( eventParam_string ) );
break;
case FIELD_FLOAT:
Q_snprintf( eventParam_string, sizeof( eventParam_string ), "%f", LESSON_VARIABLE_GET_FROM_OFFSET( float, pInfo->iOffset ) );
break;
case FIELD_INTEGER:
Q_snprintf( eventParam_string, sizeof( eventParam_string ), "%i", LESSON_VARIABLE_GET_FROM_OFFSET( int, pInfo->iOffset ) );
break;
case FIELD_BOOLEAN:
case FIELD_EHANDLE:
case FIELD_FUNCTION:
DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" );
break;
}
}
else
{
const char *pchEventString = NULL;
if ( event && !(event->IsEmpty( pParamName )) )
{
pchEventString = event->GetString( pParamName );
}
if ( pchEventString && pchEventString[0] )
{
Q_strcpy( eventParam_string, pchEventString );
}
else if ( pLessonElement->bOptionalParam )
{
// We don't want to interpret this and not finding the param is still ok
return true;
}
else
{
// This param doesn't exist, try parsing the string
Q_strncpy( eventParam_string, pParamName, sizeof( eventParam_string ) );
}
}
break;
case FIELD_BOOLEAN:
if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL )
{
// The parameter is a scripted var
const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex );
switch ( pInfo->varType )
{
case FIELD_FLOAT:
eventParam_float = ( ( LESSON_VARIABLE_GET_FROM_OFFSET( float, pInfo->iOffset ) ) ? ( 1.0f ) : ( 0.0f ) );
break;
case FIELD_INTEGER:
eventParam_float = ( ( LESSON_VARIABLE_GET_FROM_OFFSET( int, pInfo->iOffset ) ) ? ( 1.0f ) : ( 0.0f ) );
break;
case FIELD_BOOLEAN:
eventParam_float = ( ( LESSON_VARIABLE_GET_FROM_OFFSET( bool, pInfo->iOffset ) ) ? ( 1.0f ) : ( 0.0f ) );
break;
case FIELD_EHANDLE:
case FIELD_STRING:
case FIELD_FUNCTION:
DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" );
break;
}
}
else if ( event && !(event->IsEmpty( pParamName )) )
{
eventParam_float = ( event->GetBool( pParamName ) ) ? ( 1.0f ) : ( 0.0f );
}
else if ( pLessonElement->bOptionalParam )
{
// We don't want to interpret this and not finding the param is still ok
return true;
}
else if ( pParamName[ 0 ] == '0' || pParamName[ 0 ] == '1' )
{
// This param doesn't exist, try parsing the string
eventParam_float = Q_atof( pParamName ) != 0.0f;
}
else
{
DevWarning( "Invalid event field name and not an boolean \"%s\".\n", pParamName );
return false;
}
break;
case FIELD_CUSTOM:
if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL )
{
// The parameter is a scripted var
const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex );
switch ( pInfo->varType )
{
case FIELD_EHANDLE:
eventParam_BaseEntity = ( LESSON_VARIABLE_GET_FROM_OFFSET( EHANDLE, pInfo->iOffset ) ).Get();
if ( !eventParam_BaseEntity )
{
if ( pLessonElement->bOptionalParam )
{
// Not having an entity is fine
return true;
}
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tPlayer param \"%s\" returned NULL.\n", pParamName );
}
return false;
}
break;
case FIELD_FLOAT:
case FIELD_INTEGER:
case FIELD_BOOLEAN:
case FIELD_STRING:
case FIELD_FUNCTION:
DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" );
break;
}
}
else if ( event && !(event->IsEmpty( pParamName )) )
{
eventParam_BaseEntity = UTIL_PlayerByUserId( event->GetInt( pParamName ) );
if ( !eventParam_BaseEntity )
{
if ( pLessonElement->bOptionalParam )
{
// Not having an entity is fine
return true;
}
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tPlayer param \"%s\" returned NULL.\n", pParamName );
}
return false;
}
}
else if ( pLessonElement->bOptionalParam )
{
// We don't want to interpret this and not finding the param is still ok
return true;
}
else if ( Q_stricmp( pParamName, "null" ) == 0 )
{
// They explicitly want a null pointer
eventParam_BaseEntity = NULL;
}
else
{
DevWarning( "Invalid event field name \"%s\".\n", pParamName );
return false;
}
break;
case FIELD_EHANDLE:
if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL )
{
// The parameter is a scripted var
const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex );
switch ( pInfo->varType )
{
case FIELD_EHANDLE:
eventParam_BaseEntity = ( LESSON_VARIABLE_GET_FROM_OFFSET( EHANDLE, pInfo->iOffset ) ).Get();
if ( !eventParam_BaseEntity )
{
if ( pLessonElement->bOptionalParam )
{
// Not having an entity is fine
return true;
}
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tEntity param \"%s\" returned NULL.\n", pParamName );
}
return false;
}
break;
case FIELD_FLOAT:
case FIELD_INTEGER:
case FIELD_BOOLEAN:
case FIELD_STRING:
case FIELD_FUNCTION:
DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" );
break;
}
}
else if ( event && !(event->IsEmpty( pParamName )) )
{
int iEntID = event->GetInt( pParamName );
if ( iEntID >= NUM_ENT_ENTRIES )
{
AssertMsg( 0, "Invalid entity ID used in game event field!" );
DevWarning( "Invalid entity ID used in game event (%s) for param (%s).", event->GetName(), pParamName );
return false;
}
eventParam_BaseEntity = C_BaseEntity::Instance( iEntID );
if ( !eventParam_BaseEntity )
{
if ( pLessonElement->bOptionalParam )
{
// Not having an entity is fine
return true;
}
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tEntity param \"%s\" returned NULL.\n", pParamName );
}
return false;
}
}
else if ( pLessonElement->bOptionalParam )
{
// We don't want to interpret this and not finding the param is still ok
return true;
}
else if ( Q_stricmp( pParamName, "null" ) == 0 )
{
// They explicitly want a null pointer
eventParam_BaseEntity = NULL;
}
else if ( Q_stricmp( pParamName, "world" ) == 0 )
{
// They explicitly want the world
eventParam_BaseEntity = GetClientWorldEntity();
}
else
{
DevWarning( "Invalid event field name \"%s\".\n", pParamName );
return false;
}
break;
case FIELD_EMBEDDED:
{
// The parameter is a convar
ConVarRef tempCVar( pParamName );
if ( tempCVar.IsValid() )
{
eventParam_float = tempCVar.GetFloat();
Q_strncpy( eventParam_string, tempCVar.GetString(), sizeof( eventParam_string ) );
}
else
{
DevWarning( "Invalid convar name \"%s\".\n", pParamName );
return false;
}
}
break;
}
// Do the action to the specified variable
switch ( pLessonElement->iVariable )
{
// Run process action macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition)
#define LESSON_VARIABLE_MACRO PROCESS_LESSON_ACTION
#define LESSON_VARIABLE_MACRO_BOOL PROCESS_LESSON_ACTION
#define LESSON_VARIABLE_MACRO_EHANDLE PROCESS_LESSON_ACTION_EHANDLE
#define LESSON_VARIABLE_MACRO_STRING PROCESS_LESSON_ACTION_STRING
LESSON_VARIABLE_FACTORY;
#undef LESSON_VARIABLE_MACRO
#undef LESSON_VARIABLE_MACRO_BOOL
#undef LESSON_VARIABLE_MACRO_EHANDLE
#undef LESSON_VARIABLE_MACRO_STRING
}
return true;
}
bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, float &fVar, const CGameInstructorSymbol *pchParamName, float fParam )
{
switch ( iAction )
{
case LESSON_ACTION_SET:
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = [%s] ", pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam );
}
fVar = fParam;
return true;
case LESSON_ACTION_ADD:
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] += [%s] ", pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam );
}
fVar += fParam;
return true;
case LESSON_ACTION_SUBTRACT:
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] -= [%s] ", pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam );
}
fVar -= fParam;
return true;
case LESSON_ACTION_MULTIPLY:
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] *= [%s] ", pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam );
}
fVar *= fParam;
return true;
case LESSON_ACTION_IS:
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f ", fVar );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam );
}
return ( bNot ) ? ( fVar != fParam ) : ( fVar == fParam );
case LESSON_ACTION_LESS_THAN:
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f ", fVar );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam );
}
return ( bNot ) ? ( fVar >= fParam ) : ( fVar < fParam );
case LESSON_ACTION_HAS_BIT:
{
int iTemp1 = static_cast<int>( fVar );
int iTemp2 = ( 1 << static_cast<int>( fParam ) );
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t([%s] ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "0x%X ", iTemp1 );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "& [%s] ", pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "0x%X", iTemp2 );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") == 0\n" ) : ( ") != 0\n" ) );
}
return ( bNot ) ? ( ( iTemp1 & iTemp2 ) == 0 ) : ( ( iTemp1 & iTemp2 ) != 0 );
}
case LESSON_ACTION_BIT_COUNT_IS:
{
int iTemp1 = UTIL_CountNumBitsSet( static_cast<unsigned int>( fVar ) );
int iTemp2 = static_cast<int>( fParam );
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tUTIL_CountNumBitsSet([%s]) ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp1 );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " != [%s] " ) : ( " == [%s] " ), pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp2 );
}
return ( bNot ) ? ( iTemp1 != iTemp2 ) : ( iTemp1 == iTemp2 );
}
case LESSON_ACTION_BIT_COUNT_LESS_THAN:
{
int iTemp1 = UTIL_CountNumBitsSet( static_cast<unsigned int>( fVar ) );
int iTemp2 = static_cast<int>( fParam );
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tUTIL_CountNumBitsSet([%s]) ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp1 );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " >= [%s] " ) : ( " < [%s] " ), pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp2 );
}
return ( bNot ) ? ( iTemp1 >= iTemp2 ) : ( iTemp1 < iTemp2 );
}
}
DevWarning( "Invalid lesson action type used with \"%s\" variable type.\n", pchVarName );
return false;
}
bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, int &iVar, const CGameInstructorSymbol *pchParamName, float fParam )
{
float fTemp = static_cast<float>( iVar );
bool bRetVal = ProcessElementAction( iAction, bNot, pchVarName, fTemp, pchParamName, fParam );
iVar = static_cast<int>( fTemp );
return bRetVal;
}
bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, bool &bVar, const CGameInstructorSymbol *pchParamName, float fParam )
{
float fTemp = ( bVar ) ? ( 1.0f ) : ( 0.0f );
bool bRetVal = ProcessElementAction( iAction, bNot, pchVarName, fTemp, pchParamName, fParam );
bVar = ( fTemp != 0.0f );
return bRetVal;
}
bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, EHANDLE &hVar, const CGameInstructorSymbol *pchParamName, float fParam, C_BaseEntity *pParam, const char *pchParam )
{
// First try to let the mod act on the action
/*bool bModHandled = false;
bool bModReturn = Mod_ProcessElementAction( iAction, bNot, pchVarName, hVar, pchParamName, fParam, pParam, pchParam, bModHandled );
if ( bModHandled )
{
return bModReturn;
}*/
C_BaseEntity *pVar = hVar.Get();
switch ( iAction )
{
case LESSON_ACTION_SET:
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = [%s]\n", pchVarName, pchParamName->String() );
}
hVar = pParam;
return true;
}
case LESSON_ACTION_IS:
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t[%s] != [%s]\n" ) : ( "\t[%s] == [%s]\n" ), pchVarName, pchParamName->String() );
}
return ( bNot ) ? ( pVar != pParam ) : ( pVar == pParam );
case LESSON_ACTION_GET_DISTANCE:
{
if ( !pVar || !pParam )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->DistTo( [%s] )", pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "...\n" );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle or Param handle returned NULL!\n" );
}
return false;
}
C_BasePlayer *pVarPlayer = ( pVar->IsPlayer() ? static_cast< C_BasePlayer* >( pVar ) : NULL );
C_BasePlayer *pParamPlayer = ( pParam->IsPlayer() ? static_cast< C_BasePlayer* >( pParam ) : NULL );
Vector vVarPos = ( pVarPlayer ? pVarPlayer->EyePosition() : pVar->WorldSpaceCenter() );
Vector vParamPos = ( pParamPlayer ? pParamPlayer->EyePosition() : pParam->WorldSpaceCenter() );
m_fOutput = vVarPos.DistTo( vParamPos );
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->DistTo( [%s] ) ", pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", m_fOutput );
}
return true;
}
case LESSON_ACTION_GET_ANGULAR_DISTANCE:
{
if ( !pVar || !pParam )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->AngularDistTo( [%s] )", pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "...\n" );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle or Param handle returned NULL!\n" );
}
return false;
}
C_BasePlayer *pVarPlayer = ( pVar->IsPlayer() ? static_cast< C_BasePlayer* >( pVar ) : NULL );
C_BasePlayer *pParamPlayer = ( pParam->IsPlayer() ? static_cast< C_BasePlayer* >( pParam ) : NULL );
Vector vVarPos = ( pVarPlayer ? pVarPlayer->EyePosition() : pVar->WorldSpaceCenter() );
Vector vParamPos = ( pParamPlayer ? pParamPlayer->EyePosition() : pParam->WorldSpaceCenter() );
Vector vVarToParam = vParamPos - vVarPos;
VectorNormalize( vVarToParam );
Vector vVarForward;
if ( pVar->IsPlayer() )
{
AngleVectors( static_cast< C_BasePlayer* >( pVar )->EyeAngles(), &vVarForward, NULL, NULL );
}
else
{
pVar->GetVectors( &vVarForward, NULL, NULL );
}
// Set the distance in degrees
m_fOutput = ( vVarToParam.Dot( vVarForward ) - 1.0f ) * -90.0f;
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->AngularDistTo( [%s] ) ", pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", m_fOutput );
}
return true;
}
case LESSON_ACTION_GET_PLAYER_DISPLAY_NAME:
{
int iTemp = static_cast<int>( fParam );
if ( iTemp <= 0 || iTemp > 2 )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcpy( [stringINVALID], [%s]->GetPlayerName() ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam selecting string is out of range!\n" );
}
return false;
}
// Use string2 if it was specified, otherwise, use string1
CGameInstructorSymbol *pString;
char const *pchParamNameTemp = NULL;
if ( iTemp == 2 )
{
pString = &m_szString2;
pchParamNameTemp = "string2";
}
else
{
pString = &m_szString1;
pchParamNameTemp = "string1";
}
if ( !pVar )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcpy( [%s], [%s]->GetPlayerName() ", pchParamNameTemp, pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" );
}
return false;
}
*pString = pVar->GetPlayerName();
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcpy( [%s], [%s]->GetPlayerName() ", pchParamNameTemp, pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pString->String() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" );
}
return true;
}
case LESSON_ACTION_CLASSNAME_IS:
{
if ( !pVar )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t!FClassnameIs( [%s] " ) : ( "\tFClassnameIs( [%s] " ), pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "..." );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pchParam );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" );
}
return false;
}
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t!FClassnameIs( [%s] " ) : ( "\tFClassnameIs( [%s] " ), pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%s", pVar->GetClassname() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pchParam );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" );
}
return ( bNot ) ? ( !FClassnameIs( pVar, pchParam ) ) : ( FClassnameIs( pVar, pchParam ) );
}
case LESSON_ACTION_TEAM_IS:
{
int iTemp = static_cast<int>( fParam );
if ( !pVar )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetTeamNumber() ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" );
}
return false;
}
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetTeamNumber() ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", pVar->GetTeamNumber() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp );
}
return ( bNot ) ? ( pVar->GetTeamNumber() != iTemp ) : ( pVar->GetTeamNumber() == iTemp );
}
case LESSON_ACTION_MODELNAME_IS:
{
C_BaseAnimating *pBaseAnimating = dynamic_cast<C_BaseAnimating *>( pVar );
if ( !pBaseAnimating )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_stricmp( [%s]->ModelName() ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "..." );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pchParam );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") != 0\n" ) : ( ") == 0\n" ) );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseAnimating returned NULL!\n" );
}
return false;
}
const char *pchModelName = "-no model-";
CStudioHdr *pModel = pBaseAnimating->GetModelPtr();
if ( pModel )
{
const studiohdr_t *pRenderHDR = pModel->GetRenderHdr();
if ( pRenderHDR )
{
pchModelName = pRenderHDR->name;
}
}
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_stricmp( [%s]->ModelName() ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%s", pchModelName );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pchParam );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") != 0\n" ) : ( ") == 0\n" ) );
}
return ( bNot ) ? ( Q_stricmp( pchModelName, pchParam ) != 0 ) : ( Q_stricmp( pchModelName, pchParam ) == 0 );
}
case LESSON_ACTION_HEALTH_LESS_THAN:
{
int iTemp = static_cast<int>( fParam );
if ( !pVar )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetHealth() ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" );
}
return false;
}
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetHealth() ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", pVar->GetHealth() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp );
}
return ( bNot ) ? ( pVar->GetHealth() >= iTemp ) : ( pVar->GetHealth() < iTemp );
}
case LESSON_ACTION_HEALTH_PERCENTAGE_LESS_THAN:
{
if ( !pVar )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->HealthFraction() ", pchVarName, pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" );
}
return false;
}
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->HealthFraction() ", pchVarName, pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f ", pVar->HealthFraction() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam );
}
float fHealthPercentage = 1.0f;
if ( pVar->GetMaxHealth() != 0.0f )
{
fHealthPercentage = pVar->HealthFraction();
}
return ( bNot ) ? ( fHealthPercentage >= fParam ) : ( fHealthPercentage < fParam );
}
case LESSON_ACTION_GET_ACTIVE_WEAPON:
{
int iTemp = static_cast<int>( fParam );
if ( iTemp <= 0 || iTemp > 2 )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entityINVALID] = [%s]->GetActiveWeapon()\n", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam selecting string is out of range!\n" );
}
return false;
}
// Use entity2 if it was specified, otherwise, use entity1
CHandle<C_BaseEntity> *pHandle;
char const *pchParamNameTemp = NULL;
if ( iTemp == 2 )
{
pHandle = &m_hEntity2;
pchParamNameTemp = "entity2";
}
else
{
pHandle = &m_hEntity1;
pchParamNameTemp = "entity1";
}
C_BaseCombatCharacter *pBaseCombatCharacter = NULL;
if ( pVar )
{
pBaseCombatCharacter = pVar->MyCombatCharacterPointer();
}
if ( !pBaseCombatCharacter )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = [%s]->GetActiveWeapon()", pchParamNameTemp, pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" );
}
return false;
}
pHandle->Set( pBaseCombatCharacter->GetActiveWeapon() );
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = [%s]->GetActiveWeapon()", pchParamNameTemp, pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"\n", pchParam );
}
return true;
}
case LESSON_ACTION_WEAPON_IS:
{
C_BaseCombatCharacter *pBaseCombatCharacter = NULL;
if ( pVar )
{
pBaseCombatCharacter = pVar->MyCombatCharacterPointer();
}
if ( !pBaseCombatCharacter )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->GetName() ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"\n", pchParam );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" );
}
return false;
}
CBaseCombatWeapon *pBaseCombatWeapon = pBaseCombatCharacter->GetActiveWeapon();
if ( !pBaseCombatWeapon )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->GetName() ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"\n", pchParam );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetActiveWeapon returned NULL!\n" );
}
return false;
}
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->GetName() ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pBaseCombatWeapon->GetName() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"\n", pchParam );
}
return ( bNot ) ? ( Q_stricmp( pBaseCombatWeapon->GetName(), pchParam ) != 0 ) : ( Q_stricmp( pBaseCombatWeapon->GetName(), pchParam ) == 0 );
}
case LESSON_ACTION_WEAPON_HAS:
{
C_BaseCombatCharacter *pBaseCombatCharacter = NULL;
if ( pVar )
{
pBaseCombatCharacter = pVar->MyCombatCharacterPointer();
}
if ( !pBaseCombatCharacter )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_OwnsThisType([%s] " ) : ( "\t[%s]->Weapon_OwnsThisType([%s] " ), pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" );
}
return false;
}
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_OwnsThisType([%s] " ) : ( "\t[%s]->Weapon_OwnsThisType([%s] " ), pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" );
}
return ( bNot ) ? ( pBaseCombatCharacter->Weapon_OwnsThisType( pchParam ) == NULL ) : ( pBaseCombatCharacter->Weapon_OwnsThisType( pchParam ) != NULL );
}
case LESSON_ACTION_GET_ACTIVE_WEAPON_SLOT:
{
C_BaseCombatCharacter *pBaseCombatCharacter = NULL;
if ( pVar )
{
pBaseCombatCharacter = pVar->MyCombatCharacterPointer();
}
if ( !pBaseCombatCharacter )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetActiveSlot() ...\n", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" );
}
return false;
}
C_BaseCombatWeapon *pWeapon = pBaseCombatCharacter->GetActiveWeapon();
if ( !pWeapon )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetActiveSlot() ...\n", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetActiveWeapon returned NULL!\n" );
}
return false;
}
// @TODO
/*m_fOutput = pBaseCombatCharacter->Weapon_GetSlot( pWeapon->GetWpnData().szClassName );
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetSlot() ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", m_fOutput );
}*/
return true;
}
case LESSON_ACTION_GET_WEAPON_SLOT:
{
C_BaseCombatCharacter *pBaseCombatCharacter = NULL;
if ( pVar )
{
pBaseCombatCharacter = pVar->MyCombatCharacterPointer();
}
if ( !pBaseCombatCharacter )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetSlot([%s] ", pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ") ...\n" );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" );
}
return false;
}
// @TODO
/*m_fOutput = pBaseCombatCharacter->Weapon_GetSlot( pchParam );
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetSlot([%s] ", pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ") " );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", m_fOutput );
}*/
return true;
}
case LESSON_ACTION_GET_WEAPON_IN_SLOT:
{
int nTemp = static_cast<int>( fParam );
C_BaseCombatCharacter *pBaseCombatCharacter = NULL;
if ( pVar )
{
pBaseCombatCharacter = pVar->MyCombatCharacterPointer();
}
if ( !pBaseCombatCharacter )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entity1] = [%s]->GetWeapon([%s] ", pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%i\"", nTemp );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" );
}
return false;
}
m_hEntity1 = pBaseCombatCharacter->GetWeapon( nTemp );
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entity1] = [%s]->GetWeapon([%s] ", pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%i\"", nTemp );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" );
}
return true;
}
case LESSON_ACTION_CLIP_PERCENTAGE_LESS_THAN:
{
C_BaseCombatCharacter *pBaseCombatCharacter = NULL;
if ( pVar )
{
pBaseCombatCharacter = pVar->MyCombatCharacterPointer();
}
if ( !pBaseCombatCharacter )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->Clip1Percentage() ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%.1f\n", fParam );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" );
}
return false;
}
CBaseCombatWeapon *pBaseCombatWeapon = pBaseCombatCharacter->GetActiveWeapon();
if ( !pBaseCombatWeapon )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->Clip1Percentage() ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%.1f\n", fParam );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetActiveWeapon returned NULL!\n" );
}
return false;
}
float fClip1Percentage = 100.0f;
if ( pBaseCombatWeapon->UsesClipsForAmmo1() )
{
fClip1Percentage = 100.0f * ( static_cast<float>( pBaseCombatWeapon->Clip1() ) / static_cast<float>( pBaseCombatWeapon->GetMaxClip1() ) );
}
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->Clip1Percentage() ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%.1f ", fClip1Percentage );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%.1f\n", fParam );
}
return ( bNot ) ? ( fClip1Percentage >= fParam ) : ( fClip1Percentage < fParam );
}
case LESSON_ACTION_WEAPON_AMMO_LOW:
{
int iTemp = static_cast<int>( fParam );
C_BasePlayer *pBasePlayer = ToBasePlayer( pVar );
if ( !pBasePlayer )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetWeaponInSlot( ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ")->AmmoPercentage() >= 30\n" ) : ( ")->AmmoPercentage() < 30\n" ) );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BasePlayer returned NULL!\n" );
}
return false;
}
CBaseCombatWeapon *pBaseCombatWeapon = NULL;
// Get the weapon in variable slot
for ( int iWeapon = 0; iWeapon < MAX_WEAPONS; iWeapon++ )
{
CBaseCombatWeapon *pBaseCombatWeaponTemp = pBasePlayer->GetWeapon( iWeapon );
if ( pBaseCombatWeaponTemp )
{
if ( pBaseCombatWeaponTemp->GetSlot() == iTemp )
{
pBaseCombatWeapon = pBaseCombatWeaponTemp;
break;
}
}
}
if ( !pBaseCombatWeapon )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetWeaponInSlot( ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ")->AmmoPercentage() >= 30\n" ) : ( ")->AmmoPercentage() < 30\n" ) );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetActiveWeapon returned NULL!\n" );
}
return false;
}
// Check if the ammo is full
int iAmmoType = pBaseCombatWeapon->GetPrimaryAmmoType();
int iMaxAmmo = GetAmmoDef()->MaxCarry( iAmmoType/*, pBasePlayer*/ );
int iPlayerAmmo = pBasePlayer->GetAmmoCount( iAmmoType );
bool bAmmoLow = ( iPlayerAmmo < ( iMaxAmmo / 3 ) );
if ( bNot )
{
bAmmoLow = !bAmmoLow;
}
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetWeaponInSlot( ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ")->AmmoPercentage() >= 30 " ) : ( ")->AmmoPercentage() < 30 " ) );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bAmmoLow ) ? ( "true\n" ) : ( "false\n" ) );
}
return bAmmoLow;
}
case LESSON_ACTION_WEAPON_AMMO_FULL:
{
int iTemp = static_cast<int>( fParam );
C_BasePlayer *pBasePlayer = ToBasePlayer( pVar );
if ( !pBasePlayer )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoFull()\n" );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BasePlayer returned NULL!\n" );
}
return false;
}
CBaseCombatWeapon *pBaseCombatWeapon = NULL;
// Get the weapon in variable slot
for ( int iWeapon = 0; iWeapon < MAX_WEAPONS; iWeapon++ )
{
CBaseCombatWeapon *pBaseCombatWeaponTemp = pBasePlayer->GetWeapon( iWeapon );
if ( pBaseCombatWeaponTemp )
{
if ( pBaseCombatWeaponTemp->GetSlot() == iTemp )
{
pBaseCombatWeapon = pBaseCombatWeaponTemp;
break;
}
}
}
if ( !pBaseCombatWeapon )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoFull()\n" );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetWeaponInSlot returned NULL!\n" );
}
return false;
}
// Check if the ammo is full
int iAmmoType = pBaseCombatWeapon->GetPrimaryAmmoType();
int iMaxAmmo = GetAmmoDef()->MaxCarry( iAmmoType/*, pBasePlayer*/ );
int iPlayerAmmo = pBasePlayer->GetAmmoCount( iAmmoType );
bool bAmmoFull = ( iPlayerAmmo >= iMaxAmmo );
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoFull() " );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, ( bAmmoFull ) ? ( "true\n" ) : ( "false\n" ) );
}
return ( bNot ) ? ( !bAmmoFull ) : ( bAmmoFull );
}
case LESSON_ACTION_WEAPON_AMMO_EMPTY:
{
int iTemp = static_cast<int>( fParam );
C_BasePlayer *pBasePlayer = ToBasePlayer( pVar );
if ( !pBasePlayer )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoEmpty()\n" );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BasePlayer returned NULL!\n" );
}
return false;
}
CBaseCombatWeapon *pBaseCombatWeapon = NULL;
// Get the weapon in variable slot
for ( int iWeapon = 0; iWeapon < MAX_WEAPONS; iWeapon++ )
{
CBaseCombatWeapon *pBaseCombatWeaponTemp = pBasePlayer->GetWeapon( iWeapon );
if ( pBaseCombatWeaponTemp )
{
if ( pBaseCombatWeaponTemp->GetSlot() == iTemp )
{
pBaseCombatWeapon = pBaseCombatWeaponTemp;
break;
}
}
}
if ( !pBaseCombatWeapon )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoEmpty()\n" );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetWeaponInSlot returned NULL!\n" );
}
return false;
}
// Check if the ammo is empty
int iAmmoType = pBaseCombatWeapon->GetPrimaryAmmoType();
int iPlayerAmmo = pBasePlayer->GetAmmoCount( iAmmoType );
bool bAmmoEmpty = ( iPlayerAmmo <= 0 );
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoEmpty() " );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, ( bAmmoEmpty ) ? ( "true" ) : ( "false" ) );
ConColorMsg(CBaseLesson::m_rgbaVerbosePlain, " )\n" );
}
return ( bNot ) ? ( !bAmmoEmpty ) : ( bAmmoEmpty );
}
/*case LESSON_ACTION_WEAPON_CAN_USE:
{
C_BaseCombatWeapon *pBaseCombatWeapon = dynamic_cast<C_BaseCombatWeapon*>( pParam );
C_BasePlayer *pBasePlayer = ToBasePlayer( pVar );
if ( !pBasePlayer )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_CanUse([%s])\n" ) : ( "\t[%s]->Weapon_CanUse([%s])\n" ), pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BasePlayer returned NULL!\n" );
}
return false;
}
if ( !pBaseCombatWeapon )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_CanUse([%s])\n" ) : ( "\t[%s]->Weapon_CanUse([%s])\n" ), pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam BaseCombatWeapon returned NULL!\n" );
}
return false;
}
bool bCanEquip = pBasePlayer->Weapon_CanUse( pBaseCombatWeapon );
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_CanUse([%s]) " ) : ( "\t[%s]->Weapon_CanUse([%s]) " ), pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, ( bCanEquip ) ? ( "true\n" ) : ( "false\n" ) );
}
return ( bNot ) ? ( !bCanEquip ) : ( bCanEquip );
}*/
case LESSON_ACTION_USE_TARGET_IS:
{
C_BasePlayer *pBasePlayer = ToBasePlayer( pVar );
if ( !pBasePlayer )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\tC_BaseEntity::Instance([%s]->GetUseEntity()) != [%s]\n" ) : ( "\tC_BaseEntity::Instance([%s]->GetUseEntity()) == [%s]\n" ), pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as Player returned NULL!\n" );
}
return false;
}
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\tC_BaseEntity::Instance([%s]->GetUseEntity()) != [%s]\n" ) : ( "\tC_BaseEntity::Instance([%s]->GetUseEntity()) == [%s]\n" ), pchVarName, pchParamName->String() );
}
return ( bNot ) ? ( C_BaseEntity::Instance( pBasePlayer->GetUseEntity() ) != pParam ) : ( C_BaseEntity::Instance( pBasePlayer->GetUseEntity() ) == pParam );
}
case LESSON_ACTION_GET_USE_TARGET:
{
int iTemp = static_cast<int>( fParam );
if ( iTemp <= 0 || iTemp > 2 )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entityINVALID] = C_BaseEntity::Instance([%s]->GetUseEntity())\n", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam selecting string is out of range!\n" );
}
return false;
}
// Use entity2 if it was specified, otherwise, use entity1
CHandle<C_BaseEntity> *pHandle;
char const *pchParamNameTemp = NULL;
if ( iTemp == 2 )
{
pHandle = &m_hEntity2;
pchParamNameTemp = "entity2";
}
else
{
pHandle = &m_hEntity1;
pchParamNameTemp = "entity1";
}
C_BasePlayer *pBasePlayer = ToBasePlayer( pVar );
if ( !pBasePlayer )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = C_BaseEntity::Instance([%s]->GetUseEntity())\n", pchParamNameTemp, pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as Player returned NULL!\n" );
}
return false;
}
pHandle->Set( C_BaseEntity::Instance( pBasePlayer->GetUseEntity() ) );
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = C_BaseEntity::Instance([%s]->GetUseEntity())\n", pchParamNameTemp, pchVarName );
}
return true;
}
/*case LESSON_ACTION_GET_POTENTIAL_USE_TARGET:
{
int iTemp = static_cast<int>( fParam );
if ( iTemp <= 0 || iTemp > 2 )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entityINVALID] = C_BaseEntity::Instance([%s]->GetPotentialUseEntity())\n", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam selecting string is out of range!\n" );
}
return false;
}
// Use entity2 if it was specified, otherwise, use entity1
CHandle<C_BaseEntity> *pHandle;
char const *pchParamNameTemp = NULL;
if ( iTemp == 2 )
{
pHandle = &m_hEntity2;
pchParamNameTemp = "entity2";
}
else
{
pHandle = &m_hEntity1;
pchParamNameTemp = "entity1";
}
C_BasePlayer *pBasePlayer = ToBasePlayer( pVar );
if ( !pBasePlayer )
{
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = C_BaseEntity::Instance([%s]->GetPotentialUseEntity())\n", pchParamNameTemp, pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as Player returned NULL!\n" );
}
return false;
}
pHandle->Set( C_BaseEntity::Instance( pBasePlayer->GetPotentialUseEntity() ) );
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = C_BaseEntity::Instance([%s]->GetPotentialUseEntity())\n", pchParamNameTemp, pchVarName );
}
return true;
}*/
}
DevWarning( "Invalid lesson action type used with \"%s\" variable type.\n", pchVarName );
return false;
}
bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, CGameInstructorSymbol *pchVar, const CGameInstructorSymbol *pchParamName, const char *pchParam )
{
switch ( iAction )
{
case LESSON_ACTION_REFERENCE_OPEN:
{
const CBaseLesson *pLesson = GetGameInstructor().GetLesson( pchParamName->String() );
if ( !pLesson )
{
DevWarning( "Invalid lesson specified: \"%s\".", pchParamName->String() );
return false;
}
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( Color( 64, 128, 255, 255 ), ( bNot ) ? ( "\t!( [\"%s\"]->IsInstanceActive() " ) : ( "\t( [\"%s\"]->IsInstanceActive() " ), pchParamName->String() );
ConColorMsg( Color( 255, 255, 255, 255 ), "\"%s\"", (pLesson->IsInstanceActive() ? "true" : "false") );
ConColorMsg( Color( 64, 128, 255, 255 ), " )\n" );
}
return ( bNot ) ? ( !pLesson->IsInstanceActive() ) : ( pLesson->IsInstanceActive() );
}
case LESSON_ACTION_SET:
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcpy([%s], [%s] ", pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" );
}
*pchVar = pchParam;
return true;
case LESSON_ACTION_ADD:
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcat([%s], [%s] ", pchVarName, pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" );
}
char szTemp[ 256 ];
Q_strncpy( szTemp, pchVar->String(), sizeof( szTemp ) );
Q_strncat( szTemp, pchParam, sizeof( szTemp ) );
*pchVar = szTemp;
return true;
case LESSON_ACTION_IS:
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcmp([%s] ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchVar->String() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") != 0\n" ) : ( ") == 0\n" ) );
}
return ( bNot ) ? ( Q_strcmp( pchVar->String(), pchParam ) != 0 ) : ( Q_strcmp( pchVar->String(), pchParam ) == 0 );
case LESSON_ACTION_HAS_PREFIX:
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tStringHasPrefix([%s] ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchVar->String() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") == false\n" ) : ( ") == true\n" ) );
}
return ( bNot ) ? ( !StringHasPrefix( pchVar->String(), pchParam ) ) : ( StringHasPrefix( pchVar->String(), pchParam ) );
case LESSON_ACTION_LESS_THAN:
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
{
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcmp([%s] ", pchVarName );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\"%s\"", pchVar->String() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\"%s\"", pchParam );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") >= 0\n" ) : ( ") < 0\n" ) );
}
return ( bNot ) ? ( Q_strcmp( pchVar->String(), pchParam ) >= 0 ) : ( Q_strcmp( pchVar->String(), pchParam ) < 0 );
}
DevWarning( "Invalid lesson action type used with \"%s\" variable type.\n", pchVarName );
return false;
}
LessonEvent_t * CScriptedIconLesson::AddOpenEvent()
{
int iNewLessonEvent = m_OpenEvents.AddToTail();
return &(m_OpenEvents[ iNewLessonEvent ]);
}
LessonEvent_t * CScriptedIconLesson::AddCloseEvent()
{
int iNewLessonEvent = m_CloseEvents.AddToTail();
return &(m_CloseEvents[ iNewLessonEvent ]);
}
LessonEvent_t * CScriptedIconLesson::AddSuccessEvent()
{
int iNewLessonEvent = m_SuccessEvents.AddToTail();
return &(m_SuccessEvents[ iNewLessonEvent ]);
}
LessonEvent_t * CScriptedIconLesson::AddOnOpenEvent()
{
int iNewLessonEvent = m_OnOpenEvents.AddToTail();
return &(m_OnOpenEvents[ iNewLessonEvent ]);
}
LessonEvent_t * CScriptedIconLesson::AddUpdateEvent()
{
int iNewLessonEvent = m_UpdateEvents.AddToTail();
return &(m_UpdateEvents[ iNewLessonEvent ]);
}
// Static method to init the keyvalues symbols used for comparisons
void CScriptedIconLesson::PreReadLessonsFromFile()
{
static bool bFirstTime = true;
if ( !bFirstTime )
return;
bFirstTime = false;
// Run init info call macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition)
#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_INIT_SYMBOL
#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_INIT_SYMBOL
#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_INIT_SYMBOL
#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_INIT_SYMBOL
LESSON_VARIABLE_FACTORY
#undef LESSON_VARIABLE_MACRO
#undef LESSON_VARIABLE_MACRO_BOOL
#undef LESSON_VARIABLE_MACRO_EHANDLE
#undef LESSON_VARIABLE_MACRO_STRING
// And build the map of variable name to enum
// Run string to int macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition)
#define LESSON_VARIABLE_MACRO LESSON_SCRIPT_STRING_ADD_TO_MAP
#define LESSON_VARIABLE_MACRO_BOOL LESSON_SCRIPT_STRING_ADD_TO_MAP
#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_SCRIPT_STRING_ADD_TO_MAP
#define LESSON_VARIABLE_MACRO_STRING LESSON_SCRIPT_STRING_ADD_TO_MAP
LESSON_VARIABLE_FACTORY
#undef LESSON_VARIABLE_MACRO
#undef LESSON_VARIABLE_MACRO_BOOL
#undef LESSON_VARIABLE_MACRO_EHANDLE
#undef LESSON_VARIABLE_MACRO_STRING
// Set up mapping of field types
g_TypeToParamTypeMap.Insert( "float", FIELD_FLOAT );
g_TypeToParamTypeMap.Insert( "string", FIELD_STRING );
g_TypeToParamTypeMap.Insert( "int", FIELD_INTEGER );
g_TypeToParamTypeMap.Insert( "integer", FIELD_INTEGER );
g_TypeToParamTypeMap.Insert( "short", FIELD_INTEGER );
g_TypeToParamTypeMap.Insert( "long", FIELD_INTEGER );
g_TypeToParamTypeMap.Insert( "bool", FIELD_BOOLEAN );
g_TypeToParamTypeMap.Insert( "player", FIELD_CUSTOM );
g_TypeToParamTypeMap.Insert( "entity", FIELD_EHANDLE );
g_TypeToParamTypeMap.Insert( "convar", FIELD_EMBEDDED );
g_TypeToParamTypeMap.Insert( "void", FIELD_VOID );
// Set up the lesson action map
CScriptedIconLesson::LessonActionMap.Insert( "scope in", LESSON_ACTION_SCOPE_IN );
CScriptedIconLesson::LessonActionMap.Insert( "scope out", LESSON_ACTION_SCOPE_OUT );
CScriptedIconLesson::LessonActionMap.Insert( "close", LESSON_ACTION_CLOSE );
CScriptedIconLesson::LessonActionMap.Insert( "success", LESSON_ACTION_SUCCESS );
CScriptedIconLesson::LessonActionMap.Insert( "lock", LESSON_ACTION_LOCK );
CScriptedIconLesson::LessonActionMap.Insert( "present complete", LESSON_ACTION_PRESENT_COMPLETE );
CScriptedIconLesson::LessonActionMap.Insert( "present start", LESSON_ACTION_PRESENT_START );
CScriptedIconLesson::LessonActionMap.Insert( "present end", LESSON_ACTION_PRESENT_END );
CScriptedIconLesson::LessonActionMap.Insert( "reference open", LESSON_ACTION_REFERENCE_OPEN );
CScriptedIconLesson::LessonActionMap.Insert( "set", LESSON_ACTION_SET );
CScriptedIconLesson::LessonActionMap.Insert( "add", LESSON_ACTION_ADD );
CScriptedIconLesson::LessonActionMap.Insert( "subtract", LESSON_ACTION_SUBTRACT );
CScriptedIconLesson::LessonActionMap.Insert( "multiply", LESSON_ACTION_MULTIPLY );
CScriptedIconLesson::LessonActionMap.Insert( "is", LESSON_ACTION_IS );
CScriptedIconLesson::LessonActionMap.Insert( "less than", LESSON_ACTION_LESS_THAN );
CScriptedIconLesson::LessonActionMap.Insert( "has prefix", LESSON_ACTION_HAS_PREFIX );
CScriptedIconLesson::LessonActionMap.Insert( "has bit", LESSON_ACTION_HAS_BIT );
CScriptedIconLesson::LessonActionMap.Insert( "bit count is", LESSON_ACTION_BIT_COUNT_IS );
CScriptedIconLesson::LessonActionMap.Insert( "bit count less than", LESSON_ACTION_BIT_COUNT_LESS_THAN );
CScriptedIconLesson::LessonActionMap.Insert( "get distance", LESSON_ACTION_GET_DISTANCE );
CScriptedIconLesson::LessonActionMap.Insert( "get angular distance", LESSON_ACTION_GET_ANGULAR_DISTANCE );
CScriptedIconLesson::LessonActionMap.Insert( "get player display name", LESSON_ACTION_GET_PLAYER_DISPLAY_NAME );
CScriptedIconLesson::LessonActionMap.Insert( "classname is", LESSON_ACTION_CLASSNAME_IS );
CScriptedIconLesson::LessonActionMap.Insert( "modelname is", LESSON_ACTION_MODELNAME_IS );
CScriptedIconLesson::LessonActionMap.Insert( "team is", LESSON_ACTION_TEAM_IS );
CScriptedIconLesson::LessonActionMap.Insert( "health less than", LESSON_ACTION_HEALTH_LESS_THAN );
CScriptedIconLesson::LessonActionMap.Insert( "health percentage less than", LESSON_ACTION_HEALTH_PERCENTAGE_LESS_THAN );
CScriptedIconLesson::LessonActionMap.Insert( "get active weapon", LESSON_ACTION_GET_ACTIVE_WEAPON );
CScriptedIconLesson::LessonActionMap.Insert( "weapon is", LESSON_ACTION_WEAPON_IS );
CScriptedIconLesson::LessonActionMap.Insert( "weapon has", LESSON_ACTION_WEAPON_HAS );
CScriptedIconLesson::LessonActionMap.Insert( "get active weapon slot", LESSON_ACTION_GET_ACTIVE_WEAPON_SLOT );
CScriptedIconLesson::LessonActionMap.Insert( "get weapon slot", LESSON_ACTION_GET_WEAPON_SLOT );
CScriptedIconLesson::LessonActionMap.Insert( "get weapon in slot", LESSON_ACTION_GET_WEAPON_IN_SLOT );
CScriptedIconLesson::LessonActionMap.Insert( "clip percentage less than", LESSON_ACTION_CLIP_PERCENTAGE_LESS_THAN);
CScriptedIconLesson::LessonActionMap.Insert( "weapon ammo low", LESSON_ACTION_WEAPON_AMMO_LOW );
CScriptedIconLesson::LessonActionMap.Insert( "weapon ammo full", LESSON_ACTION_WEAPON_AMMO_FULL );
CScriptedIconLesson::LessonActionMap.Insert( "weapon ammo empty", LESSON_ACTION_WEAPON_AMMO_EMPTY );
CScriptedIconLesson::LessonActionMap.Insert( "weapon can use", LESSON_ACTION_WEAPON_CAN_USE );
CScriptedIconLesson::LessonActionMap.Insert( "use target is", LESSON_ACTION_USE_TARGET_IS );
CScriptedIconLesson::LessonActionMap.Insert( "get use target", LESSON_ACTION_GET_USE_TARGET );
CScriptedIconLesson::LessonActionMap.Insert( "get potential use target", LESSON_ACTION_GET_POTENTIAL_USE_TARGET );
// Add mod actions to the map
//Mod_PreReadLessonsFromFile();
}
C_GameInstructor
Y ahora si, esta clase es el Game Instructor encargada de leer los archivos que contienen las lecciones por aprender, guardarlas, actualizar la información, mostrarlas, etc... Al igual que antes, debemos agregar estos 2 archivos en el Cliente:
c_gameinstructor.h
//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============//
//
// Purpose: Client handler for instruction players how to play
//
//=============================================================================//
#ifndef _C_GAMEINSTRUCTOR_H_
#define _C_GAMEINSTRUCTOR_H_
#include "GameEventListener.h"
#include "vgui_controls/phandle.h"
class CBaseLesson;
struct LessonGroupConVarToggle_t
{
ConVarRef var;
char szLessonGroupName[ 64 ];
LessonGroupConVarToggle_t( const char *pchConVarName ) :
var( pchConVarName )
{
}
};
class C_GameInstructor : public CAutoGameSystemPerFrame, public CGameEventListener
{
public:
C_GameInstructor() : CAutoGameSystemPerFrame( "C_GameInstructor" )
{
m_bHasLoadedSaveData = false;
m_bDirtySaveData = false;
}
// Methods of IGameSystem
virtual bool Init( void );
virtual void Shutdown( void );
virtual void Update( float frametime );
void UpdateHiddenByOtherElements( void );
bool Mod_HiddenByOtherElements( void );
virtual void FireGameEvent( IGameEvent *event );
void DefineLesson( CBaseLesson *pLesson );
const CBaseLesson * GetLesson( const char *pchLessonName );
bool IsLessonOfSameTypeOpen( const CBaseLesson *pLesson ) const;
bool ReadSaveData( void );
bool WriteSaveData( void );
void RefreshDisplaysAndSuccesses( void );
void ResetDisplaysAndSuccesses( void );
void MarkDisplayed( const char *pchLessonName );
void MarkSucceeded( const char *pchLessonName );
void PlaySound( const char *pchSoundName );
bool OpenOpportunity( CBaseLesson *pLesson );
void DumpOpenOpportunities( void );
KeyValues * GetScriptKeys( void );
C_BasePlayer * GetLocalPlayer( void );
void EvaluateLessonsForGameRules( void );
void SetLessonGroupEnabled( const char *pszGroup, bool bEnabled );
private:
void FindErrors( void );
bool UpdateActiveLesson( CBaseLesson *pLesson, const CBaseLesson *pRootLesson );
void UpdateInactiveLesson( CBaseLesson *pLesson );
CBaseLesson * GetLesson_Internal( const char *pchLessonName );
void StopAllLessons( void );
void CloseAllOpenOpportunities( void );
void CloseOpportunity( CBaseLesson *pLesson );
void ReadLessonsFromFile( const char *pchFileName );
void InitLessonPrerequisites( void );
private:
CUtlVector < CBaseLesson* > m_Lessons;
CUtlVector < CBaseLesson* > m_OpenOpportunities;
CUtlVector < LessonGroupConVarToggle_t > m_LessonGroupConVarToggles;
KeyValues *m_pScriptKeys;
bool m_bNoDraw;
bool m_bHiddenDueToOtherElements;
int m_iCurrentPriority;
EHANDLE m_hLastSpectatedPlayer;
bool m_bSpectatedPlayerChanged;
char m_szPreviousStartSound[ 128 ];
float m_fNextStartSoundTime;
bool m_bHasLoadedSaveData;
bool m_bDirtySaveData;
};
C_GameInstructor &GetGameInstructor();
void GameInstructor_Init();
void GameInstructor_Shutdown();
#endif // _C_GAMEINSTRUCTOR_H_
c_gameinstructor.cpp
//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============//
//
// Purpose: Client handler implementations for instruction players how to play
//
//=============================================================================//
#include "cbase.h"
#include "c_gameinstructor.h"
#include "c_baselesson.h"
#include "filesystem.h"
#include "vprof.h"
#include "ixboxsystem.h"
#include "tier0/icommandline.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"
//=========================================================
// Configuración
//=========================================================
#define MOD_DIR "MOD"
#define GAMEINSTRUCTOR_SCRIPT_FILE "scripts/instructor_lessons.txt"
#define GAMEINSTRUCTOR_MOD_SCRIPT_FILE "scripts/mod_lessons.txt"
// Game instructor auto game system instantiation
C_GameInstructor g_GameInstructor;
C_GameInstructor &GetGameInstructor()
{
return g_GameInstructor;
}
void GameInstructorEnable_ChangeCallback( IConVar *var, const char *pOldValue, float flOldValue );
void SVGameInstructorDisable_ChangeCallback( IConVar *var, const char *pOldValue, float flOldValue );
extern ConVar sv_gameinstructor_disable;
//=========================================================
// Comandos de consola
//=========================================================
ConVar gameinstructor_verbose("gameinstructor_verbose", "1", FCVAR_CHEAT, "Set to 1 for standard debugging or 2 (in combo with gameinstructor_verbose_lesson) to show update actions.");
ConVar gameinstructor_verbose_lesson("gameinstructor_verbose_lesson", "", FCVAR_CHEAT, "Display more verbose information for lessons have this name." );
ConVar gameinstructor_find_errors("gameinstructor_find_errors", "1", FCVAR_CHEAT, "Set to 1 and the game instructor will run EVERY scripted command to uncover errors." );
ConVar gameinstructor_enable( "gameinstructor_enable", "1", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Display in game lessons that teach new players.", GameInstructorEnable_ChangeCallback );
ConVar gameinstructor_start_sound_cooldown( "gameinstructor_start_sound_cooldown", "4.0", FCVAR_NONE, "Number of seconds forced between similar lesson start sounds." );
ConVar sv_gameinstructor_disable( "sv_gameinstructor_disable", "0", FCVAR_REPLICATED, "Force all clients to disable their game instructors.", SVGameInstructorDisable_ChangeCallback );
//=========================================================
// Activa o Desactiva el Instructor del lado del cliente.
//=========================================================
void EnableDisableInstructor()
{
bool bEnabled = ( !sv_gameinstructor_disable.GetBool() && gameinstructor_enable.GetBool() );
// Game instructor has been enabled, so init it!
if ( bEnabled )
GetGameInstructor().Init();
// Game instructor has been disabled, so shut it down!
else
GetGameInstructor().Shutdown();
}
//=========================================================
//=========================================================
void GameInstructorEnable_ChangeCallback( IConVar *var, const char *pOldValue, float flOldValue )
{
if ( ( flOldValue != 0.0f ) != gameinstructor_enable.GetBool() )
EnableDisableInstructor();
}
//=========================================================
//=========================================================
void SVGameInstructorDisable_ChangeCallback( IConVar *var, const char *pOldValue, float flOldValue )
{
if ( !engine )
return;
EnableDisableInstructor();
}
//=========================================================
// Inicializa al Instructor
//=========================================================
bool C_GameInstructor::Init()
{
// if ( &GetGameInstructor() == this )
// return true;
// Instructor desactivado, no inicializar.
if ( !gameinstructor_enable.GetBool() || sv_gameinstructor_disable.GetBool() )
return true;
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Inicializando...\n" );
}
m_bNoDraw = false;
m_bHiddenDueToOtherElements = false;
m_iCurrentPriority = 0;
m_hLastSpectatedPlayer = NULL;
m_bSpectatedPlayerChanged = false;
m_szPreviousStartSound[0] = '\0';
m_fNextStartSoundTime = 0;
ReadLessonsFromFile( GAMEINSTRUCTOR_MOD_SCRIPT_FILE );
ReadLessonsFromFile( GAMEINSTRUCTOR_SCRIPT_FILE );
InitLessonPrerequisites();
ReadSaveData();
ListenForGameEvent("gameinstructor_draw");
ListenForGameEvent("gameinstructor_nodraw");
ListenForGameEvent("round_end");
ListenForGameEvent("round_start");
ListenForGameEvent("player_death");
ListenForGameEvent("player_team");
ListenForGameEvent("player_disconnect");
ListenForGameEvent("map_transition");
ListenForGameEvent("game_newmap");
ListenForGameEvent("set_instructor_group_enabled");
EvaluateLessonsForGameRules();
return true;
}
//=========================================================
// Apaga al instructor
//=========================================================
void C_GameInstructor::Shutdown()
{
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Apagando...\n" );
}
CloseAllOpenOpportunities();
WriteSaveData();
// Removemos todas las lecciones.
for ( int i = 0; i < m_Lessons.Count(); ++i )
{
if ( m_Lessons[ i ] )
{
m_Lessons[ i ]->StopListeningForAllEvents();
delete m_Lessons[ i ];
m_Lessons[ i ] = NULL;
}
}
m_Lessons.RemoveAll();
m_LessonGroupConVarToggles.RemoveAll();
// Paramos de escuchar eventos.
StopListeningForAllEvents();
}
//=========================================================
//=========================================================
void C_GameInstructor::UpdateHiddenByOtherElements()
{
//bool bHidden = Mod_HiddenByOtherElements();
bool bHidden = false;
if ( bHidden && !m_bHiddenDueToOtherElements )
StopAllLessons();
m_bHiddenDueToOtherElements = bHidden;
}
//=========================================================
//=========================================================
void C_GameInstructor::Update( float frametime )
{
VPROF_BUDGET( "C_GameInstructor::Update", "GameInstructor" );
UpdateHiddenByOtherElements();
// Instructor desactivado.
if ( !gameinstructor_enable.GetBool() || m_bNoDraw || m_bHiddenDueToOtherElements )
return;
if ( gameinstructor_find_errors.GetBool() )
{
FindErrors();
gameinstructor_find_errors.SetValue(0);
}
if ( IsConsole() )
{
// On X360 we want to save when they're not connected
// They aren't in game
if ( !engine->IsInGame() )
WriteSaveData();
else
{
const char *levelName = engine->GetLevelName();
// The are in game, but it's a background map
if ( levelName && levelName[0] && engine->IsLevelMainMenuBackground() )
WriteSaveData();
}
}
if ( m_bSpectatedPlayerChanged )
{
// Safe spot to clean out stale lessons if spectator changed
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Spectated player changed...\n" );
}
CloseAllOpenOpportunities();
m_bSpectatedPlayerChanged = false;
}
// Loop through all the lesson roots and reset their active status
for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i )
{
CBaseLesson *pLesson = m_OpenOpportunities[ i ];
CBaseLesson *pRootLesson = pLesson->GetRoot();
if ( pRootLesson->InstanceType() == LESSON_INSTANCE_SINGLE_ACTIVE )
pRootLesson->SetInstanceActive(false);
}
int iCurrentPriority = 0;
// Loop through all the open lessons
for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i )
{
CBaseLesson *pLesson = m_OpenOpportunities[ i ];
// This opportunity has closed
if ( !pLesson->IsOpenOpportunity() || pLesson->IsTimedOut() )
{
CloseOpportunity( pLesson );
continue;
}
// Lesson should be displayed, so it can affect priority
CBaseLesson *pRootLesson = pLesson->GetRoot();
bool bShouldDisplay = pLesson->ShouldDisplay();
bool bIsLocked = pLesson->IsLocked();
if ( ( bShouldDisplay || bIsLocked ) &&
( pLesson->GetPriority() >= m_iCurrentPriority || pLesson->NoPriority() || bIsLocked ) &&
( pRootLesson && ( pRootLesson->InstanceType() != LESSON_INSTANCE_SINGLE_ACTIVE || !pRootLesson->IsInstanceActive() ) ) )
{
// Lesson is at the highest priority level, isn't violating instance rules, and has met all the prerequisites
if ( UpdateActiveLesson( pLesson, pRootLesson ) || pRootLesson->IsLearned() )
{
// Lesson is active
if ( pLesson->IsVisible() || pRootLesson->IsLearned() )
{
pRootLesson->SetInstanceActive( true );
// This active or learned lesson has the highest priority so far
if ( iCurrentPriority < pLesson->GetPriority() && !pLesson->NoPriority() )
iCurrentPriority = pLesson->GetPriority();
}
}
else
{
// On second thought, this shouldn't have been displayed
bShouldDisplay = false;
}
}
else
{
// Lesson shouldn't be displayed right now
UpdateInactiveLesson( pLesson );
}
}
// Set the priority for next frame
if ( gameinstructor_verbose.GetInt() > 1 && m_iCurrentPriority != iCurrentPriority )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Priority changed from " );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "%i ", m_iCurrentPriority );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "to " );
ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "%i", iCurrentPriority );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" );
}
m_iCurrentPriority = iCurrentPriority;
}
//=========================================================
//=========================================================
void C_GameInstructor::FireGameEvent( IGameEvent *event )
{
VPROF_BUDGET( "C_GameInstructor::FireGameEvent", "GameInstructor" );
const char *name = event->GetName();
if ( Q_strcmp( name, "gameinstructor_draw" ) == 0 )
{
if ( m_bNoDraw )
{
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Set to draw...\n" );
}
m_bNoDraw = false;
}
}
else if ( Q_strcmp( name, "gameinstructor_nodraw" ) == 0 )
{
if ( !m_bNoDraw )
{
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Set to not draw...\n" );
}
m_bNoDraw = true;
StopAllLessons();
}
}
else if ( Q_strcmp( name, "round_end" ) == 0 )
{
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Round ended...\n" );
}
CloseAllOpenOpportunities();
if ( IsPC() )
{
// Good place to backup our counts
WriteSaveData();
}
}
else if ( Q_strcmp( name, "round_start" ) == 0 )
{
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Round started...\n" );
}
CloseAllOpenOpportunities();
EvaluateLessonsForGameRules();
}
else if ( Q_strcmp( name, "player_death" ) == 0 )
{
#if !defined(NO_STEAM) && defined(USE_CEG)
Steamworks_TestSecret();
Steamworks_SelfCheck();
#endif
C_BasePlayer *pLocalPlayer = GetLocalPlayer();
if ( pLocalPlayer && pLocalPlayer == UTIL_PlayerByUserId( event->GetInt( "userid" ) ) )
{
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Local player died...\n" );
}
for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i )
{
CBaseLesson *pLesson = m_OpenOpportunities[ i ];
CBaseLesson *pRootLesson = pLesson->GetRoot();
if ( !pRootLesson->CanOpenWhenDead() )
CloseOpportunity( pLesson );
}
}
}
else if ( Q_strcmp( name, "player_team" ) == 0 )
{
C_BasePlayer *pLocalPlayer = GetLocalPlayer();
if ( pLocalPlayer && pLocalPlayer == UTIL_PlayerByUserId( event->GetInt( "userid" ) ) &&
( event->GetInt( "team" ) != event->GetInt( "oldteam" ) || event->GetBool( "disconnect" ) ) )
{
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Local player changed team (or disconnected)...\n" );
}
CloseAllOpenOpportunities();
}
EvaluateLessonsForGameRules();
}
else if ( Q_strcmp( name, "player_disconnect" ) == 0 )
{
C_BasePlayer *pLocalPlayer = GetLocalPlayer();
if ( pLocalPlayer && pLocalPlayer == UTIL_PlayerByUserId( event->GetInt( "userid" ) ) )
{
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Local player disconnected...\n" );
}
CloseAllOpenOpportunities();
}
}
else if ( Q_strcmp( name, "map_transition" ) == 0 )
{
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Map transition...\n" );
}
CloseAllOpenOpportunities();
if ( m_bNoDraw )
{
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( Color( 255, 128, 64, 255 ), "[INSTRUCTOR]: " );
ConColorMsg( Color( 64, 128, 255, 255 ), "Set to draw...\n" );
}
m_bNoDraw = false;
}
if ( IsPC() )
{
// Good place to backup our counts
WriteSaveData();
}
}
else if ( Q_strcmp( name, "game_newmap" ) == 0 )
{
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "New map...\n" );
}
CloseAllOpenOpportunities();
if ( m_bNoDraw )
{
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( Color( 255, 128, 64, 255 ), "[INSTRUCTOR]: " );
ConColorMsg( Color( 64, 128, 255, 255 ), "Set to draw...\n" );
}
m_bNoDraw = false;
}
if ( IsPC() )
{
// Good place to backup our counts
WriteSaveData();
}
}
else if ( Q_strcmp( name, "set_instructor_group_enabled" ) == 0 )
{
const char *pszGroup = event->GetString( "group" );
bool bEnabled = event->GetInt( "enabled" ) != 0;
if ( pszGroup && pszGroup[0] )
SetLessonGroupEnabled(pszGroup, bEnabled);
}
}
//=========================================================
//=========================================================
void C_GameInstructor::DefineLesson( CBaseLesson *pLesson )
{
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Lesson " );
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pLesson->GetName() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "defined.\n" );
}
m_Lessons.AddToTail( pLesson );
}
//=========================================================
//=========================================================
const CBaseLesson * C_GameInstructor::GetLesson( const char *pchLessonName )
{
return GetLesson_Internal( pchLessonName );
}
//=========================================================
//=========================================================
bool C_GameInstructor::IsLessonOfSameTypeOpen( const CBaseLesson *pLesson ) const
{
for ( int i = 0; i < m_OpenOpportunities.Count(); ++i )
{
CBaseLesson *pOpenOpportunity = m_OpenOpportunities[ i ];
if ( pOpenOpportunity->GetNameSymbol() == pLesson->GetNameSymbol() )
return true;
}
return false;
}
//=========================================================
//=========================================================
bool C_GameInstructor::ReadSaveData()
{
// for external playtests, don't ever read in persisted instructor state, always start fresh
if ( CommandLine()->FindParm( "-playtest" ) )
return true;
if ( m_bHasLoadedSaveData )
return true;
// Always reset state first in case storage device
// was declined or ends up in faulty state
ResetDisplaysAndSuccesses();
m_bHasLoadedSaveData = true;
#ifdef _X360
DevMsg( "Read Game Instructor for splitscreen slot %d\n", m_nSplitScreenSlot );
if ( m_nSplitScreenSlot < 0 )
return false;
if ( m_nSplitScreenSlot >= (int) XBX_GetNumGameUsers() )
return false;
int iController = XBX_GetUserId( m_nSplitScreenSlot );
if ( iController < 0 || XBX_GetUserIsGuest( iController ) )
{
// Can't read data for guests
return false;
}
DWORD nStorageDevice = XBX_GetStorageDeviceId( iController );
if ( !XBX_DescribeStorageDevice( nStorageDevice ) )
return false;
#endif
char szFilename[_MAX_PATH];
#ifdef _X360
if ( IsX360() )
{
XBX_MakeStorageContainerRoot( iController, XBX_USER_SETTINGS_CONTAINER_DRIVE, szFilename, sizeof( szFilename ) );
int nLen = strlen( szFilename );
Q_snprintf( szFilename + nLen, sizeof( szFilename ) - nLen, ":\\game_instructor_counts.txt" );
}
else
#endif
{
Q_snprintf( szFilename, sizeof( szFilename ), "save/game_instructor_counts.txt" );
}
KeyValues *data = new KeyValues( "Game Instructor Counts" );
KeyValues::AutoDelete autoDelete(data);
if ( data->LoadFromFile( g_pFullFileSystem, szFilename, NULL ) )
{
int nVersion = 0;
for ( KeyValues *pKey = data->GetFirstSubKey(); pKey; pKey = pKey->GetNextTrueSubKey() )
{
CBaseLesson *pLesson = GetLesson_Internal( pKey->GetName() );
if ( pLesson )
{
pLesson->SetDisplayCount( pKey->GetInt( "display", 0 ) );
pLesson->SetSuccessCount( pKey->GetInt( "success", 0 ) );
if ( Q_strcmp( pKey->GetName(), "version number" ) == 0 )
{
nVersion = pLesson->GetSuccessCount();
}
}
}
CBaseLesson *pLessonVersionNumber = GetLesson_Internal( "version number" );
if ( pLessonVersionNumber && !pLessonVersionNumber->IsLearned() )
{
ResetDisplaysAndSuccesses();
pLessonVersionNumber->SetSuccessCount( pLessonVersionNumber->GetSuccessLimit() );
m_bDirtySaveData = true;
}
return true;
}
// Couldn't read from the file
return false;
}
//=========================================================
//=========================================================
bool C_GameInstructor::WriteSaveData()
{
if ( engine->IsPlayingDemo() )
return false;
if ( !m_bDirtySaveData )
return true;
#ifdef _X360
float flPlatTime = Plat_FloatTime();
static ConVarRef host_write_last_time( "host_write_last_time" );
if ( host_write_last_time.IsValid() )
{
float flTimeSinceLastWrite = flPlatTime - host_write_last_time.GetFloat();
if ( flTimeSinceLastWrite < 3.5f )
{
// Prevent writing to the same storage device twice in less than 3 second succession for TCR success!
// This happens after leaving a game in splitscreen.
//DevMsg( "Waiting to write Game Instructor for splitscreen slot %d... (%.1f seconds remain)\n", m_nSplitScreenSlot, 3.5f - flTimeSinceLastWrite );
return false;
}
}
#endif
// Always mark as clean state to avoid re-entry on
// subsequent frames when storage device might be
// in a yet-unmounted state.
m_bDirtySaveData = false;
#ifdef _X360
DevMsg( "Write Game Instructor for splitscreen slot %d at time: %.1f\n", m_nSplitScreenSlot, flPlatTime );
if ( m_nSplitScreenSlot < 0 )
return false;
if ( m_nSplitScreenSlot >= (int) XBX_GetNumGameUsers() )
return false;
int iController = XBX_GetUserId( m_nSplitScreenSlot );
if ( iController < 0 || XBX_GetUserIsGuest( iController ) )
{
// Can't save data for guests
return false;
}
DWORD nStorageDevice = XBX_GetStorageDeviceId( iController );
if ( !XBX_DescribeStorageDevice( nStorageDevice ) )
return false;
#endif
// Build key value data to save
KeyValues *data = new KeyValues( "Game Instructor Counts" );
KeyValues::AutoDelete autoDelete(data);
for ( int i = 0; i < m_Lessons.Count(); ++i )
{
CBaseLesson *pLesson = m_Lessons[i];
int iDisplayCount = pLesson->GetDisplayCount();
int iSuccessCount = pLesson->GetSuccessCount();
if ( iDisplayCount || iSuccessCount )
{
// We've got some data worth saving
KeyValues *pKVData = new KeyValues( pLesson->GetName() );
if ( iDisplayCount )
pKVData->SetInt( "display", iDisplayCount );
if ( iSuccessCount )
pKVData->SetInt( "success", iSuccessCount );
data->AddSubKey( pKVData );
}
}
// Save it!
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
data->RecursiveSaveToFile( buf, 0 );
char szFilename[_MAX_PATH];
#ifdef _X360
if ( IsX360() )
{
XBX_MakeStorageContainerRoot( iController, XBX_USER_SETTINGS_CONTAINER_DRIVE, szFilename, sizeof( szFilename ) );
int nLen = strlen( szFilename );
Q_snprintf( szFilename + nLen, sizeof( szFilename ) - nLen, ":\\game_instructor_counts.txt" );
}
else
#endif
{
Q_snprintf( szFilename, sizeof( szFilename ), "save/game_instructor_counts.txt" );
filesystem->CreateDirHierarchy( "save", "MOD" );
}
bool bWriteSuccess = filesystem->WriteFile( szFilename, MOD_DIR, buf );
#ifdef _X360
if ( xboxsystem )
{
xboxsystem->FinishContainerWrites( iController );
}
#endif
return bWriteSuccess;
}
//=========================================================
//=========================================================
void C_GameInstructor::RefreshDisplaysAndSuccesses()
{
m_bHasLoadedSaveData = false;
ReadSaveData();
}
//=========================================================
//=========================================================
void C_GameInstructor::ResetDisplaysAndSuccesses()
{
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Reset all lesson display and success counts.\n" );
}
for ( int i = 0; i < m_Lessons.Count(); ++i )
{
m_Lessons[ i ]->ResetDisplaysAndSuccesses();
}
m_bDirtySaveData = false;
}
//=========================================================
//=========================================================
void C_GameInstructor::MarkDisplayed( const char *pchLessonName )
{
CBaseLesson *pLesson = GetLesson_Internal(pchLessonName);
if ( !pLesson )
return;
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Lesson " );
ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLesson->GetName() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "marked as displayed.\n" );
}
if ( pLesson->IncDisplayCount() )
m_bDirtySaveData = true;
}
//=========================================================
//=========================================================
void C_GameInstructor::MarkSucceeded(const char *pchLessonName)
{
CBaseLesson *pLesson = GetLesson_Internal(pchLessonName);
if ( !pLesson )
return;
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Lesson " );
ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\"%s\" ", pLesson->GetName() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "marked as succeeded.\n" );
}
if ( pLesson->IncSuccessCount() )
m_bDirtySaveData = true;
}
//=========================================================
//=========================================================
void C_GameInstructor::PlaySound( const char *pchSoundName )
{
// emit alert sound
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
if ( pLocalPlayer )
{
// Local player exists
if ( pchSoundName[ 0 ] != '\0' && Q_strcmp( m_szPreviousStartSound, pchSoundName ) != 0 )
{
Q_strcpy( m_szPreviousStartSound, pchSoundName );
m_fNextStartSoundTime = 0.0f;
}
if ( gpGlobals->curtime >= m_fNextStartSoundTime && pchSoundName[ 0 ] != '\0' )
{
// A sound was specified, so play it!
pLocalPlayer->EmitSound( pchSoundName );
m_fNextStartSoundTime = gpGlobals->curtime + gameinstructor_start_sound_cooldown.GetFloat();
}
}
}
//=========================================================
//=========================================================
bool C_GameInstructor::OpenOpportunity( CBaseLesson *pLesson )
{
// Get the root lesson
CBaseLesson *pRootLesson = pLesson->GetRoot();
if ( !pRootLesson )
{
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "NOT opened (because root lesson could not be found).\n" );
}
delete pLesson;
return false;
}
C_BasePlayer *pLocalPlayer = GetLocalPlayer();
if ( !pRootLesson->CanOpenWhenDead() && ( !pLocalPlayer || !pLocalPlayer->IsAlive() ) )
{
// If the player is dead don't allow lessons that can't be opened when dead
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "NOT opened (because player is dead and can_open_when_dead not set).\n" );
}
delete pLesson;
return false;
}
if ( !pRootLesson->PrerequisitesHaveBeenMet() )
{
// If the prereqs haven't been met, don't open it
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "[INSTRUCTOR]: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "NOT opened (because prereqs haven't been met).\n" );
}
delete pLesson;
return false;
}
if ( pRootLesson->InstanceType() == LESSON_INSTANCE_FIXED_REPLACE )
{
CBaseLesson *pLessonToReplace = NULL;
CBaseLesson *pLastReplacableLesson = NULL;
int iInstanceCount = 0;
// Check how many are already open
for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i )
{
CBaseLesson *pOpenOpportunity = m_OpenOpportunities[ i ];
if ( pOpenOpportunity->GetNameSymbol() == pLesson->GetNameSymbol() &&
pOpenOpportunity->GetReplaceKeySymbol() == pLesson->GetReplaceKeySymbol() )
{
iInstanceCount++;
if ( pRootLesson->ShouldReplaceOnlyWhenStopped() )
{
if ( !pOpenOpportunity->IsInstructing() )
{
pLastReplacableLesson = pOpenOpportunity;
}
}
else
{
pLastReplacableLesson = pOpenOpportunity;
}
if ( iInstanceCount >= pRootLesson->GetFixedInstancesMax() )
{
pLessonToReplace = pLastReplacableLesson;
break;
}
}
}
if ( pLessonToReplace )
{
// Take the place of the previous instance
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " );
ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLesson->GetName() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "replacing open lesson of same type.\n" );
}
pLesson->TakePlaceOf( pLessonToReplace );
CloseOpportunity( pLessonToReplace );
}
else if ( iInstanceCount >= pRootLesson->GetFixedInstancesMax() )
{
// Don't add another lesson of this type
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "NOT opened (there is too many started lessons of this type).\n" );
}
delete pLesson;
return false;
}
}
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " );
ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLesson->GetName() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "opened.\n" );
}
m_OpenOpportunities.AddToTail( pLesson );
return true;
}
//=========================================================
//=========================================================
void C_GameInstructor::DumpOpenOpportunities()
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Open lessons...\n" );
for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i )
{
CBaseLesson *pLesson = m_OpenOpportunities[ i ];
CBaseLesson *pRootLesson = pLesson->GetRoot();
Color color;
if ( pLesson->IsInstructing() )
{
// Green
color = CBaseLesson::m_rgbaVerboseOpen;
}
else if ( pRootLesson->IsLearned() && pLesson->GetPriority() >= m_iCurrentPriority )
{
// Yellow
color = CBaseLesson::m_rgbaVerboseSuccess;
}
else
{
// Red
color = CBaseLesson::m_rgbaVerboseClose;
}
ConColorMsg( color, "\t%s\n", pLesson->GetName() );
}
}
//=========================================================
//=========================================================
KeyValues * C_GameInstructor::GetScriptKeys()
{
return m_pScriptKeys;
}
//=========================================================
//=========================================================
C_BasePlayer * C_GameInstructor::GetLocalPlayer()
{
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
// If we're not a developer, don't do the special spectator hook ups
if ( !developer.GetBool() )
return pLocalPlayer;
// If there is no local player and we're not spectating, just return that
if ( !pLocalPlayer || pLocalPlayer->GetTeamNumber() != TEAM_SPECTATOR )
return pLocalPlayer;
// We're purely a spectator let's get lessons of the person we're spectating
C_BasePlayer *pSpectatedPlayer = NULL;
if ( pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE || pLocalPlayer->GetObserverMode() == OBS_MODE_CHASE )
pSpectatedPlayer = ToBasePlayer( pLocalPlayer->GetObserverTarget() );
if ( m_hLastSpectatedPlayer != pSpectatedPlayer )
{
// We're spectating someone new! Close all the stale lessons!
m_bSpectatedPlayerChanged = true;
m_hLastSpectatedPlayer = pSpectatedPlayer;
}
return pSpectatedPlayer;
}
//=========================================================
//=========================================================
void C_GameInstructor::EvaluateLessonsForGameRules()
{
// Enable everything by default
for ( int i = 0; i < m_Lessons.Count(); ++i )
m_Lessons[ i ]->SetEnabled(true);
// Then see if we should disable anything
for ( int nConVar = 0; nConVar < m_LessonGroupConVarToggles.Count(); ++nConVar )
{
LessonGroupConVarToggle_t *pLessonGroupConVarToggle = &(m_LessonGroupConVarToggles[ nConVar ]);
if ( pLessonGroupConVarToggle->var.IsValid() )
{
if ( pLessonGroupConVarToggle->var.GetBool() )
SetLessonGroupEnabled( pLessonGroupConVarToggle->szLessonGroupName, false );
}
}
}
//=========================================================
//=========================================================
void C_GameInstructor::SetLessonGroupEnabled( const char *pszGroup, bool bEnabled )
{
for ( int i = 0; i < m_Lessons.Count(); ++i )
{
if ( !Q_stricmp(pszGroup, m_Lessons[i]->GetGroup()) )
m_Lessons[i]->SetEnabled( bEnabled );
}
}
//=========================================================
//=========================================================
void C_GameInstructor::FindErrors()
{
// Loop through all the lesson and run all their scripted actions
for ( int i = 0; i < m_Lessons.Count(); ++i )
{
CScriptedIconLesson *pLesson = dynamic_cast<CScriptedIconLesson *>( m_Lessons[ i ] );
if ( pLesson )
{
// Process all open events
for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetOpenEvents().Count(); ++iLessonEvent )
{
const LessonEvent_t *pLessonEvent = &(pLesson->GetOpenEvents()[ iLessonEvent ]);
pLesson->ProcessElements( NULL, &(pLessonEvent->elements) );
}
// Process all close events
for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetCloseEvents().Count(); ++iLessonEvent )
{
const LessonEvent_t *pLessonEvent = &(pLesson->GetCloseEvents()[ iLessonEvent ]);
pLesson->ProcessElements( NULL, &(pLessonEvent->elements) );
}
// Process all success events
for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetSuccessEvents().Count(); ++iLessonEvent )
{
const LessonEvent_t *pLessonEvent = &(pLesson->GetSuccessEvents()[ iLessonEvent ]);
pLesson->ProcessElements( NULL, &(pLessonEvent->elements) );
}
// Process all on open events
for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetOnOpenEvents().Count(); ++iLessonEvent )
{
const LessonEvent_t *pLessonEvent = &(pLesson->GetOnOpenEvents()[ iLessonEvent ]);
pLesson->ProcessElements( NULL, &(pLessonEvent->elements) );
}
// Process all update events
for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetUpdateEvents().Count(); ++iLessonEvent )
{
const LessonEvent_t *pLessonEvent = &(pLesson->GetUpdateEvents()[ iLessonEvent ]);
pLesson->ProcessElements( NULL, &(pLessonEvent->elements) );
}
}
}
}
//=========================================================
//=========================================================
bool C_GameInstructor::UpdateActiveLesson( CBaseLesson *pLesson, const CBaseLesson *pRootLesson )
{
VPROF_BUDGET( "C_GameInstructor::UpdateActiveLesson", "GameInstructor" );
bool bIsOpen = pLesson->IsInstructing();
if ( !bIsOpen && !pRootLesson->IsLearned() )
{
pLesson->SetStartTime();
pLesson->Start();
// Check to see if it successfully started
bIsOpen = ( pLesson->IsOpenOpportunity() && pLesson->ShouldDisplay() );
if ( bIsOpen )
{
// Lesson hasn't been started and hasn't been learned
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Started lesson " );
ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", pLesson->GetName() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" );
}
}
else
{
pLesson->Stop();
pLesson->ResetStartTime();
}
}
if ( bIsOpen )
{
// Update the running lesson
pLesson->Update();
return true;
}
else
{
pLesson->UpdateInactive();
return false;
}
}
//=========================================================
//=========================================================
void C_GameInstructor::UpdateInactiveLesson( CBaseLesson *pLesson )
{
VPROF_BUDGET( "C_GameInstructor::UpdateInactiveLesson", "GameInstructor" );
if ( pLesson->IsInstructing() )
{
// Lesson hasn't been stopped
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Stopped lesson " );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\"", pLesson->GetName() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" );
}
pLesson->Stop();
pLesson->ResetStartTime();
}
pLesson->UpdateInactive();
}
//=========================================================
//=========================================================
CBaseLesson * C_GameInstructor::GetLesson_Internal( const char *pchLessonName )
{
for ( int i = 0; i < m_Lessons.Count(); ++i )
{
CBaseLesson *pLesson = m_Lessons[ i ];
if ( Q_strcmp( pLesson->GetName(), pchLessonName ) == 0 )
{
return pLesson;
}
}
return NULL;
}
//=========================================================
//=========================================================
void C_GameInstructor::StopAllLessons()
{
// Stop all the current lessons
for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i )
{
CBaseLesson *pLesson = m_OpenOpportunities[ i ];
UpdateInactiveLesson( pLesson );
}
}
//=========================================================
//=========================================================
void C_GameInstructor::CloseAllOpenOpportunities()
{
// Clear out all the open opportunities
for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i )
{
CBaseLesson *pLesson = m_OpenOpportunities[ i ];
CloseOpportunity( pLesson );
}
Assert( m_OpenOpportunities.Count() == 0 );
}
//=========================================================
//=========================================================
void C_GameInstructor::CloseOpportunity( CBaseLesson *pLesson )
{
UpdateInactiveLesson( pLesson );
if ( pLesson->WasDisplayed() )
MarkDisplayed( pLesson->GetName() );
if ( gameinstructor_verbose.GetInt() > 0 )
{
ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() );
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "closed for reason: " );
ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "%s\n", pLesson->GetCloseReason() );
}
pLesson->StopListeningForAllEvents();
m_OpenOpportunities.FindAndRemove( pLesson );
delete pLesson;
}
//=========================================================
//=========================================================
void C_GameInstructor::ReadLessonsFromFile( const char *pchFileName )
{
// Static init function
CScriptedIconLesson::PreReadLessonsFromFile();
MEM_ALLOC_CREDIT();
KeyValues *pLessonKeys = new KeyValues("instructor_lessons");
KeyValues::AutoDelete autoDelete(pLessonKeys);
pLessonKeys->LoadFromFile(g_pFullFileSystem, pchFileName, NULL);
for ( m_pScriptKeys = pLessonKeys->GetFirstTrueSubKey(); m_pScriptKeys; m_pScriptKeys = m_pScriptKeys->GetNextTrueSubKey() )
{
if ( Q_stricmp(m_pScriptKeys->GetName(), "GroupConVarToggle") == 0 )
{
// Add convar group toggler to the list
int nLessonGroupConVarToggle = m_LessonGroupConVarToggles.AddToTail( LessonGroupConVarToggle_t( m_pScriptKeys->GetString( "convar" ) ) );
LessonGroupConVarToggle_t *pLessonGroupConVarToggle = &(m_LessonGroupConVarToggles[nLessonGroupConVarToggle]);
Q_strcpy( pLessonGroupConVarToggle->szLessonGroupName, m_pScriptKeys->GetString("group") );
continue;
}
// Ensure that lessons aren't added twice
if ( GetLesson_Internal(m_pScriptKeys->GetName()) )
{
DevWarning("Lesson \"%s\" defined twice!\n", m_pScriptKeys->GetName());
continue;
}
CScriptedIconLesson *pNewLesson = new CScriptedIconLesson(m_pScriptKeys->GetName(), false, false);
GetGameInstructor().DefineLesson(pNewLesson);
}
m_pScriptKeys = NULL;
}
//=========================================================
//=========================================================
void C_GameInstructor::InitLessonPrerequisites()
{
for ( int i = 0; i < m_Lessons.Count(); ++i )
m_Lessons[ i ]->InitPrerequisites();
}
//=========================================================
// Comandos
//=========================================================
CON_COMMAND_F( gameinstructor_reload_lessons, "Shuts down all open lessons and reloads them from the script file.", FCVAR_CHEAT )
{
GetGameInstructor().Shutdown();
GetGameInstructor().Init();
}
CON_COMMAND_F( gameinstructor_reset_counts, "Resets all display and success counts to zero.", FCVAR_NONE )
{
GetGameInstructor().ResetDisplaysAndSuccesses();
}
CON_COMMAND_F( gameinstructor_dump_open_lessons, "Gives a list of all currently open lessons.", FCVAR_CHEAT )
{
GetGameInstructor().DumpOpenOpportunities();
}
Arreglando problemas
El Game Instructor ya ha sido incorporado sin embargo es necesario ahora arreglar ciertos problemas con los demás archivos.
UTIL_CountNumBitsSet
Esta función es necesaria para el funcionamiento de las lecciones y debería estar en el archivo game/shared/util_shared.cpp por lo que abriremos tal archivo y buscaremos la función UTIL_StringFieldToInt aproximadamente en la línea 1089:
int UTIL_StringFieldToInt( const char *szValue, const char **pValueStrings, int iNumStrings )
{
if ( !szValue || !szValue[0] )
return -1;
for ( int i = 0; i < iNumStrings; i++ )
{
if ( FStrEq(szValue, pValueStrings[i]) )
return i;
}
Assert(0);
return -1;
}
Y justo debajo de ella incorporaremos este código:
static char s_NumBitsInNibble[ 16 ] =
{
0, // 0000 = 0
1, // 0001 = 1
1, // 0010 = 2
2, // 0011 = 3
1, // 0100 = 4
2, // 0101 = 5
2, // 0110 = 6
3, // 0111 = 7
1, // 1000 = 8
2, // 1001 = 9
2, // 1010 = 10
3, // 1011 = 11
2, // 1100 = 12
3, // 1101 = 13
3, // 1110 = 14
4, // 1111 = 15
};
int UTIL_CountNumBitsSet( unsigned int nVar )
{
int nNumBits = 0;
while ( nVar > 0 )
{
// Look up and add in bits in the bottom nibble
nNumBits += s_NumBitsInNibble[ nVar & 0x0f ];
// Shift one nibble to the right
nVar >>= 4;
}
return nNumBits;
}
int UTIL_CountNumBitsSet( uint64 nVar )
{
int nNumBits = 0;
while ( nVar > 0 )
{
// Look up and add in bits in the bottom nibble
nNumBits += s_NumBitsInNibble[ nVar & 0x0f ];
// Shift one nibble to the right
nVar >>= 4;
}
return nNumBits;
}
Y ahora como es lógico definiremos esas 2 funciones dentro de game/shared/util_shared.h, como antes buscaremos la definición de la función UTIL_StringFieldToInt que se encuentra aproximadamente en la línea 587:
int UTIL_StringFieldToInt( const char *szValue, const char **pValueStrings, int iNumStrings );
Y justo debajo de ella agregamos:
int UTIL_CountNumBitsSet( unsigned int nVar );
int UTIL_CountNumBitsSet( uint64 nVar );
GetPlayerName
Esta función como bien dice, sirve para obtener el nombre de un jugador, sin embargo en el Source SDK 2013 la misma solo es accesible desde CBasePlayer mientras que el Game Instructor lo requiere en CBaseEntity. Por lo que abriremos el archivo game/client/c_baseentity.h y deberemos agregar esta función en alguna sección accesible públicamente "public:", en mi caso la he puesto debajo de GetDebugName que se encuentra aproximadamente en la línea 860:
char const *GetDebugName( void );
Ahora debajo de ella agregamos:
virtual const char *GetPlayerName() const { return NULL; }
CHudIcons
Esta clase es la encargada de mostrar los iconos que se encuentran al lado del mensaje cuando una lección aparece en la pantalla y la misma no existe en el Source SDK 2013. Para agregarla deberemos abrir el archivo game/client/hud.h y aproximadamente en la línea 195 encontraremos lo siguiente:
extern CHud gHUD;
Ahora debajo de ella agregamos la clase:
//-----------------------------------------------------------------------------
// Purpose: CHudIcons
//-----------------------------------------------------------------------------
class CHudIcons
{
public:
CHudIcons();
~CHudIcons();
void Init();
void Shutdown();
CHudTexture *GetIcon( const char *szIcon );
// loads a new icon into the list, without duplicates
CHudTexture *AddUnsearchableHudIconToList( CHudTexture& texture );
CHudTexture *AddSearchableHudIconToList( CHudTexture& texture );
void RefreshHudTextures();
private:
void SetupNewHudTexture( CHudTexture *t );
bool m_bHudTexturesLoaded;
// Global list of known icons
CUtlDict< CHudTexture *, int > m_Icons;
};
CHudIcons &HudIcons();
Ahora abrimos el archivo game/client/hud.cpp y al final de todo el archivo es decir en la línea 1200 agregamos lo siguiente:
CHudIcons::CHudIcons() :
m_bHudTexturesLoaded( false )
{
}
CHudIcons::~CHudIcons()
{
int c = m_Icons.Count();
for ( int i = c - 1; i >= 0; i-- )
{
CHudTexture *tex = m_Icons[ i ];
g_HudTextureMemoryPool.Free( tex );
}
m_Icons.Purge();
}
void CHudIcons::Init()
{
if ( m_bHudTexturesLoaded )
return;
m_bHudTexturesLoaded = true;
CUtlDict< CHudTexture *, int > textureList;
// check to see if we have sprites for this res; if not, step down
LoadHudTextures( textureList, "scripts/hud_textures", NULL );
LoadHudTextures( textureList, "scripts/mod_textures", NULL );
LoadHudTextures( textureList, "scripts/instructor_textures", NULL );
LoadHudTextures( textureList, "scripts/instructor_modtextures", NULL );
int c = textureList.Count();
for ( int index = 0; index < c; index++ )
{
CHudTexture* tex = textureList[ index ];
AddSearchableHudIconToList( *tex );
}
FreeHudTextureList( textureList );
}
void CHudIcons::Shutdown()
{
m_bHudTexturesLoaded = false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CHudTexture *CHudIcons::AddUnsearchableHudIconToList( CHudTexture& texture )
{
// These names are composed based on the texture file name
char composedName[ 512 ];
if ( texture.bRenderUsingFont )
{
Q_snprintf( composedName, sizeof( composedName ), "%s_c%i",
texture.szTextureFile, texture.cCharacterInFont );
}
else
{
Q_snprintf( composedName, sizeof( composedName ), "%s_%i_%i_%i_%i",
texture.szTextureFile, texture.rc.left, texture.rc.top, texture.rc.right, texture.rc.bottom );
}
CHudTexture *icon = GetIcon( composedName );
if ( icon )
{
return icon;
}
CHudTexture *newTexture = ( CHudTexture * )g_HudTextureMemoryPool.Alloc();
*newTexture = texture;
SetupNewHudTexture( newTexture );
int idx = m_Icons.Insert( composedName, newTexture );
return m_Icons[ idx ];
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CHudTexture *CHudIcons::AddSearchableHudIconToList( CHudTexture& texture )
{
CHudTexture *icon = GetIcon( texture.szShortName );
if ( icon )
{
return icon;
}
CHudTexture *newTexture = ( CHudTexture * )g_HudTextureMemoryPool.Alloc();
*newTexture = texture;
SetupNewHudTexture( newTexture );
int idx = m_Icons.Insert( texture.szShortName, newTexture );
return m_Icons[ idx ];
}
//-----------------------------------------------------------------------------
// Purpose: returns a pointer to an icon in the list
//-----------------------------------------------------------------------------
CHudTexture *CHudIcons::GetIcon( const char *szIcon )
{
int i = m_Icons.Find( szIcon );
if ( i == m_Icons.InvalidIndex() )
return NULL;
return m_Icons[ i ];
}
//-----------------------------------------------------------------------------
// Purpose: Gets texture handles for the hud icon
//-----------------------------------------------------------------------------
void CHudIcons::SetupNewHudTexture( CHudTexture *t )
{
if ( t->bRenderUsingFont )
{
vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
t->hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( t->szTextureFile, true );
t->rc.top = 0;
t->rc.left = 0;
t->rc.right = vgui::surface()->GetCharacterWidth( t->hFont, t->cCharacterInFont );
t->rc.bottom = vgui::surface()->GetFontTall( t->hFont );
}
else
{
// Set up texture id and texture coordinates
t->textureId = vgui::surface()->CreateNewTextureID();
vgui::surface()->DrawSetTextureFile( t->textureId, t->szTextureFile, false, false );
int wide, tall;
vgui::surface()->DrawGetTextureSize( t->textureId, wide, tall );
t->texCoords[ 0 ] = (float)(t->rc.left + 0.5f) / (float)wide;
t->texCoords[ 1 ] = (float)(t->rc.top + 0.5f) / (float)tall;
t->texCoords[ 2 ] = (float)(t->rc.right - 0.5f) / (float)wide;
t->texCoords[ 3 ] = (float)(t->rc.bottom - 0.5f) / (float)tall;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudIcons::RefreshHudTextures()
{
if ( !m_bHudTexturesLoaded )
{
Assert( 0 );
return;
}
CUtlDict< CHudTexture *, int > textureList;
// check to see if we have sprites for this res; if not, step down
LoadHudTextures( textureList, "scripts/hud_textures", NULL );
LoadHudTextures( textureList, "scripts/mod_textures", NULL );
LoadHudTextures( textureList, "scripts/instructor_textures", NULL );
// fix up all the texture icons first
int c = textureList.Count();
for ( int index = 0; index < c; index++ )
{
CHudTexture *tex = textureList[ index ];
Assert( tex );
CHudTexture *icon = GetIcon( tex->szShortName );
if ( !icon )
continue;
// Update file
Q_strncpy( icon->szTextureFile, tex->szTextureFile, sizeof( icon->szTextureFile ) );
if ( !icon->bRenderUsingFont )
{
// Update subrect
icon->rc = tex->rc;
// Keep existing texture id, but now update texture file and texture coordinates
vgui::surface()->DrawSetTextureFile( icon->textureId, icon->szTextureFile, false, false );
// Get new texture dimensions in case it changed
int wide, tall;
vgui::surface()->DrawGetTextureSize( icon->textureId, wide, tall );
// Assign coords
icon->texCoords[ 0 ] = (float)(icon->rc.left + 0.5f) / (float)wide;
icon->texCoords[ 1 ] = (float)(icon->rc.top + 0.5f) / (float)tall;
icon->texCoords[ 2 ] = (float)(icon->rc.right - 0.5f) / (float)wide;
icon->texCoords[ 3 ] = (float)(icon->rc.bottom - 0.5f) / (float)tall;
}
}
FreeHudTextureList( textureList );
// fixup all the font icons
vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
for (int i = m_Icons.First(); m_Icons.IsValidIndex(i); i = m_Icons.Next(i))
{
CHudTexture *icon = m_Icons[i];
if ( !icon )
continue;
// Update file
if ( icon->bRenderUsingFont )
{
icon->hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( icon->szTextureFile, true );
icon->rc.top = 0;
icon->rc.left = 0;
icon->rc.right = vgui::surface()->GetCharacterWidth( icon->hFont, icon->cCharacterInFont );
icon->rc.bottom = vgui::surface()->GetFontTall( icon->hFont );
}
}
}
static CHudIcons g_HudIcons;
CHudIcons &HudIcons()
{
return g_HudIcons;
}
Como se puede ver en el código anterior los iconos se definirán en los archivos: scripts/hud_textures.txt, scripts/mod_textures.txt y scripts/instructor_textures.txt
Aunque eso no es todo, ya hemos agregado la clase pero ahora es necesario iniciarla/prepararla, para ello en ese mismo archivo ( hud.cpp ) buscaremos la función CHud::Init que se encuentra aproximadamente en la línea 395 y al final de la función ( Debajo de "FreeHudTextureList( textureList );" ) agregamos lo siguiente:
HudIcons().Init();
GetColor
El código para los comandos de consola "ConVar" tiene una nueva función llamada GetColor la cual como su nombre lo indica sirve para obtener directamente del valor de un comando convertido en Color y es necesario para el Game Instructor. Así que abriremos el archivo public/tier1/convar.h y aproximadamente en la línea 352 encontraremos lo siguiente:
FORCEINLINE_CVAR int GetInt( void ) const;
Y justo debajo agregamos:
FORCEINLINE_CVAR Color GetColor( void ) const;
Ahora en ese mismo archivo y aproximadamente en la línea 430 encontraremos lo siguiente:
//-----------------------------------------------------------------------------
// Purpose: Return ConVar value as an int
// Output : int
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR int ConVar::GetInt( void ) const
{
return m_pParent->m_nValue;
}
Justo debajo agregamos:
//-----------------------------------------------------------------------------
// Purpose: Return ConVar value as a color
// Output : Color
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR Color ConVar::GetColor( void ) const
{
unsigned char *pColorElement = ((unsigned char *)&m_pParent->m_nValue);
return Color( pColorElement[0], pColorElement[1], pColorElement[2], pColorElement[3] );
}
Nota: Ten en cuenta que "GetColor" solo funcionaría en 'ConVar', hacerlo funcionar en 'ConVarRef' tendría que ser el mismo proceso ( Aunque no es necesario para el Game Instructor )
¡Oh! Y casi lo olvido, es necesario agregar la definición de Color, para ello en el mismo archivo solo agrega:
#include "color.h"
Al inicio, justo debajo de los demás "#include"
¡Y eso es todo! Oh bueno al menos para el Game Instructor. Si compilas tu Mod ahora solo obtendrías acceso a los comandos y a los mensajes en la consola generados por el mismo. Ahora hay que prepararlo para que empiece a funcionar...
Preparando al Game Instructor
Lo primero que debemos hacer es importar las texturas y ciertos archivos principales para que el Game Instructor pueda funcionar correctamente, para facilitar la tarea he creado un pequeño .zip que contiene estos archivos:
http://www.mediafire.com/download/8rm0xiyb3xp2lr0/GameInstructor.zip
Mirror: https://mega.co.nz/#!XQFVCBaJ!CmtqpjyjKziWgrYaJXygQHlH8g1lSzT59YC4RVZLNw8
Mirror: http://raegquit.com/beerdude26/GameInstructor(2).zip
Es un archivo pequeño ( 144 KB ) pero para los desconfiados... https://www.virustotal.com/en/file/fce648a7ca6ce0933243f231d36a258f561228fd15fc47063a7e056c7d86b35f/analysis/1379772975/
Aquí explico el contenido de los archivos/carpetas:
materials/
Contiene las texturas de los iconos que aparecerán al lado del mensaje.
resource/modevents.res
Contiene los eventos que pueden ser llamados del Servidor al Cliente. Al agregar una nueva lección será necesario agregar su respectivo evento aquí o de lo contrario no funcionara. ( Contiene los eventos principales para el Game Instructor )
scripts/instructor_lessons.txt
Yep, este será el archivo que contendrá todas las lecciones que podemos hacer aparecer con el Game Instructor. También es válido usar el archivo mod_lessons.txt ( Creandolo )
scripts/mod_textures.txt
Contiene los nombres para los iconos y la textura que usará. Desde aquí puedes agregar nuevos iconos (Claro, si ya has hecho su textura)
Nota: Algunos iconos del archivo provienen de Left 4 Dead 2 .
Nota 2: Algunos archivos como "mod_textures" y "mod_events" podrían ya existir en la carpeta de tu Mod, si es así te recomiendo "fusionar" el contenido original con el contenido del .zip. Reemplazar el archivo hará que algunas funciones dejen de funcionar.
Fuentes
Antes de crear una lección es necesario configurar las Fuentes, es decir, el tipo de letra que se usará en los mensajes y a la hora de mostrar teclas como iconos. Estos no se incluyeron en el .zip ya que es muy posible que hayas editado el archivo resource/ClientScheme.res. Así que abre el archivo mencionado y busca la sección que tiene algo similar a esto:
//////////////////////// FONTS /////////////////////////////
//
// describes all the fonts
Fonts
{
Y justo debajo pega lo siguiente:
"InstructorTitle"
{
"1"
{
"name" "Arial"
"tall" "20"
"weight" "400"
"antialias" "1"
"dropshadow" "1"
}
}
"InstructorKeyBindings"
{
"1"
{
"name" "Arial"
"name" "Trade Gothic Bold"
"tall" "18"
"weight" "600"
"antialias" "1"
"dropshadow" "0"
}
}
"InstructorButtons"
{
"1"
{
"name" "Arial"
"name" "Trade Gothic Bold"
"tall" "15"
"weight" "600"
"antialias" "1"
"dropshadow" "1"
}
}
"InstructorTitleGlow"
{
"1"
{
"name" "Arial"
"name" "Trade Gothic Bold"
"tall" "20"
"weight" "400"
"antialias" "1"
"dropshadow" "1"
}
}
Como puedes ver la he ajustado para que use el tipo de letra "Arial" que básicamente es la más común en todo el universo, puedes cambiarla si lo quieres.
Rápidamente para lo que sirven:
InstructorTitle
Este sera el tipo de letra que se usará en los mensajes.
InstructorKeyBindings
Este sera el tipo de letra que se usará en las teclas que aparecerán como iconos. ( Véase la imagen del inicio )
InstructorButtons
InstructorTitleGlow
En si indica el tipo de letra para un mensaje "brillante", sin embargo parece que no funciona... Se supone que se activa con el comando locator_text_glow
Creando lecciones
Lección de Introducción. "Usando tu arma"
Primero debemos abrir el archivo scripts/instructor_lessons.txt ( Que venia con el .zip ) Y al final del archivo ( Pero antes de la } ) debemos agregar la lección que queremos.
Para comenzar debemos decidir que nombre le pondremos a esta lección, en si solo nos servirá para poder identificarlo al momento de probar y de darle una identificación única. En este caso lo llamare "Usando tu arma" así que empezaremos por escribir:
"Usando tu arma"
{
}
Ahora es necesario indicarle que tipo de lección será, en si le indica si la lección podrá abrirse varias veces en la pantalla o solo una vez, etc... Como queremos que solo se muestre una vez tendremos que definir el valor de instance_type como 2. Además es necesario ajustar la prioridad de la lección, por ahora la dejaremos en 0
"Usando tu arma"
{
"priority" "1"
"instance_type" "2"
}
Aquí el significado original de cada número:
0 = Multiple lessons of same type can be open at once 1 = Only one of each lesson type can be open at once 2 = Replace lesson of the same type 3 = Only one instance will display at a time (but all instances will be open)
Después tendremos que indicarle el mensaje que queremos darle al usuario, ten en cuenta que es posible usar las cadenas para traducir, por ejemplo: #Instructor_UseWeapon pero en este ejemplo usaremos un mensaje normal ( No lo hagas en tu mod, hazlo con las cadenas para traducir )
"Usando tu arma"
{
"instance_type" "2"
"caption" "Disparo primario"
}
Como en si se trata de una lección en la que le enseñaremos al jugador a como disparar su arma será necesario que el icono sea un comando/tecla, para ello definiremos el valor de on_screen_icon como use_binding que en si significa "Usa un comando/tecla" la cual le definiremos en el campo binding:
"Usando tu arma"
{
"instance_type" "2"
"caption" "Disparo primario"
"onscreen_icon" "use_binding"
"binding" "+attack"
}
Como puedes observar ( Y si has practicado algo de esto en Source Engine ) sabrás que el comando +attack sirve para indicar al motor que el botón izquierdo del ratón a sido presionado, por lo que cuando esta lección sea mostrada el icono será un Ratón con el botón izquierdo en rojo ( Indicando que se debe presionar )
Como se puede ver en esta página se puede usar cualquier comando. Por ejemplo +jump mostrará la tecla "Espacio" ( Como se puede ver en la imagen del inicio ) y +use mostrará la tecla "E". Claro esta, si el jugador mantiene la configuración de los controles intacta, si por ejemplo cambio la tecla de uso (E) por U el Game Instructor mostrará la tecla U.
on_screen_icon sirve para indicar el icono que aparecerá cuando el objetivo este visible al jugador, en este caso no hay objetivo por lo que siempre aparecerá, si hubiera un objeto ( Por ejemplo: un botón ) también deberíamos incluir el campo offscreen_icon que será el icono que aparecerá cuando el objetivo no este a la vista ( Junto con una flecha )
Solo como ejemplo/recordatorio lo pondremos:
"Usando tu arma"
{
"instance_type" "2"
"caption" "Disparo primario"
"onscreen_icon" "use_binding"
"offscreen_icon" "icon_info"
"binding" "+attack"
}
Si hubiera un objetivo el icono que se mostraría sería:
Ahora es necesario indicar cuantas veces máximo puede aparecer, para esto hay 2 formas: Por veces que se ha mostrado o Por veces que se ha cumplido.
Por veces que se ha mostrado indica que cuando la lección se haya mostrado cierta cantidad de veces el mismo Ya no volverá a aparecer. Suele ser la más sencilla y se define con el campo display_limit seguido del número de veces que puede aparecer.
Por veces que se ha cumplido indica que cuando una condición ( Generalmente en el código ) se cumpla la lección se marcará como "Cumplida/Aprendida" y después de un número de veces "Aprendida" la misma dejará de aparecer. Esta es la usaremos con esta lección y es definida con el campo success_limit en la cual le ajustaremos 2 veces máximo.
"Usando tu arma"
{
"instance_type" "2"
"caption" "Disparo primario"
"onscreen_icon" "use_binding"
"offscreen_icon" "icon_info"
"binding" "+attack"
"success_limit" "2"
"timeout" "8"
}
Como puedes ver también hemos agregado el campo timeout que sencillamente define el tiempo en segundos que puede permanecer abierto, ten en cuenta que si el tiempo se termina se marcará como "Vista" y solo afectará al campo display_limit
Ahora viene lo bueno... es necesario indicarle con que evento se Abrirá esta Lección y con que otro evento se marcará como "Aprendido". En este caso usaremos los eventos: instructor_primaryattack y use_primaryattack ( En si puedes nombrarlos como quieras )
"Usando tu arma"
{
"instance_type" "2"
"caption" "Disparo primario"
"onscreen_icon" "use_binding"
"offscreen_icon" "icon_info"
"binding" "+attack"
"success_limit" "2"
"timeout" "8"
"open"
{
"instructor_primaryattack"
{
}
}
"success"
{
"use_primaryattack"
{
}
}
}
Vale, con esto le indicamos con que evento se abrirá y con que evento se marcará como "Aprendida" pero ahora hay que decirle que parámetros se recibirán del evento y como usarlos. En este caso junto con los 2 eventos enviaremos el parámetro userid que indicará el usuario que debe recibir esta lección.
Hay que recordar que los eventos son enviados del Servidor al Cliente y estos son globales es decir Todos los jugadores conectados al servidor lo recibirán por lo que con userid limitamos al Game Instructor a que solo lo muestre en el jugador que le indicamos.
"Usando tu arma"
{
"instance_type" "2"
"caption" "Disparo primario"
"onscreen_icon" "use_binding"
"offscreen_icon" "icon_info"
"binding" "+attack"
"success_limit" "2"
"timeout" "8"
"open"
{
"instructor_primaryattack"
{
"local_player is" "player userid"
"icon_target set" "player local_player"
}
}
"success"
{
"use_primaryattack"
{
"local_player is" "player userid"
"void close" "void"
}
}
}
Esta es una de las pequeñas partes difíciles de entender pero aquí vamos...
local_player is
local_player significa el "Jugador local" o en si el Cliente, por lo que con esta línea le indicamos que solo muestre esa lección si el jugador local es el jugador que le indicamos en el valor ( player userid )
player userid
Este es el valor que cada condición debe tener, la primera parte ( player ) indica el tipo de valor que se esta recibiendo, en este caso es un recurso "C_BasePlayer", si fuera una cadena de texto sería string en vez de player y si fuera una entidad entonces sería entity ( C_BaseEntity ) en vez de player.
La segunda parte ( userid ) indica el nombre del valor, en este caso le pusimos así pero puede ser cualquier otro que tu le indiques. Ya lo veras cuando lleguemos a la parte donde enviemos el evento desde el código.
icon_target set
icon_target es el objetivo donde se mostrará el icono, con esto sencillamente le establecemos ( set ) el objeto/jugador/entidad a donde ira el icono.
player local_player
Y con esto solo le decimos que el objetivo es el mismo jugador por lo que el resultado será solo el mensaje e icono mostrándose en la pantalla del mismo.
Para use_primaryattack es básicamente lo mismo solo que al final después de comprobar si es el usuario local cierra la lección ( La desaparece )
Y eso es todo para esta lección, claro que hay muchos más campos y opciones para crear lecciones más avanzadas e interactivas. Si cuentas con Left 4 Dead 2 te recomiendo ver su archivo instructor_lessons.txt para darte una idea. ( Se encuentra dentro del archivo VPK: pak01_dir.vpk )
Por último es necesario agregar los eventos ( instructor_primaryattack y use_primaryattack ) al archivo resource/modevents.res, ábrelo y al final ( Pero antes de } ) escribe:
"instructor_primaryattack"
{
"userid" "short"
}
"use_primaryattack"
{
"userid" "short"
}
¡Siéntente libre de incorporar nuevas lecciones a este tutorial!
Incorporando las lecciones en el código
Como dijimos anteriormente para hacer que una lección se abra ( se muestre ) es necesario enviar el evento que registramos para el mismo desde el código, para ello y tomando como referencia la lección de Introducción "Usando tu arma" haremos lo siguiente:
"Usando tu arma"
Esta lección le muestra al usuario a como disparar un arma por lo que lo lógico sería que se mostrará cuando el mismo obtiene un arma, para ello hay que dirigirnos al archivo game/server/hl2mp/hl2mp_player.cpp ( Así es, dejamos Cliente y ahora entramos a Server ) y ubicaremos la función:
void CHL2MP_Player::Spawn(void)
¡Nota!: Como sabemos, en Half-Life 2: Deathmatch los jugadores obtienen armas al empezar, como ejemplo mostraremos la lección en la función "Spawn" después de que el jugador obtiene las armas básicas. Si tu MOD no empieza con armas al empezar, un mejor lugar para hacer esto sería en la función "Weapon_Equip" ( Hay que crearla )
Al final de la función escribiremos:
IGameEvent *pEvent = gameeventmanager->CreateEvent("instructor_primaryattack");
if ( pEvent )
{
pEvent->SetInt("userid", GetUserID());
gameeventmanager->FireEvent(pEvent);
}
Si compilamos nuestro Mod podremos ver por primera vez el Game Instructor en acción:
http://www.youtube.com/watch?v=pGMZyv3_5b4&hd=1
January 2024