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.

1526 lines
41 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Entities relating to in-level sound effects.
  4. //
  5. // ambient_generic: a sound emitter used for one-shot and looping sounds.
  6. //
  7. // env_speaker: used for public address announcements over loudspeakers.
  8. // This tries not to drown out talking NPCs.
  9. //
  10. // env_soundscape: controls what sound script an area uses.
  11. //
  12. //=============================================================================//
  13. #include "cbase.h"
  14. #include "player.h"
  15. #include "mathlib/mathlib.h"
  16. #include "ai_speech.h"
  17. #include "stringregistry.h"
  18. #include "gamerules.h"
  19. #include "game.h"
  20. #include <ctype.h>
  21. #include "entitylist.h"
  22. #include "vstdlib/random.h"
  23. #include "engine/IEngineSound.h"
  24. #include "ndebugoverlay.h"
  25. #include "soundscape.h"
  26. #include "igamesystem.h"
  27. #include "KeyValues.h"
  28. #include "filesystem.h"
  29. #ifdef PORTAL
  30. #include "portal_gamerules.h"
  31. #endif // PORTAL
  32. // memdbgon must be the last include file in a .cpp file!!!
  33. #include "tier0/memdbgon.h"
  34. //-----------------------------------------------------------------------------
  35. // Purpose: Compute a suitable attenuation value given an audible radius
  36. // Input : radius -
  37. // playEverywhere - (disable attenuation)
  38. //-----------------------------------------------------------------------------
  39. #define REFERENCE_dB 60.0
  40. #define AMBIENT_GENERIC_UPDATE_RATE 5 // update at 5hz
  41. #define AMBIENT_GENERIC_THINK_DELAY ( 1.0f / float( AMBIENT_GENERIC_UPDATE_RATE ) )
  42. #ifdef HL1_DLL
  43. ConVar hl1_ref_db_distance( "hl1_ref_db_distance", "18.0" );
  44. #define REFERENCE_dB_DISTANCE hl1_ref_db_distance.GetFloat()
  45. #else
  46. #define REFERENCE_dB_DISTANCE 36.0
  47. #endif//HL1_DLL
  48. static soundlevel_t ComputeSoundlevel( float radius, bool playEverywhere )
  49. {
  50. soundlevel_t soundlevel = SNDLVL_NONE;
  51. if ( radius > 0 && !playEverywhere )
  52. {
  53. // attenuation is set to a distance, compute falloff
  54. float dB_loss = 20 * log10( radius / REFERENCE_dB_DISTANCE );
  55. soundlevel = (soundlevel_t)(int)(40 + dB_loss); // sound at 40dB at reference distance
  56. }
  57. return soundlevel;
  58. }
  59. // ==================== GENERIC AMBIENT SOUND ======================================
  60. // runtime pitch shift and volume fadein/out structure
  61. // NOTE: IF YOU CHANGE THIS STRUCT YOU MUST CHANGE THE SAVE/RESTORE VERSION NUMBER
  62. // SEE BELOW (in the typedescription for the class)
  63. typedef struct dynpitchvol
  64. {
  65. // NOTE: do not change the order of these parameters
  66. // NOTE: unless you also change order of rgdpvpreset array elements!
  67. int preset;
  68. int pitchrun; // pitch shift % when sound is running 0 - 255
  69. int pitchstart; // pitch shift % when sound stops or starts 0 - 255
  70. int spinup; // spinup time 0 - 100
  71. int spindown; // spindown time 0 - 100
  72. int volrun; // volume change % when sound is running 0 - 10
  73. int volstart; // volume change % when sound stops or starts 0 - 10
  74. int fadein; // volume fade in time 0 - 100
  75. int fadeout; // volume fade out time 0 - 100
  76. // Low Frequency Oscillator
  77. int lfotype; // 0) off 1) square 2) triangle 3) random
  78. int lforate; // 0 - 1000, how fast lfo osciallates
  79. int lfomodpitch; // 0-100 mod of current pitch. 0 is off.
  80. int lfomodvol; // 0-100 mod of current volume. 0 is off.
  81. int cspinup; // each trigger hit increments counter and spinup pitch
  82. int cspincount;
  83. int pitch;
  84. int spinupsav;
  85. int spindownsav;
  86. int pitchfrac;
  87. int vol;
  88. int fadeinsav;
  89. int fadeoutsav;
  90. int volfrac;
  91. int lfofrac;
  92. int lfomult;
  93. } dynpitchvol_t;
  94. #define CDPVPRESETMAX 27
  95. // presets for runtime pitch and vol modulation of ambient sounds
  96. dynpitchvol_t rgdpvpreset[CDPVPRESETMAX] =
  97. {
  98. // pitch pstart spinup spindwn volrun volstrt fadein fadeout lfotype lforate modptch modvol cspnup
  99. {1, 255, 75, 95, 95, 10, 1, 50, 95, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  100. {2, 255, 85, 70, 88, 10, 1, 20, 88, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  101. {3, 255, 100, 50, 75, 10, 1, 10, 75, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  102. {4, 100, 100, 0, 0, 10, 1, 90, 90, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  103. {5, 100, 100, 0, 0, 10, 1, 80, 80, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  104. {6, 100, 100, 0, 0, 10, 1, 50, 70, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  105. {7, 100, 100, 0, 0, 5, 1, 40, 50, 1, 50, 0, 10, 0, 0,0,0,0,0,0,0,0,0,0},
  106. {8, 100, 100, 0, 0, 5, 1, 40, 50, 1, 150, 0, 10, 0, 0,0,0,0,0,0,0,0,0,0},
  107. {9, 100, 100, 0, 0, 5, 1, 40, 50, 1, 750, 0, 10, 0, 0,0,0,0,0,0,0,0,0,0},
  108. {10,128, 100, 50, 75, 10, 1, 30, 40, 2, 8, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  109. {11,128, 100, 50, 75, 10, 1, 30, 40, 2, 25, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  110. {12,128, 100, 50, 75, 10, 1, 30, 40, 2, 70, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  111. {13,50, 50, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  112. {14,70, 70, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  113. {15,90, 90, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  114. {16,120, 120, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  115. {17,180, 180, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  116. {18,255, 255, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  117. {19,200, 75, 90, 90, 10, 1, 50, 90, 2, 100, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  118. {20,255, 75, 97, 90, 10, 1, 50, 90, 1, 40, 50, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  119. {21,100, 100, 0, 0, 10, 1, 30, 50, 3, 15, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  120. {22,160, 160, 0, 0, 10, 1, 50, 50, 3, 500, 25, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  121. {23,255, 75, 88, 0, 10, 1, 40, 0, 0, 0, 0, 0, 5, 0,0,0,0,0,0,0,0,0,0},
  122. {24,200, 20, 95, 70, 10, 1, 70, 70, 3, 20, 50, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  123. {25,180, 100, 50, 60, 10, 1, 40, 60, 2, 90, 100, 100, 0, 0,0,0,0,0,0,0,0,0,0},
  124. {26,60, 60, 0, 0, 10, 1, 40, 70, 3, 80, 20, 50, 0, 0,0,0,0,0,0,0,0,0,0},
  125. {27,128, 90, 10, 10, 10, 1, 20, 40, 1, 5, 10, 20, 0, 0,0,0,0,0,0,0,0,0,0}
  126. };
  127. class CAmbientGeneric : public CPointEntity
  128. {
  129. public:
  130. DECLARE_CLASS( CAmbientGeneric, CPointEntity );
  131. CAmbientGeneric();
  132. bool KeyValue( const char *szKeyName, const char *szValue );
  133. void Spawn( void );
  134. void Precache( void );
  135. void Activate( void );
  136. void RampThink( void );
  137. void InitModulationParms(void);
  138. void ComputeMaxAudibleDistance( );
  139. // Rules about which entities need to transmit along with me
  140. virtual void SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways );
  141. virtual void UpdateOnRemove( void );
  142. void ToggleSound();
  143. void SendSound( SoundFlags_t flags );
  144. // Input handlers
  145. void InputPlaySound( inputdata_t &inputdata );
  146. void InputStopSound( inputdata_t &inputdata );
  147. void InputToggleSound( inputdata_t &inputdata );
  148. void InputPitch( inputdata_t &inputdata );
  149. void InputVolume( inputdata_t &inputdata );
  150. void InputFadeIn( inputdata_t &inputdata );
  151. void InputFadeOut( inputdata_t &inputdata );
  152. DECLARE_DATADESC();
  153. float m_radius;
  154. float m_flMaxRadius;
  155. soundlevel_t m_iSoundLevel; // dB value
  156. dynpitchvol_t m_dpv;
  157. bool m_fActive; // only true when the entity is playing a looping sound
  158. bool m_fLooping; // true when the sound played will loop
  159. char m_szSoundFile[MAX_PATH]; // Path/filename of WAV or MP3 file to play.
  160. string_t m_sSourceEntName;
  161. EHANDLE m_hSoundSource; // entity from which the sound comes
  162. int m_nSoundSourceEntIndex; // In case the entity goes away before we finish stopping the sound...
  163. private:
  164. void ValidateSoundFile( void );
  165. string_t m_iszSound;
  166. string_t m_iszPrevSound; // track if the sound has changed and we need to re-Validate
  167. };
  168. LINK_ENTITY_TO_CLASS( ambient_generic, CAmbientGeneric );
  169. BEGIN_DATADESC( CAmbientGeneric )
  170. DEFINE_KEYFIELD( m_iszSound, FIELD_SOUNDNAME, "message" ),
  171. DEFINE_KEYFIELD( m_radius, FIELD_FLOAT, "radius" ),
  172. DEFINE_KEYFIELD( m_sSourceEntName, FIELD_STRING, "SourceEntityName" ),
  173. // recomputed in Activate()
  174. // DEFINE_FIELD( m_hSoundSource, EHANDLE ),
  175. // DEFINE_FIELD( m_nSoundSourceEntIndex, FIELD_INTERGER ),
  176. DEFINE_FIELD( m_flMaxRadius, FIELD_FLOAT ),
  177. DEFINE_FIELD( m_fActive, FIELD_BOOLEAN ),
  178. DEFINE_FIELD( m_fLooping, FIELD_BOOLEAN ),
  179. DEFINE_FIELD( m_iSoundLevel, FIELD_INTEGER ),
  180. // HACKHACK - This is not really in the spirit of the save/restore design, but save this
  181. // out as a binary data block. If the dynpitchvol_t is changed, old saved games will NOT
  182. // load these correctly, so bump the save/restore version if you change the size of the struct
  183. // The right way to do this is to split the input parms (read in keyvalue) into members and re-init this
  184. // struct in Precache(), but it's unlikely that the struct will change, so it's not worth the time right now.
  185. DEFINE_ARRAY( m_dpv, FIELD_CHARACTER, sizeof(dynpitchvol_t) ),
  186. // Function Pointers
  187. DEFINE_FUNCTION( RampThink ),
  188. // Inputs
  189. DEFINE_INPUTFUNC(FIELD_VOID, "PlaySound", InputPlaySound ),
  190. DEFINE_INPUTFUNC(FIELD_VOID, "StopSound", InputStopSound ),
  191. DEFINE_INPUTFUNC(FIELD_VOID, "ToggleSound", InputToggleSound ),
  192. DEFINE_INPUTFUNC(FIELD_FLOAT, "Pitch", InputPitch ),
  193. DEFINE_INPUTFUNC(FIELD_FLOAT, "Volume", InputVolume ),
  194. DEFINE_INPUTFUNC(FIELD_FLOAT, "FadeIn", InputFadeIn ),
  195. DEFINE_INPUTFUNC(FIELD_FLOAT, "FadeOut", InputFadeOut ),
  196. END_DATADESC()
  197. #define SF_AMBIENT_SOUND_EVERYWHERE 1
  198. #define SF_AMBIENT_SOUND_START_SILENT 16
  199. #define SF_AMBIENT_SOUND_NOT_LOOPING 32
  200. //-----------------------------------------------------------------------------
  201. //
  202. //-----------------------------------------------------------------------------
  203. CAmbientGeneric::CAmbientGeneric()
  204. {
  205. m_szSoundFile[0] = 0;
  206. m_iszSound = NULL_STRING;
  207. m_iszPrevSound = NULL_STRING;
  208. }
  209. //-----------------------------------------------------------------------------
  210. // Spawn
  211. //-----------------------------------------------------------------------------
  212. void CAmbientGeneric::Spawn( void )
  213. {
  214. m_iSoundLevel = ComputeSoundlevel( m_radius, FBitSet( m_spawnflags, SF_AMBIENT_SOUND_EVERYWHERE )?true:false );
  215. ComputeMaxAudibleDistance( );
  216. ValidateSoundFile();
  217. if ( V_strlen( m_szSoundFile ) < 1 )
  218. {
  219. Warning( "Empty %s (%s) at %.2f, %.2f, %.2f\n", GetClassname(), GetDebugName(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z );
  220. UTIL_Remove(this);
  221. return;
  222. }
  223. SetSolid( SOLID_NONE );
  224. SetMoveType( MOVETYPE_NONE );
  225. // Set up think function for dynamic modification
  226. // of ambient sound's pitch or volume. Don't
  227. // start thinking yet.
  228. SetThink(&CAmbientGeneric::RampThink);
  229. SetNextThink( TICK_NEVER_THINK );
  230. m_fActive = false;
  231. if ( FBitSet ( m_spawnflags, SF_AMBIENT_SOUND_NOT_LOOPING ) )
  232. {
  233. m_fLooping = false;
  234. }
  235. else
  236. {
  237. m_fLooping = true;
  238. }
  239. m_hSoundSource = NULL;
  240. m_nSoundSourceEntIndex = -1;
  241. Precache( );
  242. // init all dynamic modulation parms
  243. InitModulationParms();
  244. }
  245. //-----------------------------------------------------------------------------
  246. // Computes the max audible radius for a given sound level
  247. //-----------------------------------------------------------------------------
  248. #define MIN_AUDIBLE_VOLUME 1.01e-3
  249. void CAmbientGeneric::ComputeMaxAudibleDistance( )
  250. {
  251. if (( m_iSoundLevel == SNDLVL_NONE ) || ( m_radius == 0.0f ))
  252. {
  253. m_flMaxRadius = -1.0f;
  254. return;
  255. }
  256. // Sadly, there's no direct way of getting at this.
  257. // We have to do an interative computation.
  258. float flGain = enginesound->GetDistGainFromSoundLevel( m_iSoundLevel, m_radius );
  259. if ( flGain <= MIN_AUDIBLE_VOLUME )
  260. {
  261. m_flMaxRadius = m_radius;
  262. return;
  263. }
  264. float flMinRadius = m_radius;
  265. float flMaxRadius = m_radius * 2;
  266. while ( true )
  267. {
  268. // First, find a min + max range surrounding the desired distance gain
  269. float flGain = enginesound->GetDistGainFromSoundLevel( m_iSoundLevel, flMaxRadius );
  270. if ( flGain <= MIN_AUDIBLE_VOLUME )
  271. break;
  272. // Always audible.
  273. if ( flMaxRadius > 1e5 )
  274. {
  275. m_flMaxRadius = -1.0f;
  276. return;
  277. }
  278. flMinRadius = flMaxRadius;
  279. flMaxRadius *= 2.0f;
  280. }
  281. // Now home in a little bit
  282. int nInterations = 4;
  283. while ( --nInterations >= 0 )
  284. {
  285. float flTestRadius = (flMinRadius + flMaxRadius) * 0.5f;
  286. float flGain = enginesound->GetDistGainFromSoundLevel( m_iSoundLevel, flTestRadius );
  287. if ( flGain <= MIN_AUDIBLE_VOLUME )
  288. {
  289. flMaxRadius = flTestRadius;
  290. }
  291. else
  292. {
  293. flMinRadius = flTestRadius;
  294. }
  295. }
  296. m_flMaxRadius = flMaxRadius;
  297. }
  298. //-----------------------------------------------------------------------------
  299. // Purpose: Input handler for changing pitch.
  300. // Input : Float new pitch from 0 - 255 (100 = as recorded).
  301. //-----------------------------------------------------------------------------
  302. void CAmbientGeneric::InputPitch( inputdata_t &inputdata )
  303. {
  304. m_dpv.pitch = clamp( FastFloatToSmallInt( inputdata.value.Float() ), 0, 255 );
  305. SendSound( SND_CHANGE_PITCH );
  306. }
  307. //-----------------------------------------------------------------------------
  308. // Purpose: Input handler for changing volume.
  309. // Input : Float new volume, from 0 - 10.
  310. //-----------------------------------------------------------------------------
  311. void CAmbientGeneric::InputVolume( inputdata_t &inputdata )
  312. {
  313. //
  314. // Multiply the input value by ten since volumes are expected to be from 0 - 100.
  315. //
  316. m_dpv.vol = clamp( RoundFloatToInt( inputdata.value.Float() * 10.f ), 0, 100 );
  317. m_dpv.volfrac = m_dpv.vol << 8;
  318. SendSound( SND_CHANGE_VOL );
  319. }
  320. //-----------------------------------------------------------------------------
  321. // Purpose: Input handler for fading in volume over time.
  322. // Input : Float volume fade in time 0 - 100 seconds
  323. //-----------------------------------------------------------------------------
  324. void CAmbientGeneric::InputFadeIn( inputdata_t &inputdata )
  325. {
  326. // cancel any fade out that might be happening
  327. m_dpv.fadeout = 0;
  328. m_dpv.fadein = inputdata.value.Float();
  329. if (m_dpv.fadein > 100) m_dpv.fadein = 100;
  330. if (m_dpv.fadein < 0) m_dpv.fadein = 0;
  331. if (m_dpv.fadein > 0)
  332. m_dpv.fadein = ( 100 << 8 ) / ( m_dpv.fadein * AMBIENT_GENERIC_UPDATE_RATE );
  333. SetNextThink( gpGlobals->curtime + 0.1f );
  334. }
  335. //-----------------------------------------------------------------------------
  336. // Purpose: Input handler for fading out volume over time.
  337. // Input : Float volume fade out time 0 - 100 seconds
  338. //-----------------------------------------------------------------------------
  339. void CAmbientGeneric::InputFadeOut( inputdata_t &inputdata )
  340. {
  341. // cancel any fade in that might be happening
  342. m_dpv.fadein = 0;
  343. m_dpv.fadeout = inputdata.value.Float();
  344. if (m_dpv.fadeout > 100) m_dpv.fadeout = 100;
  345. if (m_dpv.fadeout < 0) m_dpv.fadeout = 0;
  346. if (m_dpv.fadeout > 0)
  347. m_dpv.fadeout = ( 100 << 8 ) / ( m_dpv.fadeout * AMBIENT_GENERIC_UPDATE_RATE );
  348. SetNextThink( gpGlobals->curtime + 0.1f );
  349. }
  350. void CAmbientGeneric::ValidateSoundFile( void )
  351. {
  352. if ( m_iszSound == NULL_STRING )
  353. {
  354. m_iszPrevSound = NULL_STRING;
  355. m_szSoundFile[0] = 0;
  356. return;
  357. }
  358. if ( m_iszSound == m_iszPrevSound )
  359. return;
  360. m_iszPrevSound = m_iszSound;
  361. char *szSoundFile = (char *)STRING( m_iszSound );
  362. if ( V_strlen( szSoundFile ) < 1 )
  363. return;
  364. char szPathTest[MAX_PATH] = { 0 };
  365. char szNewSoundFile[MAX_PATH] = { 0 };
  366. // try to fix some legacy ambient generic entities that still use .wav file references for vo sounds instead of .mp3
  367. if ( V_strncmp( szSoundFile, "vo", 2 ) == 0 )
  368. {
  369. const char *pszExt = V_GetFileExtension( szSoundFile );
  370. if ( pszExt && pszExt[0] && FStrEq( pszExt, "wav" ) )
  371. {
  372. V_sprintf_safe( szPathTest, "sound/%s", szSoundFile );
  373. if ( !g_pFullFileSystem->FileExists( szPathTest ) )
  374. {
  375. V_strcpy_safe( szNewSoundFile, szSoundFile );
  376. V_SetExtension( szNewSoundFile, ".mp3", ARRAYSIZE( szNewSoundFile ) );
  377. V_sprintf_safe( szPathTest, "sound/%s", szNewSoundFile );
  378. if ( g_pFullFileSystem->FileExists( szPathTest ) )
  379. {
  380. szSoundFile = szNewSoundFile;
  381. }
  382. }
  383. }
  384. }
  385. V_strcpy_safe( m_szSoundFile, szSoundFile );
  386. }
  387. void CAmbientGeneric::Precache( void )
  388. {
  389. if ( V_strlen( m_szSoundFile ) > 1 )
  390. {
  391. if ( m_szSoundFile[0] != '!' )
  392. {
  393. PrecacheScriptSound( m_szSoundFile );
  394. }
  395. }
  396. if ( !FBitSet (m_spawnflags, SF_AMBIENT_SOUND_START_SILENT ) )
  397. {
  398. // start the sound ASAP
  399. if (m_fLooping)
  400. m_fActive = true;
  401. }
  402. }
  403. //------------------------------------------------------------------------------
  404. // Purpose:
  405. //------------------------------------------------------------------------------
  406. void CAmbientGeneric::Activate( void )
  407. {
  408. BaseClass::Activate();
  409. // Initialize sound source. If no source was given, or source can't be found
  410. // then this is the source
  411. if (m_hSoundSource == NULL)
  412. {
  413. if (m_sSourceEntName != NULL_STRING)
  414. {
  415. m_hSoundSource = gEntList.FindEntityByName( NULL, m_sSourceEntName );
  416. if ( m_hSoundSource != NULL )
  417. {
  418. m_nSoundSourceEntIndex = m_hSoundSource->entindex();
  419. }
  420. }
  421. if (m_hSoundSource == NULL)
  422. {
  423. m_hSoundSource = this;
  424. m_nSoundSourceEntIndex = entindex();
  425. }
  426. else
  427. {
  428. if ( !FBitSet( m_spawnflags, SF_AMBIENT_SOUND_EVERYWHERE ) )
  429. {
  430. AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
  431. }
  432. }
  433. }
  434. #ifdef PORTAL
  435. // This is the only way we can silence the radio sound from the first room without touching them map -- jdw
  436. if ( PortalGameRules() && PortalGameRules()->ShouldRemoveRadio() )
  437. {
  438. if ( V_strcmp( STRING( gpGlobals->mapname ), "testchmb_a_00" ) == 0 ||
  439. V_strcmp( STRING( gpGlobals->mapname ), "testchmb_a_11" ) == 0 ||
  440. V_strcmp( STRING( gpGlobals->mapname ), "testchmb_a_14" ) == 0 )
  441. {
  442. if ( V_strcmp( STRING( GetEntityName() ), "radio_sound" ) == 0 )
  443. {
  444. UTIL_Remove( this );
  445. return;
  446. }
  447. }
  448. }
  449. #endif // PORTAL
  450. // If active start the sound
  451. if ( m_fActive )
  452. {
  453. int flags = SND_SPAWNING;
  454. // If we are loading a saved game, we can't write into the init/signon buffer here, so just issue
  455. // as a regular sound message...
  456. if ( gpGlobals->eLoadType == MapLoad_Transition ||
  457. gpGlobals->eLoadType == MapLoad_LoadGame ||
  458. g_pGameRules->InRoundRestart() )
  459. {
  460. flags = SND_NOFLAGS;
  461. }
  462. // Tracker 76119: 8/12/07 ywb:
  463. // Make sure pitch and volume are set up to the correct value (especially after restoring a .sav file)
  464. flags |= ( SND_CHANGE_PITCH | SND_CHANGE_VOL );
  465. // Don't bother sending over to client if volume is zero, though
  466. if ( m_dpv.vol > 0 )
  467. {
  468. SendSound( (SoundFlags_t)flags );
  469. }
  470. SetNextThink( gpGlobals->curtime + 0.1f );
  471. }
  472. }
  473. //-----------------------------------------------------------------------------
  474. // Rules about which entities need to transmit along with me
  475. //-----------------------------------------------------------------------------
  476. void CAmbientGeneric::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
  477. {
  478. // Ambient generics never transmit; this is just a way for us to ensure
  479. // the sound source gets transmitted; that's why we don't call pInfo->m_pTransmitEdict->Set
  480. if ( !m_hSoundSource || m_hSoundSource == this || !m_fActive )
  481. return;
  482. // Don't bother sending the position of the source if we have to play everywhere
  483. if ( FBitSet( m_spawnflags, SF_AMBIENT_SOUND_EVERYWHERE ) )
  484. return;
  485. Assert( pInfo->m_pClientEnt );
  486. CBaseEntity *pClient = (CBaseEntity*)(pInfo->m_pClientEnt->GetUnknown());
  487. if ( !pClient )
  488. return;
  489. // Send the sound source if he's close enough
  490. if ( ( m_flMaxRadius < 0 ) || ( pClient->GetAbsOrigin().DistToSqr( m_hSoundSource->GetAbsOrigin() ) <= m_flMaxRadius * m_flMaxRadius ) )
  491. {
  492. m_hSoundSource->SetTransmit( pInfo, false );
  493. }
  494. }
  495. //-----------------------------------------------------------------------------
  496. // Purpose:
  497. //-----------------------------------------------------------------------------
  498. void CAmbientGeneric::UpdateOnRemove( void )
  499. {
  500. if ( m_fActive )
  501. {
  502. // Stop the sound we're generating
  503. SendSound( SND_STOP );
  504. }
  505. BaseClass::UpdateOnRemove();
  506. }
  507. //-----------------------------------------------------------------------------
  508. // Purpose: Think at 5hz if we are dynamically modifying pitch or volume of the
  509. // playing sound. This function will ramp pitch and/or volume up or
  510. // down, modify pitch/volume with lfo if active.
  511. //-----------------------------------------------------------------------------
  512. void CAmbientGeneric::RampThink( void )
  513. {
  514. int pitch = m_dpv.pitch;
  515. int vol = m_dpv.vol;
  516. int flags = 0;
  517. int fChanged = 0; // false if pitch and vol remain unchanged this round
  518. int prev;
  519. if (!m_dpv.spinup && !m_dpv.spindown && !m_dpv.fadein && !m_dpv.fadeout && !m_dpv.lfotype)
  520. return; // no ramps or lfo, stop thinking
  521. // ==============
  522. // pitch envelope
  523. // ==============
  524. if (m_dpv.spinup || m_dpv.spindown)
  525. {
  526. prev = m_dpv.pitchfrac >> 8;
  527. if (m_dpv.spinup > 0)
  528. m_dpv.pitchfrac += m_dpv.spinup;
  529. else if (m_dpv.spindown > 0)
  530. m_dpv.pitchfrac -= m_dpv.spindown;
  531. pitch = m_dpv.pitchfrac >> 8;
  532. if (pitch > m_dpv.pitchrun)
  533. {
  534. pitch = m_dpv.pitchrun;
  535. m_dpv.spinup = 0; // done with ramp up
  536. }
  537. if (pitch < m_dpv.pitchstart)
  538. {
  539. pitch = m_dpv.pitchstart;
  540. m_dpv.spindown = 0; // done with ramp down
  541. // shut sound off
  542. SendSound( SND_STOP );
  543. // return without setting m_flNextThink
  544. return;
  545. }
  546. if (pitch > 255) pitch = 255;
  547. if (pitch < 1) pitch = 1;
  548. m_dpv.pitch = pitch;
  549. fChanged |= (prev != pitch);
  550. flags |= SND_CHANGE_PITCH;
  551. }
  552. // ==================
  553. // amplitude envelope
  554. // ==================
  555. if (m_dpv.fadein || m_dpv.fadeout)
  556. {
  557. prev = m_dpv.volfrac >> 8;
  558. if (m_dpv.fadein > 0)
  559. m_dpv.volfrac += m_dpv.fadein;
  560. else if (m_dpv.fadeout > 0)
  561. m_dpv.volfrac -= m_dpv.fadeout;
  562. vol = m_dpv.volfrac >> 8;
  563. if (vol > m_dpv.volrun)
  564. {
  565. vol = m_dpv.volrun;
  566. m_dpv.volfrac = vol << 8;
  567. m_dpv.fadein = 0; // done with ramp up
  568. }
  569. if (vol < m_dpv.volstart)
  570. {
  571. vol = m_dpv.volstart;
  572. m_dpv.vol = vol;
  573. m_dpv.volfrac = vol << 8;
  574. m_dpv.fadeout = 0; // done with ramp down
  575. // shut sound off
  576. SendSound( SND_STOP );
  577. // return without setting m_flNextThink
  578. return;
  579. }
  580. if (vol > 100)
  581. {
  582. vol = 100;
  583. m_dpv.volfrac = vol << 8;
  584. }
  585. if (vol < 1)
  586. {
  587. vol = 1;
  588. m_dpv.volfrac = vol << 8;
  589. }
  590. m_dpv.vol = vol;
  591. fChanged |= (prev != vol);
  592. flags |= SND_CHANGE_VOL;
  593. }
  594. // ===================
  595. // pitch/amplitude LFO
  596. // ===================
  597. if (m_dpv.lfotype)
  598. {
  599. int pos;
  600. if (m_dpv.lfofrac > 0x6fffffff)
  601. m_dpv.lfofrac = 0;
  602. // update lfo, lfofrac/255 makes a triangle wave 0-255
  603. m_dpv.lfofrac += m_dpv.lforate;
  604. pos = m_dpv.lfofrac >> 8;
  605. if (m_dpv.lfofrac < 0)
  606. {
  607. m_dpv.lfofrac = 0;
  608. m_dpv.lforate = abs(m_dpv.lforate);
  609. pos = 0;
  610. }
  611. else if (pos > 255)
  612. {
  613. pos = 255;
  614. m_dpv.lfofrac = (255 << 8);
  615. m_dpv.lforate = -abs(m_dpv.lforate);
  616. }
  617. switch(m_dpv.lfotype)
  618. {
  619. case LFO_SQUARE:
  620. if (pos < 128)
  621. m_dpv.lfomult = 255;
  622. else
  623. m_dpv.lfomult = 0;
  624. break;
  625. case LFO_RANDOM:
  626. if (pos == 255)
  627. m_dpv.lfomult = random->RandomInt(0, 255);
  628. break;
  629. case LFO_TRIANGLE:
  630. default:
  631. m_dpv.lfomult = pos;
  632. break;
  633. }
  634. if (m_dpv.lfomodpitch)
  635. {
  636. prev = pitch;
  637. // pitch 0-255
  638. pitch += ((m_dpv.lfomult - 128) * m_dpv.lfomodpitch) / 100;
  639. if (pitch > 255) pitch = 255;
  640. if (pitch < 1) pitch = 1;
  641. fChanged |= (prev != pitch);
  642. flags |= SND_CHANGE_PITCH;
  643. }
  644. if (m_dpv.lfomodvol)
  645. {
  646. // vol 0-100
  647. prev = vol;
  648. vol += ((m_dpv.lfomult - 128) * m_dpv.lfomodvol) / 100;
  649. if (vol > 100) vol = 100;
  650. if (vol < 0) vol = 0;
  651. fChanged |= (prev != vol);
  652. flags |= SND_CHANGE_VOL;
  653. }
  654. }
  655. // Send update to playing sound only if we actually changed
  656. // pitch or volume in this routine.
  657. if (flags && fChanged)
  658. {
  659. if (pitch == PITCH_NORM)
  660. pitch = PITCH_NORM + 1; // don't send 'no pitch' !
  661. CBaseEntity* pSoundSource = m_hSoundSource;
  662. if (pSoundSource)
  663. {
  664. ValidateSoundFile();
  665. UTIL_EmitAmbientSound(pSoundSource->GetSoundSourceIndex(), pSoundSource->GetAbsOrigin(),
  666. m_szSoundFile, ( vol * 0.01 ), m_iSoundLevel, flags, pitch );
  667. }
  668. }
  669. // update ramps at 5hz
  670. SetNextThink( gpGlobals->curtime + AMBIENT_GENERIC_THINK_DELAY );
  671. return;
  672. }
  673. //-----------------------------------------------------------------------------
  674. // Purpose: Init all ramp params in preparation to play a new sound.
  675. //-----------------------------------------------------------------------------
  676. void CAmbientGeneric::InitModulationParms(void)
  677. {
  678. int pitchinc;
  679. m_dpv.volrun = m_iHealth * 10; // 0 - 100
  680. if (m_dpv.volrun > 100) m_dpv.volrun = 100;
  681. if (m_dpv.volrun < 0) m_dpv.volrun = 0;
  682. // get presets
  683. if (m_dpv.preset != 0 && m_dpv.preset <= CDPVPRESETMAX)
  684. {
  685. // load preset values
  686. m_dpv = rgdpvpreset[m_dpv.preset - 1];
  687. // fixup preset values, just like
  688. // fixups in KeyValue routine.
  689. if (m_dpv.spindown > 0)
  690. m_dpv.spindown = (101 - m_dpv.spindown) * 64;
  691. if (m_dpv.spinup > 0)
  692. m_dpv.spinup = (101 - m_dpv.spinup) * 64;
  693. m_dpv.volstart *= 10;
  694. m_dpv.volrun *= 10;
  695. if (m_dpv.fadein > 0)
  696. m_dpv.fadein = (101 - m_dpv.fadein) * 64;
  697. if (m_dpv.fadeout > 0)
  698. m_dpv.fadeout = (101 - m_dpv.fadeout) * 64;
  699. m_dpv.lforate *= 256;
  700. m_dpv.fadeinsav = m_dpv.fadein;
  701. m_dpv.fadeoutsav = m_dpv.fadeout;
  702. m_dpv.spinupsav = m_dpv.spinup;
  703. m_dpv.spindownsav = m_dpv.spindown;
  704. }
  705. m_dpv.fadein = m_dpv.fadeinsav;
  706. m_dpv.fadeout = 0;
  707. if (m_dpv.fadein)
  708. m_dpv.vol = m_dpv.volstart;
  709. else
  710. m_dpv.vol = m_dpv.volrun;
  711. m_dpv.spinup = m_dpv.spinupsav;
  712. m_dpv.spindown = 0;
  713. if (m_dpv.spinup)
  714. m_dpv.pitch = m_dpv.pitchstart;
  715. else
  716. m_dpv.pitch = m_dpv.pitchrun;
  717. if (m_dpv.pitch == 0)
  718. m_dpv.pitch = PITCH_NORM;
  719. m_dpv.pitchfrac = m_dpv.pitch << 8;
  720. m_dpv.volfrac = m_dpv.vol << 8;
  721. m_dpv.lfofrac = 0;
  722. m_dpv.lforate = abs(m_dpv.lforate);
  723. m_dpv.cspincount = 1;
  724. if (m_dpv.cspinup)
  725. {
  726. pitchinc = (255 - m_dpv.pitchstart) / m_dpv.cspinup;
  727. m_dpv.pitchrun = m_dpv.pitchstart + pitchinc;
  728. if (m_dpv.pitchrun > 255) m_dpv.pitchrun = 255;
  729. }
  730. if ((m_dpv.spinupsav || m_dpv.spindownsav || (m_dpv.lfotype && m_dpv.lfomodpitch))
  731. && (m_dpv.pitch == PITCH_NORM))
  732. m_dpv.pitch = PITCH_NORM + 1; // must never send 'no pitch' as first pitch
  733. // if we intend to pitch shift later!
  734. }
  735. //-----------------------------------------------------------------------------
  736. // Purpose: Input handler that begins playing the sound.
  737. //-----------------------------------------------------------------------------
  738. void CAmbientGeneric::InputPlaySound( inputdata_t &inputdata )
  739. {
  740. if (!m_fActive)
  741. {
  742. //Adrian: Stop our current sound before starting a new one!
  743. SendSound( SND_STOP );
  744. ToggleSound();
  745. }
  746. }
  747. //-----------------------------------------------------------------------------
  748. // Purpose: Input handler that stops playing the sound.
  749. //-----------------------------------------------------------------------------
  750. void CAmbientGeneric::InputStopSound( inputdata_t &inputdata )
  751. {
  752. if (m_fActive)
  753. {
  754. ToggleSound();
  755. }
  756. }
  757. void CAmbientGeneric::SendSound( SoundFlags_t flags)
  758. {
  759. ValidateSoundFile();
  760. CBaseEntity* pSoundSource = m_hSoundSource;
  761. if ( pSoundSource )
  762. {
  763. if ( flags == SND_STOP )
  764. {
  765. UTIL_EmitAmbientSound( pSoundSource->GetSoundSourceIndex(), pSoundSource->GetAbsOrigin(), m_szSoundFile,
  766. 0, SNDLVL_NONE, flags, 0);
  767. }
  768. else
  769. {
  770. UTIL_EmitAmbientSound( pSoundSource->GetSoundSourceIndex(), pSoundSource->GetAbsOrigin(), m_szSoundFile,
  771. (m_dpv.vol * 0.01), m_iSoundLevel, flags, m_dpv.pitch);
  772. }
  773. }
  774. else
  775. {
  776. if ( ( flags == SND_STOP ) &&
  777. ( m_nSoundSourceEntIndex != -1 ) )
  778. {
  779. UTIL_EmitAmbientSound( m_nSoundSourceEntIndex, GetAbsOrigin(), m_szSoundFile,
  780. 0, SNDLVL_NONE, flags, 0);
  781. }
  782. }
  783. }
  784. //-----------------------------------------------------------------------------
  785. // Purpose: Input handler that stops playing the sound.
  786. //-----------------------------------------------------------------------------
  787. void CAmbientGeneric::InputToggleSound( inputdata_t &inputdata )
  788. {
  789. ToggleSound();
  790. }
  791. //-----------------------------------------------------------------------------
  792. // Purpose: Turns an ambient sound on or off. If the ambient is a looping sound,
  793. // mark sound as active (m_fActive) if it's playing, innactive if not.
  794. // If the sound is not a looping sound, never mark it as active.
  795. // Input : pActivator -
  796. // pCaller -
  797. // useType -
  798. // value -
  799. //-----------------------------------------------------------------------------
  800. void CAmbientGeneric::ToggleSound()
  801. {
  802. // m_fActive is true only if a looping sound is playing.
  803. if ( m_fActive )
  804. {// turn sound off
  805. if (m_dpv.cspinup)
  806. {
  807. // Don't actually shut off. Each toggle causes
  808. // incremental spinup to max pitch
  809. if (m_dpv.cspincount <= m_dpv.cspinup)
  810. {
  811. int pitchinc;
  812. // start a new spinup
  813. m_dpv.cspincount++;
  814. pitchinc = (255 - m_dpv.pitchstart) / m_dpv.cspinup;
  815. m_dpv.spinup = m_dpv.spinupsav;
  816. m_dpv.spindown = 0;
  817. m_dpv.pitchrun = m_dpv.pitchstart + pitchinc * m_dpv.cspincount;
  818. if (m_dpv.pitchrun > 255) m_dpv.pitchrun = 255;
  819. SetNextThink( gpGlobals->curtime + 0.1f );
  820. }
  821. }
  822. else
  823. {
  824. m_fActive = false;
  825. // HACKHACK - this makes the code in Precache() work properly after a save/restore
  826. m_spawnflags |= SF_AMBIENT_SOUND_START_SILENT;
  827. if (m_dpv.spindownsav || m_dpv.fadeoutsav)
  828. {
  829. // spin it down (or fade it) before shutoff if spindown is set
  830. m_dpv.spindown = m_dpv.spindownsav;
  831. m_dpv.spinup = 0;
  832. m_dpv.fadeout = m_dpv.fadeoutsav;
  833. m_dpv.fadein = 0;
  834. SetNextThink( gpGlobals->curtime + 0.1f );
  835. }
  836. else
  837. {
  838. SendSound( SND_STOP ); // stop sound
  839. }
  840. }
  841. }
  842. else
  843. {// turn sound on
  844. // only toggle if this is a looping sound. If not looping, each
  845. // trigger will cause the sound to play. If the sound is still
  846. // playing from a previous trigger press, it will be shut off
  847. // and then restarted.
  848. if (m_fLooping)
  849. m_fActive = true;
  850. else
  851. {
  852. // shut sound off now - may be interrupting a long non-looping sound
  853. SendSound( SND_STOP ); // stop sound
  854. }
  855. // init all ramp params for startup
  856. InitModulationParms();
  857. SendSound( SND_NOFLAGS ); // send sound
  858. SetNextThink( gpGlobals->curtime + 0.1f );
  859. }
  860. }
  861. // KeyValue - load keyvalue pairs into member data of the
  862. // ambient generic. NOTE: called BEFORE spawn!
  863. bool CAmbientGeneric::KeyValue( const char *szKeyName, const char *szValue )
  864. {
  865. // NOTE: changing any of the modifiers in this code
  866. // NOTE: also requires changing InitModulationParms code.
  867. // preset
  868. if (FStrEq(szKeyName, "preset"))
  869. {
  870. m_dpv.preset = atoi(szValue);
  871. }
  872. // pitchrun
  873. else if (FStrEq(szKeyName, "pitch"))
  874. {
  875. m_dpv.pitchrun = atoi(szValue);
  876. if (m_dpv.pitchrun > 255) m_dpv.pitchrun = 255;
  877. if (m_dpv.pitchrun < 0) m_dpv.pitchrun = 0;
  878. }
  879. // pitchstart
  880. else if (FStrEq(szKeyName, "pitchstart"))
  881. {
  882. m_dpv.pitchstart = atoi(szValue);
  883. if (m_dpv.pitchstart > 255) m_dpv.pitchstart = 255;
  884. if (m_dpv.pitchstart < 0) m_dpv.pitchstart = 0;
  885. }
  886. // spinup
  887. else if (FStrEq(szKeyName, "spinup"))
  888. {
  889. m_dpv.spinup = atoi(szValue);
  890. if (m_dpv.spinup > 100) m_dpv.spinup = 100;
  891. if (m_dpv.spinup < 0) m_dpv.spinup = 0;
  892. if (m_dpv.spinup > 0)
  893. m_dpv.spinup = (101 - m_dpv.spinup) * 64;
  894. m_dpv.spinupsav = m_dpv.spinup;
  895. }
  896. // spindown
  897. else if (FStrEq(szKeyName, "spindown"))
  898. {
  899. m_dpv.spindown = atoi(szValue);
  900. if (m_dpv.spindown > 100) m_dpv.spindown = 100;
  901. if (m_dpv.spindown < 0) m_dpv.spindown = 0;
  902. if (m_dpv.spindown > 0)
  903. m_dpv.spindown = (101 - m_dpv.spindown) * 64;
  904. m_dpv.spindownsav = m_dpv.spindown;
  905. }
  906. // volstart
  907. else if (FStrEq(szKeyName, "volstart"))
  908. {
  909. m_dpv.volstart = atoi(szValue);
  910. if (m_dpv.volstart > 10) m_dpv.volstart = 10;
  911. if (m_dpv.volstart < 0) m_dpv.volstart = 0;
  912. m_dpv.volstart *= 10; // 0 - 100
  913. }
  914. // legacy fadein
  915. else if (FStrEq(szKeyName, "fadein"))
  916. {
  917. m_dpv.fadein = atoi(szValue);
  918. if (m_dpv.fadein > 100) m_dpv.fadein = 100;
  919. if (m_dpv.fadein < 0) m_dpv.fadein = 0;
  920. if (m_dpv.fadein > 0)
  921. m_dpv.fadein = (101 - m_dpv.fadein) * 64;
  922. m_dpv.fadeinsav = m_dpv.fadein;
  923. }
  924. // legacy fadeout
  925. else if (FStrEq(szKeyName, "fadeout"))
  926. {
  927. m_dpv.fadeout = atoi(szValue);
  928. if (m_dpv.fadeout > 100) m_dpv.fadeout = 100;
  929. if (m_dpv.fadeout < 0) m_dpv.fadeout = 0;
  930. if (m_dpv.fadeout > 0)
  931. m_dpv.fadeout = (101 - m_dpv.fadeout) * 64;
  932. m_dpv.fadeoutsav = m_dpv.fadeout;
  933. }
  934. // fadeinsecs
  935. else if (FStrEq(szKeyName, "fadeinsecs"))
  936. {
  937. m_dpv.fadein = atoi(szValue);
  938. if (m_dpv.fadein > 100) m_dpv.fadein = 100;
  939. if (m_dpv.fadein < 0) m_dpv.fadein = 0;
  940. if (m_dpv.fadein > 0)
  941. m_dpv.fadein = ( 100 << 8 ) / ( m_dpv.fadein * AMBIENT_GENERIC_UPDATE_RATE );
  942. m_dpv.fadeinsav = m_dpv.fadein;
  943. }
  944. // fadeoutsecs
  945. else if (FStrEq(szKeyName, "fadeoutsecs"))
  946. {
  947. m_dpv.fadeout = atoi(szValue);
  948. if (m_dpv.fadeout > 100) m_dpv.fadeout = 100;
  949. if (m_dpv.fadeout < 0) m_dpv.fadeout = 0;
  950. if (m_dpv.fadeout > 0)
  951. m_dpv.fadeout = ( 100 << 8 ) / ( m_dpv.fadeout * AMBIENT_GENERIC_UPDATE_RATE );
  952. m_dpv.fadeoutsav = m_dpv.fadeout;
  953. }
  954. // lfotype
  955. else if (FStrEq(szKeyName, "lfotype"))
  956. {
  957. m_dpv.lfotype = atoi(szValue);
  958. if (m_dpv.lfotype > 4) m_dpv.lfotype = LFO_TRIANGLE;
  959. }
  960. // lforate
  961. else if (FStrEq(szKeyName, "lforate"))
  962. {
  963. m_dpv.lforate = atoi(szValue);
  964. if (m_dpv.lforate > 1000) m_dpv.lforate = 1000;
  965. if (m_dpv.lforate < 0) m_dpv.lforate = 0;
  966. m_dpv.lforate *= 256;
  967. }
  968. // lfomodpitch
  969. else if (FStrEq(szKeyName, "lfomodpitch"))
  970. {
  971. m_dpv.lfomodpitch = atoi(szValue);
  972. if (m_dpv.lfomodpitch > 100) m_dpv.lfomodpitch = 100;
  973. if (m_dpv.lfomodpitch < 0) m_dpv.lfomodpitch = 0;
  974. }
  975. // lfomodvol
  976. else if (FStrEq(szKeyName, "lfomodvol"))
  977. {
  978. m_dpv.lfomodvol = atoi(szValue);
  979. if (m_dpv.lfomodvol > 100) m_dpv.lfomodvol = 100;
  980. if (m_dpv.lfomodvol < 0) m_dpv.lfomodvol = 0;
  981. }
  982. // cspinup
  983. else if (FStrEq(szKeyName, "cspinup"))
  984. {
  985. m_dpv.cspinup = atoi(szValue);
  986. if (m_dpv.cspinup > 100) m_dpv.cspinup = 100;
  987. if (m_dpv.cspinup < 0) m_dpv.cspinup = 0;
  988. }
  989. else
  990. return BaseClass::KeyValue( szKeyName, szValue );
  991. return true;
  992. }
  993. // =================== ROOM SOUND FX ==========================================
  994. // ==================== SENTENCE GROUPS, UTILITY FUNCTIONS ======================================
  995. int fSentencesInit = false;
  996. // ===================== SENTENCE GROUPS, MAIN ROUTINES ========================
  997. // given sentence group index, play random sentence for given entity.
  998. // returns sentenceIndex - which sentence was picked
  999. // Ipick is only needed if you plan on stopping the sound before playback is done (see SENTENCEG_Stop).
  1000. // sentenceIndex can be used to find the name/length of the sentence
  1001. int SENTENCEG_PlayRndI(edict_t *entity, int isentenceg,
  1002. float volume, soundlevel_t soundlevel, int flags, int pitch)
  1003. {
  1004. char name[64];
  1005. int ipick;
  1006. if (!fSentencesInit)
  1007. return -1;
  1008. name[0] = 0;
  1009. ipick = engine->SentenceGroupPick( isentenceg, name, sizeof( name ) );
  1010. if ( ( ipick > 0 ) && name[0] )
  1011. {
  1012. int sentenceIndex = SENTENCEG_Lookup( name );
  1013. CPASAttenuationFilter filter( GetContainingEntity( entity ), soundlevel );
  1014. CBaseEntity::EmitSentenceByIndex( filter, ENTINDEX(entity), CHAN_VOICE, sentenceIndex, volume, soundlevel, flags, pitch );
  1015. return sentenceIndex;
  1016. }
  1017. return -1;
  1018. }
  1019. //-----------------------------------------------------------------------------
  1020. // Picks a sentence, but doesn't play it
  1021. //-----------------------------------------------------------------------------
  1022. int SENTENCEG_PickRndSz(const char *szgroupname)
  1023. {
  1024. char name[64];
  1025. int ipick;
  1026. int isentenceg;
  1027. if (!fSentencesInit)
  1028. return -1;
  1029. name[0] = 0;
  1030. isentenceg = engine->SentenceGroupIndexFromName(szgroupname);
  1031. if (isentenceg < 0)
  1032. {
  1033. Warning( "No such sentence group %s\n", szgroupname );
  1034. return -1;
  1035. }
  1036. ipick = engine->SentenceGroupPick(isentenceg, name, sizeof( name ));
  1037. if (ipick >= 0 && name[0])
  1038. {
  1039. return SENTENCEG_Lookup( name );
  1040. }
  1041. return -1;
  1042. }
  1043. //-----------------------------------------------------------------------------
  1044. // Plays a sentence by sentence index
  1045. //-----------------------------------------------------------------------------
  1046. void SENTENCEG_PlaySentenceIndex( edict_t *entity, int iSentenceIndex, float volume, soundlevel_t soundlevel, int flags, int pitch )
  1047. {
  1048. if ( iSentenceIndex >= 0 )
  1049. {
  1050. CPASAttenuationFilter filter( GetContainingEntity( entity ), soundlevel );
  1051. CBaseEntity::EmitSentenceByIndex( filter, ENTINDEX(entity), CHAN_VOICE, iSentenceIndex, volume, soundlevel, flags, pitch );
  1052. }
  1053. }
  1054. int SENTENCEG_PlayRndSz(edict_t *entity, const char *szgroupname,
  1055. float volume, soundlevel_t soundlevel, int flags, int pitch)
  1056. {
  1057. char name[64];
  1058. int ipick;
  1059. int isentenceg;
  1060. if (!fSentencesInit)
  1061. return -1;
  1062. name[0] = 0;
  1063. isentenceg = engine->SentenceGroupIndexFromName(szgroupname);
  1064. if (isentenceg < 0)
  1065. {
  1066. Warning( "No such sentence group %s\n", szgroupname );
  1067. return -1;
  1068. }
  1069. ipick = engine->SentenceGroupPick(isentenceg, name, sizeof( name ));
  1070. if (ipick >= 0 && name[0])
  1071. {
  1072. int sentenceIndex = SENTENCEG_Lookup( name );
  1073. CPASAttenuationFilter filter( GetContainingEntity( entity ), soundlevel );
  1074. CBaseEntity::EmitSentenceByIndex( filter, ENTINDEX(entity), CHAN_VOICE, sentenceIndex, volume, soundlevel, flags, pitch );
  1075. return sentenceIndex;
  1076. }
  1077. return -1;
  1078. }
  1079. // play sentences in sequential order from sentence group. Reset after last sentence.
  1080. int SENTENCEG_PlaySequentialSz(edict_t *entity, const char *szgroupname,
  1081. float volume, soundlevel_t soundlevel, int flags, int pitch, int ipick, int freset)
  1082. {
  1083. char name[64];
  1084. int ipicknext;
  1085. int isentenceg;
  1086. if (!fSentencesInit)
  1087. return -1;
  1088. name[0] = 0;
  1089. isentenceg = engine->SentenceGroupIndexFromName(szgroupname);
  1090. if (isentenceg < 0)
  1091. return -1;
  1092. ipicknext = engine->SentenceGroupPickSequential(isentenceg, name, sizeof( name ), ipick, freset);
  1093. if (ipicknext >= 0 && name[0])
  1094. {
  1095. int sentenceIndex = SENTENCEG_Lookup( name );
  1096. CPASAttenuationFilter filter( GetContainingEntity( entity ), soundlevel );
  1097. CBaseEntity::EmitSentenceByIndex( filter, ENTINDEX(entity), CHAN_VOICE, sentenceIndex, volume, soundlevel, flags, pitch );
  1098. return sentenceIndex;
  1099. }
  1100. return -1;
  1101. }
  1102. #if 0
  1103. // for this entity, for the given sentence within the sentence group, stop
  1104. // the sentence.
  1105. void SENTENCEG_Stop(edict_t *entity, int isentenceg, int ipick)
  1106. {
  1107. char buffer[64];
  1108. char sznum[8];
  1109. if (!fSentencesInit)
  1110. return;
  1111. if (isentenceg < 0 || ipick < 0)
  1112. return;
  1113. Q_snprintf(buffer,sizeof(buffer),"!%s%d", engine->SentenceGroupNameFromIndex( isentenceg ), ipick );
  1114. UTIL_StopSound(entity, CHAN_VOICE, buffer);
  1115. }
  1116. #endif
  1117. // open sentences.txt, scan for groups, build rgsentenceg
  1118. // Should be called from world spawn, only works on the
  1119. // first call and is ignored subsequently.
  1120. void SENTENCEG_Init()
  1121. {
  1122. if (fSentencesInit)
  1123. return;
  1124. engine->PrecacheSentenceFile( "scripts/sentences.txt" );
  1125. fSentencesInit = true;
  1126. }
  1127. // convert sentence (sample) name to !sentencenum, return !sentencenum
  1128. int SENTENCEG_Lookup(const char *sample)
  1129. {
  1130. return engine->SentenceIndexFromName( sample + 1 );
  1131. }
  1132. int SENTENCEG_GetIndex(const char *szrootname)
  1133. {
  1134. return engine->SentenceGroupIndexFromName( szrootname );
  1135. }
  1136. void UTIL_RestartAmbientSounds( void )
  1137. {
  1138. CAmbientGeneric *pAmbient = NULL;
  1139. while ( ( pAmbient = (CAmbientGeneric*) gEntList.FindEntityByClassname( pAmbient, "ambient_generic" ) ) != NULL )
  1140. {
  1141. if ( pAmbient->m_fActive )
  1142. {
  1143. if ( V_strstr( pAmbient->m_szSoundFile, "mp3" ) )
  1144. {
  1145. pAmbient->SendSound( SND_CHANGE_VOL ); // fake a change, so we don't create 2 sounds
  1146. }
  1147. pAmbient->SendSound( SND_CHANGE_VOL ); // fake a change, so we don't create 2 sounds
  1148. }
  1149. }
  1150. }
  1151. // play a specific sentence over the HEV suit speaker - just pass player entity, and !sentencename
  1152. void UTIL_EmitSoundSuit(edict_t *entity, const char *sample)
  1153. {
  1154. float fvol;
  1155. int pitch = PITCH_NORM;
  1156. fvol = suitvolume.GetFloat();
  1157. if (random->RandomInt(0,1))
  1158. pitch = random->RandomInt(0,6) + 98;
  1159. // If friendlies are talking, reduce the volume of the suit
  1160. if ( !g_AIFriendliesTalkSemaphore.IsAvailable( GetContainingEntity( entity ) ) )
  1161. {
  1162. fvol *= 0.3;
  1163. }
  1164. if (fvol > 0.05)
  1165. {
  1166. CPASAttenuationFilter filter( GetContainingEntity( entity ) );
  1167. filter.MakeReliable();
  1168. EmitSound_t ep;
  1169. ep.m_nChannel = CHAN_STATIC;
  1170. ep.m_pSoundName = sample;
  1171. ep.m_flVolume = fvol;
  1172. ep.m_SoundLevel = SNDLVL_NORM;
  1173. ep.m_nPitch = pitch;
  1174. CBaseEntity::EmitSound( filter, ENTINDEX(entity), ep );
  1175. }
  1176. }
  1177. // play a sentence, randomly selected from the passed in group id, over the HEV suit speaker
  1178. int UTIL_EmitGroupIDSuit(edict_t *entity, int isentenceg)
  1179. {
  1180. float fvol;
  1181. int pitch = PITCH_NORM;
  1182. int sentenceIndex = -1;
  1183. fvol = suitvolume.GetFloat();
  1184. if (random->RandomInt(0,1))
  1185. pitch = random->RandomInt(0,6) + 98;
  1186. // If friendlies are talking, reduce the volume of the suit
  1187. if ( !g_AIFriendliesTalkSemaphore.IsAvailable( GetContainingEntity( entity ) ) )
  1188. {
  1189. fvol *= 0.3;
  1190. }
  1191. if (fvol > 0.05)
  1192. sentenceIndex = SENTENCEG_PlayRndI(entity, isentenceg, fvol, SNDLVL_NORM, 0, pitch);
  1193. return sentenceIndex;
  1194. }
  1195. // play a sentence, randomly selected from the passed in groupname
  1196. int UTIL_EmitGroupnameSuit(edict_t *entity, const char *groupname)
  1197. {
  1198. float fvol;
  1199. int pitch = PITCH_NORM;
  1200. int sentenceIndex = -1;
  1201. fvol = suitvolume.GetFloat();
  1202. if (random->RandomInt(0,1))
  1203. pitch = random->RandomInt(0,6) + 98;
  1204. // If friendlies are talking, reduce the volume of the suit
  1205. if ( !g_AIFriendliesTalkSemaphore.IsAvailable( GetContainingEntity( entity ) ) )
  1206. {
  1207. fvol *= 0.3;
  1208. }
  1209. if (fvol > 0.05)
  1210. sentenceIndex = SENTENCEG_PlayRndSz(entity, groupname, fvol, SNDLVL_NORM, 0, pitch);
  1211. return sentenceIndex;
  1212. }
  1213. // ===================== MATERIAL TYPE DETECTION, MAIN ROUTINES ========================
  1214. //
  1215. // Used to detect the texture the player is standing on, map the
  1216. // texture name to a material type. Play footstep sound based
  1217. // on material type.
  1218. char TEXTURETYPE_Find( trace_t *ptr )
  1219. {
  1220. const surfacedata_t *psurfaceData = physprops->GetSurfaceData( ptr->surface.surfaceProps );
  1221. return psurfaceData->game.material;
  1222. }