diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/shared/hl2mp/weapon_physcannon.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/shared/hl2mp/weapon_physcannon.cpp')
| -rw-r--r-- | mp/src/game/shared/hl2mp/weapon_physcannon.cpp | 3685 |
1 files changed, 3685 insertions, 0 deletions
diff --git a/mp/src/game/shared/hl2mp/weapon_physcannon.cpp b/mp/src/game/shared/hl2mp/weapon_physcannon.cpp new file mode 100644 index 00000000..97245b14 --- /dev/null +++ b/mp/src/game/shared/hl2mp/weapon_physcannon.cpp @@ -0,0 +1,3685 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Physics cannon
+//
+//=============================================================================//
+
+#include "cbase.h"
+
+#ifdef CLIENT_DLL
+ #include "c_hl2mp_player.h"
+ #include "vcollide_parse.h"
+ #include "engine/ivdebugoverlay.h"
+ #include "iviewrender_beams.h"
+ #include "beamdraw.h"
+ #include "c_te_effect_dispatch.h"
+ #include "model_types.h"
+ #include "clienteffectprecachesystem.h"
+ #include "fx_interpvalue.h"
+#else
+ #include "hl2mp_player.h"
+ #include "soundent.h"
+ #include "ndebugoverlay.h"
+ #include "ai_basenpc.h"
+ #include "player_pickup.h"
+ #include "physics_prop_ragdoll.h"
+ #include "globalstate.h"
+ #include "props.h"
+ #include "te_effect_dispatch.h"
+ #include "util.h"
+#endif
+
+#include "gamerules.h"
+#include "soundenvelope.h"
+#include "engine/IEngineSound.h"
+#include "physics.h"
+#include "in_buttons.h"
+#include "IEffects.h"
+#include "shake.h"
+#include "beam_shared.h"
+#include "Sprite.h"
+#include "weapon_physcannon.h"
+#include "physics_saverestore.h"
+#include "movevars_shared.h"
+#include "weapon_hl2mpbasehlmpcombatweapon.h"
+#include "vphysics/friction.h"
+#include "debugoverlay_shared.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define SPRITE_SCALE 128.0f
+
+static const char *s_pWaitForUpgradeContext = "WaitForUpgrade";
+
+ConVar g_debug_physcannon( "g_debug_physcannon", "0", FCVAR_REPLICATED | FCVAR_CHEAT );
+
+ConVar physcannon_minforce( "physcannon_minforce", "700", FCVAR_REPLICATED | FCVAR_CHEAT );
+ConVar physcannon_maxforce( "physcannon_maxforce", "1500", FCVAR_REPLICATED | FCVAR_CHEAT );
+ConVar physcannon_maxmass( "physcannon_maxmass", "250", FCVAR_REPLICATED | FCVAR_CHEAT );
+ConVar physcannon_tracelength( "physcannon_tracelength", "250", FCVAR_REPLICATED | FCVAR_CHEAT );
+ConVar physcannon_chargetime("physcannon_chargetime", "2", FCVAR_REPLICATED | FCVAR_CHEAT );
+ConVar physcannon_pullforce( "physcannon_pullforce", "4000", FCVAR_REPLICATED | FCVAR_CHEAT );
+ConVar physcannon_cone( "physcannon_cone", "0.97", FCVAR_REPLICATED | FCVAR_CHEAT );
+ConVar physcannon_ball_cone( "physcannon_ball_cone", "0.997", FCVAR_REPLICATED | FCVAR_CHEAT );
+ConVar player_throwforce( "player_throwforce", "1000", FCVAR_REPLICATED | FCVAR_CHEAT );
+
+#ifndef CLIENT_DLL
+extern ConVar hl2_normspeed;
+extern ConVar hl2_walkspeed;
+#endif
+
+#define PHYSCANNON_BEAM_SPRITE "sprites/orangelight1.vmt"
+#define PHYSCANNON_BEAM_SPRITE_NOZ "sprites/orangelight1_noz.vmt"
+#define PHYSCANNON_GLOW_SPRITE "sprites/glow04_noz"
+#define PHYSCANNON_ENDCAP_SPRITE "sprites/orangeflare1"
+#define PHYSCANNON_CENTER_GLOW "sprites/orangecore1"
+#define PHYSCANNON_BLAST_SPRITE "sprites/orangecore2"
+
+#ifdef CLIENT_DLL
+
+ //Precahce the effects
+ CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectPhysCannon )
+ CLIENTEFFECT_MATERIAL( "sprites/orangelight1" )
+ CLIENTEFFECT_MATERIAL( "sprites/orangelight1_noz" )
+ CLIENTEFFECT_MATERIAL( PHYSCANNON_GLOW_SPRITE )
+ CLIENTEFFECT_MATERIAL( PHYSCANNON_ENDCAP_SPRITE )
+ CLIENTEFFECT_MATERIAL( PHYSCANNON_CENTER_GLOW )
+ CLIENTEFFECT_MATERIAL( PHYSCANNON_BLAST_SPRITE )
+ CLIENTEFFECT_REGISTER_END()
+
+#endif // CLIENT_DLL
+
+#ifndef CLIENT_DLL
+
+void PhysCannonBeginUpgrade( CBaseAnimating *pAnim )
+{
+
+}
+
+bool PlayerHasMegaPhysCannon( void )
+{
+ return false;
+}
+
+bool PhysCannonAccountableForObject( CBaseCombatWeapon *pPhysCannon, CBaseEntity *pObject )
+{
+ // BRJ: FIXME! This can't be implemented trivially, so I'm leaving it to Steve or Adrian
+ Assert( 0 );
+ return false;
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// this will hit skip the pass entity, but not anything it owns
+// (lets player grab own grenades)
+class CTraceFilterNoOwnerTest : public CTraceFilterSimple
+{
+public:
+ DECLARE_CLASS( CTraceFilterNoOwnerTest, CTraceFilterSimple );
+
+ CTraceFilterNoOwnerTest( const IHandleEntity *passentity, int collisionGroup )
+ : CTraceFilterSimple( NULL, collisionGroup ), m_pPassNotOwner(passentity)
+ {
+ }
+
+ virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
+ {
+ if ( pHandleEntity != m_pPassNotOwner )
+ return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
+
+ return false;
+ }
+
+protected:
+ const IHandleEntity *m_pPassNotOwner;
+};
+
+static void MatrixOrthogonalize( matrix3x4_t &matrix, int column )
+{
+ Vector columns[3];
+ int i;
+
+ for ( i = 0; i < 3; i++ )
+ {
+ MatrixGetColumn( matrix, i, columns[i] );
+ }
+
+ int index0 = column;
+ int index1 = (column+1)%3;
+ int index2 = (column+2)%3;
+
+ columns[index2] = CrossProduct( columns[index0], columns[index1] );
+ columns[index1] = CrossProduct( columns[index2], columns[index0] );
+ VectorNormalize( columns[index2] );
+ VectorNormalize( columns[index1] );
+ MatrixSetColumn( columns[index1], index1, matrix );
+ MatrixSetColumn( columns[index2], index2, matrix );
+}
+
+#define SIGN(x) ( (x) < 0 ? -1 : 1 )
+
+static QAngle AlignAngles( const QAngle &angles, float cosineAlignAngle )
+{
+ matrix3x4_t alignMatrix;
+ AngleMatrix( angles, alignMatrix );
+
+ // NOTE: Must align z first
+ for ( int j = 3; --j >= 0; )
+ {
+ Vector vec;
+ MatrixGetColumn( alignMatrix, j, vec );
+ for ( int i = 0; i < 3; i++ )
+ {
+ if ( fabs(vec[i]) > cosineAlignAngle )
+ {
+ vec[i] = SIGN(vec[i]);
+ vec[(i+1)%3] = 0;
+ vec[(i+2)%3] = 0;
+ MatrixSetColumn( vec, j, alignMatrix );
+ MatrixOrthogonalize( alignMatrix, j );
+ break;
+ }
+ }
+ }
+
+ QAngle out;
+ MatrixAngles( alignMatrix, out );
+ return out;
+}
+
+
+static void TraceCollideAgainstBBox( const CPhysCollide *pCollide, const Vector &start, const Vector &end, const QAngle &angles, const Vector &boxOrigin, const Vector &mins, const Vector &maxs, trace_t *ptr )
+{
+ physcollision->TraceBox( boxOrigin, boxOrigin + (start-end), mins, maxs, pCollide, start, angles, ptr );
+
+ if ( ptr->DidHit() )
+ {
+ ptr->endpos = start * (1-ptr->fraction) + end * ptr->fraction;
+ ptr->startpos = start;
+ ptr->plane.dist = -ptr->plane.dist;
+ ptr->plane.normal *= -1;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Computes a local matrix for the player clamped to valid carry ranges
+//-----------------------------------------------------------------------------
+// when looking level, hold bottom of object 8 inches below eye level
+#define PLAYER_HOLD_LEVEL_EYES -8
+
+// when looking down, hold bottom of object 0 inches from feet
+#define PLAYER_HOLD_DOWN_FEET 2
+
+// when looking up, hold bottom of object 24 inches above eye level
+#define PLAYER_HOLD_UP_EYES 24
+
+// use a +/-30 degree range for the entire range of motion of pitch
+#define PLAYER_LOOK_PITCH_RANGE 30
+
+// player can reach down 2ft below his feet (otherwise he'll hold the object above the bottom)
+#define PLAYER_REACH_DOWN_DISTANCE 24
+
+static void ComputePlayerMatrix( CBasePlayer *pPlayer, matrix3x4_t &out )
+{
+ if ( !pPlayer )
+ return;
+
+ QAngle angles = pPlayer->EyeAngles();
+ Vector origin = pPlayer->EyePosition();
+
+ // 0-360 / -180-180
+ //angles.x = init ? 0 : AngleDistance( angles.x, 0 );
+ //angles.x = clamp( angles.x, -PLAYER_LOOK_PITCH_RANGE, PLAYER_LOOK_PITCH_RANGE );
+ angles.x = 0;
+
+ float feet = pPlayer->GetAbsOrigin().z + pPlayer->WorldAlignMins().z;
+ float eyes = origin.z;
+ float zoffset = 0;
+ // moving up (negative pitch is up)
+ if ( angles.x < 0 )
+ {
+ zoffset = RemapVal( angles.x, 0, -PLAYER_LOOK_PITCH_RANGE, PLAYER_HOLD_LEVEL_EYES, PLAYER_HOLD_UP_EYES );
+ }
+ else
+ {
+ zoffset = RemapVal( angles.x, 0, PLAYER_LOOK_PITCH_RANGE, PLAYER_HOLD_LEVEL_EYES, PLAYER_HOLD_DOWN_FEET + (feet - eyes) );
+ }
+ origin.z += zoffset;
+ angles.x = 0;
+ AngleMatrix( angles, origin, out );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+
+// derive from this so we can add save/load data to it
+struct game_shadowcontrol_params_t : public hlshadowcontrol_params_t
+{
+ DECLARE_SIMPLE_DATADESC();
+};
+
+BEGIN_SIMPLE_DATADESC( game_shadowcontrol_params_t )
+
+ DEFINE_FIELD( targetPosition, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( targetRotation, FIELD_VECTOR ),
+ DEFINE_FIELD( maxAngular, FIELD_FLOAT ),
+ DEFINE_FIELD( maxDampAngular, FIELD_FLOAT ),
+ DEFINE_FIELD( maxSpeed, FIELD_FLOAT ),
+ DEFINE_FIELD( maxDampSpeed, FIELD_FLOAT ),
+ DEFINE_FIELD( dampFactor, FIELD_FLOAT ),
+ DEFINE_FIELD( teleportDistance, FIELD_FLOAT ),
+
+END_DATADESC()
+
+//-----------------------------------------------------------------------------
+class CGrabController : public IMotionEvent
+{
+public:
+
+ CGrabController( void );
+ ~CGrabController( void );
+ void AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, IPhysicsObject *pPhys, bool bIsMegaPhysCannon, const Vector &vGrabPosition, bool bUseGrabPosition );
+ void DetachEntity( bool bClearVelocity );
+ void OnRestore();
+
+ bool UpdateObject( CBasePlayer *pPlayer, float flError );
+
+ void SetTargetPosition( const Vector &target, const QAngle &targetOrientation );
+ float ComputeError();
+ float GetLoadWeight( void ) const { return m_flLoadWeight; }
+ void SetAngleAlignment( float alignAngleCosine ) { m_angleAlignment = alignAngleCosine; }
+ void SetIgnorePitch( bool bIgnore ) { m_bIgnoreRelativePitch = bIgnore; }
+ QAngle TransformAnglesToPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer );
+ QAngle TransformAnglesFromPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer );
+
+ CBaseEntity *GetAttached() { return (CBaseEntity *)m_attachedEntity; }
+
+ IMotionEvent::simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
+ float GetSavedMass( IPhysicsObject *pObject );
+
+ QAngle m_attachedAnglesPlayerSpace;
+ Vector m_attachedPositionObjectSpace;
+
+private:
+ // Compute the max speed for an attached object
+ void ComputeMaxSpeed( CBaseEntity *pEntity, IPhysicsObject *pPhysics );
+
+ game_shadowcontrol_params_t m_shadow;
+ float m_timeToArrive;
+ float m_errorTime;
+ float m_error;
+ float m_contactAmount;
+ float m_angleAlignment;
+ bool m_bCarriedEntityBlocksLOS;
+ bool m_bIgnoreRelativePitch;
+
+ float m_flLoadWeight;
+ float m_savedRotDamping[VPHYSICS_MAX_OBJECT_LIST_COUNT];
+ float m_savedMass[VPHYSICS_MAX_OBJECT_LIST_COUNT];
+ EHANDLE m_attachedEntity;
+ QAngle m_vecPreferredCarryAngles;
+ bool m_bHasPreferredCarryAngles;
+
+
+ IPhysicsMotionController *m_controller;
+ int m_frameCount;
+ friend class CWeaponPhysCannon;
+};
+
+const float DEFAULT_MAX_ANGULAR = 360.0f * 10.0f;
+const float REDUCED_CARRY_MASS = 1.0f;
+
+CGrabController::CGrabController( void )
+{
+ m_shadow.dampFactor = 1.0;
+ m_shadow.teleportDistance = 0;
+ m_errorTime = 0;
+ m_error = 0;
+ // make this controller really stiff!
+ m_shadow.maxSpeed = 1000;
+ m_shadow.maxAngular = DEFAULT_MAX_ANGULAR;
+ m_shadow.maxDampSpeed = m_shadow.maxSpeed*2;
+ m_shadow.maxDampAngular = m_shadow.maxAngular;
+ m_attachedEntity = NULL;
+ m_vecPreferredCarryAngles = vec3_angle;
+ m_bHasPreferredCarryAngles = false;
+}
+
+CGrabController::~CGrabController( void )
+{
+ DetachEntity( false );
+}
+
+void CGrabController::OnRestore()
+{
+ if ( m_controller )
+ {
+ m_controller->SetEventHandler( this );
+ }
+}
+
+void CGrabController::SetTargetPosition( const Vector &target, const QAngle &targetOrientation )
+{
+ m_shadow.targetPosition = target;
+ m_shadow.targetRotation = targetOrientation;
+
+ m_timeToArrive = gpGlobals->frametime;
+
+ CBaseEntity *pAttached = GetAttached();
+ if ( pAttached )
+ {
+ IPhysicsObject *pObj = pAttached->VPhysicsGetObject();
+
+ if ( pObj != NULL )
+ {
+ pObj->Wake();
+ }
+ else
+ {
+ DetachEntity( false );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CGrabController::ComputeError()
+{
+ if ( m_errorTime <= 0 )
+ return 0;
+
+ CBaseEntity *pAttached = GetAttached();
+ if ( pAttached )
+ {
+ Vector pos;
+ IPhysicsObject *pObj = pAttached->VPhysicsGetObject();
+
+ if ( pObj )
+ {
+ pObj->GetShadowPosition( &pos, NULL );
+
+ float error = (m_shadow.targetPosition - pos).Length();
+ if ( m_errorTime > 0 )
+ {
+ if ( m_errorTime > 1 )
+ {
+ m_errorTime = 1;
+ }
+ float speed = error / m_errorTime;
+ if ( speed > m_shadow.maxSpeed )
+ {
+ error *= 0.5;
+ }
+ m_error = (1-m_errorTime) * m_error + error * m_errorTime;
+ }
+ }
+ else
+ {
+ DevMsg( "Object attached to Physcannon has no physics object\n" );
+ DetachEntity( false );
+ return 9999; // force detach
+ }
+ }
+
+ if ( pAttached->IsEFlagSet( EFL_IS_BEING_LIFTED_BY_BARNACLE ) )
+ {
+ m_error *= 3.0f;
+ }
+
+ m_errorTime = 0;
+
+ return m_error;
+}
+
+
+#define MASS_SPEED_SCALE 60
+#define MAX_MASS 40
+
+void CGrabController::ComputeMaxSpeed( CBaseEntity *pEntity, IPhysicsObject *pPhysics )
+{
+#ifndef CLIENT_DLL
+ m_shadow.maxSpeed = 1000;
+ m_shadow.maxAngular = DEFAULT_MAX_ANGULAR;
+
+ // Compute total mass...
+ float flMass = PhysGetEntityMass( pEntity );
+ float flMaxMass = physcannon_maxmass.GetFloat();
+ if ( flMass <= flMaxMass )
+ return;
+
+ float flLerpFactor = clamp( flMass, flMaxMass, 500.0f );
+ flLerpFactor = SimpleSplineRemapVal( flLerpFactor, flMaxMass, 500.0f, 0.0f, 1.0f );
+
+ float invMass = pPhysics->GetInvMass();
+ float invInertia = pPhysics->GetInvInertia().Length();
+
+ float invMaxMass = 1.0f / MAX_MASS;
+ float ratio = invMaxMass / invMass;
+ invMass = invMaxMass;
+ invInertia *= ratio;
+
+ float maxSpeed = invMass * MASS_SPEED_SCALE * 200;
+ float maxAngular = invInertia * MASS_SPEED_SCALE * 360;
+
+ m_shadow.maxSpeed = Lerp( flLerpFactor, m_shadow.maxSpeed, maxSpeed );
+ m_shadow.maxAngular = Lerp( flLerpFactor, m_shadow.maxAngular, maxAngular );
+#endif
+}
+
+
+QAngle CGrabController::TransformAnglesToPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer )
+{
+ if ( m_bIgnoreRelativePitch )
+ {
+ matrix3x4_t test;
+ QAngle angleTest = pPlayer->EyeAngles();
+ angleTest.x = 0;
+ AngleMatrix( angleTest, test );
+ return TransformAnglesToLocalSpace( anglesIn, test );
+ }
+ return TransformAnglesToLocalSpace( anglesIn, pPlayer->EntityToWorldTransform() );
+}
+
+QAngle CGrabController::TransformAnglesFromPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer )
+{
+ if ( m_bIgnoreRelativePitch )
+ {
+ matrix3x4_t test;
+ QAngle angleTest = pPlayer->EyeAngles();
+ angleTest.x = 0;
+ AngleMatrix( angleTest, test );
+ return TransformAnglesToWorldSpace( anglesIn, test );
+ }
+ return TransformAnglesToWorldSpace( anglesIn, pPlayer->EntityToWorldTransform() );
+}
+
+
+void CGrabController::AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, IPhysicsObject *pPhys, bool bIsMegaPhysCannon, const Vector &vGrabPosition, bool bUseGrabPosition )
+{
+ // play the impact sound of the object hitting the player
+ // used as feedback to let the player know he picked up the object
+#ifndef CLIENT_DLL
+ PhysicsImpactSound( pPlayer, pPhys, CHAN_STATIC, pPhys->GetMaterialIndex(), pPlayer->VPhysicsGetObject()->GetMaterialIndex(), 1.0, 64 );
+#endif
+ Vector position;
+ QAngle angles;
+ pPhys->GetPosition( &position, &angles );
+ // If it has a preferred orientation, use that instead.
+#ifndef CLIENT_DLL
+ Pickup_GetPreferredCarryAngles( pEntity, pPlayer, pPlayer->EntityToWorldTransform(), angles );
+#endif
+
+// ComputeMaxSpeed( pEntity, pPhys );
+
+ // Carried entities can never block LOS
+ m_bCarriedEntityBlocksLOS = pEntity->BlocksLOS();
+ pEntity->SetBlocksLOS( false );
+ m_controller = physenv->CreateMotionController( this );
+ m_controller->AttachObject( pPhys, true );
+ // Don't do this, it's causing trouble with constraint solvers.
+ //m_controller->SetPriority( IPhysicsMotionController::HIGH_PRIORITY );
+
+ pPhys->Wake();
+ PhysSetGameFlags( pPhys, FVPHYSICS_PLAYER_HELD );
+ SetTargetPosition( position, angles );
+ m_attachedEntity = pEntity;
+ IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
+ int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
+ m_flLoadWeight = 0;
+ float damping = 10;
+ float flFactor = count / 7.5f;
+ if ( flFactor < 1.0f )
+ {
+ flFactor = 1.0f;
+ }
+ for ( int i = 0; i < count; i++ )
+ {
+ float mass = pList[i]->GetMass();
+ pList[i]->GetDamping( NULL, &m_savedRotDamping[i] );
+ m_flLoadWeight += mass;
+ m_savedMass[i] = mass;
+
+ // reduce the mass to prevent the player from adding crazy amounts of energy to the system
+ pList[i]->SetMass( REDUCED_CARRY_MASS / flFactor );
+ pList[i]->SetDamping( NULL, &damping );
+ }
+
+ // Give extra mass to the phys object we're actually picking up
+ pPhys->SetMass( REDUCED_CARRY_MASS );
+ pPhys->EnableDrag( false );
+
+ m_errorTime = -1.0f; // 1 seconds until error starts accumulating
+ m_error = 0;
+ m_contactAmount = 0;
+
+ m_attachedAnglesPlayerSpace = TransformAnglesToPlayerSpace( angles, pPlayer );
+ if ( m_angleAlignment != 0 )
+ {
+ m_attachedAnglesPlayerSpace = AlignAngles( m_attachedAnglesPlayerSpace, m_angleAlignment );
+ }
+
+ VectorITransform( pEntity->WorldSpaceCenter(), pEntity->EntityToWorldTransform(), m_attachedPositionObjectSpace );
+
+#ifndef CLIENT_DLL
+ // If it's a prop, see if it has desired carry angles
+ CPhysicsProp *pProp = dynamic_cast<CPhysicsProp *>(pEntity);
+ if ( pProp )
+ {
+ m_bHasPreferredCarryAngles = pProp->GetPropDataAngles( "preferred_carryangles", m_vecPreferredCarryAngles );
+ }
+ else
+ {
+ m_bHasPreferredCarryAngles = false;
+ }
+#else
+
+ m_bHasPreferredCarryAngles = false;
+#endif
+
+}
+
+static void ClampPhysicsVelocity( IPhysicsObject *pPhys, float linearLimit, float angularLimit )
+{
+ Vector vel;
+ AngularImpulse angVel;
+ pPhys->GetVelocity( &vel, &angVel );
+ float speed = VectorNormalize(vel) - linearLimit;
+ float angSpeed = VectorNormalize(angVel) - angularLimit;
+ speed = speed < 0 ? 0 : -speed;
+ angSpeed = angSpeed < 0 ? 0 : -angSpeed;
+ vel *= speed;
+ angVel *= angSpeed;
+ pPhys->AddVelocity( &vel, &angVel );
+}
+
+void CGrabController::DetachEntity( bool bClearVelocity )
+{
+ CBaseEntity *pEntity = GetAttached();
+ if ( pEntity )
+ {
+ // Restore the LS blocking state
+ pEntity->SetBlocksLOS( m_bCarriedEntityBlocksLOS );
+ IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
+ int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
+
+ for ( int i = 0; i < count; i++ )
+ {
+ IPhysicsObject *pPhys = pList[i];
+ if ( !pPhys )
+ continue;
+
+ // on the odd chance that it's gone to sleep while under anti-gravity
+ pPhys->EnableDrag( true );
+ pPhys->Wake();
+ pPhys->SetMass( m_savedMass[i] );
+ pPhys->SetDamping( NULL, &m_savedRotDamping[i] );
+ PhysClearGameFlags( pPhys, FVPHYSICS_PLAYER_HELD );
+ if ( bClearVelocity )
+ {
+ PhysForceClearVelocity( pPhys );
+ }
+ else
+ {
+#ifndef CLIENT_DLL
+ ClampPhysicsVelocity( pPhys, hl2_normspeed.GetFloat() * 1.5f, 2.0f * 360.0f );
+#endif
+ }
+
+ }
+ }
+
+ m_attachedEntity = NULL;
+ if ( physenv )
+ {
+ physenv->DestroyMotionController( m_controller );
+ }
+ m_controller = NULL;
+}
+
+static bool InContactWithHeavyObject( IPhysicsObject *pObject, float heavyMass )
+{
+ bool contact = false;
+ IPhysicsFrictionSnapshot *pSnapshot = pObject->CreateFrictionSnapshot();
+ while ( pSnapshot->IsValid() )
+ {
+ IPhysicsObject *pOther = pSnapshot->GetObject( 1 );
+ if ( !pOther->IsMoveable() || pOther->GetMass() > heavyMass )
+ {
+ contact = true;
+ break;
+ }
+ pSnapshot->NextFrictionData();
+ }
+ pObject->DestroyFrictionSnapshot( pSnapshot );
+ return contact;
+}
+
+IMotionEvent::simresult_e CGrabController::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
+{
+ game_shadowcontrol_params_t shadowParams = m_shadow;
+ if ( InContactWithHeavyObject( pObject, GetLoadWeight() ) )
+ {
+ m_contactAmount = Approach( 0.1f, m_contactAmount, deltaTime*2.0f );
+ }
+ else
+ {
+ m_contactAmount = Approach( 1.0f, m_contactAmount, deltaTime*2.0f );
+ }
+ shadowParams.maxAngular = m_shadow.maxAngular * m_contactAmount * m_contactAmount * m_contactAmount;
+#ifndef CLIENT_DLL
+ m_timeToArrive = pObject->ComputeShadowControl( shadowParams, m_timeToArrive, deltaTime );
+#else
+ m_timeToArrive = pObject->ComputeShadowControl( shadowParams, (TICK_INTERVAL*2), deltaTime );
+#endif
+
+ // Slide along the current contact points to fix bouncing problems
+ Vector velocity;
+ AngularImpulse angVel;
+ pObject->GetVelocity( &velocity, &angVel );
+ PhysComputeSlideDirection( pObject, velocity, angVel, &velocity, &angVel, GetLoadWeight() );
+ pObject->SetVelocityInstantaneous( &velocity, NULL );
+
+ linear.Init();
+ angular.Init();
+ m_errorTime += deltaTime;
+
+ return SIM_LOCAL_ACCELERATION;
+}
+
+float CGrabController::GetSavedMass( IPhysicsObject *pObject )
+{
+ CBaseEntity *pHeld = m_attachedEntity;
+ if ( pHeld )
+ {
+ if ( pObject->GetGameData() == (void*)pHeld )
+ {
+ IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
+ int count = pHeld->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
+ for ( int i = 0; i < count; i++ )
+ {
+ if ( pList[i] == pObject )
+ return m_savedMass[i];
+ }
+ }
+ }
+ return 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Player pickup controller
+//-----------------------------------------------------------------------------
+
+class CPlayerPickupController : public CBaseEntity
+{
+ DECLARE_CLASS( CPlayerPickupController, CBaseEntity );
+public:
+ void Init( CBasePlayer *pPlayer, CBaseEntity *pObject );
+ void Shutdown( bool bThrown = false );
+ bool OnControls( CBaseEntity *pControls ) { return true; }
+ void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+ void OnRestore()
+ {
+ m_grabController.OnRestore();
+ }
+ void VPhysicsUpdate( IPhysicsObject *pPhysics ){}
+ void VPhysicsShadowUpdate( IPhysicsObject *pPhysics ) {}
+
+ bool IsHoldingEntity( CBaseEntity *pEnt );
+ CGrabController &GetGrabController() { return m_grabController; }
+
+private:
+ CGrabController m_grabController;
+ CBasePlayer *m_pPlayer;
+};
+
+LINK_ENTITY_TO_CLASS( player_pickup, CPlayerPickupController );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pPlayer -
+// *pObject -
+//-----------------------------------------------------------------------------
+void CPlayerPickupController::Init( CBasePlayer *pPlayer, CBaseEntity *pObject )
+{
+#ifndef CLIENT_DLL
+ // Holster player's weapon
+ if ( pPlayer->GetActiveWeapon() )
+ {
+ if ( !pPlayer->GetActiveWeapon()->Holster() )
+ {
+ Shutdown();
+ return;
+ }
+ }
+
+
+ CHL2MP_Player *pOwner = (CHL2MP_Player *)ToBasePlayer( pPlayer );
+ if ( pOwner )
+ {
+ pOwner->EnableSprint( false );
+ }
+
+ // If the target is debris, convert it to non-debris
+ if ( pObject->GetCollisionGroup() == COLLISION_GROUP_DEBRIS )
+ {
+ // Interactive debris converts back to debris when it comes to rest
+ pObject->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS );
+ }
+
+ // done so I'll go across level transitions with the player
+ SetParent( pPlayer );
+ m_grabController.SetIgnorePitch( true );
+ m_grabController.SetAngleAlignment( DOT_30DEGREE );
+ m_pPlayer = pPlayer;
+ IPhysicsObject *pPhysics = pObject->VPhysicsGetObject();
+ Pickup_OnPhysGunPickup( pObject, m_pPlayer );
+
+ m_grabController.AttachEntity( pPlayer, pObject, pPhysics, false, vec3_origin, false );
+
+ m_pPlayer->m_Local.m_iHideHUD |= HIDEHUD_WEAPONSELECTION;
+ m_pPlayer->SetUseEntity( this );
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : bool -
+//-----------------------------------------------------------------------------
+void CPlayerPickupController::Shutdown( bool bThrown )
+{
+#ifndef CLIENT_DLL
+ CBaseEntity *pObject = m_grabController.GetAttached();
+
+ bool bClearVelocity = false;
+ if ( !bThrown && pObject && pObject->VPhysicsGetObject() && pObject->VPhysicsGetObject()->GetContactPoint(NULL,NULL) )
+ {
+ bClearVelocity = true;
+ }
+
+ m_grabController.DetachEntity( bClearVelocity );
+
+ if ( pObject != NULL )
+ {
+ Pickup_OnPhysGunDrop( pObject, m_pPlayer, bThrown ? THROWN_BY_PLAYER : DROPPED_BY_PLAYER );
+ }
+
+ if ( m_pPlayer )
+ {
+ CHL2MP_Player *pOwner = (CHL2MP_Player *)ToBasePlayer( m_pPlayer );
+ if ( pOwner )
+ {
+ pOwner->EnableSprint( true );
+ }
+
+ m_pPlayer->SetUseEntity( NULL );
+ if ( m_pPlayer->GetActiveWeapon() )
+ {
+ if ( !m_pPlayer->GetActiveWeapon()->Deploy() )
+ {
+ // We tried to restore the player's weapon, but we couldn't.
+ // This usually happens when they're holding an empty weapon that doesn't
+ // autoswitch away when out of ammo. Switch to next best weapon.
+ m_pPlayer->SwitchToNextBestWeapon( NULL );
+ }
+ }
+
+ m_pPlayer->m_Local.m_iHideHUD &= ~HIDEHUD_WEAPONSELECTION;
+ }
+ Remove();
+
+#endif
+
+}
+
+
+void CPlayerPickupController::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ if ( ToBasePlayer(pActivator) == m_pPlayer )
+ {
+ CBaseEntity *pAttached = m_grabController.GetAttached();
+
+ // UNDONE: Use vphysics stress to decide to drop objects
+ // UNDONE: Must fix case of forcing objects into the ground you're standing on (causes stress) before that will work
+ if ( !pAttached || useType == USE_OFF || (m_pPlayer->m_nButtons & IN_ATTACK2) || m_grabController.ComputeError() > 12 )
+ {
+ Shutdown();
+ return;
+ }
+
+ //Adrian: Oops, our object became motion disabled, let go!
+ IPhysicsObject *pPhys = pAttached->VPhysicsGetObject();
+ if ( pPhys && pPhys->IsMoveable() == false )
+ {
+ Shutdown();
+ return;
+ }
+
+#if STRESS_TEST
+ vphysics_objectstress_t stress;
+ CalculateObjectStress( pPhys, pAttached, &stress );
+ if ( stress.exertedStress > 250 )
+ {
+ Shutdown();
+ return;
+ }
+#endif
+ // +ATTACK will throw phys objects
+ if ( m_pPlayer->m_nButtons & IN_ATTACK )
+ {
+ Shutdown( true );
+ Vector vecLaunch;
+ m_pPlayer->EyeVectors( &vecLaunch );
+ // JAY: Scale this with mass because some small objects really go flying
+ float massFactor = clamp( pPhys->GetMass(), 0.5, 15 );
+ massFactor = RemapVal( massFactor, 0.5, 15, 0.5, 4 );
+ vecLaunch *= player_throwforce.GetFloat() * massFactor;
+
+ pPhys->ApplyForceCenter( vecLaunch );
+ AngularImpulse aVel = RandomAngularImpulse( -10, 10 ) * massFactor;
+ pPhys->ApplyTorqueCenter( aVel );
+ return;
+ }
+
+ if ( useType == USE_SET )
+ {
+ // update position
+ m_grabController.UpdateObject( m_pPlayer, 12 );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pEnt -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CPlayerPickupController::IsHoldingEntity( CBaseEntity *pEnt )
+{
+ return ( m_grabController.GetAttached() == pEnt );
+}
+
+void PlayerPickupObject( CBasePlayer *pPlayer, CBaseEntity *pObject )
+{
+
+#ifndef CLIENT_DLL
+
+ //Don't pick up if we don't have a phys object.
+ if ( pObject->VPhysicsGetObject() == NULL )
+ return;
+
+ CPlayerPickupController *pController = (CPlayerPickupController *)CBaseEntity::Create( "player_pickup", pObject->GetAbsOrigin(), vec3_angle, pPlayer );
+
+ if ( !pController )
+ return;
+
+ pController->Init( pPlayer, pObject );
+
+#endif
+
+}
+
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+// CInterpolatedValue class
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+
+#ifdef CLIENT_DLL
+
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+// CPhysCannonEffect class
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+
+class CPhysCannonEffect
+{
+public:
+ CPhysCannonEffect( void ) : m_vecColor( 255, 255, 255 ), m_bVisible( true ), m_nAttachment( -1 ) {};
+
+ void SetAttachment( int attachment ) { m_nAttachment = attachment; }
+ int GetAttachment( void ) const { return m_nAttachment; }
+
+ void SetVisible( bool visible = true ) { m_bVisible = visible; }
+ int IsVisible( void ) const { return m_bVisible; }
+
+ void SetColor( const Vector &color ) { m_vecColor = color; }
+ const Vector &GetColor( void ) const { return m_vecColor; }
+
+ bool SetMaterial( const char *materialName )
+ {
+ m_hMaterial.Init( materialName, TEXTURE_GROUP_CLIENT_EFFECTS );
+ return ( m_hMaterial != NULL );
+ }
+
+ CMaterialReference &GetMaterial( void ) { return m_hMaterial; }
+
+ CInterpolatedValue &GetAlpha( void ) { return m_Alpha; }
+ CInterpolatedValue &GetScale( void ) { return m_Scale; }
+
+private:
+ CInterpolatedValue m_Alpha;
+ CInterpolatedValue m_Scale;
+
+ Vector m_vecColor;
+ bool m_bVisible;
+ int m_nAttachment;
+ CMaterialReference m_hMaterial;
+};
+
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+// CPhysCannonEffectBeam class
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+
+class CPhysCannonEffectBeam
+{
+public:
+ CPhysCannonEffectBeam( void ) : m_pBeam( NULL ) {};
+
+ ~CPhysCannonEffectBeam( void )
+ {
+ Release();
+ }
+
+ void Release( void )
+ {
+ if ( m_pBeam != NULL )
+ {
+ m_pBeam->flags = 0;
+ m_pBeam->die = gpGlobals->curtime - 1;
+
+ m_pBeam = NULL;
+ }
+ }
+
+ void Init( int startAttachment, int endAttachment, CBaseEntity *pEntity, bool firstPerson )
+ {
+ if ( m_pBeam != NULL )
+ return;
+
+ BeamInfo_t beamInfo;
+
+ beamInfo.m_pStartEnt = pEntity;
+ beamInfo.m_nStartAttachment = startAttachment;
+ beamInfo.m_pEndEnt = pEntity;
+ beamInfo.m_nEndAttachment = endAttachment;
+ beamInfo.m_nType = TE_BEAMPOINTS;
+ beamInfo.m_vecStart = vec3_origin;
+ beamInfo.m_vecEnd = vec3_origin;
+
+ beamInfo.m_pszModelName = ( firstPerson ) ? PHYSCANNON_BEAM_SPRITE_NOZ : PHYSCANNON_BEAM_SPRITE;
+
+ beamInfo.m_flHaloScale = 0.0f;
+ beamInfo.m_flLife = 0.0f;
+
+ if ( firstPerson )
+ {
+ beamInfo.m_flWidth = 0.0f;
+ beamInfo.m_flEndWidth = 4.0f;
+ }
+ else
+ {
+ beamInfo.m_flWidth = 0.5f;
+ beamInfo.m_flEndWidth = 2.0f;
+ }
+
+ beamInfo.m_flFadeLength = 0.0f;
+ beamInfo.m_flAmplitude = 16;
+ beamInfo.m_flBrightness = 255.0;
+ beamInfo.m_flSpeed = 150.0f;
+ beamInfo.m_nStartFrame = 0.0;
+ beamInfo.m_flFrameRate = 30.0;
+ beamInfo.m_flRed = 255.0;
+ beamInfo.m_flGreen = 255.0;
+ beamInfo.m_flBlue = 255.0;
+ beamInfo.m_nSegments = 8;
+ beamInfo.m_bRenderable = true;
+ beamInfo.m_nFlags = FBEAM_FOREVER;
+
+ m_pBeam = beams->CreateBeamEntPoint( beamInfo );
+ }
+
+ void SetVisible( bool state = true )
+ {
+ if ( m_pBeam == NULL )
+ return;
+
+ m_pBeam->brightness = ( state ) ? 255.0f : 0.0f;
+ }
+
+private:
+ Beam_t *m_pBeam;
+};
+
+#endif
+
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+// CWeaponPhysCannon class
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+
+#ifdef CLIENT_DLL
+#define CWeaponPhysCannon C_WeaponPhysCannon
+#endif
+
+class CWeaponPhysCannon : public CBaseHL2MPCombatWeapon
+{
+public:
+ DECLARE_CLASS( CWeaponPhysCannon, CBaseHL2MPCombatWeapon );
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponPhysCannon( void );
+
+ void Drop( const Vector &vecVelocity );
+ void Precache();
+ virtual void OnRestore();
+ virtual void StopLoopingSounds();
+ virtual void UpdateOnRemove(void);
+ void PrimaryAttack();
+ void SecondaryAttack();
+ void WeaponIdle();
+ void ItemPreFrame();
+ void ItemPostFrame();
+
+ void ForceDrop( void );
+ bool DropIfEntityHeld( CBaseEntity *pTarget ); // Drops its held entity if it matches the entity passed in
+ CGrabController &GetGrabController() { return m_grabController; }
+
+ bool CanHolster( void );
+ bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
+ bool Deploy( void );
+
+ bool HasAnyAmmo( void ) { return true; }
+
+ virtual void SetViewModel( void );
+ virtual const char *GetShootSound( int iIndex ) const;
+
+#ifndef CLIENT_DLL
+ CNetworkQAngle ( m_attachedAnglesPlayerSpace );
+#else
+ QAngle m_attachedAnglesPlayerSpace;
+#endif
+
+ CNetworkVector ( m_attachedPositionObjectSpace );
+
+ CNetworkHandle( CBaseEntity, m_hAttachedObject );
+
+ EHANDLE m_hOldAttachedObject;
+
+protected:
+ enum FindObjectResult_t
+ {
+ OBJECT_FOUND = 0,
+ OBJECT_NOT_FOUND,
+ OBJECT_BEING_DETACHED,
+ };
+
+ void DoEffect( int effectType, Vector *pos = NULL );
+
+ void OpenElements( void );
+ void CloseElements( void );
+
+ // Pickup and throw objects.
+ bool CanPickupObject( CBaseEntity *pTarget );
+ void CheckForTarget( void );
+
+#ifndef CLIENT_DLL
+ bool AttachObject( CBaseEntity *pObject, const Vector &vPosition );
+ FindObjectResult_t FindObject( void );
+ CBaseEntity *FindObjectInCone( const Vector &vecOrigin, const Vector &vecDir, float flCone );
+#endif // !CLIENT_DLL
+
+ void UpdateObject( void );
+ void DetachObject( bool playSound = true, bool wasLaunched = false );
+ void LaunchObject( const Vector &vecDir, float flForce );
+ void StartEffects( void ); // Initialize all sprites and beams
+ void StopEffects( bool stopSound = true ); // Hide all effects temporarily
+ void DestroyEffects( void ); // Destroy all sprites and beams
+
+ // Punt objects - this is pointing at an object in the world and applying a force to it.
+ void PuntNonVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr );
+ void PuntVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr );
+
+ // Velocity-based throw common to punt and launch code.
+ void ApplyVelocityBasedForce( CBaseEntity *pEntity, const Vector &forward );
+
+ // Physgun effects
+ void DoEffectClosed( void );
+ void DoEffectReady( void );
+ void DoEffectHolding( void );
+ void DoEffectLaunch( Vector *pos );
+ void DoEffectNone( void );
+ void DoEffectIdle( void );
+
+ // Trace length
+ float TraceLength();
+
+ // Sprite scale factor
+ float SpriteScaleFactor();
+
+ float GetLoadPercentage();
+ CSoundPatch *GetMotorSound( void );
+
+ void DryFire( void );
+ void PrimaryFireEffect( void );
+
+#ifndef CLIENT_DLL
+ // What happens when the physgun picks up something
+ void Physgun_OnPhysGunPickup( CBaseEntity *pEntity, CBasePlayer *pOwner, PhysGunPickup_t reason );
+#endif // !CLIENT_DLL
+
+#ifdef CLIENT_DLL
+
+ enum EffectType_t
+ {
+ PHYSCANNON_CORE = 0,
+
+ PHYSCANNON_BLAST,
+
+ PHYSCANNON_GLOW1, // Must be in order!
+ PHYSCANNON_GLOW2,
+ PHYSCANNON_GLOW3,
+ PHYSCANNON_GLOW4,
+ PHYSCANNON_GLOW5,
+ PHYSCANNON_GLOW6,
+
+ PHYSCANNON_ENDCAP1, // Must be in order!
+ PHYSCANNON_ENDCAP2,
+ PHYSCANNON_ENDCAP3, // Only used in third-person!
+
+ NUM_PHYSCANNON_PARAMETERS // Must be last!
+ };
+
+#define NUM_GLOW_SPRITES ((CWeaponPhysCannon::PHYSCANNON_GLOW6-CWeaponPhysCannon::PHYSCANNON_GLOW1)+1)
+#define NUM_ENDCAP_SPRITES ((CWeaponPhysCannon::PHYSCANNON_ENDCAP3-CWeaponPhysCannon::PHYSCANNON_ENDCAP1)+1)
+
+#define NUM_PHYSCANNON_BEAMS 3
+
+ virtual int DrawModel( int flags );
+ virtual void ViewModelDrawn( C_BaseViewModel *pBaseViewModel );
+ virtual bool IsTransparent( void );
+ virtual void OnDataChanged( DataUpdateType_t type );
+ virtual void ClientThink( void );
+
+ void ManagePredictedObject( void );
+ void DrawEffects( void );
+ void GetEffectParameters( EffectType_t effectID, color32 &color, float &scale, IMaterial **pMaterial, Vector &vecAttachment );
+ void DrawEffectSprite( EffectType_t effectID );
+ inline bool IsEffectVisible( EffectType_t effectID );
+ void UpdateElementPosition( void );
+
+ // We need to render opaque and translucent pieces
+ RenderGroup_t GetRenderGroup( void ) { return RENDER_GROUP_TWOPASS; }
+
+ CInterpolatedValue m_ElementParameter; // Used to interpolate the position of the articulated elements
+ CPhysCannonEffect m_Parameters[NUM_PHYSCANNON_PARAMETERS]; // Interpolated parameters for the effects
+ CPhysCannonEffectBeam m_Beams[NUM_PHYSCANNON_BEAMS]; // Beams
+
+ int m_nOldEffectState; // Used for parity checks
+ bool m_bOldOpen; // Used for parity checks
+
+ void NotifyShouldTransmit( ShouldTransmitState_t state );
+
+#endif // CLIENT_DLL
+
+ int m_nChangeState; // For delayed state change of elements
+ float m_flCheckSuppressTime; // Amount of time to suppress the checking for targets
+ bool m_flLastDenySoundPlayed; // Debounce for deny sound
+ int m_nAttack2Debounce;
+
+ CNetworkVar( bool, m_bActive );
+ CNetworkVar( int, m_EffectState ); // Current state of the effects on the gun
+ CNetworkVar( bool, m_bOpen );
+
+ bool m_bResetOwnerEntity;
+
+ float m_flElementDebounce;
+
+ CSoundPatch *m_sndMotor; // Whirring sound for the gun
+
+ CGrabController m_grabController;
+
+ float m_flRepuntObjectTime;
+ EHANDLE m_hLastPuntedObject;
+
+private:
+ CWeaponPhysCannon( const CWeaponPhysCannon & );
+
+#ifndef CLIENT_DLL
+ DECLARE_ACTTABLE();
+#endif
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponPhysCannon, DT_WeaponPhysCannon )
+
+BEGIN_NETWORK_TABLE( CWeaponPhysCannon, DT_WeaponPhysCannon )
+#ifdef CLIENT_DLL
+ RecvPropBool( RECVINFO( m_bActive ) ),
+ RecvPropEHandle( RECVINFO( m_hAttachedObject ) ),
+ RecvPropVector( RECVINFO( m_attachedPositionObjectSpace ) ),
+ RecvPropFloat( RECVINFO( m_attachedAnglesPlayerSpace[0] ) ),
+ RecvPropFloat( RECVINFO( m_attachedAnglesPlayerSpace[1] ) ),
+ RecvPropFloat( RECVINFO( m_attachedAnglesPlayerSpace[2] ) ),
+ RecvPropInt( RECVINFO( m_EffectState ) ),
+ RecvPropBool( RECVINFO( m_bOpen ) ),
+#else
+ SendPropBool( SENDINFO( m_bActive ) ),
+ SendPropEHandle( SENDINFO( m_hAttachedObject ) ),
+ SendPropVector(SENDINFO( m_attachedPositionObjectSpace ), -1, SPROP_COORD),
+ SendPropAngle( SENDINFO_VECTORELEM(m_attachedAnglesPlayerSpace, 0 ), 11 ),
+ SendPropAngle( SENDINFO_VECTORELEM(m_attachedAnglesPlayerSpace, 1 ), 11 ),
+ SendPropAngle( SENDINFO_VECTORELEM(m_attachedAnglesPlayerSpace, 2 ), 11 ),
+ SendPropInt( SENDINFO( m_EffectState ) ),
+ SendPropBool( SENDINFO( m_bOpen ) ),
+#endif
+END_NETWORK_TABLE()
+
+#ifdef CLIENT_DLL
+BEGIN_PREDICTION_DATA( CWeaponPhysCannon )
+ DEFINE_PRED_FIELD( m_EffectState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bOpen, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_physcannon, CWeaponPhysCannon );
+PRECACHE_WEAPON_REGISTER( weapon_physcannon );
+
+#ifndef CLIENT_DLL
+
+acttable_t CWeaponPhysCannon::m_acttable[] =
+{
+ { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PHYSGUN, false },
+ { ACT_HL2MP_RUN, ACT_HL2MP_RUN_PHYSGUN, false },
+ { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PHYSGUN, false },
+ { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PHYSGUN, false },
+ { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PHYSGUN, false },
+ { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PHYSGUN, false },
+ { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PHYSGUN, false },
+};
+
+IMPLEMENT_ACTTABLE(CWeaponPhysCannon);
+
+#endif
+
+
+enum
+{
+ ELEMENT_STATE_NONE = -1,
+ ELEMENT_STATE_OPEN,
+ ELEMENT_STATE_CLOSED,
+};
+
+enum
+{
+ EFFECT_NONE,
+ EFFECT_CLOSED,
+ EFFECT_READY,
+ EFFECT_HOLDING,
+ EFFECT_LAUNCH,
+};
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CWeaponPhysCannon::CWeaponPhysCannon( void )
+{
+ m_bOpen = false;
+ m_nChangeState = ELEMENT_STATE_NONE;
+ m_flCheckSuppressTime = 0.0f;
+ m_EffectState = (int)EFFECT_NONE;
+ m_flLastDenySoundPlayed = false;
+
+#ifdef CLIENT_DLL
+ m_nOldEffectState = EFFECT_NONE;
+ m_bOldOpen = false;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Precache
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::Precache( void )
+{
+ PrecacheModel( PHYSCANNON_BEAM_SPRITE );
+ PrecacheModel( PHYSCANNON_BEAM_SPRITE_NOZ );
+
+ PrecacheScriptSound( "Weapon_PhysCannon.HoldSound" );
+
+ BaseClass::Precache();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Restore
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::OnRestore()
+{
+ BaseClass::OnRestore();
+ m_grabController.OnRestore();
+
+ // Tracker 8106: Physcannon effects disappear through level transition, so
+ // just recreate any effects here
+ if ( m_EffectState != EFFECT_NONE )
+ {
+ DoEffect( m_EffectState, NULL );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// On Remove
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::UpdateOnRemove(void)
+{
+ DestroyEffects( );
+ BaseClass::UpdateOnRemove();
+}
+
+#ifdef CLIENT_DLL
+void CWeaponPhysCannon::OnDataChanged( DataUpdateType_t type )
+{
+ BaseClass::OnDataChanged( type );
+
+ if ( type == DATA_UPDATE_CREATED )
+ {
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+
+ C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, false );
+ StartEffects();
+ }
+
+ if ( GetOwner() == NULL )
+ {
+ if ( m_hAttachedObject )
+ {
+ m_hAttachedObject->VPhysicsDestroyObject();
+ }
+
+ if ( m_hOldAttachedObject )
+ {
+ m_hOldAttachedObject->VPhysicsDestroyObject();
+ }
+ }
+
+ // Update effect state when out of parity with the server
+ if ( m_nOldEffectState != m_EffectState )
+ {
+ DoEffect( m_EffectState );
+ m_nOldEffectState = m_EffectState;
+ }
+
+ // Update element state when out of parity
+ if ( m_bOldOpen != m_bOpen )
+ {
+ if ( m_bOpen )
+ {
+ m_ElementParameter.InitFromCurrent( 1.0f, 0.2f, INTERP_SPLINE );
+ }
+ else
+ {
+ m_ElementParameter.InitFromCurrent( 0.0f, 0.5f, INTERP_SPLINE );
+ }
+
+ m_bOldOpen = (bool) m_bOpen;
+ }
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Sprite scale factor
+//-----------------------------------------------------------------------------
+inline float CWeaponPhysCannon::SpriteScaleFactor()
+{
+ return 1.0f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponPhysCannon::Deploy( void )
+{
+ CloseElements();
+ DoEffect( EFFECT_READY );
+
+ bool bReturn = BaseClass::Deploy();
+
+ m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime;
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner )
+ {
+ pOwner->SetNextAttack( gpGlobals->curtime );
+ }
+
+ return bReturn;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::SetViewModel( void )
+{
+ BaseClass::SetViewModel();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Force the cannon to drop anything it's carrying
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::ForceDrop( void )
+{
+ CloseElements();
+ DetachObject();
+ StopEffects();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Drops its held entity if it matches the entity passed in
+// Input : *pTarget -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponPhysCannon::DropIfEntityHeld( CBaseEntity *pTarget )
+{
+ if ( pTarget == NULL )
+ return false;
+
+ CBaseEntity *pHeld = m_grabController.GetAttached();
+
+ if ( pHeld == NULL )
+ return false;
+
+ if ( pHeld == pTarget )
+ {
+ ForceDrop();
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::Drop( const Vector &vecVelocity )
+{
+ ForceDrop();
+
+#ifndef CLIENT_DLL
+ UTIL_Remove( this );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponPhysCannon::CanHolster( void )
+{
+ //Don't holster this weapon if we're holding onto something
+ if ( m_bActive )
+ return false;
+
+ return BaseClass::CanHolster();
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponPhysCannon::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ //Don't holster this weapon if we're holding onto something
+ if ( m_bActive )
+ return false;
+
+ ForceDrop();
+ DestroyEffects();
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DryFire( void )
+{
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+
+ WeaponSound( EMPTY );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::PrimaryFireEffect( void )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ pOwner->ViewPunch( QAngle(-6, SharedRandomInt( "physcannonfire", -2,2) ,0) );
+
+#ifndef CLIENT_DLL
+ color32 white = { 245, 245, 255, 32 };
+ UTIL_ScreenFade( pOwner, white, 0.1f, 0.0f, FFADE_IN );
+#endif
+
+ WeaponSound( SINGLE );
+}
+
+#define MAX_KNOCKBACK_FORCE 128
+
+//-----------------------------------------------------------------------------
+// Punt non-physics
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::PuntNonVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr )
+{
+ if ( m_hLastPuntedObject == pEntity && gpGlobals->curtime < m_flRepuntObjectTime )
+ return;
+
+#ifndef CLIENT_DLL
+ CTakeDamageInfo info;
+
+ info.SetAttacker( GetOwner() );
+ info.SetInflictor( this );
+ info.SetDamage( 1.0f );
+ info.SetDamageType( DMG_CRUSH | DMG_PHYSGUN );
+ info.SetDamageForce( forward ); // Scale?
+ info.SetDamagePosition( tr.endpos );
+
+ m_hLastPuntedObject = pEntity;
+ m_flRepuntObjectTime = gpGlobals->curtime + 0.5f;
+
+ pEntity->DispatchTraceAttack( info, forward, &tr );
+
+ ApplyMultiDamage();
+
+ //Explosion effect
+ DoEffect( EFFECT_LAUNCH, &tr.endpos );
+#endif
+
+ PrimaryFireEffect();
+ SendWeaponAnim( ACT_VM_SECONDARYATTACK );
+
+ m_nChangeState = ELEMENT_STATE_CLOSED;
+ m_flElementDebounce = gpGlobals->curtime + 0.5f;
+ m_flCheckSuppressTime = gpGlobals->curtime + 0.25f;
+}
+
+
+#ifndef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// What happens when the physgun picks up something
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::Physgun_OnPhysGunPickup( CBaseEntity *pEntity, CBasePlayer *pOwner, PhysGunPickup_t reason )
+{
+ // If the target is debris, convert it to non-debris
+ if ( pEntity->GetCollisionGroup() == COLLISION_GROUP_DEBRIS )
+ {
+ // Interactive debris converts back to debris when it comes to rest
+ pEntity->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS );
+ }
+
+ Pickup_OnPhysGunPickup( pEntity, pOwner, reason );
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Punt vphysics
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::PuntVPhysics( CBaseEntity *pEntity, const Vector &vecForward, trace_t &tr )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+
+ if ( m_hLastPuntedObject == pEntity && gpGlobals->curtime < m_flRepuntObjectTime )
+ return;
+
+ m_hLastPuntedObject = pEntity;
+ m_flRepuntObjectTime = gpGlobals->curtime + 0.5f;
+
+#ifndef CLIENT_DLL
+ CTakeDamageInfo info;
+
+ Vector forward = vecForward;
+
+ info.SetAttacker( GetOwner() );
+ info.SetInflictor( this );
+ info.SetDamage( 0.0f );
+ info.SetDamageType( DMG_PHYSGUN );
+ pEntity->DispatchTraceAttack( info, forward, &tr );
+ ApplyMultiDamage();
+
+
+ if ( Pickup_OnAttemptPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON ) )
+ {
+ IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
+ int listCount = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
+ if ( !listCount )
+ {
+ //FIXME: Do we want to do this if there's no physics object?
+ Physgun_OnPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON );
+ DryFire();
+ return;
+ }
+
+ if( forward.z < 0 )
+ {
+ //reflect, but flatten the trajectory out a bit so it's easier to hit standing targets
+ forward.z *= -0.65f;
+ }
+
+ // NOTE: Do this first to enable motion (if disabled) - so forces will work
+ // Tell the object it's been punted
+ Physgun_OnPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON );
+
+ // don't push vehicles that are attached to the world via fixed constraints
+ // they will just wiggle...
+ if ( (pList[0]->GetGameFlags() & FVPHYSICS_CONSTRAINT_STATIC) && pEntity->GetServerVehicle() )
+ {
+ forward.Init();
+ }
+
+ if ( !Pickup_ShouldPuntUseLaunchForces( pEntity, PHYSGUN_FORCE_PUNTED ) )
+ {
+ int i;
+
+ // limit mass to avoid punting REALLY huge things
+ float totalMass = 0;
+ for ( i = 0; i < listCount; i++ )
+ {
+ totalMass += pList[i]->GetMass();
+ }
+ float maxMass = 250;
+ IServerVehicle *pVehicle = pEntity->GetServerVehicle();
+ if ( pVehicle )
+ {
+ maxMass *= 2.5; // 625 for vehicles
+ }
+ float mass = MIN(totalMass, maxMass); // max 250kg of additional force
+
+ // Put some spin on the object
+ for ( i = 0; i < listCount; i++ )
+ {
+ const float hitObjectFactor = 0.5f;
+ const float otherObjectFactor = 1.0f - hitObjectFactor;
+ // Must be light enough
+ float ratio = pList[i]->GetMass() / totalMass;
+ if ( pList[i] == pEntity->VPhysicsGetObject() )
+ {
+ ratio += hitObjectFactor;
+ ratio = MIN(ratio,1.0f);
+ }
+ else
+ {
+ ratio *= otherObjectFactor;
+ }
+ pList[i]->ApplyForceCenter( forward * 15000.0f * ratio );
+ pList[i]->ApplyForceOffset( forward * mass * 600.0f * ratio, tr.endpos );
+ }
+ }
+ else
+ {
+ ApplyVelocityBasedForce( pEntity, vecForward );
+ }
+ }
+
+#endif
+ // Add recoil
+ QAngle recoil = QAngle( random->RandomFloat( 1.0f, 2.0f ), random->RandomFloat( -1.0f, 1.0f ), 0 );
+ pOwner->ViewPunch( recoil );
+
+ //Explosion effect
+ DoEffect( EFFECT_LAUNCH, &tr.endpos );
+
+ PrimaryFireEffect();
+ SendWeaponAnim( ACT_VM_SECONDARYATTACK );
+
+ m_nChangeState = ELEMENT_STATE_CLOSED;
+ m_flElementDebounce = gpGlobals->curtime + 0.5f;
+ m_flCheckSuppressTime = gpGlobals->curtime + 0.25f;
+
+ // Don't allow the gun to regrab a thrown object!!
+ m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Applies velocity-based forces to throw the entity. This code is
+// called from both punt and launch carried code.
+// ASSUMES: that pEntity is a vphysics entity.
+// Input : -
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::ApplyVelocityBasedForce( CBaseEntity *pEntity, const Vector &forward )
+{
+#ifndef CLIENT_DLL
+ IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
+ Assert(pPhysicsObject); // Shouldn't ever get here with a non-vphysics object.
+ if (!pPhysicsObject)
+ return;
+
+ float flForceMax = physcannon_maxforce.GetFloat();
+ float flForce = flForceMax;
+
+ float mass = pPhysicsObject->GetMass();
+ if (mass > 100)
+ {
+ mass = MIN(mass, 1000);
+ float flForceMin = physcannon_minforce.GetFloat();
+ flForce = SimpleSplineRemapVal(mass, 100, 600, flForceMax, flForceMin);
+ }
+
+ Vector vVel = forward * flForce;
+ // FIXME: Josh needs to put a real value in for PHYSGUN_FORCE_PUNTED
+ AngularImpulse aVel = Pickup_PhysGunLaunchAngularImpulse( pEntity, PHYSGUN_FORCE_PUNTED );
+
+ pPhysicsObject->AddVelocity( &vVel, &aVel );
+
+#endif
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Trace length
+//-----------------------------------------------------------------------------
+float CWeaponPhysCannon::TraceLength()
+{
+ return physcannon_tracelength.GetFloat();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+// This mode is a toggle. Primary fire one time to pick up a physics object.
+// With an object held, click primary fire again to drop object.
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::PrimaryAttack( void )
+{
+ if( m_flNextPrimaryAttack > gpGlobals->curtime )
+ return;
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ if( m_bActive )
+ {
+ // Punch the object being held!!
+ Vector forward;
+ pOwner->EyeVectors( &forward );
+
+ // Validate the item is within punt range
+ CBaseEntity *pHeld = m_grabController.GetAttached();
+ Assert( pHeld != NULL );
+
+ if ( pHeld != NULL )
+ {
+ float heldDist = ( pHeld->WorldSpaceCenter() - pOwner->WorldSpaceCenter() ).Length();
+
+ if ( heldDist > physcannon_tracelength.GetFloat() )
+ {
+ // We can't punt this yet
+ DryFire();
+ return;
+ }
+ }
+
+ LaunchObject( forward, physcannon_maxforce.GetFloat() );
+
+ PrimaryFireEffect();
+ SendWeaponAnim( ACT_VM_SECONDARYATTACK );
+ return;
+ }
+
+ // If not active, just issue a physics punch in the world.
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
+
+ Vector forward;
+ pOwner->EyeVectors( &forward );
+
+ // NOTE: Notice we're *not* using the mega tracelength here
+ // when you have the mega cannon. Punting has shorter range.
+ Vector start, end;
+ start = pOwner->Weapon_ShootPosition();
+ float flPuntDistance = physcannon_tracelength.GetFloat();
+ VectorMA( start, flPuntDistance, forward, end );
+
+ CTraceFilterNoOwnerTest filter( pOwner, COLLISION_GROUP_NONE );
+ trace_t tr;
+ UTIL_TraceHull( start, end, -Vector(8,8,8), Vector(8,8,8), MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
+ bool bValid = true;
+ CBaseEntity *pEntity = tr.m_pEnt;
+ if ( tr.fraction == 1 || !tr.m_pEnt || tr.m_pEnt->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) )
+ {
+ bValid = false;
+ }
+ else if ( (pEntity->GetMoveType() != MOVETYPE_VPHYSICS) && ( pEntity->m_takedamage == DAMAGE_NO ) )
+ {
+ bValid = false;
+ }
+
+ // If the entity we've hit is invalid, try a traceline instead
+ if ( !bValid )
+ {
+ UTIL_TraceLine( start, end, MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
+ if ( tr.fraction == 1 || !tr.m_pEnt || tr.m_pEnt->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) )
+ {
+ // Play dry-fire sequence
+ DryFire();
+ return;
+ }
+
+ pEntity = tr.m_pEnt;
+ }
+
+ // See if we hit something
+ if ( pEntity->GetMoveType() != MOVETYPE_VPHYSICS )
+ {
+ if ( pEntity->m_takedamage == DAMAGE_NO )
+ {
+ DryFire();
+ return;
+ }
+
+ if( GetOwner()->IsPlayer() )
+ {
+ // Don't let the player zap any NPC's except regular antlions and headcrabs.
+ if( pEntity->IsPlayer() )
+ {
+ DryFire();
+ return;
+ }
+ }
+
+ PuntNonVPhysics( pEntity, forward, tr );
+ }
+ else
+ {
+ if ( pEntity->VPhysicsIsFlesh( ) )
+ {
+ DryFire();
+ return;
+ }
+ PuntVPhysics( pEntity, forward, tr );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Click secondary attack whilst holding an object to hurl it.
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::SecondaryAttack( void )
+{
+#ifndef CLIENT_DLL
+ if ( m_flNextSecondaryAttack > gpGlobals->curtime )
+ return;
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ // See if we should drop a held item
+ if ( ( m_bActive ) && ( pOwner->m_afButtonPressed & IN_ATTACK2 ) )
+ {
+ // Drop the held object
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
+
+ DetachObject();
+
+ DoEffect( EFFECT_READY );
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+ }
+ else
+ {
+ // Otherwise pick it up
+ FindObjectResult_t result = FindObject();
+ switch ( result )
+ {
+ case OBJECT_FOUND:
+ WeaponSound( SPECIAL1 );
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f;
+
+ // We found an object. Debounce the button
+ m_nAttack2Debounce |= pOwner->m_nButtons;
+ break;
+
+ case OBJECT_NOT_FOUND:
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.1f;
+ CloseElements();
+ break;
+
+ case OBJECT_BEING_DETACHED:
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.01f;
+ break;
+ }
+
+ DoEffect( EFFECT_HOLDING );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::WeaponIdle( void )
+{
+ if ( HasWeaponIdleTimeElapsed() )
+ {
+ if ( m_bActive )
+ {
+ //Shake when holding an item
+ SendWeaponAnim( ACT_VM_RELOAD );
+ }
+ else
+ {
+ //Otherwise idle simply
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+ }
+}
+
+#ifndef CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pObject -
+//-----------------------------------------------------------------------------
+bool CWeaponPhysCannon::AttachObject( CBaseEntity *pObject, const Vector &vPosition )
+{
+
+ if ( m_bActive )
+ return false;
+
+ if ( CanPickupObject( pObject ) == false )
+ return false;
+
+ m_grabController.SetIgnorePitch( false );
+ m_grabController.SetAngleAlignment( 0 );
+
+ IPhysicsObject *pPhysics = pObject->VPhysicsGetObject();
+
+ // Must be valid
+ if ( !pPhysics )
+ return false;
+
+ CHL2MP_Player *pOwner = (CHL2MP_Player *)ToBasePlayer( GetOwner() );
+
+ m_bActive = true;
+ if( pOwner )
+ {
+ // NOTE: This can change the mass; so it must be done before max speed setting
+ Physgun_OnPhysGunPickup( pObject, pOwner, PICKED_UP_BY_CANNON );
+ }
+
+ // NOTE :This must happen after OnPhysGunPickup because that can change the mass
+ m_grabController.AttachEntity( pOwner, pObject, pPhysics, false, vPosition, false );
+ m_hAttachedObject = pObject;
+ m_attachedPositionObjectSpace = m_grabController.m_attachedPositionObjectSpace;
+ m_attachedAnglesPlayerSpace = m_grabController.m_attachedAnglesPlayerSpace;
+
+ m_bResetOwnerEntity = false;
+
+ if ( m_hAttachedObject->GetOwnerEntity() == NULL )
+ {
+ m_hAttachedObject->SetOwnerEntity( pOwner );
+ m_bResetOwnerEntity = true;
+ }
+
+/* if( pOwner )
+ {
+ pOwner->EnableSprint( false );
+
+ float loadWeight = ( 1.0f - GetLoadPercentage() );
+ float maxSpeed = hl2_walkspeed.GetFloat() + ( ( hl2_normspeed.GetFloat() - hl2_walkspeed.GetFloat() ) * loadWeight );
+
+ //Msg( "Load perc: %f -- Movement speed: %f/%f\n", loadWeight, maxSpeed, hl2_normspeed.GetFloat() );
+ pOwner->SetMaxSpeed( maxSpeed );
+ }*/
+
+ // Don't drop again for a slight delay, in case they were pulling objects near them
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.4f;
+
+ DoEffect( EFFECT_HOLDING );
+ OpenElements();
+
+ if ( GetMotorSound() )
+ {
+ (CSoundEnvelopeController::GetController()).Play( GetMotorSound(), 0.0f, 50 );
+ (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 100, 0.5f );
+ (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.8f, 0.5f );
+ }
+
+
+
+ return true;
+}
+
+CWeaponPhysCannon::FindObjectResult_t CWeaponPhysCannon::FindObject( void )
+{
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+
+ Assert( pPlayer );
+ if ( pPlayer == NULL )
+ return OBJECT_NOT_FOUND;
+
+ Vector forward;
+ pPlayer->EyeVectors( &forward );
+
+ // Setup our positions
+ Vector start = pPlayer->Weapon_ShootPosition();
+ float testLength = TraceLength() * 4.0f;
+ Vector end = start + forward * testLength;
+
+ // Try to find an object by looking straight ahead
+ trace_t tr;
+ CTraceFilterNoOwnerTest filter( pPlayer, COLLISION_GROUP_NONE );
+ UTIL_TraceLine( start, end, MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
+
+ // Try again with a hull trace
+ if ( ( tr.fraction == 1.0 ) || ( tr.m_pEnt == NULL ) || ( tr.m_pEnt->IsWorld() ) )
+ {
+ UTIL_TraceHull( start, end, -Vector(4,4,4), Vector(4,4,4), MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
+ }
+
+ CBaseEntity *pEntity = tr.m_pEnt ? tr.m_pEnt->GetRootMoveParent() : NULL;
+ bool bAttach = false;
+ bool bPull = false;
+
+ // If we hit something, pick it up or pull it
+ if ( ( tr.fraction != 1.0f ) && ( tr.m_pEnt ) && ( tr.m_pEnt->IsWorld() == false ) )
+ {
+ // Attempt to attach if within range
+ if ( tr.fraction <= 0.25f )
+ {
+ bAttach = true;
+ }
+ else if ( tr.fraction > 0.25f )
+ {
+ bPull = true;
+ }
+ }
+
+ // Find anything within a general cone in front
+ CBaseEntity *pConeEntity = NULL;
+
+ if (!bAttach && !bPull)
+ {
+ pConeEntity = FindObjectInCone( start, forward, physcannon_cone.GetFloat() );
+ }
+
+ if ( pConeEntity )
+ {
+ pEntity = pConeEntity;
+
+ // If the object is near, grab it. Else, pull it a bit.
+ if ( pEntity->WorldSpaceCenter().DistToSqr( start ) <= (testLength * testLength) )
+ {
+ bAttach = true;
+ }
+ else
+ {
+ bPull = true;
+ }
+ }
+
+ if ( CanPickupObject( pEntity ) == false )
+ {
+ // Make a noise to signify we can't pick this up
+ if ( !m_flLastDenySoundPlayed )
+ {
+ m_flLastDenySoundPlayed = true;
+ WeaponSound( SPECIAL3 );
+ }
+
+ return OBJECT_NOT_FOUND;
+ }
+
+ // Check to see if the object is constrained + needs to be ripped off...
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ if ( !Pickup_OnAttemptPhysGunPickup( pEntity, pOwner, PICKED_UP_BY_CANNON ) )
+ return OBJECT_BEING_DETACHED;
+
+ if ( bAttach )
+ {
+ return AttachObject( pEntity, tr.endpos ) ? OBJECT_FOUND : OBJECT_NOT_FOUND;
+ }
+
+ if ( !bPull )
+ return OBJECT_NOT_FOUND;
+
+ // FIXME: This needs to be run through the CanPickupObject logic
+ IPhysicsObject *pObj = pEntity->VPhysicsGetObject();
+ if ( !pObj )
+ return OBJECT_NOT_FOUND;
+
+ // If we're too far, simply start to pull the object towards us
+ Vector pullDir = start - pEntity->WorldSpaceCenter();
+ VectorNormalize( pullDir );
+ pullDir *= physcannon_pullforce.GetFloat();
+
+ float mass = PhysGetEntityMass( pEntity );
+ if ( mass < 50.0f )
+ {
+ pullDir *= (mass + 0.5) * (1/50.0f);
+ }
+
+ // Nudge it towards us
+ pObj->ApplyForceCenter( pullDir );
+ return OBJECT_NOT_FOUND;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+CBaseEntity *CWeaponPhysCannon::FindObjectInCone( const Vector &vecOrigin, const Vector &vecDir, float flCone )
+{
+ // Find the nearest physics-based item in a cone in front of me.
+ CBaseEntity *list[256];
+ float flNearestDist = TraceLength() + 1.0;
+ Vector mins = vecOrigin - Vector( flNearestDist, flNearestDist, flNearestDist );
+ Vector maxs = vecOrigin + Vector( flNearestDist, flNearestDist, flNearestDist );
+
+ CBaseEntity *pNearest = NULL;
+
+ int count = UTIL_EntitiesInBox( list, 256, mins, maxs, 0 );
+ for( int i = 0 ; i < count ; i++ )
+ {
+ if ( !list[ i ]->VPhysicsGetObject() )
+ continue;
+
+ // Closer than other objects
+ Vector los = ( list[ i ]->WorldSpaceCenter() - vecOrigin );
+ float flDist = VectorNormalize( los );
+ if( flDist >= flNearestDist )
+ continue;
+
+ // Cull to the cone
+ if ( DotProduct( los, vecDir ) <= flCone )
+ continue;
+
+ // Make sure it isn't occluded!
+ trace_t tr;
+ CTraceFilterNoOwnerTest filter( GetOwner(), COLLISION_GROUP_NONE );
+ UTIL_TraceLine( vecOrigin, list[ i ]->WorldSpaceCenter(), MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
+ if( tr.m_pEnt == list[ i ] )
+ {
+ flNearestDist = flDist;
+ pNearest = list[ i ];
+ }
+ }
+
+ return pNearest;
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CGrabController::UpdateObject( CBasePlayer *pPlayer, float flError )
+{
+ CBaseEntity *pEntity = GetAttached();
+ if ( !pEntity )
+ return false;
+ if ( ComputeError() > flError )
+ return false;
+ if ( pPlayer->GetGroundEntity() == pEntity )
+ return false;
+ if (!pEntity->VPhysicsGetObject() )
+ return false;
+
+ //Adrian: Oops, our object became motion disabled, let go!
+ IPhysicsObject *pPhys = pEntity->VPhysicsGetObject();
+ if ( pPhys && pPhys->IsMoveable() == false )
+ {
+ return false;
+ }
+
+ if ( m_frameCount == gpGlobals->framecount )
+ {
+ return true;
+ }
+ m_frameCount = gpGlobals->framecount;
+ Vector forward, right, up;
+ QAngle playerAngles = pPlayer->EyeAngles();
+
+ float pitch = AngleDistance(playerAngles.x,0);
+ playerAngles.x = clamp( pitch, -75, 75 );
+ AngleVectors( playerAngles, &forward, &right, &up );
+
+ // Now clamp a sphere of object radius at end to the player's bbox
+ Vector radial = physcollision->CollideGetExtent( pPhys->GetCollide(), vec3_origin, pEntity->GetAbsAngles(), -forward );
+ Vector player2d = pPlayer->CollisionProp()->OBBMaxs();
+ float playerRadius = player2d.Length2D();
+ float flDot = DotProduct( forward, radial );
+
+ float radius = playerRadius + fabs( flDot );
+
+ float distance = 24 + ( radius * 2.0f );
+
+ Vector start = pPlayer->Weapon_ShootPosition();
+ Vector end = start + ( forward * distance );
+
+ trace_t tr;
+ CTraceFilterSkipTwoEntities traceFilter( pPlayer, pEntity, COLLISION_GROUP_NONE );
+ Ray_t ray;
+ ray.Init( start, end );
+ enginetrace->TraceRay( ray, MASK_SOLID_BRUSHONLY, &traceFilter, &tr );
+
+ if ( tr.fraction < 0.5 )
+ {
+ end = start + forward * (radius*0.5f);
+ }
+ else if ( tr.fraction <= 1.0f )
+ {
+ end = start + forward * ( distance - radius );
+ }
+
+ Vector playerMins, playerMaxs, nearest;
+ pPlayer->CollisionProp()->WorldSpaceAABB( &playerMins, &playerMaxs );
+ Vector playerLine = pPlayer->CollisionProp()->WorldSpaceCenter();
+ CalcClosestPointOnLine( end, playerLine+Vector(0,0,playerMins.z), playerLine+Vector(0,0,playerMaxs.z), nearest, NULL );
+
+ Vector delta = end - nearest;
+ float len = VectorNormalize(delta);
+ if ( len < radius )
+ {
+ end = nearest + radius * delta;
+ }
+
+ QAngle angles = TransformAnglesFromPlayerSpace( m_attachedAnglesPlayerSpace, pPlayer );
+
+ //Show overlays of radius
+ if ( g_debug_physcannon.GetBool() )
+ {
+
+#ifdef CLIENT_DLL
+
+ debugoverlay->AddBoxOverlay( end, -Vector( 2,2,2 ), Vector(2,2,2), angles, 0, 255, 255, true, 0 );
+
+ debugoverlay->AddBoxOverlay( GetAttached()->WorldSpaceCenter(),
+ -Vector( radius, radius, radius),
+ Vector( radius, radius, radius ),
+ angles,
+ 255, 255, 0,
+ true,
+ 0.0f );
+
+#else
+
+ NDebugOverlay::Box( end, -Vector( 2,2,2 ), Vector(2,2,2), 0, 255, 0, true, 0 );
+
+ NDebugOverlay::Box( GetAttached()->WorldSpaceCenter(),
+ -Vector( radius+5, radius+5, radius+5),
+ Vector( radius+5, radius+5, radius+5 ),
+ 255, 0, 0,
+ true,
+ 0.0f );
+#endif
+ }
+
+#ifndef CLIENT_DLL
+ // If it has a preferred orientation, update to ensure we're still oriented correctly.
+ Pickup_GetPreferredCarryAngles( pEntity, pPlayer, pPlayer->EntityToWorldTransform(), angles );
+
+
+ // We may be holding a prop that has preferred carry angles
+ if ( m_bHasPreferredCarryAngles )
+ {
+ matrix3x4_t tmp;
+ ComputePlayerMatrix( pPlayer, tmp );
+ angles = TransformAnglesToWorldSpace( m_vecPreferredCarryAngles, tmp );
+ }
+
+#endif
+
+ matrix3x4_t attachedToWorld;
+ Vector offset;
+ AngleMatrix( angles, attachedToWorld );
+ VectorRotate( m_attachedPositionObjectSpace, attachedToWorld, offset );
+
+ SetTargetPosition( end - offset, angles );
+
+ return true;
+}
+
+void CWeaponPhysCannon::UpdateObject( void )
+{
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+ Assert( pPlayer );
+
+ float flError = 12;
+ if ( !m_grabController.UpdateObject( pPlayer, flError ) )
+ {
+ DetachObject();
+ return;
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DetachObject( bool playSound, bool wasLaunched )
+{
+#ifndef CLIENT_DLL
+ if ( m_bActive == false )
+ return;
+
+ CHL2MP_Player *pOwner = (CHL2MP_Player *)ToBasePlayer( GetOwner() );
+ if( pOwner != NULL )
+ {
+ pOwner->EnableSprint( true );
+ pOwner->SetMaxSpeed( hl2_normspeed.GetFloat() );
+ }
+
+ CBaseEntity *pObject = m_grabController.GetAttached();
+
+ m_grabController.DetachEntity( wasLaunched );
+
+ if ( pObject != NULL )
+ {
+ Pickup_OnPhysGunDrop( pObject, pOwner, wasLaunched ? LAUNCHED_BY_CANNON : DROPPED_BY_CANNON );
+ }
+
+ // Stop our looping sound
+ if ( GetMotorSound() )
+ {
+ (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f );
+ (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f );
+ }
+
+ if ( pObject && m_bResetOwnerEntity == true )
+ {
+ pObject->SetOwnerEntity( NULL );
+ }
+
+ m_bActive = false;
+ m_hAttachedObject = NULL;
+
+
+ if ( playSound )
+ {
+ //Play the detach sound
+ WeaponSound( MELEE_MISS );
+ }
+
+#else
+
+ m_grabController.DetachEntity( wasLaunched );
+
+ if ( m_hAttachedObject )
+ {
+ m_hAttachedObject->VPhysicsDestroyObject();
+ }
+#endif
+}
+
+
+#ifdef CLIENT_DLL
+void CWeaponPhysCannon::ManagePredictedObject( void )
+{
+ CBaseEntity *pAttachedObject = m_hAttachedObject.Get();
+
+ if ( m_hAttachedObject )
+ {
+ // NOTE :This must happen after OnPhysGunPickup because that can change the mass
+ if ( pAttachedObject != GetGrabController().GetAttached() )
+ {
+ IPhysicsObject *pPhysics = pAttachedObject->VPhysicsGetObject();
+
+ if ( pPhysics == NULL )
+ {
+ solid_t tmpSolid;
+ PhysModelParseSolid( tmpSolid, m_hAttachedObject, pAttachedObject->GetModelIndex() );
+
+ pAttachedObject->VPhysicsInitNormal( SOLID_VPHYSICS, 0, false, &tmpSolid );
+ }
+
+ pPhysics = pAttachedObject->VPhysicsGetObject();
+
+ if ( pPhysics )
+ {
+ m_grabController.SetIgnorePitch( false );
+ m_grabController.SetAngleAlignment( 0 );
+
+ GetGrabController().AttachEntity( ToBasePlayer( GetOwner() ), pAttachedObject, pPhysics, false, vec3_origin, false );
+ GetGrabController().m_attachedPositionObjectSpace = m_attachedPositionObjectSpace;
+ GetGrabController().m_attachedAnglesPlayerSpace = m_attachedAnglesPlayerSpace;
+ }
+ }
+ }
+ else
+ {
+ if ( m_hOldAttachedObject && m_hOldAttachedObject->VPhysicsGetObject() )
+ {
+ GetGrabController().DetachEntity( false );
+
+ m_hOldAttachedObject->VPhysicsDestroyObject();
+ }
+ }
+
+ m_hOldAttachedObject = m_hAttachedObject;
+}
+
+#endif
+
+#ifdef CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose: Update the pose parameter for the gun
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::UpdateElementPosition( void )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ float flElementPosition = m_ElementParameter.Interp( gpGlobals->curtime );
+
+ if ( ShouldDrawUsingViewModel() )
+ {
+ if ( pOwner != NULL )
+ {
+ CBaseViewModel *vm = pOwner->GetViewModel();
+
+ if ( vm != NULL )
+ {
+ vm->SetPoseParameter( "active", flElementPosition );
+ }
+ }
+ }
+ else
+ {
+ SetPoseParameter( "active", flElementPosition );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Think function for the client
+//-----------------------------------------------------------------------------
+
+void CWeaponPhysCannon::ClientThink( void )
+{
+ // Update our elements visually
+ UpdateElementPosition();
+
+ // Update our effects
+ DoEffectIdle();
+}
+
+#endif // CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::ItemPreFrame()
+{
+ BaseClass::ItemPreFrame();
+
+#ifdef CLIENT_DLL
+ C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer();
+
+ if ( localplayer && !localplayer->IsObserver() )
+ ManagePredictedObject();
+#endif
+
+ // Update the object if the weapon is switched on.
+ if( m_bActive )
+ {
+ UpdateObject();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::CheckForTarget( void )
+{
+#ifndef CLIENT_DLL
+ //See if we're suppressing this
+ if ( m_flCheckSuppressTime > gpGlobals->curtime )
+ return;
+
+ // holstered
+ if ( IsEffectActive( EF_NODRAW ) )
+ return;
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ if ( m_bActive )
+ return;
+
+ Vector aimDir;
+ pOwner->EyeVectors( &aimDir );
+
+ Vector startPos = pOwner->Weapon_ShootPosition();
+ Vector endPos;
+ VectorMA( startPos, TraceLength(), aimDir, endPos );
+
+ trace_t tr;
+ UTIL_TraceHull( startPos, endPos, -Vector(4,4,4), Vector(4,4,4), MASK_SHOT|CONTENTS_GRATE, pOwner, COLLISION_GROUP_NONE, &tr );
+
+ if ( ( tr.fraction != 1.0f ) && ( tr.m_pEnt != NULL ) )
+ {
+ // FIXME: Try just having the elements always open when pointed at a physics object
+ if ( CanPickupObject( tr.m_pEnt ) || Pickup_ForcePhysGunOpen( tr.m_pEnt, pOwner ) )
+ // if ( ( tr.m_pEnt->VPhysicsGetObject() != NULL ) && ( tr.m_pEnt->GetMoveType() == MOVETYPE_VPHYSICS ) )
+ {
+ m_nChangeState = ELEMENT_STATE_NONE;
+ OpenElements();
+ return;
+ }
+ }
+
+ // Close the elements after a delay to prevent overact state switching
+ if ( ( m_flElementDebounce < gpGlobals->curtime ) && ( m_nChangeState == ELEMENT_STATE_NONE ) )
+ {
+ m_nChangeState = ELEMENT_STATE_CLOSED;
+ m_flElementDebounce = gpGlobals->curtime + 0.5f;
+ }
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Idle effect (pulsing)
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DoEffectIdle( void )
+{
+#ifdef CLIENT_DLL
+
+ StartEffects();
+
+ //if ( ShouldDrawUsingViewModel() )
+ {
+ // Turn on the glow sprites
+ for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ m_Parameters[i].GetScale().SetAbsolute( random->RandomFloat( 0.075f, 0.05f ) * SPRITE_SCALE );
+ m_Parameters[i].GetAlpha().SetAbsolute( random->RandomInt( 24, 32 ) );
+ }
+
+ // Turn on the glow sprites
+ for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ m_Parameters[i].GetScale().SetAbsolute( random->RandomFloat( 3, 5 ) );
+ m_Parameters[i].GetAlpha().SetAbsolute( random->RandomInt( 200, 255 ) );
+ }
+
+ if ( m_EffectState != EFFECT_HOLDING )
+ {
+ // Turn beams off
+ m_Beams[0].SetVisible( false );
+ m_Beams[1].SetVisible( false );
+ m_Beams[2].SetVisible( false );
+ }
+ }
+ /*
+ else
+ {
+ // Turn on the glow sprites
+ for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ m_Parameters[i].GetScale().SetAbsolute( random->RandomFloat( 0.075f, 0.05f ) * SPRITE_SCALE );
+ m_Parameters[i].GetAlpha().SetAbsolute( random->RandomInt( 24, 32 ) );
+ }
+
+ // Turn on the glow sprites
+ for ( i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ m_Parameters[i].GetScale().SetAbsolute( random->RandomFloat( 3, 5 ) );
+ m_Parameters[i].GetAlpha().SetAbsolute( random->RandomInt( 200, 255 ) );
+ }
+
+ if ( m_EffectState != EFFECT_HOLDING )
+ {
+ // Turn beams off
+ m_Beams[0].SetVisible( false );
+ m_Beams[1].SetVisible( false );
+ m_Beams[2].SetVisible( false );
+ }
+ }
+ */
+#endif
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::ItemPostFrame()
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ if ( pOwner == NULL )
+ {
+ // We found an object. Debounce the button
+ m_nAttack2Debounce = 0;
+ return;
+ }
+
+ //Check for object in pickup range
+ if ( m_bActive == false )
+ {
+ CheckForTarget();
+
+ if ( ( m_flElementDebounce < gpGlobals->curtime ) && ( m_nChangeState != ELEMENT_STATE_NONE ) )
+ {
+ if ( m_nChangeState == ELEMENT_STATE_OPEN )
+ {
+ OpenElements();
+ }
+ else if ( m_nChangeState == ELEMENT_STATE_CLOSED )
+ {
+ CloseElements();
+ }
+
+ m_nChangeState = ELEMENT_STATE_NONE;
+ }
+ }
+
+ // NOTE: Attack2 will be considered to be pressed until the first item is picked up.
+ int nAttack2Mask = pOwner->m_nButtons & (~m_nAttack2Debounce);
+ if ( nAttack2Mask & IN_ATTACK2 )
+ {
+ SecondaryAttack();
+ }
+ else
+ {
+ // Reset our debouncer
+ m_flLastDenySoundPlayed = false;
+
+ if ( m_bActive == false )
+ {
+ DoEffect( EFFECT_READY );
+ }
+ }
+
+ if (( pOwner->m_nButtons & IN_ATTACK2 ) == 0 )
+ {
+ m_nAttack2Debounce = 0;
+ }
+
+ if ( pOwner->m_nButtons & IN_ATTACK )
+ {
+ PrimaryAttack();
+ }
+ else
+ {
+ WeaponIdle();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#define PHYSCANNON_DANGER_SOUND_RADIUS 128
+
+void CWeaponPhysCannon::LaunchObject( const Vector &vecDir, float flForce )
+{
+ CBaseEntity *pObject = m_grabController.GetAttached();
+
+ if ( !(m_hLastPuntedObject == pObject && gpGlobals->curtime < m_flRepuntObjectTime) )
+ {
+ // FIRE!!!
+ if( pObject != NULL )
+ {
+ DetachObject( false, true );
+
+ m_hLastPuntedObject = pObject;
+ m_flRepuntObjectTime = gpGlobals->curtime + 0.5f;
+
+ // Launch
+ ApplyVelocityBasedForce( pObject, vecDir );
+
+ // Don't allow the gun to regrab a thrown object!!
+ m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
+
+ Vector center = pObject->WorldSpaceCenter();
+
+ //Do repulse effect
+ DoEffect( EFFECT_LAUNCH, ¢er );
+
+ m_hAttachedObject = NULL;
+ m_bActive = false;
+ }
+ }
+
+ // Stop our looping sound
+ if ( GetMotorSound() )
+ {
+ (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f );
+ (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f );
+ }
+
+ //Close the elements and suppress checking for a bit
+ m_nChangeState = ELEMENT_STATE_CLOSED;
+ m_flElementDebounce = gpGlobals->curtime + 0.1f;
+ m_flCheckSuppressTime = gpGlobals->curtime + 0.25f;
+}
+
+bool UTIL_IsCombineBall( CBaseEntity *pEntity );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pTarget -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponPhysCannon::CanPickupObject( CBaseEntity *pTarget )
+{
+#ifndef CLIENT_DLL
+ if ( pTarget == NULL )
+ return false;
+
+ if ( pTarget->GetBaseAnimating() && pTarget->GetBaseAnimating()->IsDissolving() )
+ return false;
+
+ if ( pTarget->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) )
+ return false;
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner && pOwner->GetGroundEntity() == pTarget )
+ return false;
+
+ if ( pTarget->VPhysicsIsFlesh( ) )
+ return false;
+
+ IPhysicsObject *pObj = pTarget->VPhysicsGetObject();
+
+ if ( pObj && pObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
+ return false;
+
+ if ( UTIL_IsCombineBall( pTarget ) )
+ {
+ return CBasePlayer::CanPickupObject( pTarget, 0, 0 );
+ }
+
+ return CBasePlayer::CanPickupObject( pTarget, physcannon_maxmass.GetFloat(), 0 );
+#else
+ return false;
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::OpenElements( void )
+{
+ if ( m_bOpen )
+ return;
+
+ WeaponSound( SPECIAL2 );
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ SendWeaponAnim( ACT_VM_IDLE );
+
+ m_bOpen = true;
+
+ DoEffect( EFFECT_READY );
+
+#ifdef CLIENT
+ // Element prediction
+ m_ElementParameter.InitFromCurrent( 1.0f, 0.2f, INTERP_SPLINE );
+ m_bOldOpen = true;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::CloseElements( void )
+{
+ if ( m_bOpen == false )
+ return;
+
+ WeaponSound( MELEE_HIT );
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ SendWeaponAnim( ACT_VM_IDLE );
+
+ m_bOpen = false;
+
+ if ( GetMotorSound() )
+ {
+ (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f );
+ (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f );
+ }
+
+ DoEffect( EFFECT_CLOSED );
+
+#ifdef CLIENT
+ // Element prediction
+ m_ElementParameter.InitFromCurrent( 0.0f, 0.5f, INTERP_SPLINE );
+ m_bOldOpen = false;
+#endif
+}
+
+#define PHYSCANNON_MAX_MASS 500
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CWeaponPhysCannon::GetLoadPercentage( void )
+{
+ float loadWeight = m_grabController.GetLoadWeight();
+ loadWeight /= physcannon_maxmass.GetFloat();
+ loadWeight = clamp( loadWeight, 0.0f, 1.0f );
+ return loadWeight;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CSoundPatch
+//-----------------------------------------------------------------------------
+CSoundPatch *CWeaponPhysCannon::GetMotorSound( void )
+{
+ if ( m_sndMotor == NULL )
+ {
+ CPASAttenuationFilter filter( this );
+
+ m_sndMotor = (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_STATIC, "Weapon_PhysCannon.HoldSound", ATTN_NORM );
+ }
+
+ return m_sndMotor;
+}
+
+
+//-----------------------------------------------------------------------------
+// Shuts down sounds
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::StopLoopingSounds()
+{
+ if ( m_sndMotor != NULL )
+ {
+ (CSoundEnvelopeController::GetController()).SoundDestroy( m_sndMotor );
+ m_sndMotor = NULL;
+ }
+
+#ifndef CLIENT_DLL
+ BaseClass::StopLoopingSounds();
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DestroyEffects( void )
+{
+#ifdef CLIENT_DLL
+
+ // Free our beams
+ m_Beams[0].Release();
+ m_Beams[1].Release();
+ m_Beams[2].Release();
+
+#endif
+
+ // Stop everything
+ StopEffects();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::StopEffects( bool stopSound )
+{
+ // Turn off our effect state
+ DoEffect( EFFECT_NONE );
+
+#ifndef CLIENT_DLL
+ //Shut off sounds
+ if ( stopSound && GetMotorSound() != NULL )
+ {
+ (CSoundEnvelopeController::GetController()).SoundFadeOut( GetMotorSound(), 0.1f );
+ }
+#endif // !CLIENT_DLL
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::StartEffects( void )
+{
+#ifdef CLIENT_DLL
+
+ // ------------------------------------------
+ // Core
+ // ------------------------------------------
+
+ if ( m_Parameters[PHYSCANNON_CORE].GetMaterial() == NULL )
+ {
+ m_Parameters[PHYSCANNON_CORE].GetScale().Init( 0.0f, 1.0f, 0.1f );
+ m_Parameters[PHYSCANNON_CORE].GetAlpha().Init( 255.0f, 255.0f, 0.1f );
+ m_Parameters[PHYSCANNON_CORE].SetAttachment( 1 );
+
+ if ( m_Parameters[PHYSCANNON_CORE].SetMaterial( PHYSCANNON_CENTER_GLOW ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+ // ------------------------------------------
+ // Blast
+ // ------------------------------------------
+
+ if ( m_Parameters[PHYSCANNON_BLAST].GetMaterial() == NULL )
+ {
+ m_Parameters[PHYSCANNON_BLAST].GetScale().Init( 0.0f, 1.0f, 0.1f );
+ m_Parameters[PHYSCANNON_BLAST].GetAlpha().Init( 255.0f, 255.0f, 0.1f );
+ m_Parameters[PHYSCANNON_BLAST].SetAttachment( 1 );
+ m_Parameters[PHYSCANNON_BLAST].SetVisible( false );
+
+ if ( m_Parameters[PHYSCANNON_BLAST].SetMaterial( PHYSCANNON_BLAST_SPRITE ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+ // ------------------------------------------
+ // Glows
+ // ------------------------------------------
+
+ const char *attachNamesGlowThirdPerson[NUM_GLOW_SPRITES] =
+ {
+ "fork1m",
+ "fork1t",
+ "fork2m",
+ "fork2t",
+ "fork3m",
+ "fork3t",
+ };
+
+ const char *attachNamesGlow[NUM_GLOW_SPRITES] =
+ {
+ "fork1b",
+ "fork1m",
+ "fork1t",
+ "fork2b",
+ "fork2m",
+ "fork2t"
+ };
+
+ //Create the glow sprites
+ for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ if ( m_Parameters[i].GetMaterial() != NULL )
+ continue;
+
+ m_Parameters[i].GetScale().SetAbsolute( 0.05f * SPRITE_SCALE );
+ m_Parameters[i].GetAlpha().SetAbsolute( 64.0f );
+
+ // Different for different views
+ if ( ShouldDrawUsingViewModel() )
+ {
+ m_Parameters[i].SetAttachment( LookupAttachment( attachNamesGlow[i-PHYSCANNON_GLOW1] ) );
+ }
+ else
+ {
+ m_Parameters[i].SetAttachment( LookupAttachment( attachNamesGlowThirdPerson[i-PHYSCANNON_GLOW1] ) );
+ }
+ m_Parameters[i].SetColor( Vector( 255, 128, 0 ) );
+
+ if ( m_Parameters[i].SetMaterial( PHYSCANNON_GLOW_SPRITE ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+ // ------------------------------------------
+ // End caps
+ // ------------------------------------------
+
+ const char *attachNamesEndCap[NUM_ENDCAP_SPRITES] =
+ {
+ "fork1t",
+ "fork2t",
+ "fork3t"
+ };
+
+ //Create the glow sprites
+ for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ if ( m_Parameters[i].GetMaterial() != NULL )
+ continue;
+
+ m_Parameters[i].GetScale().SetAbsolute( 0.05f * SPRITE_SCALE );
+ m_Parameters[i].GetAlpha().SetAbsolute( 255.0f );
+ m_Parameters[i].SetAttachment( LookupAttachment( attachNamesEndCap[i-PHYSCANNON_ENDCAP1] ) );
+ m_Parameters[i].SetVisible( false );
+
+ if ( m_Parameters[i].SetMaterial( PHYSCANNON_ENDCAP_SPRITE ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Closing effects
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DoEffectClosed( void )
+{
+
+#ifdef CLIENT_DLL
+
+ // Turn off the end-caps
+ for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ m_Parameters[i].SetVisible( false );
+ }
+
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Ready effects
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DoEffectReady( void )
+{
+
+#ifdef CLIENT_DLL
+
+ // Special POV case
+ if ( ShouldDrawUsingViewModel() )
+ {
+ //Turn on the center sprite
+ m_Parameters[PHYSCANNON_CORE].GetScale().InitFromCurrent( 14.0f, 0.2f );
+ m_Parameters[PHYSCANNON_CORE].GetAlpha().InitFromCurrent( 128.0f, 0.2f );
+ m_Parameters[PHYSCANNON_CORE].SetVisible();
+ }
+ else
+ {
+ //Turn off the center sprite
+ m_Parameters[PHYSCANNON_CORE].GetScale().InitFromCurrent( 8.0f, 0.2f );
+ m_Parameters[PHYSCANNON_CORE].GetAlpha().InitFromCurrent( 0.0f, 0.2f );
+ m_Parameters[PHYSCANNON_CORE].SetVisible();
+ }
+
+ // Turn on the glow sprites
+ for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ m_Parameters[i].GetScale().InitFromCurrent( 0.4f * SPRITE_SCALE, 0.2f );
+ m_Parameters[i].GetAlpha().InitFromCurrent( 64.0f, 0.2f );
+ m_Parameters[i].SetVisible();
+ }
+
+ // Turn on the glow sprites
+ for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ m_Parameters[i].SetVisible( false );
+ }
+
+#endif
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Holding effects
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DoEffectHolding( void )
+{
+
+#ifdef CLIENT_DLL
+
+ if ( ShouldDrawUsingViewModel() )
+ {
+ // Scale up the center sprite
+ m_Parameters[PHYSCANNON_CORE].GetScale().InitFromCurrent( 16.0f, 0.2f );
+ m_Parameters[PHYSCANNON_CORE].GetAlpha().InitFromCurrent( 255.0f, 0.1f );
+ m_Parameters[PHYSCANNON_CORE].SetVisible();
+
+ // Prepare for scale up
+ m_Parameters[PHYSCANNON_BLAST].SetVisible( false );
+
+ // Turn on the glow sprites
+ for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ m_Parameters[i].GetScale().InitFromCurrent( 0.5f * SPRITE_SCALE, 0.2f );
+ m_Parameters[i].GetAlpha().InitFromCurrent( 64.0f, 0.2f );
+ m_Parameters[i].SetVisible();
+ }
+
+ // Turn on the glow sprites
+ // NOTE: The last glow is left off for first-person
+ for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES-1); i++ )
+ {
+ m_Parameters[i].SetVisible();
+ }
+
+ // Create our beams
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ CBaseEntity *pBeamEnt = pOwner->GetViewModel();
+
+ // Setup the beams
+ m_Beams[0].Init( LookupAttachment( "fork1t" ), 1, pBeamEnt, true );
+ m_Beams[1].Init( LookupAttachment( "fork2t" ), 1, pBeamEnt, true );
+
+ // Set them visible
+ m_Beams[0].SetVisible();
+ m_Beams[1].SetVisible();
+ }
+ else
+ {
+ // Scale up the center sprite
+ m_Parameters[PHYSCANNON_CORE].GetScale().InitFromCurrent( 14.0f, 0.2f );
+ m_Parameters[PHYSCANNON_CORE].GetAlpha().InitFromCurrent( 255.0f, 0.1f );
+ m_Parameters[PHYSCANNON_CORE].SetVisible();
+
+ // Prepare for scale up
+ m_Parameters[PHYSCANNON_BLAST].SetVisible( false );
+
+ // Turn on the glow sprites
+ for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ m_Parameters[i].GetScale().InitFromCurrent( 0.5f * SPRITE_SCALE, 0.2f );
+ m_Parameters[i].GetAlpha().InitFromCurrent( 64.0f, 0.2f );
+ m_Parameters[i].SetVisible();
+ }
+
+ // Turn on the glow sprites
+ for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ m_Parameters[i].SetVisible();
+ }
+
+ // Setup the beams
+ m_Beams[0].Init( LookupAttachment( "fork1t" ), 1, this, false );
+ m_Beams[1].Init( LookupAttachment( "fork2t" ), 1, this, false );
+ m_Beams[2].Init( LookupAttachment( "fork3t" ), 1, this, false );
+
+ // Set them visible
+ m_Beams[0].SetVisible();
+ m_Beams[1].SetVisible();
+ m_Beams[2].SetVisible();
+ }
+
+#endif
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Launch effects
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DoEffectLaunch( Vector *pos )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ if ( pOwner == NULL )
+ return;
+
+ Vector endPos;
+ Vector shotDir;
+
+ // See if we need to predict this position
+ if ( pos == NULL )
+ {
+ // Hit an entity if we're holding one
+ if ( m_hAttachedObject )
+ {
+ endPos = m_hAttachedObject->WorldSpaceCenter();
+
+ shotDir = endPos - pOwner->Weapon_ShootPosition();
+ VectorNormalize( shotDir );
+ }
+ else
+ {
+ // Otherwise try and find the right spot
+ endPos = pOwner->Weapon_ShootPosition();
+ pOwner->EyeVectors( &shotDir );
+
+ trace_t tr;
+ UTIL_TraceLine( endPos, endPos + ( shotDir * MAX_TRACE_LENGTH ), MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr );
+
+ endPos = tr.endpos;
+ shotDir = endPos - pOwner->Weapon_ShootPosition();
+ VectorNormalize( shotDir );
+ }
+ }
+ else
+ {
+ // Use what is supplied
+ endPos = *pos;
+ shotDir = ( endPos - pOwner->Weapon_ShootPosition() );
+ VectorNormalize( shotDir );
+ }
+
+ // End hit
+ CPVSFilter filter( endPos );
+
+ // Don't send this to the owning player, they already had it predicted
+ if ( IsPredicted() )
+ {
+ filter.UsePredictionRules();
+ }
+
+ // Do an impact hit
+ CEffectData data;
+ data.m_vOrigin = endPos;
+#ifdef CLIENT_DLL
+ data.m_hEntity = GetRefEHandle();
+#else
+ data.m_nEntIndex = entindex();
+#endif
+
+ te->DispatchEffect( filter, 0.0, data.m_vOrigin, "PhyscannonImpact", data );
+
+#ifdef CLIENT_DLL
+
+ //Turn on the blast sprite and scale
+ m_Parameters[PHYSCANNON_BLAST].GetScale().Init( 8.0f, 64.0f, 0.1f );
+ m_Parameters[PHYSCANNON_BLAST].GetAlpha().Init( 255.0f, 0.0f, 0.2f );
+ m_Parameters[PHYSCANNON_BLAST].SetVisible();
+
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Shutdown for the weapon when it's holstered
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DoEffectNone( void )
+{
+#ifdef CLIENT_DLL
+
+ //Turn off main glows
+ m_Parameters[PHYSCANNON_CORE].SetVisible( false );
+ m_Parameters[PHYSCANNON_BLAST].SetVisible( false );
+
+ for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ m_Parameters[i].SetVisible( false );
+ }
+
+ // Turn on the glow sprites
+ for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ m_Parameters[i].SetVisible( false );
+ }
+
+ m_Beams[0].SetVisible( false );
+ m_Beams[1].SetVisible( false );
+ m_Beams[2].SetVisible( false );
+
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : effectType -
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DoEffect( int effectType, Vector *pos )
+{
+ m_EffectState = effectType;
+
+#ifdef CLIENT_DLL
+ // Save predicted state
+ m_nOldEffectState = m_EffectState;
+#endif
+
+ switch( effectType )
+ {
+ case EFFECT_CLOSED:
+ DoEffectClosed( );
+ break;
+
+ case EFFECT_READY:
+ DoEffectReady( );
+ break;
+
+ case EFFECT_HOLDING:
+ DoEffectHolding();
+ break;
+
+ case EFFECT_LAUNCH:
+ DoEffectLaunch( pos );
+ break;
+
+ default:
+ case EFFECT_NONE:
+ DoEffectNone();
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iIndex -
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CWeaponPhysCannon::GetShootSound( int iIndex ) const
+{
+ return BaseClass::GetShootSound( iIndex );
+}
+
+#ifdef CLIENT_DLL
+
+extern void FormatViewModelAttachment( Vector &vOrigin, bool bInverse );
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the complete list of values needed to render an effect from an
+// effect parameter
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::GetEffectParameters( EffectType_t effectID, color32 &color, float &scale, IMaterial **pMaterial, Vector &vecAttachment )
+{
+ const float dt = gpGlobals->curtime;
+
+ // Get alpha
+ float alpha = m_Parameters[effectID].GetAlpha().Interp( dt );
+
+ // Get scale
+ scale = m_Parameters[effectID].GetScale().Interp( dt );
+
+ // Get material
+ *pMaterial = (IMaterial *) m_Parameters[effectID].GetMaterial();
+
+ // Setup the color
+ color.r = (int) m_Parameters[effectID].GetColor().x;
+ color.g = (int) m_Parameters[effectID].GetColor().y;
+ color.b = (int) m_Parameters[effectID].GetColor().z;
+ color.a = (int) alpha;
+
+ // Setup the attachment
+ int attachment = m_Parameters[effectID].GetAttachment();
+ QAngle angles;
+
+ // Format for first-person
+ if ( ShouldDrawUsingViewModel() )
+ {
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner != NULL )
+ {
+ pOwner->GetViewModel()->GetAttachment( attachment, vecAttachment, angles );
+ ::FormatViewModelAttachment( vecAttachment, true );
+ }
+ }
+ else
+ {
+ GetAttachment( attachment, vecAttachment, angles );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Whether or not an effect is set to display
+//-----------------------------------------------------------------------------
+bool CWeaponPhysCannon::IsEffectVisible( EffectType_t effectID )
+{
+ return m_Parameters[effectID].IsVisible();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draws the effect sprite, given an effect parameter ID
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DrawEffectSprite( EffectType_t effectID )
+{
+ color32 color;
+ float scale;
+ IMaterial *pMaterial;
+ Vector vecAttachment;
+
+ // Don't draw invisible effects
+ if ( IsEffectVisible( effectID ) == false )
+ return;
+
+ // Get all of our parameters
+ GetEffectParameters( effectID, color, scale, &pMaterial, vecAttachment );
+
+ // Msg( "Scale: %.2f\tAlpha: %.2f\n", scale, alpha );
+
+ // Don't render fully translucent objects
+ if ( color.a <= 0.0f )
+ return;
+
+ // Draw the sprite
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( pMaterial, this );
+ DrawSprite( vecAttachment, scale, scale, color );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Render our third-person effects
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DrawEffects( void )
+{
+ // Draw the core effects
+ DrawEffectSprite( PHYSCANNON_CORE );
+ DrawEffectSprite( PHYSCANNON_BLAST );
+
+ // Draw the glows
+ for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ DrawEffectSprite( (EffectType_t) i );
+ }
+
+ // Draw the endcaps
+ for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ DrawEffectSprite( (EffectType_t) i );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Third-person function call to render world model
+//-----------------------------------------------------------------------------
+int CWeaponPhysCannon::DrawModel( int flags )
+{
+ // Only render these on the transparent pass
+ if ( flags & STUDIO_TRANSPARENCY )
+ {
+ DrawEffects();
+ return 1;
+ }
+
+ // Only do this on the opaque pass
+ return BaseClass::DrawModel( flags );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: First-person function call after viewmodel has been drawn
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::ViewModelDrawn( C_BaseViewModel *pBaseViewModel )
+{
+ // Render our effects
+ DrawEffects();
+
+ // Pass this back up
+ BaseClass::ViewModelDrawn( pBaseViewModel );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: We are always considered transparent
+//-----------------------------------------------------------------------------
+bool CWeaponPhysCannon::IsTransparent( void )
+{
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::NotifyShouldTransmit( ShouldTransmitState_t state )
+{
+ BaseClass::NotifyShouldTransmit(state);
+
+ if ( state == SHOULDTRANSMIT_END )
+ {
+ DoEffect( EFFECT_NONE );
+ }
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// EXTERNAL API
+//-----------------------------------------------------------------------------
+void PhysCannonForceDrop( CBaseCombatWeapon *pActiveWeapon, CBaseEntity *pOnlyIfHoldingThis )
+{
+ CWeaponPhysCannon *pCannon = dynamic_cast<CWeaponPhysCannon *>(pActiveWeapon);
+ if ( pCannon )
+ {
+ if ( pOnlyIfHoldingThis )
+ {
+ pCannon->DropIfEntityHeld( pOnlyIfHoldingThis );
+ }
+ else
+ {
+ pCannon->ForceDrop();
+ }
+ }
+}
+
+bool PlayerPickupControllerIsHoldingEntity( CBaseEntity *pPickupControllerEntity, CBaseEntity *pHeldEntity )
+{
+ CPlayerPickupController *pController = dynamic_cast<CPlayerPickupController *>(pPickupControllerEntity);
+
+ return pController ? pController->IsHoldingEntity( pHeldEntity ) : false;
+}
+
+
+float PhysCannonGetHeldObjectMass( CBaseCombatWeapon *pActiveWeapon, IPhysicsObject *pHeldObject )
+{
+ float mass = 0.0f;
+ CWeaponPhysCannon *pCannon = dynamic_cast<CWeaponPhysCannon *>(pActiveWeapon);
+ if ( pCannon )
+ {
+ CGrabController &grab = pCannon->GetGrabController();
+ mass = grab.GetSavedMass( pHeldObject );
+ }
+
+ return mass;
+}
+
+CBaseEntity *PhysCannonGetHeldEntity( CBaseCombatWeapon *pActiveWeapon )
+{
+ CWeaponPhysCannon *pCannon = dynamic_cast<CWeaponPhysCannon *>(pActiveWeapon);
+ if ( pCannon )
+ {
+ CGrabController &grab = pCannon->GetGrabController();
+ return grab.GetAttached();
+ }
+
+ return NULL;
+}
+
+float PlayerPickupGetHeldObjectMass( CBaseEntity *pPickupControllerEntity, IPhysicsObject *pHeldObject )
+{
+ float mass = 0.0f;
+ CPlayerPickupController *pController = dynamic_cast<CPlayerPickupController *>(pPickupControllerEntity);
+ if ( pController )
+ {
+ CGrabController &grab = pController->GetGrabController();
+ mass = grab.GetSavedMass( pHeldObject );
+ }
+ return mass;
+}
+
+#ifdef CLIENT_DLL
+
+extern void FX_GaussExplosion( const Vector &pos, const Vector &dir, int type );
+
+void CallbackPhyscannonImpact( const CEffectData &data )
+{
+ C_BaseEntity *pEnt = data.GetEntity();
+ if ( pEnt == NULL )
+ return;
+
+ Vector vecAttachment;
+ QAngle vecAngles;
+
+ C_BaseCombatWeapon *pWeapon = dynamic_cast<C_BaseCombatWeapon *>(pEnt);
+
+ if ( pWeapon == NULL )
+ return;
+
+ pWeapon->GetAttachment( 1, vecAttachment, vecAngles );
+
+ Vector dir = ( data.m_vOrigin - vecAttachment );
+ VectorNormalize( dir );
+
+ // Do special first-person fix-up
+ if ( pWeapon->GetOwner() == CBasePlayer::GetLocalPlayer() )
+ {
+ // Translate the attachment entity to the viewmodel
+ C_BasePlayer *pPlayer = dynamic_cast<C_BasePlayer *>(pWeapon->GetOwner());
+
+ if ( pPlayer )
+ {
+ pEnt = pPlayer->GetViewModel();
+ }
+
+ // Format attachment for first-person view!
+ ::FormatViewModelAttachment( vecAttachment, true );
+
+ // Explosions at the impact point
+ FX_GaussExplosion( data.m_vOrigin, -dir, 0 );
+
+ // Draw a beam
+ BeamInfo_t beamInfo;
+
+ beamInfo.m_pStartEnt = pEnt;
+ beamInfo.m_nStartAttachment = 1;
+ beamInfo.m_pEndEnt = NULL;
+ beamInfo.m_nEndAttachment = -1;
+ beamInfo.m_vecStart = vec3_origin;
+ beamInfo.m_vecEnd = data.m_vOrigin;
+ beamInfo.m_pszModelName = PHYSCANNON_BEAM_SPRITE;
+ beamInfo.m_flHaloScale = 0.0f;
+ beamInfo.m_flLife = 0.1f;
+ beamInfo.m_flWidth = 12.0f;
+ beamInfo.m_flEndWidth = 4.0f;
+ beamInfo.m_flFadeLength = 0.0f;
+ beamInfo.m_flAmplitude = 0;
+ beamInfo.m_flBrightness = 255.0;
+ beamInfo.m_flSpeed = 0.0f;
+ beamInfo.m_nStartFrame = 0.0;
+ beamInfo.m_flFrameRate = 30.0;
+ beamInfo.m_flRed = 255.0;
+ beamInfo.m_flGreen = 255.0;
+ beamInfo.m_flBlue = 255.0;
+ beamInfo.m_nSegments = 16;
+ beamInfo.m_bRenderable = true;
+ beamInfo.m_nFlags = FBEAM_ONLYNOISEONCE;
+
+ beams->CreateBeamEntPoint( beamInfo );
+ }
+ else
+ {
+ // Explosion at the starting point
+ FX_GaussExplosion( vecAttachment, dir, 0 );
+ }
+}
+
+DECLARE_CLIENT_EFFECT( "PhyscannonImpact", CallbackPhyscannonImpact );
+
+#endif
|