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/server/physgun.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/server/physgun.cpp')
| -rw-r--r-- | mp/src/game/server/physgun.cpp | 1523 |
1 files changed, 1523 insertions, 0 deletions
diff --git a/mp/src/game/server/physgun.cpp b/mp/src/game/server/physgun.cpp new file mode 100644 index 00000000..3ed9d1ed --- /dev/null +++ b/mp/src/game/server/physgun.cpp @@ -0,0 +1,1523 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "beam_shared.h"
+#include "player.h"
+#include "gamerules.h"
+#include "basecombatweapon.h"
+#include "baseviewmodel.h"
+#include "vphysics/constraints.h"
+#include "physics.h"
+#include "in_buttons.h"
+#include "IEffects.h"
+#include "engine/IEngineSound.h"
+#include "ndebugoverlay.h"
+#include "physics_saverestore.h"
+#include "player_pickup.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+ConVar phys_gunmass("phys_gunmass", "200");
+ConVar phys_gunvel("phys_gunvel", "400");
+ConVar phys_gunforce("phys_gunforce", "5e5" );
+ConVar phys_guntorque("phys_guntorque", "100" );
+ConVar phys_gunglueradius("phys_gunglueradius", "128" );
+
+static int g_physgunBeam;
+#define PHYSGUN_BEAM_SPRITE "sprites/physbeam.vmt"
+
+#define MAX_PELLETS 16
+
+class CWeaponGravityGun;
+
+class CGravityPellet : public CBaseAnimating
+{
+ DECLARE_CLASS( CGravityPellet, CBaseAnimating );
+public:
+ DECLARE_DATADESC();
+
+ ~CGravityPellet();
+ void Precache()
+ {
+ SetModelName( MAKE_STRING( "models/weapons/glueblob.mdl" ) );
+ PrecacheModel( STRING( GetModelName() ) );
+ BaseClass::Precache();
+ }
+ void Spawn()
+ {
+ Precache();
+ SetModel( STRING( GetModelName() ) );
+ SetSolid( SOLID_NONE );
+ SetMoveType( MOVETYPE_NONE );
+ AddEffects( EF_NOSHADOW );
+ SetRenderColor( 255, 0, 0 );
+ m_isInert = false;
+ }
+
+ bool IsInert()
+ {
+ return m_isInert;
+ }
+
+ bool MakeConstraint( CBaseEntity *pObject )
+ {
+ IPhysicsObject *pReference = g_PhysWorldObject;
+ if ( GetMoveParent() )
+ {
+ pReference = GetMoveParent()->VPhysicsGetObject();
+ }
+ IPhysicsObject *pAttached = pObject->VPhysicsGetObject();
+ if ( !pReference || !pAttached )
+ {
+ return false;
+ }
+
+ constraint_fixedparams_t fixed;
+ fixed.Defaults();
+ fixed.InitWithCurrentObjectState( pReference, pAttached );
+
+ m_pConstraint = physenv->CreateFixedConstraint( pReference, pAttached, NULL, fixed );
+ m_pConstraint->SetGameData( (void *)this );
+
+ MakeInert();
+ return true;
+ }
+
+ void MakeInert()
+ {
+ SetRenderColor( 64, 64, 128 );
+ m_isInert = true;
+ }
+
+ void InputOnBreak( inputdata_t &inputdata )
+ {
+ UTIL_Remove(this);
+ }
+
+ IPhysicsConstraint *m_pConstraint;
+ bool m_isInert;
+};
+
+LINK_ENTITY_TO_CLASS(gravity_pellet, CGravityPellet);
+PRECACHE_REGISTER(gravity_pellet);
+
+BEGIN_DATADESC( CGravityPellet )
+
+ DEFINE_PHYSPTR( m_pConstraint ),
+ DEFINE_FIELD( m_isInert, FIELD_BOOLEAN ),
+ // physics system will fire this input if the constraint breaks due to physics
+ DEFINE_INPUTFUNC( FIELD_VOID, "ConstraintBroken", InputOnBreak ),
+
+END_DATADESC()
+
+
+CGravityPellet::~CGravityPellet()
+{
+ if ( m_pConstraint )
+ {
+ physenv->DestroyConstraint( m_pConstraint );
+ }
+}
+
+class CGravControllerPoint : public IMotionEvent
+{
+ DECLARE_SIMPLE_DATADESC();
+
+public:
+ CGravControllerPoint( void );
+ ~CGravControllerPoint( void );
+ void AttachEntity( CBaseEntity *pEntity, IPhysicsObject *pPhys, const Vector &position );
+ void DetachEntity( void );
+ void SetMaxVelocity( float maxVel )
+ {
+ m_maxVel = maxVel;
+ }
+ void SetTargetPosition( const Vector &target )
+ {
+ m_targetPosition = target;
+ if ( m_attachedEntity == NULL )
+ {
+ m_worldPosition = target;
+ }
+ m_timeToArrive = gpGlobals->frametime;
+ }
+
+ void SetAutoAlign( const Vector &localDir, const Vector &localPos, const Vector &worldAlignDir, const Vector &worldAlignPos )
+ {
+ m_align = true;
+ m_localAlignNormal = -localDir;
+ m_localAlignPosition = localPos;
+ m_targetAlignNormal = worldAlignDir;
+ m_targetAlignPosition = worldAlignPos;
+ }
+
+ void ClearAutoAlign()
+ {
+ m_align = false;
+ }
+
+ IMotionEvent::simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
+ Vector m_localPosition;
+ Vector m_targetPosition;
+ Vector m_worldPosition;
+ Vector m_localAlignNormal;
+ Vector m_localAlignPosition;
+ Vector m_targetAlignNormal;
+ Vector m_targetAlignPosition;
+ bool m_align;
+ float m_saveDamping;
+ float m_maxVel;
+ float m_maxAcceleration;
+ Vector m_maxAngularAcceleration;
+ EHANDLE m_attachedEntity;
+ QAngle m_targetRotation;
+ float m_timeToArrive;
+
+ IPhysicsMotionController *m_controller;
+};
+
+
+BEGIN_SIMPLE_DATADESC( CGravControllerPoint )
+
+ DEFINE_FIELD( m_localPosition, FIELD_VECTOR ),
+ DEFINE_FIELD( m_targetPosition, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_worldPosition, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_localAlignNormal, FIELD_VECTOR ),
+ DEFINE_FIELD( m_localAlignPosition, FIELD_VECTOR ),
+ DEFINE_FIELD( m_targetAlignNormal, FIELD_VECTOR ),
+ DEFINE_FIELD( m_targetAlignPosition, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_align, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_saveDamping, FIELD_FLOAT ),
+ DEFINE_FIELD( m_maxVel, FIELD_FLOAT ),
+ DEFINE_FIELD( m_maxAcceleration, FIELD_FLOAT ),
+ DEFINE_FIELD( m_maxAngularAcceleration, FIELD_VECTOR ),
+ DEFINE_FIELD( m_attachedEntity, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_targetRotation, FIELD_VECTOR ),
+ DEFINE_FIELD( m_timeToArrive, FIELD_FLOAT ),
+
+ // Physptrs can't be saved in embedded classes... this is to silence classcheck
+ // DEFINE_PHYSPTR( m_controller ),
+
+END_DATADESC()
+
+
+CGravControllerPoint::CGravControllerPoint( void )
+{
+ m_attachedEntity = NULL;
+}
+
+CGravControllerPoint::~CGravControllerPoint( void )
+{
+ DetachEntity();
+}
+
+
+void CGravControllerPoint::AttachEntity( CBaseEntity *pEntity, IPhysicsObject *pPhys, const Vector &position )
+{
+ m_attachedEntity = pEntity;
+ pPhys->WorldToLocal( &m_localPosition, position );
+ m_worldPosition = position;
+ pPhys->GetDamping( NULL, &m_saveDamping );
+ float damping = 2;
+ pPhys->SetDamping( NULL, &damping );
+ m_controller = physenv->CreateMotionController( this );
+ m_controller->AttachObject( pPhys, true );
+ m_controller->SetPriority( IPhysicsMotionController::HIGH_PRIORITY );
+ SetTargetPosition( position );
+ m_maxAcceleration = phys_gunforce.GetFloat() * pPhys->GetInvMass();
+ m_targetRotation = pEntity->GetAbsAngles();
+ float torque = phys_guntorque.GetFloat();
+ m_maxAngularAcceleration = torque * pPhys->GetInvInertia();
+}
+
+void CGravControllerPoint::DetachEntity( void )
+{
+ CBaseEntity *pEntity = m_attachedEntity;
+ if ( pEntity )
+ {
+ IPhysicsObject *pPhys = pEntity->VPhysicsGetObject();
+ if ( pPhys )
+ {
+ // on the odd chance that it's gone to sleep while under anti-gravity
+ pPhys->Wake();
+ pPhys->SetDamping( NULL, &m_saveDamping );
+ }
+ }
+ m_attachedEntity = NULL;
+ physenv->DestroyMotionController( m_controller );
+ m_controller = NULL;
+
+ // UNDONE: Does this help the networking?
+ m_targetPosition = vec3_origin;
+ m_worldPosition = vec3_origin;
+}
+
+void AxisAngleQAngle( const Vector &axis, float angle, QAngle &outAngles )
+{
+ // map back to HL rotation axes
+ outAngles.z = axis.x * angle;
+ outAngles.x = axis.y * angle;
+ outAngles.y = axis.z * angle;
+}
+
+IMotionEvent::simresult_e CGravControllerPoint::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
+{
+ Vector vel;
+ AngularImpulse angVel;
+
+ float fracRemainingSimTime = 1.0;
+ if ( m_timeToArrive > 0 )
+ {
+ fracRemainingSimTime *= deltaTime / m_timeToArrive;
+ if ( fracRemainingSimTime > 1 )
+ {
+ fracRemainingSimTime = 1;
+ }
+ }
+
+ m_timeToArrive -= deltaTime;
+ if ( m_timeToArrive < 0 )
+ {
+ m_timeToArrive = 0;
+ }
+
+ float invDeltaTime = (1.0f / deltaTime);
+ Vector world;
+ pObject->LocalToWorld( &world, m_localPosition );
+ m_worldPosition = world;
+ pObject->GetVelocity( &vel, &angVel );
+ //pObject->GetVelocityAtPoint( world, &vel );
+ float damping = 1.0;
+ world += vel * deltaTime * damping;
+ Vector delta = (m_targetPosition - world) * fracRemainingSimTime * invDeltaTime;
+ Vector alignDir;
+ linear = vec3_origin;
+ angular = vec3_origin;
+
+ if ( m_align )
+ {
+ QAngle angles;
+ Vector origin;
+ Vector axis;
+ AngularImpulse torque;
+
+ pObject->GetShadowPosition( &origin, &angles );
+ // align local normal to target normal
+ VMatrix tmp = SetupMatrixOrgAngles( origin, angles );
+ Vector worldNormal = tmp.VMul3x3( m_localAlignNormal );
+ axis = CrossProduct( worldNormal, m_targetAlignNormal );
+ float trig = VectorNormalize(axis);
+ float alignRotation = RAD2DEG(asin(trig));
+ axis *= alignRotation;
+ if ( alignRotation < 10 )
+ {
+ float dot = DotProduct( worldNormal, m_targetAlignNormal );
+ // probably 180 degrees off
+ if ( dot < 0 )
+ {
+ if ( worldNormal.x < 0.5 )
+ {
+ axis.Init(10,0,0);
+ }
+ else
+ {
+ axis.Init(0,0,10);
+ }
+ alignRotation = 10;
+ }
+ }
+
+ // Solve for the rotation around the target normal (at the local align pos) that will
+ // move the grabbed spot to the destination.
+ Vector worldRotCenter = tmp.VMul4x3( m_localAlignPosition );
+ Vector rotSrc = world - worldRotCenter;
+ Vector rotDest = m_targetPosition - worldRotCenter;
+
+ // Get a basis in the plane perpendicular to m_targetAlignNormal
+ Vector srcN = rotSrc;
+ VectorNormalize( srcN );
+ Vector tangent = CrossProduct( srcN, m_targetAlignNormal );
+ float len = VectorNormalize( tangent );
+
+ // needs at least ~5 degrees, or forget rotation (0.08 ~= sin(5))
+ if ( len > 0.08 )
+ {
+ Vector binormal = CrossProduct( m_targetAlignNormal, tangent );
+
+ // Now project the src & dest positions into that plane
+ Vector planeSrc( DotProduct( rotSrc, tangent ), DotProduct( rotSrc, binormal ), 0 );
+ Vector planeDest( DotProduct( rotDest, tangent ), DotProduct( rotDest, binormal ), 0 );
+
+ float rotRadius = VectorNormalize( planeSrc );
+ float destRadius = VectorNormalize( planeDest );
+ if ( rotRadius > 0.1 )
+ {
+ if ( destRadius < rotRadius )
+ {
+ destRadius = rotRadius;
+ }
+ //float ratio = rotRadius / destRadius;
+ float angleSrc = atan2( planeSrc.y, planeSrc.x );
+ float angleDest = atan2( planeDest.y, planeDest.x );
+ float angleDiff = angleDest - angleSrc;
+ angleDiff = RAD2DEG(angleDiff);
+ axis += m_targetAlignNormal * angleDiff;
+ //world = m_targetPosition;// + rotDest * (1-ratio);
+// NDebugOverlay::Line( worldRotCenter, worldRotCenter-m_targetAlignNormal*50, 255, 0, 0, false, 0.1 );
+// NDebugOverlay::Line( worldRotCenter, worldRotCenter+tangent*50, 0, 255, 0, false, 0.1 );
+// NDebugOverlay::Line( worldRotCenter, worldRotCenter+binormal*50, 0, 0, 255, false, 0.1 );
+ }
+ }
+
+ torque = WorldToLocalRotation( tmp, axis, 1 );
+ torque *= fracRemainingSimTime * invDeltaTime;
+ torque -= angVel * 1.0; // damping
+ for ( int i = 0; i < 3; i++ )
+ {
+ if ( torque[i] > 0 )
+ {
+ if ( torque[i] > m_maxAngularAcceleration[i] )
+ torque[i] = m_maxAngularAcceleration[i];
+ }
+ else
+ {
+ if ( torque[i] < -m_maxAngularAcceleration[i] )
+ torque[i] = -m_maxAngularAcceleration[i];
+ }
+ }
+ torque *= invDeltaTime;
+ angular += torque;
+ // Calculate an acceleration that pulls the object toward the constraint
+ // When you're out of alignment, don't pull very hard
+ float factor = fabsf(alignRotation);
+ if ( factor < 5 )
+ {
+ factor = clamp( factor, 0, 5 ) * (1/5);
+ alignDir = m_targetAlignPosition - worldRotCenter;
+ // Limit movement to the part along m_targetAlignNormal if worldRotCenter is on the backside of
+ // of the target plane (one inch epsilon)!
+ float planeForward = DotProduct( alignDir, m_targetAlignNormal );
+ if ( planeForward > 1 )
+ {
+ alignDir = m_targetAlignNormal * planeForward;
+ }
+ Vector accel = alignDir * invDeltaTime * fracRemainingSimTime * (1-factor) * 0.20 * invDeltaTime;
+ float mag = accel.Length();
+ if ( mag > m_maxAcceleration )
+ {
+ accel *= (m_maxAcceleration/mag);
+ }
+ linear += accel;
+ }
+ linear -= vel*damping*invDeltaTime;
+ // UNDONE: Factor in the change in worldRotCenter due to applied torque!
+ }
+ else
+ {
+ // clamp future velocity to max speed
+ Vector nextVel = delta + vel;
+ float nextSpeed = nextVel.Length();
+ if ( nextSpeed > m_maxVel )
+ {
+ nextVel *= (m_maxVel / nextSpeed);
+ delta = nextVel - vel;
+ }
+
+ delta *= invDeltaTime;
+
+ float linearAccel = delta.Length();
+ if ( linearAccel > m_maxAcceleration )
+ {
+ delta *= m_maxAcceleration / linearAccel;
+ }
+
+ Vector accel;
+ AngularImpulse angAccel;
+ pObject->CalculateForceOffset( delta, world, &accel, &angAccel );
+
+ linear += accel;
+ angular += angAccel;
+ }
+
+ return SIM_GLOBAL_ACCELERATION;
+}
+
+
+struct pelletlist_t
+{
+ DECLARE_SIMPLE_DATADESC();
+
+ Vector localNormal; // normal in parent space
+ CHandle<CGravityPellet> pellet;
+ EHANDLE parent;
+};
+
+class CWeaponGravityGun : public CBaseCombatWeapon
+{
+ DECLARE_DATADESC();
+
+public:
+ DECLARE_CLASS( CWeaponGravityGun, CBaseCombatWeapon );
+
+ CWeaponGravityGun();
+ void Spawn( void );
+ void OnRestore( void );
+ void Precache( void );
+
+ void PrimaryAttack( void );
+ void SecondaryAttack( void );
+ void WeaponIdle( void );
+ void ItemPostFrame( void );
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo )
+ {
+ EffectDestroy();
+ return BaseClass::Holster();
+ }
+
+ bool Reload( void );
+ void Equip( CBaseCombatCharacter *pOwner )
+ {
+ // add constraint ammo
+ pOwner->SetAmmoCount( MAX_PELLETS, m_iSecondaryAmmoType );
+ BaseClass::Equip( pOwner );
+ }
+ void Drop(const Vector &vecVelocity)
+ {
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ pOwner->SetAmmoCount( 0, m_iSecondaryAmmoType );
+ // destroy all constraints
+ BaseClass::Drop(vecVelocity);
+ }
+
+ bool HasAnyAmmo( void );
+
+ void AttachObject( CBaseEntity *pEdict, const Vector& start, const Vector &end, float distance );
+ void DetachObject( void );
+
+ void EffectCreate( void );
+ void EffectUpdate( void );
+ void EffectDestroy( void );
+
+ void SoundCreate( void );
+ void SoundDestroy( void );
+ void SoundStop( void );
+ void SoundStart( void );
+ void SoundUpdate( void );
+ void AddPellet( CGravityPellet *pPellet, CBaseEntity *pParent, const Vector &surfaceNormal );
+ void DeleteActivePellets();
+ void SortPelletsForObject( CBaseEntity *pObject );
+ void SetObjectPelletsColor( int r, int g, int b );
+ void CreatePelletAttraction( float radius, CBaseEntity *pObject );
+ IPhysicsObject *GetPelletPhysObject( int pelletIndex );
+ void GetPelletWorldCoords( int pelletIndex, Vector *worldPos, Vector *worldNormal )
+ {
+ if ( worldPos )
+ {
+ *worldPos = m_activePellets[pelletIndex].pellet->GetAbsOrigin();
+ }
+ if ( worldNormal )
+ {
+ if ( m_activePellets[pelletIndex].parent )
+ {
+ EntityMatrix tmp;
+ tmp.InitFromEntity( m_activePellets[pelletIndex].parent );
+ *worldNormal = tmp.LocalToWorldRotation( m_activePellets[pelletIndex].localNormal );
+ }
+ else
+ {
+ *worldNormal = m_activePellets[pelletIndex].localNormal;
+ }
+ }
+ }
+
+ int ObjectCaps( void )
+ {
+ int caps = BaseClass::ObjectCaps();
+ if ( m_active )
+ {
+ caps |= FCAP_DIRECTIONAL_USE;
+ }
+ return caps;
+ }
+
+ CBaseEntity *GetBeamEntity();
+
+ DECLARE_SERVERCLASS();
+
+private:
+ CNetworkVar( int, m_active );
+ bool m_useDown;
+ EHANDLE m_hObject;
+ float m_distance;
+ float m_movementLength;
+ float m_lastYaw;
+ int m_soundState;
+ CNetworkVar( int, m_viewModelIndex );
+ Vector m_originalObjectPosition;
+
+ CGravControllerPoint m_gravCallback;
+ pelletlist_t m_activePellets[MAX_PELLETS];
+ int m_pelletCount;
+ int m_objectPelletCount;
+
+ int m_pelletHeld;
+ int m_pelletAttract;
+ float m_glueTime;
+ CNetworkVar( bool, m_glueTouching );
+};
+
+IMPLEMENT_SERVERCLASS_ST( CWeaponGravityGun, DT_WeaponGravityGun )
+ SendPropVector( SENDINFO_NAME(m_gravCallback.m_targetPosition, m_targetPosition), -1, SPROP_COORD ),
+ SendPropVector( SENDINFO_NAME(m_gravCallback.m_worldPosition, m_worldPosition), -1, SPROP_COORD ),
+ SendPropInt( SENDINFO(m_active), 1, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO(m_glueTouching), 1, SPROP_UNSIGNED ),
+ SendPropModelIndex( SENDINFO(m_viewModelIndex) ),
+END_SEND_TABLE()
+
+LINK_ENTITY_TO_CLASS( weapon_physgun, CWeaponGravityGun );
+PRECACHE_WEAPON_REGISTER(weapon_physgun);
+
+//---------------------------------------------------------
+// Save/Restore
+//---------------------------------------------------------
+BEGIN_SIMPLE_DATADESC( pelletlist_t )
+
+ DEFINE_FIELD( localNormal, FIELD_VECTOR ),
+ DEFINE_FIELD( pellet, FIELD_EHANDLE ),
+ DEFINE_FIELD( parent, FIELD_EHANDLE ),
+
+END_DATADESC()
+
+BEGIN_DATADESC( CWeaponGravityGun )
+
+ DEFINE_FIELD( m_active, FIELD_INTEGER ),
+ DEFINE_FIELD( m_useDown, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_hObject, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_distance, FIELD_FLOAT ),
+ DEFINE_FIELD( m_movementLength, FIELD_FLOAT ),
+ DEFINE_FIELD( m_lastYaw, FIELD_FLOAT ),
+ DEFINE_FIELD( m_soundState, FIELD_INTEGER ),
+ DEFINE_FIELD( m_viewModelIndex, FIELD_INTEGER ),
+ DEFINE_FIELD( m_originalObjectPosition, FIELD_POSITION_VECTOR ),
+ DEFINE_EMBEDDED( m_gravCallback ),
+ // Physptrs can't be saved in embedded classes..
+ DEFINE_PHYSPTR( m_gravCallback.m_controller ),
+ DEFINE_EMBEDDED_AUTO_ARRAY( m_activePellets ),
+ DEFINE_FIELD( m_pelletCount, FIELD_INTEGER ),
+ DEFINE_FIELD( m_objectPelletCount, FIELD_INTEGER ),
+ DEFINE_FIELD( m_pelletHeld, FIELD_INTEGER ),
+ DEFINE_FIELD( m_pelletAttract, FIELD_INTEGER ),
+ DEFINE_FIELD( m_glueTime, FIELD_TIME ),
+ DEFINE_FIELD( m_glueTouching, FIELD_BOOLEAN ),
+
+END_DATADESC()
+
+
+enum physgun_soundstate { SS_SCANNING, SS_LOCKEDON };
+enum physgun_soundIndex { SI_LOCKEDON = 0, SI_SCANNING = 1, SI_LIGHTOBJECT = 2, SI_HEAVYOBJECT = 3, SI_ON, SI_OFF };
+
+
+//=========================================================
+//=========================================================
+
+CWeaponGravityGun::CWeaponGravityGun()
+{
+ m_active = false;
+ m_bFiresUnderwater = true;
+ m_pelletAttract = -1;
+ m_pelletHeld = -1;
+}
+
+//=========================================================
+//=========================================================
+void CWeaponGravityGun::Spawn( )
+{
+ BaseClass::Spawn();
+// SetModel( GetWorldModel() );
+
+ FallInit();
+}
+
+void CWeaponGravityGun::OnRestore( void )
+{
+ BaseClass::OnRestore();
+
+ if ( m_gravCallback.m_controller )
+ {
+ m_gravCallback.m_controller->SetEventHandler( &m_gravCallback );
+ }
+}
+
+
+//=========================================================
+//=========================================================
+void CWeaponGravityGun::Precache( void )
+{
+ BaseClass::Precache();
+
+ g_physgunBeam = PrecacheModel(PHYSGUN_BEAM_SPRITE);
+
+ PrecacheScriptSound( "Weapon_Physgun.Scanning" );
+ PrecacheScriptSound( "Weapon_Physgun.LockedOn" );
+ PrecacheScriptSound( "Weapon_Physgun.Scanning" );
+ PrecacheScriptSound( "Weapon_Physgun.LightObject" );
+ PrecacheScriptSound( "Weapon_Physgun.HeavyObject" );
+}
+
+void CWeaponGravityGun::EffectCreate( void )
+{
+ EffectUpdate();
+ m_active = true;
+}
+
+
+void CWeaponGravityGun::EffectUpdate( void )
+{
+ Vector start, angles, forward, right;
+ trace_t tr;
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ m_viewModelIndex = pOwner->entindex();
+ // Make sure I've got a view model
+ CBaseViewModel *vm = pOwner->GetViewModel();
+ if ( vm )
+ {
+ m_viewModelIndex = vm->entindex();
+ }
+
+ pOwner->EyeVectors( &forward, &right, NULL );
+
+ start = pOwner->Weapon_ShootPosition();
+ Vector end = start + forward * 4096;
+
+ UTIL_TraceLine( start, end, MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr );
+ end = tr.endpos;
+ float distance = tr.fraction * 4096;
+ if ( tr.fraction != 1 )
+ {
+ // too close to the player, drop the object
+ if ( distance < 36 )
+ {
+ DetachObject();
+ return;
+ }
+ }
+
+ if ( m_hObject == NULL && tr.DidHitNonWorldEntity() )
+ {
+ CBaseEntity *pEntity = tr.m_pEnt;
+ // inform the object what was hit
+ ClearMultiDamage();
+ pEntity->DispatchTraceAttack( CTakeDamageInfo( pOwner, pOwner, 0, DMG_PHYSGUN ), forward, &tr );
+ ApplyMultiDamage();
+ AttachObject( pEntity, start, tr.endpos, distance );
+ m_lastYaw = pOwner->EyeAngles().y;
+ }
+
+ // Add the incremental player yaw to the target transform
+ matrix3x4_t curMatrix, incMatrix, nextMatrix;
+ AngleMatrix( m_gravCallback.m_targetRotation, curMatrix );
+ AngleMatrix( QAngle(0,pOwner->EyeAngles().y - m_lastYaw,0), incMatrix );
+ ConcatTransforms( incMatrix, curMatrix, nextMatrix );
+ MatrixAngles( nextMatrix, m_gravCallback.m_targetRotation );
+ m_lastYaw = pOwner->EyeAngles().y;
+
+ CBaseEntity *pObject = m_hObject;
+ if ( pObject )
+ {
+ if ( m_useDown )
+ {
+ if ( pOwner->m_afButtonPressed & IN_USE )
+ {
+ m_useDown = false;
+ }
+ }
+ else
+ {
+ if ( pOwner->m_afButtonPressed & IN_USE )
+ {
+ m_useDown = true;
+ }
+ }
+
+ if ( m_useDown )
+ {
+ pOwner->SetPhysicsFlag( PFLAG_DIROVERRIDE, true );
+ if ( pOwner->m_nButtons & IN_FORWARD )
+ {
+ m_distance = UTIL_Approach( 1024, m_distance, gpGlobals->frametime * 100 );
+ }
+ if ( pOwner->m_nButtons & IN_BACK )
+ {
+ m_distance = UTIL_Approach( 40, m_distance, gpGlobals->frametime * 100 );
+ }
+ }
+
+ if ( pOwner->m_nButtons & IN_WEAPON1 )
+ {
+ m_distance = UTIL_Approach( 1024, m_distance, m_distance * 0.1 );
+ }
+ if ( pOwner->m_nButtons & IN_WEAPON2 )
+ {
+ m_distance = UTIL_Approach( 40, m_distance, m_distance * 0.1 );
+ }
+
+ // Send the object a physics damage message (0 damage). Some objects interpret this
+ // as something else being in control of their physics temporarily.
+ pObject->TakeDamage( CTakeDamageInfo( this, pOwner, 0, DMG_PHYSGUN ) );
+
+ Vector newPosition = start + forward * m_distance;
+ // 24 is a little larger than 16 * sqrt(2) (extent of player bbox)
+ // HACKHACK: We do this so we can "ignore" the player and the object we're manipulating
+ // If we had a filter for tracelines, we could simply filter both ents and start from "start"
+ Vector awayfromPlayer = start + forward * 24;
+
+ UTIL_TraceLine( start, awayfromPlayer, MASK_SOLID, pOwner, COLLISION_GROUP_NONE, &tr );
+ if ( tr.fraction == 1 )
+ {
+ UTIL_TraceLine( awayfromPlayer, newPosition, MASK_SOLID, pObject, COLLISION_GROUP_NONE, &tr );
+ Vector dir = tr.endpos - newPosition;
+ float distance = VectorNormalize(dir);
+ float maxDist = m_gravCallback.m_maxVel * gpGlobals->frametime;
+ if ( distance > maxDist )
+ {
+ newPosition += dir * maxDist;
+ }
+ else
+ {
+ newPosition = tr.endpos;
+ }
+ }
+ else
+ {
+ newPosition = tr.endpos;
+ }
+
+ CreatePelletAttraction( phys_gunglueradius.GetFloat(), pObject );
+
+ // If I'm looking more than 20 degrees away from the glue point, then give up
+ // This lets the player "gesture" for the glue to let go.
+ Vector pelletDir = m_gravCallback.m_worldPosition - start;
+ VectorNormalize(pelletDir);
+ if ( DotProduct( pelletDir, forward ) < 0.939 ) // 0.939 ~= cos(20deg)
+ {
+ // lose attach for 2 seconds if you're too far away
+ m_glueTime = gpGlobals->curtime + 1;
+ }
+
+ if ( m_pelletHeld >= 0 && gpGlobals->curtime > m_glueTime )
+ {
+ CGravityPellet *pPelletAttract = m_activePellets[m_pelletAttract].pellet;
+
+ g_pEffects->Sparks( pPelletAttract->GetAbsOrigin() );
+ }
+
+ m_gravCallback.SetTargetPosition( newPosition );
+ Vector dir = (newPosition - pObject->GetLocalOrigin());
+ m_movementLength = dir.Length();
+ }
+ else
+ {
+ m_gravCallback.SetTargetPosition( end );
+ }
+ if ( m_pelletHeld >= 0 && gpGlobals->curtime > m_glueTime )
+ {
+ Vector worldNormal, worldPos;
+ GetPelletWorldCoords( m_pelletAttract, &worldPos, &worldNormal );
+
+ m_gravCallback.SetAutoAlign( m_activePellets[m_pelletHeld].localNormal, m_activePellets[m_pelletHeld].pellet->GetLocalOrigin(), worldNormal, worldPos );
+ }
+ else
+ {
+ m_gravCallback.ClearAutoAlign();
+ }
+}
+
+void CWeaponGravityGun::SoundCreate( void )
+{
+ m_soundState = SS_SCANNING;
+ SoundStart();
+}
+
+
+void CWeaponGravityGun::SoundDestroy( void )
+{
+ SoundStop();
+}
+
+
+void CWeaponGravityGun::SoundStop( void )
+{
+ switch( m_soundState )
+ {
+ case SS_SCANNING:
+ GetOwner()->StopSound( "Weapon_Physgun.Scanning" );
+ break;
+ case SS_LOCKEDON:
+ GetOwner()->StopSound( "Weapon_Physgun.Scanning" );
+ GetOwner()->StopSound( "Weapon_Physgun.LockedOn" );
+ GetOwner()->StopSound( "Weapon_Physgun.LightObject" );
+ GetOwner()->StopSound( "Weapon_Physgun.HeavyObject" );
+ break;
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the linear fraction of value between low & high (0.0 - 1.0) * scale
+// e.g. UTIL_LineFraction( 1.5, 1, 2, 1 ); will return 0.5 since 1.5 is
+// halfway between 1 and 2
+// Input : value - a value between low & high (clamped)
+// low - the value that maps to zero
+// high - the value that maps to "scale"
+// scale - the output scale
+// Output : parametric fraction between low & high
+//-----------------------------------------------------------------------------
+static float UTIL_LineFraction( float value, float low, float high, float scale )
+{
+ if ( value < low )
+ value = low;
+ if ( value > high )
+ value = high;
+
+ float delta = high - low;
+ if ( delta == 0 )
+ return 0;
+
+ return scale * (value-low) / delta;
+}
+
+void CWeaponGravityGun::SoundStart( void )
+{
+ CPASAttenuationFilter filter( GetOwner() );
+ filter.MakeReliable();
+
+ switch( m_soundState )
+ {
+ case SS_SCANNING:
+ {
+ EmitSound( filter, GetOwner()->entindex(), "Weapon_Physgun.Scanning" );
+ }
+ break;
+ case SS_LOCKEDON:
+ {
+ // BUGBUG - If you start a sound with a pitch of 100, the pitch shift doesn't work!
+
+ EmitSound( filter, GetOwner()->entindex(), "Weapon_Physgun.LockedOn" );
+ EmitSound( filter, GetOwner()->entindex(), "Weapon_Physgun.Scanning" );
+ EmitSound( filter, GetOwner()->entindex(), "Weapon_Physgun.LightObject" );
+ EmitSound( filter, GetOwner()->entindex(), "Weapon_Physgun.HeavyObject" );
+ }
+ break;
+ }
+ // volume, att, flags, pitch
+}
+
+void CWeaponGravityGun::SoundUpdate( void )
+{
+ int newState;
+
+ if ( m_hObject )
+ newState = SS_LOCKEDON;
+ else
+ newState = SS_SCANNING;
+
+ if ( newState != m_soundState )
+ {
+ SoundStop();
+ m_soundState = newState;
+ SoundStart();
+ }
+
+ switch( m_soundState )
+ {
+ case SS_SCANNING:
+ break;
+ case SS_LOCKEDON:
+ {
+ CPASAttenuationFilter filter( GetOwner() );
+ filter.MakeReliable();
+
+ float height = m_hObject->GetAbsOrigin().z - m_originalObjectPosition.z;
+
+ // go from pitch 90 to 150 over a height of 500
+ int pitch = 90 + (int)UTIL_LineFraction( height, 0, 500, 60 );
+
+ CSoundParameters params;
+ if ( GetParametersForSound( "Weapon_Physgun.LockedOn", params, NULL ) )
+ {
+ EmitSound_t ep( params );
+ ep.m_nFlags = SND_CHANGE_VOL | SND_CHANGE_PITCH;
+ ep.m_nPitch = pitch;
+
+ EmitSound( filter, GetOwner()->entindex(), ep );
+ }
+
+ // attenutate the movement sounds over 200 units of movement
+ float distance = UTIL_LineFraction( m_movementLength, 0, 200, 1.0 );
+
+ // blend the "mass" sounds between 50 and 500 kg
+ IPhysicsObject *pPhys = m_hObject->VPhysicsGetObject();
+
+ float fade = UTIL_LineFraction( pPhys->GetMass(), 50, 500, 1.0 );
+
+ if ( GetParametersForSound( "Weapon_Physgun.LightObject", params, NULL ) )
+ {
+ EmitSound_t ep( params );
+ ep.m_nFlags = SND_CHANGE_VOL;
+ ep.m_flVolume = fade * distance;
+
+ EmitSound( filter, GetOwner()->entindex(), ep );
+ }
+
+ if ( GetParametersForSound( "Weapon_Physgun.HeavyObject", params, NULL ) )
+ {
+ EmitSound_t ep( params );
+ ep.m_nFlags = SND_CHANGE_VOL;
+ ep.m_flVolume = (1.0 - fade) * distance;
+
+ EmitSound( filter, GetOwner()->entindex(), ep );
+ }
+ }
+ break;
+ }
+}
+
+
+void CWeaponGravityGun::AddPellet( CGravityPellet *pPellet, CBaseEntity *pAttach, const Vector &surfaceNormal )
+{
+ Assert(m_pelletCount<MAX_PELLETS);
+
+ m_activePellets[m_pelletCount].localNormal = surfaceNormal;
+ if ( pAttach )
+ {
+ EntityMatrix tmp;
+ tmp.InitFromEntity( pAttach );
+ m_activePellets[m_pelletCount].localNormal = tmp.WorldToLocalRotation( surfaceNormal );
+ }
+ m_activePellets[m_pelletCount].pellet = pPellet;
+ m_activePellets[m_pelletCount].parent = pAttach;
+ m_pelletCount++;
+}
+
+void CWeaponGravityGun::SortPelletsForObject( CBaseEntity *pObject )
+{
+ m_objectPelletCount = 0;
+ for ( int i = 0; i < m_pelletCount; i++ )
+ {
+ // move pellets attached to the active object to the front of the list
+ if ( m_activePellets[i].parent == pObject && !m_activePellets[i].pellet->IsInert() )
+ {
+ if ( i != 0 )
+ {
+ pelletlist_t tmp = m_activePellets[m_objectPelletCount];
+ m_activePellets[m_objectPelletCount] = m_activePellets[i];
+ m_activePellets[i] = tmp;
+ }
+ m_objectPelletCount++;
+ }
+ }
+
+ SetObjectPelletsColor( 192, 255, 192 );
+}
+
+void CWeaponGravityGun::SetObjectPelletsColor( int r, int g, int b )
+{
+ color32 color;
+ color.r = r;
+ color.g = g;
+ color.b = b;
+ color.a = 255;
+
+ for ( int i = 0; i < m_objectPelletCount; i++ )
+ {
+ CGravityPellet *pPellet = m_activePellets[i].pellet;
+ if ( !pPellet || pPellet->IsInert() )
+ continue;
+
+ pPellet->m_clrRender = color;
+ }
+}
+
+CBaseEntity *CWeaponGravityGun::GetBeamEntity()
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ if ( !pOwner )
+ return NULL;
+
+ // Make sure I've got a view model
+ CBaseViewModel *vm = pOwner->GetViewModel();
+ if ( vm )
+ return vm;
+
+ return pOwner;
+}
+
+void CWeaponGravityGun::DeleteActivePellets()
+{
+ CBaseEntity *pEnt = GetBeamEntity();
+
+ for ( int i = 0; i < m_pelletCount; i++ )
+ {
+ CGravityPellet *pPellet = m_activePellets[i].pellet;
+ if ( !pPellet )
+ continue;
+
+ Vector forward;
+ AngleVectors( pPellet->GetAbsAngles(), &forward );
+ g_pEffects->Dust( pPellet->GetAbsOrigin(), forward, 32, 30 );
+
+ // UNDONE: Probably should just do this client side
+ CBeam *pBeam = CBeam::BeamCreate( PHYSGUN_BEAM_SPRITE, 1.5 );
+ pBeam->PointEntInit( pPellet->GetAbsOrigin(), pEnt );
+ pBeam->SetEndAttachment( 1 );
+ pBeam->SetBrightness( 255 );
+ pBeam->SetColor( 255, 0, 0 );
+ pBeam->RelinkBeam();
+ pBeam->LiveForTime( 0.1 );
+
+ UTIL_Remove( pPellet );
+ }
+ m_pelletCount = 0;
+}
+
+void CWeaponGravityGun::CreatePelletAttraction( float radius, CBaseEntity *pObject )
+{
+ int nearPellet = -1;
+ int objectPellet = -1;
+ float best = radius*radius;
+ // already have a pellet, check for in range
+ if ( m_pelletAttract >= 0 )
+ {
+ Vector attract, held;
+ GetPelletWorldCoords( m_pelletAttract, &attract, NULL );
+ GetPelletWorldCoords( m_pelletHeld, &held, NULL );
+ float dist = (attract - held).Length();
+ if ( dist < radius * 2 )
+ {
+ nearPellet = m_pelletAttract;
+ objectPellet = m_pelletHeld;
+ best = dist * dist;
+ }
+ }
+
+ if ( nearPellet < 0 )
+ {
+
+ for ( int i = 0; i < m_objectPelletCount; i++ )
+ {
+ CGravityPellet *pPellet = m_activePellets[i].pellet;
+ if ( !pPellet )
+ continue;
+ for ( int j = m_objectPelletCount; j < m_pelletCount; j++ )
+ {
+ CGravityPellet *pTest = m_activePellets[j].pellet;
+ if ( !pTest )
+ continue;
+
+ if ( pTest->IsInert() )
+ continue;
+ float distSqr = (pTest->GetAbsOrigin() - pPellet->GetAbsOrigin()).LengthSqr();
+ if ( distSqr < best )
+ {
+ Vector worldPos, worldNormal;
+ GetPelletWorldCoords( j, &worldPos, &worldNormal );
+ // don't attract backside pellets (unless current pellet - prevent oscillation)
+ float dist = DotProduct( worldPos, worldNormal );
+ if ( m_pelletAttract == j || DotProduct( pPellet->GetAbsOrigin(), worldNormal ) - dist >= 0 )
+ {
+ best = distSqr;
+ nearPellet = j;
+ objectPellet = i;
+ }
+ }
+ }
+ }
+ }
+
+ m_glueTouching = false;
+ if ( nearPellet < 0 || objectPellet < 0 )
+ {
+ m_pelletAttract = -1;
+ m_pelletHeld = -1;
+ return;
+ }
+
+ if ( nearPellet != m_pelletAttract || objectPellet != m_pelletHeld )
+ {
+ m_glueTime = gpGlobals->curtime;
+
+ m_pelletAttract = nearPellet;
+ m_pelletHeld = objectPellet;
+ }
+
+ // check for bonding
+ if ( best < 3*3 )
+ {
+ // This makes the pull towards the pellet stop getting stronger since some part of
+ // the object is touching
+ m_glueTouching = true;
+ }
+ }
+
+
+IPhysicsObject *CWeaponGravityGun::GetPelletPhysObject( int pelletIndex )
+{
+ if ( pelletIndex < 0 )
+ return NULL;
+
+ CBaseEntity *pEntity = m_activePellets[pelletIndex].parent;
+ if ( pEntity )
+ return pEntity->VPhysicsGetObject();
+
+ return g_PhysWorldObject;
+}
+
+void CWeaponGravityGun::EffectDestroy( void )
+{
+ m_active = false;
+ SoundStop();
+
+ DetachObject();
+}
+
+void CWeaponGravityGun::DetachObject( void )
+{
+ m_pelletHeld = -1;
+ m_pelletAttract = -1;
+ m_glueTouching = false;
+ SetObjectPelletsColor( 255, 0, 0 );
+ m_objectPelletCount = 0;
+
+ if ( m_hObject )
+ {
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ Pickup_OnPhysGunDrop( m_hObject, pOwner, DROPPED_BY_CANNON );
+
+ m_gravCallback.DetachEntity();
+ m_hObject = NULL;
+ }
+}
+
+void CWeaponGravityGun::AttachObject( CBaseEntity *pObject, const Vector& start, const Vector &end, float distance )
+{
+ m_hObject = pObject;
+ m_useDown = false;
+ IPhysicsObject *pPhysics = pObject ? (pObject->VPhysicsGetObject()) : NULL;
+ if ( pPhysics && pObject->GetMoveType() == MOVETYPE_VPHYSICS )
+ {
+ m_distance = distance;
+
+ m_gravCallback.AttachEntity( pObject, pPhysics, end );
+ float mass = pPhysics->GetMass();
+ Msg( "Object mass: %.2f lbs (%.2f kg)\n", kg2lbs(mass), mass );
+ float vel = phys_gunvel.GetFloat();
+ if ( mass > phys_gunmass.GetFloat() )
+ {
+ vel = (vel*phys_gunmass.GetFloat())/mass;
+ }
+ m_gravCallback.SetMaxVelocity( vel );
+// Msg( "Object mass: %.2f lbs (%.2f kg) %f %f %f\n", kg2lbs(mass), mass, pObject->GetAbsOrigin().x, pObject->GetAbsOrigin().y, pObject->GetAbsOrigin().z );
+// Msg( "ANG: %f %f %f\n", pObject->GetAbsAngles().x, pObject->GetAbsAngles().y, pObject->GetAbsAngles().z );
+
+ m_originalObjectPosition = pObject->GetAbsOrigin();
+
+ m_pelletAttract = -1;
+ m_pelletHeld = -1;
+
+ pPhysics->Wake();
+ SortPelletsForObject( pObject );
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ if( pOwner )
+ {
+ Pickup_OnPhysGunPickup( pObject, pOwner );
+ }
+ }
+ else
+ {
+ m_hObject = NULL;
+ }
+}
+
+//=========================================================
+//=========================================================
+void CWeaponGravityGun::PrimaryAttack( void )
+{
+ if ( !m_active )
+ {
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+ EffectCreate();
+ SoundCreate();
+ }
+ else
+ {
+ EffectUpdate();
+ SoundUpdate();
+ }
+}
+
+void CWeaponGravityGun::SecondaryAttack( void )
+{
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.1;
+ if ( m_active )
+ {
+ EffectDestroy();
+ SoundDestroy();
+ return;
+ }
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ Assert( pOwner );
+
+ if ( pOwner->GetAmmoCount(m_iSecondaryAmmoType) <= 0 )
+ return;
+
+ m_viewModelIndex = pOwner->entindex();
+ // Make sure I've got a view model
+ CBaseViewModel *vm = pOwner->GetViewModel();
+ if ( vm )
+ {
+ m_viewModelIndex = vm->entindex();
+ }
+
+ Vector forward;
+ pOwner->EyeVectors( &forward );
+
+ Vector start = pOwner->Weapon_ShootPosition();
+ Vector end = start + forward * 4096;
+
+ trace_t tr;
+ UTIL_TraceLine( start, end, MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr );
+ if ( tr.fraction == 1.0 || (tr.surface.flags & SURF_SKY) )
+ return;
+
+ CBaseEntity *pHit = tr.m_pEnt;
+
+ if ( pHit->entindex() == 0 )
+ {
+ pHit = NULL;
+ }
+ else
+ {
+ // if the object has no physics object, or isn't a physprop or brush entity, then don't glue
+ if ( !pHit->VPhysicsGetObject() || pHit->GetMoveType() != MOVETYPE_VPHYSICS )
+ return;
+ }
+
+ QAngle angles;
+ WeaponSound( SINGLE );
+ pOwner->RemoveAmmo( 1, m_iSecondaryAmmoType );
+
+ VectorAngles( tr.plane.normal, angles );
+ Vector endPoint = tr.endpos + tr.plane.normal;
+ CGravityPellet *pPellet = (CGravityPellet *)CBaseEntity::Create( "gravity_pellet", endPoint, angles, this );
+ if ( pHit )
+ {
+ pPellet->SetParent( pHit );
+ }
+ AddPellet( pPellet, pHit, tr.plane.normal );
+
+ // UNDONE: Probably should just do this client side
+ CBaseEntity *pEnt = GetBeamEntity();
+ CBeam *pBeam = CBeam::BeamCreate( PHYSGUN_BEAM_SPRITE, 1.5 );
+ pBeam->PointEntInit( endPoint, pEnt );
+ pBeam->SetEndAttachment( 1 );
+ pBeam->SetBrightness( 255 );
+ pBeam->SetColor( 255, 0, 0 );
+ pBeam->RelinkBeam();
+ pBeam->LiveForTime( 0.1 );
+
+}
+
+void CWeaponGravityGun::WeaponIdle( void )
+{
+ if ( HasWeaponIdleTimeElapsed() )
+ {
+ SendWeaponAnim( ACT_VM_IDLE );
+ if ( m_active )
+ {
+ CBaseEntity *pObject = m_hObject;
+ // pellet is touching object, so glue it
+ if ( pObject && m_glueTouching )
+ {
+ CGravityPellet *pPellet = m_activePellets[m_pelletAttract].pellet;
+ if ( pPellet->MakeConstraint( pObject ) )
+ {
+ WeaponSound( SPECIAL1 );
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.75;
+ m_activePellets[m_pelletHeld].pellet->MakeInert();
+ }
+ }
+
+ EffectDestroy();
+ SoundDestroy();
+ }
+ }
+}
+
+void CWeaponGravityGun::ItemPostFrame( void )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ if (!pOwner)
+ return;
+
+ if ( pOwner->m_afButtonPressed & IN_ATTACK2 )
+ {
+ SecondaryAttack();
+ }
+ else if ( pOwner->m_nButtons & IN_ATTACK )
+ {
+ PrimaryAttack();
+ }
+ else if ( pOwner->m_afButtonPressed & IN_RELOAD )
+ {
+ Reload();
+ }
+ // -----------------------
+ // No buttons down
+ // -----------------------
+ else
+ {
+ WeaponIdle( );
+ return;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponGravityGun::HasAnyAmmo( void )
+{
+ //Always report that we have ammo
+ return true;
+}
+
+//=========================================================
+//=========================================================
+bool CWeaponGravityGun::Reload( void )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner->GetAmmoCount(m_iSecondaryAmmoType) != MAX_PELLETS )
+ {
+ pOwner->SetAmmoCount( MAX_PELLETS, m_iSecondaryAmmoType );
+ DeleteActivePellets();
+ WeaponSound( RELOAD );
+ return true;
+ }
+
+ return false;
+}
+
+#define NUM_COLLISION_TESTS 2500
+void CC_CollisionTest( const CCommand &args )
+{
+ if ( !physenv )
+ return;
+
+ Msg( "Testing collision system\n" );
+ int i;
+ CBaseEntity *pSpot = gEntList.FindEntityByClassname( NULL, "info_player_start");
+ Vector start = pSpot->GetAbsOrigin();
+ static Vector *targets = NULL;
+ static bool first = true;
+ static float test[2] = {1,1};
+ if ( first )
+ {
+ targets = new Vector[NUM_COLLISION_TESTS];
+ float radius = 0;
+ float theta = 0;
+ float phi = 0;
+ for ( i = 0; i < NUM_COLLISION_TESTS; i++ )
+ {
+ radius += NUM_COLLISION_TESTS * 123.123;
+ radius = fabs(fmod(radius, 128));
+ theta += NUM_COLLISION_TESTS * 76.76;
+ theta = fabs(fmod(theta, DEG2RAD(360)));
+ phi += NUM_COLLISION_TESTS * 1997.99;
+ phi = fabs(fmod(phi, DEG2RAD(180)));
+
+ float st, ct, sp, cp;
+ SinCos( theta, &st, &ct );
+ SinCos( phi, &sp, &cp );
+
+ targets[i].x = radius * ct * sp;
+ targets[i].y = radius * st * sp;
+ targets[i].z = radius * cp;
+
+ // make the trace 1024 units long
+ Vector dir = targets[i] - start;
+ VectorNormalize(dir);
+ targets[i] = start + dir * 1024;
+ }
+ first = false;
+ }
+
+ //Vector results[NUM_COLLISION_TESTS];
+
+ int testType = 0;
+ if ( args.ArgC() >= 2 )
+ {
+ testType = atoi( args[1] );
+ }
+ float duration = 0;
+ Vector size[2];
+ size[0].Init(0,0,0);
+ size[1].Init(16,16,16);
+ unsigned int dots = 0;
+
+ for ( int j = 0; j < 2; j++ )
+ {
+ float startTime = engine->Time();
+ if ( testType == 1 )
+ {
+ const CPhysCollide *pCollide = g_PhysWorldObject->GetCollide();
+ trace_t tr;
+
+ for ( i = 0; i < NUM_COLLISION_TESTS; i++ )
+ {
+ physcollision->TraceBox( start, targets[i], -size[j], size[j], pCollide, vec3_origin, vec3_angle, &tr );
+ dots += physcollision->ReadStat(0);
+ //results[i] = tr.endpos;
+ }
+ }
+ else
+ {
+ testType = 0;
+ CBaseEntity *pWorld = GetContainingEntity( INDEXENT(0) );
+ trace_t tr;
+
+ for ( i = 0; i < NUM_COLLISION_TESTS; i++ )
+ {
+ UTIL_TraceModel( start, targets[i], -size[j], size[j], pWorld, COLLISION_GROUP_NONE, &tr );
+ //results[i] = tr.endpos;
+ }
+ }
+
+ duration += engine->Time() - startTime;
+ }
+ test[testType] = duration;
+ Msg("%d collisions in %.2f ms (%u dots)\n", NUM_COLLISION_TESTS, duration*1000, dots );
+ Msg("Current speed ratio: %.2fX BSP:JGJK\n", test[1] / test[0] );
+#if 0
+ int red = 255, green = 0, blue = 0;
+ for ( i = 0; i < NUM_COLLISION_TESTS; i++ )
+ {
+ NDebugOverlay::Line( start, results[i], red, green, blue, false, 2 );
+ }
+#endif
+}
+static ConCommand collision_test("collision_test", CC_CollisionTest, "Tests collision system", FCVAR_CHEAT );
|