2023-10-03 17:23:56 +03:00
//========= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ============//
2020-04-22 12:56:21 -04:00
//
// Purpose: Client side implementation of CBaseCombatWeapon.
//
// $NoKeywords: $
//=============================================================================//
# include "cbase.h"
# include "history_resource.h"
# include "iclientmode.h"
# include "iinput.h"
# include "weapon_selection.h"
# include "hud_crosshair.h"
# include "engine/ivmodelinfo.h"
# include "tier0/vprof.h"
# include "hltvcamera.h"
# include "tier1/KeyValues.h"
# include "toolframework/itoolframework.h"
# include "toolframework_client.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BaseCombatWeapon : : SetDormant ( bool bDormant )
{
// If I'm going from active to dormant and I'm carried by another player, holster me.
2023-10-03 17:23:56 +03:00
if ( ! IsDormant ( ) & & bDormant & & ! IsCarriedByLocalPlayer ( ) )
2020-04-22 12:56:21 -04:00
{
Holster ( NULL ) ;
}
BaseClass : : SetDormant ( bDormant ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BaseCombatWeapon : : NotifyShouldTransmit ( ShouldTransmitState_t state )
{
BaseClass : : NotifyShouldTransmit ( state ) ;
if ( state = = SHOULDTRANSMIT_END )
{
if ( m_iState = = WEAPON_IS_ACTIVE )
{
m_iState = WEAPON_IS_CARRIED_BY_PLAYER ;
}
}
else if ( state = = SHOULDTRANSMIT_START )
{
if ( m_iState = = WEAPON_IS_CARRIED_BY_PLAYER )
{
if ( GetOwner ( ) & & GetOwner ( ) - > GetActiveWeapon ( ) = = this )
{
// Restore the Activeness of the weapon if we client-twiddled it off in the first case above.
m_iState = WEAPON_IS_ACTIVE ;
}
}
}
}
2023-10-03 17:23:56 +03:00
static inline bool ShouldDrawLocalPlayer ( C_BasePlayer * pl )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
Assert ( pl ) ;
return pl - > ShouldDrawLocalPlayer ( ) ;
2020-04-22 12:56:21 -04:00
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BaseCombatWeapon : : OnRestore ( )
{
BaseClass : : OnRestore ( ) ;
// if the player is holding this weapon,
// mark it as just restored so it won't show as a new pickup
2023-10-03 17:23:56 +03:00
if ( C_BasePlayer : : IsLocalPlayer ( GetOwner ( ) ) )
2020-04-22 12:56:21 -04:00
{
m_bJustRestored = true ;
}
}
int C_BaseCombatWeapon : : GetWorldModelIndex ( void )
{
return m_iWorldModelIndex ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : bnewentity -
//-----------------------------------------------------------------------------
void C_BaseCombatWeapon : : OnDataChanged ( DataUpdateType_t updateType )
{
2023-10-03 17:23:56 +03:00
BaseClass : : OnDataChanged ( updateType ) ;
2020-04-22 12:56:21 -04:00
// If it's being carried by the *local* player, on the first update,
// find the registered weapon for this ID
C_BaseCombatCharacter * pOwner = GetOwner ( ) ;
2023-10-03 17:23:56 +03:00
C_BasePlayer * pPlayer = ToBasePlayer ( pOwner ) ;
2020-04-22 12:56:21 -04:00
// check if weapon is carried by local player
2023-10-03 17:23:56 +03:00
bool bIsLocalPlayer = C_BasePlayer : : IsLocalPlayer ( pPlayer ) ;
if ( bIsLocalPlayer )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
ACTIVE_SPLITSCREEN_PLAYER_GUARD ( C_BasePlayer : : GetSplitScreenSlotForPlayer ( pPlayer ) ) ;
2020-04-22 12:56:21 -04:00
// If I was just picked up, or created & immediately carried, add myself to this client's list of weapons
2023-10-03 17:23:56 +03:00
if ( ( m_iState ! = WEAPON_NOT_CARRIED ) & &
( m_iOldState = = WEAPON_NOT_CARRIED ) )
2020-04-22 12:56:21 -04:00
{
// Tell the HUD this weapon's been picked up
if ( ShouldDrawPickup ( ) )
{
CBaseHudWeaponSelection * pHudSelection = GetHudWeaponSelection ( ) ;
if ( pHudSelection )
{
pHudSelection - > OnWeaponPickup ( this ) ;
}
pPlayer - > EmitSound ( " Player.PickupWeapon " ) ;
}
}
}
2023-10-03 17:23:56 +03:00
2022-03-01 23:00:42 +03:00
UpdateVisibility ( ) ;
2020-04-22 12:56:21 -04:00
m_iOldState = m_iState ;
m_bJustRestored = false ;
}
//-----------------------------------------------------------------------------
// Is anyone carrying it?
//-----------------------------------------------------------------------------
bool C_BaseCombatWeapon : : IsBeingCarried ( ) const
{
return ( m_hOwner . Get ( ) ! = NULL ) ;
}
//-----------------------------------------------------------------------------
// Is the carrier alive?
//-----------------------------------------------------------------------------
bool C_BaseCombatWeapon : : IsCarrierAlive ( ) const
{
if ( ! m_hOwner . Get ( ) )
return false ;
return m_hOwner . Get ( ) - > GetHealth ( ) > 0 ;
}
//-----------------------------------------------------------------------------
// Should this object cast shadows?
//-----------------------------------------------------------------------------
ShadowType_t C_BaseCombatWeapon : : ShadowCastType ( )
{
if ( IsEffectActive ( /*EF_NODRAW |*/ EF_NOSHADOW ) )
return SHADOWS_NONE ;
if ( ! IsBeingCarried ( ) )
return SHADOWS_RENDER_TO_TEXTURE ;
2023-10-03 17:23:56 +03:00
if ( IsCarriedByLocalPlayer ( ) )
2020-04-22 12:56:21 -04:00
return SHADOWS_NONE ;
2023-10-03 17:23:56 +03:00
return ( m_iState ! = WEAPON_IS_CARRIED_BY_PLAYER ) ? SHADOWS_RENDER_TO_TEXTURE : SHADOWS_NONE ;
2020-04-22 12:56:21 -04:00
}
//-----------------------------------------------------------------------------
// Purpose: This weapon is the active weapon, and it should now draw anything
// it wants to. This gets called every frame.
//-----------------------------------------------------------------------------
void C_BaseCombatWeapon : : Redraw ( )
{
2023-10-03 17:23:56 +03:00
if ( GetClientMode ( ) - > ShouldDrawCrosshair ( ) )
2020-04-22 12:56:21 -04:00
{
DrawCrosshair ( ) ;
}
// ammo drawing has been moved into hud_ammo.cpp
}
//-----------------------------------------------------------------------------
// Purpose: Draw the weapon's crosshair
//-----------------------------------------------------------------------------
void C_BaseCombatWeapon : : DrawCrosshair ( )
{
2023-10-03 17:23:56 +03:00
# ifndef INFESTED_DLL
2020-04-22 12:56:21 -04:00
C_BasePlayer * player = C_BasePlayer : : GetLocalPlayer ( ) ;
if ( ! player )
return ;
2023-10-03 17:23:56 +03:00
Color clr = GetHud ( ) . m_clrNormal ;
2020-04-22 12:56:21 -04:00
/*
// TEST: if the thing under your crosshair is on a different team, light the crosshair with a different color.
Vector vShootPos , vShootAngles ;
GetShootPosition ( vShootPos , vShootAngles ) ;
Vector vForward ;
AngleVectors ( vShootAngles , & vForward ) ;
// Change the color depending on if we're looking at a friend or an enemy.
CPartitionFilterListMask filter ( PARTITION_ALL_CLIENT_EDICTS ) ;
trace_t tr ;
traceline - > TraceLine ( vShootPos , vShootPos + vForward * 10000 , COLLISION_GROUP_NONE , MASK_SHOT , & tr , true , ~ 0 , & filter ) ;
if ( tr . index ! = 0 & & tr . index ! = INVALID_CLIENTENTITY_HANDLE )
{
C_BaseEntity * pEnt = ClientEntityList ( ) . GetBaseEntityFromHandle ( tr . index ) ;
if ( pEnt )
{
if ( pEnt - > GetTeamNumber ( ) ! = player - > GetTeamNumber ( ) )
{
g = b = 0 ;
}
}
}
*/
2022-03-01 23:00:42 +03:00
CHudCrosshair * crosshair = GET_HUDELEMENT ( CHudCrosshair ) ;
if ( ! crosshair )
2020-04-22 12:56:21 -04:00
return ;
// Find out if this weapon's auto-aimed onto a target
2023-10-03 17:23:56 +03:00
bool bOnTarget = ( m_iState = = WEAPON_IS_ACTIVE ) & & player - > m_fOnTarget ;
2020-04-22 12:56:21 -04:00
if ( player - > GetFOV ( ) > = 90 )
{
// normal crosshairs
if ( bOnTarget & & GetWpnData ( ) . iconAutoaim )
{
clr [ 3 ] = 255 ;
2022-03-01 23:00:42 +03:00
crosshair - > SetCrosshair ( GetWpnData ( ) . iconAutoaim , clr ) ;
2020-04-22 12:56:21 -04:00
}
else if ( GetWpnData ( ) . iconCrosshair )
{
clr [ 3 ] = 255 ;
2022-03-01 23:00:42 +03:00
crosshair - > SetCrosshair ( GetWpnData ( ) . iconCrosshair , clr ) ;
2020-04-22 12:56:21 -04:00
}
else
{
2022-03-01 23:00:42 +03:00
crosshair - > ResetCrosshair ( ) ;
2020-04-22 12:56:21 -04:00
}
}
else
{
Color white ( 255 , 255 , 255 , 255 ) ;
// zoomed crosshairs
if ( bOnTarget & & GetWpnData ( ) . iconZoomedAutoaim )
2022-03-01 23:00:42 +03:00
crosshair - > SetCrosshair ( GetWpnData ( ) . iconZoomedAutoaim , white ) ;
2020-04-22 12:56:21 -04:00
else if ( GetWpnData ( ) . iconZoomedCrosshair )
2022-03-01 23:00:42 +03:00
crosshair - > SetCrosshair ( GetWpnData ( ) . iconZoomedCrosshair , white ) ;
2020-04-22 12:56:21 -04:00
else
2022-03-01 23:00:42 +03:00
crosshair - > ResetCrosshair ( ) ;
2020-04-22 12:56:21 -04:00
}
2023-10-03 17:23:56 +03:00
# endif
2020-04-22 12:56:21 -04:00
}
//-----------------------------------------------------------------------------
// Purpose: This weapon is the active weapon, and the viewmodel for it was just drawn.
//-----------------------------------------------------------------------------
void C_BaseCombatWeapon : : ViewModelDrawn ( C_BaseViewModel * pViewModel )
{
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if this client's carrying this weapon
//-----------------------------------------------------------------------------
bool C_BaseCombatWeapon : : IsCarriedByLocalPlayer ( void )
{
if ( ! GetOwner ( ) )
return false ;
2023-10-03 17:23:56 +03:00
return ( C_BasePlayer : : IsLocalPlayer ( GetOwner ( ) ) ) ;
2020-04-22 12:56:21 -04:00
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if this weapon is the local client's currently wielded weapon
//-----------------------------------------------------------------------------
bool C_BaseCombatWeapon : : IsActiveByLocalPlayer ( void )
{
if ( IsCarriedByLocalPlayer ( ) )
{
return ( m_iState = = WEAPON_IS_ACTIVE ) ;
}
return false ;
}
bool C_BaseCombatWeapon : : GetShootPosition ( Vector & vOrigin , QAngle & vAngles )
{
// Get the entity because the weapon doesn't have the right angles.
C_BaseCombatCharacter * pEnt = ToBaseCombatCharacter ( GetOwner ( ) ) ;
if ( pEnt )
{
if ( pEnt = = C_BasePlayer : : GetLocalPlayer ( ) )
{
vAngles = pEnt - > EyeAngles ( ) ;
}
else
{
vAngles = pEnt - > GetRenderAngles ( ) ;
}
}
else
{
vAngles . Init ( ) ;
}
2023-10-03 17:23:56 +03:00
C_BasePlayer * player = ToBasePlayer ( pEnt ) ;
bool bUseViewModel = false ;
if ( C_BasePlayer : : IsLocalPlayer ( pEnt ) )
{
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT ( pEnt ) ;
bUseViewModel = ! player - > ShouldDrawLocalPlayer ( ) ;
}
2020-04-22 12:56:21 -04:00
QAngle vDummy ;
2023-10-03 17:23:56 +03:00
if ( IsActiveByLocalPlayer ( ) & & bUseViewModel )
2020-04-22 12:56:21 -04:00
{
C_BaseViewModel * vm = player ? player - > GetViewModel ( 0 ) : NULL ;
if ( vm )
{
int iAttachment = vm - > LookupAttachment ( " muzzle " ) ;
if ( vm - > GetAttachment ( iAttachment , vOrigin , vDummy ) )
{
return true ;
}
}
}
else
{
// Thirdperson
int iAttachment = LookupAttachment ( " muzzle " ) ;
if ( GetAttachment ( iAttachment , vOrigin , vDummy ) )
{
return true ;
}
}
vOrigin = GetRenderOrigin ( ) ;
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool C_BaseCombatWeapon : : ShouldDraw ( void )
{
if ( m_iWorldModelIndex = = 0 )
return false ;
// FIXME: All weapons with owners are set to transmit in CBaseCombatWeapon::UpdateTransmitState,
// even if they have EF_NODRAW set, so we have to check this here. Ideally they would never
// transmit except for the weapons owned by the local player.
if ( IsEffectActive ( EF_NODRAW ) )
return false ;
C_BaseCombatCharacter * pOwner = GetOwner ( ) ;
// weapon has no owner, always draw it
if ( ! pOwner )
return true ;
bool bIsActive = ( m_iState = = WEAPON_IS_ACTIVE ) ;
C_BasePlayer * pLocalPlayer = C_BasePlayer : : GetLocalPlayer ( ) ;
// carried by local player?
if ( pOwner = = pLocalPlayer )
{
// Only ever show the active weapon
if ( ! bIsActive )
return false ;
2023-10-03 17:23:56 +03:00
// 3rd person mode
if ( pLocalPlayer - > ShouldDrawLocalPlayer ( ) )
2020-04-22 12:56:21 -04:00
return true ;
// don't draw active weapon if not in some kind of 3rd person mode, the viewmodel will do that
return false ;
}
// If it's a player, then only show active weapons
if ( pOwner - > IsPlayer ( ) )
{
// Show it if it's active...
return bIsActive ;
}
// FIXME: We may want to only show active weapons on NPCs
// These are carried by AIs; always show them
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Return true if a weapon-pickup icon should be displayed when this weapon is received
//-----------------------------------------------------------------------------
bool C_BaseCombatWeapon : : ShouldDrawPickup ( void )
{
if ( GetWeaponFlags ( ) & ITEM_FLAG_NOITEMPICKUP )
return false ;
if ( m_bJustRestored )
return false ;
return true ;
}
2023-10-03 17:23:56 +03:00
//----------------------------------------------------------------------------
// Hooks into the fast path render system
//----------------------------------------------------------------------------
IClientModelRenderable * C_BaseCombatWeapon : : GetClientModelRenderable ( )
{
if ( ! m_bReadyToDraw )
return 0 ;
// check if local player chases owner of this weapon in first person
C_BasePlayer * localplayer = C_BasePlayer : : GetLocalPlayer ( ) ;
if ( localplayer & & localplayer - > IsObserver ( ) & & GetOwner ( ) )
{
// don't draw weapon if chasing this guy as spectator
// we don't check that in ShouldDraw() since this may change
// without notification
if ( localplayer - > GetObserverMode ( ) = = OBS_MODE_IN_EYE & &
localplayer - > GetObserverTarget ( ) = = GetOwner ( ) )
return NULL ;
}
if ( ! BaseClass : : GetClientModelRenderable ( ) )
return NULL ;
EnsureCorrectRenderingModel ( ) ;
return this ;
}
2020-04-22 12:56:21 -04:00
//-----------------------------------------------------------------------------
// Purpose: Render the weapon. Draw the Viewmodel if the weapon's being carried
// by this player, otherwise draw the worldmodel.
//-----------------------------------------------------------------------------
2023-10-03 17:23:56 +03:00
int C_BaseCombatWeapon : : DrawModel ( int flags , const RenderableInstance_t & instance )
2020-04-22 12:56:21 -04:00
{
VPROF_BUDGET ( " C_BaseCombatWeapon::DrawModel " , VPROF_BUDGETGROUP_MODEL_RENDERING ) ;
if ( ! m_bReadyToDraw )
return 0 ;
if ( ! IsVisible ( ) )
return 0 ;
// check if local player chases owner of this weapon in first person
C_BasePlayer * localplayer = C_BasePlayer : : GetLocalPlayer ( ) ;
if ( localplayer & & localplayer - > IsObserver ( ) & & GetOwner ( ) )
{
// don't draw weapon if chasing this guy as spectator
// we don't check that in ShouldDraw() since this may change
// without notification
if ( localplayer - > GetObserverMode ( ) = = OBS_MODE_IN_EYE & &
localplayer - > GetObserverTarget ( ) = = GetOwner ( ) )
return false ;
}
2023-10-03 17:23:56 +03:00
// See comment below
EnsureCorrectRenderingModel ( ) ;
2020-04-22 12:56:21 -04:00
2023-10-03 17:23:56 +03:00
return BaseClass : : DrawModel ( flags , instance ) ;
}
2020-04-22 12:56:21 -04:00
2023-10-03 17:23:56 +03:00
// If the local player is visible (thirdperson mode, tf2 taunts, etc., then make sure that we are using the
// w_ (world) model not the v_ (view) model or else the model can flicker, etc.
// Otherwise, if we're not the local player, always use the world model
void C_BaseCombatWeapon : : EnsureCorrectRenderingModel ( )
{
2020-04-22 12:56:21 -04:00
C_BasePlayer * localplayer = C_BasePlayer : : GetLocalPlayer ( ) ;
if ( localplayer & &
localplayer = = GetOwner ( ) & &
2023-10-03 17:23:56 +03:00
! localplayer - > ShouldDrawLocalPlayer ( ) )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
return ;
2020-04-22 12:56:21 -04:00
}
2023-10-03 17:23:56 +03:00
// BRJ 10/14/02
// FIXME: Remove when Yahn's client-side prediction is done
// It's a hacky workaround for the model indices fighting
// (GetRenderBounds uses the model index, which is for the view model)
SetModelIndex ( GetWorldModelIndex ( ) ) ;
// Validate our current sequence just in case ( in theory the view and weapon models should have the same sequences for sequences that overlap at least )
CStudioHdr * pStudioHdr = GetModelPtr ( ) ;
if ( pStudioHdr & &
GetSequence ( ) > = pStudioHdr - > GetNumSeq ( ) )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
SetSequence ( 0 ) ;
2020-04-22 12:56:21 -04:00
}
}
//-----------------------------------------------------------------------------
// tool recording
//-----------------------------------------------------------------------------
void C_BaseCombatWeapon : : GetToolRecordingState ( KeyValues * msg )
{
if ( ! ToolsEnabled ( ) )
return ;
int nModelIndex = GetModelIndex ( ) ;
int nWorldModelIndex = GetWorldModelIndex ( ) ;
if ( nModelIndex ! = nWorldModelIndex )
{
SetModelIndex ( nWorldModelIndex ) ;
}
BaseClass : : GetToolRecordingState ( msg ) ;
if ( m_iState = = WEAPON_NOT_CARRIED )
{
BaseEntityRecordingState_t * pBaseEntity = ( BaseEntityRecordingState_t * ) msg - > GetPtr ( " baseentity " ) ;
pBaseEntity - > m_nOwner = - 1 ;
}
else
{
msg - > SetInt ( " worldmodel " , 1 ) ;
if ( m_iState = = WEAPON_IS_ACTIVE )
{
BaseEntityRecordingState_t * pBaseEntity = ( BaseEntityRecordingState_t * ) msg - > GetPtr ( " baseentity " ) ;
pBaseEntity - > m_bVisible = true ;
}
}
if ( nModelIndex ! = nWorldModelIndex )
{
SetModelIndex ( nModelIndex ) ;
}
}