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 ); |
|
} |