Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1595 lines
45 KiB

  1. //========= Copyright � 1996-2008, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Client handler implementations for instruction players how to play
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "c_gameinstructor.h"
  8. #include "c_baselesson.h"
  9. #include "c_keyvalue_saver.h"
  10. #include "filesystem.h"
  11. #include "vprof.h"
  12. #include "ixboxsystem.h"
  13. #include "tier0/icommandline.h"
  14. #include "iclientmode.h"
  15. #include "isaverestore.h"
  16. #include "saverestoretypes.h"
  17. #include "matchmaking/imatchframework.h"
  18. #include "matchmaking/mm_helpers.h"
  19. #if defined( PORTAL2 )
  20. #include "matchmaking/portal2/imatchext_portal2.h"
  21. #endif
  22. #if defined( CSTRIKE15 )
  23. #include "cs_gamerules.h"
  24. #include "matchmaking/cstrike15/imatchext_cstrike15.h"
  25. #endif
  26. // memdbgon must be the last include file in a .cpp file!!!
  27. #include "tier0/memdbgon.h"
  28. PRECACHE_REGISTER_BEGIN( GLOBAL, GameinstructorIcons )
  29. PRECACHE( KV_DEP_FILE, "scripts/instructor_texturemanifest.txt" )
  30. PRECACHE_REGISTER_END()
  31. #define MOD_DIR "MOD"
  32. #define GAMEINSTRUCTOR_SCRIPT_FILE "scripts/instructor_lessons.txt"
  33. #define GAMEINSTRUCTOR_MOD_SCRIPT_FILE "scripts/mod_lessons.txt"
  34. #define GAMEINSTRUCTOR_SAVE_FILE "game_instructor_counts.txt"
  35. // Game instructor auto game system instantiation
  36. C_GameInstructor g_GameInstructor[ MAX_SPLITSCREEN_PLAYERS ];
  37. C_GameInstructor &GetGameInstructor()
  38. {
  39. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  40. return g_GameInstructor[ GET_ACTIVE_SPLITSCREEN_SLOT() ];
  41. }
  42. ConVar gameinstructor_save_restore_lessons( "gameinstructor_save_restore_lessons", "1", FCVAR_CHEAT, "Set to 0 to disable save/load of open lesson opportunities in single player." );
  43. ConVar gameinstructor_verbose( "gameinstructor_verbose", "0", FCVAR_CHEAT, "Set to 1 for standard debugging or 2 (in combo with gameinstructor_verbose_lesson) to show update actions." );
  44. ConVar gameinstructor_verbose_lesson( "gameinstructor_verbose_lesson", "", FCVAR_CHEAT, "Display more verbose information for lessons have this name." );
  45. ConVar gameinstructor_find_errors( "gameinstructor_find_errors", "0", FCVAR_CHEAT, "Set to 1 and the game instructor will run EVERY scripted command to uncover errors." );
  46. void GameInstructorEnable_ChangeCallback( IConVar *var, const char *pOldValue, float flOldValue );
  47. ConVar gameinstructor_enable( "gameinstructor_enable", "1", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Display in game lessons that teach new players.", GameInstructorEnable_ChangeCallback );
  48. extern ConVar sv_gameinstructor_disable;
  49. ConVar gameinstructor_start_sound_cooldown( "gameinstructor_start_sound_cooldown", "4.0", FCVAR_NONE, "Number of seconds forced between similar lesson start sounds." );
  50. static void FixGameInstructorLessonNameForTitleData( char *chBuffer )
  51. {
  52. for ( ; chBuffer && *chBuffer; ++ chBuffer )
  53. {
  54. char const ch = *chBuffer;
  55. if ( ch >= 'a' && ch <= 'z' )
  56. continue;
  57. if ( ch >= 'A' && ch <= 'Z' )
  58. continue;
  59. if ( ch >= '0' && ch <= '9' )
  60. continue;
  61. if ( ch == '.' || ch == '_' )
  62. continue;
  63. *chBuffer = '_';
  64. }
  65. }
  66. // Enable or Disable the game instructor based on the client setting
  67. void EnableDisableInstructor( void )
  68. {
  69. bool bEnabled = (!sv_gameinstructor_disable.GetBool() && gameinstructor_enable.GetBool());
  70. #if defined( CSTRIKE15 )
  71. if ( CSGameRules() && CSGameRules()->IsPlayingTraining() )
  72. {
  73. bEnabled = true;
  74. }
  75. #endif
  76. if ( bEnabled )
  77. {
  78. // Game instructor has been enabled, so init it!
  79. GetGameInstructor().Init();
  80. }
  81. else
  82. {
  83. // Game instructor has been disabled, so shut it down!
  84. GetGameInstructor().Shutdown();
  85. }
  86. }
  87. void GameInstructorEnable_ChangeCallback( IConVar *var, const char *pOldValue, float flOldValue )
  88. {
  89. for ( int i = 0 ; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  90. {
  91. ACTIVE_SPLITSCREEN_PLAYER_GUARD( i );
  92. if ( ( flOldValue != 0.0f ) != gameinstructor_enable.GetBool() )
  93. {
  94. EnableDisableInstructor();
  95. }
  96. }
  97. }
  98. void SVGameInstructorDisable_ChangeCallback( IConVar *var, const char *pOldValue, float flOldValue );
  99. ConVar sv_gameinstructor_disable( "sv_gameinstructor_disable", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Force all clients to disable their game instructors.", SVGameInstructorDisable_ChangeCallback );
  100. void SVGameInstructorDisable_ChangeCallback( IConVar *var, const char *pOldValue, float flOldValue )
  101. {
  102. if ( !engine )
  103. return;
  104. for ( int i = 0 ; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  105. {
  106. ACTIVE_SPLITSCREEN_PLAYER_GUARD( i );
  107. EnableDisableInstructor();
  108. }
  109. }
  110. void GameInstructor_KeyValueBuilder( KeyValues *pKeyValues )
  111. {
  112. GetGameInstructor().KeyValueBuilder( pKeyValues );
  113. }
  114. // Merged from L4D but waiting on other code to be merged before this can compile
  115. class CGameInstructorUserNotificationsListener : public IMatchEventsSink
  116. {
  117. public:
  118. CGameInstructorUserNotificationsListener() : m_nRefCount( 0 ) {}
  119. public:
  120. void RefCount( int nDelta );
  121. public:
  122. virtual void OnEvent( KeyValues *pEvent );
  123. protected:
  124. void OnGameUsersChanged();
  125. void OnStorageDeviceAvailable( int iCtrlr );
  126. protected:
  127. int m_nRefCount;
  128. };
  129. void CGameInstructorUserNotificationsListener::RefCount( int nDelta )
  130. {
  131. if ( !g_pMatchFramework )
  132. return;
  133. if ( m_nRefCount <= 0 && nDelta > 0 )
  134. {
  135. g_pMatchFramework->GetEventsSubscription()->Subscribe( this );
  136. }
  137. if ( m_nRefCount > 0 && m_nRefCount - nDelta <= 0 )
  138. {
  139. g_pMatchFramework->GetEventsSubscription()->Unsubscribe( this );
  140. }
  141. m_nRefCount = MAX( 0, m_nRefCount + nDelta );
  142. }
  143. void CGameInstructorUserNotificationsListener::OnEvent( KeyValues *pEvent )
  144. {
  145. char const *szEvent = pEvent->GetName();
  146. if ( !Q_stricmp( szEvent, "OnProfileDataLoaded" ) )
  147. {
  148. OnStorageDeviceAvailable( pEvent->GetInt( "iController" ) );
  149. }
  150. else if ( !Q_stricmp( szEvent, "OnProfilesChanged" ) )
  151. {
  152. OnGameUsersChanged();
  153. }
  154. }
  155. void CGameInstructorUserNotificationsListener::OnGameUsersChanged()
  156. {
  157. for ( int i = 0 ; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  158. {
  159. ACTIVE_SPLITSCREEN_PLAYER_GUARD( i );
  160. GetGameInstructor().ResetDisplaysAndSuccesses();
  161. }
  162. }
  163. void CGameInstructorUserNotificationsListener::OnStorageDeviceAvailable( int iCtrlr )
  164. {
  165. #ifdef _GAMECONSOLE
  166. if ( iCtrlr < 0 || iCtrlr >= XUSER_MAX_COUNT )
  167. return;
  168. int iSlot = XBX_GetSlotByUserId( iCtrlr );
  169. if ( iSlot < 0 || iSlot >= MAX_SPLITSCREEN_PLAYERS )
  170. return;
  171. #elif !defined( SPLIT_SCREEN_STUBS )
  172. int iSlot = iCtrlr;
  173. #endif
  174. ACTIVE_SPLITSCREEN_PLAYER_GUARD( iSlot );
  175. GetGameInstructor().RefreshDisplaysAndSuccesses();
  176. }
  177. CGameInstructorUserNotificationsListener s_GameInstructorUserNotificationsListener;
  178. void GameInstructor_Init()
  179. {
  180. // Subscribe for match events
  181. s_GameInstructorUserNotificationsListener.RefCount( +1 );
  182. }
  183. void GameInstructor_Shutdown()
  184. {
  185. // Unsubscribe for match events
  186. s_GameInstructorUserNotificationsListener.RefCount( -1 );
  187. }
  188. //
  189. // C_GameInstructor
  190. //
  191. bool C_GameInstructor::Init( void )
  192. {
  193. // Make sure split slot is up to date
  194. for ( int i = 0 ; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  195. {
  196. ACTIVE_SPLITSCREEN_PLAYER_GUARD( i );
  197. if ( &GetGameInstructor() == this )
  198. {
  199. SetSlot( i );
  200. break;
  201. }
  202. }
  203. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenSlot );
  204. #if defined( CSTRIKE15 )
  205. if ( (!gameinstructor_enable.GetBool() || sv_gameinstructor_disable.GetBool()) && !(CSGameRules() && CSGameRules()->IsPlayingTraining()) )
  206. #else
  207. if ( !gameinstructor_enable.GetBool() || sv_gameinstructor_disable.GetBool() )
  208. #endif
  209. {
  210. // Don't init if it's disabled
  211. return true;
  212. }
  213. if ( gameinstructor_verbose.GetInt() > 0 )
  214. {
  215. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  216. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Initializing...\n" );
  217. }
  218. if ( m_bEnsureThatInitIsNotCalledMultipleTimes )
  219. {
  220. if ( gameinstructor_verbose.GetInt() > 0 )
  221. {
  222. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  223. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Initializing is prevented by reentry guard, high level code probably has a problem...\n" );
  224. }
  225. return true;
  226. }
  227. m_bEnsureThatInitIsNotCalledMultipleTimes = true;
  228. m_bNoDraw = false;
  229. m_bHiddenDueToOtherElements = false;
  230. m_iCurrentPriority = 0;
  231. m_hLastSpectatedPlayer = NULL;
  232. m_bSpectatedPlayerChanged = false;
  233. m_szPreviousStartSound[ 0 ] = '\0';
  234. m_fNextStartSoundTime = 0;
  235. ReadLessonsFromFile( GAMEINSTRUCTOR_MOD_SCRIPT_FILE );
  236. ReadLessonsFromFile( GAMEINSTRUCTOR_SCRIPT_FILE );
  237. InitLessonPrerequisites();
  238. KeyValueSaver().InitKeyValues( GAMEINSTRUCTOR_SAVE_FILE, GameInstructor_KeyValueBuilder );
  239. ListenForGameEvent( "gameinstructor_draw" );
  240. ListenForGameEvent( "gameinstructor_nodraw" );
  241. ListenForGameEvent( "round_end" );
  242. ListenForGameEvent( "round_start" );
  243. ListenForGameEvent( "player_death" );
  244. ListenForGameEvent( "player_team" );
  245. ListenForGameEvent( "player_disconnect" );
  246. ListenForGameEvent( "map_transition" );
  247. ListenForGameEvent( "game_newmap" );
  248. #if defined( _X360 )
  249. ListenForGameEvent( "reset_game_titledata" );
  250. ListenForGameEvent( "read_game_titledata" );
  251. ListenForGameEvent( "write_game_titledata" );
  252. #endif
  253. #ifdef TERROR
  254. ListenForGameEvent( "player_bot_replace" );
  255. ListenForGameEvent( "bot_player_replace" );
  256. #endif
  257. ListenForGameEvent( "set_instructor_group_enabled" );
  258. EvaluateLessonsForGameRules();
  259. return true;
  260. }
  261. void C_GameInstructor::Shutdown( void )
  262. {
  263. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenSlot );
  264. if ( gameinstructor_verbose.GetInt() > 0 )
  265. {
  266. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  267. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Shutting down...\n" );
  268. }
  269. CloseAllOpenOpportunities();
  270. WriteSaveData();
  271. // Clear out all the lessons
  272. for ( int i = 0; i < m_Lessons.Count(); ++i )
  273. {
  274. if ( m_Lessons[ i ] )
  275. {
  276. m_Lessons[ i ]->StopListeningForAllEvents();
  277. delete m_Lessons[ i ];
  278. m_Lessons[ i ] = NULL;
  279. }
  280. }
  281. m_Lessons.RemoveAll();
  282. m_LessonGroupConVarToggles.RemoveAll();
  283. // Stop listening for events
  284. StopListeningForAllEvents();
  285. m_bEnsureThatInitIsNotCalledMultipleTimes = false;
  286. }
  287. void C_GameInstructor::UpdateHiddenByOtherElements( void )
  288. {
  289. bool bHidden = Mod_HiddenByOtherElements();
  290. if ( bHidden && !m_bHiddenDueToOtherElements )
  291. {
  292. StopAllLessons();
  293. }
  294. m_bHiddenDueToOtherElements = bHidden;
  295. }
  296. void C_GameInstructor::Update( float frametime )
  297. {
  298. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenSlot );
  299. VPROF_BUDGET( "C_GameInstructor::Update", "GameInstructor" );
  300. UpdateHiddenByOtherElements();
  301. #if defined( CSTRIKE15 )
  302. if ( (!gameinstructor_enable.GetBool() || m_bNoDraw || m_bHiddenDueToOtherElements) && !(CSGameRules() && CSGameRules()->IsPlayingTraining()) )
  303. #else
  304. if ( !gameinstructor_enable.GetBool() || m_bNoDraw || m_bHiddenDueToOtherElements )
  305. #endif
  306. {
  307. // Don't update if disabled or hidden
  308. return;
  309. }
  310. if ( gameinstructor_find_errors.GetBool() )
  311. {
  312. FindErrors();
  313. gameinstructor_find_errors.SetValue( 0 );
  314. }
  315. if ( m_bSpectatedPlayerChanged )
  316. {
  317. // Safe spot to clean out stale lessons if spectator changed
  318. if ( gameinstructor_verbose.GetInt() > 0 )
  319. {
  320. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  321. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Spectated player changed...\n" );
  322. }
  323. CloseAllOpenOpportunities();
  324. m_bSpectatedPlayerChanged = false;
  325. }
  326. // Loop through all the lesson roots and reset their active status
  327. for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i )
  328. {
  329. CBaseLesson *pLesson = m_OpenOpportunities[ i ];
  330. CBaseLesson *pRootLesson = pLesson->GetRoot();
  331. if ( pRootLesson->InstanceType() == LESSON_INSTANCE_SINGLE_ACTIVE )
  332. {
  333. pRootLesson->SetInstanceActive( false );
  334. }
  335. }
  336. int iCurrentPriority = 0;
  337. // Loop through all the open lessons
  338. for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i )
  339. {
  340. CBaseLesson *pLesson = m_OpenOpportunities[ i ];
  341. if ( !pLesson->IsOpenOpportunity() || pLesson->IsTimedOut() )
  342. {
  343. // This opportunity has closed
  344. CloseOpportunity( pLesson );
  345. RANDOM_CEG_TEST_SECRET_PERIOD( 11, 23 );
  346. continue;
  347. }
  348. // Lesson should be displayed, so it can affect priority
  349. CBaseLesson *pRootLesson = pLesson->GetRoot();
  350. bool bShouldDisplay = pLesson->ShouldDisplay();
  351. bool bIsLocked = pLesson->IsLocked();
  352. if ( ( bShouldDisplay || bIsLocked ) &&
  353. ( pLesson->GetPriority() >= m_iCurrentPriority || pLesson->NoPriority() || bIsLocked ) &&
  354. ( pRootLesson && ( pRootLesson->InstanceType() != LESSON_INSTANCE_SINGLE_ACTIVE || !pRootLesson->IsInstanceActive() ) ) )
  355. {
  356. // Lesson is at the highest priority level, isn't violating instance rules, and has met all the prerequisites
  357. if ( UpdateActiveLesson( pLesson, pRootLesson ) || pRootLesson->IsLearned() )
  358. {
  359. // Lesson is active
  360. if ( pLesson->IsVisible() || pRootLesson->IsLearned() )
  361. {
  362. pRootLesson->SetInstanceActive( true );
  363. if ( iCurrentPriority < pLesson->GetPriority() && !pLesson->NoPriority() )
  364. {
  365. // This active or learned lesson has the highest priority so far
  366. iCurrentPriority = pLesson->GetPriority();
  367. }
  368. }
  369. }
  370. else
  371. {
  372. // On second thought, this shouldn't have been displayed
  373. bShouldDisplay = false;
  374. }
  375. }
  376. else
  377. {
  378. // Lesson shouldn't be displayed right now
  379. UpdateInactiveLesson( pLesson );
  380. }
  381. }
  382. // Set the priority for next frame
  383. if ( gameinstructor_verbose.GetInt() > 1 && m_iCurrentPriority != iCurrentPriority )
  384. {
  385. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  386. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Priority changed from " );
  387. ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "%i ", m_iCurrentPriority );
  388. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "to " );
  389. ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "%i", iCurrentPriority );
  390. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" );
  391. }
  392. m_iCurrentPriority = iCurrentPriority;
  393. }
  394. void C_GameInstructor::FireGameEvent( IGameEvent *event )
  395. {
  396. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenSlot );
  397. VPROF_BUDGET( "C_GameInstructor::FireGameEvent", "GameInstructor" );
  398. const char *name = event->GetName();
  399. if ( Q_strcmp( name, "gameinstructor_draw" ) == 0 )
  400. {
  401. if ( m_bNoDraw )
  402. {
  403. if ( gameinstructor_verbose.GetInt() > 0 )
  404. {
  405. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  406. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Set to draw...\n" );
  407. }
  408. m_bNoDraw = false;
  409. }
  410. }
  411. else if ( Q_strcmp( name, "gameinstructor_nodraw" ) == 0 )
  412. {
  413. if ( !m_bNoDraw )
  414. {
  415. if ( gameinstructor_verbose.GetInt() > 0 )
  416. {
  417. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  418. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Set to not draw...\n" );
  419. }
  420. m_bNoDraw = true;
  421. StopAllLessons();
  422. }
  423. }
  424. else if ( Q_strcmp( name, "round_end" ) == 0 )
  425. {
  426. if ( gameinstructor_verbose.GetInt() > 0 )
  427. {
  428. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  429. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Round ended...\n" );
  430. }
  431. CloseAllOpenOpportunities();
  432. }
  433. else if ( Q_strcmp( name, "round_start" ) == 0 )
  434. {
  435. if ( gameinstructor_verbose.GetInt() > 0 )
  436. {
  437. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  438. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Round started...\n" );
  439. }
  440. CloseAllOpenOpportunities();
  441. EvaluateLessonsForGameRules();
  442. }
  443. else if ( Q_strcmp( name, "player_death" ) == 0 )
  444. {
  445. STEAMWORKS_TESTSECRET_AMORTIZE( 37 );
  446. C_BasePlayer *pLocalPlayer = GetLocalPlayer();
  447. if ( pLocalPlayer && pLocalPlayer == UTIL_PlayerByUserId( event->GetInt( "userid" ) ) )
  448. {
  449. if ( gameinstructor_verbose.GetInt() > 0 )
  450. {
  451. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  452. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Local player died...\n" );
  453. }
  454. for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i )
  455. {
  456. CBaseLesson *pLesson = m_OpenOpportunities[ i ];
  457. CBaseLesson *pRootLesson = pLesson->GetRoot();
  458. if ( !pRootLesson->CanOpenWhenDead() )
  459. {
  460. CloseOpportunity( pLesson );
  461. }
  462. }
  463. }
  464. }
  465. else if ( Q_strcmp( name, "player_team" ) == 0 )
  466. {
  467. C_BasePlayer *pLocalPlayer = GetLocalPlayer();
  468. if ( pLocalPlayer && pLocalPlayer == UTIL_PlayerByUserId( event->GetInt( "userid" ) ) &&
  469. ( event->GetInt( "team" ) != event->GetInt( "oldteam" ) || event->GetBool( "disconnect" ) ) )
  470. {
  471. if ( gameinstructor_verbose.GetInt() > 0 )
  472. {
  473. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  474. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Local player changed team (or disconnected)...\n" );
  475. }
  476. CloseAllOpenOpportunities();
  477. }
  478. EvaluateLessonsForGameRules();
  479. }
  480. else if ( Q_strcmp( name, "player_disconnect" ) == 0 )
  481. {
  482. C_BasePlayer *pLocalPlayer = GetLocalPlayer();
  483. if ( pLocalPlayer && pLocalPlayer == UTIL_PlayerByUserId( event->GetInt( "userid" ) ) )
  484. {
  485. if ( gameinstructor_verbose.GetInt() > 0 )
  486. {
  487. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  488. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Local player disconnected...\n" );
  489. }
  490. STEAMWORKS_SELFCHECK();
  491. CloseAllOpenOpportunities();
  492. }
  493. }
  494. else if ( Q_strcmp( name, "map_transition" ) == 0 )
  495. {
  496. if ( gameinstructor_verbose.GetInt() > 0 )
  497. {
  498. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  499. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Map transition...\n" );
  500. }
  501. CloseAllOpenOpportunities();
  502. if ( m_bNoDraw )
  503. {
  504. if ( gameinstructor_verbose.GetInt() > 0 )
  505. {
  506. ConColorMsg( Color( 255, 128, 64, 255 ), "GAME INSTRUCTOR: " );
  507. ConColorMsg( Color( 64, 128, 255, 255 ), "Set to draw...\n" );
  508. }
  509. m_bNoDraw = false;
  510. }
  511. }
  512. else if ( Q_strcmp( name, "game_newmap" ) == 0 )
  513. {
  514. if ( gameinstructor_verbose.GetInt() > 0 )
  515. {
  516. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  517. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "New map...\n" );
  518. }
  519. CloseAllOpenOpportunities();
  520. if ( m_bNoDraw )
  521. {
  522. if ( gameinstructor_verbose.GetInt() > 0 )
  523. {
  524. ConColorMsg( Color( 255, 128, 64, 255 ), "GAME INSTRUCTOR: " );
  525. ConColorMsg( Color( 64, 128, 255, 255 ), "Set to draw...\n" );
  526. }
  527. m_bNoDraw = false;
  528. }
  529. }
  530. #ifdef TERROR
  531. else if ( Q_strcmp( name, "player_bot_replace" ) == 0 )
  532. {
  533. C_BasePlayer *pLocalPlayer = GetLocalPlayer();
  534. if ( pLocalPlayer && pLocalPlayer == UTIL_PlayerByUserId( event->GetInt( "player" ) ) )
  535. {
  536. CloseAllOpenOpportunities();
  537. }
  538. else
  539. {
  540. for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i )
  541. {
  542. CBaseLesson *pLesson = m_OpenOpportunities[ i ];
  543. pLesson->SwapOutPlayers( event->GetInt( "player" ), event->GetInt( "bot" ) );
  544. }
  545. }
  546. }
  547. else if ( Q_strcmp( name, "bot_player_replace" ) == 0 )
  548. {
  549. C_BasePlayer *pLocalPlayer = GetLocalPlayer();
  550. if ( pLocalPlayer && pLocalPlayer == UTIL_PlayerByUserId( event->GetInt( "player" ) ) )
  551. {
  552. CloseAllOpenOpportunities();
  553. }
  554. else
  555. {
  556. for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i )
  557. {
  558. CBaseLesson *pLesson = m_OpenOpportunities[ i ];
  559. pLesson->SwapOutPlayers( event->GetInt( "bot" ), event->GetInt( "player" ) );
  560. }
  561. }
  562. }
  563. #endif
  564. else if ( Q_strcmp( name, "set_instructor_group_enabled" ) == 0 )
  565. {
  566. const char *pszGroup = event->GetString( "group" );
  567. bool bEnabled = event->GetInt( "enabled" ) != 0;
  568. if ( pszGroup && pszGroup[0] )
  569. {
  570. SetLessonGroupEnabled( pszGroup, bEnabled );
  571. }
  572. }
  573. #if defined( _X360 )
  574. else if ( Q_strcmp( name, "read_game_titledata" ) == 0 )
  575. {
  576. ReadSaveData();
  577. }
  578. else if ( Q_strcmp( name, "write_game_titledata" ) == 0 )
  579. {
  580. KeyValueSaver().MarkKeyValuesDirty( GAMEINSTRUCTOR_SAVE_FILE );
  581. WriteSaveData();
  582. }
  583. else if ( Q_strcmp( name, "reset_game_titledata" ) == 0 )
  584. {
  585. ResetDisplaysAndSuccesses();
  586. }
  587. #endif
  588. }
  589. void C_GameInstructor::DefineLesson( CBaseLesson *pLesson )
  590. {
  591. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenSlot );
  592. if ( gameinstructor_verbose.GetInt() > 0 )
  593. {
  594. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  595. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Lesson " );
  596. ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pLesson->GetName() );
  597. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "defined.\n" );
  598. }
  599. m_Lessons.AddToTail( pLesson );
  600. }
  601. const CBaseLesson * C_GameInstructor::GetLesson( const char *pchLessonName )
  602. {
  603. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenSlot );
  604. return GetLesson_Internal( pchLessonName );
  605. }
  606. bool C_GameInstructor::IsLessonOfSameTypeOpen( const CBaseLesson *pLesson ) const
  607. {
  608. for ( int i = 0; i < m_OpenOpportunities.Count(); ++i )
  609. {
  610. CBaseLesson *pOpenOpportunity = m_OpenOpportunities[ i ];
  611. if ( pOpenOpportunity->GetNameSymbol() == pLesson->GetNameSymbol() )
  612. {
  613. return true;
  614. }
  615. }
  616. return false;
  617. }
  618. void C_GameInstructor::SaveGameBlock( ISave *pSave )
  619. {
  620. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenSlot );
  621. if ( gameinstructor_save_restore_lessons.GetBool() )
  622. {
  623. // Save the lessons
  624. int nCount = m_OpenOpportunities.Count();
  625. pSave->WriteInt( &nCount );
  626. for ( int i = 0; i < nCount; i++ )
  627. {
  628. pSave->StartBlock();
  629. pSave->WriteAll( static_cast< CScriptedIconLesson * >( m_OpenOpportunities[ i ] ) );
  630. pSave->EndBlock();
  631. }
  632. }
  633. else
  634. {
  635. int nCount = 0;
  636. pSave->WriteInt( &nCount );
  637. }
  638. }
  639. //-----------------------------------------------------------------------------
  640. // Purpose:
  641. // Input : *pRestore -
  642. // fCreatePlayers -
  643. //-----------------------------------------------------------------------------
  644. void C_GameInstructor::RestoreGameBlock( IRestore *pRestore, bool fCreatePlayers )
  645. {
  646. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenSlot );
  647. CGameSaveRestoreInfo *pSaveData = pRestore->GetGameSaveRestoreInfo();
  648. // Game Instructor is a singleton so we only need to restore it once,
  649. // from the level that we are going into.
  650. if( !pSaveData->levelInfo.fUseLandmark )
  651. {
  652. CloseAllOpenOpportunities();
  653. if ( gameinstructor_save_restore_lessons.GetBool() )
  654. {
  655. // Read in the lessons
  656. int nCount = pRestore->ReadInt();
  657. for ( int i = 0; i < nCount; i++ )
  658. {
  659. CScriptedIconLesson *pOpenLesson = new CScriptedIconLesson( "", false, true, m_nSplitScreenSlot );
  660. pRestore->StartBlock();
  661. pRestore->ReadAll( pOpenLesson );
  662. pRestore->EndBlock();
  663. const CScriptedIconLesson *pRootLesson = static_cast<const CScriptedIconLesson *>( GetLesson( pOpenLesson->GetName() ) );
  664. pOpenLesson->SetRoot( const_cast<CScriptedIconLesson *>( pRootLesson ) );
  665. GetGameInstructor().OpenOpportunity( pOpenLesson );
  666. }
  667. }
  668. else
  669. {
  670. CScriptedIconLesson *pOpenLessonDummy = new CScriptedIconLesson( "", false, true, m_nSplitScreenSlot );
  671. int nCount = pRestore->ReadInt();
  672. for ( int i = 0; i < nCount; i++ )
  673. {
  674. pRestore->StartBlock();
  675. pRestore->ReadAll( pOpenLessonDummy );
  676. pRestore->EndBlock();
  677. }
  678. delete pOpenLessonDummy;
  679. }
  680. }
  681. }
  682. bool C_GameInstructor::ReadSaveData( void )
  683. {
  684. // for external playtests, don't ever read in persisted instructor state, always start fresh
  685. if( CommandLine()->FindParm( "-playtest" ) )
  686. return true;
  687. if ( m_bHasLoadedSaveData )
  688. return true;
  689. // Always reset state first in case storage device
  690. // was declined or ends up in faulty state
  691. ResetDisplaysAndSuccesses();
  692. if ( m_nSplitScreenSlot < 0 || m_nSplitScreenSlot >= (int) XBX_GetNumGameUsers() )
  693. return true;
  694. IPlayerLocal *pPlayer = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( XBX_GetUserId( m_nSplitScreenSlot ) );
  695. if ( !pPlayer )
  696. return true;
  697. TitleDataFieldsDescription_t const *fields = g_pMatchFramework->GetMatchTitle()->DescribeTitleDataStorage();
  698. #if defined( _X360 )
  699. // check version number is valid to ensure there is good data before reading
  700. ConVarRef cl_titledataversionblock3 ( "cl_titledataversionblock3" );
  701. TitleDataFieldsDescription_t const *versionField = TitleDataFieldsDescriptionFindByString( fields, "TITLEDATA.BLOCK3.VERSION" );
  702. if ( !versionField || versionField->m_eDataType != TitleDataFieldsDescription_t::DT_uint16 )
  703. {
  704. Warning( "C_GameInstructor::ReadSaveData TITLEDATA.BLOCK3.VERSION is expected to be defined as DT_uint16\n" );
  705. return true;
  706. }
  707. int versionNumber = TitleDataFieldsDescriptionGetValue<uint16>( versionField, pPlayer );
  708. if ( versionNumber != cl_titledataversionblock3.GetInt() )
  709. {
  710. Warning ( "C_GameInstructor::ReadSaveData wrong version # for TITLEDATA.BLOCK3.VERSION; expected %d, got %d\n", cl_titledataversionblock3.GetInt(), versionNumber );
  711. return true;
  712. }
  713. #endif
  714. m_bHasLoadedSaveData = true;
  715. int nVersion = 0;
  716. for ( int i = 0; i < m_Lessons.Count();++i )
  717. {
  718. CBaseLesson *pLesson = m_Lessons[i];
  719. if ( !pLesson || ( pLesson->GetDisplayLimit() == 0 && pLesson->GetSuccessLimit() == 0 ) )
  720. continue;
  721. CFmtStr tdKey( "GI.lesson.%s", pLesson->GetName() );
  722. FixGameInstructorLessonNameForTitleData( tdKey.Access() );
  723. TitleDataFieldsDescription_t const *fdKey = TitleDataFieldsDescriptionFindByString( fields, tdKey );
  724. if ( !fdKey )
  725. {
  726. Warning( "C_GameInstructor::ReadSaveData failed to read %s\n", tdKey.Access() );
  727. continue;
  728. }
  729. TitleData3::GameInstructorData_t::LessonInfo_t li;
  730. li.u8dummy = TitleDataFieldsDescriptionGetValue<uint8>( fdKey, pPlayer );
  731. pLesson->SetDisplayCount( li.display );
  732. pLesson->SetSuccessCount( li.success );
  733. if ( Q_strcmp( pLesson->GetName(), "version number" ) == 0 )
  734. {
  735. nVersion = pLesson->GetSuccessCount();
  736. }
  737. }
  738. CBaseLesson *pLessonVersionNumber = GetLesson_Internal( "version number" );
  739. if ( pLessonVersionNumber && !pLessonVersionNumber->IsLearned() )
  740. {
  741. ResetDisplaysAndSuccesses();
  742. pLessonVersionNumber->SetSuccessCount( pLessonVersionNumber->GetSuccessLimit() );
  743. KeyValueSaver().MarkKeyValuesDirty( GAMEINSTRUCTOR_SAVE_FILE );
  744. }
  745. #ifdef TERROR
  746. else if ( IsPressDemoMode() )
  747. {
  748. ResetDisplaysAndSuccesses();
  749. KeyValueSaver().MarkKeyValuesDirty( GAMEINSTRUCTOR_SAVE_FILE );
  750. }
  751. #endif
  752. return true;
  753. }
  754. bool C_GameInstructor::WriteSaveData( void )
  755. {
  756. if ( engine->IsPlayingDemo() )
  757. return false;
  758. return KeyValueSaver().WriteDirtyKeyValues( GAMEINSTRUCTOR_SAVE_FILE );
  759. }
  760. void C_GameInstructor::KeyValueBuilder( KeyValues *pKeyValues )
  761. {
  762. if ( m_nSplitScreenSlot < 0 || m_nSplitScreenSlot >= (int) XBX_GetNumGameUsers() )
  763. return;
  764. IPlayerLocal *pPlayer = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( XBX_GetUserId( m_nSplitScreenSlot ) );
  765. if ( !pPlayer )
  766. return;
  767. TitleDataFieldsDescription_t const *fields = g_pMatchFramework->GetMatchTitle()->DescribeTitleDataStorage();
  768. // Build key value data to save
  769. for ( int i = 0; i < m_Lessons.Count();++i )
  770. {
  771. CBaseLesson *pLesson = m_Lessons[ i ];
  772. TitleData3::GameInstructorData_t::LessonInfo_t li;
  773. li.u8dummy = 0;
  774. li.display = pLesson->GetDisplayCount() & 0xF;
  775. li.success = pLesson->GetSuccessCount() & 0xF;
  776. CFmtStr tdKey( "GI.lesson.%s", pLesson->GetName() );
  777. FixGameInstructorLessonNameForTitleData( tdKey.Access() );
  778. TitleDataFieldsDescription_t const *fdKey = TitleDataFieldsDescriptionFindByString( fields, tdKey );
  779. if ( fdKey )
  780. {
  781. TitleDataFieldsDescriptionSetValue<uint8>( fdKey, pPlayer, li.u8dummy );
  782. }
  783. else
  784. {
  785. DevWarning( "C_GameInstructor::KeyValueBuilder did not save %s; is it missing from inc_gameinstructor_lessons.inc\n", tdKey.Access() );
  786. }
  787. }
  788. }
  789. void C_GameInstructor::RefreshDisplaysAndSuccesses( void )
  790. {
  791. m_bHasLoadedSaveData = false;
  792. ReadSaveData();
  793. }
  794. void C_GameInstructor::ResetDisplaysAndSuccesses( void )
  795. {
  796. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenSlot );
  797. if ( gameinstructor_verbose.GetInt() > 0 )
  798. {
  799. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  800. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Reset all lesson display and success counts.\n" );
  801. }
  802. for ( int i = 0; i < m_Lessons.Count(); ++i )
  803. {
  804. m_Lessons[ i ]->ResetDisplaysAndSuccesses();
  805. }
  806. }
  807. void C_GameInstructor::MarkDisplayed( const char *pchLessonName )
  808. {
  809. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenSlot );
  810. CBaseLesson *pLesson = GetLesson_Internal( pchLessonName );
  811. if ( !pLesson )
  812. return;
  813. if ( gameinstructor_verbose.GetInt() > 0 )
  814. {
  815. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  816. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Lesson " );
  817. ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLesson->GetName() );
  818. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "marked as displayed.\n" );
  819. }
  820. if ( pLesson->IncDisplayCount() )
  821. {
  822. KeyValueSaver().MarkKeyValuesDirty( GAMEINSTRUCTOR_SAVE_FILE );
  823. }
  824. }
  825. void C_GameInstructor::MarkSucceeded( const char *pchLessonName )
  826. {
  827. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenSlot );
  828. CBaseLesson *pLesson = GetLesson_Internal( pchLessonName );
  829. if ( !pLesson )
  830. return;
  831. if ( gameinstructor_verbose.GetInt() > 0 )
  832. {
  833. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  834. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Lesson " );
  835. ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\"%s\" ", pLesson->GetName() );
  836. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "marked as succeeded.\n" );
  837. }
  838. if ( pLesson->IncSuccessCount() )
  839. {
  840. KeyValueSaver().MarkKeyValuesDirty( GAMEINSTRUCTOR_SAVE_FILE );
  841. }
  842. }
  843. void C_GameInstructor::PlaySound( const char *pchSoundName )
  844. {
  845. // emit alert sound
  846. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  847. if ( pLocalPlayer )
  848. {
  849. // Local player exists
  850. if ( pchSoundName[ 0 ] != '\0' && Q_strcmp( m_szPreviousStartSound, pchSoundName ) != 0 )
  851. {
  852. V_strcpy_safe( m_szPreviousStartSound, pchSoundName );
  853. m_fNextStartSoundTime = 0.0f;
  854. }
  855. if ( gpGlobals->curtime >= m_fNextStartSoundTime && pchSoundName[ 0 ] != '\0' )
  856. {
  857. // A sound was specified, so play it!
  858. pLocalPlayer->EmitSound( pchSoundName );
  859. m_fNextStartSoundTime = gpGlobals->curtime + gameinstructor_start_sound_cooldown.GetFloat();
  860. }
  861. }
  862. }
  863. bool C_GameInstructor::OpenOpportunity( CBaseLesson *pLesson )
  864. {
  865. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenSlot );
  866. // Get the root lesson
  867. CBaseLesson *pRootLesson = pLesson->GetRoot();
  868. if ( !pRootLesson )
  869. {
  870. if ( gameinstructor_verbose.GetInt() > 0 )
  871. {
  872. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  873. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " );
  874. ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() );
  875. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "NOT opened (because root lesson could not be found).\n" );
  876. }
  877. delete pLesson;
  878. return false;
  879. }
  880. C_BasePlayer *pLocalPlayer = GetLocalPlayer();
  881. if ( !pRootLesson->CanOpenWhenDead() && ( !pLocalPlayer || !pLocalPlayer->IsAlive() ) )
  882. {
  883. // If the player is dead don't allow lessons that can't be opened when dead
  884. if ( gameinstructor_verbose.GetInt() > 0 )
  885. {
  886. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  887. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " );
  888. ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() );
  889. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "NOT opened (because player is dead and can_open_when_dead not set).\n" );
  890. }
  891. delete pLesson;
  892. return false;
  893. }
  894. if ( !pRootLesson->CanOpenOnceLearned() && pRootLesson->IsLearned() )
  895. {
  896. // If the player has learned this and we don't want it to open onced learned
  897. if ( gameinstructor_verbose.GetInt() > 0 )
  898. {
  899. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  900. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " );
  901. ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() );
  902. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "NOT opened (because this is learned and ONCE_LEARNED_NEVER_OPEN is set).\n" );
  903. }
  904. delete pLesson;
  905. return false;
  906. }
  907. if ( !pRootLesson->PrerequisitesHaveBeenMet() )
  908. {
  909. // If the prereqs haven't been met, don't open it
  910. if ( gameinstructor_verbose.GetInt() > 0 )
  911. {
  912. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  913. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " );
  914. ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() );
  915. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "NOT opened (because prereqs haven't been met).\n" );
  916. }
  917. delete pLesson;
  918. return false;
  919. }
  920. if ( pRootLesson->InstanceType() == LESSON_INSTANCE_FIXED_REPLACE )
  921. {
  922. CBaseLesson *pLessonToReplace = NULL;
  923. CBaseLesson *pLastReplacableLesson = NULL;
  924. int iInstanceCount = 0;
  925. // Check how many are already open
  926. for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i )
  927. {
  928. CBaseLesson *pOpenOpportunity = m_OpenOpportunities[ i ];
  929. if ( pOpenOpportunity->GetNameSymbol() == pLesson->GetNameSymbol() &&
  930. pOpenOpportunity->GetReplaceKeySymbol() == pLesson->GetReplaceKeySymbol() )
  931. {
  932. iInstanceCount++;
  933. if ( pRootLesson->ShouldReplaceOnlyWhenStopped() )
  934. {
  935. if ( !pOpenOpportunity->IsInstructing() )
  936. {
  937. pLastReplacableLesson = pOpenOpportunity;
  938. }
  939. }
  940. else
  941. {
  942. pLastReplacableLesson = pOpenOpportunity;
  943. }
  944. if ( iInstanceCount >= pRootLesson->GetFixedInstancesMax() )
  945. {
  946. pLessonToReplace = pLastReplacableLesson;
  947. break;
  948. }
  949. }
  950. }
  951. if ( pLessonToReplace )
  952. {
  953. // Take the place of the previous instance
  954. if ( gameinstructor_verbose.GetInt() > 0 )
  955. {
  956. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  957. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " );
  958. ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLesson->GetName() );
  959. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "replacing open lesson of same type.\n" );
  960. }
  961. pLesson->TakePlaceOf( pLessonToReplace );
  962. CloseOpportunity( pLessonToReplace );
  963. }
  964. else if ( iInstanceCount >= pRootLesson->GetFixedInstancesMax() )
  965. {
  966. // Don't add another lesson of this type
  967. if ( gameinstructor_verbose.GetInt() > 0 )
  968. {
  969. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  970. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " );
  971. ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() );
  972. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "NOT opened (there is too many started lessons of this type).\n" );
  973. }
  974. delete pLesson;
  975. return false;
  976. }
  977. }
  978. if ( gameinstructor_verbose.GetInt() > 0 )
  979. {
  980. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  981. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " );
  982. ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLesson->GetName() );
  983. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "opened.\n" );
  984. }
  985. m_OpenOpportunities.AddToTail( pLesson );
  986. return true;
  987. }
  988. void C_GameInstructor::DumpOpenOpportunities( void )
  989. {
  990. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  991. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Open lessons...\n" );
  992. for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i )
  993. {
  994. CBaseLesson *pLesson = m_OpenOpportunities[ i ];
  995. CBaseLesson *pRootLesson = pLesson->GetRoot();
  996. Color color;
  997. if ( pLesson->IsInstructing() )
  998. {
  999. // Green
  1000. color = CBaseLesson::m_rgbaVerboseOpen;
  1001. }
  1002. else if ( pRootLesson->IsLearned() && pLesson->GetPriority() >= m_iCurrentPriority )
  1003. {
  1004. // Yellow
  1005. color = CBaseLesson::m_rgbaVerboseSuccess;
  1006. }
  1007. else
  1008. {
  1009. // Red
  1010. color = CBaseLesson::m_rgbaVerboseClose;
  1011. }
  1012. ConColorMsg( color, "\t%s\n", pLesson->GetName() );
  1013. }
  1014. }
  1015. KeyValues * C_GameInstructor::GetScriptKeys( void )
  1016. {
  1017. return m_pScriptKeys;
  1018. }
  1019. C_BasePlayer * C_GameInstructor::GetLocalPlayer( void )
  1020. {
  1021. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  1022. // If we're not a developer, don't do the special spectator hook ups
  1023. if ( !developer.GetBool() )
  1024. return pLocalPlayer;
  1025. // If there is no local player and we're not spectating, just return that
  1026. if ( !pLocalPlayer || pLocalPlayer->GetTeamNumber() != TEAM_SPECTATOR )
  1027. return pLocalPlayer;
  1028. // We're purely a spectator let's get lessons of the person we're spectating
  1029. C_BasePlayer *pSpectatedPlayer = NULL;
  1030. if ( pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE || pLocalPlayer->GetObserverMode() == OBS_MODE_CHASE )
  1031. {
  1032. pSpectatedPlayer = ToBasePlayer( pLocalPlayer->GetObserverTarget() );
  1033. }
  1034. if ( m_hLastSpectatedPlayer != pSpectatedPlayer )
  1035. {
  1036. // We're spectating someone new! Close all the stale lessons!
  1037. m_bSpectatedPlayerChanged = true;
  1038. m_hLastSpectatedPlayer = pSpectatedPlayer;
  1039. }
  1040. return pSpectatedPlayer;
  1041. }
  1042. void C_GameInstructor::EvaluateLessonsForGameRules( void )
  1043. {
  1044. // Enable everything by default
  1045. for ( int i = 0; i < m_Lessons.Count(); ++i )
  1046. {
  1047. m_Lessons[ i ]->SetEnabled( true );
  1048. }
  1049. // Then see if we should disable anything
  1050. for ( int nConVar = 0; nConVar < m_LessonGroupConVarToggles.Count(); ++nConVar )
  1051. {
  1052. LessonGroupConVarToggle_t *pLessonGroupConVarToggle = &(m_LessonGroupConVarToggles[ nConVar ]);
  1053. if ( pLessonGroupConVarToggle->var.IsValid() )
  1054. {
  1055. if ( pLessonGroupConVarToggle->var.GetBool() )
  1056. {
  1057. SetLessonGroupEnabled( pLessonGroupConVarToggle->szLessonGroupName, false );
  1058. }
  1059. }
  1060. }
  1061. }
  1062. void C_GameInstructor::SetLessonGroupEnabled( const char *pszGroup, bool bEnabled )
  1063. {
  1064. for ( int i = 0; i < m_Lessons.Count(); ++i )
  1065. {
  1066. if ( !Q_stricmp(pszGroup, m_Lessons[i]->GetGroup()) )
  1067. {
  1068. m_Lessons[ i ]->SetEnabled( bEnabled );
  1069. }
  1070. }
  1071. }
  1072. void C_GameInstructor::FindErrors( void )
  1073. {
  1074. // Loop through all the lesson and run all their scripted actions
  1075. for ( int i = 0; i < m_Lessons.Count(); ++i )
  1076. {
  1077. CScriptedIconLesson *pLesson = dynamic_cast<CScriptedIconLesson *>( m_Lessons[ i ] );
  1078. if ( pLesson )
  1079. {
  1080. // Process all open events
  1081. for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetOpenEvents().Count(); ++iLessonEvent )
  1082. {
  1083. const LessonEvent_t *pLessonEvent = &(pLesson->GetOpenEvents()[ iLessonEvent ]);
  1084. pLesson->ProcessElements( NULL, &(pLessonEvent->elements) );
  1085. }
  1086. // Process all close events
  1087. for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetCloseEvents().Count(); ++iLessonEvent )
  1088. {
  1089. const LessonEvent_t *pLessonEvent = &(pLesson->GetCloseEvents()[ iLessonEvent ]);
  1090. pLesson->ProcessElements( NULL, &(pLessonEvent->elements) );
  1091. }
  1092. // Process all success events
  1093. for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetSuccessEvents().Count(); ++iLessonEvent )
  1094. {
  1095. const LessonEvent_t *pLessonEvent = &(pLesson->GetSuccessEvents()[ iLessonEvent ]);
  1096. pLesson->ProcessElements( NULL, &(pLessonEvent->elements) );
  1097. }
  1098. // Process all on open events
  1099. for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetOnOpenEvents().Count(); ++iLessonEvent )
  1100. {
  1101. const LessonEvent_t *pLessonEvent = &(pLesson->GetOnOpenEvents()[ iLessonEvent ]);
  1102. pLesson->ProcessElements( NULL, &(pLessonEvent->elements) );
  1103. }
  1104. // Process all update events
  1105. for ( int iLessonEvent = 0; iLessonEvent < pLesson->GetUpdateEvents().Count(); ++iLessonEvent )
  1106. {
  1107. const LessonEvent_t *pLessonEvent = &(pLesson->GetUpdateEvents()[ iLessonEvent ]);
  1108. pLesson->ProcessElements( NULL, &(pLessonEvent->elements) );
  1109. }
  1110. }
  1111. }
  1112. }
  1113. bool C_GameInstructor::UpdateActiveLesson( CBaseLesson *pLesson, const CBaseLesson *pRootLesson )
  1114. {
  1115. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenSlot );
  1116. VPROF_BUDGET( "C_GameInstructor::UpdateActiveLesson", "GameInstructor" );
  1117. bool bIsOpen = pLesson->IsInstructing();
  1118. RANDOM_CEG_TEST_SECRET()
  1119. if ( !bIsOpen && !pRootLesson->IsLearned() )
  1120. {
  1121. pLesson->SetStartTime();
  1122. pLesson->Start();
  1123. // Check to see if it successfully started
  1124. bIsOpen = ( pLesson->IsOpenOpportunity() && pLesson->ShouldDisplay() );
  1125. if ( bIsOpen )
  1126. {
  1127. // Lesson hasn't been started and hasn't been learned
  1128. if ( gameinstructor_verbose.GetInt() > 0 )
  1129. {
  1130. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  1131. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Started lesson " );
  1132. ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", pLesson->GetName() );
  1133. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" );
  1134. }
  1135. }
  1136. else
  1137. {
  1138. pLesson->Stop();
  1139. pLesson->ResetStartTime();
  1140. }
  1141. }
  1142. if ( bIsOpen )
  1143. {
  1144. // Update the running lesson
  1145. pLesson->Update();
  1146. return true;
  1147. }
  1148. else
  1149. {
  1150. pLesson->UpdateInactive();
  1151. return false;
  1152. }
  1153. }
  1154. void C_GameInstructor::UpdateInactiveLesson( CBaseLesson *pLesson )
  1155. {
  1156. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenSlot );
  1157. VPROF_BUDGET( "C_GameInstructor::UpdateInactiveLesson", "GameInstructor" );
  1158. if ( pLesson->IsInstructing() )
  1159. {
  1160. // Lesson hasn't been stopped
  1161. if ( gameinstructor_verbose.GetInt() > 0 )
  1162. {
  1163. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  1164. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Stopped lesson " );
  1165. ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\"", pLesson->GetName() );
  1166. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" );
  1167. }
  1168. pLesson->Stop();
  1169. pLesson->ResetStartTime();
  1170. }
  1171. pLesson->UpdateInactive();
  1172. }
  1173. CBaseLesson * C_GameInstructor::GetLesson_Internal( const char *pchLessonName )
  1174. {
  1175. for ( int i = 0; i < m_Lessons.Count(); ++i )
  1176. {
  1177. CBaseLesson *pLesson = m_Lessons[ i ];
  1178. if ( Q_strcmp( pLesson->GetName(), pchLessonName ) == 0 )
  1179. {
  1180. return pLesson;
  1181. }
  1182. }
  1183. return NULL;
  1184. }
  1185. void C_GameInstructor::StopAllLessons( void )
  1186. {
  1187. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenSlot );
  1188. // Stop all the current lessons
  1189. for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i )
  1190. {
  1191. CBaseLesson *pLesson = m_OpenOpportunities[ i ];
  1192. UpdateInactiveLesson( pLesson );
  1193. }
  1194. }
  1195. void C_GameInstructor::CloseAllOpenOpportunities( void )
  1196. {
  1197. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenSlot );
  1198. // Clear out all the open opportunities
  1199. for ( int i = m_OpenOpportunities.Count() - 1; i >= 0; --i )
  1200. {
  1201. CBaseLesson *pLesson = m_OpenOpportunities[ i ];
  1202. CloseOpportunity( pLesson );
  1203. }
  1204. Assert( m_OpenOpportunities.Count() == 0 );
  1205. }
  1206. void C_GameInstructor::CloseOpportunity( CBaseLesson *pLesson )
  1207. {
  1208. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenSlot );
  1209. UpdateInactiveLesson( pLesson );
  1210. if ( pLesson->WasDisplayed() )
  1211. {
  1212. MarkDisplayed( pLesson->GetName() );
  1213. }
  1214. if ( gameinstructor_verbose.GetInt() > 0 )
  1215. {
  1216. ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " );
  1217. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " );
  1218. ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLesson->GetName() );
  1219. ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "closed for reason: " );
  1220. ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "%s\n", pLesson->GetCloseReason() );
  1221. }
  1222. pLesson->StopListeningForAllEvents();
  1223. m_OpenOpportunities.FindAndRemove( pLesson );
  1224. delete pLesson;
  1225. }
  1226. void C_GameInstructor::ReadLessonsFromFile( const char *pchFileName )
  1227. {
  1228. // Static init function
  1229. CScriptedIconLesson::PreReadLessonsFromFile();
  1230. MEM_ALLOC_CREDIT();
  1231. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenSlot );
  1232. KeyValues *pLessonKeys = new KeyValues( "instructor_lessons" );
  1233. KeyValues::AutoDelete autoDelete(pLessonKeys);
  1234. pLessonKeys->LoadFromFile( g_pFullFileSystem, pchFileName, NULL );
  1235. for ( m_pScriptKeys = pLessonKeys->GetFirstTrueSubKey(); m_pScriptKeys; m_pScriptKeys = m_pScriptKeys->GetNextTrueSubKey() )
  1236. {
  1237. if ( Q_stricmp( m_pScriptKeys->GetName(), "GroupConVarToggle" ) == 0 )
  1238. {
  1239. // Add convar group toggler to the list
  1240. int nLessonGroupConVarToggle = m_LessonGroupConVarToggles.AddToTail( LessonGroupConVarToggle_t( m_pScriptKeys->GetString( "convar" ) ) );
  1241. LessonGroupConVarToggle_t *pLessonGroupConVarToggle = &(m_LessonGroupConVarToggles[ nLessonGroupConVarToggle ]);
  1242. V_strcpy_safe( pLessonGroupConVarToggle->szLessonGroupName, m_pScriptKeys->GetString( "group" ) );
  1243. continue;
  1244. }
  1245. // Ensure that lessons aren't added twice
  1246. if ( GetLesson_Internal( m_pScriptKeys->GetName() ) )
  1247. {
  1248. DevWarning( "Lesson \"%s\" defined twice!\n", m_pScriptKeys->GetName() );
  1249. continue;
  1250. }
  1251. CScriptedIconLesson *pNewLesson = new CScriptedIconLesson( m_pScriptKeys->GetName(), false, false, m_nSplitScreenSlot );
  1252. GetGameInstructor().DefineLesson( pNewLesson );
  1253. }
  1254. m_pScriptKeys = NULL;
  1255. }
  1256. void C_GameInstructor::InitLessonPrerequisites( void )
  1257. {
  1258. for ( int i = 0; i < m_Lessons.Count(); ++i )
  1259. {
  1260. m_Lessons[ i ]->InitPrerequisites();
  1261. }
  1262. }
  1263. //====================================================================================================
  1264. // CLIENTSIDE GAME INSTRUCTOR SAVE/RESTORE
  1265. //====================================================================================================
  1266. static short GAMEINSTRUCTOR_SAVE_RESTORE_VERSION = 1;
  1267. class CGameInstructorSaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler
  1268. {
  1269. struct QueuedItem_t;
  1270. public:
  1271. CGameInstructorSaveRestoreBlockHandler()
  1272. {
  1273. }
  1274. const char *GetBlockName()
  1275. {
  1276. return "GameInstructor";
  1277. }
  1278. virtual void Save( ISave *pSave )
  1279. {
  1280. ACTIVE_SPLITSCREEN_PLAYER_GUARD( 0 );
  1281. GetGameInstructor().SaveGameBlock( pSave );
  1282. }
  1283. virtual void WriteSaveHeaders( ISave *pSave )
  1284. {
  1285. pSave->WriteShort( &GAMEINSTRUCTOR_SAVE_RESTORE_VERSION );
  1286. }
  1287. virtual void ReadRestoreHeaders( IRestore *pRestore )
  1288. {
  1289. // No reason why any future version shouldn't try to retain backward compatibility. The default here is to not do so.
  1290. short version = pRestore->ReadShort();
  1291. m_bDoLoad = ( version == GAMEINSTRUCTOR_SAVE_RESTORE_VERSION );
  1292. }
  1293. virtual void Restore( IRestore *pRestore, bool fCreatePlayers )
  1294. {
  1295. if ( m_bDoLoad )
  1296. {
  1297. ACTIVE_SPLITSCREEN_PLAYER_GUARD( 0 );
  1298. GetGameInstructor().RestoreGameBlock( pRestore, fCreatePlayers );
  1299. }
  1300. }
  1301. private:
  1302. bool m_bDoLoad;
  1303. };
  1304. CGameInstructorSaveRestoreBlockHandler g_GameInstructorSaveRestoreBlockHandler;
  1305. ISaveRestoreBlockHandler *GetGameInstructorRestoreBlockHandler()
  1306. {
  1307. return &g_GameInstructorSaveRestoreBlockHandler;
  1308. }
  1309. CON_COMMAND_F( gameinstructor_reload_lessons, "Shuts down all open lessons and reloads them from the script file.", FCVAR_CHEAT )
  1310. {
  1311. for ( int i = 0 ; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  1312. {
  1313. ACTIVE_SPLITSCREEN_PLAYER_GUARD( i );
  1314. GetGameInstructor().Shutdown();
  1315. GetGameInstructor().Init();
  1316. }
  1317. }
  1318. CON_COMMAND_F( gameinstructor_reset_counts, "Resets all display and success counts to zero.", FCVAR_NONE )
  1319. {
  1320. for ( int i = 0 ; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  1321. {
  1322. ACTIVE_SPLITSCREEN_PLAYER_GUARD( i );
  1323. GetGameInstructor().ResetDisplaysAndSuccesses();
  1324. }
  1325. }
  1326. CON_COMMAND_F( gameinstructor_dump_open_lessons, "Gives a list of all currently open lessons.", FCVAR_CHEAT )
  1327. {
  1328. for ( int i = 0 ; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  1329. {
  1330. ACTIVE_SPLITSCREEN_PLAYER_GUARD( i );
  1331. GetGameInstructor().DumpOpenOpportunities();
  1332. }
  1333. }