You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1353 lines
37 KiB
1353 lines
37 KiB
//========= 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 );
|
|
}
|