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.
580 lines
16 KiB
580 lines
16 KiB
5 years ago
|
//========= 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];
|
||
|
}
|
||
|
|
||
|
|
||
|
|