Instantly share code, notes, and snippets.

Embed
What would you like to do?
HL2 mod snippet - Physgun weapon code
//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#include "cbase.h"
#include "hud.h"
#include "in_buttons.h"
#include "beamdraw.h"
#include "c_weapon__stubs.h"
#include "ClientEffectPrecacheSystem.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectGravityGun )
CLIENTEFFECT_MATERIAL( "sprites/physbeam" )
CLIENTEFFECT_REGISTER_END()
class C_BeamQuadratic : public CDefaultClientRenderable
{
public:
C_BeamQuadratic();
void Update( C_BaseEntity *pOwner );
matrix3x4_t z;
const matrix3x4_t& RenderableToWorldTransform()
{
return z;
}
// IClientRenderable
virtual const Vector& GetRenderOrigin( void ) { return m_worldPosition; }
virtual const QAngle& GetRenderAngles( void ) { return vec3_angle; }
virtual bool ShouldDraw( void ) { return true; }
virtual bool IsTransparent( void ) { return true; }
virtual bool ShouldReceiveProjectedTextures( int flags ) { return false; }
virtual int DrawModel( int flags );
// Returns the bounds relative to the origin (render bounds)
virtual void GetRenderBounds( Vector& mins, Vector& maxs )
{
// bogus. But it should draw if you can see the end point
mins.Init(-32,-32,-32);
maxs.Init(32,32,32);
}
C_BaseEntity *m_pOwner;
Vector m_targetPosition;
Vector m_worldPosition;
int m_active;
int m_glueTouching;
int m_viewModelIndex;
};
class C_WeaponGravityGun : public C_BaseCombatWeapon
{
DECLARE_CLASS( C_WeaponGravityGun, C_BaseCombatWeapon );
public:
C_WeaponGravityGun() {}
DECLARE_CLIENTCLASS();
DECLARE_PREDICTABLE();
int KeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
{
if ( gHUD.m_iKeyBits & IN_ATTACK )
{
switch ( keynum )
{
case MOUSE_WHEEL_UP:
gHUD.m_iKeyBits |= IN_WEAPON1;
return 0;
case MOUSE_WHEEL_DOWN:
gHUD.m_iKeyBits |= IN_WEAPON2;
return 0;
}
}
// Allow engine to process
return BaseClass::KeyInput( down, keynum, pszCurrentBinding );
}
void OnDataChanged( DataUpdateType_t updateType )
{
BaseClass::OnDataChanged( updateType );
m_beam.Update( this );
}
private:
C_WeaponGravityGun( const C_WeaponGravityGun & );
C_BeamQuadratic m_beam;
};
STUB_WEAPON_CLASS_IMPLEMENT( weapon_physgun, C_WeaponGravityGun );
IMPLEMENT_CLIENTCLASS_DT( C_WeaponGravityGun, DT_WeaponGravityGun, CWeaponGravityGun )
RecvPropVector( RECVINFO_NAME(m_beam.m_targetPosition,m_targetPosition) ),
RecvPropVector( RECVINFO_NAME(m_beam.m_worldPosition, m_worldPosition) ),
RecvPropInt( RECVINFO_NAME(m_beam.m_active, m_active) ),
RecvPropInt( RECVINFO_NAME(m_beam.m_glueTouching, m_glueTouching) ),
RecvPropInt( RECVINFO_NAME(m_beam.m_viewModelIndex, m_viewModelIndex) ),
END_RECV_TABLE()
C_BeamQuadratic::C_BeamQuadratic()
{
m_pOwner = NULL;
m_hRenderHandle = INVALID_CLIENT_RENDER_HANDLE;
}
void C_BeamQuadratic::Update( C_BaseEntity *pOwner )
{
m_pOwner = pOwner;
if ( m_active )
{
if ( m_hRenderHandle == INVALID_CLIENT_RENDER_HANDLE )
{
ClientLeafSystem()->AddRenderable( this, RENDER_GROUP_TRANSLUCENT_ENTITY );
}
else
{
ClientLeafSystem()->RenderableChanged( m_hRenderHandle );
}
}
else if ( !m_active && m_hRenderHandle != INVALID_CLIENT_RENDER_HANDLE )
{
ClientLeafSystem()->RemoveRenderable( m_hRenderHandle );
m_hRenderHandle = INVALID_CLIENT_RENDER_HANDLE;
}
}
int C_BeamQuadratic::DrawModel( int )
{
Vector points[3];
QAngle tmpAngle;
if ( !m_active )
return 0;
C_BaseEntity *pEnt = cl_entitylist->GetEnt( m_viewModelIndex );
if ( !pEnt )
return 0;
pEnt->GetAttachment( 1, points[0], tmpAngle );
points[1] = 0.5 * (m_targetPosition + points[0]);
// a little noise 11t & 13t should be somewhat non-periodic looking
//points[1].z += 4*sin( gpGlobals->curtime*11 ) + 5*cos( gpGlobals->curtime*13 );
points[2] = m_worldPosition;
IMaterial *pMat = materials->FindMaterial( "sprites/physbeam", TEXTURE_GROUP_CLIENT_EFFECTS );
Vector color;
if ( m_glueTouching )
{
color.Init(1,0,0);
}
else
{
color.Init(1,1,1);
}
float scrollOffset = gpGlobals->curtime - (int)gpGlobals->curtime;
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->Bind( pMat );
DrawBeamQuadratic( points[0], points[1], points[2], 13, color, scrollOffset );
return 1;
}
//========= 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", "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 50
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 already been pressed
{
if ( pOwner->m_afButtonPressed & IN_USE ) // then if use pressed
{
m_useDown = false;
IPhysicsObject *pPhys = pObject->VPhysicsGetObject();
pPhys->EnableMotion(true);
//Reattach
DetachObject();
AttachObject( pObject, start, tr.endpos, distance );
}
}
else
{
if ( pOwner->m_afButtonPressed & IN_USE )
{
m_useDown = true;
IPhysicsObject *pPhys = pObject->VPhysicsGetObject();
pPhys->EnableMotion(false);
}
}
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();
}
NetworkStateChanged();
}
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;
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_CollisionTest1( 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_test1("collision_test1", CC_CollisionTest1, "Tests collision system", FCVAR_CHEAT );
@MrAloxa

This comment has been minimized.

Copy link

MrAloxa commented Nov 26, 2018

how to install???

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment