2025-02-19 18:39:00 -05:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
# include "cbase.h"
# include "ai_basenpc.h"
# include "animation.h"
# include "basecombatweapon.h"
# include "player.h" // For gEvilImpulse101 / CBasePlayer
# include "gamerules.h" // For g_pGameRules
# include <KeyValues.h>
# include "ammodef.h"
# include "baseviewmodel.h"
# include "in_buttons.h"
# include "soundent.h"
# include "weapon_parse.h"
# include "game.h"
# include "engine/IEngineSound.h"
# include "sendproxy.h"
# include "tier1/strtools.h"
# include "vphysics/constraints.h"
# include "npcevent.h"
# include "igamesystem.h"
# include "collisionutils.h"
# include "iservervehicle.h"
# include "func_break.h"
# ifdef HL2MP
# include "hl2mp_gamerules.h"
# endif
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
extern int gEvilImpulse101 ; // In Player.h
// -----------------------------------------
// Sprite Index info
// -----------------------------------------
short g_sModelIndexLaser ; // holds the index for the laser beam
const char * g_pModelNameLaser = " sprites/laserbeam.vmt " ;
short g_sModelIndexLaserDot ; // holds the index for the laser beam dot
short g_sModelIndexFireball ; // holds the index for the fireball
short g_sModelIndexSmoke ; // holds the index for the smoke cloud
short g_sModelIndexWExplosion ; // holds the index for the underwater explosion
short g_sModelIndexBubbles ; // holds the index for the bubbles model
short g_sModelIndexBloodDrop ; // holds the sprite index for the initial blood
short g_sModelIndexBloodSpray ; // holds the sprite index for splattered blood
BEGIN_ENT_SCRIPTDESC ( CBaseCombatWeapon , BASECOMBATWEAPON_DERIVED_FROM , " Base Combat Weapon " )
DEFINE_SCRIPTFUNC ( SetCustomViewModel , " Sets a custom view model for this weapon by model name " )
DEFINE_SCRIPTFUNC ( SetCustomViewModelModelIndex , " Sets a custom view model for this weapon by modelindex " )
DEFINE_SCRIPTFUNC ( GetSubType , " Get the weapon subtype " )
DEFINE_SCRIPTFUNC ( SetSubType , " Set the weapon subtype " )
DEFINE_SCRIPTFUNC ( IsAllowedToSwitch , " Are we allowed to switch to this weapon? " )
DEFINE_SCRIPTFUNC ( CanBeSelected , " Can this weapon be selected " )
DEFINE_SCRIPTFUNC ( VisibleInWeaponSelection , " Is this weapon visible in weapon selection " )
DEFINE_SCRIPTFUNC ( HasAnyAmmo , " Do we have any ammo? " )
DEFINE_SCRIPTFUNC ( HasPrimaryAmmo , " Do we have any primary ammo? " )
DEFINE_SCRIPTFUNC ( HasSecondaryAmmo , " Do we have any secondary ammo? " )
DEFINE_SCRIPTFUNC ( UsesPrimaryAmmo , " Do we use primary ammo? " )
DEFINE_SCRIPTFUNC ( UsesSecondaryAmmo , " Do we use secondary ammo? " )
DEFINE_SCRIPTFUNC ( UsesClipsForAmmo1 , " Do we use clips for ammo 1? " )
DEFINE_SCRIPTFUNC ( UsesClipsForAmmo2 , " Do we use clips for ammo 2? " )
DEFINE_SCRIPTFUNC ( PrimaryAttack , " Force a primary attack " )
DEFINE_SCRIPTFUNC ( SecondaryAttack , " Force a secondary attack " )
DEFINE_SCRIPTFUNC ( GetMaxClip1 , " Max size of clip1 " )
DEFINE_SCRIPTFUNC ( GetMaxClip2 , " Max size of clip2 " )
DEFINE_SCRIPTFUNC ( GetDefaultClip1 , " Default size of clip1 " )
DEFINE_SCRIPTFUNC ( GetDefaultClip2 , " Default size of clip2 " )
DEFINE_SCRIPTFUNC ( GetWeight , " Get the weapon weighting/importance " )
DEFINE_SCRIPTFUNC ( GetWeaponFlags , " Get the weapon flags " )
DEFINE_SCRIPTFUNC ( GetSlot , " Gets the weapon's current slot " )
DEFINE_SCRIPTFUNC ( GetPosition , " Gets the weapon's current position " )
DEFINE_SCRIPTFUNC ( GetName , " Gets the weapon's name " )
DEFINE_SCRIPTFUNC ( GetPrintName , " Gets the weapon's print name " )
DEFINE_SCRIPTFUNC ( IsMeleeWeapon , " Returns whether this is a melee weapon " )
DEFINE_SCRIPTFUNC ( GetPrimaryAmmoType , " Returns the primary ammo type " )
DEFINE_SCRIPTFUNC ( GetSecondaryAmmoType , " Returns the secondary ammo type " )
DEFINE_SCRIPTFUNC ( Clip1 , " Current ammo in clip1 " )
DEFINE_SCRIPTFUNC ( Clip2 , " Current ammo in clip2 " )
DEFINE_SCRIPTFUNC ( SetClip1 , " Set current ammo in clip1 " )
DEFINE_SCRIPTFUNC ( SetClip2 , " Set current ammo in clip2 " )
DEFINE_SCRIPTFUNC ( GetPrimaryAmmoCount , " Current primary ammo count if no clip is used or to give a player if they pick up this weapon legacy style (not TF) " )
DEFINE_SCRIPTFUNC ( GetSecondaryAmmoCount , " Current secondary ammo count if no clip is used or to give a player if they pick up this weapon legacy style (not TF) " )
END_SCRIPTDESC ( ) ;
ConVar weapon_showproficiency ( " weapon_showproficiency " , " 0 " ) ;
extern ConVar ai_debug_shoot_positions ;
//-----------------------------------------------------------------------------
// Purpose: Precache global weapon sounds
//-----------------------------------------------------------------------------
void W_Precache ( void )
{
PrecacheFileWeaponInfoDatabase ( filesystem , g_pGameRules - > GetEncryptionKey ( ) ) ;
# ifdef HL1_DLL
g_sModelIndexWExplosion = CBaseEntity : : PrecacheModel ( " sprites/WXplo1.vmt " ) ; // underwater fireball
g_sModelIndexBloodSpray = CBaseEntity : : PrecacheModel ( " sprites/bloodspray.vmt " ) ; // initial blood
g_sModelIndexBloodDrop = CBaseEntity : : PrecacheModel ( " sprites/blood.vmt " ) ; // splattered blood
g_sModelIndexLaserDot = CBaseEntity : : PrecacheModel ( " sprites/laserdot.vmt " ) ;
# endif // HL1_DLL
# ifndef TF_DLL
g_sModelIndexFireball = CBaseEntity : : PrecacheModel ( " sprites/zerogxplode.vmt " ) ; // fireball
g_sModelIndexSmoke = CBaseEntity : : PrecacheModel ( " sprites/steam1.vmt " ) ; // smoke
g_sModelIndexBubbles = CBaseEntity : : PrecacheModel ( " sprites/bubble.vmt " ) ; //bubbles
g_sModelIndexLaser = CBaseEntity : : PrecacheModel ( ( char * ) g_pModelNameLaser ) ;
PrecacheParticleSystem ( " blood_impact_red_01 " ) ;
PrecacheParticleSystem ( " blood_impact_green_01 " ) ;
PrecacheParticleSystem ( " blood_impact_yellow_01 " ) ;
CBaseEntity : : PrecacheModel ( " effects/bubble.vmt " ) ; //bubble trails
CBaseEntity : : PrecacheModel ( " models/weapons/w_bullet.mdl " ) ;
# endif
CBaseEntity : : PrecacheScriptSound ( " BaseCombatWeapon.WeaponDrop " ) ;
CBaseEntity : : PrecacheScriptSound ( " BaseCombatWeapon.WeaponMaterialize " ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Transmit weapon data
//-----------------------------------------------------------------------------
int CBaseCombatWeapon : : UpdateTransmitState ( void )
{
// If the weapon is being carried by a CBaseCombatCharacter, let the combat character do the logic
// about whether or not to transmit it.
if ( GetOwner ( ) )
{
return SetTransmitState ( FL_EDICT_PVSCHECK ) ;
}
else
{
// If it's just lying around, then use CBaseEntity's visibility test to see if it should be sent.
return BaseClass : : UpdateTransmitState ( ) ;
}
}
void CBaseCombatWeapon : : Operator_FrameUpdate ( CBaseCombatCharacter * pOperator )
{
StudioFrameAdvance ( ) ; // animate
if ( IsSequenceFinished ( ) )
{
if ( SequenceLoops ( ) )
{
// animation does loop, which means we're playing subtle idle. Might need to fidget.
int iSequence = SelectWeightedSequence ( GetActivity ( ) ) ;
if ( iSequence ! = ACTIVITY_NOT_AVAILABLE )
{
ResetSequence ( iSequence ) ; // Set to new anim (if it's there)
}
}
#if 0
else
{
// animation that just ended doesn't loop! That means we just finished a fidget
// and should return to our heaviest weighted idle (the subtle one)
SelectHeaviestSequence ( GetActivity ( ) ) ;
}
# endif
}
// Animation events are passed back to the weapon's owner/operator
DispatchAnimEvents ( pOperator ) ;
// Update and dispatch the viewmodel events
CBasePlayer * pOwner = ToBasePlayer ( GetOwner ( ) ) ;
if ( pOwner = = NULL )
return ;
CBaseViewModel * vm = pOwner - > GetViewModel ( m_nViewModelIndex ) ;
if ( vm ! = NULL )
{
vm - > StudioFrameAdvance ( ) ;
vm - > DispatchAnimEvents ( this ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pEvent -
// *pOperator -
//-----------------------------------------------------------------------------
void CBaseCombatWeapon : : Operator_HandleAnimEvent ( animevent_t * pEvent , CBaseCombatCharacter * pOperator )
{
if ( ( pEvent - > type & AE_TYPE_NEWEVENTSYSTEM ) & & ( pEvent - > type & AE_TYPE_SERVER ) )
{
if ( pEvent - > event = = AE_NPC_WEAPON_FIRE )
{
bool bSecondary = ( atoi ( pEvent - > options ) ! = 0 ) ;
Operator_ForceNPCFire ( pOperator , bSecondary ) ;
return ;
}
else if ( pEvent - > event = = AE_WPN_PLAYWPNSOUND )
{
int iSnd = GetWeaponSoundFromString ( pEvent - > options ) ;
if ( iSnd ! = - 1 )
{
WeaponSound ( ( WeaponSound_t ) iSnd ) ;
}
}
}
DevWarning ( 2 , " Unhandled animation event %d from %s --> %s \n " , pEvent - > event , pOperator - > GetClassname ( ) , GetClassname ( ) ) ;
}
// NOTE: This should never be called when a character is operating the weapon. Animation events should be
// routed through the character, and then back into CharacterAnimEvent()
void CBaseCombatWeapon : : HandleAnimEvent ( animevent_t * pEvent )
{
//If the player is receiving this message, pass it through
CBasePlayer * pOwner = ToBasePlayer ( GetOwner ( ) ) ;
if ( pOwner ! = NULL )
{
Operator_HandleAnimEvent ( pEvent , pOwner ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Make the weapon visible and tangible
//-----------------------------------------------------------------------------
CBaseEntity * CBaseCombatWeapon : : Respawn ( void )
{
// make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code
// will decide when to make the weapon visible and touchable.
CBaseEntity * pNewWeapon = CBaseEntity : : Create ( GetClassname ( ) , g_pGameRules - > VecWeaponRespawnSpot ( this ) , GetLocalAngles ( ) , GetOwnerEntity ( ) ) ;
if ( pNewWeapon )
{
pNewWeapon - > AddEffects ( EF_NODRAW ) ; // invisible for now
pNewWeapon - > SetTouch ( NULL ) ; // no touch
pNewWeapon - > SetThink ( & CBaseCombatWeapon : : AttemptToMaterialize ) ;
UTIL_DropToFloor ( this , MASK_SOLID ) ;
// not a typo! We want to know when the weapon the player just picked up should respawn! This new entity we created is the replacement,
// but when it should respawn is based on conditions belonging to the weapon that was taken.
pNewWeapon - > SetNextThink ( gpGlobals - > curtime + g_pGameRules - > FlWeaponRespawnTime ( this ) ) ;
}
else
{
Warning ( " Respawn failed to create %s! \n " , GetClassname ( ) ) ;
}
return pNewWeapon ;
}
//-----------------------------------------------------------------------------
// Purpose: Weapons ignore other weapons when LOS tracing
//-----------------------------------------------------------------------------
class CWeaponLOSFilter : public CTraceFilterSkipTwoEntities
{
DECLARE_CLASS ( CWeaponLOSFilter , CTraceFilterSkipTwoEntities ) ;
public :
CWeaponLOSFilter ( IHandleEntity * pHandleEntity , IHandleEntity * pHandleEntity2 , int collisionGroup ) :
CTraceFilterSkipTwoEntities ( pHandleEntity , pHandleEntity2 , collisionGroup ) , m_pVehicle ( NULL )
{
// If the tracing entity is in a vehicle, then ignore it
if ( pHandleEntity ! = NULL )
{
CBaseCombatCharacter * pBCC = ( ( CBaseEntity * ) pHandleEntity ) - > MyCombatCharacterPointer ( ) ;
if ( pBCC ! = NULL )
{
m_pVehicle = pBCC - > GetVehicleEntity ( ) ;
}
}
}
virtual bool ShouldHitEntity ( IHandleEntity * pServerEntity , int contentsMask )
{
CBaseEntity * pEntity = ( CBaseEntity * ) pServerEntity ;
if ( pEntity - > GetCollisionGroup ( ) = = COLLISION_GROUP_WEAPON )
return false ;
// Don't collide with the tracing entity's vehicle (if it exists)
if ( pServerEntity = = m_pVehicle )
return false ;
if ( pEntity - > GetHealth ( ) > 0 )
{
CBreakable * pBreakable = dynamic_cast < CBreakable * > ( pEntity ) ;
if ( pBreakable & & pBreakable - > IsBreakable ( ) & & pBreakable - > GetMaterialType ( ) = = matGlass )
{
return false ;
}
}
return BaseClass : : ShouldHitEntity ( pServerEntity , contentsMask ) ;
}
private :
CBaseEntity * m_pVehicle ;
} ;
//-----------------------------------------------------------------------------
// Purpose: Check the weapon LOS for an owner at an arbitrary position
// If bSetConditions is true, LOS related conditions will also be set
//-----------------------------------------------------------------------------
bool CBaseCombatWeapon : : WeaponLOSCondition ( const Vector & ownerPos , const Vector & targetPos , bool bSetConditions )
{
// --------------------
// Check for occlusion
// --------------------
CAI_BaseNPC * npcOwner = m_hOwner . Get ( ) - > MyNPCPointer ( ) ;
// Find its relative shoot position
Vector vecRelativeShootPosition ;
VectorSubtract ( npcOwner - > Weapon_ShootPosition ( ) , npcOwner - > GetAbsOrigin ( ) , vecRelativeShootPosition ) ;
Vector barrelPos = ownerPos + vecRelativeShootPosition ;
// FIXME: If we're in a vehicle, we need some sort of way to handle shooting out of them
// Use the custom LOS trace filter
CWeaponLOSFilter traceFilter ( m_hOwner . Get ( ) , npcOwner - > GetEnemy ( ) , COLLISION_GROUP_BREAKABLE_GLASS ) ;
trace_t tr ;
UTIL_TraceLine ( barrelPos , targetPos , MASK_SHOT , & traceFilter , & tr ) ;
// See if we completed the trace without interruption
if ( tr . fraction = = 1.0 )
{
if ( ai_debug_shoot_positions . GetBool ( ) )
{
NDebugOverlay : : Line ( barrelPos , targetPos , 0 , 255 , 0 , false , 1.0 ) ;
}
return true ;
}
CBaseEntity * pHitEnt = tr . m_pEnt ;
CBasePlayer * pEnemyPlayer = ToBasePlayer ( npcOwner - > GetEnemy ( ) ) ;
// is player in a vehicle? if so, verify vehicle is target and return if so (so npc shoots at vehicle)
if ( pEnemyPlayer & & pEnemyPlayer - > IsInAVehicle ( ) )
{
// Ok, player in vehicle, check if vehicle is target we're looking at, fire if it is
// Also, check to see if the owner of the entity is the vehicle, in which case it's valid too.
// This catches vehicles that use bone followers.
CBaseEntity * pVehicle = pEnemyPlayer - > GetVehicle ( ) - > GetVehicleEnt ( ) ;
if ( pHitEnt = = pVehicle | | pHitEnt - > GetOwnerEntity ( ) = = pVehicle )
return true ;
}
// Hitting our enemy is a success case
if ( pHitEnt = = npcOwner - > GetEnemy ( ) )
{
if ( ai_debug_shoot_positions . GetBool ( ) )
{
NDebugOverlay : : Line ( barrelPos , targetPos , 0 , 255 , 0 , false , 1.0 ) ;
}
return true ;
}
// If a vehicle is blocking the view, grab its driver and use that as the combat character
CBaseCombatCharacter * pBCC ;
IServerVehicle * pVehicle = pHitEnt - > GetServerVehicle ( ) ;
if ( pVehicle )
{
pBCC = pVehicle - > GetPassenger ( ) ;
}
else
{
pBCC = ToBaseCombatCharacter ( pHitEnt ) ;
}
if ( pBCC )
{
if ( npcOwner - > IRelationType ( pBCC ) = = D_HT )
return true ;
if ( bSetConditions )
{
npcOwner - > SetCondition ( COND_WEAPON_BLOCKED_BY_FRIEND ) ;
}
}
else if ( bSetConditions )
{
npcOwner - > SetCondition ( COND_WEAPON_SIGHT_OCCLUDED ) ;
npcOwner - > SetEnemyOccluder ( pHitEnt ) ;
if ( ai_debug_shoot_positions . GetBool ( ) )
{
NDebugOverlay : : Line ( tr . startpos , tr . endpos , 255 , 0 , 0 , false , 1.0 ) ;
}
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose: Base class always returns not bits
//-----------------------------------------------------------------------------
int CBaseCombatWeapon : : WeaponRangeAttack1Condition ( float flDot , float flDist )
{
if ( UsesPrimaryAmmo ( ) & & ! HasPrimaryAmmo ( ) )
{
return COND_NO_PRIMARY_AMMO ;
}
else if ( flDist < m_fMinRange1 )
{
return COND_TOO_CLOSE_TO_ATTACK ;
}
else if ( flDist > m_fMaxRange1 )
{
return COND_TOO_FAR_TO_ATTACK ;
}
else if ( flDot < 0.5 ) // UNDONE: Why check this here? Isn't the AI checking this already?
{
return COND_NOT_FACING_ATTACK ;
}
return COND_CAN_RANGE_ATTACK1 ;
}
//-----------------------------------------------------------------------------
// Purpose: Base class always returns not bits
//-----------------------------------------------------------------------------
int CBaseCombatWeapon : : WeaponRangeAttack2Condition ( float flDot , float flDist )
{
// currently disabled
return COND_NONE ;
if ( m_bReloadsSingly )
{
if ( m_iClip2 < = 0 )
{
return COND_NO_SECONDARY_AMMO ;
}
else if ( flDist < m_fMinRange2 )
{
return COND_TOO_CLOSE_TO_ATTACK ;
}
else if ( flDist > m_fMaxRange2 )
{
return COND_TOO_FAR_TO_ATTACK ;
}
else if ( flDot < 0.5 )
{
return COND_NOT_FACING_ATTACK ;
}
return COND_CAN_RANGE_ATTACK2 ;
}
return COND_NONE ;
}
//-----------------------------------------------------------------------------
// Purpose: Base class always returns not bits
//-----------------------------------------------------------------------------
int CBaseCombatWeapon : : WeaponMeleeAttack1Condition ( float flDot , float flDist )
{
return COND_NONE ;
}
//-----------------------------------------------------------------------------
// Purpose: Base class always returns not bits
//-----------------------------------------------------------------------------
int CBaseCombatWeapon : : WeaponMeleeAttack2Condition ( float flDot , float flDist )
{
return COND_NONE ;
}
//====================================================================================
// WEAPON DROPPING / DESTRUCTION
//====================================================================================
void CBaseCombatWeapon : : Delete ( void )
{
SetTouch ( NULL ) ;
// FIXME: why doesn't this just remove itself now?
SetThink ( & CBaseCombatWeapon : : SUB_Remove ) ;
SetNextThink ( gpGlobals - > curtime + 0.1f ) ;
}
void CBaseCombatWeapon : : DestroyItem ( void )
{
CBaseCombatCharacter * pOwner = m_hOwner . Get ( ) ;
if ( pOwner )
{
// if attached to a player, remove.
pOwner - > RemovePlayerItem ( this ) ;
}
Kill ( ) ;
}
void CBaseCombatWeapon : : Kill ( void )
{
SetTouch ( NULL ) ;
// FIXME: why doesn't this just remove itself now?
// FIXME: how is this different than Delete(), and why do they have the same code in them?
SetThink ( & CBaseCombatWeapon : : SUB_Remove ) ;
SetNextThink ( gpGlobals - > curtime + 0.1f ) ;
}
//====================================================================================
// FALL TO GROUND
//====================================================================================
//-----------------------------------------------------------------------------
// Purpose: Setup for the fall
//-----------------------------------------------------------------------------
void CBaseCombatWeapon : : FallInit ( void )
{
SetModel ( GetWorldModel ( ) ) ;
VPhysicsDestroyObject ( ) ;
if ( ! VPhysicsInitNormal ( SOLID_BBOX , GetSolidFlags ( ) | FSOLID_TRIGGER , false ) )
{
SetMoveType ( MOVETYPE_FLYGRAVITY ) ;
SetSolid ( SOLID_BBOX ) ;
AddSolidFlags ( FSOLID_TRIGGER ) ;
}
else
{
# if !defined( CLIENT_DLL )
// Constrained start?
if ( HasSpawnFlags ( SF_WEAPON_START_CONSTRAINED ) )
{
//Constrain the weapon in place
IPhysicsObject * pReferenceObject , * pAttachedObject ;
pReferenceObject = g_PhysWorldObject ;
pAttachedObject = VPhysicsGetObject ( ) ;
if ( pReferenceObject & & pAttachedObject )
{
constraint_fixedparams_t fixed ;
fixed . Defaults ( ) ;
fixed . InitWithCurrentObjectState ( pReferenceObject , pAttachedObject ) ;
fixed . constraint . forceLimit = lbs2kg ( 10000 ) ;
fixed . constraint . torqueLimit = lbs2kg ( 10000 ) ;
m_pConstraint = physenv - > CreateFixedConstraint ( pReferenceObject , pAttachedObject , NULL , fixed ) ;
m_pConstraint - > SetGameData ( ( void * ) this ) ;
}
}
# endif //CLIENT_DLL
}
SetPickupTouch ( ) ;
SetThink ( & CBaseCombatWeapon : : FallThink ) ;
SetNextThink ( gpGlobals - > curtime + 0.1f ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Items that have just spawned run this think to catch them when
// they hit the ground. Once we're sure that the object is grounded,
// we change its solid type to trigger and set it in a large box that
// helps the player get it.
//-----------------------------------------------------------------------------
void CBaseCombatWeapon : : FallThink ( void )
{
SetNextThink ( gpGlobals - > curtime + 0.1f ) ;
bool shouldMaterialize = false ;
IPhysicsObject * pPhysics = VPhysicsGetObject ( ) ;
if ( pPhysics )
{
shouldMaterialize = pPhysics - > IsAsleep ( ) ;
}
else
{
shouldMaterialize = ( GetFlags ( ) & FL_ONGROUND ) ? true : false ;
}
if ( shouldMaterialize )
{
// clatter if we have an owner (i.e., dropped by someone)
// don't clatter if the gun is waiting to respawn (if it's waiting, it is invisible!)
if ( GetOwnerEntity ( ) )
{
EmitSound ( " BaseCombatWeapon.WeaponDrop " ) ;
}
Materialize ( ) ;
}
}
//====================================================================================
// WEAPON SPAWNING
//====================================================================================
//-----------------------------------------------------------------------------
// Purpose: Make a weapon visible and tangible
//-----------------------------------------------------------------------------//
void CBaseCombatWeapon : : Materialize ( void )
{
if ( IsEffectActive ( EF_NODRAW ) )
{
// changing from invisible state to visible.
# ifdef HL2MP
EmitSound ( " AlyxEmp.Charge " ) ;
# else
EmitSound ( " BaseCombatWeapon.WeaponMaterialize " ) ;
# endif
RemoveEffects ( EF_NODRAW ) ;
DoMuzzleFlash ( ) ;
}
# ifdef HL2MP
if ( HasSpawnFlags ( SF_NORESPAWN ) = = false )
{
VPhysicsInitNormal ( SOLID_BBOX , GetSolidFlags ( ) | FSOLID_TRIGGER , false ) ;
SetMoveType ( MOVETYPE_VPHYSICS ) ;
HL2MPRules ( ) - > AddLevelDesignerPlacedObject ( this ) ;
}
# else
SetSolid ( SOLID_BBOX ) ;
AddSolidFlags ( FSOLID_TRIGGER ) ;
# endif
SetPickupTouch ( ) ;
SetThink ( NULL ) ;
}
//-----------------------------------------------------------------------------
// Purpose: See if the game rules will let this weapon respawn
//-----------------------------------------------------------------------------
void CBaseCombatWeapon : : AttemptToMaterialize ( void )
{
float time = g_pGameRules - > FlWeaponTryRespawn ( this ) ;
if ( time = = 0 )
{
Materialize ( ) ;
return ;
}
SetNextThink ( gpGlobals - > curtime + time ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Weapon has been picked up, should it respawn?
//-----------------------------------------------------------------------------
void CBaseCombatWeapon : : CheckRespawn ( void )
{
switch ( g_pGameRules - > WeaponShouldRespawn ( this ) )
{
case GR_WEAPON_RESPAWN_YES :
Respawn ( ) ;
break ;
case GR_WEAPON_RESPAWN_NO :
return ;
break ;
}
}
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 " ) ;
void OnBaseCombatWeaponCreated ( CBaseCombatWeapon * pWeapon )
{
g_WeaponList . AddWeapon ( pWeapon ) ;
}
void OnBaseCombatWeaponDestroyed ( CBaseCombatWeapon * pWeapon )
{
g_WeaponList . RemoveWeapon ( pWeapon ) ;
}
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 ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CBaseCombatWeapon : : ObjectCaps ( void )
{
int caps = BaseClass : : ObjectCaps ( ) ;
if ( ! IsFollowingEntity ( ) & & ! HasSpawnFlags ( SF_WEAPON_NO_PLAYER_PICKUP ) )
{
caps | = FCAP_IMPULSE_USE ;
}
return caps ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseCombatWeapon : : Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value )
{
CBasePlayer * pPlayer = ToBasePlayer ( pActivator ) ;
if ( pPlayer )
{
m_OnPlayerUse . FireOutput ( pActivator , pCaller ) ;
//
// Bump the weapon to try equipping it before picking it up physically. This is
// important in a few spots in the game where the player could potentially +use pickup
// and then THROW AWAY a vital weapon, rendering them unable to continue the game.
//
if ( pPlayer - > BumpWeapon ( this ) )
{
OnPickedUp ( pPlayer ) ;
}
else
{
pPlayer - > PickupObject ( this ) ;
}
}
}
void CBaseCombatWeapon : : SetCustomViewModel ( const char * pszCustomViewModel )
{
if ( ! pszCustomViewModel | | ! * pszCustomViewModel )
{
m_nCustomViewmodelModelIndex = 0 ;
return ;
}
const bool bPrecacheAllowed = CBaseEntity : : IsPrecacheAllowed ( ) ;
CBaseEntity : : SetAllowPrecache ( true ) ;
SetCustomViewModelModelIndex ( PrecacheModel ( pszCustomViewModel , false ) ) ;
CBaseEntity : : SetAllowPrecache ( bPrecacheAllowed ) ;
}
void CBaseCombatWeapon : : SetCustomViewModelModelIndex ( int nCustomViewModelModelIndex )
{
if ( nCustomViewModelModelIndex < 0 )
nCustomViewModelModelIndex = 0 ;
m_nCustomViewmodelModelIndex = nCustomViewModelModelIndex ;
}