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.
545 lines
15 KiB
545 lines
15 KiB
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// |
|
// |
|
// 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. |
|
if ( !IsDormant() && bDormant && !IsCarriedByLocalPlayer() ) |
|
{ |
|
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; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
static inline bool ShouldDrawLocalPlayer( C_BasePlayer *pl ) |
|
{ |
|
|
|
Assert( pl ); |
|
return pl->ShouldDrawLocalPlayer(); |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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 |
|
if ( C_BasePlayer::IsLocalPlayer( GetOwner() ) ) |
|
{ |
|
m_bJustRestored = true; |
|
} |
|
} |
|
|
|
int C_BaseCombatWeapon::GetWorldModelIndex( void ) |
|
{ |
|
return m_iWorldModelIndex; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : bnewentity - |
|
//----------------------------------------------------------------------------- |
|
void C_BaseCombatWeapon::OnDataChanged( DataUpdateType_t updateType ) |
|
{ |
|
BaseClass::OnDataChanged( updateType ); |
|
|
|
// If it's being carried by the *local* player, on the first update, |
|
// find the registered weapon for this ID |
|
|
|
C_BaseCombatCharacter *pOwner = GetOwner(); |
|
C_BasePlayer *pPlayer = ToBasePlayer( pOwner ); |
|
|
|
// check if weapon is carried by local player |
|
bool bIsLocalPlayer = C_BasePlayer::IsLocalPlayer( pPlayer ); |
|
if ( bIsLocalPlayer ) |
|
{ |
|
ACTIVE_SPLITSCREEN_PLAYER_GUARD( C_BasePlayer::GetSplitScreenSlotForPlayer( pPlayer ) ); |
|
|
|
// If I was just picked up, or created & immediately carried, add myself to this client's list of weapons |
|
if ( ( m_iState != WEAPON_NOT_CARRIED ) && |
|
( m_iOldState == WEAPON_NOT_CARRIED ) ) |
|
{ |
|
// Tell the HUD this weapon's been picked up |
|
if ( ShouldDrawPickup() ) |
|
{ |
|
CBaseHudWeaponSelection *pHudSelection = GetHudWeaponSelection(); |
|
if ( pHudSelection ) |
|
{ |
|
pHudSelection->OnWeaponPickup( this ); |
|
} |
|
|
|
pPlayer->EmitSound( "Player.PickupWeapon" ); |
|
} |
|
} |
|
} |
|
|
|
UpdateVisibility(); |
|
|
|
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; |
|
|
|
if (IsCarriedByLocalPlayer()) |
|
return SHADOWS_NONE; |
|
|
|
return (m_iState != WEAPON_IS_CARRIED_BY_PLAYER) ? SHADOWS_RENDER_TO_TEXTURE : SHADOWS_NONE; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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() |
|
{ |
|
if ( GetClientMode()->ShouldDrawCrosshair() ) |
|
{ |
|
DrawCrosshair(); |
|
} |
|
|
|
// ammo drawing has been moved into hud_ammo.cpp |
|
} |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Draw the weapon's crosshair |
|
//----------------------------------------------------------------------------- |
|
void C_BaseCombatWeapon::DrawCrosshair() |
|
{ |
|
#ifndef INFESTED_DLL |
|
C_BasePlayer *player = C_BasePlayer::GetLocalPlayer(); |
|
if ( !player ) |
|
return; |
|
|
|
Color clr = GetHud().m_clrNormal; |
|
/* |
|
|
|
// 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; |
|
} |
|
} |
|
} |
|
*/ |
|
|
|
CHudCrosshair *crosshair = GET_HUDELEMENT( CHudCrosshair ); |
|
if ( !crosshair ) |
|
return; |
|
|
|
// Find out if this weapon's auto-aimed onto a target |
|
bool bOnTarget = ( m_iState == WEAPON_IS_ACTIVE ) && player->m_fOnTarget; |
|
|
|
if ( player->GetFOV() >= 90 ) |
|
{ |
|
// normal crosshairs |
|
if ( bOnTarget && GetWpnData().iconAutoaim ) |
|
{ |
|
clr[3] = 255; |
|
|
|
crosshair->SetCrosshair( GetWpnData().iconAutoaim, clr ); |
|
} |
|
else if ( GetWpnData().iconCrosshair ) |
|
{ |
|
clr[3] = 255; |
|
crosshair->SetCrosshair( GetWpnData().iconCrosshair, clr ); |
|
} |
|
else |
|
{ |
|
crosshair->ResetCrosshair(); |
|
} |
|
} |
|
else |
|
{ |
|
Color white( 255, 255, 255, 255 ); |
|
|
|
// zoomed crosshairs |
|
if (bOnTarget && GetWpnData().iconZoomedAutoaim) |
|
crosshair->SetCrosshair(GetWpnData().iconZoomedAutoaim, white); |
|
else if ( GetWpnData().iconZoomedCrosshair ) |
|
crosshair->SetCrosshair( GetWpnData().iconZoomedCrosshair, white ); |
|
else |
|
crosshair->ResetCrosshair(); |
|
} |
|
#endif |
|
} |
|
//----------------------------------------------------------------------------- |
|
// 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; |
|
|
|
return ( C_BasePlayer::IsLocalPlayer( GetOwner() ) ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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(); |
|
} |
|
|
|
C_BasePlayer *player = ToBasePlayer( pEnt ); |
|
bool bUseViewModel = false; |
|
if ( C_BasePlayer::IsLocalPlayer( pEnt ) ) |
|
{ |
|
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( pEnt ); |
|
bUseViewModel = !player->ShouldDrawLocalPlayer(); |
|
} |
|
|
|
QAngle vDummy; |
|
if ( IsActiveByLocalPlayer() && bUseViewModel ) |
|
{ |
|
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; |
|
|
|
// 3rd person mode |
|
if ( pLocalPlayer->ShouldDrawLocalPlayer() ) |
|
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; |
|
} |
|
|
|
|
|
//---------------------------------------------------------------------------- |
|
// 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; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Render the weapon. Draw the Viewmodel if the weapon's being carried |
|
// by this player, otherwise draw the worldmodel. |
|
//----------------------------------------------------------------------------- |
|
int C_BaseCombatWeapon::DrawModel( int flags, const RenderableInstance_t &instance ) |
|
{ |
|
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; |
|
} |
|
|
|
// See comment below |
|
EnsureCorrectRenderingModel(); |
|
|
|
return BaseClass::DrawModel( flags, instance ); |
|
} |
|
|
|
// 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() |
|
{ |
|
C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer(); |
|
if ( localplayer && |
|
localplayer == GetOwner() && |
|
!localplayer->ShouldDrawLocalPlayer() ) |
|
{ |
|
return; |
|
} |
|
|
|
// 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() ) |
|
{ |
|
SetSequence( 0 ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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 ); |
|
} |
|
}
|
|
|