summaryrefslogtreecommitdiff
path: root/game/server/tf2/tf_shieldgrenade.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/tf2/tf_shieldgrenade.cpp')
-rw-r--r--game/server/tf2/tf_shieldgrenade.cpp424
1 files changed, 424 insertions, 0 deletions
diff --git a/game/server/tf2/tf_shieldgrenade.cpp b/game/server/tf2/tf_shieldgrenade.cpp
new file mode 100644
index 0000000..157395f
--- /dev/null
+++ b/game/server/tf2/tf_shieldgrenade.cpp
@@ -0,0 +1,424 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "BaseAnimating.h"
+#include "tf_shieldgrenade.h"
+#include "tf_shieldshared.h"
+#include "tf_shield_flat.h"
+#include "tf_player.h"
+#include "engine/IEngineSound.h"
+#include "Sprite.h"
+
+#define SHIELD_GRENADE_FUSE_TIME 2.25f
+
+//-----------------------------------------------------------------------------
+//
+// The shield grenade class
+//
+//-----------------------------------------------------------------------------
+
+class CShieldGrenade : public CBaseAnimating
+{
+ DECLARE_CLASS( CShieldGrenade, CBaseAnimating );
+public:
+ DECLARE_DATADESC();
+
+ CShieldGrenade();
+
+ virtual void Spawn( void );
+ virtual void Precache( void );
+ virtual void UpdateOnRemove( void );
+ void SetLifetime( float timer );
+
+ int GetDamageType() const { return DMG_ENERGYBEAM; }
+
+ void StickyTouch( CBaseEntity *pOther );
+ void BeepThink( void );
+ void ShieldActiveThink( void );
+ void DeathThink( void );
+
+ virtual bool CanTakeEMPDamage() { return true; }
+ virtual bool TakeEMPDamage( float duration );
+
+private:
+ // Check when we're done with EMP
+ void CheckEMPDamageFinish( );
+ void CreateShield( );
+ void ComputeActiveThinkTime( );
+ void BounceSound( );
+
+private:
+ // Make sure all grenades explode
+ Vector m_LastCollision;
+
+ // Time when EMP runs out
+ float m_flEMPDamageEndTime;
+ float m_ShieldLifetime;
+ float m_flDetonateTime;
+
+ // Are we EMPed?
+ bool m_IsEMPed;
+ bool m_IsDeployed;
+
+ // The deployed shield
+ CHandle<CShieldFlat> m_hDeployedShield;
+
+ CSprite *m_pLiveSprite;
+};
+
+//-----------------------------------------------------------------------------
+// Data table
+//-----------------------------------------------------------------------------
+
+// Global Savedata for friction modifier
+BEGIN_DATADESC( CShieldGrenade )
+
+ // Function Pointers
+ DEFINE_FUNCTION( StickyTouch ),
+ DEFINE_FUNCTION( BeepThink ),
+ DEFINE_FUNCTION( ShieldActiveThink ),
+ DEFINE_FUNCTION( DeathThink ),
+
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( grenade_shield, CShieldGrenade );
+PRECACHE_REGISTER( grenade_shield );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CShieldGrenade::CShieldGrenade()
+{
+ UseClientSideAnimation();
+ m_hDeployedShield.Set(0);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CShieldGrenade::Precache( void )
+{
+ PrecacheModel( "models/weapons/w_grenade.mdl" );
+
+ PrecacheScriptSound( "ShieldGrenade.Bounce" );
+ PrecacheScriptSound( "ShieldGrenade.StickBeep" );
+
+ PrecacheModel( "sprites/redglow1.vmt" );
+
+ BaseClass::Precache();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CShieldGrenade::Spawn( void )
+{
+ BaseClass::Spawn();
+ m_LastCollision.Init( 0, 0, 0 );
+ SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
+ SetSolid( SOLID_BBOX );
+ SetGravity( 1.0 );
+ SetFriction( 0.9 );
+ SetModel( "models/weapons/w_grenade.mdl");
+ UTIL_SetSize(this, Vector( -4, -4, -4), Vector(4, 4, 4));
+ m_IsEMPed = false;
+ m_IsDeployed = false;
+
+ m_flEMPDamageEndTime = 0.0f;
+
+ SetTouch( StickyTouch );
+ SetCollisionGroup( TFCOLLISION_GROUP_GRENADE );
+
+ // Create a green light
+ m_pLiveSprite = CSprite::SpriteCreate( "sprites/redglow1.vmt", GetLocalOrigin() + Vector(0,0,1), false );
+ m_pLiveSprite->SetTransparency( kRenderGlow, 0, 0, 255, 128, kRenderFxNoDissipation );
+ m_pLiveSprite->SetScale( 1 );
+ m_pLiveSprite->SetAttachment( this, 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CShieldGrenade::UpdateOnRemove( void )
+{
+ if ( m_pLiveSprite )
+ {
+ UTIL_Remove( m_pLiveSprite );
+ m_pLiveSprite = NULL;
+ }
+
+ BaseClass::UpdateOnRemove();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CShieldGrenade::SetLifetime( float timer )
+{
+ m_ShieldLifetime = timer;
+}
+
+
+//-----------------------------------------------------------------------------
+// EMP Related methods
+//-----------------------------------------------------------------------------
+bool CShieldGrenade::TakeEMPDamage( float duration )
+{
+ m_flEMPDamageEndTime = gpGlobals->curtime + duration;
+ m_IsEMPed = true;
+ if (m_hDeployedShield)
+ {
+ m_hDeployedShield->SetEMPed(true);
+
+ // Recompute the next think time
+ ComputeActiveThinkTime();
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: See if EMP impairment time has elapsed
+//-----------------------------------------------------------------------------
+void CShieldGrenade::CheckEMPDamageFinish( void )
+{
+ if ( !m_flEMPDamageEndTime || gpGlobals->curtime < m_flEMPDamageEndTime )
+ return;
+
+ m_flEMPDamageEndTime = 0.0f;
+ m_IsEMPed = false;
+ if (m_hDeployedShield)
+ {
+ m_hDeployedShield->SetEMPed(false);
+
+ // Recompute the next think time
+ ComputeActiveThinkTime();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Plays a random bounce sound
+//-----------------------------------------------------------------------------
+void CShieldGrenade ::BounceSound( void )
+{
+ EmitSound( "ShieldGrenade.Bounce" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Make the grenade stick to whatever it touches
+//-----------------------------------------------------------------------------
+void CShieldGrenade::StickyTouch( CBaseEntity *pOther )
+{
+ if (m_IsDeployed)
+ return;
+
+ // The touch can get called multiple times - create an ignore case if we
+ // have already stuck.
+ if ( m_LastCollision == GetLocalOrigin() )
+ return;
+
+ // Only stick to floors...
+ Vector up( 0, 0, 1 );
+ if ( DotProduct( GetTouchTrace().plane.normal, up ) < 0.5f )
+ return;
+
+ // Only stick to BSP models
+ if ( pOther->IsBSPModel() == false )
+ return;
+
+ BounceSound();
+ SetAbsVelocity( vec3_origin );
+ SetMoveType( MOVETYPE_NONE );
+
+ // Beep
+ EmitSound( "ShieldGrenade.StickBeep" );
+
+ // Start ticking...
+ SetThink( BeepThink );
+ m_IsDeployed = true;
+ SetNextThink( gpGlobals->curtime + 0.01f );
+ m_flDetonateTime = gpGlobals->curtime + SHIELD_GRENADE_FUSE_TIME;
+
+ m_LastCollision = GetLocalOrigin();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Play beeping sounds until the charge explode
+//-----------------------------------------------------------------------------
+void CShieldGrenade::CreateShield( void )
+{
+ /*
+ // Set the orientation of the shield based on
+ // the closest teammate's relative position...
+ float mindist = FLT_MAX;
+ Vector dir;
+
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( UTIL_PlayerByIndex(i) );
+ if ( pPlayer )
+ {
+ if (pPlayer->GetTeamNumber() == GetTeamNumber())
+ {
+ Vector tempdir;
+ VectorSubtract( pPlayer->Center(), Center(), tempdir );
+ float dist = VectorNormalize( tempdir );
+ if (dist < mindist)
+ {
+ mindist = dist;
+ VectorCopy( tempdir, dir );
+ }
+ }
+ }
+ }
+
+ if( mindist == FLT_MAX )
+ {
+ AngleVectors( GetAngles(), &dir );
+ }
+
+ // Never pitch the shield
+ dir.z = 0.0f;
+ VectorNormalize(dir);
+
+ QAngle relAngles;
+ VMatrix parentMatrix;
+ VMatrix worldShieldMatrix;
+ VMatrix relativeMatrix;
+ VMatrix parentInvMatrix;
+
+ // Construct a transform from shield to grenade (parent)
+ MatrixFromAngles( GetAngles(), parentMatrix );
+
+#ifdef _DEBUG
+ bool ok =
+#endif
+ MatrixInverseGeneral( parentMatrix, parentInvMatrix );
+ Assert( ok );
+
+ Vector up( 0, 0, 1 );
+ Vector left;
+ CrossProduct( up, dir, left );
+ MatrixSetIdentity( worldShieldMatrix );
+ worldShieldMatrix.SetUp( up );
+ worldShieldMatrix.SetLeft( left );
+ worldShieldMatrix.SetForward( dir );
+
+ MatrixMultiply( parentInvMatrix, worldShieldMatrix, relativeMatrix );
+ MatrixToAngles( relativeMatrix, relAngles );
+ */
+
+ Vector offset( 0, 0, SHIELD_GRENADE_HEIGHT * 0.5f );
+ m_hDeployedShield = CreateFlatShield( this, SHIELD_GRENADE_WIDTH,
+ SHIELD_GRENADE_HEIGHT, offset, vec3_angle );
+
+ // Notify it about EMP state
+ if (m_IsEMPed)
+ m_hDeployedShield->SetEMPed(true);
+
+ // Play a sound
+// WeaponSound( SPECIAL1 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Play beeping sounds until the charge explode
+//-----------------------------------------------------------------------------
+void CShieldGrenade::BeepThink( void )
+{
+ if (!IsInWorld())
+ {
+ UTIL_Remove( this );
+ return;
+ }
+
+ if (m_flDetonateTime <= gpGlobals->curtime)
+ {
+ // Here we must project the shield
+ CreateShield();
+ SetThink( ShieldActiveThink );
+ m_flDetonateTime = gpGlobals->curtime + m_ShieldLifetime;
+
+ // Get the EMP state correct
+ CheckEMPDamageFinish();
+ ComputeActiveThinkTime();
+ }
+ else
+ {
+ SetNextThink( gpGlobals->curtime + 1.0f );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute next think time while active
+//-----------------------------------------------------------------------------
+void CShieldGrenade::ComputeActiveThinkTime( void )
+{
+ // Next think should be when we detonate, unless we un-EMP before then
+ SetNextThink( gpGlobals->curtime + m_flDetonateTime );
+ if (m_IsEMPed)
+ {
+ Assert( m_flEMPDamageEndTime != 0.0f );
+ if ( m_flEMPDamageEndTime < GetNextThink() )
+ SetNextThink( gpGlobals->curtime + m_flEMPDamageEndTime );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Here's where the grenade "detonates"
+//-----------------------------------------------------------------------------
+void CShieldGrenade::ShieldActiveThink( void )
+{
+ if (m_flDetonateTime > gpGlobals->curtime)
+ {
+ // If it's not time to die, check EMP state
+ CheckEMPDamageFinish();
+ }
+ else
+ {
+ if (m_hDeployedShield)
+ {
+ m_hDeployedShield->Activate( false );
+ }
+ SetNextThink( gpGlobals->curtime + SHIELD_FLAT_SHUTDOWN_TIME + 0.2f );
+ SetThink( DeathThink );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CShieldGrenade::DeathThink( void )
+{
+ // kill the grenade
+ if (m_hDeployedShield)
+ {
+ UTIL_Remove( m_hDeployedShield );
+ m_hDeployedShield.Set(0);
+ }
+ UTIL_Remove( this );
+}
+
+//-----------------------------------------------------------------------------
+// Creates a shield grenade
+//-----------------------------------------------------------------------------
+CBaseEntity *CreateShieldGrenade( const Vector &position, const QAngle &angles, const Vector &velocity, const QAngle &angVelocity, CBaseEntity *pOwner, float timer )
+{
+ CShieldGrenade *pGrenade = (CShieldGrenade *)CBaseEntity::Create( "grenade_shield", position, angles, pOwner );
+ pGrenade->SetLifetime( timer );
+ pGrenade->SetAbsVelocity( velocity );
+
+ if (pOwner)
+ {
+ pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
+ }
+
+ return pGrenade;
+} \ No newline at end of file