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.

395 lines
14 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. // Information about algorithmic stuff that can occur on both client + server
  5. //
  6. // In order to reduce network traffic, it's possible to create a algorithms
  7. // that will work on both the client and the server and be totally repeatable.
  8. // All we need do is to send down initial conditions and let the algorithm
  9. // compute the values at various times. Note that this algorithm will be called
  10. // at different times with different frequencies on the client and server.
  11. //
  12. // The trick here is that in order for it to be repeatable, the algorithm either
  13. // cannot depend on random numbers, or, if it does, we need to make sure that
  14. // the random numbers generated are effectively done at the beginning of time,
  15. // so that differences in frame rate on client and server won't matter. It also
  16. // is important that the initial state sent across the network is identical
  17. // bitwise so that we produce the exact same results. Therefore no compression
  18. // should be used in the datatables.
  19. //
  20. // Note also that each algorithm must have its own random number stream so that
  21. // it cannot possibly interact with other code using random numbers that will
  22. // be called at various different intervals on the client + server. Use the
  23. // CUniformRandomStream class for this.
  24. //
  25. // There are two types of client-server neutral code: Code that doesn't interact
  26. // with player prediction, and code that does. The code that doesn't interact
  27. // with player prediction simply has to be able to produce the result f(time)
  28. // where time is monotonically increasing. For prediction, we have to produce
  29. // the result f(time) where time does *not* monotonically increase (time can be
  30. // anywhere between the "current" time and the prior 10 seconds).
  31. //
  32. // Code that is not used by player prediction can maintain state because later
  33. // calls will always compute the value at some future time. This computation can
  34. // use random number generation, but with the following restriction: Your code
  35. // must generate exactly the same number of random numbers regardless of how
  36. // frequently the code is called.
  37. //
  38. // In specific, this means that all random numbers used must either be computed
  39. // at init time, or must be used in an 'event-based form'. Namely, use random
  40. // numbers to compute the time at which events occur and the random inputs for
  41. // those events. When simulating forward, you must simulate all intervening
  42. // time and generate the same number of random numbers.
  43. //
  44. // For functions planned to be used by player prediction, one method is to use
  45. // some sort of stateless computation (where the only states are the initial
  46. // state and time). Note that random number generators have state implicit in
  47. // the number of calls made to that random number generator, and therefore you
  48. // cannot call a random number generator unless you are able to
  49. //
  50. // 1) Use a random number generator that can return the ith random number, namely:
  51. //
  52. // float r = random( i ); // i == the ith number in the random sequence
  53. //
  54. // 2) Be able to accurately know at any given time t how many random numbers
  55. // have already been generated (namely, compute the i in part 1 above).
  56. //
  57. // There is another alternative for code meant to be used by player prediction:
  58. // you could just store a history of 'events' from which you could completely
  59. // determine the value of f(time). That history would need to be at least 10
  60. // seconds long, which is guaranteed to be longer than the amount of time that
  61. // prediction would need. I've written a class which I haven't tested yet (but
  62. // will be using soon) called CTimedEventQueue (currently located in
  63. // env_wind_shared.h) which I plan to use to solve my problem (getting wind to
  64. // blow players).
  65. //
  66. //=============================================================================//
  67. #include "cbase.h"
  68. #include "env_wind_shared.h"
  69. #include "soundenvelope.h"
  70. #include "IEffects.h"
  71. #include "engine/IEngineSound.h"
  72. #include "sharedInterface.h"
  73. #include "renderparm.h"
  74. #ifdef CLIENT_DLL
  75. #include "physics_softbody.h"
  76. #endif
  77. // memdbgon must be the last include file in a .cpp file!!!
  78. #include "tier0/memdbgon.h"
  79. #ifdef CLIENT_DLL
  80. // Test concommand for wind/tree sway. Couldn't think of a better way to put it.
  81. // Will move it out of this file when we figure out how the weather control will be implemented.
  82. CON_COMMAND( cl_tree_sway_dir, "sets tree sway wind direction and strength" )
  83. {
  84. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  85. if ( args.ArgC() == 3 )
  86. {
  87. Vector windDir;
  88. windDir.x = V_atof( args.Arg( 1 ) );
  89. windDir.y = V_atof( args.Arg( 2 ) );
  90. windDir.z = 0;
  91. pRenderContext->SetVectorRenderingParameter( VECTOR_RENDERPARM_WIND_DIRECTION, windDir );
  92. }
  93. }
  94. #endif
  95. //-----------------------------------------------------------------------------
  96. // globals
  97. //-----------------------------------------------------------------------------
  98. static CUtlLinkedList< CEnvWindShared * > s_windControllers;
  99. CEnvWindShared::CEnvWindShared() : m_WindAveQueue(10), m_WindVariationQueue(10)
  100. {
  101. m_pWindSound = NULL;
  102. s_windControllers.AddToTail( this );
  103. }
  104. CEnvWindShared::~CEnvWindShared()
  105. {
  106. if (m_pWindSound)
  107. {
  108. CSoundEnvelopeController::GetController().Shutdown( m_pWindSound );
  109. }
  110. s_windControllers.FindAndRemove( this );
  111. }
  112. void CEnvWindShared::Init( int nEntIndex, int iRandomSeed, float flTime,
  113. int iInitialWindYaw, float flInitialWindSpeed )
  114. {
  115. m_iEntIndex = nEntIndex;
  116. m_flWindAngleVariation = m_flWindSpeedVariation = 1.0f;
  117. m_flStartTime = m_flSimTime = m_flSwitchTime = m_flVariationTime = flTime;
  118. m_iWindSeed = iRandomSeed;
  119. m_Stream.SetSeed( iRandomSeed );
  120. m_WindVariationStream.SetSeed( iRandomSeed );
  121. m_iWindDir = m_iInitialWindDir = iInitialWindYaw;
  122. // Bound it for networking as a postive integer
  123. m_iInitialWindDir = (int)( anglemod( m_iInitialWindDir ) );
  124. m_flAveWindSpeed = m_flWindSpeed = m_flInitialWindSpeed = flInitialWindSpeed;
  125. /*
  126. // Cache in the wind sound...
  127. if (!g_pEffects->IsServer())
  128. {
  129. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  130. m_pWindSound = controller.SoundCreate( -1, CHAN_STATIC,
  131. "EnvWind.Loop", ATTN_NONE );
  132. controller.Play( m_pWindSound, 0.0f, 100 );
  133. }
  134. */
  135. // Next time a change happens (which will happen immediately), it'll stop gusting
  136. m_bGusting = true;
  137. }
  138. //-----------------------------------------------------------------------------
  139. // Computes wind variation
  140. //-----------------------------------------------------------------------------
  141. #define WIND_VARIATION_UPDATE_TIME 0.1f
  142. void CEnvWindShared::ComputeWindVariation( float flTime )
  143. {
  144. // The wind variation is updated every 10th of a second..
  145. while( flTime >= m_flVariationTime )
  146. {
  147. m_flWindAngleVariation = m_WindVariationStream.RandomFloat( -10, 10 );
  148. m_flWindSpeedVariation = 1.0 + m_WindVariationStream.RandomFloat( -0.2, 0.2 );
  149. m_flVariationTime += WIND_VARIATION_UPDATE_TIME;
  150. }
  151. }
  152. //-----------------------------------------------------------------------------
  153. // Updates the wind sound
  154. //-----------------------------------------------------------------------------
  155. void CEnvWindShared::UpdateWindSound( float flTotalWindSpeed )
  156. {
  157. if (!g_pEffects->IsServer())
  158. {
  159. float flDuration = random->RandomFloat( 1.0f, 2.0f );
  160. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  161. // FIXME: Tweak with these numbers
  162. float flNormalizedWindSpeed = flTotalWindSpeed / 150.0f;
  163. if (flNormalizedWindSpeed > 1.0f)
  164. flNormalizedWindSpeed = 1.0f;
  165. float flPitch = 120 * Bias( flNormalizedWindSpeed, 0.3f ) + 100;
  166. float flVolume = 0.3f * Bias( flNormalizedWindSpeed, 0.3f ) + 0.7f;
  167. controller.SoundChangePitch( m_pWindSound, flPitch, flDuration );
  168. controller.SoundChangeVolume( m_pWindSound, flVolume, flDuration );
  169. }
  170. }
  171. //-----------------------------------------------------------------------------
  172. // Updates the swaying of trees
  173. //-----------------------------------------------------------------------------
  174. #define TREE_SWAY_UPDATE_TIME 2.0f
  175. void CEnvWindShared::UpdateTreeSway( float flTime )
  176. {
  177. #ifdef CLIENT_DLL
  178. while( flTime >= m_flSwayTime )
  179. {
  180. // Since the wind is constantly changing, but we need smooth values, we cache them off here.
  181. m_PrevSwayVector = m_CurrentSwayVector;
  182. m_CurrentSwayVector = m_currentWindVector;
  183. m_flSwayTime += TREE_SWAY_UPDATE_TIME;
  184. }
  185. // Update vertex shader
  186. float flPercentage = ( 1 - ( ( m_flSwayTime - flTime ) / TREE_SWAY_UPDATE_TIME ) );
  187. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  188. // Dividing by 2 helps the numbers the shader is expecting stay in line with other expected game values.
  189. Vector vecWind = Lerp( flPercentage, m_PrevSwayVector, m_CurrentSwayVector ) / 2;
  190. pRenderContext->SetVectorRenderingParameter( VECTOR_RENDERPARM_WIND_DIRECTION, vecWind );
  191. #endif
  192. }
  193. //-----------------------------------------------------------------------------
  194. // Updates the wind speed
  195. //-----------------------------------------------------------------------------
  196. #define WIND_ACCELERATION 150.0f // wind speed can accelerate this many units per second
  197. #define WIND_DECELERATION 15.0f // wind speed can decelerate this many units per second
  198. float CEnvWindShared::WindThink( float flTime )
  199. {
  200. // NOTE: This algorithm can be client-server neutal because we're using
  201. // the random number generator to generate *time* at which the wind changes.
  202. // We therefore need to structure the algorithm so that no matter the
  203. // frequency of calls to this function we produce the same wind speeds...
  204. ComputeWindVariation( flTime );
  205. // Update Tree Sway
  206. UpdateTreeSway( flTime );
  207. while (true)
  208. {
  209. // First, simulate up to the next switch time...
  210. float flTimeToSwitch = m_flSwitchTime - m_flSimTime;
  211. float flMaxDeltaTime = flTime - m_flSimTime;
  212. bool bGotToSwitchTime = (flMaxDeltaTime > flTimeToSwitch);
  213. float flSimDeltaTime = bGotToSwitchTime ? flTimeToSwitch : flMaxDeltaTime;
  214. // Now that we've chosen
  215. // either ramp up, or sleep till change
  216. bool bReachedSteadyState = true;
  217. if ( m_flAveWindSpeed > m_flWindSpeed )
  218. {
  219. m_flWindSpeed += WIND_ACCELERATION * flSimDeltaTime;
  220. if (m_flWindSpeed > m_flAveWindSpeed)
  221. m_flWindSpeed = m_flAveWindSpeed;
  222. else
  223. bReachedSteadyState = false;
  224. }
  225. else if ( m_flAveWindSpeed < m_flWindSpeed )
  226. {
  227. m_flWindSpeed -= WIND_DECELERATION * flSimDeltaTime;
  228. if (m_flWindSpeed < m_flAveWindSpeed)
  229. m_flWindSpeed = m_flAveWindSpeed;
  230. else
  231. bReachedSteadyState = false;
  232. }
  233. // Update the sim time
  234. // If we didn't get to a switch point, then we're done simulating for now
  235. if (!bGotToSwitchTime)
  236. {
  237. m_flSimTime = flTime;
  238. // We're about to exit, let's set the wind velocity...
  239. QAngle vecWindAngle( 0, m_iWindDir + m_flWindAngleVariation, 0 );
  240. AngleVectors( vecWindAngle, &m_currentWindVector );
  241. float flTotalWindSpeed = m_flWindSpeed * m_flWindSpeedVariation;
  242. #ifdef CLIENT_DLL
  243. g_SoftbodyEnvironment.SetWindDesc( m_currentWindVector, flTotalWindSpeed );
  244. #endif
  245. m_currentWindVector *= flTotalWindSpeed;
  246. // If we reached a steady state, we don't need to be called until the switch time
  247. // Otherwise, we should be called immediately
  248. // FIXME: If we ever call this from prediction, we'll need
  249. // to only update the sound if it's a new time
  250. // Or, we'll need to update the sound elsewhere.
  251. // Update the sound....
  252. // UpdateWindSound( flTotalWindSpeed );
  253. // Always immediately call, the wind is forever varying
  254. return ( flTime + 0.01f );
  255. }
  256. m_flSimTime = m_flSwitchTime;
  257. // Switch gusting state..
  258. if( m_bGusting )
  259. {
  260. // wind is gusting, so return to normal wind
  261. m_flAveWindSpeed = m_Stream.RandomInt( m_iMinWind, m_iMaxWind );
  262. // set up for another gust later
  263. m_bGusting = false;
  264. m_flSwitchTime += m_flMinGustDelay + m_Stream.RandomFloat( 0, m_flMaxGustDelay );
  265. #ifndef CLIENT_DLL
  266. m_OnGustEnd.FireOutput( NULL, NULL );
  267. #endif
  268. }
  269. else
  270. {
  271. // time for a gust.
  272. m_flAveWindSpeed = m_Stream.RandomInt( m_iMinGust, m_iMaxGust );
  273. // change wind direction, maybe a lot
  274. m_iWindDir = anglemod( m_iWindDir + m_Stream.RandomInt(-m_iGustDirChange, m_iGustDirChange) );
  275. // set up to stop the gust in a short while
  276. m_bGusting = true;
  277. #ifndef CLIENT_DLL
  278. m_OnGustStart.FireOutput( NULL, NULL );
  279. #endif
  280. // !!!HACKHACK - gust duration tied to the length of a particular wave file
  281. m_flSwitchTime += m_flGustDuration;
  282. }
  283. }
  284. }
  285. //-----------------------------------------------------------------------------
  286. // Method to reset wind speed..
  287. //-----------------------------------------------------------------------------
  288. void ResetWindspeed()
  289. {
  290. FOR_EACH_LL( s_windControllers, it )
  291. {
  292. s_windControllers[it]->m_currentWindVector.Init( 0, 0, 0 );
  293. }
  294. #ifdef CLIENT_DLL
  295. g_SoftbodyEnvironment.SetNoWind();
  296. #endif
  297. }
  298. //-----------------------------------------------------------------------------
  299. // GetWindspeedAtTime was never finished to actually take time in to consideration. We don't need
  300. // features that aren't written, but we do need to have multiple wind controllers on a map, so
  301. // we need to find the one that is affecting the given location and return its speed.
  302. //-----------------------------------------------------------------------------
  303. Vector GetWindspeedAtLocation( const Vector &location )
  304. {
  305. FOR_EACH_LL( s_windControllers, it )
  306. {
  307. CEnvWindShared *thisWindController = s_windControllers[it];
  308. float distance = (thisWindController->m_location - location).Length();
  309. if( distance < thisWindController->m_windRadius )
  310. {
  311. // This location is within our area of influence, so return our computer wind vector
  312. return thisWindController->m_currentWindVector;
  313. }
  314. }
  315. FOR_EACH_LL( s_windControllers, it )
  316. {
  317. CEnvWindShared *thisWindController = s_windControllers[it];
  318. if( thisWindController->m_windRadius == -1.0f )
  319. {
  320. // We do a second search for a global controller so you don't have to worry about order in the list.
  321. return thisWindController->m_currentWindVector;
  322. }
  323. }
  324. return Vector(0,0,0);// No wind
  325. }
  326. //-----------------------------------------------------------------------------
  327. // Method to sample the windspeed at a particular time
  328. //-----------------------------------------------------------------------------
  329. void GetWindspeedAtTime( float flTime, Vector &vecVelocity )
  330. {
  331. // For now, ignore history and time.. fix later when we use wind to affect
  332. // client-side prediction
  333. if ( s_windControllers.Count() == 0 )
  334. {
  335. vecVelocity.Init( 0, 0, 0 );
  336. }
  337. else
  338. {
  339. VectorCopy( s_windControllers[ s_windControllers.Head() ]->m_currentWindVector, vecVelocity );
  340. }
  341. }