From 6f1a84505356897a4c466fd824c08046fad41517 Mon Sep 17 00:00:00 2001 From: ray <1041137+ray@users.noreply.github.com> Date: Mon, 6 Apr 2026 20:29:18 -0400 Subject: [PATCH] Fizzgun Server-sided jank one until we can finish the rewrite, just need this for now for crash testing Signed-off-by: ray <1041137+ray@users.noreply.github.com> --- .../scripts/vscripts/gamemodes/sandbox.nut | 2 +- game/jbmod/sound/weapons/flaregun/impact.wav | Bin 0 -> 32720 bytes game/jbmod/sound/weapons/physgun_loop1.wav | Bin 0 -> 7420 bytes game/jbmod/sound/weapons/physgun_loop2.wav | Bin 0 -> 9244 bytes game/jbmod/sound/weapons/physgun_loop3.wav | Bin 0 -> 9016 bytes game/jbmod/sound/weapons/physgun_loop4.wav | Bin 0 -> 9016 bytes game/jbmod/sound/weapons/physgun_off.wav | Bin 0 -> 29508 bytes game/jbmod/sound/weapons/physgun_on.wav | Bin 0 -> 37582 bytes src/game/client/client_jbmod.vpc | 1 + src/game/client/jbmod/c_weapon_physgun.cpp | 181 ++ src/game/server/jbmod/weapon_physgun.cpp | 1717 +++++++++++++++++ src/game/server/server_jbmod.vpc | 1 + src/game/shared/jbmod/jbmod_gamerules.cpp | 1 + 13 files changed, 1902 insertions(+), 1 deletion(-) create mode 100644 game/jbmod/sound/weapons/flaregun/impact.wav create mode 100644 game/jbmod/sound/weapons/physgun_loop1.wav create mode 100644 game/jbmod/sound/weapons/physgun_loop2.wav create mode 100644 game/jbmod/sound/weapons/physgun_loop3.wav create mode 100644 game/jbmod/sound/weapons/physgun_loop4.wav create mode 100644 game/jbmod/sound/weapons/physgun_off.wav create mode 100644 game/jbmod/sound/weapons/physgun_on.wav create mode 100644 src/game/client/jbmod/c_weapon_physgun.cpp create mode 100644 src/game/server/jbmod/weapon_physgun.cpp diff --git a/game/jbmod/scripts/vscripts/gamemodes/sandbox.nut b/game/jbmod/scripts/vscripts/gamemodes/sandbox.nut index d9b73bfc35c..0a7912746b0 100644 --- a/game/jbmod/scripts/vscripts/gamemodes/sandbox.nut +++ b/game/jbmod/scripts/vscripts/gamemodes/sandbox.nut @@ -31,5 +31,5 @@ IncludeScript( "gamemodes/base.nut" ); function OnPlayerSpawn( player ) { player.EquipSuit(); - player.GiveItem( "weapon_physcannon" ); + player.GiveItem( "weapon_physgun" ); } diff --git a/game/jbmod/sound/weapons/flaregun/impact.wav b/game/jbmod/sound/weapons/flaregun/impact.wav new file mode 100644 index 0000000000000000000000000000000000000000..96a91c7146ef69b1ac7167b22f1bc9055e411436 GIT binary patch literal 32720 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 K_5Z(Kn*2ZJ=<4SH literal 0 HcmV?d00001 diff --git a/game/jbmod/sound/weapons/physgun_loop1.wav b/game/jbmod/sound/weapons/physgun_loop1.wav new file mode 100644 index 0000000000000000000000000000000000000000..378c82e1d8b26ed171265b78d15d648a09260c3a GIT binary patch literal 7420 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 Vf9|b4_9cRWwLPCd_ov=Z{s_1vtf&A0 literal 0 HcmV?d00001 diff --git a/game/jbmod/sound/weapons/physgun_loop3.wav b/game/jbmod/sound/weapons/physgun_loop3.wav new file mode 100644 index 0000000000000000000000000000000000000000..59d79872f5b07ca88df2bd71fd43f8d1fe1af621 GIT binary patch literal 9016 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 LcmDiaec%3Hh(0)p literal 0 HcmV?d00001 diff --git a/game/jbmod/sound/weapons/physgun_loop4.wav b/game/jbmod/sound/weapons/physgun_loop4.wav new file mode 100644 index 0000000000000000000000000000000000000000..0c9f544ad1ca6f18968b82c0340a5506a33eebb1 GIT binary patch literal 9016 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 literal 0 HcmV?d00001 diff --git a/game/jbmod/sound/weapons/physgun_on.wav b/game/jbmod/sound/weapons/physgun_on.wav new file mode 100644 index 0000000000000000000000000000000000000000..928cd3a7e2612aba19b056e7ad172a94eb6c8cfb GIT binary patch literal 37582 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 dH~z2w>%aQ9zxu0%#`YZ1rtbhOA{|f?Kq>}&u literal 0 HcmV?d00001 diff --git a/src/game/client/client_jbmod.vpc b/src/game/client/client_jbmod.vpc index 1d4fa4da637..c5ff838a0ab 100644 --- a/src/game/client/client_jbmod.vpc +++ b/src/game/client/client_jbmod.vpc @@ -187,6 +187,7 @@ $Project "Client (JBMod)" $File "$SRCDIR\game\shared\jbmod\weapon_stunstick.cpp" $File "$SRCDIR\game\shared\jbmod\weapon_scripted.cpp" $File "$SRCDIR\game\shared\jbmod\weapon_scripted.h" + $File "jbmod\c_weapon_physgun.cpp" } $Folder "UI" diff --git a/src/game/client/jbmod/c_weapon_physgun.cpp b/src/game/client/jbmod/c_weapon_physgun.cpp new file mode 100644 index 00000000000..a6e19902952 --- /dev/null +++ b/src/game/client/jbmod/c_weapon_physgun.cpp @@ -0,0 +1,181 @@ +//========= Copyright 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" +#include "c_jbmod_player.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 ); + + // IClientRenderable + virtual const Vector &GetRenderOrigin( void ) { return m_worldPosition; } + virtual const QAngle &GetRenderAngles( void ) { return vec3_angle; } + virtual const matrix3x4_t &RenderableToWorldTransform(); + 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( -99999, -99999, -99999 ); + maxs.Init( 99999, 99999, 99999 ); + } + + C_BaseEntity *m_pOwner; + Vector m_targetPosition; + Vector m_worldPosition; + int m_active; + int m_glueTouching; +}; + + +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 ); + } + + void UpdateOnRemove( void ) + { + m_beam.m_active = false; + m_beam.Update( this ); + + BaseClass::UpdateOnRemove(); + } + +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 ) ), +END_RECV_TABLE() + +C_BeamQuadratic::C_BeamQuadratic() +{ + m_pOwner = NULL; +} + +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 ); + } +} + + +int C_BeamQuadratic::DrawModel( int ) +{ + Vector points[3]; + QAngle tmpAngle; + + if ( !m_active ) + return 0; + + C_JBMod_Player *pOwner = ToJBModPlayer( m_pOwner->GetOwnerEntity() ); + C_BaseEntity *pEnt = pOwner->GetRenderedWeaponModel(); + 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; +} + +const matrix3x4_t &C_BeamQuadratic::RenderableToWorldTransform() +{ + static matrix3x4_t mat; + AngleMatrix( GetRenderAngles(), GetRenderOrigin(), mat ); + return mat; +} diff --git a/src/game/server/jbmod/weapon_physgun.cpp b/src/game/server/jbmod/weapon_physgun.cpp new file mode 100644 index 00000000000..da82a27ed4d --- /dev/null +++ b/src/game/server/jbmod/weapon_physgun.cpp @@ -0,0 +1,1717 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "beam_shared.h" +#include "player.h" +#include "jbmod_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" +#include "ai_basenpc.h" +#include "physics_prop_ragdoll.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +ConVar phys_gunmass( "phys_gunmass", "200", FCVAR_REPLICATED | FCVAR_NOTIFY ); +ConVar phys_gunvel( "phys_gunvel", "400", FCVAR_REPLICATED | FCVAR_NOTIFY ); +ConVar phys_gunforce( "phys_gunforce", "20e20", FCVAR_REPLICATED | FCVAR_NOTIFY ); +ConVar phys_guntorque( "phys_guntorque", "100", FCVAR_REPLICATED | FCVAR_NOTIFY ); +ConVar phys_gunglueradius( "phys_gunglueradius", "128", FCVAR_REPLICATED | FCVAR_NOTIFY ); +ConVar phys_gunjank( "phys_gunjank", "0", FCVAR_REPLICATED | FCVAR_NOTIFY ); + +static int g_physgunBeam; +#define PHYSGUN_BEAM_SPRITE "sprites/physbeam.vmt" + +#define MAX_PELLETS 512 +#define MAX_FREEZE 2048 + +class CWeaponGravityGun; + +class CGravityPellet : public CBaseAnimating +{ + DECLARE_CLASS( CGravityPellet, CBaseAnimating ); +public: + DECLARE_DATADESC(); + + ~CGravityPellet(); + void Precache() + { + SetModelName( MAKE_STRING( "models/weapons/w_bugbait.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; + m_pPair = NULL; + } + + bool IsInert() + { + return m_isInert; + } + + bool MakeConstraint( CBaseEntity *pObject, CGravityPellet *pHeld, CGravityPellet *pBond ) + { + 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 ); + + pHeld->m_pPair = pBond; + pBond->m_pPair = pHeld; + + MakeInert(); + return true; + } + + void MakeActive() + { + SetRenderColor( 255, 0, 0 ); + m_isInert = false; + } + + void MakeInert() + { + SetRenderColor( 64, 64, 128 ); + m_isInert = true; + } + + void InputOnBreak( inputdata_t &inputdata ) + { + if ( m_pPair ) + { + UTIL_Remove( m_pPair ); + m_pPair = NULL; + } + UTIL_Remove( this ); + } + + IPhysicsConstraint *m_pConstraint; + bool m_isInert; + CGravityPellet *m_pPair; +}; + +LINK_ENTITY_TO_CLASS( gravity_pellet, CGravityPellet ); +PRECACHE_REGISTER( gravity_pellet ); + +BEGIN_DATADESC( CGravityPellet ) + +DEFINE_PHYSPTR( m_pConstraint ), +DEFINE_FIELD( m_isInert, FIELD_BOOLEAN ), +DEFINE_FIELD( m_pPair, FIELD_CLASSPTR ), +// 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_pPair ) + { + m_pPair->m_pPair = NULL; + } + if ( m_pConstraint ) + { + physenv->DestroyConstraint( m_pConstraint ); + } +} + +class CGravControllerPoint : public IMotionEvent +{ + DECLARE_SIMPLE_DATADESC(); + +public: + CGravControllerPoint( void ); + ~CGravControllerPoint( void ); + void AttachEntity( CBasePlayer *pPlayer, 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; + m_shadow.targetPosition = target; + if ( m_attachedEntity == NULL ) + { + m_worldPosition = target; + } + m_timeToArrive = gpGlobals->frametime; + + if ( m_attachedEntity != NULL ) + { + CBaseEntity *pEntity = m_attachedEntity; + if ( pEntity ) + { + IPhysicsObject *pObject = pEntity->VPhysicsGetObject(); + if ( pObject ) + { + pObject->Wake(); + + QAngle angles; + Vector origin; + pObject->GetShadowPosition( &origin, &angles ); + Vector localorigin; + pObject->LocalToWorld( &localorigin, m_localPosition ); + Vector diff = origin - localorigin; + + m_shadow.targetPosition = target + diff; + } + } + } + } + void SetTargetOrientation( const QAngle &targetOrientation ) + { + m_targetRotation = targetOrientation; + m_shadow.targetRotation = targetOrientation; + m_timeToArrive = gpGlobals->frametime; + } + void SetTarget( const Vector &target, const QAngle &targetOrientation ) + { + m_targetPosition = target; + m_targetRotation = targetOrientation; + m_shadow.targetPosition = target; + m_shadow.targetRotation = targetOrientation; + 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; + float m_contactAmount; + hlshadowcontrol_params_t m_shadow; + + 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_shadow.dampFactor = 1.0; + m_shadow.teleportDistance = 0; + m_shadow.maxSpeed = 1000; + m_shadow.maxAngular = 360.0f * 10.0f; + m_shadow.maxDampSpeed = m_shadow.maxSpeed * 2; + m_shadow.maxDampAngular = m_shadow.maxAngular; + + m_attachedEntity = NULL; + m_contactAmount = 0.0f; +} + +CGravControllerPoint::~CGravControllerPoint( void ) +{ + DetachEntity(); +} + + +void CGravControllerPoint::AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, IPhysicsObject *pPhys, const Vector &position ) +{ + if ( phys_gunjank.GetFloat() == 0 ) + { + Vector vposition; + QAngle vangles; + pPhys->GetPosition( &vposition, &vangles ); + Pickup_GetPreferredCarryAngles( pEntity, pPlayer, pPlayer->EntityToWorldTransform(), vangles ); + m_controller = physenv->CreateMotionController( this ); + m_controller->AttachObject( pPhys, true ); + m_attachedEntity = pEntity; + pPhys->Wake(); + PhysSetGameFlags( pPhys, FVPHYSICS_PLAYER_HELD ); + SetTarget( vposition, vangles ); + pPhys->EnableCollisions( true ); + m_contactAmount = 0; + pPhys->WorldToLocal( &m_localPosition, position ); + m_worldPosition = position; + m_maxAcceleration = phys_gunforce.GetFloat() * pPhys->GetInvMass(); + float torque = phys_guntorque.GetFloat(); + m_maxAngularAcceleration = torque * pPhys->GetInvInertia(); + } + else + { + 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 ); + SetTarget( position, pEntity->GetAbsAngles() ); + m_maxAcceleration = phys_gunforce.GetFloat() * pPhys->GetInvMass(); + 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 ) + { + if ( phys_gunjank.GetFloat() == 0 ) + { + pPhys->EnableCollisions( true ); + PhysClearGameFlags( pPhys, FVPHYSICS_PLAYER_HELD ); + Vector vel; + AngularImpulse angVel; + pPhys->GetVelocity( &vel, &angVel ); + float speed = VectorNormalize( vel ) - ( 190.0f * 1.5f ); + float angSpeed = VectorNormalize( angVel ) - ( 2.0f * 360.0f ); + speed = speed < 0 ? 0 : -speed; + angSpeed = angSpeed < 0 ? 0 : -angSpeed; + vel *= speed; + angVel *= angSpeed; + pPhys->AddVelocity( &vel, &angVel ); + } + + // 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 ) +{ + if ( phys_gunjank.GetFloat() == 0 ) + { + hlshadowcontrol_params_t shadowParams = m_shadow; + m_contactAmount = Approach( 1.0f, m_contactAmount, deltaTime * 2.0f ); + shadowParams.maxAngular = m_shadow.maxAngular * m_contactAmount * m_contactAmount * m_contactAmount; + m_timeToArrive = pObject->ComputeShadowControl( shadowParams, m_timeToArrive, deltaTime ); + Vector vel; + AngularImpulse angVel; + pObject->GetVelocity( &vel, &angVel ); + PhysComputeSlideDirection( pObject, vel, angVel, &vel, &angVel, 0.0f ); + pObject->SetVelocityInstantaneous( &vel, NULL ); + Vector world; + pObject->LocalToWorld( &world, m_localPosition ); + m_worldPosition = world; + linear.Init(); + angular.Init(); + return SIM_GLOBAL_ACCELERATION; + } + else + { + 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 pellet; + EHANDLE parent; +}; + +struct freezelist_t +{ + DECLARE_SIMPLE_DATADESC(); + + EHANDLE parent; + IPhysicsObject *physobject; +}; + +class CWeaponGravityGun : public CBaseCombatWeapon +{ + DECLARE_DATADESC(); + +public: + DECLARE_CLASS( CWeaponGravityGun, CBaseCombatWeapon ); + + CWeaponGravityGun(); + void Spawn( void ); + void OnRestore( void ); + void UpdateOnRemove( 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_iPrimaryAmmoType ); + BaseClass::Equip( pOwner ); + } + void Drop( const Vector &vecVelocity ) + { + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + pOwner->SetAmmoCount( 0, m_iPrimaryAmmoType ); + // 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 FreezeObject( void ); + void UnfreezeObjects( void ); + void SortFreezeList( void ); + void AddPellet( CGravityPellet *pPellet, CBaseEntity *pParent, const Vector &surfaceNormal ); + void DeleteActivePellets(); + void SortPelletsForObject( CBaseEntity *pObject ); + void SortPelletList( void ); + 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; + Vector m_originalObjectPosition; + + CGravControllerPoint m_gravCallback; + pelletlist_t m_activePellets[MAX_PELLETS]; + freezelist_t m_activeFreeze[MAX_FREEZE]; + int m_pelletCount; + int m_freezeCount; + int m_objectPelletCount; + + int m_pelletHeld; + int m_pelletAttract; + float m_glueTime; + CNetworkVar( bool, m_glueTouching ); + + DECLARE_ACTTABLE(); +}; + +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 ), +END_SEND_TABLE() + +acttable_t CWeaponGravityGun::m_acttable[] = +{ + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PHYSGUN, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_PHYSGUN, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PHYSGUN, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PHYSGUN, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PHYSGUN, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PHYSGUN, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PHYSGUN, false }, +}; +IMPLEMENT_ACTTABLE( CWeaponGravityGun ); + +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_SIMPLE_DATADESC( freezelist_t ) + +DEFINE_FIELD( parent, FIELD_EHANDLE ), +DEFINE_PHYSPTR( physobject ), + +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_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_EMBEDDED_AUTO_ARRAY( m_activeFreeze ), +DEFINE_FIELD( m_pelletCount, FIELD_INTEGER ), +DEFINE_FIELD( m_freezeCount, 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::UpdateOnRemove( void ) +{ + if ( m_active ) + SoundStop(); + m_active = false; + + BaseClass::UpdateOnRemove(); +} + + +//========================================================= +//========================================================= +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; + + 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(); // FIXME(JBMOD): This causes crashes + 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 + if ( phys_gunjank.GetFloat() > 0 ) + { + 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 ( phys_gunjank.GetFloat() == 0 ) + { + if ( pOwner->m_afButtonPressed & IN_SPEED ) + pObject->VPhysicsGetObject()->EnableCollisions( false ); + if ( pOwner->m_afButtonReleased & IN_SPEED ) + pObject->VPhysicsGetObject()->EnableCollisions( true ); + if ( ( pOwner->m_nButtons | pOwner->m_afButtonPressed | pOwner->m_afButtonReleased ) & IN_WEAPON1 ) + m_distance = UTIL_Approach( 1024, m_distance, m_distance * 0.1 ); + if ( ( pOwner->m_nButtons | pOwner->m_afButtonPressed | pOwner->m_afButtonReleased ) & IN_WEAPON2 ) + m_distance = UTIL_Approach( 40, m_distance, m_distance * 0.1 ); + } + else { + if ( m_useDown && 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 ); + } + + 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() ); + } + + if ( phys_gunjank.GetFloat() == 0 ) + { + IPhysicsObject *pPhys = pObject->VPhysicsGetObject(); + if ( pPhys ) + { + Vector world; + pPhys->LocalToWorld( &world, m_gravCallback.m_localPosition ); + } + } + + 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 = 1.0; + if ( pPhys != NULL ) + 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 ) +{ + SortPelletList(); + m_objectPelletCount = 0; + for ( int i = 0; i < m_pelletCount; i++ ) + { + if ( !m_activePellets[i].pellet ) + continue; + // 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( 0, 255, 0 ); +} + +void CWeaponGravityGun::SortPelletList( void ) +{ + m_pelletCount = 0; + for ( int j = 0, i = 0; j < MAX_PELLETS; j++ ) + { + pelletlist_t tmp = m_activePellets[j]; + if ( !tmp.pellet ) + { + i++; + continue; + } + m_activePellets[j - i] = tmp; + m_pelletCount++; + } +} + +void CWeaponGravityGun::SortFreezeList( void ) +{ + m_freezeCount = 0; + for ( int j = 0, i = 0; j < MAX_FREEZE; j++ ) + { + freezelist_t tmp = m_activeFreeze[j]; + if ( !tmp.physobject || !tmp.parent ) + { + i++; + continue; + } + m_activeFreeze[j - i] = tmp; + m_freezeCount++; + } +} + +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() +{ + CJBMod_Player *pOwner = ToJBModPlayer( GetOwner() ); + if ( !pOwner ) + return NULL; + + CBaseCombatWeapon *pWeapon = pOwner->GetActiveWeapon(); + if ( pWeapon ) + return pWeapon; + + 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->EnergySplash( pPellet->GetAbsOrigin(), forward ); + + // 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( 0, 255, 0 ); + pBeam->RelinkBeam(); + pBeam->LiveForTime( 0.1 ); + g_pEffects->Sparks( pPellet->GetAbsOrigin() ); + + UTIL_Remove( pPellet ); + } + m_pelletCount = 0; +} + +void CWeaponGravityGun::CreatePelletAttraction( float radius, CBaseEntity *pObject ) +{ + if ( m_pelletHeld != -1 ) + { + if ( !m_activePellets[m_pelletHeld].pellet.IsValid() || (CBaseEntity *)m_activePellets[m_pelletHeld].pellet == NULL ) + { + m_pelletHeld = -1; + m_pelletAttract = -1; + return; + } + } + if ( m_pelletAttract != -1 ) + { + if ( !m_activePellets[m_pelletAttract].pellet.IsValid() || (CBaseEntity *)m_activePellets[m_pelletAttract].pellet == NULL ) + { + m_pelletHeld = -1; + m_pelletAttract = -1; + return; + } + } + + 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 ) +{ + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + if ( !pOwner ) + return; + + m_useDown = false; + IPhysicsObject *pPhysics = pObject ? ( pObject->VPhysicsGetObject() ) : NULL; + if ( pPhysics && pObject->GetMoveType() == MOVETYPE_VPHYSICS ) + { + pPhysics->EnableMotion( true ); + SortFreezeList(); + + if ( pObject->GetCollisionGroup() == COLLISION_GROUP_DEBRIS ) + pObject->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS ); + + if ( pObject->IsNPC() && !pObject->IsEFlagSet( EFL_NO_MEGAPHYSCANNON_RAGDOLL ) && pObject->MyNPCPointer()->CanBecomeRagdoll() ) + { + Assert( pObject->MyNPCPointer()->CanBecomeRagdoll() ); + CTakeDamageInfo info( GetOwner(), GetOwner(), 1.0f, DMG_GENERIC ); + CBaseEntity *pRagdoll = CreateServerRagdoll( pObject->MyNPCPointer(), 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true ); + PhysSetEntityGameFlags( pRagdoll, FVPHYSICS_NO_SELF_COLLISIONS ); + pRagdoll->SetCollisionBounds( pObject->CollisionProp()->OBBMins(), pObject->CollisionProp()->OBBMaxs() ); + CTakeDamageInfo ragdollInfo( GetOwner(), GetOwner(), 10000.0, DMG_PHYSGUN | DMG_REMOVENORAGDOLL ); + pObject->TakeDamage( ragdollInfo ); + pObject = pRagdoll; + pPhysics = pRagdoll->VPhysicsGetObject(); + } + + m_hObject = pObject; + m_distance = distance; + + m_gravCallback.AttachEntity( pOwner, pObject, pPhysics, end ); + float mass = pPhysics->GetMass(); + 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 ); + + 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_iPrimaryAmmoType ) <= 0 ) + return; + + 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_iPrimaryAmmoType ); + + 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 ); + g_pEffects->Sparks( pPellet->GetAbsOrigin() ); + +} + +void CWeaponGravityGun::FreezeObject() +{ + if ( m_active && m_hObject ) + { + // FIXME(JBMOD): Don't freeze vehicles for now + CBaseEntity *pObject = m_hObject; + if ( pObject->GetServerVehicle() != NULL ) + { + if ( Q_strcmp( STRING( pObject->m_iClassname ), "prop_vehicle_airboat" ) && Q_strcmp( STRING( pObject->m_iClassname ), "prop_vehicle_prisoner_pod" ) ) + return; + } + + IPhysicsObject *pPhysicsObject = m_hObject->VPhysicsGetObject(); + + if ( pPhysicsObject ) { + m_activeFreeze[m_freezeCount].parent = m_hObject; + m_activeFreeze[m_freezeCount].physobject = pPhysicsObject; + m_freezeCount++; + + pPhysicsObject->EnableMotion( false ); + + m_flNextPrimaryAttack = gpGlobals->curtime + 0.75; + EffectDestroy(); + SoundDestroy(); + } + } +} + +void CWeaponGravityGun::UnfreezeObjects() +{ + SortFreezeList(); + for ( int i = 0; i < m_freezeCount; i++ ) + { + CBaseEntity *pObject = m_activeFreeze[i].parent; + IPhysicsObject *pPhysicsObject = m_activeFreeze[i].physobject; + if ( pObject && pObject->VPhysicsGetObject() && pPhysicsObject ) { + pPhysicsObject->Wake(); + bool motion = pPhysicsObject->IsMotionEnabled(); + if ( motion ) {} + pPhysicsObject->EnableMotion( true ); + } + } + m_freezeCount = 0; +} + +void CWeaponGravityGun::WeaponIdle( void ) +{ + 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, m_activePellets[m_pelletHeld].pellet, m_activePellets[m_pelletAttract].pellet ) ) + { + 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 ( m_active && ( pOwner->m_afButtonPressed & IN_ATTACK2 ) ) + { + FreezeObject(); + } + else if ( pOwner->m_afButtonPressed & IN_ATTACK2 ) + { + if ( m_flNextSecondaryAttack <= gpGlobals->curtime ) + SecondaryAttack(); + } + else if ( pOwner->m_nButtons & IN_ATTACK ) + { + if ( m_flNextPrimaryAttack <= gpGlobals->curtime ) + 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_iPrimaryAmmoType ) != MAX_PELLETS || m_freezeCount > 0 ) + { + pOwner->SetAmmoCount( MAX_PELLETS, m_iPrimaryAmmoType ); + DeleteActivePellets(); + UnfreezeObjects(); + WeaponSound( RELOAD ); + return true; + } + + return false; +} diff --git a/src/game/server/server_jbmod.vpc b/src/game/server/server_jbmod.vpc index a50b2e8dbae..23d04372b59 100644 --- a/src/game/server/server_jbmod.vpc +++ b/src/game/server/server_jbmod.vpc @@ -467,6 +467,7 @@ $Project "Server (JBMod)" $File "$SRCDIR\game\shared\jbmod\weapon_stunstick.cpp" $File "$SRCDIR\game\shared\jbmod\weapon_scripted.cpp" $File "$SRCDIR\game\shared\jbmod\weapon_scripted.h" + $File "jbmod\weapon_physgun.cpp" } } } diff --git a/src/game/shared/jbmod/jbmod_gamerules.cpp b/src/game/shared/jbmod/jbmod_gamerules.cpp index 4099fb6a2eb..1690465cfa6 100644 --- a/src/game/shared/jbmod/jbmod_gamerules.cpp +++ b/src/game/shared/jbmod/jbmod_gamerules.cpp @@ -1015,6 +1015,7 @@ CAmmoDef *GetAmmoDef() def.AddAmmoType("SMG1_Grenade", DMG_BURN, TRACER_NONE, 0, 0, 3, 0, 0 ); def.AddAmmoType("Grenade", DMG_BURN, TRACER_NONE, 0, 0, 5, 0, 0 ); def.AddAmmoType("slam", DMG_BURN, TRACER_NONE, 0, 0, 5, 0, 0 ); + def.AddAmmoType("Gravity", DMG_CLUB, TRACER_NONE, 0, 0, 8, 0, 0 ); } return &def;