//========= 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( 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