diff --git a/README.md b/README.md index a8065d2d91d..12d1243f1fc 100644 --- a/README.md +++ b/README.md @@ -238,6 +238,14 @@ Other sources: //------------------------------------------------------------------------------------------------------------------------- +Mapbase additionally contains the following features originally created for Team Fortress 2 Classic: + +- TF-Style Instructor Hint Background by MaartenS11 +- TF-Style Instructor Hint Icons by Wheat (This is asset-based and not reflected in the code) +- env_instructor_hint team and class support by azzy + +//------------------------------------------------------------------------------------------------------------------------- + If there is anything missing from this list, please contact Blixibon. //========================================================================================================================= diff --git a/src/game/client/c_baselesson.cpp b/src/game/client/c_baselesson.cpp index ec01575bb89..5c308a47fb1 100644 --- a/src/game/client/c_baselesson.cpp +++ b/src/game/client/c_baselesson.cpp @@ -18,6 +18,9 @@ #include "vstdlib/IKeyValuesSystem.h" #ifdef MAPBASE #include "usermessages.h" +#ifdef TF_CLIENT_DLL +#include "c_tf_player.h" +#endif #endif // memdbgon must be the last include file in a .cpp file!!! @@ -448,6 +451,8 @@ void CIconLesson::Init() m_iIconTargetPos = ICON_TARGET_EYE_POSITION; m_szHudHint = ""; + m_iHintEntSpawnFlags = 0; + m_iHintEntTeam = 0; #else m_szCaptionColor = "255,255,255";// Default to white #endif @@ -701,6 +706,36 @@ bool CIconLesson::ShouldDisplay() const } } +#ifdef MAPBASE + if ( m_iHintEntSpawnFlags || m_iHintEntTeam ) + { + C_BasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer(); + if ( pLocalPlayer ) + { +#ifdef TF_CLIENT_DLL + C_TFPlayer *pTFPlayer = static_cast( pLocalPlayer ); + if ( m_iHintEntSpawnFlags ) + { + int iClass = pTFPlayer->GetPlayerClass()->GetClassIndex(); + int bits = pow(2, iClass); + if ( !(m_iHintEntSpawnFlags & bits) ) + { + return false; + } + } +#endif + if ( m_iHintEntTeam ) + { + int iTeam = pLocalPlayer->GetTeamNumber(); + if ( m_iHintEntTeam != iTeam ) + { + return false; + } + } + } + } +#endif + // Ok to display return true; } @@ -1033,6 +1068,9 @@ Vector CIconLesson::GetIconTargetPosition( C_BaseEntity *pIconTarget ) \ LESSON_VARIABLE_MACRO( ICON_TARGET_POS, m_iIconTargetPos, int ) \ LESSON_VARIABLE_MACRO_STRING( HUD_HINT_AFTER_LEARNED, m_szHudHint, CGameInstructorSymbol ) \ + \ + LESSON_VARIABLE_MACRO( ENT_SPAWNFLAGS, m_iHintEntSpawnFlags, int ) \ + LESSON_VARIABLE_MACRO( ENT_TEAM, m_iHintEntTeam, int ) \ // Create keyvalues name symbol @@ -1298,6 +1336,22 @@ void CScriptedIconLesson::Init() // Initialize from the key value file InitFromKeys( GetGameInstructor().GetScriptKeys() ); +#if defined(MAPBASE) && defined(TF_CLIENT_DLL) + // Enable tf_scan_potential_use_target if this lesson utilizes use_target + extern ConVar tf_scan_potential_use_target; + CGameInstructorSymbol szUseTargetEvent( "use_target" ); + + CUtlVector< LessonEvent_t > &openEvents = m_OpenEvents; + for ( int i = 0; i < openEvents.Count(); i++ ) + { + if ( openEvents[i].szEventName == szUseTargetEvent ) + { + tf_scan_potential_use_target.SetValue( 1 ); + break; + } + } +#endif + if ( m_iPriority >= LESSON_PRIORITY_MAX ) { DevWarning( "Priority level not set for lesson: %s\n", GetName() ); @@ -1606,7 +1660,11 @@ void CScriptedIconLesson::ProcessOpenGameEvents( const CScriptedIconLesson *pRoo } MEM_ALLOC_CREDIT(); +#ifdef MAPBASE + CScriptedIconLesson *pOpenLesson = CreateLessonCopy(); +#else CScriptedIconLesson *pOpenLesson = new CScriptedIconLesson( GetName(), false, true ); +#endif // Run copy macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) #define LESSON_VARIABLE_MACRO LESSON_VARIABLE_COPY @@ -2657,13 +2715,13 @@ bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const ch 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 bModHandled = false; bool bModReturn = Mod_ProcessElementAction( iAction, bNot, pchVarName, hVar, pchParamName, fParam, pParam, pchParam, bModHandled ); if ( bModHandled ) { return bModReturn; - }*/ + } C_BaseEntity *pVar = hVar.Get(); @@ -3619,7 +3677,7 @@ bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const ch return true; } - /*case LESSON_ACTION_GET_POTENTIAL_USE_TARGET: + case LESSON_ACTION_GET_POTENTIAL_USE_TARGET: { int iTemp = static_cast( fParam ); @@ -3670,7 +3728,7 @@ bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const ch } return true; - }*/ + } } DevWarning( "Invalid lesson action type used with \"%s\" variable type.\n", pchVarName ); @@ -3891,5 +3949,5 @@ void CScriptedIconLesson::PreReadLessonsFromFile() CScriptedIconLesson::LessonActionMap.Insert( "get potential use target", LESSON_ACTION_GET_POTENTIAL_USE_TARGET ); // Add mod actions to the map - //Mod_PreReadLessonsFromFile(); + Mod_PreReadLessonsFromFile(); } diff --git a/src/game/client/c_baselesson.h b/src/game/client/c_baselesson.h index b413d282ea0..02b4c89c0b1 100644 --- a/src/game/client/c_baselesson.h +++ b/src/game/client/c_baselesson.h @@ -277,6 +277,9 @@ class CIconLesson : public CTextLesson }; CGameInstructorSymbol m_szHudHint; + + int m_iHintEntSpawnFlags; + int m_iHintEntTeam; #endif }; @@ -402,6 +405,10 @@ class CScriptedIconLesson : public CIconLesson bool ProcessElements( IGameEvent *event, const CUtlVector< LessonElement_t > *pElements ); +#ifdef MAPBASE + virtual CScriptedIconLesson *CreateLessonCopy() { return new CScriptedIconLesson( GetName(), false, true ); } +#endif + private: void InitElementsFromKeys( CUtlVector< LessonElement_t > *pLessonElements, KeyValues *pKey ); void InitElementsFromElements( CUtlVector< LessonElement_t > *pLessonElements, const CUtlVector< LessonElement_t > *pLessonElements2 ); @@ -428,6 +435,7 @@ class CScriptedIconLesson : public CIconLesson private: static CUtlDict< int, int > LessonActionMap; +protected: EHANDLE m_hLocalPlayer; float m_fOutput; CHandle m_hEntity1; @@ -439,6 +447,7 @@ class CScriptedIconLesson : public CIconLesson float m_fFloat1; float m_fFloat2; +private: CUtlVector< CGameInstructorSymbol > m_PrerequisiteNames; CUtlVector< LessonEvent_t > m_OpenEvents; CUtlVector< LessonEvent_t > m_CloseEvents; diff --git a/src/game/client/c_baseplayer.h b/src/game/client/c_baseplayer.h index b6ffbfe1c27..c6c1b5dde14 100644 --- a/src/game/client/c_baseplayer.h +++ b/src/game/client/c_baseplayer.h @@ -319,6 +319,9 @@ class C_BasePlayer : public C_BaseCombatCharacter, public CGameEventListener bool IsPoisoned( void ) { return m_Local.m_bPoisoned; } C_BaseEntity *GetUseEntity(); +#ifdef MAPBASE // From Alien Swarm SDK + virtual C_BaseEntity *GetPotentialUseEntity() { return GetUseEntity(); } +#endif // Vehicles... IClientVehicle *GetVehicle(); diff --git a/src/game/client/c_gameinstructor.cpp b/src/game/client/c_gameinstructor.cpp index ff5f0e538ea..e37dc7491e4 100644 --- a/src/game/client/c_gameinstructor.cpp +++ b/src/game/client/c_gameinstructor.cpp @@ -171,8 +171,7 @@ void C_GameInstructor::Shutdown() //========================================================= void C_GameInstructor::UpdateHiddenByOtherElements() { - //bool bHidden = Mod_HiddenByOtherElements(); - bool bHidden = false; + bool bHidden = Mod_HiddenByOtherElements(); if ( bHidden && !m_bHiddenDueToOtherElements ) StopAllLessons(); @@ -1276,8 +1275,23 @@ void C_GameInstructor::ReadLessonsFromFile( const char *pchFileName ) continue; } +#ifdef MAPBASE + const char *pszLessonType = m_pScriptKeys->GetString( "lesson_type", "locator" ); + if ( pszLessonType ) + { + if (FStrEq( pszLessonType, "locator" )) + { + DefineLesson( new CScriptedIconLesson( m_pScriptKeys->GetName(), false, false ) ); + } + else if (!Mod_DefineLessonType( m_pScriptKeys->GetName(), pszLessonType )) + { + Warning( "Lesson \"%s\" has unknown type \"%s\"\n", m_pScriptKeys->GetName(), pszLessonType ); + } + } +#else CScriptedIconLesson *pNewLesson = new CScriptedIconLesson(m_pScriptKeys->GetName(), false, false); GetGameInstructor().DefineLesson(pNewLesson); +#endif } m_pScriptKeys = NULL; diff --git a/src/game/client/c_gameinstructor.h b/src/game/client/c_gameinstructor.h index 14ae908caa2..d905774508e 100644 --- a/src/game/client/c_gameinstructor.h +++ b/src/game/client/c_gameinstructor.h @@ -47,6 +47,10 @@ class C_GameInstructor : public CAutoGameSystemPerFrame, public CGameEventListen void DefineLesson( CBaseLesson *pLesson ); +#ifdef MAPBASE + bool Mod_DefineLessonType( const char *pszLessonName, const char *pszLessonType ); +#endif + const CBaseLesson * GetLesson( const char *pchLessonName ); bool IsLessonOfSameTypeOpen( const CBaseLesson *pLesson ) const; @@ -60,6 +64,7 @@ class C_GameInstructor : public CAutoGameSystemPerFrame, public CGameEventListen void PlaySound( const char *pchSoundName ); bool OpenOpportunity( CBaseLesson *pLesson ); + void CloseOpportunity( CBaseLesson *pLesson ); void DumpOpenOpportunities( void ); @@ -83,7 +88,6 @@ class C_GameInstructor : public CAutoGameSystemPerFrame, public CGameEventListen void StopAllLessons( void ); void CloseAllOpenOpportunities( void ); - void CloseOpportunity( CBaseLesson *pLesson ); void InitLessonPrerequisites( void ); diff --git a/src/game/client/c_mod_lesson_stubs.cpp b/src/game/client/c_mod_lesson_stubs.cpp new file mode 100644 index 00000000000..eee2c7235b0 --- /dev/null +++ b/src/game/client/c_mod_lesson_stubs.cpp @@ -0,0 +1,89 @@ +//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============// +// +// Purpose: Stub for custom mod lesson actions. +// This is so that mods can do actions +// Remove this file from the mod's vpc and include your own. +// +//=============================================================================// + +#include "cbase.h" + +#include "c_gameinstructor.h" +#include "c_baselesson.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +extern ConVar gameinstructor_verbose; + + +enum Mod_LessonAction +{ + // Enum starts from end of LessonAction + LESSON_ACTION_MOD_CUSTOM_ACTION_STUB = LESSON_ACTION_MOD_START, + + LESSON_ACTION_TOTAL +}; + + +void CScriptedIconLesson::Mod_PreReadLessonsFromFile( void ) +{ + // Add custom actions to the map + CScriptedIconLesson::LessonActionMap.Insert( "custom action stub", LESSON_ACTION_MOD_CUSTOM_ACTION_STUB ); +} + + +bool CScriptedIconLesson::Mod_ProcessElementAction( int iAction, bool bNot, const char *pchVarName, EHANDLE &hVar, const CGameInstructorSymbol *pchParamName, float fParam, C_BaseEntity *pParam, const char *pchParam, bool &bModHandled ) +{ + // Assume we're going to handle the action + bModHandled = true; + + C_BaseEntity *pVar; + pVar = hVar.Get(); + + switch ( iAction ) + { + case LESSON_ACTION_MOD_CUSTOM_ACTION_STUB: + { + float flStub = 0.0f; //pVar->GetStubValue(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetStubValue() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%.1f ", flStub ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%.1f\n", fParam ); + } + + return ( flStub ) ? ( flStub >= fParam ) : ( flStub < fParam ); + } + + default: + // Didn't handle this action + bModHandled = false; + break; + } + + return false; +} + + +bool C_GameInstructor::Mod_HiddenByOtherElements( void ) +{ + return false; +} + + +#ifdef MAPBASE +// +// This function allows you to add new lesson types beyond the default icon hints. +// Use this to integrate instructor lessons into another game-specific HUD element. +// +// Note that, due to some technical constraints, your custom type must still derive from CScriptedIconLesson. +// +bool C_GameInstructor::Mod_DefineLessonType( const char *pszLessonName, const char *pszLessonType ) +{ + return false; +} +#endif diff --git a/src/game/client/client_mapbase.vpc b/src/game/client/client_mapbase.vpc index 12f3382bbf6..48c6201d1fc 100644 --- a/src/game/client/client_mapbase.vpc +++ b/src/game/client/client_mapbase.vpc @@ -4,9 +4,6 @@ // Project Script //----------------------------------------------------------------------------- -$Include "$SRCDIR\game\client\client_mapbase_hl2.vpc" [$HL2||$EPISODIC||$HL2MP] -$Include "$SRCDIR\game\client\client_mapbase_tf.vpc" [$TF] - $Configuration { $Compiler @@ -30,6 +27,7 @@ $Project $File "c_baselesson.h" $File "c_gameinstructor.cpp" $File "c_gameinstructor.h" + $File "c_mod_lesson_stubs.cpp" $File "hud_locator_target.cpp" $File "hud_locator_target.h" $File "c_postprocesscontroller.cpp" @@ -81,3 +79,6 @@ $Project $Lib "raytrace" } } + +$Include "$SRCDIR\game\client\client_mapbase_hl2.vpc" [$HL2||$EPISODIC||$HL2MP] +$Include "$SRCDIR\game\client\client_mapbase_tf.vpc" [$TF] diff --git a/src/game/client/client_mapbase_tf.vpc b/src/game/client/client_mapbase_tf.vpc index a55794757a4..78d79585961 100644 --- a/src/game/client/client_mapbase_tf.vpc +++ b/src/game/client/client_mapbase_tf.vpc @@ -6,6 +6,11 @@ $Project { + $Folder "Source Files" + { + -$File "c_mod_lesson_stubs.cpp" + } + $Folder "Source Files" { $Folder "Mapbase" @@ -13,6 +18,8 @@ $Project $Folder "TF" { $File "mapbase\tf\tf_hud_external_timer.cpp" + $File "mapbase\c_tf_lesson.cpp" + $File "mapbase\c_tf_lesson.h" } } } diff --git a/src/game/client/hud.cpp b/src/game/client/hud.cpp index 4afd7044503..ec3a99172cf 100644 --- a/src/game/client/hud.cpp +++ b/src/game/client/hud.cpp @@ -1236,6 +1236,9 @@ void CHudIcons::Init() LoadHudTextures( textureList, "scripts/instructor_textures", NULL ); #ifdef HL2_CLIENT_DLL LoadHudTextures( textureList, "scripts/instructor_textures_hl2", NULL ); +#endif +#ifdef TF_CLIENT_DLL + LoadHudTextures( textureList, "scripts/instructor_textures_tf", NULL ); #endif LoadHudTextures( textureList, "scripts/instructor_modtextures", NULL ); diff --git a/src/game/client/hud_locator_target.cpp b/src/game/client/hud_locator_target.cpp index dead20ed889..f8a63304e7f 100644 --- a/src/game/client/hud_locator_target.cpp +++ b/src/game/client/hud_locator_target.cpp @@ -67,7 +67,11 @@ ConVar locator_start_at_crosshair( "locator_start_at_crosshair", "0", FCVAR_NONE ConVar locator_topdown_style( "locator_topdown_style", "0", FCVAR_NONE, "Topdown games set this to handle distance and offscreen location differently." ); +#if defined(TF_CLIENT_DLL) && defined(MAPBASE) +ConVar locator_background_style( "locator_background_style", "1", FCVAR_NONE, "Setting this to 1 will show rectangle backgrounds behind the items word-bubble pointers." ); +#else ConVar locator_background_style( "locator_background_style", "0", FCVAR_NONE, "Setting this to 1 will show rectangle backgrounds behind the items word-bubble pointers." ); +#endif 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." ); @@ -715,6 +719,10 @@ class CLocatorPanel : public vgui::EditablePanel 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 ); +#ifdef MAPBASE + int DrawTargetBackgroundCaption( CLocatorTarget *pTarget, int x, int y ); + bool IsUsingBackground( CLocatorTarget *pTarget = NULL ); +#endif 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 ); @@ -762,6 +770,12 @@ class CLocatorPanel : public vgui::EditablePanel int m_staticIconPosition;// Helps us stack static icons CLocatorTarget m_targets[MAX_LOCATOR_TARGETS]; + +#ifdef MAPBASE + vgui::Panel* m_pBackgroundPanel; + vgui::Label* m_pCaptionLabel; + vgui::Label* m_pCaptionLabelShadow; +#endif }; //----------------------------------------------------------------------------- @@ -852,6 +866,12 @@ CLocatorPanel::CLocatorPanel( Panel *parent, const char *name ) : EditablePanel( m_textureID_ArrowUp = -1; m_textureID_ArrowDown = -1; m_textureID_SimpleArrow = -1; + +#ifdef MAPBASE + m_pBackgroundPanel = NULL; + m_pCaptionLabel = NULL; + m_pCaptionLabelShadow = NULL; +#endif } //----------------------------------------------------------------------------- @@ -870,6 +890,14 @@ void CLocatorPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); LoadControlSettings("resource/UI/Locator.res"); + +#ifdef MAPBASE + m_pBackgroundPanel = FindChildByName( "LocatorBG" ); + m_pCaptionLabel = dynamic_cast( FindChildByName( "CaptionLabel" ) ); + if ( m_pCaptionLabel ) m_pCaptionLabel->SetParent( m_pBackgroundPanel ); + m_pCaptionLabelShadow = dynamic_cast( FindChildByName( "CaptionLabelShadow" ) ); + if ( m_pCaptionLabelShadow ) m_pCaptionLabelShadow->SetParent( m_pBackgroundPanel ); +#endif } //----------------------------------------------------------------------------- @@ -879,10 +907,12 @@ void CLocatorPanel::PerformLayout( void ) { BaseClass::PerformLayout(); +#if !defined(TF_CLIENT_DLL) || !defined(MAPBASE) vgui::Panel *pPanel = FindChildByName( "LocatorBG" ); if ( pPanel ) pPanel->SetPos( (GetWide() - pPanel->GetWide()) * 0.5, (GetTall() - pPanel->GetTall()) * 0.5 ); +#endif } //----------------------------------------------------------------------------- @@ -937,7 +967,13 @@ void CLocatorPanel::CollectGarbage() if( m_targets[ i ].m_isActive ) { if( gpGlobals->framecount - m_targets[ i ].m_frameLastUpdated > 20 ) + { +#ifdef MAPBASE + if ( IsUsingBackground( &m_targets[ i ] ) && m_pBackgroundPanel ) + m_pBackgroundPanel->SetVisible( false ); +#endif m_targets[ i ].Deactivate(); + } } } } @@ -1549,6 +1585,10 @@ void CLocatorPanel::PaintTarget( CLocatorTarget *pTarget ) // 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; +#ifdef MAPBASE + if ( IsUsingBackground( pTarget ) && m_pBackgroundPanel ) + m_pBackgroundPanel->SetVisible( false ); +#endif } else { @@ -1566,6 +1606,7 @@ void CLocatorPanel::PaintTarget( CLocatorTarget *pTarget ) //----------------------------------------------------------------------------- void CLocatorPanel::DrawPointerBackground( CLocatorTarget *pTarget, int nPointerX, int nPointerY, int nWide, int nTall, bool bPointer ) { +#ifndef MAPBASE // Not used with restored locator_background_style if ( locator_background_style.GetInt() == 0 || pTarget->m_alpha == 0 ) return; @@ -1590,6 +1631,7 @@ void CLocatorPanel::DrawPointerBackground( CLocatorTarget *pTarget, int nPointer 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 ); +#endif } //----------------------------------------------------------------------------- @@ -1613,7 +1655,17 @@ void CLocatorPanel::DrawStaticIcon( CLocatorTarget *pTarget ) AnimateIconAlpha( pTarget->GetIconEffectsFlags(), &pTarget->m_alpha, pTarget->m_fadeStart ); // Figure out the caption width - pTarget->m_captionWide = GetScreenWidthForCaption( pTarget->GetCaptionText(), m_hCaptionFont ); +#ifdef MAPBASE + if ( IsUsingBackground( pTarget ) ) + { + // Setup text and figure out the caption width. The caption width will be used to move the arrow next to the caption if it is on the right. + pTarget->m_captionWide = DrawTargetBackgroundCaption(pTarget, pTarget->GetIconX() + iconWide + ICON_GAP, pTarget->GetIconCenterY()) + YRES(5); // 5 padding. + } + else +#endif + { + pTarget->m_captionWide = GetScreenWidthForCaption( pTarget->GetCaptionText(), m_hCaptionFont ); + } bool bDrawMultilineCaption = false; @@ -1712,7 +1764,20 @@ void CLocatorPanel::DrawStaticIcon( CLocatorTarget *pTarget ) } } - DrawTargetCaption( pTarget, pTarget->GetIconX() + iconWide + ICON_GAP, pTarget->GetIconCenterY(), bDrawMultilineCaption ); +#ifdef MAPBASE + if ( IsUsingBackground( pTarget ) && m_pBackgroundPanel ) + { + m_pBackgroundPanel->SetVisible( true ); + m_pBackgroundPanel->SetAlpha( pTarget->m_alpha ); + + DrawTargetBackgroundCaption( pTarget, pTarget->GetIconX() + iconWide + ICON_GAP, pTarget->GetIconCenterY() ); + } + else +#endif + { + 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 ); @@ -1747,6 +1812,10 @@ void CLocatorPanel::DrawDynamicIcon( CLocatorTarget *pTarget, bool bDrawCaption, if( pTarget->m_bOccluded && !( (pTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_FORCE_CAPTION) || locator_topdown_style.GetBool() ) ) { +#ifdef MAPBASE + if ( IsUsingBackground( pTarget ) && m_pBackgroundPanel ) + m_pBackgroundPanel->SetVisible( false ); +#endif return; } @@ -1761,24 +1830,41 @@ void CLocatorPanel::DrawDynamicIcon( CLocatorTarget *pTarget, bool bDrawCaption, iWide /= pTarget->m_widthScale_onscreen; } - // Figure out the caption width - pTarget->m_captionWide = GetScreenWidthForCaption( pTarget->GetCaptionText(), m_hCaptionFont ); - + // Figure out the caption width bool bDrawMultilineCaption = false; - - if ( m_iShouldWrapStaticLocators > 0 ) // conditionalized in locator.res + +#ifdef MAPBASE + if ( IsUsingBackground( pTarget ) && m_pCaptionLabel ) { - if ( pTarget->m_captionWide > ( ScreenWidth() * locator_split_maxwide_percent.GetFloat() ) ) + m_pCaptionLabel->SetText( pTarget->GetCaptionText() ); + int captionWidth, captionHeight; + m_pCaptionLabel->GetContentSize( captionWidth, captionHeight ); + pTarget->m_captionWide = captionWidth; + } + else +#endif + { + pTarget->m_captionWide = GetScreenWidthForCaption( pTarget->GetCaptionText(), m_hCaptionFont ); + + if ( m_iShouldWrapStaticLocators > 0 ) // conditionalized in locator.res { - // we will double-line this - pTarget->m_captionWide = pTarget->m_captionWide * locator_split_len.GetFloat(); - bDrawMultilineCaption = true; + 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() ); + +#ifdef MAPBASE + if ( IsUsingBackground( pTarget ) && m_pBackgroundPanel ) + m_pBackgroundPanel->SetVisible( pTarget->m_bOnscreen && pTarget->m_isActive && bShouldDrawCaption && bDrawCaption ); +#endif if( pTarget->m_bOnscreen && bDrawCaption && bShouldDrawCaption ) { @@ -1883,7 +1969,9 @@ void CLocatorPanel::DrawDynamicIcon( CLocatorTarget *pTarget, bool bDrawCaption, DrawBindingName( pTarget, pTarget->DrawBindingName(), pTarget->GetIconX() + (iWide>>1), pTarget->GetIconY() + (pTarget->m_tall>>1), pTarget->m_bDrawControllerButtonOffscreen ); } +#ifndef MAPBASE if ( locator_background_style.GetInt() == 0 ) +#endif { // Draw the arrow. DrawIndicatorArrow( pTarget->GetIconX(), pTarget->GetIconY(), iWide, pTarget->m_tall, 0, pTarget->m_drawArrowDirection ); @@ -1894,6 +1982,13 @@ void CLocatorPanel::DrawDynamicIcon( CLocatorTarget *pTarget, bool bDrawCaption, if( bDrawCaption ) { //ScreenWidth() * * pTarget->m_widthScale_onscreen +#ifdef MAPBASE + if ( IsUsingBackground( pTarget ) && m_pCaptionLabel ) + { + DrawTargetBackgroundCaption( pTarget, pTarget->GetIconCenterX() + ICON_GAP + pTarget->GetIconWidth() * 0.5, pTarget->GetIconCenterY() ); + } + else +#endif DrawTargetCaption( pTarget, pTarget->GetIconCenterX() + ICON_GAP + pTarget->GetIconWidth() * 0.5, pTarget->GetIconCenterY(), bDrawMultilineCaption ); } if ( pTarget->DrawBindingName() ) @@ -1996,6 +2091,63 @@ void CLocatorPanel::DrawTargetCaption( CLocatorTarget *pTarget, int x, int y, bo } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Some targets have text captions. Draw the text. +//----------------------------------------------------------------------------- +int CLocatorPanel::DrawTargetBackgroundCaption( CLocatorTarget *pTarget, int x, int y ) +{ + if (!m_pCaptionLabel || !m_pBackgroundPanel) + return 0; + + // Draw text and background. + m_pCaptionLabel->SetText(pTarget->GetCaptionText()); + int distanceWide, distanceTall; + m_pCaptionLabel->GetContentSize(distanceWide, distanceTall); + distanceWide += XRES(24); + distanceTall += YRES(18); + m_pCaptionLabel->SetSize(distanceWide, distanceTall); + + int fontTall = vgui::surface()->GetFontTall(m_pCaptionLabel->GetFont()); + m_pBackgroundPanel->SetPos(x - 1, y - (fontTall / 2) - 1 - YRES(18 / 2)); + m_pBackgroundPanel->SetSize(distanceWide, distanceTall); + m_pBackgroundPanel->SetPaintBackgroundEnabled(locator_background_style.GetInt() == 1); + + if (!m_pCaptionLabelShadow) + return distanceWide; + + // Set text for the shadow label. + m_pCaptionLabelShadow->SetText(pTarget->GetCaptionText()); + m_pCaptionLabelShadow->SetSize(distanceWide, distanceTall); + m_pCaptionLabelShadow->SetVisible(locator_text_drop_shadow.GetBool()); + + return distanceWide; +} + +//----------------------------------------------------------------------------- +// Purpose: Whether or not to draw with the background +//----------------------------------------------------------------------------- +bool CLocatorPanel::IsUsingBackground( CLocatorTarget *pTarget ) +{ + if ( pTarget ) + { + // HACKHACK: Since we have only one background and label, just use them on the first hint. + // It is very rare for multiple hints to show up, although this could still be done better. + if ( pTarget->m_serialNumber != m_targets[0].m_serialNumber ) + return false; + + // No need for a background if we have no text + if ( !pTarget->GetCaptionText() || !*pTarget->GetCaptionText() ) + return false; + } + + if ( locator_background_style.GetInt() >= 1 ) + return true; + + return false; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Figure out how wide (pixels) a string will be if rendered with this font // @@ -2202,6 +2354,11 @@ void CLocatorPanel::RemoveTarget( int hTarget ) if( pTarget ) { +#ifdef MAPBASE + if ( IsUsingBackground( pTarget ) && m_pBackgroundPanel ) + m_pBackgroundPanel->SetVisible( false ); +#endif + pTarget->Deactivate(); } } diff --git a/src/game/client/mapbase/c_tf_lesson.cpp b/src/game/client/mapbase/c_tf_lesson.cpp new file mode 100644 index 00000000000..707be43e01c --- /dev/null +++ b/src/game/client/mapbase/c_tf_lesson.cpp @@ -0,0 +1,787 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: TF2 lesson actions + new Game Instructor lesson types which use TF HUD elements +// +// Author: Blixibon +// +//=============================================================================// + +#include "cbase.h" + +#include "c_tf_lesson.h" +#include "tf_hud_training.h" +#include "tf_hud_objectivestatus.h" +#include "tf_hud_notification_panel.h" + +//----------------------------------------------------------------------------- + +extern ConVar gameinstructor_verbose; + +enum Mod_LessonAction +{ + // Enum starts from end of LessonAction + LESSON_ACTION_IS_TF_CLASS = LESSON_ACTION_MOD_START, + LESSON_ACTION_IS_TF_GAMEMODE, + LESSON_ACTION_IS_TRAINING, + LESSON_ACTION_IS_ROUND_STATE, + + LESSON_ACTION_WEAPON_ID_HAS, + LESSON_ACTION_WEAPON_ATTR_HAS, + + LESSON_ACTION_ENEMY_TEAM_HAS_TF_WEAPON, + + //LESSON_ACTION_IN_RESPAWN_ROOM, + + LESSON_ACTION_TOTAL +}; + + +void CScriptedIconLesson::Mod_PreReadLessonsFromFile( void ) +{ + // Add custom actions to the map + CScriptedIconLesson::LessonActionMap.Insert( "is class", LESSON_ACTION_IS_TF_CLASS ); + CScriptedIconLesson::LessonActionMap.Insert( "is gamemode", LESSON_ACTION_IS_TF_GAMEMODE ); + CScriptedIconLesson::LessonActionMap.Insert( "is training", LESSON_ACTION_IS_TRAINING ); + CScriptedIconLesson::LessonActionMap.Insert( "is roundstate", LESSON_ACTION_IS_ROUND_STATE ); + + CScriptedIconLesson::LessonActionMap.Insert( "weapon id has", LESSON_ACTION_WEAPON_ID_HAS ); + CScriptedIconLesson::LessonActionMap.Insert( "weapon attr has", LESSON_ACTION_WEAPON_ATTR_HAS ); + + CScriptedIconLesson::LessonActionMap.Insert( "enemy team has weapon", LESSON_ACTION_ENEMY_TEAM_HAS_TF_WEAPON ); +} + +bool CScriptedIconLesson::Mod_ProcessElementAction( int iAction, bool bNot, const char *pchVarName, EHANDLE &hVar, const CGameInstructorSymbol *pchParamName, float fParam, C_BaseEntity *pParam, const char *pchParam, bool &bModHandled ) +{ + // Assume we're going to handle the action + bModHandled = true; + + C_BaseEntity *pVar; + pVar = hVar.Get(); + + switch ( iAction ) + { + case LESSON_ACTION_IS_TF_CLASS: + { + const char *pszPlayerClass = "undefined"; + + C_TFPlayer *pTFPlayer = dynamic_cast( pVar ); + if ( pTFPlayer ) + { + C_TFPlayerClass *pClass = pTFPlayer->GetPlayerClass(); + if (pClass) + { + pszPlayerClass = pClass->GetName(); + } + } + + bool bIsClass = FStrEq( pchParam, pszPlayerClass ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->IsTFClass() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%s ", pszPlayerClass ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " != [%s] " ) : ( " == [%s] " ), pchParam ); + } + + return bNot ? !bIsClass : bIsClass; + } + + case LESSON_ACTION_IS_TF_GAMEMODE: + { + bool bIsGameMode = false; + const char *pszGameType = "undefined"; + + if ( TFGameRules() ) + { + // Special cases first, then determine directly from game type + if ( FStrEq( pchParam, "plr" ) ) + { + bIsGameMode = TFGameRules()->HasMultipleTrains(); + pszGameType = bIsGameMode ? "plr" : "not plr"; + } + else if ( TFGameRules()->GetGameType() != TF_GAMETYPE_UNDEFINED ) + { + // The +10 is to skip the localization prefix + // (removing #Gametype from "#Gametype_CTF") + pszGameType = (TFGameRules()->GetGameTypeName() + 10); + bIsGameMode = FStrEq( pszGameType, pchParam ); + } + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->IsTFGamemode() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%s ", pszGameType ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " != [%s] " ) : ( " == [%s] " ), pchParam ); + } + + return bNot ? !bIsGameMode : bIsGameMode; + } + + case LESSON_ACTION_IS_TRAINING: + { + bool bIsTraining = false; + + if ( TFGameRules() ) + { + bIsTraining = TFGameRules()->IsInTraining(); + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->IsInTraining() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " != [%s] " ) : ( " == [%s] " ), bIsTraining ? "true" : "false" ); + } + + return bNot ? !bIsTraining : bIsTraining; + } + + case LESSON_ACTION_IS_ROUND_STATE: + { + bool bIsRoundState = false; + const char *pszRoundState = "undefined"; + + if ( TFGameRules() ) + { + int nRoundState = TFGameRules()->State_Get(); + switch (nRoundState) + { + case GR_STATE_PREROUND: pszRoundState = "preround"; break; + case GR_STATE_RND_RUNNING: pszRoundState = "running"; break; + case GR_STATE_TEAM_WIN: pszRoundState = "win"; break; + case GR_STATE_RESTART: pszRoundState = "restart"; break; + case GR_STATE_STALEMATE: pszRoundState = "stalemate"; break; + case GR_STATE_GAME_OVER: pszRoundState = "gameover"; break; + case GR_STATE_BONUS: pszRoundState = "bonus"; break; + case GR_STATE_BETWEEN_RNDS: pszRoundState = "between"; break; + } + + bIsRoundState = FStrEq( pszRoundState, pchParam ); + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->IsTFRoundState() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%s ", pszRoundState ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " != [%s] " ) : ( " == [%s] " ), pchParam ); + } + + return bNot ? !bIsRoundState : bIsRoundState; + } + + case LESSON_ACTION_WEAPON_ID_HAS: + { + C_BaseCombatCharacter *pBCC = NULL; + C_BaseCombatWeapon *pWeapon = NULL; + + if ( pVar ) + { + pBCC = pVar->MyCombatCharacterPointer(); + if ( !pBCC ) + { + pWeapon = pVar->MyCombatWeaponPointer(); + } + } + + if ( !pBCC && !pWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->HasWeaponID([%llu] " ) : ( "\t[%s]->HasWeaponID([%llu] " ), pchVarName, (itemid_t)fParam ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter or BaseCombatWeapon returned NULL!\n" ); + } + + return false; + } + + if ( pBCC ) + { + for (int i=0;iGetWeapon(i) ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->HasWeaponID([%llu]) " ) : ( "\t[%s]->HasWeaponID([%llu]) " ), pchVarName, (itemid_t)fParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " != [%hu (%s)]\n" ) : ( " == [%hu (%s)]\n" ), pBCC->GetWeapon(i)->GetAttributeContainer()->GetItem()->GetItemDefIndex(), pBCC->GetWeapon(i)->GetClassname() ); + } + + if ( pBCC->GetWeapon(i)->GetAttributeContainer()->GetItem()->GetItemDefIndex() == (itemid_t)fParam ) + { + pWeapon = pBCC->GetWeapon(i); + break; + } + } + } + + /*if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->HasWeaponID([%llu]) " ) : ( "\t[%s]->HasWeaponID([%llu]) " ), pchVarName, (itemid_t)fParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " != [%llu] " ) : ( " == [%llu] " ), (itemid_t)fParam ); + }*/ + } + else if ( pWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->HasWeaponID([%llu]) " ) : ( "\t[%s]->HasWeaponID([%llu]) " ), pchVarName, (itemid_t)fParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " != [%hu (%s)]\n" ) : ( " == [%hu (%s)]\n" ), pWeapon->GetAttributeContainer()->GetItem()->GetItemDefIndex(), pWeapon->GetClassname() ); + } + + if ( pWeapon->GetAttributeContainer()->GetItem()->GetItemDefIndex() != (itemid_t)fParam ) + { + pWeapon = NULL; + } + } + + return bNot ? pWeapon == NULL : pWeapon != NULL; + } + + case LESSON_ACTION_WEAPON_ATTR_HAS: + { + // TODO: Figure out way to anonymously check if weapon has an attribute + Warning( "\"weapon attr has\" not implemented\n" ); + return bNot ? !false : false; + +#if 0 + C_BaseCombatCharacter *pBCC = NULL; + C_BaseCombatWeapon *pWeapon = NULL; + + if ( pVar ) + { + pBCC = pVar->MyCombatCharacterPointer(); + if ( !pBCC ) + { + pWeapon = pVar->MyCombatWeaponPointer(); + } + } + + if ( !pBCC && !pWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->HasWeaponAttribute([%s] " ) : ( "\t[%s]->HasWeaponAttribute([%s])\n" ), pchVarName, pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter or BaseCombatWeapon returned NULL!\n" ); + } + + return false; + } + + if ( pBCC ) + { + for (int i=0;iGetWeapon(i) ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->HasWeaponAttribute([%s]) " ) : ( "\t[%s]->HasWeaponAttribute([%s]) " ), pchVarName, pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " != [%hu (%s)]\n" ) : ( " == [%hu (%s)]\n" ), pBCC->GetWeapon(i)->GetAttributeContainer()->GetItem()->GetItemDefIndex(), pBCC->GetWeapon(i)->GetClassname() ); + } + + if ( pBCC->GetWeapon(i)->GetAttributeList()->GetAttributeByName( pchParam ) ) + { + pWeapon = pBCC->GetWeapon(i); + break; + } + } + } + + /*if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->HasWeaponID([%llu]) " ) : ( "\t[%s]->HasWeaponID([%llu]) " ), pchVarName, (itemid_t)fParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " != [%llu] " ) : ( " == [%llu] " ), (itemid_t)fParam ); + }*/ + } + else if ( pWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->HasWeaponAttribute([%llu]) " ) : ( "\t[%s]->HasWeaponAttribute([%llu]) " ), pchVarName, (itemid_t)fParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " != [%hu (%s)]\n" ) : ( " == [%hu (%s)]\n" ), pWeapon->GetAttributeContainer()->GetItem()->GetItemDefIndex(), pWeapon->GetClassname() ); + } + + if ( !pWeapon->GetAttributeList()->GetAttributeByName( pchParam ) ) + { + pWeapon = NULL; + } + } + + return bNot ? !pWeapon : pWeapon; +#endif + } + + case LESSON_ACTION_ENEMY_TEAM_HAS_TF_WEAPON: + { + bool bEnemyHasWeapon = false; + + C_TFPlayer *pLocalPlayer = dynamic_cast(pVar); + + if ( pLocalPlayer ) + { + for ( int i = 1; i < gpGlobals->maxClients; i++ ) + { + C_TFPlayer *pTFPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) ); + if ( !pTFPlayer || pTFPlayer->GetTeamNumber() == pLocalPlayer->GetTeamNumber() || !IsValidTFTeam( pTFPlayer->GetTeamNumber() ) ) + continue; + + if ( pchParam && *pchParam ) + { + if ( pTFPlayer->Weapon_OwnsThisType( pchParam ) ) + { + bEnemyHasWeapon = true; + break; + } + } + else + { + // Check by direct ID + for (int i=0;iGetWeapon(i) ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->EnemyHasTFWeapon([%llu]) " ) : ( "\t[%s]->EnemyHasTFWeapon([%llu]) " ), pchVarName, (itemid_t)fParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " != [%hu (%s)]\n" ) : ( " == [%hu (%s)]\n" ), pTFPlayer->GetWeapon(i)->GetAttributeContainer()->GetItem()->GetItemDefIndex(), pTFPlayer->GetWeapon(i)->GetClassname() ); + } + + if ( pTFPlayer->GetWeapon(i)->GetAttributeContainer()->GetItem()->GetItemDefIndex() == (itemid_t)fParam ) + { + bEnemyHasWeapon = true; + break; + } + } + } + } + } + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->EnemyHasTFWeapon()", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " != [%s] " ) : ( " == [%s] " ), pchParam ); + } + + return bNot ? !bEnemyHasWeapon : bEnemyHasWeapon; + } + + default: + // Didn't handle this action + bModHandled = false; + break; + } + + return false; +} + + +bool C_GameInstructor::Mod_HiddenByOtherElements( void ) +{ + return false; +} + +bool C_GameInstructor::Mod_DefineLessonType( const char *pszLessonName, const char *pszLessonType ) +{ + if ( FStrEq( pszLessonType, "tf_training_objective" ) ) + { + DefineLesson( new CTFObjectiveLesson( m_pScriptKeys->GetName(), false, false ) ); + return true; + } + else if ( FStrEq( pszLessonType, "tf_notification" ) ) + { + DefineLesson( new CTFNotificationLesson( m_pScriptKeys->GetName(), false, false ) ); + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// +// CTFBaseLesson +// +//----------------------------------------------------------------------------- + +#define TF_LESSON_MIN_TIME_ON_SCREEN_TO_MARK_DISPLAYED 1.5f +#define TF_LESSON_DISTANCE_UPDATE_RATE 0.25f + +void CTFBaseLesson::Init() +{ + ListenForGameEvent( "localplayer_respawn" ); + //ListenForGameEvent( "client_disconnect" ); + //ListenForGameEvent( "round_start" ); +} + +//========================================================= +//========================================================= +void CTFBaseLesson::Update() +{ + if ( !DoDelayedPlayerSwaps() ) + return; + + // Check if it has been onscreen long enough to count as being displayed + if ( m_fOnScreenStartTime != 0.0f ) + { + if ( gpGlobals->curtime - m_fOnScreenStartTime >= TF_LESSON_MIN_TIME_ON_SCREEN_TO_MARK_DISPLAYED ) + { + // Lesson on screen long enough to be counted as displayed + m_bWasDisplayed = true; + + // Also apply to root (since we use it to keep track of when to show these hints) + if ( GetRoot() ) + { + CTFBaseLesson *pRoot = assert_cast(GetRoot()); + if ( pRoot ) + { + pRoot->MarkAsDisplayed(); + } + } + } + } + + if ( m_fUpdateDistanceTime < gpGlobals->curtime ) + { + // Update it's distance from the local player + C_BaseEntity *pTarget = m_hEntity1.Get(); // m_hIconTarget.Get() + C_BasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer(); + + if ( !pLocalPlayer || !pTarget || pLocalPlayer == pTarget ) + { + m_fCurrentDistance = 0.0f; + } + else + { + m_fCurrentDistance = pLocalPlayer->EyePosition().DistTo( pTarget->WorldSpaceCenter() ); + } + + m_fUpdateDistanceTime = gpGlobals->curtime + TF_LESSON_DISTANCE_UPDATE_RATE; + } + + //BaseClass::Update(); +} + +//========================================================= +//========================================================= +void CTFBaseLesson::UpdateInactive() +{ + if ( !DoDelayedPlayerSwaps() ) + return; + + if ( m_fUpdateDistanceTime < gpGlobals->curtime ) + { + // Update it's distance from the local player + C_BaseEntity *pTarget = m_hEntity1.Get(); // m_hIconTarget.Get() + C_BasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer(); + + if ( !pLocalPlayer || !pTarget || pLocalPlayer == pTarget ) + { + m_fCurrentDistance = 0.0f; + } + else + { + m_fCurrentDistance = pLocalPlayer->EyePosition().DistTo( pTarget->WorldSpaceCenter() ); + } + + m_fUpdateDistanceTime = gpGlobals->curtime + TF_LESSON_DISTANCE_UPDATE_RATE; + } + + //BaseClass::UpdateInactive(); +} + +//========================================================= +//========================================================= +bool CTFBaseLesson::ShouldDisplay() const +{ + if ( !IsInstructing() ) + { + // Already displayed this session + if ( GetRoot() && GetRoot()->WasDisplayed() ) + return false; + + if ( TFGameRules() ) + { + // Not while waiting for players + if ( TFGameRules()->IsInWaitingForPlayers() ) + return false; + + // For now, no hints in arena prep stage so we don't overlap with the player count + if ( TFGameRules()->IsInArenaMode() && TFGameRules()->State_Get() == GR_STATE_PREROUND ) + return false; + } + } + + // Ok to display + return BaseClass::ShouldDisplay(); +} + +//========================================================= +//========================================================= +void CTFBaseLesson::FireGameEvent( IGameEvent *event ) +{ + if ( FStrEq( event->GetName(), "localplayer_respawn" ) && IsInstructing() && !m_bCanOpenWhenDead ) + { + // Close active lessons on respawn + SetCloseReason( "Player respawned" ); + GetGameInstructor().CloseOpportunity( this ); + } + /*else if ( FStrEq( event->GetName(), "client_disconnect" ) ) + { + // UNDONE: Allow TF lessons to show again when connecting to another server + GetGameInstructor().CloseOpportunity( this ); + m_bWasDisplayed = false; + }*/ + /*else if ( FStrEq( event->GetName(), "round_start" ) ) + { + // UNDONE: Reset on round start + GetGameInstructor().CloseOpportunity( this ); + m_bWasDisplayed = false; + }*/ + + BaseClass::FireGameEvent( event ); +} + +//----------------------------------------------------------------------------- +// +// CTFObjectiveLesson +// +//----------------------------------------------------------------------------- + +#define TF_LESSON_MIN_TIME_ON_SCREEN_TO_MARK_DISPLAYED 1.5f +#define TF_LESSON_DISTANCE_UPDATE_RATE 0.25f + +CTFObjectiveLesson *CTFObjectiveLesson::g_pActiveTFObjectiveLesson = NULL; + +void CTFObjectiveLesson::Init() +{ +} + +//========================================================= +//========================================================= +void CTFObjectiveLesson::Start() +{ + if ( !DoDelayedPlayerSwaps() ) + return; + + // Shouldn't show these lessons if we're actually in training + if ( !TFGameRules() || TFGameRules()->IsInTraining() ) + return; + + CTFHudObjectiveStatus *pStatus = GET_HUDELEMENT( CTFHudObjectiveStatus ); + if ( pStatus ) + { + if ( g_pActiveTFObjectiveLesson && g_pActiveTFObjectiveLesson->IsInstructing() ) + { + // Replace the currently active lesson + g_pActiveTFObjectiveLesson->SetCloseReason( "Replaced with another lesson" ); + GetGameInstructor().CloseOpportunity( g_pActiveTFObjectiveLesson ); + } + + g_pActiveTFObjectiveLesson = this; + + pStatus->SetTrainingGameInstructor( true ); + + if ( V_strchr( m_szOnscreenIcon.String(), '%' ) ) + { + char szImage[MAX_PATH] = { 0 }; + + char szToken[256]; + V_strncpy( szToken, m_szOnscreenIcon.String(), sizeof( szToken ) ); + + bool bVar = false; + const char *token = strtok( szToken, "%" ); + while (token != NULL) + { + if (bVar) + { + if (V_strnicmp( token, "team", 4 ) == 0) + { + C_BasePlayer *pPlayer = GetGameInstructor().GetLocalPlayer(); + int iTeam = pPlayer ? pPlayer->GetTeamNumber() : TEAM_UNASSIGNED; + if (iTeam < 0 || iTeam >= TF_TEAM_COUNT) + iTeam = TEAM_UNASSIGNED; + + if (FStrEq(token + 4, "_short")) + { + V_strncat( szImage, g_aTeamNamesShort[iTeam], sizeof( szImage ) ); + } + else + { + V_strncat( szImage, g_aTeamNames[iTeam], sizeof( szImage ) ); + } + } + } + else + { + V_strncat( szImage, token, sizeof( szImage ) ); + } + + bVar = !bVar; + token = strtok( NULL, "%" ); + } + + pStatus->SetTrainingImage( szImage ); + } + else + { + pStatus->SetTrainingImage( m_szOnscreenIcon.String() ); + } + + pStatus->SetTrainingObjective( m_szDisplayParamText.String() ); + pStatus->SetTrainingText( m_szDisplayText.String() ); + + pStatus->SetTrainingOverridePos( m_bFixedPosition, m_fFixedPositionX, m_fFixedPositionY ); + + TFGameRules()->SetShowingTFObjectiveLesson( true ); + + // HACKHACK: Can't put these in Init() due to base class usage + m_bNoIconTarget = true; + m_bAllowNodrawTarget = true; + + m_fOnScreenStartTime = gpGlobals->curtime; + } +} + +//========================================================= +//========================================================= +void CTFObjectiveLesson::Stop() +{ + if ( !DoDelayedPlayerSwaps() ) + return; + + if ( !TFGameRules() ) + return; + + if ( IsInstructing() ) + { + // Set to false within CTFHudTraining + TFGameRules()->SetShowingTFObjectiveLesson( false ); + + CTFHudObjectiveStatus *pStatus = GET_HUDELEMENT( CTFHudObjectiveStatus ); + if ( pStatus ) + { + pStatus->SetTrainingGameInstructor( false ); + } + + Assert( g_pActiveTFObjectiveLesson == this ); + g_pActiveTFObjectiveLesson = NULL; + } + + m_fOnScreenStartTime = 0.0f; + m_fStartTime = 0.0f; +} + +//----------------------------------------------------------------------------- +// +// CTFNotificationLesson +// +//----------------------------------------------------------------------------- + +CTFNotificationLesson *CTFNotificationLesson::g_pActiveTFNotificationLesson = NULL; + +void CTFNotificationLesson::Init() +{ + ListenForGameEvent( "localplayer_respawn" ); +} + +//========================================================= +//========================================================= +void CTFNotificationLesson::Start() +{ + if ( !DoDelayedPlayerSwaps() ) + return; + + CHudNotificationPanel *pNotificationPanel = GET_HUDELEMENT( CHudNotificationPanel ); + if ( pNotificationPanel ) + { + if ( g_pActiveTFNotificationLesson && g_pActiveTFNotificationLesson->IsInstructing() ) + { + // Replace the currently active lesson + g_pActiveTFNotificationLesson->SetCloseReason( "Replaced with another lesson" ); + GetGameInstructor().CloseOpportunity( g_pActiveTFNotificationLesson ); + } + + g_pActiveTFNotificationLesson = this; + + char szImage[MAX_PATH] = { 0 }; + + if ( V_strchr( m_szOnscreenIcon.String(), '%' ) ) + { + char szToken[256]; + V_strncpy( szToken, m_szOnscreenIcon.String(), sizeof( szToken ) ); + + bool bVar = false; + const char *token = strtok( szToken, "%" ); + while (token != NULL) + { + if (bVar) + { + if (V_strnicmp( token, "team", 4 ) == 0) + { + C_BasePlayer *pPlayer = GetGameInstructor().GetLocalPlayer(); + int iTeam = pPlayer ? pPlayer->GetTeamNumber() : TEAM_UNASSIGNED; + if (iTeam < 0 || iTeam >= TF_TEAM_COUNT) + iTeam = TEAM_UNASSIGNED; + + if (FStrEq(token + 4, "_short")) + { + V_strncat( szImage, g_aTeamNamesShort[iTeam], sizeof( szImage ) ); + } + else + { + V_strncat( szImage, g_aTeamNames[iTeam], sizeof( szImage ) ); + } + } + } + else + { + V_strncat( szImage, token, sizeof( szImage ) ); + } + + bVar = !bVar; + token = strtok( NULL, "%" ); + } + } + + wchar_t wszText[MAX_TRAINING_MSG_LENGTH]; + CTFHudTraining::FormatTrainingText( m_szDisplayText.String(), wszText ); + + C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + + pNotificationPanel->SetupNotifyCustom( wszText, szImage, pLocalPlayer ? pLocalPlayer->GetTeamNumber() : TEAM_UNASSIGNED, m_fTimeout ); + + // HACKHACK: Can't put these in Init() due to base class usage + m_bNoIconTarget = true; + m_bAllowNodrawTarget = true; + + m_fOnScreenStartTime = gpGlobals->curtime; + } +} + +//========================================================= +//========================================================= +void CTFNotificationLesson::Stop() +{ + if ( !DoDelayedPlayerSwaps() ) + return; + + if ( !TFGameRules() ) + return; + + if ( IsInstructing() ) + { + CHudNotificationPanel *pNotificationPanel = GET_HUDELEMENT( CHudNotificationPanel ); + if ( pNotificationPanel ) + { + pNotificationPanel->HideNotify(); + } + + Assert( g_pActiveTFNotificationLesson == this ); + g_pActiveTFNotificationLesson = NULL; + } + + m_fOnScreenStartTime = 0.0f; + m_fStartTime = 0.0f; +} diff --git a/src/game/client/mapbase/c_tf_lesson.h b/src/game/client/mapbase/c_tf_lesson.h new file mode 100644 index 00000000000..0f8b3b75447 --- /dev/null +++ b/src/game/client/mapbase/c_tf_lesson.h @@ -0,0 +1,83 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: TF2 lesson actions + new Game Instructor lesson types which use TF HUD elements +// +// Author: Blixibon +// +//=============================================================================// + +#ifndef C_TF_LESSON_H +#define C_TF_LESSON_H +#ifdef _WIN32 +#pragma once +#endif + +#include "c_gameinstructor.h" +#include "c_baselesson.h" + +//----------------------------------------------------------------------------- +// Purpose: An instructor lesson that integrates a TF HUD element instead of +// a traditional icon hint. +// +// TODO: Lesson classes are seemingly set up so that there can be non-icon +// lessons, but those lessons cannot be loaded by mod_lessons or use game events. +// An overhaul of the lesson system would be needed for this lesson type to derive +// directly from these non-icon lesson classes. +//----------------------------------------------------------------------------- +class CTFBaseLesson : public CScriptedIconLesson +{ +public: + DECLARE_LESSON( CTFBaseLesson, CScriptedIconLesson ) + + void Init( void ); // NOT virtual, each constructor calls their own + virtual void Update( void ); + virtual void UpdateInactive( void ); + + void MarkAsDisplayed() { m_bWasDisplayed = true; } + virtual bool ShouldDisplay( void ) const; + virtual void FireGameEvent( IGameEvent *event ); + +private: +}; + +//----------------------------------------------------------------------------- +// Purpose: An instructor lesson that uses the training objective UI. +//----------------------------------------------------------------------------- +class CTFObjectiveLesson : public CTFBaseLesson +{ +public: + DECLARE_LESSON( CTFObjectiveLesson, CTFBaseLesson ) + + void Init( void ); // NOT virtual, each constructor calls their own + virtual void Start( void ); + virtual void Stop( void ); + + virtual CScriptedIconLesson *CreateLessonCopy() { return new CTFObjectiveLesson( GetName(), false, true ); } + +private: + + // Pointer to the lesson currently displayed on the HUD + static CTFObjectiveLesson *g_pActiveTFObjectiveLesson; +}; + +//----------------------------------------------------------------------------- +// Purpose: An instructor lesson that uses the notification UI. +//----------------------------------------------------------------------------- +class CTFNotificationLesson : public CTFBaseLesson +{ +public: + DECLARE_LESSON( CTFNotificationLesson, CTFBaseLesson ) + + void Init( void ); // NOT virtual, each constructor calls their own + virtual void Start( void ); + virtual void Stop( void ); + + virtual CScriptedIconLesson *CreateLessonCopy() { return new CTFNotificationLesson( GetName(), false, true ); } + +private: + + // Pointer to the lesson currently displayed on the HUD + static CTFNotificationLesson *g_pActiveTFNotificationLesson; +}; + +#endif diff --git a/src/game/client/tf/c_tf_player.cpp b/src/game/client/tf/c_tf_player.cpp index 73d464cc810..bdc25588641 100644 --- a/src/game/client/tf/c_tf_player.cpp +++ b/src/game/client/tf/c_tf_player.cpp @@ -220,6 +220,10 @@ ConVar tf_taunt_first_person( "tf_taunt_first_person", "0", FCVAR_NONE, "1 = tau ConVar tf_romevision_opt_in( "tf_romevision_opt_in", "0", FCVAR_ARCHIVE, "Enable Romevision in Mann vs. Machine mode when available." ); ConVar tf_romevision_skip_prompt( "tf_romevision_skip_prompt", "0", FCVAR_ARCHIVE, "If nonzero, skip the prompt about sharing Romevision." ); +#ifdef MAPBASE +ConVar tf_scan_potential_use_target( "tf_scan_potential_use_target", "0", FCVAR_NONE, "Allows player to periodically store its \"potential use target\", a generic entity that the player may be looking at or interacting with. May be enabled automatically by game instructor, etc." ); +#endif + #define BDAY_HAT_MODEL "models/effects/bday_hat.mdl" #define BOMB_HAT_MODEL "models/props_lakeside_event/bomb_temp_hat.mdl" @@ -6057,6 +6061,24 @@ void C_TFPlayer::ClientThink() engine->ClientCmd("voicemenu 1 8"); } } + +#ifdef MAPBASE + if ( IsLocalPlayer() && tf_scan_potential_use_target.GetBool() && m_flPotentialUseEntityScanTime < gpGlobals->curtime ) + { + // Fire an event if we have a new use entity + C_BaseEntity *pOldEnt = m_hPotentialUseEntity; + C_BaseEntity *pNewEnt = GetPotentialUseEntity(); + if ( pNewEnt != pOldEnt && pNewEnt ) + { + IGameEvent *event = gameeventmanager->CreateEvent( "use_target" ); + if ( event ) + { + event->SetInt( "targetid", pNewEnt->entindex() ); + gameeventmanager->FireEventClientSide( event ); + } + } + } +#endif } void C_TFPlayer::Touch( CBaseEntity *pOther ) diff --git a/src/game/client/tf/c_tf_player.h b/src/game/client/tf/c_tf_player.h index 0fcee373088..e2deed7f8c4 100644 --- a/src/game/client/tf/c_tf_player.h +++ b/src/game/client/tf/c_tf_player.h @@ -513,6 +513,11 @@ class C_TFPlayer : public C_BasePlayer, public IHasAttributes, public IInventory bool IsPlayerOnSteamFriendsList( C_BasePlayer *pPlayer ); +#ifdef MAPBASE + virtual C_BaseEntity *GetPotentialUseEntity(); + C_BaseEntity *FindPotentialUseEntity(); +#endif + protected: void ResetFlexWeights( CStudioHdr *pStudioHdr ); @@ -966,6 +971,11 @@ class C_TFPlayer : public C_BasePlayer, public IHasAttributes, public IInventory float m_flTempForceDrawViewModelCycle = 0.0f; CNetworkVar( int, m_iPlayerSkinOverride ); + +#ifdef MAPBASE + EHANDLE m_hPotentialUseEntity = NULL; + float m_flPotentialUseEntityScanTime = 0.0f; +#endif }; inline C_TFPlayer* ToTFPlayer( C_BaseEntity *pEntity ) diff --git a/src/game/client/tf/tf_hud_notification_panel.cpp b/src/game/client/tf/tf_hud_notification_panel.cpp index 26333fe5a5a..5f3a70f58f8 100644 --- a/src/game/client/tf/tf_hud_notification_panel.cpp +++ b/src/game/client/tf/tf_hud_notification_panel.cpp @@ -160,7 +160,7 @@ void CHudNotificationPanel::MsgFunc_HudNotifyCustom( bf_read &msg ) //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- -void CHudNotificationPanel::SetupNotifyCustom( const char *pszText, const char *pszIcon, int iBackgroundTeam ) +void CHudNotificationPanel::SetupNotifyCustom( const char *pszText, const char *pszIcon, int iBackgroundTeam, float overrideDuration ) { // Reload the base LoadControlSettings( "resource/UI/notifications/base_notification.res" ); @@ -182,7 +182,7 @@ void CHudNotificationPanel::SetupNotifyCustom( const char *pszText, const char * } // set up the fade time - m_flFadeTime = gpGlobals->curtime + tf_hud_notification_duration.GetFloat(); + m_flFadeTime = gpGlobals->curtime + ( overrideDuration > 0.f ? overrideDuration : tf_hud_notification_duration.GetFloat() ); InvalidateLayout(); } @@ -190,7 +190,7 @@ void CHudNotificationPanel::SetupNotifyCustom( const char *pszText, const char * //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- -void CHudNotificationPanel::SetupNotifyCustom( const wchar_t *pszText, const char *pszIcon, int iBackgroundTeam ) +void CHudNotificationPanel::SetupNotifyCustom( const wchar_t *pszText, const char *pszIcon, int iBackgroundTeam, float overrideDuration ) { // Reload the base LoadControlSettings( "resource/UI/notifications/base_notification.res" ); @@ -212,7 +212,7 @@ void CHudNotificationPanel::SetupNotifyCustom( const wchar_t *pszText, const cha } // set up the fade time - m_flFadeTime = gpGlobals->curtime + tf_hud_notification_duration.GetFloat(); + m_flFadeTime = gpGlobals->curtime + ( overrideDuration > 0.f ? overrideDuration : tf_hud_notification_duration.GetFloat() ); InvalidateLayout(); } @@ -235,6 +235,17 @@ void CHudNotificationPanel::SetupNotifyCustom( const wchar_t *pszText, HudNotifi InvalidateLayout(); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudNotificationPanel::HideNotify() +{ + // Hides current message + m_flFadeTime = 0.0f; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/src/game/client/tf/tf_hud_notification_panel.h b/src/game/client/tf/tf_hud_notification_panel.h index f835270c62f..69ecb3bb178 100644 --- a/src/game/client/tf/tf_hud_notification_panel.h +++ b/src/game/client/tf/tf_hud_notification_panel.h @@ -45,10 +45,14 @@ class CHudNotificationPanel : public CHudElement, public EditablePanel void MsgFunc_HudNotify( bf_read &msg ); void MsgFunc_HudNotifyCustom( bf_read &msg ); - void SetupNotifyCustom( const char *pszText, const char *pszIcon, int iBackgroundTeam ); - void SetupNotifyCustom( const wchar_t *pszText, const char *pszIcon, int iBackgroundTeam ); + void SetupNotifyCustom( const char *pszText, const char *pszIcon, int iBackgroundTeam, float overrideDuration = 0.0f ); + void SetupNotifyCustom( const wchar_t *pszText, const char *pszIcon, int iBackgroundTeam, float overrideDuration = 0.0f ); void SetupNotifyCustom( const wchar_t *pszText, HudNotification_t type, float overrideDuration = 0.0f ); +#ifdef MAPBASE + void HideNotify(); +#endif + virtual void LevelInit( void ) { m_flFadeTime = 0; }; bool LoadManifest( void ); diff --git a/src/game/client/tf/tf_hud_objectivestatus.cpp b/src/game/client/tf/tf_hud_objectivestatus.cpp index 3f2869c0fbf..c21cb11d2db 100644 --- a/src/game/client/tf/tf_hud_objectivestatus.cpp +++ b/src/game/client/tf/tf_hud_objectivestatus.cpp @@ -159,7 +159,7 @@ CControlPointProgressBar *CTFHudObjectiveStatus::GetControlPointProgressBar( voi //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- -void CTFHudObjectiveStatus::SetTrainingText( char *text ) +void CTFHudObjectiveStatus::SetTrainingText( const char *text ) { if ( NULL == m_pTrainingPanel ) return; @@ -170,13 +170,48 @@ void CTFHudObjectiveStatus::SetTrainingText( char *text ) //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- -void CTFHudObjectiveStatus::SetTrainingObjective( char *text ) +void CTFHudObjectiveStatus::SetTrainingObjective( const char *text ) { if ( NULL == m_pTrainingPanel ) return; m_pTrainingPanel->SetTrainingObjective( text ); } + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFHudObjectiveStatus::SetTrainingImage( const char *img ) +{ + if ( NULL == m_pTrainingPanel ) + return; + + m_pTrainingPanel->SetTrainingImage( img ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFHudObjectiveStatus::SetTrainingGameInstructor( bool bIsInstructor ) +{ + if ( NULL == m_pTrainingPanel ) + return; + + m_pTrainingPanel->SetTrainingGameInstructor( bIsInstructor ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFHudObjectiveStatus::SetTrainingOverridePos( bool bOverride, float flX, float flY ) +{ + if ( NULL == m_pTrainingPanel ) + return; + + m_pTrainingPanel->SetTrainingOverridePos( bOverride, flX, flY ); +} +#endif //============================================================================= // HPE_END //============================================================================= diff --git a/src/game/client/tf/tf_hud_objectivestatus.h b/src/game/client/tf/tf_hud_objectivestatus.h index 143966c5171..2ab3321cfc7 100644 --- a/src/game/client/tf/tf_hud_objectivestatus.h +++ b/src/game/client/tf/tf_hud_objectivestatus.h @@ -44,8 +44,13 @@ class CTFHudObjectiveStatus : public CHudElement, public vgui::EditablePanel // HPE_BEGIN // [msmith] Functions for training stuff. //============================================================================= - void SetTrainingText( char *msg); - void SetTrainingObjective (char *obj); + void SetTrainingText( const char *msg); + void SetTrainingObjective (const char *obj); +#ifdef MAPBASE + void SetTrainingImage (const char *img); + void SetTrainingGameInstructor (bool bIsInstructor); + void SetTrainingOverridePos (bool bOverride, float flX, float flY); +#endif //============================================================================= // HPE_END //============================================================================= diff --git a/src/game/client/tf/tf_hud_training.cpp b/src/game/client/tf/tf_hud_training.cpp index a14839e6867..230ab463212 100644 --- a/src/game/client/tf/tf_hud_training.cpp +++ b/src/game/client/tf/tf_hud_training.cpp @@ -16,6 +16,8 @@ using namespace vgui; +extern void AddSubKeyNamed( KeyValues *pKeys, const char *pszName ); + //----------------------------------------------------------------------------- // Purpose: Localize text for training messages. Used in annotations and in the training hud. Assumes the output string size is greater than or equal to MAX_TRAINING_MSG_LENGTH //----------------------------------------------------------------------------- @@ -128,6 +130,10 @@ CTFHudTraining::CTFHudTraining( Panel *parent, const char *name ) : EditablePane ivgui()->AddTickSignal( GetVPanel(), 10 ); ListenForGameEvent( "teamplay_round_start" ); + +#ifdef MAPBASE + m_szImage[0] = '\0'; +#endif } @@ -161,6 +167,9 @@ void CTFHudTraining::Reset( void ) SetDialogVariable( "goal", emptyText ); if (m_pMsgLabel) m_pMsgLabel->SetText(emptyText); +#ifdef MAPBASE + if (m_pMsgLabelShadow) m_pMsgLabelShadow->SetText(emptyText); +#endif if (m_pPressSpacebarToContinueLabel) m_pPressSpacebarToContinueLabel->SetVisible( false ); } @@ -172,14 +181,139 @@ void CTFHudTraining::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); + KeyValues *pConditions = NULL; + +#ifdef MAPBASE + if (m_szImage[0]) + { + if ( !pConditions ) + pConditions = new KeyValues( "conditions" ); + + AddSubKeyNamed( pConditions, "if_image" ); + } + + if ( m_bIsGameInstructor ) + { + if ( !pConditions ) + pConditions = new KeyValues( "conditions" ); + + AddSubKeyNamed( pConditions, "if_instructor" ); + } +#endif + // load control settings... - LoadControlSettings( "resource/UI/HudTraining.res" ); + LoadControlSettings( "resource/UI/HudTraining.res", NULL, NULL, pConditions ); m_pMsgLabel = dynamic_cast( FindChildByName("MsgLabel") ); m_pPressSpacebarToContinueLabel = dynamic_cast( FindChildByName("PressSpacebarToContinue") ); + +#ifdef MAPBASE + m_pGoalLabel = dynamic_cast(FindChildByName( "GoalLabel" )); + m_pGoalLabelShadow = dynamic_cast(FindChildByName( "GoalLabelShadow" )); + m_pMsgLabelShadow = dynamic_cast(FindChildByName( "MsgLabelShadow" )); + m_pMsgBG = dynamic_cast(FindChildByName( "HudTrainingMsgBG" )); + + m_pImage = dynamic_cast(FindChildByName( "HudTrainingImage" )); + if (m_pImage && m_szImage[0]) + { + m_pImage->SetImage( m_szImage ); + } +#endif } -void CTFHudTraining::SetTrainingObjective(char *szRawString) +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFHudTraining::PerformLayout() +{ + BaseClass::PerformLayout(); + +#ifdef MAPBASE + //m_pMsgLabel->SetFont( scheme()->GetIScheme( m_pMsgLabel->GetScheme() )->GetFont( "InstructionalText", true ) ); + + if ( m_bIsGameInstructor ) + { + int x, y, wide, tall; + + if ( m_bOverridePos ) + { + x = (ScreenWidth() * m_flOverrideX) - (GetWide() / 2); + y = (ScreenHeight() * m_flOverrideY) - (GetTall() / 2); + SetPos( x, y ); + } + + /*if (m_pMsgLabel->GetNumLines() > 5) + { + m_pMsgLabel->SetFont( scheme()->GetIScheme( m_pMsgLabel->GetScheme() )->GetFont( "InstructionalTextSmall", true ) ); + }*/ + + int iOldTall = m_pMsgLabel->GetTall(); + + m_pMsgLabel->SetToFullHeight(); + m_pMsgLabel->SetVerticalScrollbar( false ); + m_pMsgLabelShadow->SetToFullHeight(); + m_pMsgLabelShadow->SetVerticalScrollbar( false ); + + int iTallDiff = m_pMsgLabel->GetTall() - iOldTall; + + if ( iTallDiff > 0 ) + { + GetBounds( x, y, wide, tall ); + + // Artificial padding + const int iPadding = iTallDiff/8; + y -= iPadding; + tall += iTallDiff; + + SetBounds( x, y, wide, tall ); + + if (m_pGoalLabel) + { + m_pGoalLabel->GetPos( x, y ); + y += iPadding; + m_pGoalLabel->SetPos( x, y ); + } + + if (m_pGoalLabelShadow) + { + m_pGoalLabelShadow->GetPos( x, y ); + y += iPadding; + m_pGoalLabelShadow->SetPos( x, y ); + } + + if (m_pMsgLabel) + { + m_pMsgLabel->GetPos( x, y ); + y += iPadding; + m_pMsgLabel->SetPos( x, y ); + } + + if (m_pMsgLabelShadow) + { + m_pMsgLabelShadow->GetPos( x, y ); + y += iPadding; + m_pMsgLabelShadow->SetPos( x, y ); + } + + if (m_pMsgBG) + { + m_pMsgBG->GetBounds( x, y, wide, tall ); + tall += iTallDiff + (iPadding*2); + m_pMsgBG->SetBounds( x, y, wide, tall ); + } + + if (m_pImage) + { + m_pImage->GetPos( x, y ); + y += iPadding; + m_pImage->SetPos( x, y ); + } + } + } +#endif +} + +void CTFHudTraining::SetTrainingObjective(const char *szRawString) { wchar_t wszText[MAX_TRAINING_MSG_LENGTH]; @@ -199,7 +333,7 @@ void CTFHudTraining::SetTrainingObjective(char *szRawString) } -void CTFHudTraining::SetTrainingText(char *szRawString) +void CTFHudTraining::SetTrainingText(const char *szRawString) { static wchar_t wszText[MAX_TRAINING_MSG_LENGTH]; @@ -211,11 +345,19 @@ void CTFHudTraining::SetTrainingText(char *szRawString) if (!FormatTrainingText(szRawString, wszText)) { m_pMsgLabel->SetText( "" ); +#ifdef MAPBASE + if ( m_pMsgLabelShadow ) + m_pMsgLabelShadow->SetText( "" ); +#endif return; } // clear the text first m_pMsgLabel->SetText(""); +#ifdef MAPBASE + if ( m_pMsgLabelShadow ) + m_pMsgLabelShadow->SetText( "" ); +#endif enum { @@ -255,6 +397,14 @@ void CTFHudTraining::SetTrainingText(char *szRawString) wszInsertedText[len-1] = 0; m_pMsgLabel->InsertColorChange( color ); m_pMsgLabel->InsertString( wszInsertedText ); + +#ifdef MAPBASE + if ( m_pMsgLabelShadow ) + { + m_pMsgLabelShadow->InsertString( wszInsertedText ); + } +#endif + // skip past the color change character startIdx = endIdx + 1; } @@ -264,7 +414,35 @@ void CTFHudTraining::SetTrainingText(char *szRawString) } //m_pMessageFlashEndTime = gpGlobals->curtime + MESSAGE_FLASH_TIME; - g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "TrainingHudBounce"); + +#ifdef MAPBASE + if (m_bIsGameInstructor) + { + m_pMsgLabel->SetUnusedScrollbarInvisible( true ); + if ( m_pMsgLabelShadow ) + m_pMsgLabelShadow->SetUnusedScrollbarInvisible( true ); + + InvalidateLayout( true, false ); + + /* + int x, y; + GetPos( x, y ); + + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( this, "Position", Color( x, y - 60, 0, 0 ), 0.0f, 0.0f, vgui::AnimationController::INTERPOLATOR_LINEAR ); + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( this, "Position", Color( x, y, 0, 0 ), 0.0f, 2.0f, vgui::AnimationController::INTERPOLATOR_BOUNCE ); + */ + + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "TrainingHudBlink" ); + } + else +#endif + { + m_pMsgLabel->SetUnusedScrollbarInvisible( false ); + if ( m_pMsgLabelShadow ) + m_pMsgLabelShadow->SetUnusedScrollbarInvisible( false ); + + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "TrainingHudBounce"); + } C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); if ( pLocalPlayer ) @@ -274,6 +452,27 @@ void CTFHudTraining::SetTrainingText(char *szRawString) } +#ifdef MAPBASE +void CTFHudTraining::SetTrainingImage( const char *image ) +{ + V_strncpy( m_szImage, image, sizeof( m_szImage ) ); + InvalidateLayout( false, true ); +} + +void CTFHudTraining::SetTrainingOverridePos( bool bOverride, float flX, float flY ) +{ + m_bOverridePos = bOverride; + m_flOverrideX = flX; + m_flOverrideY = flY; +} + +void CTFHudTraining::SetTrainingGameInstructor( bool bIsInstructor ) +{ + m_bIsGameInstructor = bIsInstructor; +} +#endif + + //----------------------------------------------------------------------------- // Purpose: Receive messages about changes in state //----------------------------------------------------------------------------- diff --git a/src/game/client/tf/tf_hud_training.h b/src/game/client/tf/tf_hud_training.h index 7e9a470e90f..46b90440b91 100644 --- a/src/game/client/tf/tf_hud_training.h +++ b/src/game/client/tf/tf_hud_training.h @@ -42,18 +42,39 @@ class CTFHudTraining : public vgui::EditablePanel, public CGameEventListener static bool FormatTrainingText( const char* input, wchar_t* output ); virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void PerformLayout(); virtual void Reset(); virtual void FireGameEvent( IGameEvent *event ); virtual bool IsVisible( void ); virtual void OnTick(); - void SetTrainingText( char *msg ); - void SetTrainingObjective( char *msg ); + void SetTrainingText( const char *msg ); + void SetTrainingObjective( const char *msg ); +#ifdef MAPBASE + void SetTrainingImage( const char *image ); + void SetTrainingOverridePos( bool bOverride, float flX, float flY ); + + void SetTrainingGameInstructor( bool bIsInstructor ); +#endif private: CExRichText *m_pMsgLabel; CExLabel *m_pPressSpacebarToContinueLabel; +#ifdef MAPBASE + CExLabel *m_pGoalLabel; + CExLabel *m_pGoalLabelShadow; + CExRichText *m_pMsgLabelShadow; + CTFImagePanel *m_pMsgBG; + + vgui::ImagePanel *m_pImage; + char m_szImage[MAX_PATH]; + + bool m_bOverridePos = false; + float m_flOverrideX, m_flOverrideY = 0.0f; + + bool m_bIsGameInstructor = false; +#endif }; diff --git a/src/game/client/tf/tf_hud_trainingmessage.cpp b/src/game/client/tf/tf_hud_trainingmessage.cpp index 7609acde566..cd04a80072f 100644 --- a/src/game/client/tf/tf_hud_trainingmessage.cpp +++ b/src/game/client/tf/tf_hud_trainingmessage.cpp @@ -40,6 +40,9 @@ class CHudTrainingMsg : public CHudElement, public EditablePanel void MsgFunc_TrainingMsg( bf_read &msg ); void MsgFunc_TrainingObjective( bf_read &msg ); +#ifdef MAPBASE + void MsgFunc_TrainingImage( bf_read &msg ); +#endif private: }; @@ -47,6 +50,9 @@ class CHudTrainingMsg : public CHudElement, public EditablePanel DECLARE_HUDELEMENT( CHudTrainingMsg ); DECLARE_HUD_MESSAGE( CHudTrainingMsg, TrainingMsg ); DECLARE_HUD_MESSAGE( CHudTrainingMsg, TrainingObjective ); +#ifdef MAPBASE +DECLARE_HUD_MESSAGE( CHudTrainingMsg, TrainingImage ); +#endif //----------------------------------------------------------------------------- // Purpose: @@ -65,6 +71,9 @@ void CHudTrainingMsg::Init() { HOOK_HUD_MESSAGE( CHudTrainingMsg, TrainingMsg ); HOOK_HUD_MESSAGE( CHudTrainingMsg, TrainingObjective ); +#ifdef MAPBASE + HOOK_HUD_MESSAGE( CHudTrainingMsg, TrainingImage ); +#endif } //----------------------------------------------------------------------------- @@ -111,3 +120,23 @@ void CHudTrainingMsg::MsgFunc_TrainingObjective( bf_read &msg ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Activates the hint display +//----------------------------------------------------------------------------- +void CHudTrainingMsg::MsgFunc_TrainingImage( bf_read &msg ) +{ + if ( engine->IsPlayingDemo() ) + return; + + char szString[MAX_TRAINING_MSG_LENGTH]; + msg.ReadString( szString, MAX_TRAINING_MSG_LENGTH ); + + CTFHudObjectiveStatus *pStatus = GET_HUDELEMENT( CTFHudObjectiveStatus ); + if ( pStatus ) + { + pStatus->SetTrainingImage(szString); + } +} +#endif + diff --git a/src/game/server/env_instructor_hint.cpp b/src/game/server/env_instructor_hint.cpp index 3ee20fb2c2c..c5212937d5b 100644 --- a/src/game/server/env_instructor_hint.cpp +++ b/src/game/server/env_instructor_hint.cpp @@ -215,7 +215,7 @@ void CEnvInstructorHint::InputShowHint( inputdata_t &inputdata ) if ( inputdata.value.StringID() != NULL_STRING ) { - CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, inputdata.value.String() ); + CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, inputdata.value.String(), NULL, inputdata.pActivator, inputdata.pCaller ); pActivator = dynamic_cast( pTarget ); if ( pActivator ) { @@ -263,6 +263,8 @@ void CEnvInstructorHint::InputShowHint( inputdata_t &inputdata ) #ifdef MAPBASE event->SetString( "hint_start_sound", m_iszStartSound.ToCStr() ); event->SetInt( "hint_target_pos", m_iHintTargetPos ); + event->SetInt( "hint_ent_spawnflags", GetSpawnFlags() ); + event->SetInt( "hint_ent_team", GetTeamNumber() ); #endif gameeventmanager->FireEvent( event ); diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index cbedca4487c..3878dbcdce1 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -579,6 +579,7 @@ BEGIN_ENT_SCRIPTDESC( CBasePlayer, CBaseCombatCharacter, "The player entity." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetViewModel, "GetViewModel", "Returns the viewmodel of the specified index." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetUseEntity, "GetUseEntity", "Gets the player's current use entity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetPotentialUseEntity, "GetPotentialUseEntity", "Gets the player's current potential use entity." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetHeldObject, "GetHeldObject", "Gets the player's currently held object IF it is being held by a gravity gun. To check for the player's held +USE object, use the standalone GetPlayerHeldEntity function." ) // diff --git a/src/game/server/player.h b/src/game/server/player.h index 4ede609d1a0..0522941eb11 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -428,6 +428,7 @@ class CBasePlayer : public CBaseCombatCharacter HSCRIPT ScriptGetViewModel( int viewmodelindex ); HSCRIPT ScriptGetUseEntity() { return ToHScript( GetUseEntity() ); } + HSCRIPT ScriptGetPotentialUseEntity() { return ToHScript( GetPotentialUseEntity() ); } HSCRIPT ScriptGetHeldObject() { return ToHScript( GetHeldObject() ); } #endif @@ -776,6 +777,9 @@ class CBasePlayer : public CBaseCombatCharacter void SetMuzzleFlashTime( float flTime ); void SetUseEntity( CBaseEntity *pUseEntity ); CBaseEntity *GetUseEntity(); +#ifdef MAPBASE // From Alien Swarm SDK + virtual CBaseEntity *GetPotentialUseEntity() { return GetUseEntity(); } +#endif virtual float GetPlayerMaxSpeed(); diff --git a/src/game/server/server_mapbase.vpc b/src/game/server/server_mapbase.vpc index fab62daa4b1..50308597bcf 100644 --- a/src/game/server/server_mapbase.vpc +++ b/src/game/server/server_mapbase.vpc @@ -4,9 +4,6 @@ // Project Base Script //----------------------------------------------------------------------------- -$Include "$SRCDIR\game\server\server_mapbase_hl2.vpc" [$HL2||$EPISODIC||$HL2MP] -$Include "$SRCDIR\game\server\server_mapbase_tf.vpc" [$TF] - $Configuration { $Compiler @@ -102,3 +99,6 @@ $Project $Lib "responserules" [$NEW_RESPONSE_SYSTEM] } } + +$Include "$SRCDIR\game\server\server_mapbase_hl2.vpc" [$HL2||$EPISODIC||$HL2MP] +$Include "$SRCDIR\game\server\server_mapbase_tf.vpc" [$TF] diff --git a/src/game/server/tf/tf_player.h b/src/game/server/tf/tf_player.h index 224ddbeb00c..2248a15848f 100644 --- a/src/game/server/tf/tf_player.h +++ b/src/game/server/tf/tf_player.h @@ -1099,6 +1099,11 @@ class CTFPlayer : public CBaseMultiplayerPlayer, public IHasAttributes, public I // Talk control virtual bool CanPlayerTalk() OVERRIDE; +#ifdef MAPBASE + virtual CBaseEntity *GetPotentialUseEntity(); + CBaseEntity *FindPotentialUseEntity(); +#endif + protected: // Creation/Destruction. diff --git a/src/game/shared/tf/tf_gamerules.cpp b/src/game/shared/tf/tf_gamerules.cpp index 5183ea893b7..dbda77f5a79 100644 --- a/src/game/shared/tf/tf_gamerules.cpp +++ b/src/game/shared/tf/tf_gamerules.cpp @@ -3379,6 +3379,10 @@ CTFGameRules::CTFGameRules() //============================================================================= m_bIsTrainingHUDVisible.Set( false ); +#if defined(MAPBASE) && defined(CLIENT_DLL) + m_bShowingTFObjectiveLesson = false; +#endif + m_bIsInItemTestingMode.Set( false ); // Set turbo physics on. Do it here for now. @@ -19304,6 +19308,9 @@ BEGIN_DATADESC( CTrainingModeLogic ) DEFINE_KEYFIELD( m_nextMapName, FIELD_STRING, "nextMap" ), DEFINE_INPUTFUNC( FIELD_STRING, "ShowTrainingMsg", InputShowTrainingMsg ), DEFINE_INPUTFUNC( FIELD_STRING, "ShowTrainingObjective", InputShowTrainingObjective ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_STRING, "ShowTrainingImage", InputShowTrainingImage ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "ForcePlayerSpawnAsClassOutput", InputForcePlayerSpawnAsClassOutput ), DEFINE_INPUTFUNC( FIELD_VOID, "KickBots", InputKickAllBots ), DEFINE_INPUTFUNC( FIELD_VOID, "ShowTrainingHUD", InputShowTrainingHUD ), @@ -19359,6 +19366,17 @@ void CTrainingModeLogic::SetTrainingObjective(const char *text) MessageEnd(); } +#ifdef MAPBASE +void CTrainingModeLogic::SetTrainingImage(const char *text) +{ + CBroadcastRecipientFilter allusers; + allusers.MakeReliable(); + UserMessageBegin( allusers, "TrainingImage" ); + WRITE_STRING( text ); + MessageEnd(); +} +#endif + void CTrainingModeLogic::OnPlayerSpawned( CTFPlayer* pPlayer ) { if ( pPlayer->GetDesiredPlayerClassIndex() == TF_CLASS_UNDEFINED ) @@ -19524,6 +19542,16 @@ void CTrainingModeLogic::InputShowTrainingObjective( inputdata_t &inputdata ) UpdateHUDObjective(); } +#ifdef MAPBASE +void CTrainingModeLogic::InputShowTrainingImage( inputdata_t &inputdata ) +{ + if ( !TFGameRules()->IsInTraining() ) + return; + + SetTrainingImage( inputdata.value.String() ); +} +#endif + void CTrainingModeLogic::InputShowTrainingHUD( inputdata_t &inputdata ) { if ( !TFGameRules()->IsInTraining() ) diff --git a/src/game/shared/tf/tf_gamerules.h b/src/game/shared/tf/tf_gamerules.h index 56bd58bab57..7d3a8d165b9 100644 --- a/src/game/shared/tf/tf_gamerules.h +++ b/src/game/shared/tf/tf_gamerules.h @@ -684,9 +684,19 @@ bool IsCreepWaveMode( void ) const; //============================================================================= // HPE_END //============================================================================= + +#if defined(MAPBASE) && defined(CLIENT_DLL) + bool IsTrainingHUDVisible( void ) { return (IsInTraining() && m_bIsTrainingHUDVisible) || IsShowingTFObjectiveLesson(); } +#else bool IsTrainingHUDVisible( void ) { return IsInTraining() && m_bIsTrainingHUDVisible; } +#endif void SetTrainingHUDVisible( bool bVisible ) { m_bIsTrainingHUDVisible.Set( bVisible ); } +#if defined(MAPBASE) && defined(CLIENT_DLL) + bool IsShowingTFObjectiveLesson() { return m_bShowingTFObjectiveLesson; } + void SetShowingTFObjectiveLesson( bool bShowingLesson ) { m_bShowingTFObjectiveLesson = bShowingLesson; } +#endif + virtual bool IsInItemTestingMode( void ) { return m_bIsInItemTestingMode; } void SetInItemTestingMode( bool bInTesting ) { m_bIsInItemTestingMode.Set( bInTesting ); } int ItemTesting_GetBotAnim( void ) { return m_iItemTesting_BotAnim; } @@ -1166,6 +1176,10 @@ bool IsCreepWaveMode( void ) const; //============================================================================= CNetworkVar( bool, m_bIsTrainingHUDVisible ); +#if defined(MAPBASE) && defined(CLIENT_DLL) + bool m_bShowingTFObjectiveLesson; // Whether or not the training HUD is currently being used for an instructor lesson +#endif + CNetworkVar( bool, m_bIsInItemTestingMode ); int m_iItemTesting_BotAnim; float m_flItemTesting_BotAnimSpeed; @@ -1616,6 +1630,9 @@ class CTrainingModeLogic : public CPointEntity void SetupOnRoundStart( void ); void SetTrainingMsg( const char *msg ); void SetTrainingObjective( const char *msg ); +#ifdef MAPBASE + void SetTrainingImage( const char *msg ); +#endif void OnPlayerSpawned( CTFPlayer *pPlayer ); void OnPlayerDied( CTFPlayer *pPlayer, CBaseEntity *pKiller ); void OnBotDied( CTFPlayer *pPlayer, CBaseEntity *pKiller ); @@ -1634,6 +1651,9 @@ class CTrainingModeLogic : public CPointEntity void InputKickAllBots( inputdata_t &inputdata ); void InputShowTrainingMsg( inputdata_t &inputdata ); void InputShowTrainingObjective( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputShowTrainingImage( inputdata_t &inputdata ); +#endif void InputShowTrainingHUD( inputdata_t &inputdata ); void InputHideTrainingHUD( inputdata_t &inputdata ); void InputEndTraining( inputdata_t &inputdata ); diff --git a/src/game/shared/tf/tf_player_shared.cpp b/src/game/shared/tf/tf_player_shared.cpp index abe67cf9c09..9241eef0c00 100644 --- a/src/game/shared/tf/tf_player_shared.cpp +++ b/src/game/shared/tf/tf_player_shared.cpp @@ -14679,3 +14679,103 @@ bool CTFPlayer::IsHelpmeButtonPressed() const return m_flHelpmeButtonPressTime != 0.f; } + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CBaseEntity *CTFPlayer::GetPotentialUseEntity() +{ +#ifdef CLIENT_DLL + if ( m_flPotentialUseEntityScanTime < gpGlobals->curtime ) + { + // See if our potential use entity has changed + C_BaseEntity *pUseEnt = FindPotentialUseEntity(); + if ( pUseEnt != m_hPotentialUseEntity ) + { + m_hPotentialUseEntity = pUseEnt; + + if ( pUseEnt ) + { + IGameEvent *event = gameeventmanager->CreateEvent( "use_target" ); + if ( event ) + { + event->SetInt( "targetid", pUseEnt->entindex() ); + gameeventmanager->FireEventClientSide( event ); + } + } + } + + m_flPotentialUseEntityScanTime = gpGlobals->curtime + 0.5f; + } + + return m_hPotentialUseEntity; +#else + // Storing it is not needed at the moment + return FindPotentialUseEntity(); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Figure out what entity we're interacting with/has our attention. +// Similar to an event from L4D, used for game instructor and other generic purposes +//----------------------------------------------------------------------------- +CBaseEntity *CTFPlayer::FindPotentialUseEntity() +{ + if ( !IsAlive() ) + return NULL; + + // If we actually have a use entity, return that + if ( GetUseEntity() ) + return GetUseEntity(); + + // If we're taunting with someone, return the partner + if ( GetTauntPartner() ) + return GetTauntPartner(); + + // If we're a medic healing someone, return our patient + CBaseEntity *pHealTarget = MedicGetHealTarget(); + if ( pHealTarget ) + return pHealTarget; + + CBaseEntity *pGroundEnt = GetGroundEntity(); + if ( pGroundEnt ) + { + // If we're standing on a building/object, use that + if ( pGroundEnt->IsBaseObject() ) + return pGroundEnt; + } + +#ifdef CLIENT_DLL + CBaseEntity *pHealer = m_hHealer; +#else + CBaseEntity *pHealer = m_Shared.GetHealerByIndex( 0 ); +#endif + if ( pHealer ) + { + // Return our primary healer + return pHealer; + } + + // Find something directly in front of us + trace_t tr; +#ifdef CLIENT_DLL + Vector vecEyeDir; + AngleVectors( EyeAngles(), &vecEyeDir ); +#else + Vector vecEyeDir = EyeDirection3D(); +#endif + UTIL_TraceLine( EyePosition(), EyePosition() + vecEyeDir * 128.0f, (MASK_SOLID | CONTENTS_HITBOX), this, COLLISION_GROUP_NONE, &tr ); + if ( tr.DidHitNonWorldEntity() ) + { + return tr.m_pEnt; + } + + // If we're standing on something that isn't the world, return it + if ( pGroundEnt && !pGroundEnt->IsWorld() ) + return pGroundEnt; + + return NULL; +} +#endif + diff --git a/src/game/shared/tf/tf_usermessages.cpp b/src/game/shared/tf/tf_usermessages.cpp index 7e665668fcd..94e69abef26 100644 --- a/src/game/shared/tf/tf_usermessages.cpp +++ b/src/game/shared/tf/tf_usermessages.cpp @@ -66,6 +66,9 @@ void RegisterUserMessages() //============================================================================= usermessages->Register( "TrainingMsg", -1 ); // Displays a training message usermessages->Register( "TrainingObjective", -1 ); // Displays a training objective +#ifdef MAPBASE + usermessages->Register( "TrainingImage", -1 ); // Displays a training image +#endif //============================================================================= // HPE_END //=============================================================================