diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/cstrike/weapon_c4.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/shared/cstrike/weapon_c4.cpp')
| -rw-r--r-- | game/shared/cstrike/weapon_c4.cpp | 1353 |
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 |