summaryrefslogtreecommitdiff
path: root/game/shared/cstrike/weapon_c4.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/cstrike/weapon_c4.cpp')
-rw-r--r--game/shared/cstrike/weapon_c4.cpp1353
1 files changed, 1353 insertions, 0 deletions
diff --git a/game/shared/cstrike/weapon_c4.cpp b/game/shared/cstrike/weapon_c4.cpp
new file mode 100644
index 0000000..e75d6a3
--- /dev/null
+++ b/game/shared/cstrike/weapon_c4.cpp
@@ -0,0 +1,1353 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_c4.h"
+#include "in_buttons.h"
+#include "cs_gamerules.h"
+#include "decals.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "KeyValues.h"
+#include "fx_cs_shared.h"
+#include "obstacle_pushaway.h"
+
+#if defined( CLIENT_DLL )
+ #include "c_cs_player.h"
+#else
+ #include "cs_player.h"
+ #include "explode.h"
+ #include "mapinfo.h"
+ #include "team.h"
+ #include "func_bomb_target.h"
+ #include "vguiscreen.h"
+ #include "bot.h"
+ #include "cs_player.h"
+ #include <KeyValues.h>
+
+//=============================================================================
+// HPE_BEGIN
+// [dwenger] Necessary for stats tracking
+//=============================================================================
+#include "cs_gamestats.h"
+#include "cs_achievement_constants.h"
+//=============================================================================
+// HPE_END
+//=============================================================================
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+#define BLINK_INTERVAL 2.0
+#define PLANTED_C4_MODEL "models/weapons/w_c4_planted.mdl"
+#define HEIST_MODE_C4_TIME 25
+
+int g_sModelIndexC4Glow = -1;
+
+#define WEAPON_C4_ARM_TIME 3.0
+
+
+#ifdef CLIENT_DLL
+
+#else
+
+
+ LINK_ENTITY_TO_CLASS( planted_c4, CPlantedC4 );
+ PRECACHE_REGISTER( planted_c4 );
+
+ BEGIN_DATADESC( CPlantedC4 )
+ DEFINE_FUNCTION( C4Think )
+ END_DATADESC()
+
+
+ IMPLEMENT_SERVERCLASS_ST( CPlantedC4, DT_PlantedC4 )
+ SendPropBool( SENDINFO(m_bBombTicking) ),
+ SendPropFloat( SENDINFO(m_flC4Blow), 0, SPROP_NOSCALE ),
+ SendPropFloat( SENDINFO(m_flTimerLength), 0, SPROP_NOSCALE ),
+ SendPropFloat( SENDINFO(m_flDefuseLength), 0, SPROP_NOSCALE ),
+ SendPropFloat( SENDINFO(m_flDefuseCountDown), 0, SPROP_NOSCALE ),
+ END_SEND_TABLE()
+
+
+BEGIN_PREDICTION_DATA( CPlantedC4 )
+END_PREDICTION_DATA()
+
+
+
+ CUtlVector< CPlantedC4* > g_PlantedC4s;
+
+
+ CPlantedC4::CPlantedC4()
+ {
+ g_PlantedC4s.AddToTail( this );
+ //=============================================================================
+ // HPE_BEGIN:
+ //=============================================================================
+
+ // [tj] No planter initially
+ m_pPlanter = NULL;
+
+ // [tj] Assume this is the original owner
+ m_bPlantedAfterPickup = false;
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ }
+
+ CPlantedC4::~CPlantedC4()
+ {
+ g_PlantedC4s.FindAndRemove( this );
+
+ int i;
+ // Kill the control panels
+ for ( i = m_hScreens.Count(); --i >= 0; )
+ {
+ DestroyVGuiScreen( m_hScreens[i].Get() );
+ }
+ m_hScreens.RemoveAll();
+ }
+
+ int CPlantedC4::UpdateTransmitState()
+ {
+ return SetTransmitState( FL_EDICT_FULLCHECK );
+ }
+
+ int CPlantedC4::ShouldTransmit( const CCheckTransmitInfo *pInfo )
+ {
+ // Terrorists always need this object for the radar
+ // Everybody needs it for hiding the round timer and showing the planted C4 scenario icon
+ return FL_EDICT_ALWAYS;
+ }
+
+ void CPlantedC4::Precache()
+ {
+ g_sModelIndexC4Glow = PrecacheModel( "sprites/ledglow.vmt" );
+ PrecacheModel( PLANTED_C4_MODEL );
+ PrecacheVGuiScreen( "c4_panel" );
+
+ engine->ForceModelBounds( PLANTED_C4_MODEL, Vector( -7, -13, -3 ), Vector( 9, 12, 11 ) );
+
+ PrecacheParticleSystem( "bomb_explosion_huge" );
+ }
+
+ void CPlantedC4::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
+ {
+ pPanelName = "c4_panel";
+ }
+
+ void CPlantedC4::GetControlPanelClassName( int nPanelIndex, const char *&pPanelName )
+ {
+ pPanelName = "vgui_screen";
+ }
+
+ //-----------------------------------------------------------------------------
+ // This is called by the base object when it's time to spawn the control panels
+ //-----------------------------------------------------------------------------
+ void CPlantedC4::SpawnControlPanels()
+ {
+ char buf[64];
+
+ // FIXME: Deal with dynamically resizing control panels?
+
+ // If we're attached to an entity, spawn control panels on it instead of use
+ CBaseAnimating *pEntityToSpawnOn = this;
+ const char *pOrgLL = "controlpanel%d_ll";
+ const char *pOrgUR = "controlpanel%d_ur";
+ const char *pAttachmentNameLL = pOrgLL;
+ const char *pAttachmentNameUR = pOrgUR;
+
+ Assert( pEntityToSpawnOn );
+
+ // Lookup the attachment point...
+ int nPanel;
+ for ( nPanel = 0; true; ++nPanel )
+ {
+ Q_snprintf( buf, sizeof( buf ), pAttachmentNameLL, nPanel );
+ int nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
+ if (nLLAttachmentIndex <= 0)
+ {
+ // Try and use my panels then
+ pEntityToSpawnOn = this;
+ Q_snprintf( buf, sizeof( buf ), pOrgLL, nPanel );
+ nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
+ if (nLLAttachmentIndex <= 0)
+ return;
+ }
+
+ Q_snprintf( buf, sizeof( buf ), pAttachmentNameUR, nPanel );
+ int nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
+ if (nURAttachmentIndex <= 0)
+ {
+ // Try and use my panels then
+ Q_snprintf( buf, sizeof( buf ), pOrgUR, nPanel );
+ nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
+ if (nURAttachmentIndex <= 0)
+ return;
+ }
+
+ const char *pScreenName;
+ GetControlPanelInfo( nPanel, pScreenName );
+ if (!pScreenName)
+ continue;
+
+ const char *pScreenClassname;
+ GetControlPanelClassName( nPanel, pScreenClassname );
+ if ( !pScreenClassname )
+ continue;
+
+ // Compute the screen size from the attachment points...
+ matrix3x4_t panelToWorld;
+ pEntityToSpawnOn->GetAttachment( nLLAttachmentIndex, panelToWorld );
+
+ matrix3x4_t worldToPanel;
+ MatrixInvert( panelToWorld, worldToPanel );
+
+ // Now get the lower right position + transform into panel space
+ Vector lr, lrlocal;
+ pEntityToSpawnOn->GetAttachment( nURAttachmentIndex, panelToWorld );
+ MatrixGetColumn( panelToWorld, 3, lr );
+ VectorTransform( lr, worldToPanel, lrlocal );
+
+ float flWidth = lrlocal.x;
+ float flHeight = lrlocal.y;
+
+ CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, pEntityToSpawnOn, this, nLLAttachmentIndex );
+ pScreen->ChangeTeam( GetTeamNumber() );
+ pScreen->SetActualSize( flWidth, flHeight );
+ pScreen->SetActive( true );
+ pScreen->MakeVisibleOnlyToTeammates( false );
+ int nScreen = m_hScreens.AddToTail( );
+ m_hScreens[nScreen].Set( pScreen );
+ }
+ }
+
+ void CPlantedC4::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
+ {
+ // Are we already marked for transmission?
+ if ( pInfo->m_pTransmitEdict->Get( entindex() ) )
+ return;
+
+ BaseClass::SetTransmit( pInfo, bAlways );
+
+ // Force our screens to be sent too.
+ for ( int i=0; i < m_hScreens.Count(); i++ )
+ {
+ CVGuiScreen *pScreen = m_hScreens[i].Get();
+ pScreen->SetTransmit( pInfo, bAlways );
+ }
+ }
+
+ CPlantedC4* CPlantedC4::ShootSatchelCharge( CCSPlayer *pevOwner, Vector vecStart, QAngle vecAngles )
+ {
+ CPlantedC4 *pGrenade = dynamic_cast< CPlantedC4* >( CreateEntityByName( "planted_c4" ) );
+ if ( pGrenade )
+ {
+ vecAngles[0] = 0;
+ vecAngles[2] = 0;
+ pGrenade->Init( pevOwner, vecStart, vecAngles );
+ return pGrenade;
+ }
+ else
+ {
+ Warning( "Can't create planted_c4 entity!\n" );
+ return NULL;
+ }
+ }
+
+
+ void CPlantedC4::Init( CCSPlayer *pevOwner, Vector vecStart, QAngle vecAngles )
+ {
+ SetMoveType( MOVETYPE_NONE );
+ SetSolid( SOLID_NONE );
+
+ SetModel( PLANTED_C4_MODEL ); // Change this to c4 model
+
+ SetCollisionBounds( Vector( 0, 0, 0 ), Vector( 8, 8, 8 ) );
+
+ SetAbsOrigin( vecStart );
+ SetAbsAngles( vecAngles );
+ SetOwnerEntity( pevOwner );
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Set the planter when the bomb is planted.
+ //=============================================================================
+
+ SetPlanter( pevOwner );
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+
+ // Detonate in "time" seconds
+ SetThink( &CPlantedC4::C4Think );
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ m_flTimerLength = mp_c4timer.GetInt();
+
+ m_flC4Blow = gpGlobals->curtime + m_flTimerLength;
+ m_flNextDefuse = 0;
+
+ m_bStartDefuse = false;
+ m_bBombTicking = true;
+ SetFriction( 0.9 );
+
+ m_flDefuseLength = 0.0f;
+
+ SpawnControlPanels();
+ }
+
+ void CPlantedC4::C4Think()
+ {
+ if (!IsInWorld())
+ {
+ UTIL_Remove( this );
+ return;
+ }
+
+ //Bomb is dead, don't think anymore
+ if( !m_bBombTicking )
+ {
+ SetThink( NULL );
+ return;
+ }
+
+
+ SetNextThink( gpGlobals->curtime + 0.12 );
+
+#ifndef CLIENT_DLL
+ // let the bots hear the bomb beeping
+ // BOTPORT: Emit beep events at same time as client effects
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_beep" );
+ if( event )
+ {
+ event->SetInt( "entindex", entindex() );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+
+ // IF the timer has expired ! blow this bomb up!
+ if (m_flC4Blow <= gpGlobals->curtime)
+ {
+ // give the defuser credit for defusing the bomb
+ CCSPlayer* pBombOwner = ToCSPlayer(GetOwnerEntity());
+ if ( pBombOwner )
+ {
+ if (CSGameRules()->m_iRoundWinStatus == WINNER_NONE)
+ pBombOwner->IncrementFragCount( 3 );
+ }
+
+ CSGameRules()->m_bBombDropped = false;
+
+ trace_t tr;
+ Vector vecSpot = GetAbsOrigin();
+ vecSpot[2] += 8;
+
+ UTIL_TraceLine( vecSpot, vecSpot + Vector ( 0, 0, -40 ), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
+
+ Explode( &tr, DMG_BLAST );
+
+ CSGameRules()->m_bBombPlanted = false;
+
+ CCS_GameStats.Event_BombExploded(pBombOwner);
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_exploded" );
+ if( event )
+ {
+ event->SetInt( "userid", pBombOwner?pBombOwner->GetUserID():-1 );
+ event->SetInt( "site", m_iBombSiteIndex );
+ event->SetInt( "priority", 9 );
+ gameeventmanager->FireEvent( event );
+ }
+
+ // skip additional processing once the bomb has exploded
+ return;
+ }
+
+ //if the defusing process has started
+ if ((m_bStartDefuse == true) && (m_pBombDefuser != NULL))
+ {
+ //if the defusing process has not ended yet
+ if ( m_flDefuseCountDown > gpGlobals->curtime)
+ {
+ int iOnGround = FBitSet( m_pBombDefuser->GetFlags(), FL_ONGROUND );
+
+ //if the bomb defuser has stopped defusing the bomb
+ if( m_flNextDefuse < gpGlobals->curtime || !iOnGround )
+ {
+ if ( !iOnGround && m_pBombDefuser->IsAlive() )
+ ClientPrint( m_pBombDefuser, HUD_PRINTCENTER, "#C4_Defuse_Must_Be_On_Ground");
+
+ // release the player from being frozen
+ m_pBombDefuser->m_bIsDefusing = false;
+
+#ifndef CLIENT_DLL
+ // tell the bots someone has aborted defusing
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortdefuse" );
+ if( event )
+ {
+ event->SetInt("userid", m_pBombDefuser->GetUserID() );
+ event->SetInt( "priority", 6 );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+
+ //cancel the progress bar
+ m_pBombDefuser->SetProgressBarTime( 0 );
+ m_pBombDefuser->OnCanceledDefuse();
+ m_pBombDefuser = NULL;
+ m_bStartDefuse = false;
+ m_flDefuseCountDown = 0;
+ m_flDefuseLength = 0; //force it to show completely defused
+ }
+
+ return;
+ }
+
+ //if the defuse process has ended, kill the c4
+ if ( !m_pBombDefuser->IsDead() )
+ {
+ //=============================================================================
+ // HPE_BEGIN
+ // [dwenger] Stats update for bomb defusing
+ //=============================================================================
+ CCS_GameStats.Event_BombDefused( m_pBombDefuser );
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_defused" );
+ if( event )
+ {
+ event->SetInt("userid", m_pBombDefuser->GetUserID() );
+ event->SetInt("site", m_iBombSiteIndex );
+ event->SetInt( "priority", 9 );
+ gameeventmanager->FireEvent( event );
+
+ //=============================================================================
+ // HPE_BEGIN
+ // [dwenger] Server-side processing for defusing bombs
+ //=============================================================================
+ m_pBombDefuser->AwardAchievement(CSWinBombDefuse);
+
+ float timeToDetonation = (m_flC4Blow - gpGlobals->curtime);
+
+ if ((timeToDetonation > 0.0f) && (timeToDetonation <= AchievementConsts::BombDefuseCloseCall_MaxTimeRemaining))
+ {
+ // Give achievement for defusing with < 1 second before detonation
+ m_pBombDefuser->AwardAchievement(CSBombDefuseCloseCall);
+ }
+
+ if ((timeToDetonation > 0.0f) && (m_pBombDefuser->HasDefuser()) && (timeToDetonation < AchievementConsts::BombDefuseNeededKit_MaxTime))
+ {
+ // Give achievement for defusing with a defuse kit when not having the kit would have taken too long
+ m_pBombDefuser->AwardAchievement(CSDefuseAndNeededKit);
+ }
+
+ // [dwenger] Added for fun-fact support
+ if ( m_pBombDefuser->PickedUpDefuser() )
+ {
+ // Defuser kit was picked up, so set the fun fact
+ m_pBombDefuser->SetDefusedWithPickedUpKit(true);
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ }
+
+
+ Vector soundPosition = m_pBombDefuser->GetAbsOrigin() + Vector( 0, 0, 5 );
+ CPASAttenuationFilter filter( soundPosition );
+
+ EmitSound( filter, entindex(), "c4.disarmfinish" );
+
+ // The bomb has just been disarmed.. Check to see if the round should end now
+ m_bBombTicking = false;
+
+ // release the player from being frozen
+ m_pBombDefuser->m_bIsDefusing = false;
+
+ CSGameRules()->m_bBombDefused = true;
+ //=============================================================================
+ // HPE_BEGIN:
+ // [menglish] Give the bomb defuser an mvp if they ended the round
+ //=============================================================================
+ bool roundWasAlreadyWon = (CSGameRules()->m_iRoundWinStatus != WINNER_NONE);
+
+ if(CSGameRules()->CheckWinConditions() && !roundWasAlreadyWon)
+ {
+ m_pBombDefuser->IncrementNumMVPs( CSMVP_BOMBDEFUSE );
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ // give the defuser credit for defusing the bomb
+ m_pBombDefuser->IncrementFragCount( 3 );
+
+ CSGameRules()->m_bBombDropped = false;
+ CSGameRules()->m_bBombPlanted = false;
+
+ // Clear their progress bar.
+ m_pBombDefuser->SetProgressBarTime( 0 );
+
+ m_pBombDefuser = NULL;
+ m_bStartDefuse = false;
+
+ m_flDefuseLength = 10;
+
+ return;
+ }
+
+ //if it gets here then the previouse defuser has taken off or been killed
+
+#ifndef CLIENT_DLL
+ // tell the bots someone has aborted defusing
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortdefuse" );
+ if ( event )
+ {
+ event->SetInt("userid", m_pBombDefuser->GetUserID() );
+ event->SetInt( "priority", 6 );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+
+ // release the player from being frozen
+ m_pBombDefuser->m_bIsDefusing = false;
+ m_bStartDefuse = false;
+ m_pBombDefuser = NULL;
+ }
+ }
+
+ // Regular explosions
+ void CPlantedC4::Explode( trace_t *pTrace, int bitsDamageType )
+ {
+ // Check to see if the round is over after the bomb went off...
+ CSGameRules()->m_bTargetBombed = true;
+ m_bBombTicking = false;
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Saving off this value so we can see if the detonation is what caused the round to end.
+ //=============================================================================
+ bool roundWasAlreadyWon = (CSGameRules()->m_iRoundWinStatus != WINNER_NONE);
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ bool bWin = CSGameRules()->CheckWinConditions();
+
+ //=============================================================================
+ // HPE_BEGIN
+ //=============================================================================
+
+ // [dwenger] Server-side processing for winning round by planting a bomb
+ if (bWin)
+ {
+ CCSPlayer *pBombOwner = ToCSPlayer( GetOwnerEntity() );
+ if ( pBombOwner )
+ {
+ pBombOwner->AwardAchievement(CSWinBombPlant);
+
+ //[tj]more specific achievement for planting the bomb after recovering it.
+ if (m_bPlantedAfterPickup)
+ {
+ pBombOwner->AwardAchievement(CSWinBombPlantAfterRecovery);
+ }
+ // [menglish] awarding mvp to bomb planter
+ if (!roundWasAlreadyWon)
+ {
+ pBombOwner->IncrementNumMVPs( CSMVP_BOMBPLANT );
+ }
+ }
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ // Do the Damage
+ float flBombRadius = 500;
+ if ( g_pMapInfo )
+ flBombRadius = g_pMapInfo->m_flBombRadius;
+
+ // Output to the bomb target ent
+ CBaseEntity *pTarget = NULL;
+ variant_t emptyVariant;
+ while ((pTarget = gEntList.FindEntityByClassname( pTarget, "func_bomb_target" )) != NULL)
+ {
+ //Adrian - But only to the one we want!
+ if ( pTarget->entindex() != m_iBombSiteIndex )
+ continue;
+
+ pTarget->AcceptInput( "BombExplode", this, this, emptyVariant, 0 );
+ break;
+ }
+
+ // Pull out of the wall a bit
+ if ( pTrace->fraction != 1.0 )
+ {
+ SetAbsOrigin( pTrace->endpos + (pTrace->plane.normal * 0.6) );
+ }
+
+ {
+ Vector pos = GetAbsOrigin() + Vector( 0,0,8 );
+
+ // add an explosion TE so it affects clientside physics
+ CPASFilter filter( pos );
+ te->Explosion( filter, 0.0,
+ &pos,
+ g_sModelIndexFireball,
+ 50.0,
+ 25,
+ TE_EXPLFLAG_NONE,
+ flBombRadius * 3.5,
+ 200 );
+ }
+
+ // Sound! for everyone
+ CBroadcastRecipientFilter filter;
+ EmitSound( filter, entindex(), "c4.explode" );
+
+
+ // Decal!
+ UTIL_DecalTrace( pTrace, "Scorch" );
+
+
+ // Shake!
+ UTIL_ScreenShake( pTrace->endpos, 25.0, 150.0, 1.0, 3000, SHAKE_START );
+
+
+ SetOwnerEntity( NULL ); // can't traceline attack owner if this is set
+
+ CSGameRules()->RadiusDamage(
+ CTakeDamageInfo( this, GetOwnerEntity(), flBombRadius, bitsDamageType ),
+ GetAbsOrigin(),
+ flBombRadius * 3.5, //Matt - don't ask me, this is how CS does it.
+ CLASS_NONE,
+ true ); // IGNORE THE WORLD!!
+
+ // send director message, that something important happed here
+ /*
+ MESSAGE_BEGIN( MSG_SPEC, SVC_DIRECTOR );
+ WRITE_BYTE ( 9 ); // command length in bytes
+ WRITE_BYTE ( DRC_CMD_EVENT ); // bomb explode
+ WRITE_SHORT( ENTINDEX(this->edict()) ); // index number of primary entity
+ WRITE_SHORT( 0 ); // index number of secondary entity
+ WRITE_LONG( 15 | DRC_FLAG_FINAL ); // eventflags (priority and flags)
+ MESSAGE_END();
+ */
+ }
+
+
+ // For CTs to defuse the c4
+ void CPlantedC4::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+ {
+ //Can't defuse if its already defused or if it has blown up
+ if( !m_bBombTicking )
+ {
+ SetUse( NULL );
+ return;
+ }
+
+ CCSPlayer *player = dynamic_cast< CCSPlayer* >( pActivator );
+
+ if ( !player || player->GetTeamNumber() != TEAM_CT )
+ return;
+
+ if ( m_bStartDefuse )
+ {
+ if ( player != m_pBombDefuser )
+ {
+ if ( player->m_iNextTimeCheck < gpGlobals->curtime )
+ {
+ ClientPrint( player, HUD_PRINTCENTER, "#Bomb_Already_Being_Defused" );
+ player->m_iNextTimeCheck = gpGlobals->curtime + 1;
+ }
+ return;
+ }
+
+ m_flNextDefuse = gpGlobals->curtime + 0.5;
+ }
+ else
+ {
+ // freeze the player in place while defusing
+
+ IGameEvent * event = gameeventmanager->CreateEvent("bomb_begindefuse" );
+ if( event )
+ {
+ event->SetInt( "userid", player->GetUserID() );
+ if ( player->HasDefuser() )
+ {
+ event->SetInt( "haskit", 1 );
+ // TODO show messages on clients on event
+ ClientPrint( player, HUD_PRINTCENTER, "#Defusing_Bomb_With_Defuse_Kit" );
+ }
+ else
+ {
+ event->SetInt( "haskit", 0 );
+ // TODO show messages on clients on event
+ ClientPrint( player, HUD_PRINTCENTER, "#Defusing_Bomb_Without_Defuse_Kit" );
+ }
+ event->SetInt( "priority", 8 );
+ gameeventmanager->FireEvent( event );
+ }
+
+ Vector soundPosition = player->GetAbsOrigin() + Vector( 0, 0, 5 );
+ CPASAttenuationFilter filter( soundPosition );
+
+ EmitSound( filter, entindex(), "c4.disarmstart" );
+
+ m_flDefuseLength = player->HasDefuser() ? 5 : 10;
+
+
+ m_flNextDefuse = gpGlobals->curtime + 0.5;
+ m_pBombDefuser = player;
+ m_bStartDefuse = TRUE;
+ player->m_bIsDefusing = true;
+
+ m_flDefuseCountDown = gpGlobals->curtime + m_flDefuseLength;
+
+ //start the progress bar
+ player->SetProgressBarTime( m_flDefuseLength );
+
+
+ player->OnStartedDefuse();
+ }
+ }
+
+
+#endif
+
+
+
+// -------------------------------------------------------------------------------- //
+// Tables.
+// -------------------------------------------------------------------------------- //
+
+IMPLEMENT_NETWORKCLASS_ALIASED( C4, DT_WeaponC4 )
+
+BEGIN_NETWORK_TABLE( CC4, DT_WeaponC4 )
+ #ifdef CLIENT_DLL
+ RecvPropBool( RECVINFO( m_bStartedArming ) ),
+ RecvPropBool( RECVINFO( m_bBombPlacedAnimation ) ),
+ RecvPropFloat( RECVINFO( m_fArmedTime ) )
+ #else
+ SendPropBool( SENDINFO( m_bStartedArming ) ),
+ SendPropBool( SENDINFO( m_bBombPlacedAnimation ) ),
+ SendPropFloat( SENDINFO( m_fArmedTime ), 0, SPROP_NOSCALE )
+ #endif
+END_NETWORK_TABLE()
+
+#if defined CLIENT_DLL
+BEGIN_PREDICTION_DATA( CC4 )
+ DEFINE_PRED_FIELD( m_bStartedArming, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bBombPlacedAnimation, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_fArmedTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE )
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_c4, CC4 );
+PRECACHE_WEAPON_REGISTER( weapon_c4 );
+
+
+
+// -------------------------------------------------------------------------------- //
+// Globals.
+// -------------------------------------------------------------------------------- //
+
+CUtlVector< CC4* > g_C4s;
+
+
+
+// -------------------------------------------------------------------------------- //
+// CC4 implementation.
+// -------------------------------------------------------------------------------- //
+
+CC4::CC4()
+{
+ g_C4s.AddToTail( this );
+ m_bDroppedFromDeath = false;
+
+#if defined( CLIENT_DLL )
+ m_szScreenText[0] = '\0';
+#endif
+
+}
+
+
+CC4::~CC4()
+{
+ g_C4s.FindAndRemove( this );
+}
+
+void CC4::Spawn()
+{
+ BaseClass::Spawn();
+
+ //Don't allow players to shoot the C4 around
+ SetCollisionGroup( COLLISION_GROUP_DEBRIS );
+
+ //Don't be damaged / moved by explosions
+ m_takedamage = DAMAGE_NO;
+
+ m_bBombPlanted = false;
+}
+
+void CC4::ItemPostFrame()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ // Disable all the firing code.. the C4 grenade is all custom.
+ if ( pPlayer->m_nButtons & IN_ATTACK )
+ {
+ PrimaryAttack();
+ }
+ else
+ {
+ WeaponIdle();
+ }
+}
+
+#if defined( CLIENT_DLL )
+
+ bool CC4::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
+ {
+ if( event == 7001 )
+ {
+ //set the screen text to the string in 'options'
+ Q_strncpy( m_szScreenText, options, 16 );
+
+ return true;
+ }
+ return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options );
+ }
+
+ char *CC4::GetScreenText( void )
+ {
+ if( m_bStartedArming )
+ return m_szScreenText;
+ else
+ return "";
+ }
+
+#endif //CLIENT_DLL
+
+#ifdef GAME_DLL
+
+
+ unsigned int CC4::PhysicsSolidMaskForEntity( void ) const
+ {
+ return BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_PLAYERCLIP;
+ }
+
+ void CC4::Precache()
+ {
+ PrecacheVGuiScreen( "c4_view_panel" );
+
+ PrecacheScriptSound( "c4.disarmfinish" );
+ PrecacheScriptSound( "c4.explode" );
+ PrecacheScriptSound( "c4.disarmstart" );
+ PrecacheScriptSound( "c4.plant" );
+ PrecacheScriptSound( "C4.PlantSound" );
+
+ BaseClass::Precache();
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Gets info about the control panels
+ //-----------------------------------------------------------------------------
+ void CC4::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
+ {
+ pPanelName = "c4_view_panel";
+ }
+
+ bool CC4::Holster( CBaseCombatWeapon *pSwitchingTo )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( pPlayer )
+ pPlayer->SetProgressBarTime( 0 );
+
+ if ( m_bStartedArming )
+ {
+ AbortBombPlant();
+ }
+
+ return BaseClass::Holster( pSwitchingTo );
+ }
+
+
+ bool CC4::ShouldRemoveOnRoundRestart()
+ {
+ // Doesn't matter if we have an owner or not.. always remove the C4 when the round restarts.
+ // The gamerules will give another C4 to some lucky player.
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( pPlayer && pPlayer->GetActiveWeapon() == this )
+ engine->ClientCommand( pPlayer->edict(), "lastinv reset\n" );
+ return true;
+ }
+
+#endif
+
+
+void CC4::PrimaryAttack()
+{
+ bool bArmingTimeSatisfied = false;
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ int onGround = FBitSet( pPlayer->GetFlags(), FL_ONGROUND );
+ CBaseEntity *groundEntity = (onGround) ? pPlayer->GetGroundEntity() : NULL;
+ if ( groundEntity )
+ {
+ // Don't let us stand on players, breakables, or pushaway physics objects to plant
+ if ( groundEntity->IsPlayer() ||
+ IsPushableEntity( groundEntity ) ||
+#ifndef CLIENT_DLL
+ IsBreakableEntity( groundEntity ) ||
+#endif // !CLIENT_DLL
+ IsPushAwayEntity( groundEntity ) )
+ {
+ onGround = false;
+ }
+ }
+
+ if( m_bStartedArming == false && m_bBombPlanted == false )
+ {
+ if( pPlayer->m_bInBombZone && onGround )
+ {
+ m_bStartedArming = true;
+ m_fArmedTime = gpGlobals->curtime + WEAPON_C4_ARM_TIME;
+ m_bBombPlacedAnimation = false;
+
+
+#if !defined( CLIENT_DLL )
+ // init the beep flags
+ int i;
+ for( i=0;i<NUM_BEEPS;i++ )
+ m_bPlayedArmingBeeps[i] = false;
+
+ // freeze the player in place while planting
+
+ // player "arming bomb" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ pPlayer->SetNextAttack( gpGlobals->curtime );
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_beginplant" );
+ if( event )
+ {
+ event->SetInt("userid", pPlayer->GetUserID() );
+ event->SetInt("site", pPlayer->m_iBombSiteIndex );
+ event->SetInt( "priority", 8 );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+
+ FX_PlantBomb( pPlayer->entindex(), pPlayer->Weapon_ShootPosition(), PLANTBOMB_PLANT );
+ }
+ else
+ {
+ if ( !pPlayer->m_bInBombZone )
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "#C4_Plant_At_Bomb_Spot");
+ }
+ else
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "#C4_Plant_Must_Be_On_Ground");
+ }
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
+ return;
+ }
+ }
+ else
+ {
+ if ( !onGround || !pPlayer->m_bInBombZone )
+ {
+ if( !pPlayer->m_bInBombZone )
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "#C4_Arming_Cancelled" );
+ }
+ else
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "#C4_Plant_Must_Be_On_Ground" );
+ }
+
+ AbortBombPlant();
+
+ if(m_bBombPlacedAnimation == true) //this means the placement animation is canceled
+ {
+ SendWeaponAnim( ACT_VM_DRAW );
+ }
+ else
+ {
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+
+ return;
+ }
+ else
+ {
+#ifndef CLIENT_DLL
+ PlayArmingBeeps();
+#endif
+
+ if( gpGlobals->curtime >= m_fArmedTime ) //the c4 is ready to be armed
+ {
+ //check to make sure the player is still in the bomb target area
+ bArmingTimeSatisfied = true;
+ }
+ else if( ( gpGlobals->curtime >= (m_fArmedTime - 0.75) ) && ( !m_bBombPlacedAnimation ) )
+ {
+ //call the c4 Placement animation
+ m_bBombPlacedAnimation = true;
+
+ SendWeaponAnim( ACT_VM_SECONDARYATTACK );
+
+#if !defined( CLIENT_DLL )
+ // player "place" animation
+ //pPlayer->SetAnimation( PLAYER_HOLDBOMB );
+#endif
+ }
+ }
+ }
+
+ if ( bArmingTimeSatisfied && m_bStartedArming )
+ {
+ m_bStartedArming = false;
+ m_fArmedTime = 0;
+
+ if( pPlayer->m_bInBombZone )
+ {
+#if !defined( CLIENT_DLL )
+ CPlantedC4 *pC4 = CPlantedC4::ShootSatchelCharge( pPlayer, pPlayer->GetAbsOrigin(), pPlayer->GetAbsAngles() );
+
+ if ( pC4 )
+ {
+ pC4->SetBombSiteIndex( pPlayer->m_iBombSiteIndex );
+
+ trace_t tr;
+ UTIL_TraceEntity( pC4, GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,-200), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
+ pC4->SetAbsOrigin( tr.endpos );
+
+ CBombTarget *pBombTarget = (CBombTarget*)UTIL_EntityByIndex( pPlayer->m_iBombSiteIndex );
+
+ if ( pBombTarget )
+ {
+ CBaseEntity *pAttachPoint = gEntList.FindEntityByName( NULL, pBombTarget->GetBombMountTarget() );
+
+ if ( pAttachPoint )
+ {
+ pC4->SetAbsOrigin( pAttachPoint->GetAbsOrigin() );
+ pC4->SetAbsAngles( pAttachPoint->GetAbsAngles() );
+ pC4->SetParent( pAttachPoint );
+ }
+
+ variant_t emptyVariant;
+ pBombTarget->AcceptInput( "BombPlanted", pC4, pC4, emptyVariant, 0 );
+ }
+
+ // [tj] If the bomb is planted by someone that picked it up after the
+ // original owner was killed, pass that along to the planted bomb
+ pC4->SetPlantedAfterPickup( m_bDroppedFromDeath );
+ }
+
+ //=============================================================================
+ // HPE_BEGIN
+ // [dwenger] Stats update for bomb planting
+ //=============================================================================
+
+ // Determine how elapsed time from start of round until the bomb was planted
+ float plantingTime = gpGlobals->curtime - CSGameRules()->GetRoundStartTime();
+
+ // Award achievement to bomb planter if time <= 25 seconds
+ if ((plantingTime > 0.0f) && (plantingTime <= AchievementConsts::FastBombPlant_Time))
+ {
+ pPlayer->AwardAchievement(CSPlantBombWithin25Seconds);
+ }
+
+ CCS_GameStats.Event_BombPlanted( pPlayer );
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_planted" );
+ if( event )
+ {
+ event->SetInt("userid", pPlayer->GetUserID() );
+ event->SetInt("site", pPlayer->m_iBombSiteIndex );
+ event->SetInt("posx", pPlayer->GetAbsOrigin().x );
+ event->SetInt("posy", pPlayer->GetAbsOrigin().y );
+ event->SetInt( "priority", 8 );
+ gameeventmanager->FireEvent( event );
+ }
+
+ // Fire a beep event also so the bots have a chance to hear the bomb
+ event = gameeventmanager->CreateEvent( "bomb_beep" );
+
+ if ( event )
+ {
+ event->SetInt( "entindex", entindex() );
+ gameeventmanager->FireEvent( event );
+ }
+
+ pPlayer->SetProgressBarTime( 0 );
+
+ CSGameRules()->m_bBombDropped = false;
+ CSGameRules()->m_bBombPlanted = true;
+
+ // Play the plant sound.
+ Vector plantPosition = pPlayer->GetAbsOrigin() + Vector( 0, 0, 5 );
+ CPASAttenuationFilter filter( plantPosition );
+ EmitSound( filter, entindex(), "c4.plant" );
+
+ // No more c4!
+ pPlayer->Weapon_Drop( this, NULL, NULL );
+ UTIL_Remove( this );
+#endif
+
+ //don't allow the planting to start over again next frame.
+ m_bBombPlanted = true;
+
+ return;
+ }
+ else
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "#C4_Activated_At_Bomb_Spot" );
+
+#if !defined( CLIENT_DLL )
+ //pPlayer->SetAnimation( PLAYER_HOLDBOMB );
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortplant" );
+ if( event )
+ {
+ event->SetInt("userid", pPlayer->GetUserID() );
+ event->SetInt("site", pPlayer->m_iBombSiteIndex );
+ event->SetInt( "priority", 8 );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
+ return;
+ }
+ }
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.3;
+ SetWeaponIdleTime( gpGlobals->curtime + SharedRandomFloat("C4IdleTime", 10, 15 ) );
+}
+
+void CC4::WeaponIdle()
+{
+ // if the player releases the attack button cancel the arming sequence
+ if ( m_bStartedArming )
+ {
+ AbortBombPlant();
+
+ CCSPlayer *pPlayer = GetPlayerOwner();
+
+ // TODO: make this use SendWeaponAnim and activities when the C4 has the activities hooked up.
+ if ( pPlayer )
+ {
+ SendWeaponAnim( ACT_VM_IDLE );
+ pPlayer->SetNextAttack( gpGlobals->curtime );
+ }
+
+ if(m_bBombPlacedAnimation == true) //this means the placement animation is canceled
+ SendWeaponAnim( ACT_VM_DRAW );
+ else
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+}
+
+void CC4::UpdateShieldState( void )
+{
+ //ADRIANTODO
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( pPlayer->HasShield() )
+ {
+ pPlayer->SetShieldDrawnState( false );
+
+ CBaseViewModel *pVM = pPlayer->GetViewModel( 1 );
+
+ if ( pVM )
+ {
+ pVM->AddEffects( EF_NODRAW );
+ }
+ //pPlayer->SetHitBoxSet( 3 );
+ }
+ else
+ BaseClass::UpdateShieldState();
+}
+
+
+int m_iBeepFrames[NUM_BEEPS] = { 27, 37, 45, 51, 57, 63, 67 };
+int iNumArmingAnimFrames = 83;
+
+void CC4::PlayArmingBeeps( void )
+{
+ float flStartTime = m_fArmedTime - WEAPON_C4_ARM_TIME;
+
+ float flProgress = ( gpGlobals->curtime - flStartTime ) / ( WEAPON_C4_ARM_TIME - 0.75 );
+
+ int currentFrame = (int)( (float)iNumArmingAnimFrames * flProgress );
+
+ int i;
+ for( i=0;i<NUM_BEEPS;i++ )
+ {
+ if( currentFrame <= m_iBeepFrames[i] )
+ {
+ break;
+ }
+ else if( !m_bPlayedArmingBeeps[i] )
+ {
+ m_bPlayedArmingBeeps[i] = true;
+
+ CCSPlayer *owner = GetPlayerOwner();
+ Vector soundPosition = owner->GetAbsOrigin() + Vector( 0, 0, 5 );
+ CPASAttenuationFilter filter( soundPosition );
+
+ filter.RemoveRecipient( owner );
+
+ // remove anyone that is first person spec'ing the planter
+ int i;
+ CBasePlayer *pPlayer;
+ for( i=1;i<=gpGlobals->maxClients;i++ )
+ {
+ pPlayer = UTIL_PlayerByIndex( i );
+
+ if ( !pPlayer )
+ continue;
+
+ if( pPlayer->GetObserverMode() == OBS_MODE_IN_EYE && pPlayer->GetObserverTarget() == GetOwner() )
+ {
+ filter.RemoveRecipient( pPlayer );
+ }
+ }
+
+ EmitSound(filter, entindex(), "c4.click");
+
+ break;
+ }
+ }
+}
+
+float CC4::GetMaxSpeed() const
+{
+ if ( m_bStartedArming )
+ return CS_PLAYER_SPEED_STOPPED;
+ else
+ return BaseClass::GetMaxSpeed();
+}
+
+
+void CC4::OnPickedUp( CBaseCombatCharacter *pNewOwner )
+{
+ BaseClass::OnPickedUp( pNewOwner );
+
+#if !defined( CLIENT_DLL )
+ CCSPlayer *pPlayer = dynamic_cast<CCSPlayer *>( pNewOwner );
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_pickup" );
+ if ( event )
+ {
+ event->SetInt( "userid", pPlayer->GetUserID() );
+ event->SetInt( "priority", 6 );
+ gameeventmanager->FireEvent( event );
+ }
+
+ if ( pPlayer->m_bShowHints && !(pPlayer->m_iDisplayHistoryBits & DHF_BOMB_RETRIEVED) )
+ {
+ pPlayer->m_iDisplayHistoryBits |= DHF_BOMB_RETRIEVED;
+ pPlayer->HintMessage( "#Hint_you_have_the_bomb", false );
+ }
+ else
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "#Got_bomb" );
+ }
+
+ pPlayer->SetBombPickupTime(gpGlobals->curtime);
+#endif
+}
+
+// HACK - Ask Mike Booth...
+#ifndef CLIENT_DLL
+ #include "cs_bot.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+void CC4::Drop( const Vector &vecVelocity )
+{
+#if !defined( CLIENT_DLL )
+ if ( !CSGameRules()->m_bBombPlanted ) // its not dropped if its planted
+ {
+ // tell the bots about the dropped bomb
+ TheCSBots()->SetLooseBomb( this );
+
+ CBasePlayer *pPlayer = dynamic_cast<CBasePlayer *>(GetOwnerEntity());
+ Assert( pPlayer );
+ if ( pPlayer )
+ {
+ IGameEvent * event = gameeventmanager->CreateEvent("bomb_dropped" );
+ if ( event )
+ {
+ event->SetInt( "userid", pPlayer->GetUserID() );
+ event->SetInt( "priority", 6 );
+ gameeventmanager->FireEvent( event );
+ }
+ }
+ }
+#endif
+
+ if ( m_bStartedArming )
+ AbortBombPlant(); // stop arming sequence
+
+ BaseClass::Drop( vecVelocity );
+}
+
+void CC4::AbortBombPlant()
+{
+ m_bStartedArming = false;
+
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+#if !defined( CLIENT_DLL )
+ m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
+
+ pPlayer->SetProgressBarTime( 0 );
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortplant" );
+ if( event )
+ {
+ event->SetInt("userid", pPlayer->GetUserID() );
+ event->SetInt("site", pPlayer->m_iBombSiteIndex );
+ event->SetInt( "priority", 8 );
+ gameeventmanager->FireEvent( event );
+ }
+
+#endif
+
+ FX_PlantBomb( pPlayer->entindex(), pPlayer->Weapon_ShootPosition(), PLANTBOMB_ABORT );
+} \ No newline at end of file