All pastes #452683 Raw Edit

Unnamed

public cpp v1 · immutable
·published 2007-04-22 13:25 UTC
rendered paste body
//========= Copyright © 1996-2005, 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", "1000000000000000000000000000");ConVar phys_gunvel("phys_gunvel", "10e10");ConVar phys_gunforce("phys_gunforce", "1000000" );ConVar phys_guntorque("phys_guntorque", "15e15" );ConVar phys_gunglueradius("phys_gunglueradius", "400" );ConVar _physguncurrenthealth("_physguncurrenthealth","100" );static int g_physgunBeam;#define PHYSGUN_BEAM_SPRITE		"sprites/physbeam.vmt"#define MAX_PELLETS	16class 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 *pPhysics = pObject ? (pObject->VPhysicsGetObject()) : NULL;	pPhysics->Sleep();	}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 )	{m_hObject->SetHealth(100); //	Dirty Way to stop the object breaking causing the client to crash...		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;	//Msg("Debugging Info for Model: " & pObject->GetModelName );	//	Msg("-> MoveType: " + pObject->GetFlags() );	//	Msg("-> AbsOrigin: " + pObject->GetAbsOrigin() );	//	Msg("-> GetAbsVelocity: " + pObject->GetAbsVelocity().x + " " + pObject->GetAbsVelocity().y + " " + pObject->GetAbsVelocity().z );	//	Msg("condump");	if ( pPhysics && pObject->GetMoveType() == MOVETYPE_VPHYSICS )	{		pPhysics->EnableMotion(true);		pObject->SetHealth ( 9999999 );  //	Dirty Way to stop the object breaking causing the client to crash...		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( 9999999 );//		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;		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 );	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 );// Start Freeze Prop on secondary code (Marine)	Vector start = pOwner->Weapon_ShootPosition();	Vector end = start + forward * 16000;	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->GetMoveType() == MOVETYPE_VPHYSICS ){		IPhysicsObject *pPhysics = pHit ? (pHit->VPhysicsGetObject()) : NULL;		pPhysics->EnableMotion(false);				}	}	// End Freeze Prop on secondary code (Marine)	//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( void )////{////	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 ( engine->Cmd_Argc() >= 2 )////	{////		testType = atoi(engine->Cmd_Argv(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 );