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.
851 lines
24 KiB
851 lines
24 KiB
5 years ago
|
//========= 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
|
||
|
|