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.

1027 lines
28 KiB

  1. //===== Copyright � 1996-2008, Valve Corporation, All rights reserved. ======//
  2. //
  3. // ambient_generic: a sound emitter used for one-shot and looping sounds.
  4. //
  5. //
  6. //===========================================================================//
  7. #include "cbase.h"
  8. #include "ambientgeneric.h"
  9. #include "engine/IEngineSound.h"
  10. // memdbgon must be the last include file in a .cpp file!!!
  11. #include "tier0/memdbgon.h"
  12. //-----------------------------------------------------------------------------
  13. // Purpose: Compute a suitable attenuation value given an audible radius
  14. // Input : radius -
  15. // playEverywhere - (disable attenuation)
  16. //-----------------------------------------------------------------------------
  17. #define REFERENCE_dB 60.0
  18. #define AMBIENT_GENERIC_UPDATE_RATE 5 // update at 5hz
  19. #define AMBIENT_GENERIC_THINK_DELAY ( 1.0f / float( AMBIENT_GENERIC_UPDATE_RATE ) )
  20. #ifdef HL1_DLL
  21. ConVar hl1_ref_db_distance( "hl1_ref_db_distance", "18.0" );
  22. #define REFERENCE_dB_DISTANCE hl1_ref_db_distance.GetFloat()
  23. #else
  24. #define REFERENCE_dB_DISTANCE 36.0
  25. #endif//HL1_DLL
  26. static soundlevel_t ComputeSoundlevel( float radius, bool playEverywhere )
  27. {
  28. soundlevel_t soundlevel = SNDLVL_NONE;
  29. if ( radius > 0 && !playEverywhere )
  30. {
  31. // attenuation is set to a distance, compute falloff
  32. float dB_loss = 20 * log10( radius / REFERENCE_dB_DISTANCE );
  33. soundlevel = (soundlevel_t)(int)(40 + dB_loss); // sound at 40dB at reference distance
  34. }
  35. return soundlevel;
  36. }
  37. // ==================== GENERIC AMBIENT SOUND ======================================
  38. #define CDPVPRESETMAX 27
  39. // presets for runtime pitch and vol modulation of ambient sounds
  40. dynpitchvol_t rgdpvpreset[CDPVPRESETMAX] =
  41. {
  42. // pitch pstart spinup spindwn volrun volstrt fadein fadeout lfotype lforate modptch modvol cspnup
  43. {1, 255, 75, 95, 95, 10, 1, 50, 95, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  44. {2, 255, 85, 70, 88, 10, 1, 20, 88, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  45. {3, 255, 100, 50, 75, 10, 1, 10, 75, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  46. {4, 100, 100, 0, 0, 10, 1, 90, 90, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  47. {5, 100, 100, 0, 0, 10, 1, 80, 80, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  48. {6, 100, 100, 0, 0, 10, 1, 50, 70, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  49. {7, 100, 100, 0, 0, 5, 1, 40, 50, 1, 50, 0, 10, 0, 0,0,0,0,0,0,0,0,0,0},
  50. {8, 100, 100, 0, 0, 5, 1, 40, 50, 1, 150, 0, 10, 0, 0,0,0,0,0,0,0,0,0,0},
  51. {9, 100, 100, 0, 0, 5, 1, 40, 50, 1, 750, 0, 10, 0, 0,0,0,0,0,0,0,0,0,0},
  52. {10,128, 100, 50, 75, 10, 1, 30, 40, 2, 8, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  53. {11,128, 100, 50, 75, 10, 1, 30, 40, 2, 25, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  54. {12,128, 100, 50, 75, 10, 1, 30, 40, 2, 70, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  55. {13,50, 50, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  56. {14,70, 70, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  57. {15,90, 90, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  58. {16,120, 120, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  59. {17,180, 180, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  60. {18,255, 255, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  61. {19,200, 75, 90, 90, 10, 1, 50, 90, 2, 100, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  62. {20,255, 75, 97, 90, 10, 1, 50, 90, 1, 40, 50, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  63. {21,100, 100, 0, 0, 10, 1, 30, 50, 3, 15, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  64. {22,160, 160, 0, 0, 10, 1, 50, 50, 3, 500, 25, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  65. {23,255, 75, 88, 0, 10, 1, 40, 0, 0, 0, 0, 0, 5, 0,0,0,0,0,0,0,0,0,0},
  66. {24,200, 20, 95, 70, 10, 1, 70, 70, 3, 20, 50, 0, 0, 0,0,0,0,0,0,0,0,0,0},
  67. {25,180, 100, 50, 60, 10, 1, 40, 60, 2, 90, 100, 100, 0, 0,0,0,0,0,0,0,0,0,0},
  68. {26,60, 60, 0, 0, 10, 1, 40, 70, 3, 80, 20, 50, 0, 0,0,0,0,0,0,0,0,0,0},
  69. {27,128, 90, 10, 10, 10, 1, 20, 40, 1, 5, 10, 20, 0, 0,0,0,0,0,0,0,0,0,0}
  70. };
  71. #ifndef INFESTED_DLL
  72. LINK_ENTITY_TO_CLASS( ambient_generic, CAmbientGeneric );
  73. #endif
  74. BEGIN_DATADESC( CAmbientGeneric )
  75. DEFINE_KEYFIELD( m_iszSound, FIELD_SOUNDNAME, "message" ),
  76. DEFINE_KEYFIELD( m_radius, FIELD_FLOAT, "radius" ),
  77. DEFINE_KEYFIELD( m_sSourceEntName, FIELD_STRING, "SourceEntityName" ),
  78. // recomputed in Activate()
  79. // DEFINE_FIELD( m_hSoundSource, EHANDLE ),
  80. // DEFINE_FIELD( m_nSoundSourceEntIndex, FIELD_INTERGER ),
  81. DEFINE_FIELD( m_flMaxRadius, FIELD_FLOAT ),
  82. DEFINE_FIELD( m_fActive, FIELD_BOOLEAN ),
  83. DEFINE_FIELD( m_fLooping, FIELD_BOOLEAN ),
  84. DEFINE_FIELD( m_iSoundLevel, FIELD_INTEGER ),
  85. // HACKHACK - This is not really in the spirit of the save/restore design, but save this
  86. // out as a binary data block. If the dynpitchvol_t is changed, old saved games will NOT
  87. // load these correctly, so bump the save/restore version if you change the size of the struct
  88. // The right way to do this is to split the input parms (read in keyvalue) into members and re-init this
  89. // struct in Precache(), but it's unlikely that the struct will change, so it's not worth the time right now.
  90. DEFINE_ARRAY( m_dpv, FIELD_CHARACTER, sizeof(dynpitchvol_t) ),
  91. // Function Pointers
  92. DEFINE_FUNCTION( RampThink ),
  93. // Inputs
  94. DEFINE_INPUTFUNC(FIELD_VOID, "PlaySound", InputPlaySound ),
  95. DEFINE_INPUTFUNC(FIELD_VOID, "StopSound", InputStopSound ),
  96. DEFINE_INPUTFUNC(FIELD_VOID, "ToggleSound", InputToggleSound ),
  97. DEFINE_INPUTFUNC(FIELD_FLOAT, "Pitch", InputPitch ),
  98. DEFINE_INPUTFUNC(FIELD_FLOAT, "Volume", InputVolume ),
  99. DEFINE_INPUTFUNC(FIELD_FLOAT, "FadeIn", InputFadeIn ),
  100. DEFINE_INPUTFUNC(FIELD_FLOAT, "FadeOut", InputFadeOut ),
  101. END_DATADESC()
  102. //-----------------------------------------------------------------------------
  103. // Spawn
  104. //-----------------------------------------------------------------------------
  105. void CAmbientGeneric::Spawn( void )
  106. {
  107. m_iSoundLevel = ComputeSoundlevel( m_radius, FBitSet( m_spawnflags, SF_AMBIENT_SOUND_EVERYWHERE )?true:false );
  108. ComputeMaxAudibleDistance( );
  109. char *szSoundFile = (char *)STRING( m_iszSound );
  110. if ( !m_iszSound || strlen( szSoundFile ) < 1 )
  111. {
  112. Warning( "Empty %s (%s) at %.2f, %.2f, %.2f\n", GetClassname(), GetDebugName(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z );
  113. UTIL_Remove(this);
  114. return;
  115. }
  116. SetSolid( SOLID_NONE );
  117. SetMoveType( MOVETYPE_NONE );
  118. // Set up think function for dynamic modification
  119. // of ambient sound's pitch or volume. Don't
  120. // start thinking yet.
  121. SetThink(&CAmbientGeneric::RampThink);
  122. SetNextThink( TICK_NEVER_THINK );
  123. m_fActive = false;
  124. if ( FBitSet ( m_spawnflags, SF_AMBIENT_SOUND_NOT_LOOPING ) )
  125. {
  126. m_fLooping = false;
  127. }
  128. else
  129. {
  130. m_fLooping = true;
  131. }
  132. m_hSoundSource = NULL;
  133. m_nSoundSourceEntIndex = -1;
  134. Precache( );
  135. // init all dynamic modulation parms
  136. InitModulationParms();
  137. }
  138. //-----------------------------------------------------------------------------
  139. // Computes the max audible radius for a given sound level
  140. //-----------------------------------------------------------------------------
  141. #define MIN_AUDIBLE_VOLUME 1.01e-3
  142. void CAmbientGeneric::ComputeMaxAudibleDistance( )
  143. {
  144. if (( m_iSoundLevel == SNDLVL_NONE ) || ( m_radius == 0.0f ))
  145. {
  146. m_flMaxRadius = -1.0f;
  147. return;
  148. }
  149. // Sadly, there's no direct way of getting at this.
  150. // We have to do an interative computation.
  151. float flGain = enginesound->GetDistGainFromSoundLevel( m_iSoundLevel, m_radius );
  152. if ( flGain <= MIN_AUDIBLE_VOLUME )
  153. {
  154. m_flMaxRadius = m_radius;
  155. return;
  156. }
  157. float flMinRadius = m_radius;
  158. float flMaxRadius = m_radius * 2;
  159. while ( true )
  160. {
  161. // First, find a min + max range surrounding the desired distance gain
  162. float flGain = enginesound->GetDistGainFromSoundLevel( m_iSoundLevel, flMaxRadius );
  163. if ( flGain <= MIN_AUDIBLE_VOLUME )
  164. break;
  165. // Always audible.
  166. if ( flMaxRadius > 1e5 )
  167. {
  168. m_flMaxRadius = -1.0f;
  169. return;
  170. }
  171. flMinRadius = flMaxRadius;
  172. flMaxRadius *= 2.0f;
  173. }
  174. // Now home in a little bit
  175. int nInterations = 4;
  176. while ( --nInterations >= 0 )
  177. {
  178. float flTestRadius = (flMinRadius + flMaxRadius) * 0.5f;
  179. float flGain = enginesound->GetDistGainFromSoundLevel( m_iSoundLevel, flTestRadius );
  180. if ( flGain <= MIN_AUDIBLE_VOLUME )
  181. {
  182. flMaxRadius = flTestRadius;
  183. }
  184. else
  185. {
  186. flMinRadius = flTestRadius;
  187. }
  188. }
  189. m_flMaxRadius = flMaxRadius;
  190. }
  191. //-----------------------------------------------------------------------------
  192. // Purpose: Input handler for changing pitch.
  193. // Input : Float new pitch from 0 - 255 (100 = as recorded).
  194. //-----------------------------------------------------------------------------
  195. void CAmbientGeneric::InputPitch( inputdata_t &inputdata )
  196. {
  197. m_dpv.pitch = clamp( inputdata.value.Float(), 0, 255 );
  198. SendSound( SND_CHANGE_PITCH );
  199. }
  200. //-----------------------------------------------------------------------------
  201. // Purpose: Input handler for changing volume.
  202. // Input : Float new volume, from 0 - 10.
  203. //-----------------------------------------------------------------------------
  204. void CAmbientGeneric::InputVolume( inputdata_t &inputdata )
  205. {
  206. //
  207. // Multiply the input value by ten since volumes are expected to be from 0 - 100.
  208. //
  209. m_dpv.vol = clamp( inputdata.value.Float(), 0, 10 ) * 10;
  210. m_dpv.volfrac = m_dpv.vol << 8;
  211. SendSound( SND_CHANGE_VOL );
  212. }
  213. //-----------------------------------------------------------------------------
  214. // Purpose: Input handler for fading in volume over time.
  215. // Input : Float volume fade in time 0 - 100 seconds
  216. //-----------------------------------------------------------------------------
  217. void CAmbientGeneric::InputFadeIn( inputdata_t &inputdata )
  218. {
  219. // cancel any fade out that might be happening
  220. m_dpv.fadeout = 0;
  221. m_dpv.fadein = inputdata.value.Float();
  222. if (m_dpv.fadein > 100) m_dpv.fadein = 100;
  223. if (m_dpv.fadein < 0) m_dpv.fadein = 0;
  224. if (m_dpv.fadein > 0)
  225. m_dpv.fadein = ( 100 << 8 ) / ( m_dpv.fadein * AMBIENT_GENERIC_UPDATE_RATE );
  226. SetNextThink( gpGlobals->curtime + 0.1f );
  227. }
  228. //-----------------------------------------------------------------------------
  229. // Purpose: Input handler for fading out volume over time.
  230. // Input : Float volume fade out time 0 - 100 seconds
  231. //-----------------------------------------------------------------------------
  232. void CAmbientGeneric::InputFadeOut( inputdata_t &inputdata )
  233. {
  234. // cancel any fade in that might be happening
  235. m_dpv.fadein = 0;
  236. m_dpv.fadeout = inputdata.value.Float();
  237. if (m_dpv.fadeout > 100) m_dpv.fadeout = 100;
  238. if (m_dpv.fadeout < 0) m_dpv.fadeout = 0;
  239. if (m_dpv.fadeout > 0)
  240. m_dpv.fadeout = ( 100 << 8 ) / ( m_dpv.fadeout * AMBIENT_GENERIC_UPDATE_RATE );
  241. SetNextThink( gpGlobals->curtime + 0.1f );
  242. }
  243. void CAmbientGeneric::Precache( void )
  244. {
  245. char *szSoundFile = (char *)STRING( m_iszSound );
  246. if ( m_iszSound != NULL_STRING && strlen( szSoundFile ) > 1 )
  247. {
  248. if (*szSoundFile != '!')
  249. {
  250. PrecacheScriptSound(szSoundFile);
  251. }
  252. }
  253. if ( !FBitSet (m_spawnflags, SF_AMBIENT_SOUND_START_SILENT ) )
  254. {
  255. // start the sound ASAP
  256. if (m_fLooping)
  257. m_fActive = true;
  258. }
  259. }
  260. //------------------------------------------------------------------------------
  261. // Purpose:
  262. //------------------------------------------------------------------------------
  263. void CAmbientGeneric::Activate( void )
  264. {
  265. BaseClass::Activate();
  266. // Initialize sound source. If no source was given, or source can't be found
  267. // then this is the source
  268. if (m_hSoundSource == NULL)
  269. {
  270. if (m_sSourceEntName != NULL_STRING)
  271. {
  272. m_hSoundSource = gEntList.FindEntityByName( NULL, m_sSourceEntName );
  273. if ( m_hSoundSource != NULL )
  274. {
  275. m_nSoundSourceEntIndex = m_hSoundSource->entindex();
  276. }
  277. }
  278. if (m_hSoundSource == NULL)
  279. {
  280. m_hSoundSource = this;
  281. m_nSoundSourceEntIndex = entindex();
  282. }
  283. else
  284. {
  285. if ( !FBitSet( m_spawnflags, SF_AMBIENT_SOUND_EVERYWHERE ) )
  286. {
  287. AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
  288. }
  289. }
  290. }
  291. // If active start the sound
  292. if ( m_fActive )
  293. {
  294. int flags = SND_SPAWNING;
  295. // If we are loading a saved game, we can't write into the init/signon buffer here, so just issue
  296. // as a regular sound message...
  297. if ( gpGlobals->eLoadType == MapLoad_Transition ||
  298. gpGlobals->eLoadType == MapLoad_LoadGame ||
  299. g_pGameRules->InRoundRestart() )
  300. {
  301. flags = SND_NOFLAGS;
  302. }
  303. // Tracker 76119: 8/12/07 ywb:
  304. // Make sure pitch and volume are set up to the correct value (especially after restoring a .sav file)
  305. flags |= ( SND_CHANGE_PITCH | SND_CHANGE_VOL );
  306. // Don't bother sending over to client if volume is zero, though
  307. CSoundParameters params;
  308. GetParametersForSound( STRING( m_iszSound ), params, NULL );
  309. bool isNewScriptSound = params.m_hSoundScriptHash != SOUNDEMITTER_INVALID_HASH && params.m_nSoundEntryVersion > 1;
  310. bool isLoading = gpGlobals->eLoadType == MapLoad_LoadGame;
  311. if ( m_dpv.vol > 0 && !(isLoading && isNewScriptSound) )
  312. {
  313. SendSound( (SoundFlags_t)flags );
  314. }
  315. SetNextThink( gpGlobals->curtime + 0.1f );
  316. }
  317. }
  318. //-----------------------------------------------------------------------------
  319. // Rules about which entities need to transmit along with me
  320. //-----------------------------------------------------------------------------
  321. void CAmbientGeneric::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
  322. {
  323. // Ambient generics never transmit; this is just a way for us to ensure
  324. // the sound source gets transmitted; that's why we don't call pInfo->m_pTransmitEdict->Set
  325. if ( !m_hSoundSource || m_hSoundSource == this || !m_fActive )
  326. return;
  327. // Don't bother sending the position of the source if we have to play everywhere
  328. if ( FBitSet( m_spawnflags, SF_AMBIENT_SOUND_EVERYWHERE ) )
  329. return;
  330. Assert( pInfo->m_pClientEnt );
  331. CBaseEntity *pClient = (CBaseEntity*)(pInfo->m_pClientEnt->GetUnknown());
  332. if ( !pClient )
  333. return;
  334. // Send the sound source if he's close enough
  335. if ( ( m_flMaxRadius < 0 ) || ( pClient->GetAbsOrigin().DistToSqr( m_hSoundSource->GetAbsOrigin() ) <= m_flMaxRadius * m_flMaxRadius ) )
  336. {
  337. m_hSoundSource->SetTransmit( pInfo, false );
  338. }
  339. }
  340. //-----------------------------------------------------------------------------
  341. // Purpose:
  342. //-----------------------------------------------------------------------------
  343. void CAmbientGeneric::UpdateOnRemove( void )
  344. {
  345. if ( m_fActive )
  346. {
  347. // Stop the sound we're generating
  348. SendSound( SND_STOP );
  349. }
  350. BaseClass::UpdateOnRemove();
  351. }
  352. //-----------------------------------------------------------------------------
  353. // Purpose: Think at 5hz if we are dynamically modifying pitch or volume of the
  354. // playing sound. This function will ramp pitch and/or volume up or
  355. // down, modify pitch/volume with lfo if active.
  356. //-----------------------------------------------------------------------------
  357. void CAmbientGeneric::RampThink( void )
  358. {
  359. int pitch = m_dpv.pitch;
  360. int vol = m_dpv.vol;
  361. int flags = 0;
  362. int fChanged = 0; // false if pitch and vol remain unchanged this round
  363. int prev;
  364. if (!m_dpv.spinup && !m_dpv.spindown && !m_dpv.fadein && !m_dpv.fadeout && !m_dpv.lfotype)
  365. return; // no ramps or lfo, stop thinking
  366. // ==============
  367. // pitch envelope
  368. // ==============
  369. if (m_dpv.spinup || m_dpv.spindown)
  370. {
  371. prev = m_dpv.pitchfrac >> 8;
  372. if (m_dpv.spinup > 0)
  373. m_dpv.pitchfrac += m_dpv.spinup;
  374. else if (m_dpv.spindown > 0)
  375. m_dpv.pitchfrac -= m_dpv.spindown;
  376. pitch = m_dpv.pitchfrac >> 8;
  377. if (pitch > m_dpv.pitchrun)
  378. {
  379. pitch = m_dpv.pitchrun;
  380. m_dpv.spinup = 0; // done with ramp up
  381. }
  382. if (pitch < m_dpv.pitchstart)
  383. {
  384. pitch = m_dpv.pitchstart;
  385. m_dpv.spindown = 0; // done with ramp down
  386. // shut sound off
  387. SendSound( SND_STOP );
  388. // return without setting m_flNextThink
  389. return;
  390. }
  391. if (pitch > 255) pitch = 255;
  392. if (pitch < 1) pitch = 1;
  393. m_dpv.pitch = pitch;
  394. fChanged |= (prev != pitch);
  395. flags |= SND_CHANGE_PITCH;
  396. }
  397. // ==================
  398. // amplitude envelope
  399. // ==================
  400. if (m_dpv.fadein || m_dpv.fadeout)
  401. {
  402. prev = m_dpv.volfrac >> 8;
  403. if (m_dpv.fadein > 0)
  404. m_dpv.volfrac += m_dpv.fadein;
  405. else if (m_dpv.fadeout > 0)
  406. m_dpv.volfrac -= m_dpv.fadeout;
  407. vol = m_dpv.volfrac >> 8;
  408. if (vol > m_dpv.volrun)
  409. {
  410. vol = m_dpv.volrun;
  411. m_dpv.volfrac = vol << 8;
  412. m_dpv.fadein = 0; // done with ramp up
  413. }
  414. if (vol < m_dpv.volstart)
  415. {
  416. vol = m_dpv.volstart;
  417. m_dpv.vol = vol;
  418. m_dpv.volfrac = vol << 8;
  419. m_dpv.fadeout = 0; // done with ramp down
  420. // shut sound off
  421. SendSound( SND_STOP );
  422. // return without setting m_flNextThink
  423. return;
  424. }
  425. if (vol > 100)
  426. {
  427. vol = 100;
  428. m_dpv.volfrac = vol << 8;
  429. }
  430. if (vol < 1)
  431. {
  432. vol = 1;
  433. m_dpv.volfrac = vol << 8;
  434. }
  435. m_dpv.vol = vol;
  436. fChanged |= (prev != vol);
  437. flags |= SND_CHANGE_VOL;
  438. }
  439. // ===================
  440. // pitch/amplitude LFO
  441. // ===================
  442. if (m_dpv.lfotype)
  443. {
  444. int pos;
  445. if (m_dpv.lfofrac > 0x6fffffff)
  446. m_dpv.lfofrac = 0;
  447. // update lfo, lfofrac/255 makes a triangle wave 0-255
  448. m_dpv.lfofrac += m_dpv.lforate;
  449. pos = m_dpv.lfofrac >> 8;
  450. if (m_dpv.lfofrac < 0)
  451. {
  452. m_dpv.lfofrac = 0;
  453. m_dpv.lforate = abs(m_dpv.lforate);
  454. pos = 0;
  455. }
  456. else if (pos > 255)
  457. {
  458. pos = 255;
  459. m_dpv.lfofrac = (255 << 8);
  460. m_dpv.lforate = -abs(m_dpv.lforate);
  461. }
  462. switch(m_dpv.lfotype)
  463. {
  464. case LFO_SQUARE:
  465. if (pos < 128)
  466. m_dpv.lfomult = 255;
  467. else
  468. m_dpv.lfomult = 0;
  469. break;
  470. case LFO_RANDOM:
  471. if (pos == 255)
  472. m_dpv.lfomult = random->RandomInt(0, 255);
  473. break;
  474. case LFO_TRIANGLE:
  475. default:
  476. m_dpv.lfomult = pos;
  477. break;
  478. }
  479. if (m_dpv.lfomodpitch)
  480. {
  481. prev = pitch;
  482. // pitch 0-255
  483. pitch += ((m_dpv.lfomult - 128) * m_dpv.lfomodpitch) / 100;
  484. if (pitch > 255) pitch = 255;
  485. if (pitch < 1) pitch = 1;
  486. fChanged |= (prev != pitch);
  487. flags |= SND_CHANGE_PITCH;
  488. }
  489. if (m_dpv.lfomodvol)
  490. {
  491. // vol 0-100
  492. prev = vol;
  493. vol += ((m_dpv.lfomult - 128) * m_dpv.lfomodvol) / 100;
  494. if (vol > 100) vol = 100;
  495. if (vol < 0) vol = 0;
  496. fChanged |= (prev != vol);
  497. flags |= SND_CHANGE_VOL;
  498. }
  499. }
  500. // Send update to playing sound only if we actually changed
  501. // pitch or volume in this routine.
  502. if (flags && fChanged)
  503. {
  504. if (pitch == PITCH_NORM)
  505. pitch = PITCH_NORM + 1; // don't send 'no pitch' !
  506. CBaseEntity* pSoundSource = m_hSoundSource;
  507. if (pSoundSource)
  508. {
  509. UTIL_EmitAmbientSound(pSoundSource->GetSoundSourceIndex(), pSoundSource->GetAbsOrigin(),
  510. STRING( m_iszSound ), (vol * 0.01), m_iSoundLevel, flags, pitch);
  511. }
  512. }
  513. // update ramps at 5hz
  514. SetNextThink( gpGlobals->curtime + AMBIENT_GENERIC_THINK_DELAY );
  515. return;
  516. }
  517. //-----------------------------------------------------------------------------
  518. // Purpose: Init all ramp params in preparation to play a new sound.
  519. //-----------------------------------------------------------------------------
  520. void CAmbientGeneric::InitModulationParms(void)
  521. {
  522. int pitchinc;
  523. m_dpv.volrun = m_iHealth * 10; // 0 - 100
  524. if (m_dpv.volrun > 100) m_dpv.volrun = 100;
  525. if (m_dpv.volrun < 0) m_dpv.volrun = 0;
  526. // get presets
  527. if (m_dpv.preset != 0 && m_dpv.preset <= CDPVPRESETMAX)
  528. {
  529. // load preset values
  530. m_dpv = rgdpvpreset[m_dpv.preset - 1];
  531. // fixup preset values, just like
  532. // fixups in KeyValue routine.
  533. if (m_dpv.spindown > 0)
  534. m_dpv.spindown = (101 - m_dpv.spindown) * 64;
  535. if (m_dpv.spinup > 0)
  536. m_dpv.spinup = (101 - m_dpv.spinup) * 64;
  537. m_dpv.volstart *= 10;
  538. m_dpv.volrun *= 10;
  539. if (m_dpv.fadein > 0)
  540. m_dpv.fadein = (101 - m_dpv.fadein) * 64;
  541. if (m_dpv.fadeout > 0)
  542. m_dpv.fadeout = (101 - m_dpv.fadeout) * 64;
  543. m_dpv.lforate *= 256;
  544. m_dpv.fadeinsav = m_dpv.fadein;
  545. m_dpv.fadeoutsav = m_dpv.fadeout;
  546. m_dpv.spinupsav = m_dpv.spinup;
  547. m_dpv.spindownsav = m_dpv.spindown;
  548. }
  549. m_dpv.fadein = m_dpv.fadeinsav;
  550. m_dpv.fadeout = 0;
  551. if (m_dpv.fadein)
  552. m_dpv.vol = m_dpv.volstart;
  553. else
  554. m_dpv.vol = m_dpv.volrun;
  555. m_dpv.spinup = m_dpv.spinupsav;
  556. m_dpv.spindown = 0;
  557. if (m_dpv.spinup)
  558. m_dpv.pitch = m_dpv.pitchstart;
  559. else
  560. m_dpv.pitch = m_dpv.pitchrun;
  561. if (m_dpv.pitch == 0)
  562. m_dpv.pitch = PITCH_NORM;
  563. m_dpv.pitchfrac = m_dpv.pitch << 8;
  564. m_dpv.volfrac = m_dpv.vol << 8;
  565. m_dpv.lfofrac = 0;
  566. m_dpv.lforate = abs(m_dpv.lforate);
  567. m_dpv.cspincount = 1;
  568. if (m_dpv.cspinup)
  569. {
  570. pitchinc = (255 - m_dpv.pitchstart) / m_dpv.cspinup;
  571. m_dpv.pitchrun = m_dpv.pitchstart + pitchinc;
  572. if (m_dpv.pitchrun > 255) m_dpv.pitchrun = 255;
  573. }
  574. if ((m_dpv.spinupsav || m_dpv.spindownsav || (m_dpv.lfotype && m_dpv.lfomodpitch))
  575. && (m_dpv.pitch == PITCH_NORM))
  576. m_dpv.pitch = PITCH_NORM + 1; // must never send 'no pitch' as first pitch
  577. // if we intend to pitch shift later!
  578. }
  579. //-----------------------------------------------------------------------------
  580. // Purpose: Input handler that begins playing the sound.
  581. //-----------------------------------------------------------------------------
  582. void CAmbientGeneric::InputPlaySound( inputdata_t &inputdata )
  583. {
  584. if (!m_fActive)
  585. {
  586. //Adrian: Stop our current sound before starting a new one!
  587. SendSound( SND_STOP );
  588. ToggleSound();
  589. }
  590. }
  591. //-----------------------------------------------------------------------------
  592. // Purpose: Input handler that stops playing the sound.
  593. //-----------------------------------------------------------------------------
  594. void CAmbientGeneric::InputStopSound( inputdata_t &inputdata )
  595. {
  596. if (m_fActive)
  597. {
  598. ToggleSound();
  599. }
  600. }
  601. void CAmbientGeneric::SendSound( SoundFlags_t flags)
  602. {
  603. char *szSoundFile = (char *)STRING( m_iszSound );
  604. CBaseEntity* pSoundSource = m_hSoundSource;
  605. if ( pSoundSource )
  606. {
  607. if ( flags == SND_STOP )
  608. {
  609. UTIL_EmitAmbientSound(pSoundSource->GetSoundSourceIndex(), pSoundSource->GetAbsOrigin(), szSoundFile,
  610. 0, SNDLVL_NONE, flags, 0);
  611. }
  612. else
  613. {
  614. UTIL_EmitAmbientSound(pSoundSource->GetSoundSourceIndex(), pSoundSource->GetAbsOrigin(), szSoundFile,
  615. (m_dpv.vol * 0.01), m_iSoundLevel, flags, m_dpv.pitch);
  616. }
  617. }
  618. else
  619. {
  620. if ( ( flags == SND_STOP ) &&
  621. ( m_nSoundSourceEntIndex != -1 ) )
  622. {
  623. UTIL_EmitAmbientSound(m_nSoundSourceEntIndex, GetAbsOrigin(), szSoundFile,
  624. 0, SNDLVL_NONE, flags, 0);
  625. }
  626. }
  627. }
  628. //-----------------------------------------------------------------------------
  629. // Purpose: Input handler that stops playing the sound.
  630. //-----------------------------------------------------------------------------
  631. void CAmbientGeneric::InputToggleSound( inputdata_t &inputdata )
  632. {
  633. ToggleSound();
  634. }
  635. //-----------------------------------------------------------------------------
  636. // Purpose: Turns an ambient sound on or off. If the ambient is a looping sound,
  637. // mark sound as active (m_fActive) if it's playing, innactive if not.
  638. // If the sound is not a looping sound, never mark it as active.
  639. // Input : pActivator -
  640. // pCaller -
  641. // useType -
  642. // value -
  643. //-----------------------------------------------------------------------------
  644. void CAmbientGeneric::ToggleSound()
  645. {
  646. // m_fActive is true only if a looping sound is playing.
  647. if ( m_fActive )
  648. {// turn sound off
  649. if (m_dpv.cspinup)
  650. {
  651. // Don't actually shut off. Each toggle causes
  652. // incremental spinup to max pitch
  653. if (m_dpv.cspincount <= m_dpv.cspinup)
  654. {
  655. int pitchinc;
  656. // start a new spinup
  657. m_dpv.cspincount++;
  658. pitchinc = (255 - m_dpv.pitchstart) / m_dpv.cspinup;
  659. m_dpv.spinup = m_dpv.spinupsav;
  660. m_dpv.spindown = 0;
  661. m_dpv.pitchrun = m_dpv.pitchstart + pitchinc * m_dpv.cspincount;
  662. if (m_dpv.pitchrun > 255) m_dpv.pitchrun = 255;
  663. SetNextThink( gpGlobals->curtime + 0.1f );
  664. }
  665. }
  666. else
  667. {
  668. m_fActive = false;
  669. // HACKHACK - this makes the code in Precache() work properly after a save/restore
  670. m_spawnflags |= SF_AMBIENT_SOUND_START_SILENT;
  671. if (m_dpv.spindownsav || m_dpv.fadeoutsav)
  672. {
  673. // spin it down (or fade it) before shutoff if spindown is set
  674. m_dpv.spindown = m_dpv.spindownsav;
  675. m_dpv.spinup = 0;
  676. m_dpv.fadeout = m_dpv.fadeoutsav;
  677. m_dpv.fadein = 0;
  678. SetNextThink( gpGlobals->curtime + 0.1f );
  679. }
  680. else
  681. {
  682. SendSound( SND_STOP ); // stop sound
  683. }
  684. }
  685. }
  686. else
  687. {// turn sound on
  688. // only toggle if this is a looping sound. If not looping, each
  689. // trigger will cause the sound to play. If the sound is still
  690. // playing from a previous trigger press, it will be shut off
  691. // and then restarted.
  692. if (m_fLooping)
  693. m_fActive = true;
  694. else
  695. {
  696. // shut sound off now - may be interrupting a long non-looping sound
  697. SendSound( SND_STOP ); // stop sound
  698. }
  699. // init all ramp params for startup
  700. InitModulationParms();
  701. SendSound( SND_NOFLAGS ); // send sound
  702. SetNextThink( gpGlobals->curtime + 0.1f );
  703. }
  704. }
  705. // KeyValue - load keyvalue pairs into member data of the
  706. // ambient generic. NOTE: called BEFORE spawn!
  707. bool CAmbientGeneric::KeyValue( const char *szKeyName, const char *szValue )
  708. {
  709. // NOTE: changing any of the modifiers in this code
  710. // NOTE: also requires changing InitModulationParms code.
  711. // preset
  712. if (FStrEq(szKeyName, "preset"))
  713. {
  714. m_dpv.preset = atoi(szValue);
  715. }
  716. // pitchrun
  717. else if (FStrEq(szKeyName, "pitch"))
  718. {
  719. m_dpv.pitchrun = atoi(szValue);
  720. if (m_dpv.pitchrun > 255) m_dpv.pitchrun = 255;
  721. if (m_dpv.pitchrun < 0) m_dpv.pitchrun = 0;
  722. }
  723. // pitchstart
  724. else if (FStrEq(szKeyName, "pitchstart"))
  725. {
  726. m_dpv.pitchstart = atoi(szValue);
  727. if (m_dpv.pitchstart > 255) m_dpv.pitchstart = 255;
  728. if (m_dpv.pitchstart < 0) m_dpv.pitchstart = 0;
  729. }
  730. // spinup
  731. else if (FStrEq(szKeyName, "spinup"))
  732. {
  733. m_dpv.spinup = atoi(szValue);
  734. if (m_dpv.spinup > 100) m_dpv.spinup = 100;
  735. if (m_dpv.spinup < 0) m_dpv.spinup = 0;
  736. if (m_dpv.spinup > 0)
  737. m_dpv.spinup = (101 - m_dpv.spinup) * 64;
  738. m_dpv.spinupsav = m_dpv.spinup;
  739. }
  740. // spindown
  741. else if (FStrEq(szKeyName, "spindown"))
  742. {
  743. m_dpv.spindown = atoi(szValue);
  744. if (m_dpv.spindown > 100) m_dpv.spindown = 100;
  745. if (m_dpv.spindown < 0) m_dpv.spindown = 0;
  746. if (m_dpv.spindown > 0)
  747. m_dpv.spindown = (101 - m_dpv.spindown) * 64;
  748. m_dpv.spindownsav = m_dpv.spindown;
  749. }
  750. // volstart
  751. else if (FStrEq(szKeyName, "volstart"))
  752. {
  753. m_dpv.volstart = atoi(szValue);
  754. if (m_dpv.volstart > 10) m_dpv.volstart = 10;
  755. if (m_dpv.volstart < 0) m_dpv.volstart = 0;
  756. m_dpv.volstart *= 10; // 0 - 100
  757. }
  758. // legacy fadein
  759. else if (FStrEq(szKeyName, "fadein"))
  760. {
  761. m_dpv.fadein = atoi(szValue);
  762. if (m_dpv.fadein > 100) m_dpv.fadein = 100;
  763. if (m_dpv.fadein < 0) m_dpv.fadein = 0;
  764. if (m_dpv.fadein > 0)
  765. m_dpv.fadein = (101 - m_dpv.fadein) * 64;
  766. m_dpv.fadeinsav = m_dpv.fadein;
  767. }
  768. // legacy fadeout
  769. else if (FStrEq(szKeyName, "fadeout"))
  770. {
  771. m_dpv.fadeout = atoi(szValue);
  772. if (m_dpv.fadeout > 100) m_dpv.fadeout = 100;
  773. if (m_dpv.fadeout < 0) m_dpv.fadeout = 0;
  774. if (m_dpv.fadeout > 0)
  775. m_dpv.fadeout = (101 - m_dpv.fadeout) * 64;
  776. m_dpv.fadeoutsav = m_dpv.fadeout;
  777. }
  778. // fadeinsecs
  779. else if (FStrEq(szKeyName, "fadeinsecs"))
  780. {
  781. m_dpv.fadein = atoi(szValue);
  782. if (m_dpv.fadein > 100) m_dpv.fadein = 100;
  783. if (m_dpv.fadein < 0) m_dpv.fadein = 0;
  784. if (m_dpv.fadein > 0)
  785. m_dpv.fadein = ( 100 << 8 ) / ( m_dpv.fadein * AMBIENT_GENERIC_UPDATE_RATE );
  786. m_dpv.fadeinsav = m_dpv.fadein;
  787. }
  788. // fadeoutsecs
  789. else if (FStrEq(szKeyName, "fadeoutsecs"))
  790. {
  791. m_dpv.fadeout = atoi(szValue);
  792. if (m_dpv.fadeout > 100) m_dpv.fadeout = 100;
  793. if (m_dpv.fadeout < 0) m_dpv.fadeout = 0;
  794. if (m_dpv.fadeout > 0)
  795. m_dpv.fadeout = ( 100 << 8 ) / ( m_dpv.fadeout * AMBIENT_GENERIC_UPDATE_RATE );
  796. m_dpv.fadeoutsav = m_dpv.fadeout;
  797. }
  798. // lfotype
  799. else if (FStrEq(szKeyName, "lfotype"))
  800. {
  801. m_dpv.lfotype = atoi(szValue);
  802. if (m_dpv.lfotype > 4) m_dpv.lfotype = LFO_TRIANGLE;
  803. }
  804. // lforate
  805. else if (FStrEq(szKeyName, "lforate"))
  806. {
  807. m_dpv.lforate = atoi(szValue);
  808. if (m_dpv.lforate > 1000) m_dpv.lforate = 1000;
  809. if (m_dpv.lforate < 0) m_dpv.lforate = 0;
  810. m_dpv.lforate *= 256;
  811. }
  812. // lfomodpitch
  813. else if (FStrEq(szKeyName, "lfomodpitch"))
  814. {
  815. m_dpv.lfomodpitch = atoi(szValue);
  816. if (m_dpv.lfomodpitch > 100) m_dpv.lfomodpitch = 100;
  817. if (m_dpv.lfomodpitch < 0) m_dpv.lfomodpitch = 0;
  818. }
  819. // lfomodvol
  820. else if (FStrEq(szKeyName, "lfomodvol"))
  821. {
  822. m_dpv.lfomodvol = atoi(szValue);
  823. if (m_dpv.lfomodvol > 100) m_dpv.lfomodvol = 100;
  824. if (m_dpv.lfomodvol < 0) m_dpv.lfomodvol = 0;
  825. }
  826. // cspinup
  827. else if (FStrEq(szKeyName, "cspinup"))
  828. {
  829. m_dpv.cspinup = atoi(szValue);
  830. if (m_dpv.cspinup > 100) m_dpv.cspinup = 100;
  831. if (m_dpv.cspinup < 0) m_dpv.cspinup = 0;
  832. }
  833. else
  834. return BaseClass::KeyValue( szKeyName, szValue );
  835. return true;
  836. }