source-engine/game/shared/cstrike/weapon_csbase.cpp

1918 lines
50 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Laser Rifle & Shield combo
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "in_buttons.h"
#include "takedamageinfo.h"
#include "weapon_csbase.h"
#include "ammodef.h"
#include "cs_gamerules.h"
#define ALLOW_WEAPON_SPREAD_DISPLAY 0
#if defined( CLIENT_DLL )
#include "vgui/ISurface.h"
#include "vgui_controls/Controls.h"
#include "c_cs_player.h"
#include "hud_crosshair.h"
#include "c_te_effect_dispatch.h"
#include "c_te_legacytempents.h"
extern IVModelInfoClient* modelinfo;
#else
#include "cs_player.h"
#include "te_effect_dispatch.h"
#include "KeyValues.h"
#include "cs_ammodef.h"
extern IVModelInfo* modelinfo;
#endif
ConVar weapon_accuracy_model( "weapon_accuracy_model", "2", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY | FCVAR_ARCHIVE );
// ----------------------------------------------------------------------------- //
// Global functions.
// ----------------------------------------------------------------------------- //
struct WeaponAliasTranslationInfoStruct
{
const char* alias;
const char* translatedAlias;
};
static const WeaponAliasTranslationInfoStruct s_WeaponAliasTranslationInfo[] =
{
{ "cv47", "ak47" },
{ "defender", "galil" },
{ "krieg552", "sg552" },
{ "magnum", "awp" },
{ "d3au1", "g3sg1" },
{ "clarion", "famas" },
{ "bullpup", "aug" },
{ "krieg550", "sg550" },
{ "9x19mm", "glock" },
{ "km45", "usp" },
{ "228compact", "p228" },
{ "nighthawk", "deagle" },
{ "elites", "elite" },
{ "fn57", "fiveseven" },
{ "12gauge", "m3" },
{ "autoshotgun", "xm1014" },
{ "mp", "tmp" },
{ "smg", "mp5navy" },
{ "mp5", "mp5navy" },
{ "c90", "p90" },
{ "vest", "kevlar" },
{ "vesthelm", "assaultsuit" },
{ "smokegrenade", "sgren" },
{ "smokegrenade", "sgren" },
{ "nvgs", "nightvision" },
{ "", "" } // this needs to be last
};
struct WeaponAliasInfo
{
CSWeaponID id;
const char* alias;
};
WeaponAliasInfo s_weaponAliasInfo[] =
{
{ WEAPON_P228, "p228" },
{ WEAPON_GLOCK, "glock" },
{ WEAPON_SCOUT, "scout" },
{ WEAPON_XM1014, "xm1014" },
{ WEAPON_MAC10, "mac10" },
{ WEAPON_AUG, "aug" },
{ WEAPON_ELITE, "elite" },
{ WEAPON_FIVESEVEN, "fiveseven" },
{ WEAPON_UMP45, "ump45" },
{ WEAPON_SG550, "sg550" },
{ WEAPON_GALIL, "galil" },
{ WEAPON_FAMAS, "famas" },
{ WEAPON_USP, "usp" },
{ WEAPON_AWP, "awp" },
{ WEAPON_MP5NAVY, "mp5navy" },
{ WEAPON_M249, "m249" },
{ WEAPON_M3, "m3" },
{ WEAPON_M4A1, "m4a1" },
{ WEAPON_TMP, "tmp" },
{ WEAPON_G3SG1, "g3sg1" },
{ WEAPON_DEAGLE, "deagle" },
{ WEAPON_SG552, "sg552" },
{ WEAPON_AK47, "ak47" },
{ WEAPON_P90, "p90" },
{ WEAPON_KNIFE, "knife" },
{ WEAPON_C4, "c4" },
{ WEAPON_FLASHBANG, "flashbang" },
{ WEAPON_SMOKEGRENADE, "smokegrenade" },
{ WEAPON_SMOKEGRENADE, "sgren" },
{ WEAPON_HEGRENADE, "hegrenade" },
{ WEAPON_HEGRENADE, "hegren" },
// not sure any of these are needed
{ WEAPON_SHIELDGUN, "shield" },
{ WEAPON_SHIELDGUN, "shieldgun" },
{ WEAPON_KEVLAR, "kevlar" },
{ WEAPON_ASSAULTSUIT, "assaultsuit" },
{ WEAPON_NVG, "nightvision" },
{ WEAPON_NVG, "nvg" },
{ WEAPON_NONE, "none" },
};
bool IsAmmoType( int iAmmoType, const char *pAmmoName )
{
return GetAmmoDef()->Index( pAmmoName ) == iAmmoType;
}
//--------------------------------------------------------------------------------------------------------
//
// Given an alias, return the translated alias.
//
const char * GetTranslatedWeaponAlias( const char *szAlias )
{
for ( int i = 0; i < ARRAYSIZE(s_WeaponAliasTranslationInfo); ++i )
{
if ( Q_stricmp(s_WeaponAliasTranslationInfo[i].alias, szAlias) == 0 )
{
return s_WeaponAliasTranslationInfo[i].translatedAlias;
}
}
return szAlias;
}
//--------------------------------------------------------------------------------------------------------
//
// Given a translated alias, return the alias.
//
const char * GetWeaponAliasFromTranslated(const char *translatedAlias)
{
int i = 0;
const WeaponAliasTranslationInfoStruct *info = &(s_WeaponAliasTranslationInfo[i]);
while (info->alias[0] != 0)
{
if (Q_stricmp(translatedAlias, info->translatedAlias) == 0)
{
return info->alias;
}
info = &(s_WeaponAliasTranslationInfo[++i]);
}
return translatedAlias;
}
//--------------------------------------------------------------------------------------------------------
//
// Given an alias, return the associated weapon ID
//
CSWeaponID AliasToWeaponID( const char *szAlias )
{
if ( szAlias )
{
for ( int i=0; i < ARRAYSIZE(s_weaponAliasInfo); ++i)
{
if ( Q_stricmp( s_weaponAliasInfo[i].alias, szAlias) == 0 )
return s_weaponAliasInfo[i].id;
}
}
return WEAPON_NONE;
}
//--------------------------------------------------------------------------------------------------------
//
// Given a weapon ID, return its alias
//
const char *WeaponIDToAlias( int id )
{
for ( int i=0; i < ARRAYSIZE(s_weaponAliasInfo); ++i)
{
if ( s_weaponAliasInfo[i].id == id )
return s_weaponAliasInfo[i].alias;
}
return NULL;
}
//--------------------------------------------------------------------------------------------------------
//
// Return true if given weapon ID is a primary weapon
//
bool IsPrimaryWeapon( CSWeaponID id )
{
const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo( id );
if ( pWeaponInfo )
{
return pWeaponInfo->iSlot == WEAPON_SLOT_RIFLE;
}
return false;
}
//--------------------------------------------------------------------------------------------------------
//
// Return true if given weapon ID is a secondary weapon
//
bool IsSecondaryWeapon( CSWeaponID id )
{
const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo( id );
if ( pWeaponInfo )
return pWeaponInfo->iSlot == WEAPON_SLOT_PISTOL;
return false;
}
#ifdef CLIENT_DLL
int GetShellForAmmoType( const char *ammoname )
{
if ( !Q_strcmp( BULLET_PLAYER_762MM, ammoname ) )
return CS_SHELL_762NATO;
if ( !Q_strcmp( BULLET_PLAYER_556MM, ammoname ) )
return CS_SHELL_556;
if ( !Q_strcmp( BULLET_PLAYER_338MAG, ammoname ) )
return CS_SHELL_338MAG;
if ( !Q_strcmp( BULLET_PLAYER_BUCKSHOT, ammoname ) )
return CS_SHELL_12GAUGE;
if ( !Q_strcmp( BULLET_PLAYER_57MM, ammoname ) )
return CS_SHELL_57;
// default 9 mm
return CS_SHELL_9MM;
}
#endif
// ----------------------------------------------------------------------------- //
// CWeaponCSBase tables.
// ----------------------------------------------------------------------------- //
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCSBase, DT_WeaponCSBase )
BEGIN_NETWORK_TABLE( CWeaponCSBase, DT_WeaponCSBase )
#if !defined( CLIENT_DLL )
SendPropInt( SENDINFO( m_weaponMode ), 1, SPROP_UNSIGNED ),
SendPropFloat(SENDINFO(m_fAccuracyPenalty) ),
// world weapon models have no aminations
SendPropExclude( "DT_AnimTimeMustBeFirst", "m_flAnimTime" ),
SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
// SendPropExclude( "DT_LocalActiveWeaponData", "m_flTimeWeaponIdle" ),
#else
RecvPropInt( RECVINFO( m_weaponMode ) ),
RecvPropFloat( RECVINFO(m_fAccuracyPenalty)),
#endif
END_NETWORK_TABLE()
#if defined(CLIENT_DLL)
BEGIN_PREDICTION_DATA( CWeaponCSBase )
DEFINE_PRED_FIELD( m_flTimeWeaponIdle, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
DEFINE_PRED_FIELD( m_flNextPrimaryAttack, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
DEFINE_PRED_FIELD( m_flNextSecondaryAttack, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
DEFINE_PRED_FIELD( m_bDelayFire, FIELD_BOOLEAN, 0 ),
DEFINE_PRED_FIELD( m_flAccuracy, FIELD_FLOAT, 0 ),
DEFINE_PRED_FIELD( m_weaponMode, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD_TOL( m_fAccuracyPenalty, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, 0.00005f ),
END_PREDICTION_DATA()
#endif
LINK_ENTITY_TO_CLASS( weapon_cs_base, CWeaponCSBase );
#ifdef GAME_DLL
BEGIN_DATADESC( CWeaponCSBase )
//DEFINE_FUNCTION( DefaultTouch ),
DEFINE_THINKFUNC( FallThink )
END_DATADESC()
#endif
#if defined( CLIENT_DLL )
ConVar cl_crosshaircolor( "cl_crosshaircolor", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Set crosshair color: 0=green, 1=red, 2=blue, 3=yellow, 4=cyan, 5=custom" );
ConVar cl_dynamiccrosshair( "cl_dynamiccrosshair", "1", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Enables dynamic crosshair; 0=off, 1=normal behavior (based on actual weapon accuracy), 2=legacy simulated dynamic behavior, 3=legacy simulated static behavior" );
ConVar cl_crosshairspreadscale( "cl_crosshairspreadscale", "0.3", FCVAR_CLIENTDLL | FCVAR_ARCHIVE);
ConVar cl_scalecrosshair( "cl_scalecrosshair", "1", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Enable crosshair scaling (deprecated)" );
ConVar cl_crosshairscale( "cl_crosshairscale", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Crosshair scaling factor (deprecated)" );
ConVar cl_crosshairalpha( "cl_crosshairalpha", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
ConVar cl_crosshairusealpha( "cl_crosshairusealpha", "1", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
ConVar cl_crosshairsize( "cl_crosshairsize", "5", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
ConVar cl_crosshairthickness( "cl_crosshairthickness", "0.5", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
ConVar cl_crosshairdot( "cl_crosshairdot", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
ConVar cl_crosshaircolor_r( "cl_crosshaircolor_r", "50", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
ConVar cl_crosshaircolor_g( "cl_crosshaircolor_g", "250", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
ConVar cl_crosshaircolor_b( "cl_crosshaircolor_b", "50", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
#if ALLOW_WEAPON_SPREAD_DISPLAY
ConVar weapon_debug_spread_show( "weapon_debug_spread_show", "0", FCVAR_CLIENTDLL | FCVAR_DEVELOPMENTONLY, "Enables display of weapon accuracy; 1: show accuracy box, 2: show box with recoil offset" );
ConVar weapon_debug_spread_gap( "weapon_debug_spread_gap", "0.67", FCVAR_CLIENTDLL | FCVAR_DEVELOPMENTONLY );
#endif
// [paquin] make sure crosshair scales independent of frame rate
// unless legacy cvar is set
ConVar cl_legacy_crosshair_recoil( "cl_legacy_crosshair_recoil", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Enable legacy framerate dependent crosshair recoil");
// use old scaling behavior
ConVar cl_legacy_crosshair_scale( "cl_legacy_crosshair_scale", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Enable legacy crosshair scaling");
void DrawCrosshairRect( int x0, int y0, int x1, int y1, bool bAdditive )
{
if ( bAdditive )
{
vgui::surface()->DrawTexturedRect( x0, y0, x1, y1 );
}
else
{
// Alpha-blended crosshair
vgui::surface()->DrawFilledRect( x0, y0, x1, y1 );
}
}
#endif
// must be included after the above macros
#ifndef CLIENT_DLL
#include "cs_bot.h"
#endif
// ----------------------------------------------------------------------------- //
// CWeaponCSBase implementation.
// ----------------------------------------------------------------------------- //
CWeaponCSBase::CWeaponCSBase()
{
SetPredictionEligible( true );
m_bDelayFire = true;
m_nextPrevOwnerTouchTime = 0.0;
m_prevOwner = NULL;
AddSolidFlags( FSOLID_TRIGGER ); // Nothing collides with these but it gets touches.
#ifdef CLIENT_DLL
m_iCrosshairTextureID = 0;
#else
m_iDefaultExtraAmmo = 0;
#endif
m_fAccuracyPenalty = 0.0f;
m_weaponMode = Primary_Mode;
}
#ifndef CLIENT_DLL
bool CWeaponCSBase::KeyValue( const char *szKeyName, const char *szValue )
{
if ( !BaseClass::KeyValue( szKeyName, szValue ) )
{
if ( FStrEq( szKeyName, "ammo" ) )
{
int bullets = atoi( szValue );
if ( bullets < 0 )
return false;
m_iDefaultExtraAmmo = bullets;
return true;
}
}
return false;
}
#endif
bool CWeaponCSBase::IsPredicted() const
{
return true;
}
bool CWeaponCSBase::IsPistol() const
{
return GetCSWpnData().m_WeaponType == WEAPONTYPE_PISTOL;
}
bool CWeaponCSBase::IsFullAuto() const
{
return GetCSWpnData().m_bFullAuto;
}
bool CWeaponCSBase::PlayEmptySound()
{
//MIKETODO: certain weapons should override this to make it empty:
// C4
// Flashbang
// HE Grenade
// Smoke grenade
CPASAttenuationFilter filter( this );
filter.UsePredictionRules();
if ( IsPistol() )
{
EmitSound( filter, entindex(), "Default.ClipEmpty_Pistol" );
}
else
{
EmitSound( filter, entindex(), "Default.ClipEmpty_Rifle" );
}
return 0;
}
CCSPlayer* CWeaponCSBase::GetPlayerOwner() const
{
return dynamic_cast< CCSPlayer* >( GetOwner() );
}
//=============================================================================
// HPE_BEGIN:
//=============================================================================
//[dwenger] Accessors for the prior owner list
void CWeaponCSBase::AddToPriorOwnerList(CCSPlayer* pPlayer)
{
if ( !IsAPriorOwner( pPlayer ) )
{
// Add player to prior owner list
m_PriorOwners.AddToTail( pPlayer );
}
}
bool CWeaponCSBase::IsAPriorOwner(CCSPlayer* pPlayer)
{
return (m_PriorOwners.Find( pPlayer ) != -1);
}
//=============================================================================
// HPE_END
//=============================================================================
void CWeaponCSBase::SecondaryAttack( void )
{
#ifndef CLIENT_DLL
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return;
if ( pPlayer->HasShield() == false )
BaseClass::SecondaryAttack();
else
{
pPlayer->SetShieldDrawnState( !pPlayer->IsShieldDrawn() );
if ( pPlayer->IsShieldDrawn() )
SendWeaponAnim( ACT_SHIELD_UP );
else
SendWeaponAnim( ACT_SHIELD_DOWN );
m_flNextSecondaryAttack = gpGlobals->curtime + 0.4;
m_flNextPrimaryAttack = gpGlobals->curtime + 0.4;
}
#endif
}
bool CWeaponCSBase::SendWeaponAnim( int iActivity )
{
#ifdef CS_SHIELD_ENABLED
CCSPlayer *pPlayer = GetPlayerOwner();
if ( pPlayer && pPlayer->HasShield() )
{
CBaseViewModel *vm = pPlayer->GetViewModel( 1 );
if ( vm == NULL )
return false;
vm->SetWeaponModel( SHIELD_VIEW_MODEL, this );
int idealSequence = vm->SelectWeightedSequence( (Activity)iActivity );
if ( idealSequence >= 0 )
{
vm->SendViewModelMatchingSequence( idealSequence );
}
}
#endif
return BaseClass::SendWeaponAnim( iActivity );
}
void CWeaponCSBase::ItemPostFrame()
{
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return;
UpdateAccuracyPenalty();
UpdateShieldState();
if ((m_bInReload) && (pPlayer->m_flNextAttack <= gpGlobals->curtime))
{
// complete the reload.
int j = MIN( GetMaxClip1() - m_iClip1, pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) );
// Add them to the clip
m_iClip1 += j;
pPlayer->RemoveAmmo( j, m_iPrimaryAmmoType );
m_bInReload = false;
}
if ((pPlayer->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime))
{
if ( pPlayer->HasShield() )
CWeaponCSBase::SecondaryAttack();
else
SecondaryAttack();
pPlayer->m_nButtons &= ~IN_ATTACK2;
}
else if ((pPlayer->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime ))
{
if ( CSGameRules()->IsFreezePeriod() ) // Can't shoot during the freeze period
return;
if ( pPlayer->m_bIsDefusing )
return;
if ( pPlayer->State_Get() != STATE_ACTIVE )
return;
if ( pPlayer->IsShieldDrawn() )
return;
// we have to reset the FireOnEmpty flag before we can fire on an empty clip
if ( m_iClip1 == 0 && !m_bFireOnEmpty )
return;
// don't repeat fire if this is not a full auto weapon
if ( pPlayer->m_iShotsFired > 0 && !IsFullAuto() )
return;
#if !defined(CLIENT_DLL)
// allow the bots to react to the gunfire
if ( GetCSWpnData().m_WeaponType != WEAPONTYPE_GRENADE )
{
IGameEvent * event = gameeventmanager->CreateEvent( (HasAmmo()) ? "weapon_fire" : "weapon_fire_on_empty" );
if( event )
{
const char *weaponName = STRING( m_iClassname );
if ( strncmp( weaponName, "weapon_", 7 ) == 0 )
{
weaponName += 7;
}
event->SetInt( "userid", pPlayer->GetUserID() );
event->SetString( "weapon", weaponName );
gameeventmanager->FireEvent( event );
}
}
#endif
PrimaryAttack();
}
else if ( pPlayer->m_nButtons & IN_RELOAD && GetMaxClip1() != WEAPON_NOCLIP && !m_bInReload && m_flNextPrimaryAttack < gpGlobals->curtime)
{
// reload when reload is pressed, or if no buttons are down and weapon is empty.
//MIKETODO: add code for shields...
//if ( !FBitSet( m_iWeaponState, WPNSTATE_SHIELD_DRAWN ) )
if ( !pPlayer->IsShieldDrawn() )
{
if ( Reload() )
{
#ifndef CLIENT_DLL
// allow the bots to react to the reload
IGameEvent * event = gameeventmanager->CreateEvent( "weapon_reload" );
if( event )
{
event->SetInt( "userid", pPlayer->GetUserID() );
gameeventmanager->FireEvent( event );
}
#endif
}
}
}
else if ( !(pPlayer->m_nButtons & (IN_ATTACK|IN_ATTACK2) ) )
{
if ( weapon_accuracy_model.GetInt() == 2 )
{
// Fire button not down -- reset the shots fired count
if ( pPlayer->m_iShotsFired > 0 && ( !IsFullAuto() || m_iClip1 == 0 ) )
{
pPlayer->m_iShotsFired = 0;
}
}
// The following code prevents the player from tapping the firebutton repeatedly
// to simulate full auto and retaining the single shot accuracy of single fire
if ( m_bDelayFire )
{
m_bDelayFire = false;
if (pPlayer->m_iShotsFired > 15)
pPlayer->m_iShotsFired = 15;
m_flDecreaseShotsFired = gpGlobals->curtime + 0.4;
}
m_bFireOnEmpty = true;
// if it's a pistol then set the shots fired to 0 after the player releases a button
if ( IsPistol() )
{
pPlayer->m_iShotsFired = 0;
}
else
{
if ( (pPlayer->m_iShotsFired > 0) && (m_flDecreaseShotsFired < gpGlobals->curtime) )
{
m_flDecreaseShotsFired = gpGlobals->curtime + 0.0225;
pPlayer->m_iShotsFired--;
}
}
if ( (!IsUseable() && m_flNextPrimaryAttack < gpGlobals->curtime) )
{
// Intentionally blank -- used to switch weapons here
}
else
{
// weapon is useable. Reload if empty and weapon has waited as long as it has to after firing
if ( m_iClip1 == 0 && !(GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < gpGlobals->curtime )
{
Reload();
return;
}
}
WeaponIdle( );
return;
}
}
void CWeaponCSBase::ItemBusyFrame()
{
UpdateAccuracyPenalty();
BaseClass::ItemBusyFrame();
}
float CWeaponCSBase::GetInaccuracy() const
{
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return 0.0f;
const CCSWeaponInfo& weaponInfo = GetCSWpnData();
float fMaxSpeed = GetMaxSpeed();
if ( fMaxSpeed == 0.0f )
fMaxSpeed = GetCSWpnData().m_flMaxSpeed;
return m_fAccuracyPenalty +
RemapValClamped(pPlayer->GetAbsVelocity().Length2D(),
fMaxSpeed * CS_PLAYER_SPEED_DUCK_MODIFIER,
fMaxSpeed * 0.95f, // max out at 95% of run speed to avoid jitter near max speed
0.0f, weaponInfo.m_fInaccuracyMove[m_weaponMode]);
}
float CWeaponCSBase::GetSpread() const
{
if ( weapon_accuracy_model.GetInt() == 1 )
return 0.0f;
return GetCSWpnData().m_fSpread[m_weaponMode];
}
float CWeaponCSBase::GetMaxSpeed() const
{
// The weapon should have set this in its constructor.
float flRet = GetCSWpnData().m_flMaxSpeed;
Assert( flRet > 1 );
return flRet;
}
const CCSWeaponInfo &CWeaponCSBase::GetCSWpnData() const
{
const FileWeaponInfo_t *pWeaponInfo = &GetWpnData();
const CCSWeaponInfo *pCSInfo;
#ifdef _DEBUG
pCSInfo = dynamic_cast< const CCSWeaponInfo* >( pWeaponInfo );
Assert( pCSInfo );
#else
pCSInfo = static_cast< const CCSWeaponInfo* >( pWeaponInfo );
#endif
return *pCSInfo;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CWeaponCSBase::GetViewModel( int /*viewmodelindex = 0 -- this is ignored in the base class here*/ ) const
{
CCSPlayer *pOwner = GetPlayerOwner();
if ( pOwner == NULL )
return BaseClass::GetViewModel();
if ( pOwner->HasShield() && GetCSWpnData().m_bCanUseWithShield )
return GetCSWpnData().m_szShieldViewModel;
else
return GetWpnData().szViewModel;
return BaseClass::GetViewModel();
}
void CWeaponCSBase::Precache( void )
{
BaseClass::Precache();
#ifdef CS_SHIELD_ENABLED
if ( GetCSWpnData().m_bCanUseWithShield )
{
PrecacheModel( GetCSWpnData().m_szShieldViewModel );
}
#endif
PrecacheScriptSound( "Default.ClipEmpty_Pistol" );
PrecacheScriptSound( "Default.ClipEmpty_Rifle" );
PrecacheScriptSound( "Default.Zoom" );
}
Activity CWeaponCSBase::GetDeployActivity( void )
{
return ACT_VM_DRAW;
}
bool CWeaponCSBase::DefaultDeploy( char *szViewModel, char *szWeaponModel, int iActivity, char *szAnimExt )
{
// Msg( "deploy %s at %f\n", GetClassname(), gpGlobals->curtime );
CCSPlayer *pOwner = GetPlayerOwner();
if ( !pOwner )
{
return false;
}
pOwner->SetAnimationExtension( szAnimExt );
SetViewModel();
SendWeaponAnim( GetDeployActivity() );
pOwner->SetNextAttack( gpGlobals->curtime + SequenceDuration() );
m_flNextPrimaryAttack = gpGlobals->curtime;
m_flNextSecondaryAttack = gpGlobals->curtime;
SetWeaponVisible( true );
pOwner->SetShieldDrawnState( false );
if ( pOwner->HasShield() == true )
SetWeaponModelIndex( SHIELD_WORLD_MODEL);
else
SetWeaponModelIndex( szWeaponModel );
return true;
}
void CWeaponCSBase::UpdateShieldState( void )
{
//empty by default.
CCSPlayer *pOwner = GetPlayerOwner();
if ( pOwner == NULL )
return;
//ADRIANTODO
//Make the hitbox set switches here!!!
if ( pOwner->HasShield() == false )
{
pOwner->SetShieldDrawnState( false );
//pOwner->SetHitBoxSet( 0 );
return;
}
else
{
//pOwner->SetHitBoxSet( 1 );
}
}
void CWeaponCSBase::SetWeaponModelIndex( const char *pName )
{
m_iWorldModelIndex = modelinfo->GetModelIndex( pName );
}
bool CWeaponCSBase::CanBeSelected( void )
{
if ( !VisibleInWeaponSelection() )
return false;
return true;
}
bool CWeaponCSBase::CanDeploy( void )
{
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return false;
if ( pPlayer->HasShield() && GetCSWpnData().m_bCanUseWithShield == false )
return false;
return BaseClass::CanDeploy();
}
float CWeaponCSBase::CalculateNextAttackTime( float fCycleTime )
{
float fCurAttack = m_flNextPrimaryAttack;
float fDeltaAttack = gpGlobals->curtime - fCurAttack;
if ( fDeltaAttack < 0 || fDeltaAttack > gpGlobals->interval_per_tick )
{
fCurAttack = gpGlobals->curtime;
}
m_flNextSecondaryAttack = m_flNextPrimaryAttack = fCurAttack + fCycleTime;
return fCurAttack;
}
bool CWeaponCSBase::Holster( CBaseCombatWeapon *pSwitchingTo )
{
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return false;
if ( pPlayer )
pPlayer->SetFOV( pPlayer, 0 ); // reset the default FOV.
if ( pPlayer )
pPlayer->SetShieldDrawnState( false );
return BaseClass::Holster( pSwitchingTo );
}
bool CWeaponCSBase::Deploy()
{
CCSPlayer *pPlayer = GetPlayerOwner();
#ifdef CLIENT_DLL
m_iAlpha = 80;
if ( pPlayer )
{
pPlayer->m_iLastZoom = 0;
pPlayer->SetFOV( pPlayer, 0 );
}
#else
m_flDecreaseShotsFired = gpGlobals->curtime;
if ( pPlayer )
{
pPlayer->m_iShotsFired = 0;
pPlayer->m_bResumeZoom = false;
pPlayer->m_iLastZoom = 0;
pPlayer->SetFOV( pPlayer, 0 );
}
#endif
m_fAccuracyPenalty = 0.0f;
return BaseClass::Deploy();
}
#ifndef CLIENT_DLL
bool CWeaponCSBase::IsRemoveable()
{
if ( BaseClass::IsRemoveable() == true )
{
if ( m_nextPrevOwnerTouchTime > gpGlobals->curtime )
{
return false;
}
}
return BaseClass::IsRemoveable();
}
#endif
void CWeaponCSBase::Drop(const Vector &vecVelocity)
{
#ifdef CLIENT_DLL
BaseClass::Drop(vecVelocity);
return;
#else
// Once somebody drops a gun, it's fair game for removal when/if
// a game_weapon_manager does a cleanup on surplus weapons in the
// world.
SetRemoveable( true );
StopAnimation();
StopFollowingEntity( );
SetMoveType( MOVETYPE_FLYGRAVITY );
// clear follow stuff, setup for collision
SetGravity(1.0);
m_iState = WEAPON_NOT_CARRIED;
RemoveEffects( EF_NODRAW );
FallInit();
SetGroundEntity( NULL );
m_bInReload = false; // stop reloading
SetThink( NULL );
m_nextPrevOwnerTouchTime = gpGlobals->curtime + 0.8f;
m_prevOwner = GetPlayerOwner();
SetTouch(&CWeaponCSBase::DefaultTouch);
IPhysicsObject *pObj = VPhysicsGetObject();
if ( pObj != NULL )
{
AngularImpulse angImp( 200, 200, 200 );
pObj->AddVelocity( &vecVelocity, &angImp );
}
else
{
SetAbsVelocity( vecVelocity );
}
SetNextThink( gpGlobals->curtime );
SetOwnerEntity( NULL );
SetOwner( NULL );
#endif
}
// whats going on here is that if the player drops this weapon, they shouldn't take it back themselves
// for a little while. But if they throw it at someone else, the other player should get it immediately.
void CWeaponCSBase::DefaultTouch(CBaseEntity *pOther)
{
if ((m_prevOwner != NULL) && (pOther == m_prevOwner) && (gpGlobals->curtime < m_nextPrevOwnerTouchTime))
{
return;
}
BaseClass::DefaultTouch(pOther);
}
#if defined( CLIENT_DLL )
//-----------------------------------------------------------------------------
// Purpose: Draw the weapon's crosshair
//-----------------------------------------------------------------------------
void CWeaponCSBase::DrawCrosshair()
{
if ( !crosshair.GetInt() )
return;
CHudCrosshair *pCrosshair = GET_HUDELEMENT( CHudCrosshair );
if ( !pCrosshair )
return;
// clear crosshair
pCrosshair->SetCrosshair( 0, Color( 255, 255, 255, 255 ) );
CCSPlayer* pPlayer = (CCSPlayer*)C_BasePlayer::GetLocalPlayer();
if ( !pPlayer )
return;
// localplayer must be owner if not in Spec mode
Assert( (pPlayer == GetPlayerOwner()) || ( pPlayer->GetObserverMode()==OBS_MODE_IN_EYE) );
// Draw the targeting zone around the pCrosshair
if ( pPlayer->IsInVGuiInputMode() )
return;
int r, g, b;
switch ( cl_crosshaircolor.GetInt() )
{
case 0 : r = 50; g = 250; b = 50; break;
case 1 : r = 250; g = 50; b = 50; break;
case 2 : r = 50; g = 50; b = 250; break;
case 3 : r = 250; g = 250; b = 50; break;
case 4 : r = 50; g = 250; b = 250; break;
case 5 :
r = cl_crosshaircolor_r.GetInt();
g = cl_crosshaircolor_g.GetInt();
b = cl_crosshaircolor_b.GetInt();
break;
default : r = 50; g = 250; b = 50; break;
}
// if user is using nightvision, make the crosshair red.
if (pPlayer->m_bNightVisionOn)
{
r = 250;
g = 50;
b = 50;
}
int alpha = clamp( cl_crosshairalpha.GetInt(), 0, 255 );
vgui::surface()->DrawSetColor( r, g, b, alpha );
if ( !m_iCrosshairTextureID )
{
CHudTexture *pTexture = gHUD.GetIcon( "whiteAdditive" );
if ( pTexture )
{
m_iCrosshairTextureID = pTexture->textureId;
}
}
bool bAdditive = !cl_crosshairusealpha.GetBool() && !pPlayer->m_bNightVisionOn;
if ( bAdditive )
{
vgui::surface()->DrawSetColor( r, g, b, 200 );
vgui::surface()->DrawSetTexture( m_iCrosshairTextureID );
}
if ( pPlayer->HasShield() && pPlayer->IsShieldDrawn() == true )
return;
// no crosshair for sniper rifles
bool bCrosshairVisible = crosshair.GetBool() && GetCSWpnData().m_WeaponType != WEAPONTYPE_SNIPER_RIFLE;
if ( !bCrosshairVisible
#if ALLOW_WEAPON_SPREAD_DISPLAY
&& !weapon_debug_spread_show.GetBool()
#endif
)
return;
float fHalfFov = DEG2RAD(pPlayer->GetFOV()) * 0.5f;
int iCrosshairDistance;
int iBarSize = RoundFloatToInt(YRES(cl_crosshairsize.GetFloat()));
int iBarThickness = MAX( 1, RoundFloatToInt(YRES(cl_crosshairthickness.GetFloat())));
switch ( cl_dynamiccrosshair.GetInt() )
{
case 0:
default:
{
// static crosshair
float fSpread = (GetCSWpnData().m_fSpread[m_weaponMode] + GetCSWpnData().m_fInaccuracyStand[m_weaponMode]) * 320.0f / tanf(fHalfFov);
iCrosshairDistance = MAX( 0, RoundFloatToInt( YRES( fSpread * cl_crosshairspreadscale.GetFloat() ) ) );
}
break;
case 1:
{
float fSpread = (GetInaccuracy() + GetSpread()) * 320.0f / tanf(fHalfFov);
iCrosshairDistance = MAX( 0, RoundFloatToInt( YRES( fSpread * cl_crosshairspreadscale.GetFloat() ) ) );
}
break;
case 2:
case 3:
{
float fCrosshairDistanceGoal = GetCSWpnData().m_iCrosshairMinDistance; // The minimum distance the crosshair can achieve...
// legacy dynamic crosshair
if ( cl_dynamiccrosshair.GetInt() == 2 )
{
if ( !( pPlayer->GetFlags() & FL_ONGROUND ) )
fCrosshairDistanceGoal *= 2.0f;
else if ( pPlayer->GetFlags() & FL_DUCKING )
fCrosshairDistanceGoal *= 0.5f;
else if ( pPlayer->GetAbsVelocity().Length() > 100 )
fCrosshairDistanceGoal *= 1.5f;
}
// [jpaquin] changed to only bump up the crosshair size if the player is still shooting or is spectating someone else
int iDeltaDistance = GetCSWpnData().m_iCrosshairDeltaDistance; // Amount by which the crosshair expands when shooting (per frame)
if ( pPlayer->m_iShotsFired > m_iAmmoLastCheck && (pPlayer->m_nButtons & (IN_ATTACK|IN_ATTACK2)) )
fCrosshairDistanceGoal += iDeltaDistance;
m_iAmmoLastCheck = pPlayer->m_iShotsFired;
if ( m_flCrosshairDistance > fCrosshairDistanceGoal )
{
// [jpaquin] if we're not in legacy crosshair mode, use an exponential decay function so
// that the crosshair shrinks at the same rate regardless of the frame rate
if ( !cl_legacy_crosshair_recoil.GetBool() )
{
// .44888 on the next line makes the decay very close to what old method produces at 100fps.
m_flCrosshairDistance = Lerp(expf(-gpGlobals->frametime / 0.44888f), fCrosshairDistanceGoal, m_flCrosshairDistance);
}
else
{
m_flCrosshairDistance -= 0.1f + m_flCrosshairDistance * 0.013;
}
}
// clamp max crosshair expansion
m_flCrosshairDistance = clamp(m_flCrosshairDistance, fCrosshairDistanceGoal, 25.0f);
if ( cl_legacy_crosshair_scale.GetBool() )
{
//scale bar size to the resolution
int crosshairScale = cl_crosshairscale.GetInt();
if ( crosshairScale < 1 )
{
if ( ScreenHeight() <= 600 )
{
crosshairScale = 600;
}
else if ( ScreenHeight() <= 768 )
{
crosshairScale = 768;
}
else
{
crosshairScale = 1200;
}
}
float scale;
if( cl_scalecrosshair.GetBool() == false )
{
scale = 1.0f;
}
else
{
scale = (float)ScreenHeight() / (float)crosshairScale;
}
// calculate the inner distance of the crosshair in current screen units
iCrosshairDistance = (int)ceil( m_flCrosshairDistance * scale );
iBarSize = XRES(5); // + (iCrosshairDistance - fCrosshairDistanceGoal) / 2;
iBarSize = MAX( 1, (int)( (float)iBarSize * scale ) );
iBarThickness = MAX( 1, (int)floor( scale + 0.5f ) );
}
else
{
iCrosshairDistance = RoundFloatToInt(m_flCrosshairDistance * ScreenHeight() / 1200.0f);
}
}
break;
}
int iCenterX = ScreenWidth() / 2;
int iCenterY = ScreenHeight() / 2;
if ( bCrosshairVisible )
{
// draw horizontal crosshair lines
int iInnerLeft = iCenterX - iCrosshairDistance - iBarThickness / 2;
int iInnerRight = iInnerLeft + 2 * iCrosshairDistance + iBarThickness;
int iOuterLeft = iInnerLeft - iBarSize;
int iOuterRight = iInnerRight + iBarSize;
int y0 = iCenterY - iBarThickness / 2;
int y1 = y0 + iBarThickness;
DrawCrosshairRect( iOuterLeft, y0, iInnerLeft, y1, bAdditive );
DrawCrosshairRect( iInnerRight, y0, iOuterRight, y1, bAdditive );
// draw vertical crosshair lines
int iInnerTop = iCenterY - iCrosshairDistance - iBarThickness / 2;
int iInnerBottom = iInnerTop + 2 * iCrosshairDistance + iBarThickness;
int iOuterTop = iInnerTop - iBarSize;
int iOuterBottom = iInnerBottom + iBarSize;
int x0 = iCenterX - iBarThickness / 2;
int x1 = x0 + iBarThickness;
DrawCrosshairRect( x0, iOuterTop, x1, iInnerTop, bAdditive );
DrawCrosshairRect( x0, iInnerBottom, x1, iOuterBottom, bAdditive );
// draw dot
if ( cl_crosshairdot.GetBool() )
{
int x0 = iCenterX - iBarThickness / 2;
int x1 = x0 + iBarThickness;
int y0 = iCenterY - iBarThickness / 2;
int y1 = y0 + iBarThickness;
DrawCrosshairRect( x0, y0, x1, y1, bAdditive );
}
}
#if ALLOW_WEAPON_SPREAD_DISPLAY
// show accuracy brackets
if ( weapon_debug_spread_show.GetInt() == 1 || weapon_debug_spread_show.GetInt() == 2 )
{
if ( weapon_debug_spread_show.GetInt() == 2 )
{
const QAngle& punchAngles = pPlayer->GetPunchAngle();
Vector vecDirShooting;
AngleVectors( punchAngles, &vecDirShooting );
float iOffsetX = RoundFloatToInt(YRES(vecDirShooting.y * 320.0f / tanf(fHalfFov)));
float iOffsetY = RoundFloatToInt(YRES(vecDirShooting.z * 320.0f / tanf(fHalfFov)));
iCenterX -= iOffsetX;
iCenterY -= iOffsetY;
}
// colors
r = 250;
g = 250;
b = 50;
vgui::surface()->DrawSetColor( r, g, b, alpha );
int iBarThickness = MAX( 1, RoundFloatToInt(YRES(cl_crosshairthickness.GetFloat())));
float fSpreadDistance = (GetInaccuracy() + GetSpread()) * 320.0f / tanf(fHalfFov);
int iSpreadDistance = RoundFloatToInt(YRES(fSpreadDistance));
// draw vertical spread lines
int iInnerLeft = iCenterX - iSpreadDistance;
int iInnerRight = iCenterX + iSpreadDistance;
int iOuterLeft = iInnerLeft - iBarThickness;
int iOuterRight = iInnerRight + iBarThickness;
int iInnerTop = iCenterY - iSpreadDistance;
int iInnerBottom = iCenterY + iSpreadDistance;
int iOuterTop = iInnerTop - iBarThickness;
int iOuterBottom = iInnerBottom + iBarThickness;
int iGap = RoundFloatToInt(weapon_debug_spread_gap.GetFloat() * iSpreadDistance);
// draw horizontal lines
DrawCrosshairRect( iOuterLeft, iOuterTop, iCenterX - iGap, iInnerTop, bAdditive );
DrawCrosshairRect( iCenterX + iGap, iOuterTop, iOuterRight, iInnerTop, bAdditive );
DrawCrosshairRect( iOuterLeft, iInnerBottom, iCenterX - iGap, iOuterBottom, bAdditive );
DrawCrosshairRect( iCenterX + iGap, iInnerBottom, iOuterRight, iOuterBottom, bAdditive );
// draw vertical lines
DrawCrosshairRect( iOuterLeft, iOuterTop, iInnerLeft, iCenterY - iGap, bAdditive );
DrawCrosshairRect( iOuterLeft, iCenterY + iGap, iInnerLeft, iOuterBottom, bAdditive );
DrawCrosshairRect( iInnerRight, iOuterTop, iOuterRight, iCenterY - iGap, bAdditive );
DrawCrosshairRect( iInnerRight, iCenterY + iGap, iOuterRight, iOuterBottom, bAdditive );
}
#endif
}
void CWeaponCSBase::OnDataChanged( DataUpdateType_t type )
{
BaseClass::OnDataChanged( type );
if ( GetPredictable() && !ShouldPredict() )
ShutdownPredictable();
}
bool CWeaponCSBase::ShouldPredict()
{
if ( GetOwner() && GetOwner() == C_BasePlayer::GetLocalPlayer() )
return true;
return BaseClass::ShouldPredict();
}
void CWeaponCSBase::ProcessMuzzleFlashEvent()
{
// This is handled from the player's animstate, so it can match up to the beginning of the fire animation
}
bool CWeaponCSBase::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
{
if( event == 5001 )
{
C_CSPlayer *pPlayer = ToCSPlayer( GetOwner() );
if( pPlayer && pPlayer->GetFOV() < pPlayer->GetDefaultFOV() && HideViewModelWhenZoomed() )
return true;
CEffectData data;
data.m_fFlags = 0;
data.m_hEntity = pViewModel->GetRefEHandle();
data.m_nAttachmentIndex = 1;
data.m_flScale = GetCSWpnData().m_flMuzzleScale;
switch( GetMuzzleFlashStyle() )
{
case CS_MUZZLEFLASH_NONE:
break;
case CS_MUZZLEFLASH_X:
{
DispatchEffect( "CS_MuzzleFlash_X", data );
}
break;
case CS_MUZZLEFLASH_NORM:
default:
{
DispatchEffect( "CS_MuzzleFlash", data );
}
break;
}
return true;
}
return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options );
}
int CWeaponCSBase::GetMuzzleFlashStyle( void )
{
return GetCSWpnData().m_iMuzzleFlashStyle;
}
int CWeaponCSBase::GetMuzzleAttachment( void )
{
return LookupAttachment( "muzzle_flash" );
}
#else
//-----------------------------------------------------------------------------
// Purpose: Get the accuracy derived from weapon and player, and return it
//-----------------------------------------------------------------------------
const Vector& CWeaponCSBase::GetBulletSpread()
{
static Vector cone = VECTOR_CONE_8DEGREES;
return cone;
}
//-----------------------------------------------------------------------------
// Purpose: Match the anim speed to the weapon speed while crouching
//-----------------------------------------------------------------------------
float CWeaponCSBase::GetDefaultAnimSpeed()
{
return 1.0;
}
//-----------------------------------------------------------------------------
// Purpose: Draw the laser rifle effect
//-----------------------------------------------------------------------------
void CWeaponCSBase::BulletWasFired( const Vector &vecStart, const Vector &vecEnd )
{
}
bool CWeaponCSBase::ShouldRemoveOnRoundRestart()
{
if ( GetPlayerOwner() )
return false;
else
return true;
}
//=============================================================================
// HPE_BEGIN:
// [dwenger] Handle round restart processing for the weapon.
//=============================================================================
void CWeaponCSBase::OnRoundRestart()
{
// Clear out the list of prior owners
m_PriorOwners.RemoveAll();
}
//=============================================================================
// HPE_END
//=============================================================================
//=========================================================
// Materialize - make a CWeaponCSBase visible and tangible
//=========================================================
void CWeaponCSBase::Materialize()
{
if ( IsEffectActive( EF_NODRAW ) )
{
// changing from invisible state to visible.
RemoveEffects( EF_NODRAW );
DoMuzzleFlash();
}
AddSolidFlags( FSOLID_TRIGGER );
//SetTouch( &CWeaponCSBase::DefaultTouch );
SetThink( NULL );
}
//=========================================================
// AttemptToMaterialize - the item is trying to rematerialize,
// should it do so now or wait longer?
//=========================================================
void CWeaponCSBase::AttemptToMaterialize()
{
float time = g_pGameRules->FlWeaponTryRespawn( this );
if ( time == 0 )
{
Materialize();
return;
}
SetNextThink( gpGlobals->curtime + time );
}
//=========================================================
// CheckRespawn - a player is taking this weapon, should
// it respawn?
//=========================================================
void CWeaponCSBase::CheckRespawn()
{
//GOOSEMAN : Do not respawn weapons!
return;
}
//=========================================================
// Respawn- this item is already in the world, but it is
// invisible and intangible. Make it visible and tangible.
//=========================================================
CBaseEntity* CWeaponCSBase::Respawn()
{
// make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code
// will decide when to make the weapon visible and touchable.
CBaseEntity *pNewWeapon = CBaseEntity::Create( GetClassname(), g_pGameRules->VecWeaponRespawnSpot( this ), GetAbsAngles(), GetOwner() );
if ( pNewWeapon )
{
pNewWeapon->AddEffects( EF_NODRAW );// invisible for now
pNewWeapon->SetTouch( NULL );// no touch
pNewWeapon->SetThink( &CWeaponCSBase::AttemptToMaterialize );
UTIL_DropToFloor( this, MASK_SOLID );
// not a typo! We want to know when the weapon the player just picked up should respawn! This new entity we created is the replacement,
// but when it should respawn is based on conditions belonging to the weapon that was taken.
pNewWeapon->SetNextThink( gpGlobals->curtime + g_pGameRules->FlWeaponRespawnTime( this ) );
}
else
{
Msg( "Respawn failed to create %s!\n", GetClassname() );
}
return pNewWeapon;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponCSBase::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
CBasePlayer *pPlayer = ToBasePlayer( pActivator );
if ( pPlayer )
{
m_OnPlayerUse.FireOutput( pActivator, pCaller );
}
}
bool CWeaponCSBase::Reload()
{
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return false;
pPlayer->m_iShotsFired = 0;
bool retval = BaseClass::Reload();
return retval;
}
void CWeaponCSBase::Spawn()
{
BaseClass::Spawn();
// Override the bloat that our base class sets as it's a little bit bigger than we want.
// If it's too big, you drop a weapon and its box is so big that you're still touching it
// when it falls and you pick it up again right away.
CollisionProp()->UseTriggerBounds( true, 30 );
// Set this here to allow players to shoot dropped weapons
SetCollisionGroup( COLLISION_GROUP_WEAPON );
SetExtraAmmoCount( m_iDefaultExtraAmmo ); //Start with no additional ammo
m_nextPrevOwnerTouchTime = 0.0;
m_prevOwner = NULL;
//=============================================================================
// HPE_BEGIN:
//=============================================================================
// [tj] initialize donor of this weapon
m_donor = NULL;
m_donated = false;
m_weaponMode = Primary_Mode;
//=============================================================================
// HPE_END
//=============================================================================
}
bool CWeaponCSBase::DefaultReload( int iClipSize1, int iClipSize2, int iActivity )
{
if ( BaseClass::DefaultReload( iClipSize1, iClipSize2, iActivity ) )
{
SendReloadEvents();
return true;
}
else
{
return false;
}
}
void CWeaponCSBase::SendReloadEvents()
{
CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( GetOwner() );
if ( !pPlayer )
return;
// Send a message to any clients that have this entity to play the reload.
CPASFilter filter( pPlayer->GetAbsOrigin() );
filter.RemoveRecipient( pPlayer );
UserMessageBegin( filter, "ReloadEffect" );
WRITE_SHORT( pPlayer->entindex() );
MessageEnd();
// Make the player play his reload animation.
pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD );
}
#endif
bool CWeaponCSBase::DefaultPistolReload()
{
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return false;
if (pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) <= 0)
return true;
if ( !DefaultReload( GetCSWpnData().iDefaultClip1, 0, ACT_VM_RELOAD ) )
return false;
pPlayer->m_iShotsFired = 0;
return true;
}
bool CWeaponCSBase::IsUseable()
{
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return false;
if ( Clip1() <= 0 )
{
if ( pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) <= 0 && GetMaxClip1() != -1 )
{
// clip is empty (or nonexistant) and the player has no more ammo of this type.
return false;
}
}
return true;
}
#if defined( CLIENT_DLL )
float g_lateralBob = 0;
float g_verticalBob = 0;
static ConVar cl_bobcycle( "cl_bobcycle","0.8", FCVAR_CHEAT );
static ConVar cl_bob( "cl_bob","0.002", FCVAR_CHEAT );
static ConVar cl_bobup( "cl_bobup","0.5", FCVAR_CHEAT );
//-----------------------------------------------------------------------------
// Purpose:
// Output : float
//-----------------------------------------------------------------------------
float CWeaponCSBase::CalcViewmodelBob( void )
{
static float bobtime;
static float lastbobtime;
static float lastspeed;
float cycle;
CBasePlayer *player = ToBasePlayer( GetOwner() );
//Assert( player );
//NOTENOTE: For now, let this cycle continue when in the air, because it snaps badly without it
if ( ( !gpGlobals->frametime ) ||
( player == NULL ) ||
( cl_bobcycle.GetFloat() <= 0.0f ) ||
( cl_bobup.GetFloat() <= 0.0f ) ||
( cl_bobup.GetFloat() >= 1.0f ) )
{
//NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!)
return 0.0f;// just use old value
}
//Find the speed of the player
float speed = player->GetLocalVelocity().Length2D();
float flmaxSpeedDelta = MAX( 0, (gpGlobals->curtime - lastbobtime) * 320.0f );
// don't allow too big speed changes
speed = clamp( speed, lastspeed-flmaxSpeedDelta, lastspeed+flmaxSpeedDelta );
speed = clamp( speed, -320, 320 );
lastspeed = speed;
//FIXME: This maximum speed value must come from the server.
// MaxSpeed() is not sufficient for dealing with sprinting - jdw
float bob_offset = RemapVal( speed, 0, 320, 0.0f, 1.0f );
bobtime += ( gpGlobals->curtime - lastbobtime ) * bob_offset;
lastbobtime = gpGlobals->curtime;
//Calculate the vertical bob
cycle = bobtime - (int)(bobtime/cl_bobcycle.GetFloat())*cl_bobcycle.GetFloat();
cycle /= cl_bobcycle.GetFloat();
if ( cycle < cl_bobup.GetFloat() )
{
cycle = M_PI * cycle / cl_bobup.GetFloat();
}
else
{
cycle = M_PI + M_PI*(cycle-cl_bobup.GetFloat())/(1.0 - cl_bobup.GetFloat());
}
g_verticalBob = speed*0.005f;
g_verticalBob = g_verticalBob*0.3 + g_verticalBob*0.7*sin(cycle);
g_verticalBob = clamp( g_verticalBob, -7.0f, 4.0f );
//Calculate the lateral bob
cycle = bobtime - (int)(bobtime/cl_bobcycle.GetFloat()*2)*cl_bobcycle.GetFloat()*2;
cycle /= cl_bobcycle.GetFloat()*2;
if ( cycle < cl_bobup.GetFloat() )
{
cycle = M_PI * cycle / cl_bobup.GetFloat();
}
else
{
cycle = M_PI + M_PI*(cycle-cl_bobup.GetFloat())/(1.0 - cl_bobup.GetFloat());
}
g_lateralBob = speed*0.005f;
g_lateralBob = g_lateralBob*0.3 + g_lateralBob*0.7*sin(cycle);
g_lateralBob = clamp( g_lateralBob, -7.0f, 4.0f );
//NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!)
return 0.0f;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &origin -
// &angles -
// viewmodelindex -
//-----------------------------------------------------------------------------
void CWeaponCSBase::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles )
{
Vector forward, right;
AngleVectors( angles, &forward, &right, NULL );
CalcViewmodelBob();
// Apply bob, but scaled down to 40%
VectorMA( origin, g_verticalBob * 0.4f, forward, origin );
// Z bob a bit more
origin[2] += g_verticalBob * 0.1f;
// bob the angles
angles[ ROLL ] += g_verticalBob * 0.5f;
angles[ PITCH ] -= g_verticalBob * 0.4f;
angles[ YAW ] -= g_lateralBob * 0.3f;
// VectorMA( origin, g_lateralBob * 0.2f, right, origin );
}
#else
void CWeaponCSBase::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles )
{
}
float CWeaponCSBase::CalcViewmodelBob( void )
{
return 0.0f;
}
#endif
#ifndef CLIENT_DLL
bool CWeaponCSBase::PhysicsSplash( const Vector &centerPoint, const Vector &normal, float rawSpeed, float scaledSpeed )
{
if ( rawSpeed > 20 )
{
float size = 4.0f;
if ( !IsPistol() )
size += 2.0f;
// adjust splash size based on speed
size += RemapValClamped( rawSpeed, 0, 400, 0, 3 );
CEffectData data;
data.m_vOrigin = centerPoint;
data.m_vNormal = normal;
data.m_flScale = random->RandomFloat( size, size + 1.0f );
if ( GetWaterType() & CONTENTS_SLIME )
{
data.m_fFlags |= FX_WATER_IN_SLIME;
}
DispatchEffect( "gunshotsplash", data );
return true;
}
return false;
}
#endif // !CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pPicker -
//-----------------------------------------------------------------------------
void CWeaponCSBase::OnPickedUp( CBaseCombatCharacter *pNewOwner )
{
#if !defined( CLIENT_DLL )
RemoveEffects( EF_ITEM_BLINK );
if( pNewOwner->IsPlayer() && pNewOwner->IsAlive() )
{
m_OnPlayerPickup.FireOutput(pNewOwner, this);
// Play the pickup sound for 1st-person observers
CRecipientFilter filter;
for ( int i=0; i<gpGlobals->maxClients; ++i )
{
CBasePlayer *player = UTIL_PlayerByIndex(i);
if ( player && !player->IsAlive() && player->GetObserverMode() == OBS_MODE_IN_EYE )
{
filter.AddRecipient( player );
}
}
if ( filter.GetRecipientCount() )
{
CBaseEntity::EmitSound( filter, pNewOwner->entindex(), "Player.PickupWeapon" );
}
// Robin: We don't want to delete weapons the player has picked up, so
// clear the name of the weapon. This prevents wildcards that are meant
// to find NPCs finding weapons dropped by the NPCs as well.
SetName( NULL_STRING );
}
// Someone picked me up, so make it so that I can't be removed.
SetRemoveable( false );
#endif
}
void CWeaponCSBase::UpdateAccuracyPenalty()
{
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return;
const CCSWeaponInfo& weaponInfo = GetCSWpnData();
float fNewPenalty = 0.0f;
// on ladder?
if ( pPlayer->GetMoveType() == MOVETYPE_LADDER )
{
fNewPenalty += weaponInfo.m_fInaccuracyStand[m_weaponMode] + weaponInfo.m_fInaccuracyLadder[m_weaponMode];
}
// in the air?
// else if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
// {
// fNewPenalty += weaponInfo.m_fInaccuracyStand[m_weaponMode] + weaponInfo.m_fInaccuracyJump[m_weaponMode];
// }
else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING) )
{
fNewPenalty += weaponInfo.m_fInaccuracyCrouch[m_weaponMode];
}
else
{
fNewPenalty += weaponInfo.m_fInaccuracyStand[m_weaponMode];
}
if ( m_bInReload )
{
fNewPenalty += weaponInfo.m_fInaccuracyReload;
}
if ( fNewPenalty > m_fAccuracyPenalty )
{
m_fAccuracyPenalty = fNewPenalty;
}
else
{
float fDecayFactor;
if ( pPlayer->GetMoveType() == MOVETYPE_LADDER )
{
fDecayFactor = logf(10.0f) / weaponInfo.m_fRecoveryTimeStand;
}
else if ( !FBitSet(pPlayer->GetFlags(), FL_ONGROUND) ) // in air
{
// enforce a large recovery speed penalty (300%) for players in the air; this helps to provide
// comparable in-air accuracy to the old weapon model
fDecayFactor = logf(10.0f) / (weaponInfo.m_fRecoveryTimeCrouch * 3.0f);
}
else if ( FBitSet(pPlayer->GetFlags(), FL_DUCKING) )
{
fDecayFactor = logf(10.0f) / weaponInfo.m_fRecoveryTimeCrouch;
}
else
{
fDecayFactor = logf(10.0f) / weaponInfo.m_fRecoveryTimeStand;
}
m_fAccuracyPenalty = Lerp(expf(TICK_INTERVAL * -fDecayFactor), fNewPenalty, (float)m_fAccuracyPenalty);
}
}
const float kJumpVelocity = sqrtf(2.0f * 800.0f * 57.0f); // see CCSGameMovement::CheckJumpButton()
void CWeaponCSBase::OnJump( float fImpulse )
{
m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyJump[m_weaponMode] * fImpulse / kJumpVelocity;
}
void CWeaponCSBase::OnLand( float fVelocity )
{
float fPenalty = GetCSWpnData().m_fInaccuracyLand[m_weaponMode] * fVelocity / kJumpVelocity;
m_fAccuracyPenalty += fPenalty;
/*
// this bit of code is only if we want to punch the player view on all landings
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return;
QAngle angle = pPlayer->GetPunchAngle();
float fVKick = RAD2DEG(asinf(fPenalty)) * 0.4f;
float fHKick = SharedRandomFloat("LandPunchAngleYaw", -1.0f, +1.0f) * fVKick * 0.1f;
angle.x += fVKick; // pitch
angle.y += fHKick; // yaw
pPlayer->SetPunchAngle( angle );
*/
}