You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3532 lines
104 KiB
3532 lines
104 KiB
//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
#include "cbase.h"
|
|
#include "in_buttons.h"
|
|
#include "engine/IEngineSound.h"
|
|
#include "ammodef.h"
|
|
#include "SoundEmitterSystem/isoundemittersystembase.h"
|
|
#include "physics_saverestore.h"
|
|
#include "datacache/imdlcache.h"
|
|
#include "tier0/vprof.h"
|
|
#include "collisionutils.h"
|
|
#include "econ_entity.h"
|
|
#include "econ_item_view.h"
|
|
|
|
#if !defined( CLIENT_DLL )
|
|
|
|
// Game DLL Headers
|
|
#include "soundent.h"
|
|
#include "eventqueue.h"
|
|
#include "fmtstr.h"
|
|
#include "gameweaponmanager.h"
|
|
|
|
#else
|
|
|
|
#include "input.h"
|
|
#include "hltvreplaysystem.h"
|
|
#include "model_types.h"
|
|
|
|
#endif
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
// The minimum time a hud hint for a weapon should be on screen. If we switch away before
|
|
// this, then teh hud hint counter will be deremented so the hint will be shown again, as
|
|
// if it had never been seen. The total display time for a hud hint is specified in client
|
|
// script HudAnimations.txt (which I can't read here).
|
|
#define MIN_HUDHINT_DISPLAY_TIME 7.0f
|
|
|
|
#define HIDEWEAPON_THINK_CONTEXT "BaseCombatWeapon_HideThink"
|
|
|
|
extern bool UTIL_ItemCanBeTouchedByPlayer( CBaseEntity *pItem, CBasePlayer *pPlayer );
|
|
|
|
#if defined( CLIENT_DLL )
|
|
void RecvProxy_EffectFlagsWeaponWorldmodel( const CRecvProxyData *pData, void *pStruct, void *pOut );
|
|
extern void RecvProxy_IntToMoveParent( const CRecvProxyData *pData, void *pStruct, void *pOut );
|
|
void RecvProxy_WeaponWorldmodel( const CRecvProxyData *pData, void *pStruct, void *pOut );
|
|
void RecvProxy_WeaponWorldmodelCosmetics( const CRecvProxyData *pData, void *pStruct, void *pOut );
|
|
#endif
|
|
|
|
IMPLEMENT_NETWORKCLASS_ALIASED( BaseWeaponWorldModel, DT_BaseWeaponWorldModel )
|
|
LINK_ENTITY_TO_CLASS_ALIASED( weaponworldmodel, BaseWeaponWorldModel );
|
|
|
|
BEGIN_NETWORK_TABLE_NOBASE(CBaseWeaponWorldModel, DT_BaseWeaponWorldModel)
|
|
#if !defined( CLIENT_DLL )
|
|
SendPropModelIndex(SENDINFO(m_nModelIndex)),
|
|
SendPropInt (SENDINFO(m_nBody), ANIMATION_BODY_BITS ), // increased to 32 bits to support number of bits equal to number of bodygroups
|
|
SendPropInt (SENDINFO(m_fEffects), EF_MAX_BITS, SPROP_UNSIGNED),
|
|
SendPropEHandle (SENDINFO_NAME(m_hMoveParent, moveparent)),
|
|
SendPropEHandle (SENDINFO(m_hCombatWeaponParent)),
|
|
#else
|
|
RecvPropInt (RECVINFO(m_nModelIndex), 0, RecvProxy_WeaponWorldmodel),
|
|
RecvPropInt (RECVINFO(m_nBody)),
|
|
RecvPropInt (RECVINFO(m_fEffects), 0, RecvProxy_EffectFlagsWeaponWorldmodel),
|
|
RecvPropInt (RECVINFO_NAME(m_hNetworkMoveParent, moveparent), 0, RecvProxy_IntToMoveParent),
|
|
RecvPropEHandle (RECVINFO(m_hCombatWeaponParent), RecvProxy_WeaponWorldmodelCosmetics),
|
|
#endif
|
|
END_NETWORK_TABLE()
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
BEGIN_PREDICTION_DATA( CBaseWeaponWorldModel )
|
|
DEFINE_PRED_FIELD( m_nModelIndex, FIELD_SHORT, FTYPEDESC_INSENDTABLE | FTYPEDESC_MODELINDEX ),
|
|
DEFINE_PRED_FIELD( m_nBody, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
|
|
DEFINE_PRED_FIELD( m_fEffects, FIELD_INTEGER, FTYPEDESC_INSENDTABLE | FTYPEDESC_OVERRIDE ),
|
|
DEFINE_FIELD( m_hCombatWeaponParent, FIELD_EHANDLE ),
|
|
END_PREDICTION_DATA()
|
|
|
|
void RecvProxy_EffectFlagsWeaponWorldmodel( const CRecvProxyData *pData, void *pStruct, void *pOut )
|
|
{
|
|
CBaseWeaponWorldModel *pWeaponWorldModel = (CBaseWeaponWorldModel *) pStruct;
|
|
if ( pWeaponWorldModel )
|
|
{
|
|
if ( pWeaponWorldModel->GetEffects() != pData->m_Value.m_Int )
|
|
{
|
|
pWeaponWorldModel->SetEffects( pData->m_Value.m_Int );
|
|
}
|
|
}
|
|
}
|
|
|
|
void RecvProxy_WeaponWorldmodel( const CRecvProxyData *pData, void *pStruct, void *pOut )
|
|
{
|
|
CBaseWeaponWorldModel *model = (CBaseWeaponWorldModel *)pStruct;
|
|
if ( model )
|
|
{
|
|
int nOldModelIndex = model->GetModelIndex();
|
|
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
model->SetModelByIndex( pData->m_Value.m_Int );
|
|
|
|
if ( nOldModelIndex != model->GetModelIndex() )
|
|
model->ResetCachedBoneIndices();
|
|
}
|
|
}
|
|
|
|
void RecvProxy_WeaponWorldmodelCosmetics( const CRecvProxyData *pData, void *pStruct, void *pOut )
|
|
{
|
|
RecvProxy_IntToEHandle( pData, pStruct, pOut );
|
|
|
|
CBaseWeaponWorldModel *pWeaponWorldModel = (CBaseWeaponWorldModel *) pStruct;
|
|
if ( pWeaponWorldModel )
|
|
{
|
|
pWeaponWorldModel->ApplyCustomMaterialsAndStickers();
|
|
}
|
|
}
|
|
|
|
int CBaseWeaponWorldModel::DrawModel( int flags, const RenderableInstance_t &instance )
|
|
{
|
|
if ( (flags & STUDIO_RENDER) && ( IsEffectActive(EF_NODRAW) || !ShouldDraw() ) )
|
|
return 0;
|
|
|
|
return BaseClass::DrawModel( flags, instance );
|
|
}
|
|
|
|
void CBaseWeaponWorldModel::OnDataChanged( DataUpdateType_t type )
|
|
{
|
|
// make sure world model custom materials and stickers are up-to-date
|
|
CBaseCombatWeapon *pWeaponParent = m_hCombatWeaponParent->Get();
|
|
if ( pWeaponParent )
|
|
{
|
|
if ( IsVisible() && GetCustomMaterialCount() != pWeaponParent->GetCustomMaterialCount() )
|
|
{
|
|
ApplyCustomMaterialsAndStickers();
|
|
}
|
|
|
|
// extra sticker application check
|
|
if ( IsVisible() && ShouldDraw() && !m_bStickersApplied && pWeaponParent )
|
|
{
|
|
m_bStickersApplied = true;
|
|
pWeaponParent->ApplyThirdPersonStickers( this );
|
|
}
|
|
|
|
if ( !pWeaponParent->GetOwner() )
|
|
{
|
|
pWeaponParent->ApplyThirdPersonStickers( pWeaponParent );
|
|
}
|
|
}
|
|
|
|
if ( type == DATA_UPDATE_CREATED )
|
|
{
|
|
ResetCachedBoneIndices();
|
|
}
|
|
|
|
BaseClass::OnDataChanged( type );
|
|
|
|
ValidateParent();
|
|
|
|
SetAllowFastPath( false ); // so it can control exactly when to render
|
|
|
|
UpdateVisibility();
|
|
}
|
|
|
|
float *CBaseWeaponWorldModel::GetRenderClipPlane( void )
|
|
{
|
|
// world model weapons inherit their clip planes from their move parents when the parent is a player
|
|
if ( GetMoveParent() && GetMoveParent()->IsPlayer() )
|
|
{
|
|
return GetMoveParent()->GetRenderClipPlane();
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
bool CBaseWeaponWorldModel::SetupBones( matrix3x4a_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime )
|
|
{
|
|
if ( GetMoveParent() && GetMoveParent()->IsPlayer() )
|
|
{
|
|
|
|
if ( boneMask == BONE_USED_BY_ATTACHMENT )
|
|
{
|
|
// fixme: weapons set up more bones than necessary when asking for attachments. Particles request attachment positions
|
|
// often and cause computation down more bone chains than they actually need. There's perf to be gained here.
|
|
// For now, requests for attachments only are allowed through.
|
|
}
|
|
else
|
|
{
|
|
// This is a hacky special case. A better way to do this would be to add more granularity in content bone flags.
|
|
// CBaseWeaponWorldModels have a bunch of bones that drive the player's bones when the player sets up,
|
|
// BUT they are not necessary to compute when rendering the weapon itself.
|
|
|
|
// So the gross assumption being made here is that if we don't want an attachment (like for particle system
|
|
// attaching or sticker projection, etc) then we actually only care about vertex-weighted bones. And this
|
|
// saves a bunch of bone setup we'll never use, like on the weapons 'legs' bones, which never drive the
|
|
// mechanical parts of the gun.
|
|
|
|
boneMask = BONE_USED_BY_VERTEX_LOD0;
|
|
}
|
|
|
|
return BaseClass::SetupBones( pBoneToWorldOut, nMaxBones, boneMask, currentTime );
|
|
}
|
|
|
|
//AssertMsgOnce( false, "Attempted to SetupBones on a dropped weapon world model with no player parent!\n" );
|
|
return false;
|
|
}
|
|
|
|
#else
|
|
|
|
BEGIN_DATADESC( CBaseWeaponWorldModel )
|
|
END_DATADESC()
|
|
|
|
#endif
|
|
|
|
void CBaseWeaponWorldModel::ValidateParent( void )
|
|
{
|
|
CBaseCombatWeapon *pWeaponParent = m_hCombatWeaponParent->Get();
|
|
if ( pWeaponParent )
|
|
{
|
|
CBaseEntity *pIdealParent = pWeaponParent;
|
|
CBaseCombatCharacter *pWeaponParentOwner = pWeaponParent->GetOwner();
|
|
|
|
if ( pWeaponParentOwner && pWeaponParentOwner->IsPlayer() )
|
|
pIdealParent = pWeaponParentOwner;
|
|
|
|
if ( GetMoveParent() != pIdealParent ) // reconnect ourselves if the parent is wrong
|
|
FollowEntity( pIdealParent, pIdealParent->IsPlayer() );
|
|
|
|
AddEffects( EF_BONEMERGE_FASTCULL );
|
|
}
|
|
}
|
|
|
|
CBaseWeaponWorldModel::CBaseWeaponWorldModel( void )
|
|
{
|
|
m_nHoldsPlayerAnims = WEAPON_PLAYER_ANIMS_UNKNOWN;
|
|
m_nLeftHandAttachBoneIndex = -1;
|
|
m_nRightHandAttachBoneIndex = -1;
|
|
m_nMuzzleAttachIndex = -1;
|
|
m_nMuzzleBoneIndex = -1;
|
|
#ifdef CLIENT_DLL
|
|
m_bStickersApplied = false;
|
|
m_bMaintainSequenceTransitions = false; // disabled for perf - world model weapons do not transition their sequences
|
|
RenderWithViewModels( false );
|
|
|
|
SetUseParentLightingOrigin( true ); // don't set up bones when asked for lighting origin, just use parent's one (in this case player)
|
|
#endif
|
|
}
|
|
|
|
CBaseWeaponWorldModel::~CBaseWeaponWorldModel( void )
|
|
{
|
|
}
|
|
|
|
bool CBaseWeaponWorldModel::HasDormantOwner( void )
|
|
{
|
|
CBaseCombatWeapon *pWeaponParent = m_hCombatWeaponParent->Get();
|
|
if ( pWeaponParent && pWeaponParent->GetOwner() && pWeaponParent->GetOwner()->IsDormant() )
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void CBaseWeaponWorldModel::ResetCachedBoneIndices( void )
|
|
{
|
|
m_nLeftHandAttachBoneIndex = -1;
|
|
m_nRightHandAttachBoneIndex = -1;
|
|
}
|
|
|
|
int CBaseWeaponWorldModel::GetLeftHandAttachBoneIndex( void )
|
|
{
|
|
if ( m_nLeftHandAttachBoneIndex == -1 )
|
|
m_nLeftHandAttachBoneIndex = LookupBone( "left_hand_attach" );
|
|
|
|
return m_nLeftHandAttachBoneIndex;
|
|
}
|
|
|
|
int CBaseWeaponWorldModel::GetRightHandAttachBoneIndex( void )
|
|
{
|
|
if ( m_nRightHandAttachBoneIndex == -1 )
|
|
m_nRightHandAttachBoneIndex = LookupBone( "weapon_hand_R" );
|
|
|
|
return m_nRightHandAttachBoneIndex;
|
|
}
|
|
|
|
int CBaseWeaponWorldModel::GetMuzzleAttachIndex( void )
|
|
{
|
|
if ( m_nMuzzleAttachIndex == -1 )
|
|
m_nMuzzleAttachIndex = LookupAttachment( "muzzle_flash" );
|
|
|
|
return m_nMuzzleAttachIndex;
|
|
}
|
|
|
|
int CBaseWeaponWorldModel::GetMuzzleBoneIndex( void )
|
|
{
|
|
if ( m_nMuzzleBoneIndex == -1 )
|
|
m_nMuzzleBoneIndex = LookupBone( "weapon_muzzle" );
|
|
|
|
return m_nMuzzleBoneIndex;
|
|
}
|
|
|
|
void CBaseWeaponWorldModel::SetOwningWeapon( CBaseCombatWeapon *pWeaponParent )
|
|
{
|
|
if ( !pWeaponParent )
|
|
return;
|
|
|
|
if ( m_hCombatWeaponParent->Get() != pWeaponParent )
|
|
{
|
|
// assume the parent weapon world model
|
|
SetModel( pWeaponParent->GetWorldModel() );
|
|
|
|
ResetCachedBoneIndices();
|
|
|
|
// determine if this world model holds player animations
|
|
HoldsPlayerAnimations();
|
|
|
|
//keep a handle to this weapon
|
|
m_hCombatWeaponParent.Set( pWeaponParent );
|
|
|
|
//follow our parent asap
|
|
FollowEntity( pWeaponParent, false );
|
|
|
|
//set initial visibility
|
|
CBaseCombatCharacter *pWeaponParentOwner = pWeaponParent->GetOwner();
|
|
bool bInitialVisible = ( pWeaponParentOwner && pWeaponParentOwner->GetActiveWeapon() == pWeaponParent );
|
|
ShowWorldModel( bInitialVisible );
|
|
|
|
#ifndef CLIENT_DLL
|
|
// whatever the mag state, we want it unhidden now
|
|
SetBodygroupPreset( "show_mag" );
|
|
#endif
|
|
}
|
|
|
|
ValidateParent();
|
|
}
|
|
|
|
void CBaseWeaponWorldModel::ShowWorldModel( bool bVisible )
|
|
{
|
|
ValidateParent();
|
|
|
|
if ( bVisible )
|
|
{
|
|
RemoveEffects( EF_NODRAW );
|
|
}
|
|
else
|
|
{
|
|
AddEffects( EF_NODRAW );
|
|
}
|
|
}
|
|
|
|
bool CBaseWeaponWorldModel::HoldsPlayerAnimations( void )
|
|
{
|
|
// TODO: weapon world models need a better way to claim they hold player animations
|
|
if ( m_nHoldsPlayerAnims == WEAPON_PLAYER_ANIMS_UNKNOWN )
|
|
{
|
|
m_nHoldsPlayerAnims = ( GetModelPtr() && GetModelPtr()->GetNumSeq() > 2 ) ? WEAPON_PLAYER_ANIMS_AVAILABLE : WEAPON_PLAYER_ANIMS_NOT_AVAILABLE;
|
|
}
|
|
return ( m_nHoldsPlayerAnims == WEAPON_PLAYER_ANIMS_AVAILABLE );
|
|
}
|
|
|
|
#ifndef CLIENT_DLL
|
|
void CBaseWeaponWorldModel::HandleAnimEvent( animevent_t *pEvent )
|
|
{
|
|
int nEvent = pEvent->Event();
|
|
|
|
if ( nEvent == AE_CL_EJECT_MAG )
|
|
{
|
|
SetBodygroupPreset( "hide_mag" );
|
|
}
|
|
else if ( nEvent == AE_CL_EJECT_MAG_UNHIDE )
|
|
{
|
|
SetBodygroupPreset( "show_mag" );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
void CBaseWeaponWorldModel::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
|
|
{
|
|
if ( event == AE_CL_EJECT_MAG )
|
|
{
|
|
CBaseCombatWeapon *pWeaponParent = m_hCombatWeaponParent->Get();
|
|
if ( pWeaponParent )
|
|
{
|
|
C_BaseCombatCharacter *pPlayer = pWeaponParent->GetOwner();
|
|
if ( pPlayer )
|
|
{
|
|
pPlayer->DropPhysicsMag( options );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CBaseWeaponWorldModel::ShouldDraw( void )
|
|
{
|
|
CBaseCombatWeapon *pWeaponParent = m_hCombatWeaponParent->Get();
|
|
if ( !pWeaponParent )
|
|
return false; // don't draw if we don't have a parent weapon
|
|
|
|
CBaseCombatCharacter *pWeaponParentOwner = pWeaponParent->GetOwner();
|
|
if ( !pWeaponParentOwner || !pWeaponParentOwner->IsPlayer() || !pWeaponParent->GetOwner()->ShouldDraw() || HasDormantOwner() )
|
|
return false; // don't draw if our parent weapon is unheld, or held by a dormant or invisible player
|
|
|
|
// <sergiy> 2016/01/05 - there was a bug here, where (at least in replay, possibly in other spectator type situations) the weapon owner would substitute his active weapon with the active weapon of the observer target.
|
|
// This is seemingly done to simplify the code that deals with local player's active weapon (e.g. ironsight and effects rendering): GetLocalPlayer()->GetActiveWeapon(), when in the In-Eye mode, will always return the weapon to use for local effects (the one in the hands of the observer target).
|
|
CBaseCombatWeapon *pParentWeaponPlayerPrimary;
|
|
#if defined( CLIENT_DLL )
|
|
if ( g_HltvReplaySystem.GetHltvReplayDelay() )
|
|
pParentWeaponPlayerPrimary = pWeaponParentOwner->CBaseCombatCharacter::GetActiveWeapon(); // the ACTUAL active weapon, not a substitute from another player
|
|
else
|
|
#endif
|
|
pParentWeaponPlayerPrimary = pWeaponParentOwner->GetActiveWeapon();
|
|
|
|
if ( !pParentWeaponPlayerPrimary || pParentWeaponPlayerPrimary != pWeaponParent )
|
|
{
|
|
return false; // don't draw if it's not the primary weapon
|
|
}
|
|
|
|
C_BasePlayer * player = C_BasePlayer::GetLocalPlayer();
|
|
if ( player &&
|
|
player->IsObserver() &&
|
|
player->GetObserverMode() == OBS_MODE_IN_EYE &&
|
|
player->GetObserverTarget() == pWeaponParentOwner &&
|
|
!input->CAM_IsThirdPerson() &&
|
|
player->GetObserverInterpState() != 1 )
|
|
{
|
|
return false; // don't draw if we're spectating the parent player owner in first-person
|
|
}
|
|
|
|
if ( IsEffectActive(EF_NODRAW) && ( pWeaponParent->m_flNextPrimaryAttack > gpGlobals->curtime || pWeaponParent->m_flNextSecondaryAttack > gpGlobals->curtime ) )
|
|
{
|
|
return false; // only respect nodraw if we also can't fire (presumably deploying)
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CBaseWeaponWorldModel::ApplyCustomMaterialsAndStickers( void )
|
|
{
|
|
CBaseCombatWeapon *pWeaponParent = m_hCombatWeaponParent->Get();
|
|
if ( !pWeaponParent )
|
|
return;
|
|
|
|
// inherit custom materials
|
|
if ( pWeaponParent->GetCustomMaterialCount() != GetCustomMaterialCount() )
|
|
{
|
|
ClearCustomMaterials();
|
|
for ( int i = 0; i < pWeaponParent->GetCustomMaterialCount(); i++ )
|
|
{
|
|
SetCustomMaterial( pWeaponParent->GetCustomMaterial( i ), i );
|
|
}
|
|
SetAllowFastPath( false );
|
|
}
|
|
|
|
// apply stickers
|
|
pWeaponParent->ApplyThirdPersonStickers( this );
|
|
}
|
|
|
|
#else
|
|
|
|
int CBaseWeaponWorldModel::ShouldTransmit( const CCheckTransmitInfo *pInfo )
|
|
{
|
|
CBaseCombatWeapon *pWeaponParent = m_hCombatWeaponParent->Get();
|
|
if ( pWeaponParent )
|
|
{
|
|
CBaseEntity *pIdealParent = pWeaponParent;
|
|
CBaseCombatCharacter *pWeaponParentOwner = pWeaponParent->GetOwner();
|
|
|
|
if ( pWeaponParentOwner && pWeaponParentOwner->IsPlayer() )
|
|
pIdealParent = pWeaponParentOwner;
|
|
|
|
return pIdealParent->ShouldTransmit( pInfo );
|
|
}
|
|
else
|
|
{
|
|
// invalid situation
|
|
Assert( !"Base Weapon World Model has no weapon parent" );
|
|
return FL_EDICT_ALWAYS;
|
|
}
|
|
}
|
|
|
|
int CBaseWeaponWorldModel::UpdateTransmitState( void )
|
|
{
|
|
return SetTransmitState( FL_EDICT_FULLCHECK );
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifndef CLIENT_DLL
|
|
void CBaseCombatWeapon::ShowWeaponWorldModel( bool bVisible )
|
|
{
|
|
CBaseWeaponWorldModel *pWeaponWorldModel = GetWeaponWorldModel();
|
|
if ( pWeaponWorldModel )
|
|
{
|
|
pWeaponWorldModel->SetOwningWeapon( this );
|
|
pWeaponWorldModel->ShowWorldModel( bVisible );
|
|
}
|
|
}
|
|
|
|
// create a new world model if it doesn't exist
|
|
CBaseWeaponWorldModel* CBaseCombatWeapon::CreateWeaponWorldModel( void )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
|
|
if ( !GetWeaponWorldModel() )
|
|
{
|
|
CBaseWeaponWorldModel *pWorldModel = dynamic_cast <CBaseWeaponWorldModel*> ( CreateEntityByName( "weaponworldmodel" ) );
|
|
|
|
Assert( pWorldModel );
|
|
|
|
pWorldModel->SetOwningWeapon( this );
|
|
m_hWeaponWorldModel.Set( pWorldModel );
|
|
|
|
return pWorldModel;
|
|
}
|
|
else
|
|
{
|
|
return GetWeaponWorldModel();
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
void CBaseCombatWeapon::UpdateVisibility( void )
|
|
{
|
|
CBaseWeaponWorldModel *pWeaponWorldModel = GetWeaponWorldModel();
|
|
if ( pWeaponWorldModel )
|
|
{
|
|
pWeaponWorldModel->UpdateVisibility();
|
|
}
|
|
BaseClass::UpdateVisibility();
|
|
}
|
|
|
|
#endif
|
|
|
|
CBaseCombatWeapon::CBaseCombatWeapon()
|
|
{
|
|
// Constructor must call this
|
|
// CONSTRUCT_PREDICTABLE( CBaseCombatWeapon );
|
|
|
|
// Some default values. There should be set in the particular weapon classes
|
|
m_fMinRange1 = 65;
|
|
m_fMinRange2 = 65;
|
|
m_fMaxRange1 = 1024;
|
|
m_fMaxRange2 = 1024;
|
|
|
|
m_bReloadsSingly = false;
|
|
|
|
// Defaults to zero
|
|
m_nViewModelIndex = 0;
|
|
|
|
m_bFlipViewModel = false;
|
|
|
|
#if defined( CLIENT_DLL )
|
|
m_iState = WEAPON_NOT_CARRIED;
|
|
m_iOldState = m_iState;
|
|
m_iClip1 = -1;
|
|
m_iClip2 = -1;
|
|
m_iPrimaryAmmoType = -1;
|
|
m_iSecondaryAmmoType = -1;
|
|
m_flWeaponTauntHideTimeout = 0.0f;
|
|
#endif
|
|
|
|
m_iWeaponModule = MODULAR_BODYGROUPS_DEFAULT_NONE_SET;
|
|
|
|
#if !defined( CLIENT_DLL )
|
|
m_pConstraint = NULL;
|
|
OnBaseCombatWeaponCreated( this );
|
|
#endif
|
|
|
|
m_hWeaponFileInfo = GetInvalidWeaponInfoHandle();
|
|
|
|
#if defined( TF_DLL )
|
|
UseClientSideAnimation();
|
|
#endif
|
|
|
|
m_WeaponModelClassification = WEAPON_MODEL_IS_UNCLASSIFIED;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Destructor
|
|
//-----------------------------------------------------------------------------
|
|
CBaseCombatWeapon::~CBaseCombatWeapon( void )
|
|
{
|
|
#if !defined( CLIENT_DLL )
|
|
//Remove our constraint, if we have one
|
|
if ( m_pConstraint != NULL )
|
|
{
|
|
physenv->DestroyConstraint( m_pConstraint );
|
|
m_pConstraint = NULL;
|
|
}
|
|
OnBaseCombatWeaponDestroyed( this );
|
|
#endif
|
|
|
|
CBaseWeaponWorldModel *pWeaponWorldModel = GetWeaponWorldModel();
|
|
if ( pWeaponWorldModel )
|
|
{
|
|
UTIL_Remove( pWeaponWorldModel );
|
|
}
|
|
|
|
// Even though CBaseAnimating calls 'InvalidateMdlCache', it will *NOT* call
|
|
// the virtual CBaseCombatWeapon override. This is because the CBaseAnimating
|
|
// destructor is called AFTER the CBaseCombatWeapon destructor has run, by
|
|
// which time the object has reverted to the base type, so derived virtual
|
|
// overrides are no longer in effect.
|
|
// This matters because otherwise m_pWorldStudioHdr will leak memory!
|
|
InvalidateMdlCache();
|
|
}
|
|
|
|
void CBaseCombatWeapon::Activate( void )
|
|
{
|
|
BaseClass::Activate();
|
|
|
|
#ifndef CLIENT_DLL
|
|
if ( GetOwnerEntity() )
|
|
return;
|
|
|
|
if ( g_pGameRules->IsAllowedToSpawn( this ) == false )
|
|
{
|
|
UTIL_Remove( this );
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
void CBaseCombatWeapon::GiveDefaultAmmo( void )
|
|
{
|
|
// If I use clips, set my clips to the default
|
|
if ( UsesClipsForAmmo1() )
|
|
{
|
|
m_iClip1 = GetDefaultClip1();
|
|
}
|
|
else
|
|
{
|
|
SetPrimaryAmmoCount( GetDefaultClip1() );
|
|
m_iClip1 = WEAPON_NOCLIP;
|
|
}
|
|
if ( UsesClipsForAmmo2() )
|
|
{
|
|
m_iClip2 = GetDefaultClip2();
|
|
}
|
|
else
|
|
{
|
|
SetSecondaryAmmoCount( GetDefaultClip2() );
|
|
m_iClip2 = WEAPON_NOCLIP;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Set mode to world model and start falling to the ground
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::Spawn( void )
|
|
{
|
|
Precache();
|
|
|
|
SetSolid( SOLID_BBOX );
|
|
m_flNextEmptySoundTime = 0.0f;
|
|
|
|
// Weapons won't show up in trace calls if they are being carried...
|
|
RemoveEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID );
|
|
|
|
m_iState = WEAPON_NOT_CARRIED;
|
|
SetGlobalFadeScale( 0.0f );
|
|
|
|
// Assume
|
|
m_nViewModelIndex = 0;
|
|
|
|
m_iWeaponModule = MODULAR_BODYGROUPS_DEFAULT_NONE_SET;
|
|
|
|
GiveDefaultAmmo();
|
|
|
|
VerifyAndSetContextSensitiveWeaponModel();
|
|
|
|
#if !defined( CLIENT_DLL )
|
|
if ( GetWpnData().szAIAddOn[ 0 ] != '\0' )
|
|
{
|
|
SetAIAddOn( AllocPooledString( GetWpnData().szAIAddOn ) );
|
|
}
|
|
|
|
if( IsGameConsole() )
|
|
{
|
|
AddEffects( EF_ITEM_BLINK );
|
|
}
|
|
|
|
FallInit();
|
|
SetCollisionGroup( COLLISION_GROUP_WEAPON );
|
|
m_takedamage = DAMAGE_EVENTS_ONLY;
|
|
|
|
SetBlocksLOS( false );
|
|
|
|
// Default to non-removeable, because we don't want the
|
|
// game_weapon_manager entity to remove weapons that have
|
|
// been hand-placed by level designers. We only want to remove
|
|
// weapons that have been dropped by NPC's.
|
|
SetRemoveable( false );
|
|
|
|
//SetWeaponModules();
|
|
CreateWeaponWorldModel();
|
|
|
|
#endif
|
|
|
|
// Bloat the box for player pickup
|
|
CollisionProp()->UseTriggerBounds( true, 36 );
|
|
|
|
// Use more efficient bbox culling on the client. Otherwise, it'll setup bones for most
|
|
// characters even when they're not in the frustum.
|
|
AddEffects( EF_BONEMERGE_FASTCULL );
|
|
|
|
m_iReloadHudHintCount = 0;
|
|
m_iAltFireHudHintCount = 0;
|
|
m_flHudHintMinDisplayTime = 0;
|
|
m_iReloadActivityIndex = ACT_VM_RELOAD;
|
|
|
|
m_iNumEmptyAttacks = 0;
|
|
m_iPrimaryReserveAmmoCount = 0; // amount of reserve ammo. This used to be on the player ( m_iAmmo ) but we're moving it to the weapon.
|
|
m_iSecondaryReserveAmmoCount = 0; // amount of reserve ammo. This used to be on the player ( m_iAmmo ) but we're moving it to the weapon.
|
|
|
|
#ifndef CLIENT_DLL
|
|
m_flLastTimeInAir = 0;
|
|
#endif
|
|
|
|
}
|
|
|
|
#ifndef CLIENT_DLL
|
|
void CBaseCombatWeapon::PhysicsSimulate( void )
|
|
{
|
|
BaseClass::PhysicsSimulate();
|
|
|
|
// remember the last time we were flying through the air
|
|
if ( GetOwner() == NULL && !(GetFlags() & FL_ONGROUND) )
|
|
{
|
|
m_flLastTimeInAir = gpGlobals->curtime;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: get this game's encryption key for decoding weapon kv files
|
|
// Output : virtual const unsigned char
|
|
//-----------------------------------------------------------------------------
|
|
const unsigned char *CBaseCombatWeapon::GetEncryptionKey( void )
|
|
{
|
|
return g_pGameRules->GetEncryptionKey();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::Precache( void )
|
|
{
|
|
#if defined( CLIENT_DLL )
|
|
Assert( Q_strlen( GetClassname() ) > 0 );
|
|
// Msg( "Client got %s\n", GetClassname() );
|
|
#endif
|
|
|
|
m_iPrimaryAmmoType = m_iSecondaryAmmoType = -1;
|
|
|
|
// Add this weapon to the weapon registry, and get our index into it
|
|
// Get weapon data from script file
|
|
m_hWeaponFileInfo = LookupWeaponInfoSlot( GetClassname() );
|
|
if ( m_hWeaponFileInfo != GetInvalidWeaponInfoHandle() )
|
|
{
|
|
// Get the ammo indexes for the ammo's specified in the data file
|
|
if ( GetWpnData().GetPrimaryAmmo( GetEconItemView() )[0] )
|
|
{
|
|
m_iPrimaryAmmoType = GetAmmoDef()->Index( GetWpnData().GetPrimaryAmmo( GetEconItemView() ) );
|
|
if (m_iPrimaryAmmoType == -1)
|
|
{
|
|
Msg("ERROR: Weapon (%s) using undefined primary ammo type (%s)\n",GetClassname(), GetWpnData().GetPrimaryAmmo( GetEconItemView() ) );
|
|
}
|
|
}
|
|
if ( GetWpnData().szAmmo2[0] )
|
|
{
|
|
m_iSecondaryAmmoType = GetAmmoDef()->Index( GetWpnData().szAmmo2 );
|
|
if (m_iSecondaryAmmoType == -1)
|
|
{
|
|
Msg("ERROR: Weapon (%s) using undefined secondary ammo type (%s)\n",GetClassname(),GetWpnData().szAmmo2);
|
|
}
|
|
|
|
}
|
|
#if defined( CLIENT_DLL )
|
|
gWR.LoadWeaponSprites( GetWeaponFileInfoHandle() );
|
|
#endif
|
|
// Precache models (preload to avoid hitch)
|
|
m_iViewModelIndex = 0;
|
|
m_iWorldModelIndex = 0;
|
|
m_iWorldDroppedModelIndex = 0;
|
|
m_iWeaponModule = MODULAR_BODYGROUPS_DEFAULT_NONE_SET;
|
|
if ( GetViewModel() && GetViewModel()[0] )
|
|
{
|
|
g_pMDLCache->DisableVCollideLoad();
|
|
m_iViewModelIndex = CBaseEntity::PrecacheModel( GetViewModel() );
|
|
g_pMDLCache->EnableVCollideLoad();
|
|
}
|
|
if ( GetWorldModel() && GetWorldModel()[0] )
|
|
{
|
|
m_iWorldModelIndex = CBaseEntity::PrecacheModel( GetWorldModel() );
|
|
}
|
|
if ( GetWorldDroppedModel() && GetWorldDroppedModel()[0] )
|
|
{
|
|
m_iWorldDroppedModelIndex = CBaseEntity::PrecacheModel( GetWorldDroppedModel() );
|
|
}
|
|
|
|
// Precache sounds, too
|
|
for ( int i = 0; i < NUM_SHOOT_SOUND_TYPES; ++i )
|
|
{
|
|
const char *shootsound = GetShootSound( i );
|
|
if ( shootsound && shootsound[0] )
|
|
{
|
|
CBaseEntity::PrecacheScriptSound( shootsound );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Couldn't read data file, remove myself
|
|
Warning( "Error reading weapon data file for: %s\n", GetClassname() );
|
|
// Remove( ); //don't remove, this gets released soon!
|
|
}
|
|
|
|
const char *pszTracerName = GetTracerType();
|
|
if ( pszTracerName && pszTracerName[0] )
|
|
{
|
|
PrecacheEffect( pszTracerName );
|
|
}
|
|
|
|
PrecacheEffect( "ParticleTracer" );
|
|
PrecacheParticleSystem( "weapon_tracers" );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Get my data in the file weapon info array
|
|
//-----------------------------------------------------------------------------
|
|
const FileWeaponInfo_t &CBaseCombatWeapon::GetWpnData( void ) const
|
|
{
|
|
return *GetFileWeaponInfoFromHandle( m_hWeaponFileInfo );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
const char *CBaseCombatWeapon::GetViewModel( int /*viewmodelindex = 0 -- this is ignored in the base class here*/ ) const
|
|
{
|
|
return GetWpnData().GetViewModel( GetEconItemView(), (
|
|
( GetOwner() != NULL && GetOwner()->IsPlayer() ) ? GetOwner()->GetTeamNumber() : 0
|
|
) );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
const char *CBaseCombatWeapon::GetWorldModel( void ) const
|
|
{
|
|
return GetWpnData().GetWorldModel( GetEconItemView(), (
|
|
( GetOwner() != NULL && GetOwner()->IsPlayer() ) ? GetOwner()->GetTeamNumber() : 0
|
|
) );
|
|
}
|
|
|
|
|
|
const char *CBaseCombatWeapon::GetWorldDroppedModel( void ) const
|
|
{
|
|
const char *szWorldDroppedModel = GetWpnData().GetWorldDroppedModel( GetEconItemView(), (
|
|
( GetOwner() != NULL && GetOwner()->IsPlayer() ) ? GetOwner()->GetTeamNumber() : 0
|
|
) );
|
|
|
|
// world dropped model path is optional, but always built. Make sure the model exists before returning it.
|
|
if ( szWorldDroppedModel )
|
|
{
|
|
MDLHandle_t modelHandle = g_pMDLCache->FindMDL( szWorldDroppedModel );
|
|
if ( !g_pMDLCache->IsErrorModel( modelHandle ) )
|
|
return szWorldDroppedModel;
|
|
}
|
|
|
|
return GetWorldModel();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
const char *CBaseCombatWeapon::GetAnimPrefix( void ) const
|
|
{
|
|
return GetWpnData().szAnimationPrefix;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : char const
|
|
//-----------------------------------------------------------------------------
|
|
const char *CBaseCombatWeapon::GetPrintName( void ) const
|
|
{
|
|
if ( GetEconItemView( ) )
|
|
return GetEconItemView( )->GetItemDefinition()->GetItemBaseName();
|
|
else
|
|
return GetWpnData().szPrintName;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::UsesClipsForAmmo1( void ) const
|
|
{
|
|
return ( GetMaxClip1() != WEAPON_NOCLIP );
|
|
}
|
|
|
|
bool CBaseCombatWeapon::IsMeleeWeapon() const
|
|
{
|
|
return GetWpnData().m_bMeleeWeapon;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::UsesClipsForAmmo2( void ) const
|
|
{
|
|
return ( GetMaxClip2() != WEAPON_NOCLIP );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
int CBaseCombatWeapon::GetWeight( void ) const
|
|
{
|
|
return GetWpnData().iWeight;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Whether this weapon can be autoswitched to when the player runs out
|
|
// of ammo in their current weapon or they pick this weapon up.
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::AllowsAutoSwitchTo( void ) const
|
|
{
|
|
return GetWpnData().bAutoSwitchTo;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Whether this weapon can be autoswitched away from when the player
|
|
// runs out of ammo in this weapon or picks up another weapon or ammo.
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::AllowsAutoSwitchFrom( void ) const
|
|
{
|
|
return GetWpnData().bAutoSwitchFrom;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
int CBaseCombatWeapon::GetWeaponFlags( void ) const
|
|
{
|
|
return GetWpnData().iFlags;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
int CBaseCombatWeapon::GetSlot( void ) const
|
|
{
|
|
return GetWpnData().iSlot;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
int CBaseCombatWeapon::GetPosition( void ) const
|
|
{
|
|
return GetWpnData().iPosition;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
const char *CBaseCombatWeapon::GetName( void ) const
|
|
{
|
|
return GetWpnData().szClassName;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CHudTexture const *CBaseCombatWeapon::GetSpriteActive( void ) const
|
|
{
|
|
return GetWpnData().iconActive;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CHudTexture const *CBaseCombatWeapon::GetSpriteInactive( void ) const
|
|
{
|
|
return GetWpnData().iconInactive;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CHudTexture const *CBaseCombatWeapon::GetSpriteAmmo( void ) const
|
|
{
|
|
return GetWpnData().iconAmmo;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CHudTexture const *CBaseCombatWeapon::GetSpriteAmmo2( void ) const
|
|
{
|
|
return GetWpnData().iconAmmo2;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CHudTexture const *CBaseCombatWeapon::GetSpriteCrosshair( void ) const
|
|
{
|
|
return GetWpnData().iconCrosshair;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CHudTexture const *CBaseCombatWeapon::GetSpriteAutoaim( void ) const
|
|
{
|
|
return GetWpnData().iconAutoaim;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CHudTexture const *CBaseCombatWeapon::GetSpriteZoomedCrosshair( void ) const
|
|
{
|
|
return GetWpnData().iconZoomedCrosshair;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CHudTexture const *CBaseCombatWeapon::GetSpriteZoomedAutoaim( void ) const
|
|
{
|
|
return GetWpnData().iconZoomedAutoaim;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
const char *CBaseCombatWeapon::GetShootSound( int iIndex ) const
|
|
{
|
|
return GetWpnData().aShootSounds[ iIndex ];
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
int CBaseCombatWeapon::GetRumbleEffect() const
|
|
{
|
|
return GetWpnData().iRumbleEffect;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CBaseCombatCharacter *CBaseCombatWeapon::GetOwner() const
|
|
{
|
|
return ToBaseCombatCharacter( m_hOwner.Get() );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : BaseCombatCharacter -
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::SetOwner( CBaseCombatCharacter *owner )
|
|
{
|
|
if ( !owner )
|
|
{
|
|
#ifndef CLIENT_DLL
|
|
// Make sure the weapon updates its state when it's removed from the player
|
|
// We have to force an active state change, because it's being dropped and won't call UpdateClientData()
|
|
m_iState = WEAPON_NOT_CARRIED;
|
|
#endif
|
|
|
|
// make sure we clear out our HideThink if we have one pending
|
|
SetContextThink( NULL, 0, HIDEWEAPON_THINK_CONTEXT );
|
|
}
|
|
|
|
m_hOwner = owner;
|
|
|
|
#ifndef CLIENT_DLL
|
|
DispatchUpdateTransmitState();
|
|
#else
|
|
UpdateVisibility();
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Return false if this weapon won't let the player switch away from it
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::IsAllowedToSwitch( void )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Return true if this weapon can be selected via the weapon selection
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::CanBeSelected( void )
|
|
{
|
|
if ( !VisibleInWeaponSelection() )
|
|
return false;
|
|
|
|
return HasAmmo();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Return true if this weapon has some ammo
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::HasAmmo( void )
|
|
{
|
|
// Weapons with no ammo types can always be selected
|
|
if ( m_iPrimaryAmmoType == -1 && m_iSecondaryAmmoType == -1 )
|
|
return true;
|
|
if ( GetWeaponFlags() & ITEM_FLAG_SELECTONEMPTY )
|
|
return true;
|
|
|
|
CBasePlayer *player = ToBasePlayer( GetOwner() );
|
|
if ( !player )
|
|
return false;
|
|
return ( m_iClip1 > 0 || GetReserveAmmoCount( AMMO_POSITION_PRIMARY ) || m_iClip2 > 0 || GetReserveAmmoCount( AMMO_POSITION_SECONDARY ) );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Return true if this weapon should be seen, and hence be selectable, in the weapon selection
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::VisibleInWeaponSelection( void )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::HasWeaponIdleTimeElapsed( void )
|
|
{
|
|
if ( gpGlobals->curtime > m_flTimeWeaponIdle )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : time -
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::SetWeaponIdleTime( float time )
|
|
{
|
|
m_flTimeWeaponIdle = time;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : float
|
|
//-----------------------------------------------------------------------------
|
|
float CBaseCombatWeapon::GetWeaponIdleTime( void )
|
|
{
|
|
return m_flTimeWeaponIdle;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Drop/throw the weapon with the given velocity.
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::Drop( const Vector &vecVelocity )
|
|
{
|
|
#if !defined( CLIENT_DLL )
|
|
// 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 );
|
|
WeaponManager_AmmoMod( this );
|
|
|
|
//If it was dropped then there's no need to respawn it.
|
|
AddSpawnFlags( SF_NORESPAWN );
|
|
|
|
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 );
|
|
SetThink( &CBaseCombatWeapon::SetPickupTouch );
|
|
SetTouch(NULL);
|
|
|
|
if( hl2_episodic.GetBool() )
|
|
{
|
|
RemoveSpawnFlags( SF_WEAPON_NO_PLAYER_PICKUP );
|
|
}
|
|
|
|
IPhysicsObject *pObj = VPhysicsGetObject();
|
|
if ( pObj != NULL )
|
|
{
|
|
AngularImpulse angImp( 200, 200, 200 );
|
|
pObj->AddVelocity( &vecVelocity, &angImp );
|
|
}
|
|
else
|
|
{
|
|
SetAbsVelocity( vecVelocity );
|
|
}
|
|
|
|
CBaseEntity *pOwner = GetOwnerEntity();
|
|
|
|
SetNextThink( gpGlobals->curtime + 1.0f );
|
|
SetOwnerEntity( NULL );
|
|
SetOwner( NULL );
|
|
|
|
// If we're not allowing to spawn due to the gamerules,
|
|
// remove myself when I'm dropped by an NPC.
|
|
if ( pOwner && pOwner->IsNPC() )
|
|
{
|
|
if ( g_pGameRules->IsAllowedToSpawn( this ) == false )
|
|
{
|
|
UTIL_Remove( this );
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *pPicker -
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::OnPickedUp( CBaseCombatCharacter *pNewOwner )
|
|
{
|
|
#if !defined( CLIENT_DLL )
|
|
RemoveEffects( EF_ITEM_BLINK );
|
|
|
|
if( pNewOwner->IsPlayer() )
|
|
{
|
|
m_OnPlayerPickup.FireOutput(pNewOwner, this);
|
|
|
|
// 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 );
|
|
}
|
|
else
|
|
{
|
|
m_OnNPCPickup.FireOutput(pNewOwner, this);
|
|
}
|
|
|
|
// Someone picked me up, so make it so that I can't be removed.
|
|
SetRemoveable( false );
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : &vecTracerSrc -
|
|
// &tr -
|
|
// iTracerType -
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType )
|
|
{
|
|
CBaseEntity *pOwner = GetOwner();
|
|
|
|
if ( pOwner == NULL )
|
|
{
|
|
BaseClass::MakeTracer( vecTracerSrc, tr, iTracerType );
|
|
return;
|
|
}
|
|
|
|
const char *pszTracerName = GetTracerType();
|
|
if ( !pszTracerName )
|
|
{
|
|
pszTracerName = "weapon_tracers";
|
|
}
|
|
|
|
Vector vNewSrc = vecTracerSrc;
|
|
int iEntIndex = pOwner->entindex();
|
|
|
|
if ( g_pGameRules->IsMultiplayer() )
|
|
{
|
|
iEntIndex = entindex();
|
|
#ifdef CLIENT_DLL
|
|
C_BasePlayer *player = ToBasePlayer( pOwner );
|
|
if ( C_BasePlayer::IsLocalPlayer( player ) )
|
|
{
|
|
CBaseEntity *vm = player->GetViewModel();
|
|
if ( vm )
|
|
{
|
|
iEntIndex = vm->entindex();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int iAttachment = GetTracerAttachment();
|
|
|
|
UTIL_ParticleTracer( pszTracerName, vNewSrc, tr.endpos, iEntIndex, iAttachment, true );
|
|
}
|
|
|
|
void CBaseCombatWeapon::GiveTo( CBaseEntity *pOther )
|
|
{
|
|
DefaultTouch( pOther );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Default Touch function for player picking up a weapon (not AI)
|
|
// Input : pOther - the entity that touched me
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::DefaultTouch( CBaseEntity *pOther )
|
|
{
|
|
#if !defined( CLIENT_DLL )
|
|
// Can't pick up dissolving weapons
|
|
if ( IsDissolving() )
|
|
return;
|
|
|
|
// if it's not a player, ignore
|
|
CBasePlayer *pPlayer = ToBasePlayer(pOther);
|
|
if ( !pPlayer )
|
|
return;
|
|
|
|
if( UTIL_ItemCanBeTouchedByPlayer(this, pPlayer) )
|
|
{
|
|
// This makes sure the player could potentially take the object
|
|
// before firing the cache interaction output. That doesn't mean
|
|
// the player WILL end up taking the object, but cache interactions
|
|
// are fired as soon as you prove you have found the object, not
|
|
// when you finally acquire it.
|
|
m_OnCacheInteraction.FireOutput( pOther, this );
|
|
}
|
|
|
|
if( HasSpawnFlags(SF_WEAPON_NO_PLAYER_PICKUP) )
|
|
return;
|
|
|
|
if (pPlayer->BumpWeapon(this))
|
|
{
|
|
OnPickedUp( pPlayer );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// It's OK for base classes to override this completely
|
|
// without calling up. (sjb)
|
|
//---------------------------------------------------------
|
|
bool CBaseCombatWeapon::ShouldDisplayAltFireHUDHint()
|
|
{
|
|
if( m_iAltFireHudHintCount >= WEAPON_RELOAD_HUD_HINT_COUNT )
|
|
return false;
|
|
|
|
if( UsesSecondaryAmmo() && HasSecondaryAmmo() )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if( !UsesSecondaryAmmo() && HasPrimaryAmmo() )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::DisplayAltFireHudHint()
|
|
{
|
|
#if !defined( CLIENT_DLL )
|
|
CFmtStr hint;
|
|
hint.sprintf( "#valve_hint_alt_%s", GetClassname() );
|
|
UTIL_HudHintText( GetOwner(), hint.Access() );
|
|
m_iAltFireHudHintCount++;
|
|
m_bAltFireHudHintDisplayed = true;
|
|
m_flHudHintMinDisplayTime = gpGlobals->curtime + MIN_HUDHINT_DISPLAY_TIME;
|
|
#endif//CLIENT_DLL
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::RescindAltFireHudHint()
|
|
{
|
|
#if !defined( CLIENT_DLL )
|
|
Assert(m_bAltFireHudHintDisplayed);
|
|
|
|
UTIL_HudHintText( GetOwner(), "" );
|
|
--m_iAltFireHudHintCount;
|
|
m_bAltFireHudHintDisplayed = false;
|
|
#endif//CLIENT_DLL
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::ShouldDisplayReloadHUDHint()
|
|
{
|
|
if( m_iReloadHudHintCount >= WEAPON_RELOAD_HUD_HINT_COUNT )
|
|
return false;
|
|
|
|
CBaseCombatCharacter *pOwner = GetOwner();
|
|
|
|
if( pOwner != NULL && pOwner->IsPlayer() && UsesClipsForAmmo1() && m_iClip1 < (GetMaxClip1() / 2) )
|
|
{
|
|
// I'm owned by a player, I use clips, I have less then half a clip loaded. Now, does the player have more ammo?
|
|
if ( GetReserveAmmoCount( AMMO_POSITION_PRIMARY ) > 0 )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::DisplayReloadHudHint()
|
|
{
|
|
#if !defined( CLIENT_DLL )
|
|
UTIL_HudHintText( GetOwner(), "valve_hint_reload" );
|
|
m_iReloadHudHintCount++;
|
|
m_bReloadHudHintDisplayed = true;
|
|
m_flHudHintMinDisplayTime = gpGlobals->curtime + MIN_HUDHINT_DISPLAY_TIME;
|
|
#endif//CLIENT_DLL
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::RescindReloadHudHint()
|
|
{
|
|
#if !defined( CLIENT_DLL )
|
|
Assert(m_bReloadHudHintDisplayed);
|
|
|
|
UTIL_HudHintText( GetOwner(), "" );
|
|
--m_iReloadHudHintCount;
|
|
m_bReloadHudHintDisplayed = false;
|
|
#endif//CLIENT_DLL
|
|
}
|
|
|
|
|
|
void CBaseCombatWeapon::SetPickupTouch( void )
|
|
{
|
|
#if !defined( CLIENT_DLL )
|
|
SetTouch(&CBaseCombatWeapon::DefaultTouch);
|
|
|
|
if ( gpGlobals->maxClients > 1 )
|
|
{
|
|
if ( GetSpawnFlags() & SF_NORESPAWN )
|
|
{
|
|
SetThink( &CBaseEntity::SUB_Remove );
|
|
SetNextThink( gpGlobals->curtime + 30.0f );
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Become a child of the owner (MOVETYPE_FOLLOW)
|
|
// disables collisions, touch functions, thinking
|
|
// Input : *pOwner - new owner/operator
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::Equip( CBaseCombatCharacter *pOwner )
|
|
{
|
|
// Attach the weapon to an owner
|
|
SetAbsVelocity( vec3_origin );
|
|
RemoveSolidFlags( FSOLID_TRIGGER );
|
|
FollowEntity( pOwner );
|
|
SetOwner( pOwner );
|
|
SetOwnerEntity( pOwner );
|
|
|
|
// Break any constraint I might have to the world.
|
|
RemoveEffects( EF_ITEM_BLINK );
|
|
|
|
#if !defined( CLIENT_DLL )
|
|
if ( m_pConstraint != NULL )
|
|
{
|
|
RemoveSpawnFlags( SF_WEAPON_START_CONSTRAINED );
|
|
physenv->DestroyConstraint( m_pConstraint );
|
|
m_pConstraint = NULL;
|
|
}
|
|
#endif
|
|
|
|
|
|
m_flNextPrimaryAttack = gpGlobals->curtime;
|
|
m_flNextSecondaryAttack = gpGlobals->curtime;
|
|
SetTouch( NULL );
|
|
SetThink( NULL );
|
|
#if !defined( CLIENT_DLL )
|
|
VPhysicsDestroyObject();
|
|
#endif
|
|
|
|
m_flNextPrimaryAttack = gpGlobals->curtime;
|
|
m_flNextSecondaryAttack = gpGlobals->curtime;
|
|
|
|
VerifyAndSetContextSensitiveWeaponModel();
|
|
}
|
|
|
|
CStudioHdr* CBaseCombatWeapon::OnNewModel()
|
|
{
|
|
ClassifyWeaponModel();
|
|
return BaseClass::OnNewModel();
|
|
}
|
|
|
|
void CBaseCombatWeapon::ClassifyWeaponModel( void )
|
|
{
|
|
// I don't like this either, but the model's aren't tagged in content,
|
|
// nor are they tagged coming in from multiple years of legacy demos in
|
|
// their various forms. Game code pushes new models by raw path all over
|
|
// the place, and I just need a way to verify and set the model as the
|
|
// appropriate kind without doing an expensive string comparison or
|
|
// model loop up by string each time.
|
|
|
|
const char *pszModelName = NULL;
|
|
if ( GetModel() )
|
|
pszModelName = modelinfo->GetModelName(GetModel());
|
|
|
|
if ( !pszModelName || pszModelName[0] == 0 )
|
|
{
|
|
m_WeaponModelClassification = WEAPON_MODEL_IS_UNCLASSIFIED;
|
|
}
|
|
else if ( V_stristr( pszModelName, "models/weapons/v_" ) )
|
|
{
|
|
m_WeaponModelClassification = WEAPON_MODEL_IS_VIEWMODEL;
|
|
}
|
|
else if ( V_stristr( pszModelName, "models/weapons/w_" ) )
|
|
{
|
|
if ( V_stristr( pszModelName, "_dropped.mdl" ) )
|
|
{
|
|
m_WeaponModelClassification = WEAPON_MODEL_IS_DROPPEDMODEL;
|
|
}
|
|
else
|
|
{
|
|
m_WeaponModelClassification = WEAPON_MODEL_IS_WORLDMODEL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// valid path, just didn't match anything we were looking for.
|
|
m_WeaponModelClassification = WEAPON_MODEL_IS_UNRECOGNIZED;
|
|
}
|
|
}
|
|
|
|
void CBaseCombatWeapon::VerifyAndSetContextSensitiveWeaponModel( void )
|
|
{
|
|
// Check that the weapon model is the right kind (viewmodel, worldmodel, etc )
|
|
// Using a fast, non-string comparison check. If it's the wrong type,
|
|
// set the model to the correct version, then update the record so
|
|
// future checks are fast and don't need to continuously re-set the
|
|
// model unnecessarily.
|
|
|
|
WeaponModelClassification_t tClassification = GetWeaponModelClassification();
|
|
|
|
#ifdef CLIENT_DLL
|
|
if ( tClassification == WEAPON_MODEL_IS_UNCLASSIFIED )
|
|
{
|
|
if ( GetOwner() )
|
|
{
|
|
SetModel( GetWorldModel() );
|
|
}
|
|
else
|
|
{
|
|
SetModel( GetWorldDroppedModel() );
|
|
}
|
|
}
|
|
else if ( tClassification == WEAPON_MODEL_IS_VIEWMODEL )
|
|
{
|
|
if ( !GetOwner() )
|
|
{
|
|
SetModel( GetWorldDroppedModel() );
|
|
}
|
|
else if ( GetOwner()->ShouldDraw() )
|
|
{
|
|
SetModel( GetWorldModel() );
|
|
}
|
|
}
|
|
#else
|
|
if ( tClassification != WEAPON_MODEL_IS_VIEWMODEL && GetOwner() )
|
|
{
|
|
SetModel( GetViewModel() );
|
|
}
|
|
else if ( tClassification == WEAPON_MODEL_IS_UNCLASSIFIED || (tClassification == WEAPON_MODEL_IS_VIEWMODEL && !GetOwner()) )
|
|
{
|
|
SetModel( GetWorldDroppedModel() );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
WeaponModelClassification_t CBaseCombatWeapon::GetWeaponModelClassification( void )
|
|
{
|
|
if ( m_WeaponModelClassification == WEAPON_MODEL_IS_UNCLASSIFIED )
|
|
{
|
|
ClassifyWeaponModel();
|
|
}
|
|
return m_WeaponModelClassification;
|
|
}
|
|
|
|
void CBaseCombatWeapon::SetActivity( Activity act, float duration )
|
|
{
|
|
int sequence = SelectWeightedSequence( act );
|
|
|
|
// FORCE IDLE on sequences we don't have (which should be many)
|
|
if ( sequence == ACTIVITY_NOT_AVAILABLE )
|
|
sequence = SelectWeightedSequence( ACT_VM_IDLE );
|
|
|
|
if ( sequence != ACTIVITY_NOT_AVAILABLE )
|
|
{
|
|
SetSequence( sequence );
|
|
SetActivity( act );
|
|
SetCycle( 0 );
|
|
ResetSequenceInfo( );
|
|
|
|
if ( duration > 0 )
|
|
{
|
|
// FIXME: does this even make sense in non-shoot animations?
|
|
m_flPlaybackRate = SequenceDuration( sequence ) / duration;
|
|
m_flPlaybackRate = fpmin( m_flPlaybackRate, 12.0f); // FIXME; magic number!, network encoding range
|
|
Assert( IsFinite( m_flPlaybackRate ) );
|
|
}
|
|
else
|
|
{
|
|
m_flPlaybackRate = 1.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
//====================================================================================
|
|
// WEAPON CLIENT HANDLING
|
|
//====================================================================================
|
|
int CBaseCombatWeapon::UpdateClientData( CBasePlayer *pPlayer )
|
|
{
|
|
int iNewState = WEAPON_IS_CARRIED_BY_PLAYER;
|
|
|
|
if ( pPlayer->GetActiveWeapon() == this || IsAlwaysActive() )
|
|
{
|
|
iNewState = WEAPON_IS_ACTIVE;
|
|
}
|
|
else
|
|
{
|
|
iNewState = WEAPON_IS_CARRIED_BY_PLAYER;
|
|
}
|
|
|
|
if ( m_iState != iNewState )
|
|
{
|
|
m_iState = iNewState;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : index -
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::SetViewModelIndex( int index )
|
|
{
|
|
Assert( index >= 0 && index < MAX_VIEWMODELS );
|
|
m_nViewModelIndex = index;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : iActivity -
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::SendViewModelAnim( int nSequence )
|
|
{
|
|
#if defined( CLIENT_DLL )
|
|
if ( !IsPredicted() )
|
|
return;
|
|
#endif
|
|
|
|
if ( nSequence < 0 )
|
|
return;
|
|
|
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
|
|
|
if ( pOwner == NULL )
|
|
return;
|
|
|
|
CBaseViewModel *vm = pOwner->GetViewModel( m_nViewModelIndex );
|
|
|
|
if ( vm == NULL )
|
|
return;
|
|
|
|
SetViewModel();
|
|
Assert( vm->ViewModelIndex() == m_nViewModelIndex );
|
|
vm->SendViewModelMatchingSequence( nSequence );
|
|
}
|
|
|
|
float CBaseCombatWeapon::GetViewModelSequenceDuration()
|
|
{
|
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
|
if ( pOwner == NULL )
|
|
{
|
|
Assert( false );
|
|
return 0;
|
|
}
|
|
|
|
CBaseViewModel *vm = pOwner->GetViewModel( m_nViewModelIndex );
|
|
if ( vm == NULL )
|
|
{
|
|
Assert( false );
|
|
return 0;
|
|
}
|
|
|
|
SetViewModel();
|
|
Assert( vm->ViewModelIndex() == m_nViewModelIndex );
|
|
return vm->SequenceDuration();
|
|
}
|
|
|
|
bool CBaseCombatWeapon::IsViewModelSequenceFinished( void )
|
|
{
|
|
// These are not valid activities and always complete immediately
|
|
if ( GetActivity() == ACT_RESET || GetActivity() == ACT_INVALID )
|
|
return true;
|
|
|
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
|
if ( pOwner == NULL )
|
|
{
|
|
Assert( false );
|
|
return false;
|
|
}
|
|
|
|
CBaseViewModel *vm = pOwner->GetViewModel( m_nViewModelIndex );
|
|
if ( vm == NULL )
|
|
{
|
|
Assert( false );
|
|
return false;
|
|
}
|
|
|
|
return vm->IsSequenceFinished();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::SetViewModel()
|
|
{
|
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
|
if ( pOwner == NULL )
|
|
return;
|
|
CBaseViewModel *vm = pOwner->GetViewModel( m_nViewModelIndex );
|
|
if ( vm == NULL )
|
|
return;
|
|
Assert( vm->ViewModelIndex() == m_nViewModelIndex );
|
|
vm->SetWeaponModel( GetViewModel( m_nViewModelIndex ), this );
|
|
//#ifndef CLIENT_DLL
|
|
// SetWeaponModules();
|
|
//#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Set the desired activity for the weapon and its viewmodel counterpart
|
|
// Input : iActivity - activity to play
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::SendWeaponAnim( int iActivity )
|
|
{
|
|
//For now, just set the ideal activity and be done with it
|
|
return SetIdealActivity( (Activity) iActivity );
|
|
}
|
|
|
|
//====================================================================================
|
|
// WEAPON SELECTION
|
|
//====================================================================================
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns true if the weapon currently has ammo or doesn't need ammo
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::HasAnyAmmo( void )
|
|
{
|
|
// If I don't use ammo of any kind, I can always fire
|
|
if ( !UsesPrimaryAmmo() && !UsesSecondaryAmmo() )
|
|
return true;
|
|
|
|
// Otherwise, I need ammo of either type
|
|
return ( HasPrimaryAmmo() || HasSecondaryAmmo() );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns true if the weapon currently has ammo or doesn't need ammo
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::HasPrimaryAmmo( void )
|
|
{
|
|
// If I use a clip, and have some ammo in it, then I have ammo
|
|
if ( UsesClipsForAmmo1() )
|
|
{
|
|
if ( m_iClip1 > 0 )
|
|
return true;
|
|
}
|
|
|
|
// Otherwise, I have ammo if I have some in my ammo counts
|
|
|
|
if ( GetReserveAmmoCount( AMMO_POSITION_PRIMARY ) > 0 )
|
|
return true;
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns true if the weapon currently has ammo or doesn't need ammo
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::HasSecondaryAmmo( void )
|
|
{
|
|
// If I use a clip, and have some ammo in it, then I have ammo
|
|
if ( UsesClipsForAmmo2() )
|
|
{
|
|
if ( m_iClip2 > 0 )
|
|
return true;
|
|
}
|
|
|
|
// Otherwise, I have ammo if I have some in my ammo counts
|
|
if ( GetReserveAmmoCount( AMMO_POSITION_SECONDARY ) > 0 )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: returns true if the weapon actually uses primary ammo
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::UsesPrimaryAmmo( void )
|
|
{
|
|
if ( m_iPrimaryAmmoType < 0 )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: returns true if the weapon actually uses secondary ammo
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::UsesSecondaryAmmo( void )
|
|
{
|
|
if ( m_iSecondaryAmmoType < 0 )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Show/hide weapon and corresponding view model if any
|
|
// Input : visible -
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::SetWeaponVisible( bool visible )
|
|
{
|
|
CBaseViewModel *vm = NULL;
|
|
|
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
|
if ( pOwner )
|
|
{
|
|
vm = pOwner->GetViewModel( m_nViewModelIndex );
|
|
}
|
|
|
|
if ( pOwner )
|
|
{
|
|
AddEffects( EF_NODRAW ); // The combatweapon hides when held by a player. The weaponworldmodel renders instead.
|
|
}
|
|
else
|
|
{
|
|
if ( visible )
|
|
{
|
|
RemoveEffects( EF_NODRAW );
|
|
}
|
|
else
|
|
{
|
|
AddEffects( EF_NODRAW );
|
|
}
|
|
}
|
|
|
|
// viewmodel
|
|
if ( vm )
|
|
{
|
|
if ( visible )
|
|
{
|
|
vm->RemoveEffects( EF_NODRAW );
|
|
}
|
|
else
|
|
{
|
|
vm->AddEffects( EF_NODRAW );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::IsWeaponVisible( void )
|
|
{
|
|
CBaseViewModel *vm = NULL;
|
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
|
if ( pOwner )
|
|
{
|
|
vm = pOwner->GetViewModel( m_nViewModelIndex );
|
|
if ( vm )
|
|
{
|
|
#ifdef CLIENT_DLL
|
|
return !vm->IsDormant() && !vm->IsEffectActive(EF_NODRAW);
|
|
#else
|
|
return ( !vm->IsEffectActive(EF_NODRAW) );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: If the current weapon has more ammo, reload it. Otherwise, switch
|
|
// to the next best weapon we've got. Returns true if it took any action.
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::ReloadOrSwitchWeapons( void )
|
|
{
|
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
|
Assert( pOwner );
|
|
|
|
m_bFireOnEmpty = false;
|
|
|
|
// If we don't have any ammo, switch to the next best weapon
|
|
if ( !HasAnyAmmo() && m_flNextPrimaryAttack < gpGlobals->curtime && m_flNextSecondaryAttack < gpGlobals->curtime )
|
|
{
|
|
// weapon isn't useable, switch.
|
|
if ( ( (GetWeaponFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) == false ) && ( g_pGameRules->SwitchToNextBestWeapon( pOwner, this ) ) )
|
|
{
|
|
m_flNextPrimaryAttack = gpGlobals->curtime + 0.3;
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Weapon is useable. Reload if empty and weapon has waited as long as it has to after firing
|
|
if ( UsesClipsForAmmo1() &&
|
|
(m_iClip1 == 0) &&
|
|
(GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD) == false &&
|
|
m_flNextPrimaryAttack < gpGlobals->curtime &&
|
|
m_flNextSecondaryAttack < gpGlobals->curtime )
|
|
{
|
|
// if we're successfully reloading, we're done
|
|
if ( Reload() )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *szViewModel -
|
|
// *szWeaponModel -
|
|
// iActivity -
|
|
// *szAnimExt -
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::DefaultDeploy( char *szViewModel, char *szWeaponModel, int iActivity, char *szAnimExt )
|
|
{
|
|
// Msg( "deploy %s at %f\n", GetClassname(), gpGlobals->curtime );
|
|
|
|
// Weapons that don't autoswitch away when they run out of ammo
|
|
// can still be deployed when they have no ammo.
|
|
if ( !HasAnyAmmo() && AllowsAutoSwitchFrom() )
|
|
return false;
|
|
|
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
|
if ( pOwner )
|
|
{
|
|
// Dead men deploy no weapons
|
|
if ( pOwner->IsAlive() == false )
|
|
return false;
|
|
|
|
pOwner->SetAnimationExtension( szAnimExt );
|
|
|
|
SetViewModel();
|
|
SendWeaponAnim( iActivity );
|
|
|
|
pOwner->SetNextAttack( gpGlobals->curtime + SequenceDuration() );
|
|
}
|
|
|
|
// Can't shoot again until we've finished deploying
|
|
m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
|
|
m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
|
|
m_flHudHintMinDisplayTime = 0;
|
|
|
|
m_bAltFireHudHintDisplayed = false;
|
|
m_bReloadHudHintDisplayed = false;
|
|
m_flHudHintPollTime = gpGlobals->curtime + 5.0f;
|
|
|
|
SetWeaponVisible( true );
|
|
|
|
/*
|
|
|
|
This code is disabled for now, because moving through the weapons in the carousel
|
|
selects and deploys each weapon as you pass it. (sjb)
|
|
|
|
*/
|
|
|
|
SetContextThink( NULL, 0, HIDEWEAPON_THINK_CONTEXT );
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::Deploy( )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
|
|
#if !defined( CLIENT_DLL )
|
|
CreateWeaponWorldModel();
|
|
ShowWeaponWorldModel( false ); // don't show right away, wait for the deploy anim to unhide it
|
|
#endif
|
|
|
|
return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), GetDrawActivity(), (char*)GetAnimPrefix() );
|
|
}
|
|
|
|
Activity CBaseCombatWeapon::GetDrawActivity( void )
|
|
{
|
|
CBaseCombatCharacter *pOwner = GetOwner();
|
|
if (pOwner)
|
|
{
|
|
if ( GetReserveAmmoCount( AMMO_POSITION_PRIMARY ) <= 0 && LookupActivity( "ACT_VM_EMPTY_DRAW" ) > 0 )
|
|
{
|
|
return ACT_VM_EMPTY_DRAW;
|
|
}
|
|
}
|
|
return ACT_VM_DRAW;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::Holster( CBaseCombatWeapon *pSwitchingTo )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
|
|
#if !defined( CLIENT_DLL )
|
|
ShowWeaponWorldModel( false );
|
|
//if ( pSwitchingTo )
|
|
// pSwitchingTo->ShowWeaponWorldModel( false ); // redundant - new weapon hides on its own deploy
|
|
#endif
|
|
|
|
// cancel any reload in progress.
|
|
m_bInReload = false;
|
|
|
|
// kill any think functions
|
|
SetThink(NULL);
|
|
|
|
// Send holster animation
|
|
SendWeaponAnim( ACT_VM_HOLSTER );
|
|
|
|
// Some weapon's don't have holster anims yet, so detect that
|
|
float flSequenceDuration = 0;
|
|
if ( GetActivity() == ACT_VM_HOLSTER )
|
|
{
|
|
flSequenceDuration = SequenceDuration();
|
|
}
|
|
|
|
CBaseCombatCharacter *pOwner = GetOwner();
|
|
if (pOwner)
|
|
{
|
|
pOwner->SetNextAttack( gpGlobals->curtime + flSequenceDuration );
|
|
}
|
|
|
|
// If we don't have a holster anim, hide immediately to avoid timing issues
|
|
if ( !flSequenceDuration )
|
|
{
|
|
SetWeaponVisible( false );
|
|
}
|
|
else
|
|
{
|
|
// Hide the weapon when the holster animation's finished
|
|
SetContextThink( &CBaseCombatWeapon::HideThink, gpGlobals->curtime + flSequenceDuration, HIDEWEAPON_THINK_CONTEXT );
|
|
}
|
|
|
|
// if we were displaying a hud hint, squelch it.
|
|
if (m_flHudHintMinDisplayTime && gpGlobals->curtime < m_flHudHintMinDisplayTime)
|
|
{
|
|
if( m_bAltFireHudHintDisplayed )
|
|
RescindAltFireHudHint();
|
|
|
|
if( m_bReloadHudHintDisplayed )
|
|
RescindReloadHudHint();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
void CBaseCombatWeapon::BoneMergeFastCullBloat( Vector &localMins, Vector &localMaxs, const Vector &thisEntityMins, const Vector &thisEntityMaxs ) const
|
|
{
|
|
// The default behavior pushes it out by BONEMERGE_FASTCULL_BBOX_EXPAND in all directions, but we can do better
|
|
// since we know the weapon will never point behind him.
|
|
|
|
localMaxs.x += 20; // Leaves some space in front for long weapons.
|
|
|
|
localMins.y -= 20; // Fatten it to his left and right since he can rotate that way.
|
|
localMaxs.y += 20;
|
|
|
|
localMaxs.z += 15; // Leave some space at the top.
|
|
}
|
|
|
|
#else
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::InputHideWeapon( inputdata_t &inputdata )
|
|
{
|
|
// Only hide if we're still the active weapon. If we're not the active weapon
|
|
if ( GetOwner() && GetOwner()->GetActiveWeapon() == this )
|
|
{
|
|
SetWeaponVisible( false );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::HideThink( void )
|
|
{
|
|
// Only hide if we're still the active weapon. If we're not the active weapon
|
|
if ( GetOwner() && GetOwner()->GetActiveWeapon() == this )
|
|
{
|
|
SetWeaponVisible( false );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::ItemPreFrame( void )
|
|
{
|
|
|
|
MaintainIdealActivity();
|
|
|
|
#ifndef CLIENT_DLL
|
|
#ifndef HL2_EPISODIC
|
|
if ( IsGameConsole() )
|
|
#endif
|
|
{
|
|
// If we haven't displayed the hint enough times yet, it's time to try to
|
|
// display the hint, and the player is not standing still, try to show a hud hint.
|
|
// If the player IS standing still, assume they could change away from this weapon at
|
|
// any second.
|
|
if( (!m_bAltFireHudHintDisplayed || !m_bReloadHudHintDisplayed) && gpGlobals->curtime > m_flHudHintMinDisplayTime && gpGlobals->curtime > m_flHudHintPollTime && GetOwner() && GetOwner()->IsPlayer() )
|
|
{
|
|
CBasePlayer *pPlayer = (CBasePlayer*)(GetOwner());
|
|
|
|
if( pPlayer && pPlayer->GetStickDist() > 0.0f )
|
|
{
|
|
// If the player is moving, they're unlikely to switch away from the current weapon
|
|
// the moment this weapon displays its HUD hint.
|
|
if( ShouldDisplayReloadHUDHint() )
|
|
{
|
|
DisplayReloadHudHint();
|
|
}
|
|
else if( ShouldDisplayAltFireHUDHint() )
|
|
{
|
|
DisplayAltFireHudHint();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_flHudHintPollTime = gpGlobals->curtime + 2.0f;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//====================================================================================
|
|
// WEAPON BEHAVIOUR
|
|
//====================================================================================
|
|
void CBaseCombatWeapon::ItemPostFrame( void )
|
|
{
|
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
|
if (!pOwner)
|
|
return;
|
|
|
|
//Track the duration of the fire
|
|
//FIXME: Check for IN_ATTACK2 as well?
|
|
//FIXME: What if we're calling ItemBusyFrame?
|
|
m_fFireDuration = ( pOwner->m_nButtons & IN_ATTACK ) ? ( m_fFireDuration + gpGlobals->frametime ) : 0.0f;
|
|
|
|
if ( UsesClipsForAmmo1() )
|
|
{
|
|
CheckReload();
|
|
}
|
|
|
|
bool bFired = false;
|
|
|
|
// Secondary attack has priority
|
|
if ((pOwner->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime))
|
|
{
|
|
if (UsesSecondaryAmmo() && GetReserveAmmoCount( AMMO_POSITION_SECONDARY ) <= 0 )
|
|
{
|
|
if (m_flNextEmptySoundTime < gpGlobals->curtime)
|
|
{
|
|
WeaponSound(EMPTY);
|
|
m_flNextSecondaryAttack = m_flNextEmptySoundTime = gpGlobals->curtime + 0.5;
|
|
}
|
|
}
|
|
else if (pOwner->GetWaterLevel() == WL_Eyes && m_bAltFiresUnderwater == false)
|
|
{
|
|
// This weapon doesn't fire underwater
|
|
WeaponSound(EMPTY);
|
|
m_flNextPrimaryAttack = gpGlobals->curtime + 0.2;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// FIXME: This isn't necessarily true if the weapon doesn't have a secondary fire!
|
|
// For instance, the crossbow doesn't have a 'real' secondary fire, but it still
|
|
// stops the crossbow from firing on the 360 if the player chooses to hold down their
|
|
// zoom button. (sjb) Orange Box 7/25/2007
|
|
#if !defined(CLIENT_DLL)
|
|
if( !IsGameConsole() || !ClassMatches("weapon_crossbow") )
|
|
#endif
|
|
{
|
|
bFired = ShouldBlockPrimaryFire();
|
|
}
|
|
|
|
SecondaryAttack();
|
|
|
|
// Secondary ammo doesn't have a reload animation
|
|
if ( UsesClipsForAmmo2() )
|
|
{
|
|
// reload clip2 if empty
|
|
if (m_iClip2 < 1)
|
|
{
|
|
GiveReserveAmmo( AMMO_POSITION_SECONDARY, -1 );
|
|
m_iClip2 = m_iClip2 + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !bFired && (pOwner->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime))
|
|
{
|
|
// Clip empty? Or out of ammo on a no-clip weapon?
|
|
if ( !IsMeleeWeapon() &&
|
|
(( UsesClipsForAmmo1() && m_iClip1 <= 0) || ( !UsesClipsForAmmo1() && GetReserveAmmoCount( AMMO_POSITION_PRIMARY ) <= 0 )) )
|
|
{
|
|
HandleFireOnEmpty();
|
|
}
|
|
else if (pOwner->GetWaterLevel() == WL_Eyes && m_bFiresUnderwater == false)
|
|
{
|
|
// This weapon doesn't fire underwater
|
|
WeaponSound(EMPTY);
|
|
m_flNextPrimaryAttack = gpGlobals->curtime + 0.2;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//NOTENOTE: There is a bug with this code with regards to the way machine guns catch the leading edge trigger
|
|
// on the player hitting the attack key. It relies on the gun catching that case in the same frame.
|
|
// However, because the player can also be doing a secondary attack, the edge trigger may be missed.
|
|
// We really need to hold onto the edge trigger and only clear the condition when the gun has fired its
|
|
// first shot. Right now that's too much of an architecture change -- jdw
|
|
|
|
// If the firing button was just pressed, or the alt-fire just released, reset the firing time
|
|
if ( ( pOwner->m_afButtonPressed & IN_ATTACK ) || ( pOwner->m_afButtonReleased & IN_ATTACK2 ) || ( pOwner->m_afButtonReleased & IN_ZOOM ) )
|
|
{
|
|
m_flNextPrimaryAttack = gpGlobals->curtime;
|
|
}
|
|
|
|
PrimaryAttack();
|
|
#ifdef CLIENT_DLL
|
|
pOwner->SetFiredWeapon( true );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// -----------------------
|
|
// Reload pressed / Clip Empty
|
|
// -----------------------
|
|
if ( (pOwner->m_nButtons & IN_RELOAD) && UsesClipsForAmmo1() && !m_bInReload )
|
|
{
|
|
// reload when reload is pressed, or if no buttons are down and weapon is empty.
|
|
Reload();
|
|
m_fFireDuration = 0.0f;
|
|
}
|
|
|
|
// -----------------------
|
|
// No buttons down
|
|
// -----------------------
|
|
if (!((pOwner->m_nButtons & IN_ATTACK) || (pOwner->m_nButtons & IN_ATTACK2) || ( pOwner->m_nButtons & IN_ZOOM) || ( CanReload() && pOwner->m_nButtons & IN_RELOAD )))
|
|
{
|
|
// no fire buttons down or reloading
|
|
if ( ( m_bInReload == false ) && !ReloadOrSwitchWeapons() )
|
|
{
|
|
WeaponIdle();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CBaseCombatWeapon::HandleFireOnEmpty()
|
|
{
|
|
// If we're already firing on empty, reload if we can
|
|
if ( m_bFireOnEmpty )
|
|
{
|
|
ReloadOrSwitchWeapons();
|
|
m_fFireDuration = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
if (m_flNextEmptySoundTime < gpGlobals->curtime)
|
|
{
|
|
WeaponSound(EMPTY);
|
|
m_flNextEmptySoundTime = gpGlobals->curtime + 0.5;
|
|
}
|
|
m_bFireOnEmpty = true;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Called each frame by the player PostThink, if the player's not ready to attack yet
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::ItemBusyFrame( void )
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Base class default for getting bullet type
|
|
// Input :
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
int CBaseCombatWeapon::GetBulletType( void )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Base class default for getting spread
|
|
// Input :
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
const Vector& CBaseCombatWeapon::GetBulletSpread( void )
|
|
{
|
|
static Vector cone = VECTOR_CONE_15DEGREES;
|
|
return cone;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
const WeaponProficiencyInfo_t *CBaseCombatWeapon::GetProficiencyValues()
|
|
{
|
|
static WeaponProficiencyInfo_t defaultWeaponProficiencyTable[] =
|
|
{
|
|
{ 1.0, 1.0 },
|
|
{ 1.0, 1.0 },
|
|
{ 1.0, 1.0 },
|
|
{ 1.0, 1.0 },
|
|
{ 1.0, 1.0 },
|
|
};
|
|
|
|
COMPILE_TIME_ASSERT( ARRAYSIZE(defaultWeaponProficiencyTable) == WEAPON_PROFICIENCY_PERFECT + 1);
|
|
return defaultWeaponProficiencyTable;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Base class default for getting firerate
|
|
// Input :
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
float CBaseCombatWeapon::GetFireRate( void )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Base class default for playing shoot sound
|
|
// Input :
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::WeaponSound( WeaponSound_t sound_type, float soundtime /* = 0.0f */ )
|
|
{
|
|
// If we have some sounds from the weapon classname.txt file, play a random one of them
|
|
const char *shootsound = GetShootSound( sound_type );
|
|
if ( !shootsound || !shootsound[0] )
|
|
return;
|
|
|
|
CSoundParameters params;
|
|
|
|
if ( !GetParametersForSound( shootsound, params, NULL ) )
|
|
return;
|
|
|
|
if ( params.play_to_owner_only )
|
|
{
|
|
// Am I only to play to my owner?
|
|
if ( GetOwner() && GetOwner()->IsPlayer() )
|
|
{
|
|
CSingleUserRecipientFilter filter( ToBasePlayer( GetOwner() ) );
|
|
if ( IsPredicted() && CBaseEntity::GetPredictionPlayer() )
|
|
{
|
|
filter.UsePredictionRules();
|
|
}
|
|
EmitSound( filter, GetOwner()->entindex(), shootsound, NULL, soundtime );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Play weapon sound from the owner
|
|
if ( GetOwner() )
|
|
{
|
|
CBroadcastRecipientFilter filter;
|
|
EmitSound( filter, GetOwner()->entindex(), shootsound, NULL, soundtime );
|
|
|
|
#if !defined( CLIENT_DLL )
|
|
if( sound_type == EMPTY )
|
|
{
|
|
CSoundEnt::InsertSound( SOUND_COMBAT, GetOwner()->GetAbsOrigin(), SOUNDENT_VOLUME_EMPTY, 0.2, GetOwner() );
|
|
}
|
|
#endif
|
|
}
|
|
// If no owner play from the weapon (this is used for thrown items)
|
|
else
|
|
{
|
|
CBroadcastRecipientFilter filter;
|
|
EmitSound( filter, entindex(), shootsound, NULL, soundtime );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Stop a sound played by this weapon.
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::StopWeaponSound( WeaponSound_t sound_type )
|
|
{
|
|
//if ( IsPredicted() )
|
|
// return;
|
|
|
|
// If we have some sounds from the weapon classname.txt file, play a random one of them
|
|
const char *shootsound = GetShootSound( sound_type );
|
|
if ( !shootsound || !shootsound[0] )
|
|
return;
|
|
|
|
CSoundParameters params;
|
|
if ( !GetParametersForSound( shootsound, params, NULL ) )
|
|
return;
|
|
|
|
// Am I only to play to my owner?
|
|
if ( params.play_to_owner_only )
|
|
{
|
|
if ( GetOwner() )
|
|
{
|
|
StopSound( GetOwner()->entindex(), shootsound );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Play weapon sound from the owner
|
|
if ( GetOwner() )
|
|
{
|
|
StopSound( GetOwner()->entindex(), shootsound );
|
|
}
|
|
// If no owner play from the weapon (this is used for thrown items)
|
|
else
|
|
{
|
|
StopSound( entindex(), shootsound );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::DefaultReload( int iClipSize1, int iClipSize2, int iActivity )
|
|
{
|
|
CBaseCombatCharacter *pOwner = GetOwner();
|
|
if (!pOwner)
|
|
return false;
|
|
|
|
// If I don't have any spare ammo, I can't reload
|
|
if ( GetReserveAmmoCount( AMMO_POSITION_PRIMARY ) <= 0 )
|
|
return false;
|
|
|
|
bool bReload = false;
|
|
|
|
// If you don't have clips, then don't try to reload them.
|
|
if ( UsesClipsForAmmo1() )
|
|
{
|
|
// need to reload primary clip?
|
|
int primary = MIN(iClipSize1 - m_iClip1, GetReserveAmmoCount( AMMO_POSITION_PRIMARY ) );
|
|
if ( primary != 0 )
|
|
{
|
|
bReload = true;
|
|
}
|
|
}
|
|
|
|
if ( UsesClipsForAmmo2() )
|
|
{
|
|
// need to reload secondary clip?
|
|
int secondary = MIN(iClipSize2 - m_iClip2, GetReserveAmmoCount( AMMO_POSITION_SECONDARY ) );
|
|
if ( secondary != 0 )
|
|
{
|
|
bReload = true;
|
|
}
|
|
}
|
|
|
|
if ( !bReload )
|
|
return false;
|
|
|
|
#ifdef CLIENT_DLL
|
|
// Play reload
|
|
WeaponSound( RELOAD );
|
|
#endif
|
|
SendWeaponAnim( iActivity );
|
|
|
|
// Play the player's reload animation
|
|
if ( pOwner->IsPlayer() )
|
|
{
|
|
( ( CBasePlayer * )pOwner)->SetAnimation( PLAYER_RELOAD );
|
|
}
|
|
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
float flSequenceEndTime = gpGlobals->curtime + SequenceDuration();
|
|
pOwner->SetNextAttack( flSequenceEndTime );
|
|
m_flNextPrimaryAttack = m_flNextSecondaryAttack = flSequenceEndTime;
|
|
|
|
m_bInReload = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::Reload( void )
|
|
{
|
|
return DefaultReload( GetMaxClip1(), GetMaxClip2(), m_iReloadActivityIndex );
|
|
}
|
|
|
|
//=========================================================
|
|
void CBaseCombatWeapon::WeaponIdle( void )
|
|
{
|
|
//Idle again if we've finished
|
|
if ( HasWeaponIdleTimeElapsed() )
|
|
{
|
|
SendWeaponAnim( ACT_VM_IDLE );
|
|
}
|
|
}
|
|
|
|
|
|
//=========================================================
|
|
Activity CBaseCombatWeapon::GetPrimaryAttackActivity( void )
|
|
{
|
|
return ACT_VM_PRIMARYATTACK;
|
|
}
|
|
|
|
//=========================================================
|
|
Activity CBaseCombatWeapon::GetSecondaryAttackActivity( void )
|
|
{
|
|
return ACT_VM_SECONDARYATTACK;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Adds in view kick and weapon accuracy degradation effect
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::AddViewKick( void )
|
|
{
|
|
//NOTENOTE: By default, weapon will not kick up (defined per weapon)
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Get the string to print death notices with
|
|
//-----------------------------------------------------------------------------
|
|
char *CBaseCombatWeapon::GetDeathNoticeName( void )
|
|
{
|
|
#if !defined( CLIENT_DLL )
|
|
return (char*)STRING( m_iszName );
|
|
#else
|
|
return "GetDeathNoticeName not implemented on client yet";
|
|
#endif
|
|
}
|
|
|
|
//====================================================================================
|
|
// WEAPON RELOAD TYPES
|
|
//====================================================================================
|
|
void CBaseCombatWeapon::CheckReload( void )
|
|
{
|
|
if ( m_bReloadsSingly )
|
|
{
|
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
|
if ( !pOwner )
|
|
return;
|
|
|
|
if ((m_bInReload) && (m_flNextPrimaryAttack <= gpGlobals->curtime))
|
|
{
|
|
if ( pOwner->m_nButtons & (IN_ATTACK | IN_ATTACK2 | IN_ZOOM ) && m_iClip1 > 0 )
|
|
{
|
|
m_bInReload = false;
|
|
return;
|
|
}
|
|
|
|
// If out of ammo end reload
|
|
if ( GetReserveAmmoCount( AMMO_POSITION_PRIMARY ) <=0 )
|
|
{
|
|
FinishReload();
|
|
return;
|
|
}
|
|
// If clip not full reload again
|
|
else if (m_iClip1 < GetMaxClip1())
|
|
{
|
|
// Add them to the clip
|
|
m_iClip1 += 1;
|
|
GiveReserveAmmo( AMMO_POSITION_PRIMARY, - 1 );
|
|
|
|
Reload();
|
|
return;
|
|
}
|
|
// Clip full, stop reloading
|
|
else
|
|
{
|
|
FinishReload();
|
|
m_flNextPrimaryAttack = gpGlobals->curtime;
|
|
m_flNextSecondaryAttack = gpGlobals->curtime;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( (m_bInReload) && (m_flNextPrimaryAttack <= gpGlobals->curtime))
|
|
{
|
|
FinishReload();
|
|
m_flNextPrimaryAttack = gpGlobals->curtime;
|
|
m_flNextSecondaryAttack = gpGlobals->curtime;
|
|
m_bInReload = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Reload has finished.
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::FinishReload( void )
|
|
{
|
|
CBaseCombatCharacter *pOwner = GetOwner();
|
|
|
|
if (pOwner)
|
|
{
|
|
// If I use primary clips, reload primary
|
|
if ( UsesClipsForAmmo1() )
|
|
{
|
|
int primary = MIN( GetMaxClip1() - m_iClip1, GetReserveAmmoCount( AMMO_POSITION_PRIMARY ) );
|
|
m_iClip1 += primary;
|
|
GiveReserveAmmo( AMMO_POSITION_PRIMARY, -primary );
|
|
}
|
|
|
|
// If I use secondary clips, reload secondary
|
|
if ( UsesClipsForAmmo2() )
|
|
{
|
|
int secondary = MIN( GetMaxClip2() - m_iClip2, GetReserveAmmoCount( AMMO_POSITION_SECONDARY ) );
|
|
m_iClip2 += secondary;
|
|
GiveReserveAmmo( AMMO_POSITION_SECONDARY, -secondary );
|
|
}
|
|
|
|
if ( m_bReloadsSingly )
|
|
{
|
|
m_bInReload = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Abort any reload we have in progress
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::AbortReload( void )
|
|
{
|
|
#ifdef CLIENT_DLL
|
|
StopWeaponSound( RELOAD );
|
|
#endif
|
|
m_bInReload = false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Primary fire button attack
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::PrimaryAttack( void )
|
|
{
|
|
// If my clip is empty (and I use clips) start reload
|
|
if ( UsesClipsForAmmo1() && !m_iClip1 )
|
|
{
|
|
m_iNumEmptyAttacks++;
|
|
Reload();
|
|
return;
|
|
}
|
|
|
|
// Only the player fires this way so we can cast
|
|
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
|
|
|
if (!pPlayer)
|
|
{
|
|
return;
|
|
}
|
|
|
|
pPlayer->DoMuzzleFlash();
|
|
|
|
SendWeaponAnim( GetPrimaryAttackActivity() );
|
|
|
|
// player "shoot" animation
|
|
pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
|
|
|
FireBulletsInfo_t info;
|
|
info.m_vecSrc = pPlayer->Weapon_ShootPosition( );
|
|
|
|
info.m_vecDirShooting = pPlayer->GetAutoaimVector( AUTOAIM_SCALE_DEFAULT );
|
|
|
|
float flFishtail = GetAccuracyFishtail();
|
|
if ( flFishtail != 0.0f )
|
|
{
|
|
QAngle angShootAngles;
|
|
VectorAngles( info.m_vecDirShooting, angShootAngles );
|
|
angShootAngles.y += flFishtail;
|
|
AngleVectors( angShootAngles, &info.m_vecDirShooting );
|
|
}
|
|
|
|
// To make the firing framerate independent, we may have to fire more than one bullet here on low-framerate systems,
|
|
// especially if the weapon we're firing has a really fast rate of fire.
|
|
info.m_iShots = 0;
|
|
float fireRate = GetFireRate();
|
|
|
|
while ( m_flNextPrimaryAttack <= gpGlobals->curtime )
|
|
{
|
|
// MUST call sound before removing a round from the clip of a CMachineGun
|
|
WeaponSound(SINGLE, m_flNextPrimaryAttack);
|
|
m_flNextPrimaryAttack = m_flNextPrimaryAttack + fireRate;
|
|
info.m_iShots++;
|
|
if ( !fireRate )
|
|
break;
|
|
}
|
|
|
|
// Make sure we don't fire more than the amount in the clip
|
|
if ( UsesClipsForAmmo1() )
|
|
{
|
|
info.m_iShots = MIN( info.m_iShots, m_iClip1.Get() );
|
|
m_iClip1 -= info.m_iShots;
|
|
}
|
|
else
|
|
{
|
|
info.m_iShots = MIN( info.m_iShots, GetReserveAmmoCount( AMMO_POSITION_PRIMARY ) );
|
|
GiveReserveAmmo( AMMO_POSITION_PRIMARY, -info.m_iShots );
|
|
}
|
|
|
|
info.m_flDistance = MAX_TRACE_LENGTH;
|
|
info.m_iAmmoType = m_iPrimaryAmmoType;
|
|
info.m_iTracerFreq = 2;
|
|
|
|
#if !defined( CLIENT_DLL )
|
|
// Fire the bullets
|
|
info.m_vecSpread = pPlayer->GetAttackSpread( this );
|
|
#else
|
|
//!!!HACKHACK - what does the client want this function for?
|
|
info.m_vecSpread = pPlayer->GetActiveWeapon()->GetBulletSpread();
|
|
#endif // CLIENT_DLL
|
|
|
|
pPlayer->FireBullets( info );
|
|
|
|
if (!m_iClip1 && GetReserveAmmoCount( AMMO_POSITION_PRIMARY ) <= 0 )
|
|
{
|
|
// HEV suit - indicate out of ammo condition
|
|
pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
|
|
}
|
|
|
|
//Add our view kick in
|
|
AddViewKick();
|
|
}
|
|
|
|
void CBaseCombatWeapon::BaseForceFire( CBaseCombatCharacter *pOperator, CBaseEntity *pTarget )
|
|
{
|
|
// Ensure we have enough rounds in the clip
|
|
m_iClip1++;
|
|
|
|
// If my clip is empty (and I use clips) start reload
|
|
if ( UsesClipsForAmmo1() && !m_iClip1 )
|
|
{
|
|
Reload();
|
|
return;
|
|
}
|
|
|
|
pOperator->DoMuzzleFlash();
|
|
|
|
SendWeaponAnim( GetPrimaryAttackActivity() );
|
|
|
|
// player "shoot" animation
|
|
//pOperator->SetAnimation( PLAYER_ATTACK1 );
|
|
|
|
FireBulletsInfo_t info;
|
|
|
|
QAngle angShootDir;
|
|
GetAttachment( LookupAttachment( "muzzle" ), info.m_vecSrc, angShootDir );
|
|
|
|
if ( pTarget )
|
|
{
|
|
info.m_vecDirShooting = pTarget->WorldSpaceCenter() - info.m_vecSrc;
|
|
VectorNormalize( info.m_vecDirShooting );
|
|
}
|
|
else
|
|
{
|
|
AngleVectors( angShootDir, &info.m_vecDirShooting );
|
|
}
|
|
|
|
// To make the firing framerate independent, we may have to fire more than one bullet here on low-framerate systems,
|
|
// especially if the weapon we're firing has a really fast rate of fire.
|
|
info.m_iShots = 0;
|
|
float fireRate = GetFireRate();
|
|
|
|
while ( m_flNextPrimaryAttack <= gpGlobals->curtime )
|
|
{
|
|
// MUST call sound before removing a round from the clip of a CMachineGun
|
|
WeaponSound(SINGLE, m_flNextPrimaryAttack);
|
|
m_flNextPrimaryAttack = m_flNextPrimaryAttack + fireRate;
|
|
info.m_iShots++;
|
|
if ( !fireRate )
|
|
break;
|
|
}
|
|
|
|
// Make sure we don't fire more than the amount in the clip
|
|
if ( UsesClipsForAmmo1() )
|
|
{
|
|
info.m_iShots = Min( info.m_iShots, m_iClip1.Get() );
|
|
m_iClip1 -= info.m_iShots;
|
|
}
|
|
else
|
|
{
|
|
info.m_iShots = Min( info.m_iShots, GetReserveAmmoCount( AMMO_POSITION_PRIMARY ) );
|
|
GiveReserveAmmo( AMMO_POSITION_PRIMARY, -info.m_iShots );
|
|
}
|
|
|
|
info.m_flDistance = MAX_TRACE_LENGTH;
|
|
info.m_iAmmoType = m_iPrimaryAmmoType;
|
|
info.m_iTracerFreq = 2;
|
|
|
|
#if !defined( CLIENT_DLL )
|
|
// Fire the bullets
|
|
info.m_vecSpread = pOperator->GetAttackSpread( this );
|
|
#else
|
|
//!!!HACKHACK - what does the client want this function for?
|
|
info.m_vecSpread = GetBulletSpread();
|
|
#endif // CLIENT_DLL
|
|
|
|
pOperator->FireBullets( info );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Called every frame to check if the weapon is going through transition animations
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::MaintainIdealActivity( void )
|
|
{
|
|
// Must be transitioning
|
|
if ( GetActivity() != ACT_TRANSITION )
|
|
return;
|
|
|
|
// Must not be at our ideal already
|
|
if ( ( GetActivity() == m_IdealActivity ) && ( GetSequence() == m_nIdealSequence ) )
|
|
return;
|
|
|
|
// Must be finished with the current animation
|
|
if ( IsViewModelSequenceFinished() == false )
|
|
return;
|
|
|
|
// Move to the next animation towards our ideal
|
|
SendWeaponAnim( m_IdealActivity );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Sets the ideal activity for the weapon to be in, allowing for transitional animations in between
|
|
// Input : ideal - activity to end up at, ideally
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::SetIdealActivity( Activity ideal )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
int idealSequence = SelectWeightedSequence( ideal );
|
|
|
|
if ( idealSequence == -1 )
|
|
return false;
|
|
|
|
//Take the new activity
|
|
m_IdealActivity = ideal;
|
|
m_nIdealSequence = idealSequence;
|
|
|
|
//Find the next sequence in the potential chain of sequences leading to our ideal one
|
|
int nextSequence = FindTransitionSequence( GetSequence(), m_nIdealSequence, NULL );
|
|
|
|
// Don't use transitions when we're deploying
|
|
if ( ideal != ACT_VM_DRAW && ideal != ACT_VM_EMPTY_DRAW && IsWeaponVisible() && nextSequence != m_nIdealSequence )
|
|
{
|
|
//Set our activity to the next transitional animation
|
|
SetActivity( ACT_TRANSITION );
|
|
SetSequence( nextSequence );
|
|
SendViewModelAnim( nextSequence );
|
|
}
|
|
else
|
|
{
|
|
//Set our activity to the ideal
|
|
SetActivity( m_IdealActivity );
|
|
SetSequence( m_nIdealSequence );
|
|
SendViewModelAnim( m_nIdealSequence );
|
|
}
|
|
|
|
//Set the next time the weapon will idle
|
|
SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns information about the various control panels
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
|
|
{
|
|
pPanelName = NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns information about the various control panels
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::GetControlPanelClassName( int nPanelIndex, const char *&pPanelName )
|
|
{
|
|
pPanelName = "vgui_screen";
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Locking a weapon is an exclusive action. If you lock a weapon, that means
|
|
// you are preventing others from doing so for themselves.
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseCombatWeapon::Lock( float lockTime, CBaseEntity *pLocker )
|
|
{
|
|
m_flUnlockTime = gpGlobals->curtime + lockTime;
|
|
m_hLocker.Set( pLocker );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// If I'm still locked for a period of time, tell everyone except the person
|
|
// that locked me that I'm not available.
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseCombatWeapon::IsLocked( CBaseEntity *pAsker )
|
|
{
|
|
return ( m_flUnlockTime > gpGlobals->curtime && m_hLocker != pAsker );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input :
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
Activity CBaseCombatWeapon::ActivityOverride( Activity baseAct, bool *pRequired )
|
|
{
|
|
acttable_t *pTable = ActivityList();
|
|
int actCount = ActivityListCount();
|
|
|
|
for ( int i = 0; i < actCount; i++, pTable++ )
|
|
{
|
|
if ( baseAct == pTable->baseAct )
|
|
{
|
|
if (pRequired)
|
|
{
|
|
*pRequired = pTable->required;
|
|
}
|
|
return (Activity)pTable->weaponAct;
|
|
}
|
|
}
|
|
return baseAct;
|
|
}
|
|
|
|
class CWeaponList : public CAutoGameSystem
|
|
{
|
|
public:
|
|
CWeaponList( char const *name ) : CAutoGameSystem( name )
|
|
{
|
|
}
|
|
|
|
|
|
virtual void LevelShutdownPostEntity()
|
|
{
|
|
m_list.Purge();
|
|
}
|
|
|
|
void AddWeapon( CBaseCombatWeapon *pWeapon )
|
|
{
|
|
m_list.AddToTail( pWeapon );
|
|
}
|
|
|
|
void RemoveWeapon( CBaseCombatWeapon *pWeapon )
|
|
{
|
|
m_list.FindAndRemove( pWeapon );
|
|
}
|
|
CUtlLinkedList< CBaseCombatWeapon * > m_list;
|
|
};
|
|
|
|
CWeaponList g_WeaponList( "CWeaponList" );
|
|
|
|
#ifndef CLIENT_DLL
|
|
void OnBaseCombatWeaponCreated( CBaseCombatWeapon *pWeapon )
|
|
{
|
|
g_WeaponList.AddWeapon( pWeapon );
|
|
}
|
|
|
|
void OnBaseCombatWeaponDestroyed( CBaseCombatWeapon *pWeapon )
|
|
{
|
|
g_WeaponList.RemoveWeapon( pWeapon );
|
|
}
|
|
#endif
|
|
|
|
#ifdef CLIENT_DLL
|
|
CUtlLinkedList< CBaseCombatWeapon * >& CBaseCombatWeapon::GetWeaponList( void )
|
|
{
|
|
return g_WeaponList.m_list;
|
|
}
|
|
#else
|
|
int CBaseCombatWeapon::GetAvailableWeaponsInBox( CBaseCombatWeapon **pList, int listMax, const Vector &mins, const Vector &maxs )
|
|
{
|
|
// linear search all weapons
|
|
int count = 0;
|
|
int index = g_WeaponList.m_list.Head();
|
|
while ( index != g_WeaponList.m_list.InvalidIndex() )
|
|
{
|
|
CBaseCombatWeapon *pWeapon = g_WeaponList.m_list[index];
|
|
// skip any held weapon
|
|
if ( !pWeapon->GetOwner() )
|
|
{
|
|
// restrict to mins/maxs
|
|
if ( IsPointInBox( pWeapon->GetAbsOrigin(), mins, maxs ) )
|
|
{
|
|
if ( count < listMax )
|
|
{
|
|
pList[count] = pWeapon;
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
index = g_WeaponList.m_list.Next( index );
|
|
}
|
|
|
|
return count;
|
|
}
|
|
#endif
|
|
|
|
|
|
#if defined( CLIENT_DLL )
|
|
|
|
BEGIN_PREDICTION_DATA( CBaseCombatWeapon )
|
|
|
|
DEFINE_PRED_FIELD( m_nNextThinkTick, FIELD_INTEGER, FTYPEDESC_INSENDTABLE | FTYPEDESC_NOERRORCHECK ),
|
|
// Networked
|
|
DEFINE_PRED_FIELD( m_hOwner, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
|
|
// DEFINE_FIELD( m_hWeaponFileInfo, FIELD_SHORT ),
|
|
DEFINE_PRED_FIELD( m_iState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
|
|
DEFINE_PRED_FIELD( m_iViewModelIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE | FTYPEDESC_MODELINDEX ),
|
|
DEFINE_PRED_FIELD( m_iWorldModelIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE | FTYPEDESC_MODELINDEX ),
|
|
DEFINE_PRED_FIELD( m_iWorldDroppedModelIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE | FTYPEDESC_MODELINDEX ),
|
|
DEFINE_PRED_FIELD_TOL( m_flNextPrimaryAttack, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
|
|
DEFINE_PRED_FIELD_TOL( m_flNextSecondaryAttack, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
|
|
DEFINE_PRED_FIELD_TOL( m_flTimeWeaponIdle, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
|
|
|
|
DEFINE_PRED_FIELD( m_iPrimaryAmmoType, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
|
|
DEFINE_PRED_FIELD( m_iSecondaryAmmoType, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
|
|
DEFINE_PRED_FIELD( m_iClip1, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
|
|
DEFINE_PRED_FIELD( m_iClip2, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
|
|
|
|
DEFINE_PRED_FIELD( m_nViewModelIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
|
|
|
|
DEFINE_PRED_FIELD( m_iWeaponModule, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
|
|
DEFINE_PRED_FIELD( m_iPrimaryReserveAmmoCount, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
|
|
DEFINE_PRED_FIELD( m_iSecondaryReserveAmmoCount, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
|
|
|
|
DEFINE_PRED_FIELD( m_iNumEmptyAttacks, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
|
|
|
|
// Not networked
|
|
|
|
DEFINE_FIELD( m_bInReload, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_bFireOnEmpty, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_flNextEmptySoundTime, FIELD_FLOAT ),
|
|
DEFINE_FIELD( m_Activity, FIELD_INTEGER ),
|
|
DEFINE_FIELD( m_fFireDuration, FIELD_FLOAT ),
|
|
DEFINE_FIELD( m_iszName, FIELD_INTEGER ),
|
|
DEFINE_FIELD( m_bFiresUnderwater, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_bAltFiresUnderwater, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_fMinRange1, FIELD_FLOAT ),
|
|
DEFINE_FIELD( m_fMinRange2, FIELD_FLOAT ),
|
|
DEFINE_FIELD( m_fMaxRange1, FIELD_FLOAT ),
|
|
DEFINE_FIELD( m_fMaxRange2, FIELD_FLOAT ),
|
|
DEFINE_FIELD( m_bReloadsSingly, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_bRemoveable, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_iPrimaryAmmoCount, FIELD_INTEGER ),
|
|
DEFINE_FIELD( m_iSecondaryAmmoCount, FIELD_INTEGER ),
|
|
|
|
//DEFINE_PHYSPTR( m_pConstraint ),
|
|
|
|
// DEFINE_FIELD( m_iOldState, FIELD_INTEGER ),
|
|
// DEFINE_FIELD( m_bJustRestored, FIELD_BOOLEAN ),
|
|
|
|
// DEFINE_FIELD( m_OnPlayerPickup, COutputEvent ),
|
|
// DEFINE_FIELD( m_pConstraint, FIELD_INTEGER ),
|
|
|
|
END_PREDICTION_DATA()
|
|
|
|
#endif // ! CLIENT_DLL
|
|
|
|
// Special hack since we're aliasing the name C_BaseCombatWeapon with a macro on the client
|
|
IMPLEMENT_NETWORKCLASS_ALIASED( BaseCombatWeapon, DT_BaseCombatWeapon )
|
|
|
|
#if !defined( CLIENT_DLL )
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Save Data for Base Weapon object
|
|
//-----------------------------------------------------------------------------//
|
|
BEGIN_DATADESC( CBaseCombatWeapon )
|
|
|
|
|
|
DEFINE_FIELD( m_flNextPrimaryAttack, FIELD_TIME ),
|
|
DEFINE_FIELD( m_flNextSecondaryAttack, FIELD_TIME ),
|
|
DEFINE_FIELD( m_flTimeWeaponIdle, FIELD_TIME ),
|
|
|
|
DEFINE_FIELD( m_bInReload, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_bFireOnEmpty, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
|
|
|
|
DEFINE_FIELD( m_iState, FIELD_INTEGER ),
|
|
DEFINE_FIELD( m_iszName, FIELD_STRING ),
|
|
DEFINE_FIELD( m_iPrimaryAmmoType, FIELD_INTEGER ),
|
|
DEFINE_FIELD( m_iSecondaryAmmoType, FIELD_INTEGER ),
|
|
DEFINE_FIELD( m_iClip1, FIELD_INTEGER ),
|
|
DEFINE_FIELD( m_iClip2, FIELD_INTEGER ),
|
|
DEFINE_FIELD( m_bFiresUnderwater, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_bAltFiresUnderwater, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_fMinRange1, FIELD_FLOAT ),
|
|
DEFINE_FIELD( m_fMinRange2, FIELD_FLOAT ),
|
|
DEFINE_FIELD( m_fMaxRange1, FIELD_FLOAT ),
|
|
DEFINE_FIELD( m_fMaxRange2, FIELD_FLOAT ),
|
|
|
|
DEFINE_FIELD( m_iPrimaryAmmoCount, FIELD_INTEGER ),
|
|
DEFINE_FIELD( m_iSecondaryAmmoCount, FIELD_INTEGER ),
|
|
|
|
DEFINE_FIELD( m_nViewModelIndex, FIELD_INTEGER ),
|
|
|
|
DEFINE_FIELD( m_iWeaponModule, FIELD_INTEGER ),
|
|
|
|
// don't save these, init to 0 and regenerate
|
|
// DEFINE_FIELD( m_flNextEmptySoundTime, FIELD_TIME ),
|
|
// DEFINE_FIELD( m_Activity, FIELD_INTEGER ),
|
|
DEFINE_FIELD( m_nIdealSequence, FIELD_INTEGER ),
|
|
DEFINE_FIELD( m_IdealActivity, FIELD_INTEGER ),
|
|
|
|
DEFINE_FIELD( m_fFireDuration, FIELD_FLOAT ),
|
|
|
|
DEFINE_FIELD( m_bReloadsSingly, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_iSubType, FIELD_INTEGER ),
|
|
DEFINE_FIELD( m_bRemoveable, FIELD_BOOLEAN ),
|
|
|
|
DEFINE_FIELD( m_flUnlockTime, FIELD_TIME ),
|
|
DEFINE_FIELD( m_hLocker, FIELD_EHANDLE ),
|
|
|
|
// DEFINE_FIELD( m_iViewModelIndex, FIELD_INTEGER ),
|
|
// DEFINE_FIELD( m_iWorldModelIndex, FIELD_INTEGER ),
|
|
// DEFINE_FIELD( m_hWeaponFileInfo, ???? ),
|
|
|
|
DEFINE_PHYSPTR( m_pConstraint ),
|
|
|
|
DEFINE_FIELD( m_iReloadHudHintCount, FIELD_INTEGER ),
|
|
DEFINE_FIELD( m_iAltFireHudHintCount, FIELD_INTEGER ),
|
|
DEFINE_FIELD( m_bReloadHudHintDisplayed, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_bAltFireHudHintDisplayed, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_flHudHintPollTime, FIELD_TIME ),
|
|
DEFINE_FIELD( m_flHudHintMinDisplayTime, FIELD_TIME ),
|
|
|
|
// Just to quiet classcheck.. this field exists only on the client
|
|
// DEFINE_FIELD( m_iOldState, FIELD_INTEGER ),
|
|
// DEFINE_FIELD( m_bJustRestored, FIELD_BOOLEAN ),
|
|
|
|
// Function pointers
|
|
DEFINE_ENTITYFUNC( DefaultTouch ),
|
|
DEFINE_THINKFUNC( FallThink ),
|
|
DEFINE_THINKFUNC( Materialize ),
|
|
DEFINE_THINKFUNC( AttemptToMaterialize ),
|
|
DEFINE_THINKFUNC( DestroyItem ),
|
|
DEFINE_THINKFUNC( SetPickupTouch ),
|
|
|
|
DEFINE_THINKFUNC( HideThink ),
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "HideWeapon", InputHideWeapon ),
|
|
|
|
// Outputs
|
|
DEFINE_OUTPUT( m_OnPlayerUse, "OnPlayerUse"),
|
|
DEFINE_OUTPUT( m_OnPlayerPickup, "OnPlayerPickup"),
|
|
DEFINE_OUTPUT( m_OnNPCPickup, "OnNPCPickup"),
|
|
DEFINE_OUTPUT( m_OnCacheInteraction, "OnCacheInteraction" ),
|
|
|
|
END_DATADESC()
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Only send to local player if this weapon is the active weapon
|
|
// Input : *pStruct -
|
|
// *pVarData -
|
|
// *pRecipients -
|
|
// objectID -
|
|
// Output : void*
|
|
//-----------------------------------------------------------------------------
|
|
void* SendProxy_SendActiveLocalWeaponDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
|
|
{
|
|
// Get the weapon entity
|
|
CBaseCombatWeapon *pWeapon = (CBaseCombatWeapon*)pVarData;
|
|
if ( pWeapon )
|
|
{
|
|
// Only send this chunk of data to the player carrying this weapon
|
|
CBasePlayer *pPlayer = ToBasePlayer( pWeapon->GetOwner() );
|
|
if ( pPlayer /*&& pPlayer->GetActiveWeapon() == pWeapon*/ )
|
|
{
|
|
pRecipients->SetOnly( pPlayer->GetClientIndex() );
|
|
return (void*)pVarData;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendActiveLocalWeaponDataTable );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Only send the LocalWeaponData to the player carrying the weapon
|
|
//-----------------------------------------------------------------------------
|
|
void* SendProxy_SendLocalWeaponDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
|
|
{
|
|
// Get the weapon entity
|
|
CBaseCombatWeapon *pWeapon = (CBaseCombatWeapon*)pVarData;
|
|
if ( pWeapon )
|
|
{
|
|
// Only send this chunk of data to the player carrying this weapon
|
|
CBasePlayer *pPlayer = ToBasePlayer( pWeapon->GetOwner() );
|
|
if ( pPlayer )
|
|
{
|
|
pRecipients->SetOnly( pPlayer->GetClientIndex() );
|
|
return (void*)pVarData;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendLocalWeaponDataTable );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Only send to non-local players
|
|
//-----------------------------------------------------------------------------
|
|
void* SendProxy_SendNonLocalWeaponDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
|
|
{
|
|
pRecipients->SetAllRecipients();
|
|
|
|
CBaseCombatWeapon *pWeapon = (CBaseCombatWeapon*)pVarData;
|
|
if ( pWeapon )
|
|
{
|
|
CBasePlayer *pPlayer = ToBasePlayer( pWeapon->GetOwner() );
|
|
if ( pPlayer )
|
|
{
|
|
pRecipients->ClearRecipient( pPlayer->GetClientIndex() );
|
|
return ( void * )pVarData;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendNonLocalWeaponDataTable );
|
|
|
|
#endif
|
|
|
|
#if PREDICTION_ERROR_CHECK_LEVEL > 1
|
|
#define SendPropTime SendPropFloat
|
|
#define RecvPropTime RecvPropFloat
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Propagation data for weapons. Only sent when a player's holding it.
|
|
//-----------------------------------------------------------------------------
|
|
BEGIN_NETWORK_TABLE_NOBASE( CBaseCombatWeapon, DT_LocalActiveWeaponData )
|
|
#if !defined( CLIENT_DLL )
|
|
SendPropTime( SENDINFO( m_flNextPrimaryAttack ) ),
|
|
SendPropTime( SENDINFO( m_flNextSecondaryAttack ) ),
|
|
SendPropInt( SENDINFO( m_nNextThinkTick ) ),
|
|
SendPropTime( SENDINFO( m_flTimeWeaponIdle ) ),
|
|
|
|
#if defined( TF_DLL )
|
|
SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
|
|
#endif
|
|
|
|
#else
|
|
RecvPropTime( RECVINFO( m_flNextPrimaryAttack ) ),
|
|
RecvPropTime( RECVINFO( m_flNextSecondaryAttack ) ),
|
|
RecvPropInt( RECVINFO( m_nNextThinkTick ) ),
|
|
RecvPropTime( RECVINFO( m_flTimeWeaponIdle ) ),
|
|
#endif
|
|
END_NETWORK_TABLE()
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Propagation data for weapons. Only sent when a player's holding it.
|
|
//-----------------------------------------------------------------------------
|
|
BEGIN_NETWORK_TABLE_NOBASE( CBaseCombatWeapon, DT_LocalWeaponData )
|
|
#if !defined( CLIENT_DLL )
|
|
SendPropInt( SENDINFO(m_iPrimaryAmmoType ), 8 ),
|
|
SendPropInt( SENDINFO(m_iSecondaryAmmoType ), 8 ),
|
|
|
|
SendPropInt( SENDINFO( m_nViewModelIndex ), VIEWMODEL_INDEX_BITS, SPROP_UNSIGNED ),
|
|
|
|
SendPropInt( SENDINFO( m_bFlipViewModel ) ),
|
|
|
|
SendPropInt( SENDINFO( m_iWeaponOrigin ) ),
|
|
SendPropInt( SENDINFO(m_iWeaponModule), 8),
|
|
|
|
#if defined( TF_DLL )
|
|
SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
|
|
#endif
|
|
|
|
#else
|
|
RecvPropInt( RECVINFO(m_iPrimaryAmmoType )),
|
|
RecvPropInt( RECVINFO(m_iSecondaryAmmoType )),
|
|
|
|
RecvPropInt( RECVINFO( m_nViewModelIndex ) ),
|
|
|
|
RecvPropBool( RECVINFO( m_bFlipViewModel ) ),
|
|
|
|
RecvPropInt( RECVINFO( m_iWeaponOrigin ) ),
|
|
RecvPropInt( RECVINFO(m_iWeaponModule)),
|
|
|
|
#endif
|
|
END_NETWORK_TABLE()
|
|
|
|
|
|
#if defined( CLIENT_DLL )
|
|
|
|
void RecvProxy_State( const CRecvProxyData *pData, void *pStruct, void *pOut )
|
|
{
|
|
*(int *)pOut = pData->m_Value.m_Int;
|
|
( (C_BaseEntity*) pStruct )->UpdateVisibility();
|
|
}
|
|
|
|
#endif
|
|
|
|
BEGIN_NETWORK_TABLE(CBaseCombatWeapon, DT_BaseCombatWeapon)
|
|
#if !defined( CLIENT_DLL )
|
|
SendPropDataTable("LocalWeaponData", 0, &REFERENCE_SEND_TABLE(DT_LocalWeaponData), SendProxy_SendLocalWeaponDataTable ),
|
|
SendPropDataTable("LocalActiveWeaponData", 0, &REFERENCE_SEND_TABLE(DT_LocalActiveWeaponData), SendProxy_SendActiveLocalWeaponDataTable ),
|
|
SendPropModelIndex( SENDINFO(m_iViewModelIndex) ),
|
|
SendPropModelIndex( SENDINFO(m_iWorldModelIndex) ),
|
|
SendPropModelIndex( SENDINFO(m_iWorldDroppedModelIndex) ),
|
|
SendPropInt( SENDINFO( m_iState ), 2, SPROP_UNSIGNED ),
|
|
SendPropEHandle( SENDINFO(m_hOwner) ),
|
|
SendPropIntWithMinusOneFlag( SENDINFO(m_iClip1 ), 8 ),
|
|
SendPropIntWithMinusOneFlag( SENDINFO(m_iClip2 ), 8 ),
|
|
|
|
SendPropInt( SENDINFO( m_iPrimaryReserveAmmoCount ), 10),
|
|
SendPropInt( SENDINFO( m_iSecondaryReserveAmmoCount), 10),
|
|
SendPropEHandle( SENDINFO(m_hWeaponWorldModel) ),
|
|
SendPropInt( SENDINFO( m_iNumEmptyAttacks ), 8 ),
|
|
#else
|
|
RecvPropDataTable("LocalWeaponData", 0, 0, &REFERENCE_RECV_TABLE(DT_LocalWeaponData)),
|
|
RecvPropDataTable("LocalActiveWeaponData", 0, 0, &REFERENCE_RECV_TABLE(DT_LocalActiveWeaponData)),
|
|
RecvPropInt( RECVINFO(m_iViewModelIndex)),
|
|
RecvPropInt( RECVINFO(m_iWorldModelIndex)),
|
|
RecvPropInt( RECVINFO(m_iWorldDroppedModelIndex)),
|
|
RecvPropInt( RECVINFO( m_iState ), 0, RecvProxy_State ),
|
|
RecvPropEHandle( RECVINFO(m_hOwner ) ),
|
|
RecvPropIntWithMinusOneFlag( RECVINFO(m_iClip1 )),
|
|
RecvPropIntWithMinusOneFlag( RECVINFO(m_iClip2 )),
|
|
RecvPropInt( RECVINFO( m_iPrimaryReserveAmmoCount)),
|
|
RecvPropInt( RECVINFO( m_iSecondaryReserveAmmoCount)),
|
|
RecvPropEHandle( RECVINFO(m_hWeaponWorldModel) ),
|
|
RecvPropInt( RECVINFO( m_iNumEmptyAttacks )),
|
|
#endif
|
|
END_NETWORK_TABLE()
|
|
|
|
|
|
// float CBaseCombatWeapon::GetAttributeFloat( const char* szAttribClassName ) const
|
|
// {
|
|
// return GetWpnData().GetAttributeFloat( szAttribClassName, GetEconItemView() );
|
|
// }
|
|
//
|
|
// int CBaseCombatWeapon::GetAttributeInt( const char* szAttribClassName ) const
|
|
// {
|
|
// return GetWpnData().GetAttributeInt( szAttribClassName, GetEconItemView() );
|
|
// }
|
|
//
|
|
// bool CBaseCombatWeapon::GetAttributeBool( const char* szAttribClassName ) const
|
|
// {
|
|
// return GetWpnData().GetAttributeBool( szAttribClassName, GetEconItemView() );
|
|
// }
|
|
|
|
const CEconItemView* CBaseCombatWeapon::GetEconItemView( void ) const
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
CEconItemView* CBaseCombatWeapon::GetEconItemView( void )
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
int CBaseCombatWeapon::GetReserveAmmoCount( AmmoPosition_t nAmmoPosition, CBaseCombatCharacter * pForcedOwner/* = NULL*/ )
|
|
{
|
|
// LEGACY SUPPORT HERE
|
|
// Except for exhaustible weapons ( i.e. grenades ) we now store ammo on the weapon and not the player
|
|
|
|
bool bForceSetAmmoOnPlayer = pForcedOwner ? true : false;
|
|
|
|
CBaseCombatCharacter * pPlayer = pForcedOwner ? pForcedOwner : GetOwner();
|
|
if ( pPlayer )
|
|
{
|
|
int nAmmoType = -1;
|
|
|
|
switch ( nAmmoPosition )
|
|
{
|
|
case AMMO_POSITION_PRIMARY: nAmmoType = GetPrimaryAmmoType(); break;
|
|
case AMMO_POSITION_SECONDARY: nAmmoType = GetSecondaryAmmoType(); break;
|
|
}
|
|
|
|
if ( nAmmoType > -1 )
|
|
{
|
|
if ( pPlayer->GetAmmoCount( nAmmoType ) || bForceSetAmmoOnPlayer )
|
|
return pPlayer->GetAmmoCount( nAmmoType );
|
|
}
|
|
}
|
|
// /LEGACY
|
|
|
|
switch( nAmmoPosition )
|
|
{
|
|
case AMMO_POSITION_PRIMARY: return m_iPrimaryReserveAmmoCount;
|
|
case AMMO_POSITION_SECONDARY: return m_iSecondaryReserveAmmoCount;
|
|
default: return -1;
|
|
}
|
|
}
|
|
|
|
int CBaseCombatWeapon::SetReserveAmmoCount( AmmoPosition_t nAmmoPosition, int nCount, bool bSuppressSound /* = false */, CBaseCombatCharacter * pForcedOwner/* = NULL*/ )
|
|
{
|
|
int iAdd = 0;
|
|
|
|
// LEGACY SUPPORT HERE
|
|
// Except for exhaustible weapons ( i.e. grenades ) we now store ammo on the weapon and not the player
|
|
|
|
bool bForceSetAmmoOnPlayer = pForcedOwner ? true : false;
|
|
CBaseCombatCharacter * pPlayer = pForcedOwner ? pForcedOwner : GetOwner();
|
|
if ( pPlayer )
|
|
{
|
|
int nAmmoType = -1;
|
|
|
|
switch ( nAmmoPosition )
|
|
{
|
|
case AMMO_POSITION_PRIMARY: nAmmoType = GetPrimaryAmmoType(); break;
|
|
case AMMO_POSITION_SECONDARY: nAmmoType = GetSecondaryAmmoType(); break;
|
|
}
|
|
|
|
if ( nAmmoType > -1 )
|
|
{
|
|
// use player ammo if a player entity was passed in or if there already is ammo in this position
|
|
if ( pPlayer->GetAmmoCount( nAmmoType ) || bForceSetAmmoOnPlayer )
|
|
{
|
|
int iMax = GetAmmoDef()->MaxCarry( nAmmoType, pPlayer );
|
|
iAdd = MIN( nCount, iMax - pPlayer->GetAmmoCount( nAmmoType ) );
|
|
int iTotal = MIN( nCount, iMax );
|
|
|
|
pPlayer->SetAmmoCount( iTotal, nAmmoType );
|
|
return iAdd;
|
|
}
|
|
}
|
|
}
|
|
// /LEGACY
|
|
|
|
iAdd = MIN( nCount, GetReserveAmmoMax( nAmmoPosition ) - GetReserveAmmoCount( nAmmoPosition ) );
|
|
|
|
switch( nAmmoPosition )
|
|
{
|
|
case AMMO_POSITION_PRIMARY: m_iPrimaryReserveAmmoCount = MIN( nCount, GetReserveAmmoMax( AMMO_POSITION_PRIMARY ) ); break;
|
|
case AMMO_POSITION_SECONDARY: m_iSecondaryReserveAmmoCount = MIN( nCount, GetReserveAmmoMax( AMMO_POSITION_SECONDARY ) ); break;
|
|
default: return 0;
|
|
}
|
|
|
|
// Ammo pickup sound
|
|
if ( !bSuppressSound )
|
|
{
|
|
EmitSound( "BaseCombatCharacter.AmmoPickup" );
|
|
}
|
|
|
|
return iAdd;
|
|
}
|
|
|
|
int CBaseCombatWeapon::GiveReserveAmmo( AmmoPosition_t nAmmoPosition, int nCount, bool bSuppressSound /* = false */, CBaseCombatCharacter * pForcedOwner/* = NULL*/ )
|
|
{
|
|
if ( nCount <= 0 )
|
|
{
|
|
extern ConVar sv_infinite_ammo;
|
|
if ( sv_infinite_ammo.GetInt() == 2 ) // infinite total ammo but magazine reloads are still required.
|
|
return 0;
|
|
|
|
// supress ammo pickup sound when we're depleting ammo
|
|
bSuppressSound = true;
|
|
}
|
|
|
|
return SetReserveAmmoCount( nAmmoPosition, GetReserveAmmoCount( nAmmoPosition, pForcedOwner ) + nCount, bSuppressSound, pForcedOwner );
|
|
}
|
|
|
|
int CBaseCombatWeapon::GetReserveAmmoMax( AmmoPosition_t nAmmoPosition ) const
|
|
{
|
|
// LEGACY SUPPORT HERE
|
|
// Except for exhaustible weapons ( i.e. grenades ) we now store ammo on the weapon and not the player
|
|
CBaseCombatCharacter * pPlayer = GetOwner();
|
|
if ( pPlayer )
|
|
{
|
|
int nAmmoType = -1;
|
|
|
|
switch ( nAmmoPosition )
|
|
{
|
|
case AMMO_POSITION_PRIMARY: nAmmoType = GetPrimaryAmmoType(); break;
|
|
case AMMO_POSITION_SECONDARY: nAmmoType = GetSecondaryAmmoType(); break;
|
|
}
|
|
|
|
if ( nAmmoType > -1 )
|
|
{
|
|
// use player ammo if there already is ammo in this position
|
|
if ( pPlayer->GetAmmoCount( nAmmoType ) )
|
|
{
|
|
return GetAmmoDef()->MaxCarry( nAmmoType, pPlayer );
|
|
}
|
|
}
|
|
}
|
|
|
|
switch( nAmmoPosition )
|
|
{
|
|
case AMMO_POSITION_PRIMARY: return GetWpnData().GetPrimaryReserveAmmoMax( GetEconItemView() );
|
|
case AMMO_POSITION_SECONDARY: return GetWpnData().GetSecondaryReserveAmmoMax( GetEconItemView() );
|
|
default: Assert(0); return 0;
|
|
}
|
|
}
|