diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/tf2/tf_shieldshared.cpp | |
| download | archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip | |
Diffstat (limited to 'game/shared/tf2/tf_shieldshared.cpp')
| -rw-r--r-- | game/shared/tf2/tf_shieldshared.cpp | 579 |
1 files changed, 579 insertions, 0 deletions
diff --git a/game/shared/tf2/tf_shieldshared.cpp b/game/shared/tf2/tf_shieldshared.cpp new file mode 100644 index 0000000..a7370ce --- /dev/null +++ b/game/shared/tf2/tf_shieldshared.cpp @@ -0,0 +1,579 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: The Escort's Shield weapon effect +// +// $Workfile: $ +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#include "tf_shieldshared.h" +#include "edict.h" +#include "mathlib/vmatrix.h" +#include "engine/IEngineTrace.h" + +#ifdef CLIENT_DLL +#include "cdll_client_int.h" +#else +#include "gameinterface.h" +#endif + + + +BEGIN_PREDICTION_DATA_NO_BASE( CShieldEffect ) + + DEFINE_FIELD( m_TestPoint, FIELD_INTEGER ), + DEFINE_FIELD( m_Position, FIELD_VECTOR ), + DEFINE_FIELD( m_Velocity, FIELD_VECTOR ), + DEFINE_FIELD( m_CurrentAngles, FIELD_VECTOR ), + DEFINE_FIELD( m_Theta, FIELD_FLOAT ), + DEFINE_FIELD( m_Phi, FIELD_FLOAT ), + DEFINE_FIELD( m_ThetaVelocity, FIELD_FLOAT ), + DEFINE_FIELD( m_PhiVelocity, FIELD_FLOAT ), + DEFINE_FIELD( m_vecDesiredOrigin, FIELD_VECTOR ), + DEFINE_FIELD( m_angDesiredAngles, FIELD_VECTOR ), + DEFINE_FIELD( m_ShieldTheta, FIELD_FLOAT ), + DEFINE_FIELD( m_ShieldPhi, FIELD_FLOAT ), + +END_PREDICTION_DATA() + + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CShieldEffect::CShieldEffect( ) +{ +} + + +//----------------------------------------------------------------------------- +// compute rest positions of the springs +//----------------------------------------------------------------------------- +void CShieldEffect::ComputeRestPositions() +{ + int i; + + m_vecRenderMins.Init( FLT_MAX, FLT_MAX, FLT_MAX ); + m_vecRenderMaxs.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX ); + + // Set the initial directions and distances (in shield space)... + for ( i = 0; i < SHIELD_NUM_VERTICAL_POINTS; ++i) + { + // Choose phi centered at pi/2 + float phi = (M_PI - m_ShieldPhi) * 0.5f + m_ShieldPhi * + (float)i / (float)(SHIELD_NUM_VERTICAL_POINTS - 1); + + for (int j = 0; j < SHIELD_NUM_HORIZONTAL_POINTS; ++j) + { + // Choose theta centered at pi/2 also (y, or forward axis) + float theta = (M_PI - m_ShieldTheta) * 0.5f + m_ShieldTheta * + (float)j / (float)(SHIELD_NUM_HORIZONTAL_POINTS - 1); + + int idx = i * SHIELD_NUM_HORIZONTAL_POINTS + j; + + m_pFixedDirection[idx].x = cos(theta) * sin(phi); + m_pFixedDirection[idx].y = sin(theta) * sin(phi); + m_pFixedDirection[idx].z = cos(phi); + + m_pFixedDirection[idx] *= m_RestLength; + + VectorMin( m_vecRenderMins, m_pFixedDirection[idx], m_vecRenderMins ); + VectorMax( m_vecRenderMaxs, m_pFixedDirection[idx], m_vecRenderMaxs ); + } + } + + // Compute box for fake volume testing + Vector dist = m_pFixedDirection[0] - m_pFixedDirection[1]; + float l = dist.Length(); // * m_RestLength; + SetShieldPanelSize( Vector( -l * 0.25f, -l * 0.25f, -l * 0.25f), + Vector( l * 0.25f, l * 0.25f, l * 0.25f) ); +} + + +//----------------------------------------------------------------------------- +// Sets orientation + position +//----------------------------------------------------------------------------- +void CShieldEffect::SetDesiredOrigin( const Vector& origin ) +{ + VectorCopy( origin, m_vecDesiredOrigin ); +} + +void CShieldEffect::SetDesiredAngles( const QAngle& angles ) +{ + VectorCopy( angles, m_angDesiredAngles ); +} + +const QAngle& CShieldEffect::GetDesiredAngles() const +{ + return m_angDesiredAngles; +} + + +//----------------------------------------------------------------------------- +// Gets a point... +//----------------------------------------------------------------------------- +const Vector& CShieldEffect::GetPoint( int x, int y ) const +{ + return m_pControlPoint[ x + y * SHIELD_NUM_HORIZONTAL_POINTS ]; +} + +const Vector& CShieldEffect::GetPoint( int i ) const +{ + return m_pControlPoint[ i ]; +} + +Vector& CShieldEffect::GetPoint( int i ) +{ + return m_pControlPoint[ i ]; +} + + +//----------------------------------------------------------------------------- +// Sets the collision group +//----------------------------------------------------------------------------- +void CShieldEffect::SetCollisionGroup( int group ) +{ + m_CollisionGroup = group; +} + + +//----------------------------------------------------------------------------- +// Hooks in active bits... +//----------------------------------------------------------------------------- +void CShieldEffect::SetActiveVertexList( IActiveVertList *pActiveVerts ) +{ + m_pActiveVerts = pActiveVerts; + + // No points are visible initially + for ( int i=0; i < SHIELD_VERTEX_BYTES*8; i++ ) + m_pActiveVerts->SetActiveVertState( i, 0 ); +} + + +//----------------------------------------------------------------------------- +// Is a particular vertex active? +//----------------------------------------------------------------------------- +bool CShieldEffect::IsVertexActive( int x, int y ) const +{ + if ((x < 0) || (y < 0) || (x >= SHIELD_NUM_HORIZONTAL_POINTS) || + (y >= SHIELD_NUM_VERTICAL_POINTS)) + { + return false; + } + + int idx = x + (SHIELD_NUM_HORIZONTAL_POINTS) * y; + return m_pActiveVerts->GetActiveVertState( idx ) != 0; +} + + +//----------------------------------------------------------------------------- +// Is a particular panel active? +//----------------------------------------------------------------------------- +bool CShieldEffect::IsPanelActive( int x, int y ) const +{ + if ((x < 0) || (y < 0) || (x >= SHIELD_HORIZONTAL_PANEL_COUNT) || + (y >= SHIELD_VERTICAL_PANEL_COUNT)) + { + return false; + } + + int idx = x + (SHIELD_HORIZONTAL_PANEL_COUNT) * y; + return m_pActivePanels[idx]; +} + + +//----------------------------------------------------------------------------- +// Recompute whether the panels are active or not +//----------------------------------------------------------------------------- +void CShieldEffect::ComputePanelActivity() +{ + // Check neighbors to see how many squares we've got + for ( int i = 0; i < SHIELD_NUM_HORIZONTAL_POINTS - 1; ++i) + { + for ( int j = 0; j < SHIELD_NUM_VERTICAL_POINTS - 1; ++j) + { + int idx = i + j * (SHIELD_NUM_HORIZONTAL_POINTS - 1); + + // Test the neighbors + m_pActivePanels[idx] = + IsVertexActive( i, j ) || + IsVertexActive( i+1, j ) || + IsVertexActive( i, j+1 ) || + IsVertexActive( i+1, j+1 ); + } + } +} + + +//----------------------------------------------------------------------------- +// Compute vertex activity +//----------------------------------------------------------------------------- + +enum +{ + SHIELD_TESTS_PER_FRAME = 4 +}; + + +void CShieldEffect::ComputeVertexActivity() +{ + int i; + for ( i = 0; i < SHIELD_TESTS_PER_FRAME; ++i ) + { + // Visit points in random order... + int pt = m_PointList[m_TestPoint]; + + // Collision test... + // Check a line that goes farther out than our current point... + // This will let us check for resting contact + trace_t tr; + CTraceFilterWorldOnly traceFilter; + UTIL_TraceHull( m_Position, m_pControlPoint[pt], + m_PanelBoxMin, m_PanelBoxMax, MASK_SOLID_BRUSHONLY, &traceFilter, &tr ); + bool isActive = (!tr.allsolid) && ( (tr.fraction - 1.0f) >= 0.0f ); + + m_pActiveVerts->SetActiveVertState( pt, isActive ); + + if (++m_TestPoint >= SHIELD_NUM_CONTROL_POINTS) + m_TestPoint = 0; + + } + + ComputePanelActivity(); +} + + +//----------------------------------------------------------------------------- +// bounding box for collision +//----------------------------------------------------------------------------- +void CShieldEffect::SetShieldPanelSize( Vector& mins, Vector& maxs ) +{ + m_PanelBoxMin = mins; + m_PanelBoxMax = maxs; +} + + +//----------------------------------------------------------------------------- +// bounding box for collision +//----------------------------------------------------------------------------- +void CShieldEffect::ComputeBounds( Vector& mins, Vector& maxs ) +{ + VectorCopy( m_pControlPoint[0], mins ); + VectorCopy( m_pControlPoint[0], maxs ); + + for (int i = 1; i < SHIELD_NUM_CONTROL_POINTS; ++i) + { + VectorMin( mins, m_pControlPoint[i], mins ); + VectorMax( maxs, m_pControlPoint[i], maxs ); + } + + // Bounds are in local coords + mins -= m_Position; + maxs -= m_Position; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CShieldEffect::Precache( void ) +{ + m_RestLength = 200.0; + + m_SpringConstant = 30.0f; + m_DampConstant = 4.0f; + m_ViscousDrag = 4.0f; + m_Mass = 1.0f; + + m_AngularSpringConstant = 2.0f; + m_AngularViscousDrag = 4.0f; + + SetThetaPhi( SHIELD_INITIAL_THETA, SHIELD_INITIAL_PHI ); +} + + +//----------------------------------------------------------------------------- +// Compute orientation matrix: +//----------------------------------------------------------------------------- +void CShieldEffect::ComputeOrientationMatrix() +{ + // Generate the orientation matrix from theta and phi... + // X = forward direction, Y - left direction + Vector forward, left, up; + forward.x = cos(m_Theta) * sin(m_Phi); + forward.y = sin(m_Theta) * sin(m_Phi); + forward.z = cos(m_Phi); + + left.x = -forward.y; + left.y = forward.x; + left.z = 0; + + if ( VectorNormalize(left) == 0.0f ) + left.Init( 0.0f, 1.0f, 0.0f ); + + CrossProduct( forward, left, up ); + + m_Orientation.SetBasisVectors( forward, left, up ); + + // Turn the current matrix into angles... + MatrixToAngles( m_Orientation, m_CurrentAngles ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CShieldEffect::Spawn( const Vector& currentPosition, const QAngle& currentAngles ) +{ + Precache(); + + VectorCopy( currentPosition, m_Position ); + m_Velocity.Init(); + + Vector forward; + AngleVectors( currentAngles, &forward, 0, 0 ); + m_Phi = acos( forward.z ); + m_Theta = atan2( forward.y, forward.x ); + m_PhiVelocity = 0.0f; + m_ThetaVelocity = 0.0f; + ComputeOrientationMatrix(); + VectorCopy( currentAngles, m_CurrentAngles ); + VectorCopy( currentAngles, m_angDesiredAngles ); + + // No points are visible initially + memset( m_pActivePanels, 0, SHIELD_PANELS_COUNT ); + + m_TestPoint = 0; + + // Choose random order to visit shield verts + int i; + for ( i = 0; i < SHIELD_NUM_CONTROL_POINTS; ++i ) + { + m_PointList[i] = i; + } + + for ( i = 0; i < SHIELD_NUM_CONTROL_POINTS; ++i ) + { + int j = rand() % SHIELD_NUM_CONTROL_POINTS; + swap( m_PointList[i], m_PointList[j] ); + } +} + + +//----------------------------------------------------------------------------- +// Computes the opacity.... +//----------------------------------------------------------------------------- +float CShieldEffect::ComputeOpacity( const Vector& pt, const Vector& center ) const +{ + float dist = pt.DistTo( center ) / m_RestLength; + if (dist > 1.0) + dist = 1.0f; + return 32 + (1.0 - dist) * 192; +} + + +//----------------------------------------------------------------------------- +// Computes control points +//----------------------------------------------------------------------------- +void CShieldEffect::ComputeControlPoints() +{ + Vector forward, right, up; + AngleVectors(m_CurrentAngles, &forward, &right, &up); + + for ( int i = 0; i < SHIELD_NUM_CONTROL_POINTS; ++i ) + { + // Compute the world space position... + VectorCopy( m_Position, m_pControlPoint[i] ); + m_pControlPoint[i] += right * m_pFixedDirection[i].x; + m_pControlPoint[i] += up * m_pFixedDirection[i].z; + m_pControlPoint[i] += forward * m_pFixedDirection[i].y; + } +} + + +//----------------------------------------------------------------------------- +// Gets the frustum size +//----------------------------------------------------------------------------- +void CShieldEffect::GetPanelSize( Vector& mins, Vector& maxs ) const +{ + VectorCopy( m_PanelBoxMin, mins ); + VectorCopy( m_PanelBoxMax, maxs ); +} + + +void CShieldEffect::SetAngularSpringConstant( float flConstant ) +{ + m_AngularSpringConstant = flConstant; +} + + +//----------------------------------------------------------------------------- +// Set the shield theta & phi +//----------------------------------------------------------------------------- +void CShieldEffect::SetThetaPhi( float flTheta, float flPhi ) +{ + m_ShieldTheta = M_PI * flTheta / 180.0f; + m_ShieldPhi = M_PI * flPhi / 180.0f; + + // Computes the rest positions + ComputeRestPositions(); +} + + +//----------------------------------------------------------------------------- +// The current position (computed by Simulate on the server) +//----------------------------------------------------------------------------- +const Vector& CShieldEffect::GetCurrentPosition() +{ + return m_Position; +} + +void CShieldEffect::SetCurrentPosition( const Vector& pos ) +{ + m_Position = pos; +} + +void CShieldEffect::SetCurrentAngles( const QAngle& angles ) +{ + VectorCopy( angles, m_CurrentAngles ); +} + + +//----------------------------------------------------------------------------- +// Simulate the center of mass +//----------------------------------------------------------------------------- +void CShieldEffect::SimulateTranslation( float dt ) +{ + // Hook's law for a damped spring: + // got two particles, a and b with positions xa and xb and velocities va and vb + // and l = xa - xb + // fa = -( ks * (|l| - r) + kd * (va - vb) dot (l) / |l|) * l/|l| + Vector dx, force; + + // Case where we're connected to a control point + dx = m_Position - m_vecDesiredOrigin; + + // rest condition + float length = dx.Length(); + float speedSq = m_Velocity.LengthSqr(); + if ((length < 1e-3) && (speedSq < 1e-6)) + return; + + // Compute force + if (length > 1e-3) + dx /= length; + else + dx.Init( 0, 0, 0 ); + + float springfactor = m_SpringConstant * length; + float dampfactor = m_DampConstant * DotProduct( m_Velocity, dx ); + force = dx * -( springfactor + dampfactor ); + + assert( force.IsValid( ) ); + Vector drag = m_Velocity * m_ViscousDrag; + force -= drag; + + // Update position and velocity + m_Position += m_Velocity * dt; + m_Velocity += force * dt / m_Mass; + + assert( m_Velocity.IsValid( ) ); + + // clamp for stability + if (speedSq > 1e6) + { + m_Velocity *= 1e3 / sqrt(speedSq); + } +} + + +void CShieldEffect::SimulateRotation( float dt, const Vector& forward ) +{ + // Here's a torsional spring for the angular component... + // A little tricky: We need to actually think about 2 torsional springs, + // one in thetha (x-y plane), and one in phi (z-plane) + + float phi2 = acos( forward.z ); + float dPhi = m_Phi - phi2; + + float theta2 = atan2( forward.y, forward.x ); + float dTheta = (m_Theta - theta2); + if (dTheta > M_PI) + dTheta -= 2 * M_PI; + else if (dTheta < -M_PI) + dTheta += 2 * M_PI; + + // rest condition... + if ((fabs(dTheta) < 1e-3) && (fabs(m_ThetaVelocity) < 1e-6) && + (fabs(dPhi) < 1e-3) && (fabs(m_PhiVelocity) < 1e-6)) + { + return; + } + + float springfactor = m_AngularSpringConstant * dTheta; + float torqueTheta = -springfactor; // + dampfactor); + torqueTheta -= m_ThetaVelocity * m_AngularViscousDrag; + + springfactor = m_AngularSpringConstant * dPhi; + float torqueTPhi = -springfactor; // + dampfactor); + torqueTPhi -= m_PhiVelocity * m_AngularViscousDrag; + + // Update position and velocity + m_Theta += m_ThetaVelocity * dt; + m_ThetaVelocity += torqueTheta * dt; + m_Phi += m_PhiVelocity * dt; + m_PhiVelocity += torqueTPhi * dt; + + // clamp for stability + if (fabs(m_ThetaVelocity) > 1e2) + { + m_ThetaVelocity *= 1e2 / m_ThetaVelocity; + } + if (fabs(m_PhiVelocity) > 1e2) + { + m_PhiVelocity *= 1e2 / m_PhiVelocity; + } + + ComputeOrientationMatrix(); +} + + +//----------------------------------------------------------------------------- +// Update the shield position: +//----------------------------------------------------------------------------- +void CShieldEffect::Simulate( float dt ) +{ + // We're gonna basically assume a spring connected to the center control point + Vector forward; + AngleVectors(m_angDesiredAngles, &forward, 0, 0); + + // We've got two springs: a spring connected to the origin + // and a torsional spring connected to the view direction. + + // Stiff spring, subdivide time.... + dt /= SHIELD_TIME_SUBVISIBIONS; + for (int i = 0; i < SHIELD_TIME_SUBVISIBIONS; ++i) + { + SimulateTranslation( dt ); + SimulateRotation( dt, forward ); + } + + ComputeControlPoints(); +} + + +//----------------------------------------------------------------------------- +// Determines shield obstructions +//----------------------------------------------------------------------------- +static inline bool IsPointValid( bool* pActivePoints, int i, int j ) +{ + // Here's the control point we're checking + int idx = j * SHIELD_NUM_HORIZONTAL_POINTS + i; + + return pActivePoints[idx]; +} + + + |