Team Fortress 2 Source Code as on 22/4/2020
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.

217 lines
5.8 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "player.h"
  10. #include "mathlib/mathlib.h"
  11. #include "env_speaker.h"
  12. #include "ai_speech.h"
  13. #include "stringregistry.h"
  14. #include "gamerules.h"
  15. #include "game.h"
  16. #include <ctype.h>
  17. #include "entitylist.h"
  18. #include "vstdlib/random.h"
  19. #include "engine/IEngineSound.h"
  20. #include "ndebugoverlay.h"
  21. #include "soundscape.h"
  22. #include "AI_ResponseSystem.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include "tier0/memdbgon.h"
  25. #define SF_SPEAKER_START_SILENT 1
  26. #define SF_SPEAKER_EVERYWHERE 2
  27. extern ISaveRestoreOps *responseSystemSaveRestoreOps;
  28. #include "saverestore.h"
  29. LINK_ENTITY_TO_CLASS( env_speaker, CSpeaker );
  30. BEGIN_DATADESC( CSpeaker )
  31. DEFINE_KEYFIELD( m_delayMin, FIELD_FLOAT, "delaymin" ),
  32. DEFINE_KEYFIELD( m_delayMax, FIELD_FLOAT, "delaymax" ),
  33. DEFINE_KEYFIELD( m_iszRuleScriptFile, FIELD_STRING, "rulescript" ),
  34. DEFINE_KEYFIELD( m_iszConcept, FIELD_STRING, "concept" ),
  35. // Needs to be set up in the Activate methods of derived classes
  36. //DEFINE_CUSTOM_FIELD( m_pInstancedResponseSystem, responseSystemSaveRestoreOps ),
  37. // Function Pointers
  38. DEFINE_FUNCTION( SpeakerThink ),
  39. DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
  40. DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
  41. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  42. END_DATADESC()
  43. void CSpeaker::Spawn( void )
  44. {
  45. const char *soundfile = (const char *)STRING( m_iszRuleScriptFile );
  46. if ( Q_strlen( soundfile ) < 1 )
  47. {
  48. Warning( "'speaker' entity with no Level/Sentence! at: %f, %f, %f\n", GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z );
  49. SetNextThink( gpGlobals->curtime + 0.1f );
  50. SetThink( &CSpeaker::SUB_Remove );
  51. return;
  52. }
  53. // const char *concept = (const char *)STRING( m_iszConcept );
  54. // if ( Q_strlen( concept ) < 1 )
  55. // {
  56. // Warning( "'speaker' entity using rule set %s with empty concept string\n", soundfile );
  57. // }
  58. SetSolid( SOLID_NONE );
  59. SetMoveType( MOVETYPE_NONE );
  60. SetThink(&CSpeaker::SpeakerThink);
  61. SetNextThink( TICK_NEVER_THINK );
  62. // allow on/off switching via 'use' function.
  63. Precache( );
  64. }
  65. void CSpeaker::Precache( void )
  66. {
  67. if ( !FBitSet (m_spawnflags, SF_SPEAKER_START_SILENT ) )
  68. {
  69. // set first announcement time for random n second
  70. SetNextThink( gpGlobals->curtime + random->RandomFloat(5.0, 15.0) );
  71. }
  72. if ( !m_pInstancedResponseSystem && Q_strlen( STRING(m_iszRuleScriptFile) ) > 0 )
  73. {
  74. m_pInstancedResponseSystem = PrecacheCustomResponseSystem( STRING( m_iszRuleScriptFile ) );
  75. }
  76. }
  77. //-----------------------------------------------------------------------------
  78. // Purpose: Need a custom save restore so we can restore the instanced response system by name
  79. // after we've loaded the filename from disk...
  80. // Input : &save -
  81. //-----------------------------------------------------------------------------
  82. int CSpeaker::Save( ISave &save )
  83. {
  84. int iret = BaseClass::Save( save );
  85. if ( iret )
  86. {
  87. bool doSave = ( m_pInstancedResponseSystem && ( m_iszRuleScriptFile != NULL_STRING ) ) ? true : false;
  88. save.WriteBool( &doSave );
  89. if ( doSave )
  90. {
  91. save.StartBlock( "InstancedResponseSystem" );
  92. {
  93. SaveRestoreFieldInfo_t fieldInfo = { &m_pInstancedResponseSystem, 0, NULL };
  94. responseSystemSaveRestoreOps->Save( fieldInfo, &save );
  95. }
  96. save.EndBlock();
  97. }
  98. }
  99. return iret;
  100. }
  101. //-----------------------------------------------------------------------------
  102. // Purpose:
  103. // Input : &restore -
  104. //-----------------------------------------------------------------------------
  105. int CSpeaker::Restore( IRestore &restore )
  106. {
  107. int iret = BaseClass::Restore( restore );
  108. if ( iret )
  109. {
  110. bool doRead = false;
  111. restore.ReadBool( &doRead );
  112. if ( doRead )
  113. {
  114. char szResponseSystemBlockName[SIZE_BLOCK_NAME_BUF];
  115. restore.StartBlock( szResponseSystemBlockName );
  116. if ( !Q_stricmp( szResponseSystemBlockName, "InstancedResponseSystem" ) )
  117. {
  118. if ( !m_pInstancedResponseSystem && Q_strlen( STRING(m_iszRuleScriptFile) ) > 0 )
  119. {
  120. m_pInstancedResponseSystem = PrecacheCustomResponseSystem( STRING( m_iszRuleScriptFile ) );
  121. if ( m_pInstancedResponseSystem )
  122. {
  123. SaveRestoreFieldInfo_t fieldInfo =
  124. {
  125. &m_pInstancedResponseSystem,
  126. 0,
  127. NULL
  128. };
  129. responseSystemSaveRestoreOps->Restore( fieldInfo, &restore );
  130. }
  131. }
  132. }
  133. restore.EndBlock();
  134. }
  135. }
  136. return iret;
  137. }
  138. void CSpeaker::SpeakerThink( void )
  139. {
  140. // Wait for the talking characters to finish first.
  141. if ( !g_AIFriendliesTalkSemaphore.IsAvailable( this ) || !g_AIFoesTalkSemaphore.IsAvailable( this ) )
  142. {
  143. float releaseTime = MAX( g_AIFriendliesTalkSemaphore.GetReleaseTime(), g_AIFoesTalkSemaphore.GetReleaseTime() );
  144. // Add some slop (only up to one second)
  145. releaseTime += random->RandomFloat( 0, 1 );
  146. SetNextThink( releaseTime );
  147. return;
  148. }
  149. DispatchResponse( m_iszConcept.ToCStr() );
  150. SetNextThink( gpGlobals->curtime + random->RandomFloat(m_delayMin, m_delayMax) );
  151. // time delay until it's ok to speak: used so that two NPCs don't talk at once
  152. g_AIFriendliesTalkSemaphore.Acquire( 5, this );
  153. g_AIFoesTalkSemaphore.Acquire( 5, this );
  154. }
  155. void CSpeaker::InputTurnOn( inputdata_t &inputdata )
  156. {
  157. // turn on announcements
  158. SetNextThink( gpGlobals->curtime + 0.1 );
  159. }
  160. void CSpeaker::InputTurnOff( inputdata_t &inputdata )
  161. {
  162. // turn off announcements
  163. SetNextThink( TICK_NEVER_THINK );
  164. }
  165. //
  166. // If an announcement is pending, cancel it. If no announcement is pending, start one.
  167. //
  168. void CSpeaker::InputToggle( inputdata_t &inputdata )
  169. {
  170. int fActive = (GetNextThink() > 0.0 );
  171. // fActive is true only if an announcement is pending
  172. if ( fActive )
  173. {
  174. // turn off announcements
  175. SetNextThink( TICK_NEVER_THINK );
  176. }
  177. else
  178. {
  179. // turn on announcements
  180. SetNextThink( gpGlobals->curtime + 0.1f );
  181. }
  182. }