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.

352 lines
8.7 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // c_fish.cpp
  9. // Simple fish client-side logic
  10. // Author: Michael S. Booth, April 2005
  11. #include "cbase.h"
  12. #include <bitbuf.h>
  13. #include "engine/ivdebugoverlay.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. extern float UTIL_WaterLevel( const Vector &position, float minz, float maxz );
  17. ConVar FishDebug( "fish_debug", "0", FCVAR_CHEAT, "Show debug info for fish" );
  18. //-----------------------------------------------------------------------------
  19. /**
  20. * Client-side fish entity
  21. */
  22. class C_Fish : public C_BaseAnimating
  23. {
  24. public:
  25. DECLARE_CLASS( C_Fish, C_BaseAnimating );
  26. DECLARE_CLIENTCLASS();
  27. virtual void Spawn( void );
  28. virtual void ClientThink();
  29. virtual void OnDataChanged( DataUpdateType_t type );
  30. private:
  31. friend void RecvProxy_FishOriginX( const CRecvProxyData *pData, void *pStruct, void *pOut );
  32. friend void RecvProxy_FishOriginY( const CRecvProxyData *pData, void *pStruct, void *pOut );
  33. Vector m_pos; ///< local position
  34. Vector m_vel; ///< local velocity
  35. QAngle m_angles; ///< local angles
  36. int m_localLifeState; ///< our version of m_lifeState
  37. float m_deathDepth; ///< water depth when we died
  38. float m_deathAngle; ///< angle to float at when dead
  39. float m_buoyancy; ///< so each fish floats at a different rate when dead
  40. CountdownTimer m_wiggleTimer; ///< for simulating swimming motions
  41. float m_wigglePhase; ///< where in the wiggle sinusoid we are
  42. float m_wiggleRate; ///< the speed of our wiggling
  43. Vector m_actualPos; ///< position from server
  44. QAngle m_actualAngles; ///< angles from server
  45. Vector m_poolOrigin;
  46. float m_waterLevel; ///< Z coordinate of water surface
  47. bool m_gotUpdate; ///< true after we have received a network update
  48. enum { MAX_ERROR_HISTORY = 20 };
  49. float m_errorHistory[ MAX_ERROR_HISTORY ]; ///< error history samples
  50. int m_errorHistoryIndex;
  51. int m_errorHistoryCount;
  52. float m_averageError;
  53. };
  54. //-----------------------------------------------------------------------------
  55. void RecvProxy_FishOriginX( const CRecvProxyData *pData, void *pStruct, void *pOut )
  56. {
  57. C_Fish *fish = (C_Fish *)pStruct;
  58. float *out = (float *)pOut;
  59. *out = pData->m_Value.m_Float + fish->m_poolOrigin.x;
  60. }
  61. void RecvProxy_FishOriginY( const CRecvProxyData *pData, void *pStruct, void *pOut )
  62. {
  63. C_Fish *fish = (C_Fish *)pStruct;
  64. float *out = (float *)pOut;
  65. *out = pData->m_Value.m_Float + fish->m_poolOrigin.y;
  66. }
  67. IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_Fish, DT_CFish, CFish )
  68. RecvPropVector( RECVINFO(m_poolOrigin) ),
  69. RecvPropFloat( RECVINFO_NAME( m_actualPos.x, m_x ), 0, RecvProxy_FishOriginX ),
  70. RecvPropFloat( RECVINFO_NAME( m_actualPos.y, m_y ), 0, RecvProxy_FishOriginY ),
  71. RecvPropFloat( RECVINFO_NAME( m_actualPos.z, m_z ) ),
  72. RecvPropFloat( RECVINFO_NAME( m_actualAngles.y, m_angle ) ),
  73. RecvPropInt( RECVINFO(m_nModelIndex) ),
  74. RecvPropInt( RECVINFO(m_lifeState) ),
  75. RecvPropFloat( RECVINFO(m_waterLevel) ), ///< get this from the server in case we die when slightly out of the water due to error correction
  76. END_RECV_TABLE()
  77. //-----------------------------------------------------------------------------
  78. void C_Fish::Spawn( void )
  79. {
  80. BaseClass::Spawn();
  81. m_angles = QAngle( 0, 0, 0 );
  82. m_actualAngles = m_angles;
  83. m_vel = Vector( 0, 0, 0 );
  84. m_gotUpdate = false;
  85. m_localLifeState = LIFE_ALIVE;
  86. m_buoyancy = RandomFloat( 0.4f, 1.0f );
  87. m_errorHistoryIndex = 0;
  88. m_errorHistoryCount = 0;
  89. m_averageError = 0.0f;
  90. SetNextClientThink( CLIENT_THINK_ALWAYS );
  91. }
  92. //-----------------------------------------------------------------------------
  93. void C_Fish::ClientThink()
  94. {
  95. if (FishDebug.GetBool())
  96. {
  97. debugoverlay->AddLineOverlay( m_pos, m_actualPos, 255, 0, 0, true, 0.1f );
  98. switch( m_localLifeState )
  99. {
  100. case LIFE_DYING:
  101. debugoverlay->AddTextOverlay( m_pos, 0.1f, "DYING" );
  102. break;
  103. case LIFE_DEAD:
  104. debugoverlay->AddTextOverlay( m_pos, 0.1f, "DEAD" );
  105. break;
  106. }
  107. }
  108. float deltaT = gpGlobals->frametime;
  109. // check if we just died
  110. if (m_localLifeState == LIFE_ALIVE && m_lifeState != LIFE_ALIVE)
  111. {
  112. // we have died
  113. m_localLifeState = LIFE_DYING;
  114. m_deathDepth = m_pos.z;
  115. // determine surface float angle
  116. m_deathAngle = RandomFloat( 87.0f, 93.0f ) * ((RandomInt( 0, 100 ) < 50) ? 1.0f : -1.0f);
  117. }
  118. switch( m_localLifeState )
  119. {
  120. case LIFE_DYING:
  121. {
  122. // depth parameter
  123. float t = (m_pos.z - m_deathDepth) / (m_waterLevel - m_deathDepth);
  124. t *= t;
  125. // roll onto side
  126. m_angles.z = m_deathAngle * t;
  127. // float to surface
  128. const float fudge = 2.0f;
  129. if (m_pos.z < m_waterLevel - fudge)
  130. {
  131. m_vel.z += (1.0f - t) * m_buoyancy * deltaT;
  132. }
  133. else
  134. {
  135. m_localLifeState = LIFE_DEAD;
  136. }
  137. break;
  138. }
  139. case LIFE_DEAD:
  140. {
  141. // depth parameter
  142. float t = (m_pos.z - m_deathDepth) / (m_waterLevel - m_deathDepth);
  143. t *= t;
  144. // roll onto side
  145. m_angles.z = m_deathAngle * t;
  146. // keep near water surface
  147. const float sub = 0.5f;
  148. m_vel.z += 10.0f * (m_waterLevel - m_pos.z - sub) * deltaT;
  149. // bob on surface
  150. const float rollAmp = 5.0f;
  151. const float rollFreq = 2.33f;
  152. m_angles.z += rollAmp * sin( rollFreq * (gpGlobals->curtime + 10.0f * entindex()) ) * deltaT;
  153. const float rollAmp2 = 7.0f;
  154. const float rollFreq2 = 4.0f;
  155. m_angles.x += rollAmp2 * sin( rollFreq2 * (gpGlobals->curtime + 10.0f * entindex()) ) * deltaT;
  156. const float bobAmp = 0.75f;
  157. const float bobFreq = 4.0f;
  158. m_vel.z += bobAmp * sin( bobFreq * (gpGlobals->curtime + 10.0f * entindex()) ) * deltaT;
  159. const float bobAmp2 = 0.75f;
  160. const float bobFreq2 = 3.333f;
  161. m_vel.z += bobAmp2 * sin( bobFreq2 * (gpGlobals->curtime + 10.0f * entindex()) ) * deltaT;
  162. // decay movement speed to zero
  163. const float drag = 1.0f;
  164. m_vel.z -= drag * m_vel.z * deltaT;
  165. break;
  166. }
  167. case LIFE_ALIVE:
  168. {
  169. // use server-side Z coordinate directly
  170. m_pos.z = m_actualPos.z;
  171. // use server-side angles
  172. m_angles = m_actualAngles;
  173. // fishy wiggle based on movement
  174. if (!m_wiggleTimer.IsElapsed())
  175. {
  176. float swimPower = 1.0f - (m_wiggleTimer.GetElapsedTime() / m_wiggleTimer.GetCountdownDuration());
  177. const float amp = 6.0f * swimPower;
  178. float wiggle = amp * sin( m_wigglePhase );
  179. m_wigglePhase += m_wiggleRate * deltaT;
  180. // wiggle decay
  181. const float wiggleDecay = 5.0f;
  182. m_wiggleRate -= wiggleDecay * deltaT;
  183. m_angles.y += wiggle;
  184. }
  185. break;
  186. }
  187. }
  188. // compute error between our local position and actual server position
  189. Vector error = m_actualPos - m_pos;
  190. error.z = 0.0f;
  191. float errorLen = error.Length();
  192. if (m_localLifeState == LIFE_ALIVE)
  193. {
  194. // if error is far above average, start swimming
  195. const float wiggleThreshold = 2.0f;
  196. if (errorLen - m_averageError > wiggleThreshold)
  197. {
  198. // if error is large, we must have started swimming
  199. const float swimTime = 5.0f;
  200. m_wiggleTimer.Start( swimTime );
  201. m_wiggleRate = 2.0f * errorLen;
  202. const float maxWiggleRate = 30.0f;
  203. if (m_wiggleRate > maxWiggleRate)
  204. {
  205. m_wiggleRate = maxWiggleRate;
  206. }
  207. }
  208. // update average error
  209. m_errorHistory[ m_errorHistoryIndex++ ] = errorLen;
  210. if (m_errorHistoryIndex >= MAX_ERROR_HISTORY)
  211. {
  212. m_errorHistoryIndex = 0;
  213. m_errorHistoryCount = MAX_ERROR_HISTORY;
  214. }
  215. else if (m_errorHistoryCount < MAX_ERROR_HISTORY)
  216. {
  217. ++m_errorHistoryCount;
  218. }
  219. m_averageError = 0.0f;
  220. if (m_errorHistoryCount)
  221. {
  222. for( int r=0; r<m_errorHistoryCount; ++r )
  223. {
  224. m_averageError += m_errorHistory[r];
  225. }
  226. m_averageError /= (float)m_errorHistoryCount;
  227. }
  228. }
  229. // keep fish motion smooth by correcting towards actual server position
  230. // NOTE: This only tracks XY motion
  231. const float maxError = 20.0f;
  232. float errorT = errorLen / maxError;
  233. if (errorT > 1.0f)
  234. {
  235. errorT = 1.0f;
  236. }
  237. // we want a nonlinear spring force for tracking
  238. errorT *= errorT;
  239. // as fish move faster, their error increases - use a stiffer spring when fast, and a weak one when slow
  240. const float trackRate = 0.0f + errorT * 115.0f;
  241. m_vel.x += trackRate * error.x * deltaT;
  242. m_vel.y += trackRate * error.y * deltaT;
  243. const float trackDrag = 2.0f + errorT * 6.0f;
  244. m_vel.x -= trackDrag * m_vel.x * deltaT;
  245. m_vel.y -= trackDrag * m_vel.y * deltaT;
  246. // euler integration
  247. m_pos += m_vel * deltaT;
  248. SetNetworkOrigin( m_pos );
  249. SetAbsOrigin( m_pos );
  250. SetNetworkAngles( m_angles );
  251. SetAbsAngles( m_angles );
  252. }
  253. //-----------------------------------------------------------------------------
  254. void C_Fish::OnDataChanged( DataUpdateType_t type )
  255. {
  256. //if (!m_gotUpdate)
  257. if (type == DATA_UPDATE_CREATED)
  258. {
  259. // initial update
  260. m_gotUpdate = true;
  261. m_pos = m_actualPos;
  262. m_vel = Vector( 0, 0, 0 );
  263. return;
  264. }
  265. }