hl2_src-leak-2017/src/game/shared/cstrike/weapon_c4.cpp

1353 lines
37 KiB
C++

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