From 39ed87570bdb2f86969d4be821c94b722dc71179 Mon Sep 17 00:00:00 2001 From: Joe Ludwig Date: Wed, 26 Jun 2013 15:22:04 -0700 Subject: First version of the SOurce SDK 2013 --- mp/src/game/client/c_fish.cpp | 352 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 352 insertions(+) create mode 100644 mp/src/game/client/c_fish.cpp (limited to 'mp/src/game/client/c_fish.cpp') diff --git a/mp/src/game/client/c_fish.cpp b/mp/src/game/client/c_fish.cpp new file mode 100644 index 00000000..82e5a270 --- /dev/null +++ b/mp/src/game/client/c_fish.cpp @@ -0,0 +1,352 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// c_fish.cpp +// Simple fish client-side logic +// Author: Michael S. Booth, April 2005 + +#include "cbase.h" +#include +#include "engine/ivdebugoverlay.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern float UTIL_WaterLevel( const Vector &position, float minz, float maxz ); + + +ConVar FishDebug( "fish_debug", "0", FCVAR_CHEAT, "Show debug info for fish" ); + + +//----------------------------------------------------------------------------- +/** + * Client-side fish entity + */ +class C_Fish : public C_BaseAnimating +{ +public: + DECLARE_CLASS( C_Fish, C_BaseAnimating ); + DECLARE_CLIENTCLASS(); + + virtual void Spawn( void ); + virtual void ClientThink(); + + virtual void OnDataChanged( DataUpdateType_t type ); + +private: + friend void RecvProxy_FishOriginX( const CRecvProxyData *pData, void *pStruct, void *pOut ); + friend void RecvProxy_FishOriginY( const CRecvProxyData *pData, void *pStruct, void *pOut ); + + Vector m_pos; ///< local position + Vector m_vel; ///< local velocity + QAngle m_angles; ///< local angles + + int m_localLifeState; ///< our version of m_lifeState + + float m_deathDepth; ///< water depth when we died + float m_deathAngle; ///< angle to float at when dead + float m_buoyancy; ///< so each fish floats at a different rate when dead + + CountdownTimer m_wiggleTimer; ///< for simulating swimming motions + float m_wigglePhase; ///< where in the wiggle sinusoid we are + float m_wiggleRate; ///< the speed of our wiggling + + Vector m_actualPos; ///< position from server + QAngle m_actualAngles; ///< angles from server + + Vector m_poolOrigin; + float m_waterLevel; ///< Z coordinate of water surface + + bool m_gotUpdate; ///< true after we have received a network update + + enum { MAX_ERROR_HISTORY = 20 }; + float m_errorHistory[ MAX_ERROR_HISTORY ]; ///< error history samples + int m_errorHistoryIndex; + int m_errorHistoryCount; + float m_averageError; +}; + + +//----------------------------------------------------------------------------- +void RecvProxy_FishOriginX( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + C_Fish *fish = (C_Fish *)pStruct; + float *out = (float *)pOut; + + *out = pData->m_Value.m_Float + fish->m_poolOrigin.x; +} + +void RecvProxy_FishOriginY( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + C_Fish *fish = (C_Fish *)pStruct; + float *out = (float *)pOut; + + *out = pData->m_Value.m_Float + fish->m_poolOrigin.y; +} + + +IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_Fish, DT_CFish, CFish ) + + RecvPropVector( RECVINFO(m_poolOrigin) ), + + RecvPropFloat( RECVINFO_NAME( m_actualPos.x, m_x ), 0, RecvProxy_FishOriginX ), + RecvPropFloat( RECVINFO_NAME( m_actualPos.y, m_y ), 0, RecvProxy_FishOriginY ), + RecvPropFloat( RECVINFO_NAME( m_actualPos.z, m_z ) ), + + RecvPropFloat( RECVINFO_NAME( m_actualAngles.y, m_angle ) ), + + RecvPropInt( RECVINFO(m_nModelIndex) ), + RecvPropInt( RECVINFO(m_lifeState) ), + + RecvPropFloat( RECVINFO(m_waterLevel) ), ///< get this from the server in case we die when slightly out of the water due to error correction + +END_RECV_TABLE() + + + +//----------------------------------------------------------------------------- +void C_Fish::Spawn( void ) +{ + BaseClass::Spawn(); + + m_angles = QAngle( 0, 0, 0 ); + m_actualAngles = m_angles; + + m_vel = Vector( 0, 0, 0 ); + m_gotUpdate = false; + m_localLifeState = LIFE_ALIVE; + m_buoyancy = RandomFloat( 0.4f, 1.0f ); + + m_errorHistoryIndex = 0; + m_errorHistoryCount = 0; + m_averageError = 0.0f; + + SetNextClientThink( CLIENT_THINK_ALWAYS ); +} + + +//----------------------------------------------------------------------------- +void C_Fish::ClientThink() +{ + if (FishDebug.GetBool()) + { + debugoverlay->AddLineOverlay( m_pos, m_actualPos, 255, 0, 0, true, 0.1f ); + switch( m_localLifeState ) + { + case LIFE_DYING: + debugoverlay->AddTextOverlay( m_pos, 0.1f, "DYING" ); + break; + + case LIFE_DEAD: + debugoverlay->AddTextOverlay( m_pos, 0.1f, "DEAD" ); + break; + } + } + + float deltaT = gpGlobals->frametime; + + + // check if we just died + if (m_localLifeState == LIFE_ALIVE && m_lifeState != LIFE_ALIVE) + { + // we have died + m_localLifeState = LIFE_DYING; + + m_deathDepth = m_pos.z; + + // determine surface float angle + m_deathAngle = RandomFloat( 87.0f, 93.0f ) * ((RandomInt( 0, 100 ) < 50) ? 1.0f : -1.0f); + } + + + switch( m_localLifeState ) + { + case LIFE_DYING: + { + // depth parameter + float t = (m_pos.z - m_deathDepth) / (m_waterLevel - m_deathDepth); + t *= t; + + // roll onto side + m_angles.z = m_deathAngle * t; + + // float to surface + const float fudge = 2.0f; + if (m_pos.z < m_waterLevel - fudge) + { + m_vel.z += (1.0f - t) * m_buoyancy * deltaT; + } + else + { + m_localLifeState = LIFE_DEAD; + } + + break; + } + + case LIFE_DEAD: + { + // depth parameter + float t = (m_pos.z - m_deathDepth) / (m_waterLevel - m_deathDepth); + t *= t; + + // roll onto side + m_angles.z = m_deathAngle * t; + + // keep near water surface + const float sub = 0.5f; + m_vel.z += 10.0f * (m_waterLevel - m_pos.z - sub) * deltaT; + + // bob on surface + const float rollAmp = 5.0f; + const float rollFreq = 2.33f; + m_angles.z += rollAmp * sin( rollFreq * (gpGlobals->curtime + 10.0f * entindex()) ) * deltaT; + + const float rollAmp2 = 7.0f; + const float rollFreq2 = 4.0f; + m_angles.x += rollAmp2 * sin( rollFreq2 * (gpGlobals->curtime + 10.0f * entindex()) ) * deltaT; + + const float bobAmp = 0.75f; + const float bobFreq = 4.0f; + m_vel.z += bobAmp * sin( bobFreq * (gpGlobals->curtime + 10.0f * entindex()) ) * deltaT; + + const float bobAmp2 = 0.75f; + const float bobFreq2 = 3.333f; + m_vel.z += bobAmp2 * sin( bobFreq2 * (gpGlobals->curtime + 10.0f * entindex()) ) * deltaT; + + // decay movement speed to zero + const float drag = 1.0f; + m_vel.z -= drag * m_vel.z * deltaT; + + break; + } + + case LIFE_ALIVE: + { + // use server-side Z coordinate directly + m_pos.z = m_actualPos.z; + + // use server-side angles + m_angles = m_actualAngles; + + // fishy wiggle based on movement + if (!m_wiggleTimer.IsElapsed()) + { + float swimPower = 1.0f - (m_wiggleTimer.GetElapsedTime() / m_wiggleTimer.GetCountdownDuration()); + const float amp = 6.0f * swimPower; + float wiggle = amp * sin( m_wigglePhase ); + + m_wigglePhase += m_wiggleRate * deltaT; + + // wiggle decay + const float wiggleDecay = 5.0f; + m_wiggleRate -= wiggleDecay * deltaT; + + m_angles.y += wiggle; + } + + break; + } + } + + // compute error between our local position and actual server position + Vector error = m_actualPos - m_pos; + error.z = 0.0f; + float errorLen = error.Length(); + + if (m_localLifeState == LIFE_ALIVE) + { + // if error is far above average, start swimming + const float wiggleThreshold = 2.0f; + if (errorLen - m_averageError > wiggleThreshold) + { + // if error is large, we must have started swimming + const float swimTime = 5.0f; + m_wiggleTimer.Start( swimTime ); + + m_wiggleRate = 2.0f * errorLen; + + const float maxWiggleRate = 30.0f; + if (m_wiggleRate > maxWiggleRate) + { + m_wiggleRate = maxWiggleRate; + } + } + + // update average error + m_errorHistory[ m_errorHistoryIndex++ ] = errorLen; + if (m_errorHistoryIndex >= MAX_ERROR_HISTORY) + { + m_errorHistoryIndex = 0; + m_errorHistoryCount = MAX_ERROR_HISTORY; + } + else if (m_errorHistoryCount < MAX_ERROR_HISTORY) + { + ++m_errorHistoryCount; + } + + m_averageError = 0.0f; + if (m_errorHistoryCount) + { + for( int r=0; r 1.0f) + { + errorT = 1.0f; + } + + // we want a nonlinear spring force for tracking + errorT *= errorT; + + // as fish move faster, their error increases - use a stiffer spring when fast, and a weak one when slow + const float trackRate = 0.0f + errorT * 115.0f; + m_vel.x += trackRate * error.x * deltaT; + m_vel.y += trackRate * error.y * deltaT; + + const float trackDrag = 2.0f + errorT * 6.0f; + m_vel.x -= trackDrag * m_vel.x * deltaT; + m_vel.y -= trackDrag * m_vel.y * deltaT; + + + // euler integration + m_pos += m_vel * deltaT; + + SetNetworkOrigin( m_pos ); + SetAbsOrigin( m_pos ); + + SetNetworkAngles( m_angles ); + SetAbsAngles( m_angles ); +} + + +//----------------------------------------------------------------------------- +void C_Fish::OnDataChanged( DataUpdateType_t type ) +{ + //if (!m_gotUpdate) + + if (type == DATA_UPDATE_CREATED) + { + // initial update + m_gotUpdate = true; + + m_pos = m_actualPos; + m_vel = Vector( 0, 0, 0 ); + + return; + } +} + -- cgit v1.2.3