Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.
 
 
 
 
 
 

850 lines
24 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: The Escort's Shield weapon
//
// $Revision: $
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "in_buttons.h"
#include "tf_shieldshared.h"
#include "tf_shareddefs.h"
#include "baseentity_shared.h"
#if defined( CLIENT_DLL )
#include "c_shield.h"
#else
#include "tf_shield.h"
#include "gamerules.h"
#endif
#if defined( CLIENT_DLL )
#define CShieldMobile C_ShieldMobile
#define CShield C_Shield
#endif
//-----------------------------------------------------------------------------
// ConVars
//-----------------------------------------------------------------------------
ConVar shield_mobile_power( "shield_mobile_power","30", FCVAR_REPLICATED, "Max power level of a escort's mobile projected shield." );
ConVar shield_mobile_recharge_delay( "shield_mobile_recharge_delay","0.1", FCVAR_REPLICATED, "Time after taking damage before mobile projected shields begin to recharge." );
ConVar shield_mobile_recharge_amount( "shield_mobile_recharge_amount","2", FCVAR_REPLICATED, "Power recharged each recharge tick for mobile projected shields." );
ConVar shield_mobile_recharge_time( "shield_mobile_recharge_time","0.5", FCVAR_REPLICATED, "Time between each recharge tick for mobile projected shields." );
#define EMP_WAVE_AMPLITUDE 8.0f
//-----------------------------------------------------------------------------
// Mobile version of the shield
//-----------------------------------------------------------------------------
class CShieldMobile;
class CShieldMobileActiveVertList : public IActiveVertList
{
public:
void Init( CShieldMobile *pShield );
// IActiveVertList overrides.
public:
virtual int GetActiveVertState( int iVert );
virtual void SetActiveVertState( int iVert, int bOn );
private:
CShieldMobile *m_pShield;
};
//-----------------------------------------------------------------------------
// Mobile version of the shield
//-----------------------------------------------------------------------------
class CShieldMobile : public CShield, public IEntityEnumerator
{
DECLARE_CLASS( CShieldMobile, CShield );
public:
DECLARE_NETWORKCLASS();
DECLARE_PREDICTABLE();
friend class CShieldMobileActiveVertList;
CShieldMobile();
#ifndef CLIENT_DLL
DECLARE_DATADESC();
#endif
public:
void Spawn( void );
void Precache( void );
void ShieldThink( void );
virtual void ClientThink();
virtual void SetAngularSpringConstant( float flConstant );
virtual void SetFrontDistance( float flDistance );
virtual void ComputeWorldSpaceSurroundingBox( Vector *pWorldMins, Vector *pWorldMaxs );
virtual void SetAttachmentIndex( int nAttachmentIndex );
virtual void SetEMPed( bool isEmped );
virtual void SetAlwaysOrient( bool bOrient );
virtual bool IsAlwaysOrienting( );
virtual int Width();
virtual int Height();
virtual bool IsPanelActive( int x, int y );
virtual const Vector& GetPoint( int x, int y );
virtual void SetCenterAngles( const QAngle& angles );
virtual void SetThetaPhi( float flTheta, float flPhi );
virtual void GetRenderBounds( Vector& mins, Vector& maxs );
// All predicted weapons need to implement and return true
virtual bool IsPredicted( void ) const
{
return true;
}
public:
#ifdef CLIENT_DLL
virtual void OnDataChanged( DataUpdateType_t updateType );
virtual void GetBounds( Vector& mins, Vector& maxs );
virtual void AddEntity( );
virtual void GetShieldData( const Vector** ppVerts, float* pOpacity, float* pBlend );
virtual bool ShouldPredict( void )
{
if ( GetOwnerEntity() == C_BasePlayer::GetLocalPlayer() )
return true;
return BaseClass::ShouldPredict();
}
#endif
public:
// Inherited from IEntityEnumerator
virtual bool EnumEntity( IHandleEntity *pHandleEntity );
private:
// Teleport!
void OnTeleported( );
void SimulateShield( void );
private:
struct SweepContext_t
{
SweepContext_t( const CBaseEntity *passentity, int collisionGroup ) :
m_Filter( passentity, collisionGroup ) {}
CTraceFilterSimple m_Filter;
Vector m_vecStartDelta;
Vector m_vecEndDelta;
};
enum
{
NUM_SUBDIVISIONS = 21,
};
enum
{
SHIELD_ORIENT_TO_OWNER = 0x2
};
private:
CShieldMobile( const CShieldMobile & );
void ComputeBoundingBox( void );
void DetermineObstructions( );
private:
#ifdef CLIENT_DLL
// Is a particular panel an edge?
bool IsVertexValid( float s, float t ) const;
void PreRender( );
#endif
private:
CShieldMobileActiveVertList m_VertList;
QAngle m_tmpAngLockedAngles;
// Bitfield indicating which vertices are active
CShieldEffect m_ShieldEffect;
CNetworkArray( unsigned char, m_pVertsActive, SHIELD_NUM_CONTROL_POINTS >> 3 );
CNetworkVar( unsigned char, m_ShieldState );
CNetworkVar( float, m_flFrontDistance );
CNetworkVar( QAngle, m_angLockedAngles );
SweepContext_t *m_pEnumCtx;
// This is the width + height of the shield, not the current theta, phi
CNetworkVar( float, m_flShieldTheta );
CNetworkVar( float, m_flShieldPhi );
CNetworkVar( float, m_flSpringConstant );
CNetworkVar( int, m_nAttachmentIndex );
};
//=============================================================================
// Shield effect
//=============================================================================
#ifndef CLIENT_DLL
BEGIN_DATADESC( CShieldMobile )
DEFINE_THINKFUNC( ShieldThink ),
END_DATADESC()
#endif
LINK_ENTITY_TO_CLASS( shield_mobile, CShieldMobile );
IMPLEMENT_NETWORKCLASS_ALIASED( ShieldMobile, DT_ShieldMobile );
// -------------------------------------------------------------------------------- //
// This data only gets sent to clients that ARE this player entity.
// -------------------------------------------------------------------------------- //
BEGIN_NETWORK_TABLE(CShieldMobile, DT_ShieldMobile)
#if !defined( CLIENT_DLL )
SendPropInt (SENDINFO(m_ShieldState), 2, SPROP_UNSIGNED ),
SendPropArray(
SendPropInt( SENDINFO_ARRAY(m_pVertsActive), 8, SPROP_UNSIGNED),
m_pVertsActive),
SendPropFloat( SENDINFO(m_flFrontDistance), 0, SPROP_NOSCALE ),
SendPropFloat( SENDINFO(m_flShieldTheta), 0, SPROP_NOSCALE ),
SendPropFloat( SENDINFO(m_flShieldPhi), 0, SPROP_NOSCALE ),
SendPropFloat( SENDINFO(m_flSpringConstant), 0, SPROP_NOSCALE ),
SendPropQAngles( SENDINFO(m_angLockedAngles), 9 ),
SendPropInt (SENDINFO(m_nAttachmentIndex), 10, SPROP_UNSIGNED ),
// Don't bother sending these, they are totally controlled by the think function
SendPropExclude( "DT_BaseEntity", "m_vecOrigin" ),
SendPropExclude( "DT_BaseEntity", "m_angAbsRotation[0]" ),
SendPropExclude( "DT_BaseEntity", "m_angAbsRotation[1]" ),
SendPropExclude( "DT_BaseEntity", "m_angAbsRotation[2]" ),
#else
RecvPropInt( RECVINFO(m_ShieldState) ),
RecvPropArray(
RecvPropInt( RECVINFO(m_pVertsActive[0])),
m_pVertsActive
),
RecvPropFloat( RECVINFO(m_flFrontDistance) ),
RecvPropFloat( RECVINFO(m_flShieldTheta) ),
RecvPropFloat( RECVINFO(m_flShieldPhi) ),
RecvPropFloat( RECVINFO(m_flSpringConstant) ),
RecvPropQAngles( RECVINFO( m_angLockedAngles ) ),
RecvPropInt (RECVINFO(m_nAttachmentIndex) ),
#endif
END_NETWORK_TABLE()
BEGIN_PREDICTION_DATA( CShieldMobile )
DEFINE_PRED_FIELD( m_ShieldState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_flFrontDistance, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_angLockedAngles, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_TYPEDESCRIPTION( m_ShieldEffect, CShieldEffect ),
DEFINE_FIELD( m_nNextThinkTick, FIELD_INTEGER ),
DEFINE_FIELD( m_tmpAngLockedAngles, FIELD_VECTOR ),
// FIXME: How can I make this work now that I have an embedded collision property?
// DEFINE_PRED_FIELD( m_vecMins, FIELD_VECTOR, FTYPEDESC_INSENDTABLE | FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
// DEFINE_PRED_FIELD( m_vecMaxs, FIELD_VECTOR, FTYPEDESC_INSENDTABLE | FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
#ifdef CLIENT_DLL
DEFINE_PRED_FIELD( m_vecNetworkOrigin, FIELD_VECTOR, FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
DEFINE_PRED_FIELD( m_angNetworkAngles, FIELD_VECTOR, FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
#endif
END_PREDICTION_DATA()
//-----------------------------------------------------------------------------
// CShieldMobileActiveVertList functions
//-----------------------------------------------------------------------------
void CShieldMobileActiveVertList::Init( CShieldMobile *pShield )
{
m_pShield = pShield;
}
int CShieldMobileActiveVertList::GetActiveVertState( int iVert )
{
return m_pShield->m_pVertsActive[iVert>>3] & (1 << (iVert & 7));
}
void CShieldMobileActiveVertList::SetActiveVertState( int iVert, int bOn )
{
unsigned char val;
if ( bOn )
val = m_pShield->m_pVertsActive[iVert>>3] | (1 << (iVert & 7));
else
val = m_pShield->m_pVertsActive[iVert>>3] & ~(1 << (iVert & 7));
m_pShield->m_pVertsActive.Set( iVert>>3, val );
}
//-----------------------------------------------------------------------------
// constructor
//-----------------------------------------------------------------------------
CShieldMobile::CShieldMobile()
{
SetPredictionEligible( true );
m_VertList.Init( this );
m_flFrontDistance = 0;
SetAngularSpringConstant( 2.0f );
SetThetaPhi( SHIELD_INITIAL_THETA, SHIELD_INITIAL_PHI );
m_nAttachmentIndex = 0;
#ifdef CLIENT_DLL
InitShield( SHIELD_NUM_HORIZONTAL_POINTS, SHIELD_NUM_VERTICAL_POINTS, NUM_SUBDIVISIONS );
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CShieldMobile::Precache( void )
{
m_ShieldEffect.SetActiveVertexList( &m_VertList );
m_ShieldEffect.SetCollisionGroup( TFCOLLISION_GROUP_SHIELD );
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CShieldMobile::Spawn( void )
{
Precache();
BaseClass::Spawn();
AddSolidFlags( FSOLID_FORCE_WORLD_ALIGNED );
// Assert( GetOwnerEntity() );
m_angLockedAngles.Set( vec3_angle );
m_tmpAngLockedAngles = vec3_angle;
m_ShieldEffect.Spawn(GetAbsOrigin(), GetAbsAngles());
m_ShieldState = 0;
SetAlwaysOrient( true );
// All movement occurs during think
SetMoveType( MOVETYPE_NONE );
SetThink( ShieldThink );
SetNextThink( gpGlobals->curtime + 0.01f );
#ifdef CLIENT_DLL
// This goofiness is required so that non-predicted entities work
SetNextClientThink( CLIENT_THINK_ALWAYS );
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CShieldMobile::SetAlwaysOrient( bool bOrient )
{
if (bOrient)
{
m_ShieldState.Set( m_ShieldState.Get() | SHIELD_ORIENT_TO_OWNER );
}
else
{
m_angLockedAngles.Set( m_tmpAngLockedAngles );
m_ShieldState.Set( m_ShieldState.Get() & (~SHIELD_ORIENT_TO_OWNER) );
}
}
int CShieldMobile::Width()
{
return SHIELD_NUM_HORIZONTAL_POINTS;
}
int CShieldMobile::Height()
{
return SHIELD_NUM_VERTICAL_POINTS;
}
const Vector& CShieldMobile::GetPoint( int x, int y )
{
return m_ShieldEffect.GetPoint( x, y );
}
void CShieldMobile::SetAttachmentIndex( int nAttachmentIndex )
{
m_nAttachmentIndex = nAttachmentIndex;
}
//-----------------------------------------------------------------------------
// Returns the render bounds
//-----------------------------------------------------------------------------
void CShieldMobile::GetRenderBounds( Vector& mins, Vector& maxs )
{
mins = m_ShieldEffect.GetRenderMins();
maxs = m_ShieldEffect.GetRenderMaxs();
}
//-----------------------------------------------------------------------------
// Return true if the panel is active
//-----------------------------------------------------------------------------
bool CShieldMobile::IsPanelActive( int x, int y )
{
return m_ShieldEffect.IsPanelActive(x, y);
}
//-----------------------------------------------------------------------------
// Called when the shield is EMPed
//-----------------------------------------------------------------------------
void CShieldMobile::SetEMPed( bool isEmped )
{
CShield::SetEMPed(isEmped);
if (IsEMPed())
m_ShieldState |= SHIELD_MOBILE_EMP;
else
m_ShieldState &= ~SHIELD_MOBILE_EMP;
}
//-----------------------------------------------------------------------------
// Set the shield angles
//-----------------------------------------------------------------------------
void CShieldMobile::SetCenterAngles( const QAngle& angles )
{
// The tmp ang locked angles is simply there to prevent unnecessary network traffic
m_tmpAngLockedAngles = angles;
if ( ( m_ShieldState.Get() & SHIELD_ORIENT_TO_OWNER ) == 0 )
{
m_angLockedAngles.Set( angles );
}
}
bool CShieldMobile::IsAlwaysOrienting( )
{
return ( m_ShieldState.Get() & SHIELD_ORIENT_TO_OWNER ) != 0;
}
void CShieldMobile::SetAngularSpringConstant( float flConstant )
{
m_flSpringConstant = flConstant;
m_ShieldEffect.SetAngularSpringConstant( flConstant );
}
void CShieldMobile::SetFrontDistance( float flDistance )
{
m_flFrontDistance = flDistance;
}
void CShieldMobile::SetThetaPhi( float flTheta, float flPhi )
{
// This sets the bounds of the shield; how tall + wide is it?
m_flShieldTheta = flTheta;
m_flShieldPhi = flPhi;
m_ShieldEffect.SetThetaPhi(flTheta, flPhi);
}
//-----------------------------------------------------------------------------
// Computes the shield bounding box
//-----------------------------------------------------------------------------
void CShieldMobile::ComputeBoundingBox( void )
{
Vector mins, maxs;
m_ShieldEffect.ComputeBounds(mins, maxs);
SetCollisionBounds( mins, maxs );
}
//-----------------------------------------------------------------------------
// Compute world axis-aligned bounding box
//-----------------------------------------------------------------------------
void CShieldMobile::ComputeWorldSpaceSurroundingBox( Vector *pWorldMins, Vector *pWorldMaxs )
{
// We don't use USE_SPECIFIED_BOUNDS because that would generate a ton of network traffic
VectorAdd( CollisionProp()->GetCollisionOrigin(), CollisionProp()->OBBMins(), *pWorldMins );
VectorAdd( CollisionProp()->GetCollisionOrigin(), CollisionProp()->OBBMaxs(), *pWorldMaxs );
}
//-----------------------------------------------------------------------------
// Determines shield obstructions
//-----------------------------------------------------------------------------
void CShieldMobile::DetermineObstructions( )
{
m_ShieldEffect.ComputeVertexActivity();
}
//-----------------------------------------------------------------------------
// Called by the enumerator call in ShieldThink
//-----------------------------------------------------------------------------
bool CShieldMobile::EnumEntity( IHandleEntity *pHandleEntity )
{
#ifdef CLIENT_DLL
CBaseEntity *pOther = cl_entitylist->GetBaseEntityFromHandle( pHandleEntity->GetRefEHandle() );
#else
CBaseEntity *pOther = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() );
#endif
if (!pOther)
return true;
// Blow off non-solid things
if ( !::IsSolid(pOther->GetSolid(), pOther->GetSolidFlags()) )
return true;
// No model, blow it off
if ( !pOther->GetModelIndex() )
return true;
// Blow off point-sized things....
if ( pOther->IsPointSized() )
return true;
// Don't bother if we shouldn't be colliding with this guy...
if (!m_pEnumCtx->m_Filter.ShouldHitEntity( pOther, MASK_SOLID ))
return true;
// The shield is in its final position, so we're gonna have to determine the
// point of collision by working in the space of the final position....
// We do this by moving the obstruction by the relative movement amount...
Vector vecObsMins, vecObsMaxs, vecObsCenter;
pOther->CollisionProp()->WorldSpaceAABB( &vecObsMins, &vecObsMaxs );
vecObsCenter = (vecObsMins + vecObsMaxs) * 0.5f;
vecObsMins -= vecObsCenter;
vecObsMaxs -= vecObsCenter;
Vector vecStart, vecEnd;
VectorAdd( vecObsCenter, m_pEnumCtx->m_vecStartDelta, vecStart );
VectorAdd( vecStart, m_pEnumCtx->m_vecEndDelta, vecEnd );
Ray_t ray;
ray.Init( vecStart, vecEnd, vecObsMins, vecObsMaxs );
trace_t tr;
if (TestCollision( ray, pOther->PhysicsSolidMaskForEntity(), tr ))
{
// Ok, we got a collision. Let's indicate it happened...
// At the moment, we'll report the collision point as being on the
// surface of the shield in its final position, which is kind of bogus...
pOther->PhysicsImpact( this, tr );
}
return true;
}
//-----------------------------------------------------------------------------
// Update the shield position:
//-----------------------------------------------------------------------------
void CShieldMobile::SimulateShield( void )
{
CBaseEntity *owner = GetOwnerEntity();
Vector origin;
if ( owner )
{
if ( m_ShieldState & SHIELD_ORIENT_TO_OWNER )
{
if ( owner->IsPlayer() )
{
m_ShieldEffect.SetDesiredAngles( owner->EyeAngles() );
}
else
{
m_ShieldEffect.SetDesiredAngles( owner->GetAbsAngles() );
}
}
else
{
m_ShieldEffect.SetDesiredAngles( m_angLockedAngles );
}
if ( m_nAttachmentIndex == 0 )
{
origin = owner->EyePosition();
}
else
{
QAngle angles;
CBaseAnimating *pAnim = dynamic_cast<CBaseAnimating*>( owner );
if (pAnim)
{
pAnim->GetAttachment( m_nAttachmentIndex, origin, angles );
}
else
{
origin = owner->EyePosition();
}
}
if ( m_flFrontDistance )
{
Vector vForward;
AngleVectors( m_ShieldEffect.GetDesiredAngles(), &vForward );
origin += vForward * m_flFrontDistance;
}
}
else
{
Assert( 0 );
origin = vec3_origin;
}
// We pretty much always need to recompute this
CollisionProp()->MarkSurroundingBoundsDirty();
Vector vecOldOrigin = m_ShieldEffect.GetCurrentPosition();
Vector vecDelta;
VectorSubtract( origin, vecOldOrigin, vecDelta );
float flMaxDist = 100 + m_flFrontDistance;
if (vecDelta.LengthSqr() > flMaxDist * flMaxDist )
{
OnTeleported();
return;
}
m_ShieldEffect.SetDesiredOrigin( origin );
m_ShieldEffect.Simulate(gpGlobals->frametime);
DetermineObstructions();
SetAbsOrigin( m_ShieldEffect.GetCurrentPosition() );
SetAbsAngles( m_ShieldEffect.GetCurrentAngles() );
#ifdef CLIENT_DLL
// Necessary because we exclude the network origin
SetNetworkOrigin( m_ShieldEffect.GetCurrentPosition() );
SetNetworkAngles( m_ShieldEffect.GetCurrentAngles() );
#endif
// Compute a composite bounding box surrounding the initial + new positions..
Vector vecCompositeMins = WorldAlignMins() + vecOldOrigin;
Vector vecCompositeMaxs = WorldAlignMaxs() + vecOldOrigin;
ComputeBoundingBox();
// Sweep the shield through the world + touch things it hits...
SweepContext_t ctx( this, GetCollisionGroup() );
VectorSubtract( GetAbsOrigin(), vecOldOrigin, ctx.m_vecStartDelta );
if (ctx.m_vecStartDelta != vec3_origin)
{
// FIXME: Brutal hack; needed because IntersectRayWithTriangle misses stuff
// especially with short rays; I'm not sure what to do about this.
// This basically simulates a shield thickness of 15 units
ctx.m_vecEndDelta = ctx.m_vecStartDelta;
VectorNormalize( ctx.m_vecEndDelta );
ctx.m_vecEndDelta *= -15.0f;
Vector vecNewMins = WorldAlignMins() + GetAbsOrigin();
Vector vecNewMaxs = WorldAlignMaxs() + GetAbsOrigin();
VectorMin( vecCompositeMins, vecNewMins, vecCompositeMins );
VectorMax( vecCompositeMaxs, vecNewMaxs, vecCompositeMaxs );
m_pEnumCtx = &ctx;
enginetrace->EnumerateEntities( vecCompositeMins, vecCompositeMaxs, this );
}
}
//-----------------------------------------------------------------------------
// Update the shield position:
//-----------------------------------------------------------------------------
void CShieldMobile::ShieldThink( void )
{
SimulateShield();
#ifdef CLIENT_DLL
m_ShieldEffect.ComputeControlPoints();
m_ShieldEffect.ComputePanelActivity();
#endif
SetNextThink( gpGlobals->curtime + 0.01f );
}
void CShieldMobile::ClientThink()
{
#ifdef CLIENT_DLL
if ( GetPredictable() )
return;
SimulateShield();
m_ShieldEffect.ComputeControlPoints();
m_ShieldEffect.ComputePanelActivity();
#endif
}
//-----------------------------------------------------------------------------
// Teleport!
//-----------------------------------------------------------------------------
void CShieldMobile::OnTeleported( )
{
CBaseEntity *owner = GetOwnerEntity();
if (!owner)
return;
m_ShieldEffect.SetCurrentAngles( owner->GetAbsAngles() );
Vector origin;
origin = owner->EyePosition();
if ( m_flFrontDistance )
{
Vector vForward;
AngleVectors( m_ShieldEffect.GetCurrentAngles(), &vForward );
origin += vForward * m_flFrontDistance;
}
m_ShieldEffect.SetCurrentPosition( origin );
}
#ifdef CLIENT_DLL
//-----------------------------------------------------------------------------
// Get this after the data changes
//-----------------------------------------------------------------------------
void CShieldMobile::OnDataChanged( DataUpdateType_t updateType )
{
BaseClass::OnDataChanged( updateType );
m_ShieldEffect.SetThetaPhi( m_flShieldTheta, m_flShieldPhi );
m_ShieldEffect.SetAngularSpringConstant( m_flSpringConstant );
}
//-----------------------------------------------------------------------------
// A little pre-render processing
//-----------------------------------------------------------------------------
void C_ShieldMobile::PreRender( )
{
if (m_ShieldState & SHIELD_MOBILE_EMP)
{
// Decay fade if we've been EMPed or if we're inactive
if (m_FadeValue > 0.0f)
{
m_FadeValue -= gpGlobals->frametime / SHIELD_EMP_FADE_TIME;
if (m_FadeValue < 0.0f)
{
m_FadeValue = 0.0f;
// Reset the shield to un-wobbled state
m_ShieldEffect.ComputeControlPoints();
}
else
{
Vector dir;
AngleVectors( m_ShieldEffect.GetCurrentAngles(), & dir, 0, 0 );
// Futz with the control points if we've been EMPed
for (int i = 0; i < SHIELD_NUM_CONTROL_POINTS; ++i)
{
// Get the direction for the point
float factor = -EMP_WAVE_AMPLITUDE * sin( i * M_PI * 0.5f + gpGlobals->curtime * M_PI / SHIELD_EMP_WOBBLE_TIME );
m_ShieldEffect.GetPoint(i) += dir * factor;
}
}
}
}
else
{
// Fade back in, no longer EMPed
if (m_FadeValue < 1.0f)
{
m_FadeValue += gpGlobals->frametime / SHIELD_EMP_FADE_TIME;
if (m_FadeValue >= 1.0f)
{
m_FadeValue = 1.0f;
}
}
}
}
void CShieldMobile::AddEntity( )
{
BaseClass::AddEntity( );
PreRender();
}
//-----------------------------------------------------------------------------
// Bounds computation
//-----------------------------------------------------------------------------
void CShieldMobile::GetBounds( Vector& mins, Vector& maxs )
{
m_ShieldEffect.ComputeBounds( mins, maxs );
}
//-----------------------------------------------------------------------------
// Gets at the control point data; who knows how it was made?
//-----------------------------------------------------------------------------
void CShieldMobile::GetShieldData( const Vector** ppVerts, float* pOpacity, float* pBlend )
{
for ( int i = 0; i < SHIELD_NUM_CONTROL_POINTS; ++i )
{
ppVerts[i] = &m_ShieldEffect.GetControlPoint(i);
if ( m_pVertsActive[i >> 3] & (1 << (i & 0x7)) )
{
pOpacity[i] = m_ShieldEffect.ComputeOpacity( *ppVerts[i], GetAbsOrigin() );
pBlend[i] = 1.0f;
}
else
{
pOpacity[i] = 192.0f;
pBlend[i] = 0.0f;
}
}
}
#endif // CLIENT_DLL
#ifndef CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: Create a mobile version of the shield
//-----------------------------------------------------------------------------
CShield *CreateMobileShield( CBaseEntity *owner, float flFrontDistance )
{
CShieldMobile *pShield = (CShieldMobile*)CreateEntityByName("shield_mobile");
pShield->SetOwnerEntity( owner );
pShield->SetLocalAngles( owner->GetAbsAngles() );
pShield->SetFrontDistance( flFrontDistance );
// Start it in the right place
Vector vForward;
AngleVectors( owner->GetAbsAngles(), &vForward );
Vector vecOrigin = owner->EyePosition() + (vForward * flFrontDistance);
UTIL_SetOrigin( pShield, vecOrigin );
pShield->ChangeTeam( owner->GetTeamNumber() );
pShield->SetupRecharge( shield_mobile_power.GetFloat(), shield_mobile_recharge_delay.GetFloat(), shield_mobile_recharge_amount.GetFloat(), shield_mobile_recharge_time.GetFloat() );
pShield->Spawn();
return pShield;
}
#endif