//========= 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 //============================================================================= // 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;iSetAnimation( 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;iGetAbsOrigin() + 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( 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(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 ); }