aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/physics_cannister.cpp
diff options
context:
space:
mode:
authorJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
committerJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
commit39ed87570bdb2f86969d4be821c94b722dc71179 (patch)
treeabc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/server/physics_cannister.cpp
downloadsource-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz
source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/server/physics_cannister.cpp')
-rw-r--r--mp/src/game/server/physics_cannister.cpp483
1 files changed, 483 insertions, 0 deletions
diff --git a/mp/src/game/server/physics_cannister.cpp b/mp/src/game/server/physics_cannister.cpp
new file mode 100644
index 00000000..d63d121d
--- /dev/null
+++ b/mp/src/game/server/physics_cannister.cpp
@@ -0,0 +1,483 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "basecombatcharacter.h"
+#include "entityoutput.h"
+#include "physics.h"
+#include "explode.h"
+#include "vphysics_interface.h"
+#include "collisionutils.h"
+#include "steamjet.h"
+#include "eventqueue.h"
+#include "soundflags.h"
+#include "engine/IEngineSound.h"
+#include "props.h"
+#include "physics_cannister.h"
+#include "globals.h"
+#include "physics_saverestore.h"
+#include "shareddefs.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define SF_CANNISTER_ASLEEP 0x0001
+#define SF_CANNISTER_EXPLODE 0x0002
+
+BEGIN_SIMPLE_DATADESC( CThrustController )
+
+ DEFINE_FIELD( m_thrustVector, FIELD_VECTOR ),
+ DEFINE_FIELD( m_torqueVector, FIELD_VECTOR ),
+ DEFINE_KEYFIELD( m_thrust, FIELD_FLOAT, "thrust" ),
+
+END_DATADESC()
+
+
+LINK_ENTITY_TO_CLASS( physics_cannister, CPhysicsCannister );
+
+BEGIN_DATADESC( CPhysicsCannister )
+
+ DEFINE_OUTPUT( m_onActivate, "OnActivate" ),
+ DEFINE_OUTPUT( m_OnAwakened, "OnAwakened" ),
+ DEFINE_FIELD( m_thrustOrigin, FIELD_VECTOR ), // this is a position, but in local space
+ DEFINE_EMBEDDED( m_thruster ),
+ DEFINE_PHYSPTR( m_pController ),
+ DEFINE_FIELD( m_pJet, FIELD_CLASSPTR ),
+ DEFINE_FIELD( m_active, FIELD_BOOLEAN ),
+ DEFINE_KEYFIELD( m_thrustTime, FIELD_FLOAT, "fuel" ),
+ DEFINE_KEYFIELD( m_damage, FIELD_FLOAT, "expdamage" ),
+ DEFINE_KEYFIELD( m_damageRadius, FIELD_FLOAT, "expradius" ),
+ DEFINE_FIELD( m_activateTime, FIELD_TIME ),
+ DEFINE_KEYFIELD( m_gasSound, FIELD_SOUNDNAME, "gassound" ),
+ DEFINE_FIELD( m_bFired, FIELD_BOOLEAN ),
+
+ // Physics Influence
+ DEFINE_FIELD( m_hPhysicsAttacker, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_flLastPhysicsInfluenceTime, FIELD_TIME ),
+ DEFINE_FIELD( m_hLauncher, FIELD_EHANDLE ),
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Deactivate", InputDeactivate ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Explode", InputExplode ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Wake", InputWake ),
+
+ DEFINE_THINKFUNC( BeginShutdownThink ),
+ DEFINE_ENTITYFUNC( ExplodeTouch ),
+
+END_DATADESC()
+
+void CPhysicsCannister::Spawn( void )
+{
+ Precache();
+ SetModel( STRING(GetModelName()) );
+ SetBloodColor( DONT_BLEED );
+
+ AddSolidFlags( FSOLID_CUSTOMRAYTEST );
+ m_takedamage = DAMAGE_YES;
+ SetNextThink( TICK_NEVER_THINK );
+
+ if ( m_iHealth <= 0 )
+ m_iHealth = 25;
+
+ m_flAnimTime = gpGlobals->curtime;
+ m_flPlaybackRate = 0.0;
+ SetCycle( 0 );
+ m_bFired = false;
+
+ // not thrusting
+ m_active = false;
+
+ CreateVPhysics();
+ if ( !VPhysicsGetObject() )
+ {
+ // must have a physics object or code will crash later
+ UTIL_Remove(this);
+ }
+}
+
+void CPhysicsCannister::OnRestore()
+{
+ BaseClass::OnRestore();
+ if ( m_pController )
+ {
+ m_pController->SetEventHandler( &m_thruster );
+ }
+}
+
+bool CPhysicsCannister::CreateVPhysics()
+{
+ bool asleep = HasSpawnFlags(SF_CANNISTER_ASLEEP);
+
+ VPhysicsInitNormal( SOLID_VPHYSICS, 0, asleep );
+ return true;
+}
+
+bool CPhysicsCannister::TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace )
+{
+ Vector vecAbsMins, vecAbsMaxs;
+ CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs );
+
+ if ( !IsBoxIntersectingRay( vecAbsMins, vecAbsMaxs, ray.m_Start, ray.m_Delta ) )
+ return false;
+
+ return BaseClass::TestCollision( ray, mask, trace );
+}
+
+Vector CPhysicsCannister::CalcLocalThrust( const Vector &offset )
+{
+ matrix3x4_t nozzleMatrix;
+ Vector thrustDirection;
+
+ GetAttachment( LookupAttachment("nozzle"), nozzleMatrix );
+ MatrixGetColumn( nozzleMatrix, 2, thrustDirection );
+ MatrixGetColumn( nozzleMatrix, 3, m_thrustOrigin );
+ thrustDirection = -5*thrustDirection + offset;
+ VectorNormalize( thrustDirection );
+ return thrustDirection;
+}
+
+
+CPhysicsCannister::~CPhysicsCannister( void )
+{
+}
+
+void CPhysicsCannister::Precache( void )
+{
+ PropBreakablePrecacheAll( GetModelName() );
+ if ( m_gasSound != NULL_STRING )
+ {
+ PrecacheScriptSound( STRING(m_gasSound) );
+ }
+ BaseClass::Precache();
+}
+
+int CPhysicsCannister::OnTakeDamage( const CTakeDamageInfo &info )
+{
+ // HACKHACK: Shouldn't g_vecAttackDir be a parameter to this function?
+ if ( !m_takedamage )
+ return 0;
+
+ if ( !m_active )
+ {
+ m_iHealth -= info.GetDamage();
+ if ( m_iHealth < 0 )
+ {
+ Explode( info.GetAttacker() );
+ }
+ else
+ {
+ // explosions that don't destroy will activate
+ // 50% of the time blunt damage will activate as well
+ if ( (info.GetDamageType() & DMG_BLAST) ||
+ ( (info.GetDamageType() & (DMG_CLUB|DMG_SLASH|DMG_CRUSH) ) && random->RandomInt(1,100) < 50 ) )
+ {
+ CannisterActivate( info.GetAttacker(), g_vecAttackDir );
+ }
+ }
+ return 1;
+ }
+
+ if ( (gpGlobals->curtime - m_activateTime) <= 0.1 )
+ return 0;
+
+ if ( info.GetDamageType() & (DMG_BULLET|DMG_BUCKSHOT|DMG_BURN|DMG_BLAST) )
+ {
+ Explode( info.GetAttacker() );
+ }
+
+ return 0;
+}
+
+
+void CPhysicsCannister::TraceAttack( const CTakeDamageInfo &info, const Vector &dir, trace_t *ptr, CDmgAccumulator *pAccumulator )
+{
+ if ( !m_active && ptr->hitgroup != 0 )
+ {
+ Vector direction = -dir;
+ direction.z -= 5;
+ VectorNormalize( direction );
+ CannisterActivate( info.GetAttacker(), direction );
+ }
+ BaseClass::TraceAttack( info, dir, ptr, pAccumulator );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPhysicsCannister::CannisterActivate( CBaseEntity *pActivator, const Vector &thrustOffset )
+{
+ // already active or spent
+ if ( m_active || !m_thrustTime )
+ {
+ return;
+ }
+
+ m_hLauncher = pActivator;
+
+ Vector thrustDirection = CalcLocalThrust( thrustOffset );
+ m_onActivate.FireOutput( pActivator, this, 0 );
+ m_thruster.CalcThrust( m_thrustOrigin, thrustDirection, VPhysicsGetObject() );
+ m_pController = physenv->CreateMotionController( &m_thruster );
+ IPhysicsObject *pPhys = VPhysicsGetObject();
+ m_pController->AttachObject( pPhys, true );
+ // Make sure the object is simulated
+ pPhys->Wake();
+
+ m_active = true;
+ m_activateTime = gpGlobals->curtime;
+ SetNextThink( gpGlobals->curtime + m_thrustTime );
+ SetThink( &CPhysicsCannister::BeginShutdownThink );
+
+ QAngle angles;
+ VectorAngles( -thrustDirection, angles );
+ m_pJet = dynamic_cast<CSteamJet *>( CBaseEntity::Create( "env_steam", m_thrustOrigin, angles, this ) );
+ m_pJet->SetParent( this );
+
+ float extra = m_thruster.m_thrust * (1/5000.f);
+ extra = clamp( extra, 0.f, 1.f );
+
+ m_pJet->m_SpreadSpeed = 15 * m_thruster.m_thrust * 0.001;
+ m_pJet->m_Speed = 128 + 100 * extra;
+ m_pJet->m_StartSize = 10;
+ m_pJet->m_EndSize = 25;
+
+ m_pJet->m_Rate = 52 + (int)extra*20;
+ m_pJet->m_JetLength = 64;
+ m_pJet->m_clrRender = m_clrRender;
+
+ m_pJet->Use( this, this, USE_ON, 1 );
+ if ( m_gasSound != NULL_STRING )
+ {
+ CPASAttenuationFilter filter( this );
+
+ EmitSound_t ep;
+ ep.m_nChannel = CHAN_ITEM;
+ ep.m_pSoundName = STRING(m_gasSound);
+ ep.m_flVolume = 1.0f;
+ ep.m_SoundLevel = SNDLVL_NORM;
+
+ EmitSound( filter, entindex(), ep );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: The cannister's been fired by a weapon, so it should stay pretty accurate
+//-----------------------------------------------------------------------------
+void CPhysicsCannister::CannisterFire( CBaseEntity *pActivator )
+{
+ m_bFired = true;
+
+ // Increase thrust
+ m_thruster.m_thrust *= 4;
+
+ // Only last a short time
+ m_thrustTime = 10.0;
+
+ // Explode on contact
+ SetTouch( &CPhysicsCannister::ExplodeTouch );
+
+ CannisterActivate( pActivator, vec3_origin );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Input handler for activating the cannister.
+//-----------------------------------------------------------------------------
+void CPhysicsCannister::InputActivate( inputdata_t &data )
+{
+ CannisterActivate( data.pActivator, Vector(0,0.1,-0.25) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Input handler for deactivating the cannister.
+//-----------------------------------------------------------------------------
+void CPhysicsCannister::InputDeactivate(inputdata_t &data)
+{
+ Deactivate();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Input handler for making the cannister go boom.
+//-----------------------------------------------------------------------------
+void CPhysicsCannister::InputExplode(inputdata_t &data)
+{
+ Explode( data.pActivator );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Input handler for waking up the cannister if it is sleeping.
+//-----------------------------------------------------------------------------
+void CPhysicsCannister::InputWake( inputdata_t &data )
+{
+ IPhysicsObject *pPhys = VPhysicsGetObject();
+ if ( pPhys != NULL )
+ {
+ pPhys->Wake();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPhysicsCannister::Deactivate(void)
+{
+ if ( !m_pController )
+ return;
+
+ m_pController->DetachObject( VPhysicsGetObject() );
+ physenv->DestroyMotionController( m_pController );
+ m_pController = NULL;
+ SetNextThink( TICK_NEVER_THINK );
+ m_thrustTime = 0;
+ m_active = false;
+ if ( m_pJet )
+ {
+ ShutdownJet();
+ }
+ if ( m_gasSound != NULL_STRING )
+ {
+ StopSound( entindex(), CHAN_ITEM, STRING(m_gasSound) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPhysicsCannister::Explode( CBaseEntity *pAttacker )
+{
+ // don't recurse
+ m_takedamage = 0;
+ Deactivate();
+
+ Vector velocity;
+ AngularImpulse angVelocity;
+ IPhysicsObject *pPhysics = VPhysicsGetObject();
+
+ pPhysics->GetVelocity( &velocity, &angVelocity );
+ PropBreakableCreateAll( GetModelIndex(), pPhysics, GetAbsOrigin(), GetAbsAngles(), velocity, angVelocity, 1.0, 20, COLLISION_GROUP_DEBRIS );
+ ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), pAttacker, m_damage, 0, true );
+ UTIL_Remove( this );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Explode when I next hit a damageable entity
+//-----------------------------------------------------------------------------
+void CPhysicsCannister::ExplodeTouch( CBaseEntity *pOther )
+{
+ if ( !pOther->m_takedamage )
+ return;
+
+ Explode( m_hLauncher );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPhysicsCannister::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
+{
+ if ( m_bFired && m_active )
+ {
+ int otherIndex = !index;
+ CBaseEntity *pHitEntity = pEvent->pEntities[otherIndex];
+ if ( pEvent->deltaCollisionTime < 0.5 && (pHitEntity == this) )
+ return;
+
+ // If we hit hard enough. explode
+ if ( pEvent->collisionSpeed > 1000 )
+ {
+ Explode( m_hLauncher );
+ return;
+ }
+ }
+
+ BaseClass::VPhysicsCollision( index, pEvent );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPhysicsCannister::ShutdownJet( void )
+{
+ g_EventQueue.AddEvent( m_pJet, "kill", 5, NULL, NULL );
+
+ m_pJet->m_bEmit = false;
+ m_pJet->m_Rate = 0;
+ m_pJet = NULL;
+ SetNextThink( TICK_NEVER_THINK );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: The think just shuts the cannister down
+//-----------------------------------------------------------------------------
+void CPhysicsCannister::BeginShutdownThink( void )
+{
+ Deactivate();
+}
+
+//-----------------------------------------------------------------------------
+// Physics Attacker
+//-----------------------------------------------------------------------------
+void CPhysicsCannister::SetPhysicsAttacker( CBasePlayer *pEntity, float flTime )
+{
+ m_hPhysicsAttacker = pEntity;
+ m_flLastPhysicsInfluenceTime = flTime;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Keep track of physgun influence
+//-----------------------------------------------------------------------------
+void CPhysicsCannister::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
+{
+ SetPhysicsAttacker( pPhysGunUser, gpGlobals->curtime );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPhysicsCannister::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason )
+{
+ SetPhysicsAttacker( pPhysGunUser, gpGlobals->curtime );
+ if ( Reason == LAUNCHED_BY_CANNON )
+ {
+ CannisterActivate( pPhysGunUser, vec3_origin );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBasePlayer *CPhysicsCannister::HasPhysicsAttacker( float dt )
+{
+ if (gpGlobals->curtime - dt <= m_flLastPhysicsInfluenceTime)
+ {
+ return m_hPhysicsAttacker;
+ }
+ return NULL;
+}
+//-----------------------------------------------------------------------------
+// Purpose: Update the visible representation of the physic system's representation of this object
+//-----------------------------------------------------------------------------
+void CPhysicsCannister::VPhysicsUpdate( IPhysicsObject *pPhysics )
+{
+ BaseClass::VPhysicsUpdate( pPhysics );
+
+ // if this is the first time we have moved, fire our target
+ if ( HasSpawnFlags( SF_CANNISTER_ASLEEP ) )
+ {
+ if ( !pPhysics->IsAsleep() )
+ {
+ m_OnAwakened.FireOutput(this, this);
+ RemoveSpawnFlags( SF_CANNISTER_ASLEEP );
+ }
+ }
+} \ No newline at end of file