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.
557 lines
16 KiB
557 lines
16 KiB
//========= Copyright 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: Gets the local client's active weapon, if any. |
|
//----------------------------------------------------------------------------- |
|
C_BaseCombatWeapon *GetActiveWeapon( void ) |
|
{ |
|
C_BasePlayer *player = C_BasePlayer::GetLocalPlayer(); |
|
|
|
if ( !player ) |
|
return NULL; |
|
|
|
return player->GetActiveWeapon(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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 && GetOwner() && !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; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: To wrap PORTAL mod specific functionality into one place |
|
//----------------------------------------------------------------------------- |
|
static inline bool ShouldDrawLocalPlayerViewModel( void ) |
|
{ |
|
#if defined( PORTAL ) |
|
return false; |
|
#else |
|
return !C_BasePlayer::ShouldDrawLocalPlayer(); |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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 (GetOwner() == C_BasePlayer::GetLocalPlayer()) |
|
{ |
|
m_bJustRestored = true; |
|
} |
|
} |
|
|
|
int C_BaseCombatWeapon::GetWorldModelIndex( void ) |
|
{ |
|
if ( GameRules() ) |
|
{ |
|
const char *pBaseName = modelinfo->GetModelName( modelinfo->GetModel( m_iWorldModelIndex ) ); |
|
const char *pTranslatedName = GameRules()->TranslateEffectForVisionFilter( "weapons", pBaseName ); |
|
|
|
if ( pTranslatedName != pBaseName ) |
|
{ |
|
return modelinfo->GetModelIndex( pTranslatedName ); |
|
} |
|
} |
|
|
|
return m_iWorldModelIndex; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : bnewentity - |
|
//----------------------------------------------------------------------------- |
|
void C_BaseCombatWeapon::OnDataChanged( DataUpdateType_t updateType ) |
|
{ |
|
BaseClass::OnDataChanged(updateType); |
|
|
|
CHandle< C_BaseCombatWeapon > handle = this; |
|
|
|
// If it's being carried by the *local* player, on the first update, |
|
// find the registered weapon for this ID |
|
|
|
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); |
|
C_BaseCombatCharacter *pOwner = GetOwner(); |
|
|
|
// check if weapon is carried by local player |
|
bool bIsLocalPlayer = pPlayer && pPlayer == pOwner; |
|
if ( bIsLocalPlayer && ShouldDrawLocalPlayerViewModel() ) // TODO: figure out the purpose of the ShouldDrawLocalPlayer() test. |
|
{ |
|
// 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" ); |
|
} |
|
} |
|
} |
|
else // weapon carried by other player or not at all |
|
{ |
|
int overrideModelIndex = CalcOverrideModelIndex(); |
|
if( overrideModelIndex != -1 && overrideModelIndex != GetModelIndex() ) |
|
{ |
|
SetModelIndex( overrideModelIndex ); |
|
} |
|
} |
|
|
|
if ( updateType == DATA_UPDATE_CREATED ) |
|
{ |
|
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() && !C_BasePlayer::ShouldDrawLocalPlayer()) |
|
return SHADOWS_NONE; |
|
|
|
return SHADOWS_RENDER_TO_TEXTURE; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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 ( g_pClientMode->ShouldDrawCrosshair() ) |
|
{ |
|
DrawCrosshair(); |
|
} |
|
|
|
// ammo drawing has been moved into hud_ammo.cpp |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Draw the weapon's crosshair |
|
//----------------------------------------------------------------------------- |
|
void C_BaseCombatWeapon::DrawCrosshair() |
|
{ |
|
C_BasePlayer *player = C_BasePlayer::GetLocalPlayer(); |
|
if ( !player ) |
|
return; |
|
|
|
Color clr = gHUD.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 *pCrosshair = GET_HUDELEMENT( CHudCrosshair ); |
|
if ( !pCrosshair ) |
|
return; |
|
|
|
// Find out if this weapon's auto-aimed onto a target |
|
bool bOnTarget = ( m_iState == WEAPON_IS_ONTARGET ); |
|
|
|
if ( player->GetFOV() >= 90 ) |
|
{ |
|
// normal crosshairs |
|
if ( bOnTarget && GetWpnData().iconAutoaim ) |
|
{ |
|
clr[3] = 255; |
|
|
|
pCrosshair->SetCrosshair( GetWpnData().iconAutoaim, clr ); |
|
} |
|
else if ( GetWpnData().iconCrosshair ) |
|
{ |
|
clr[3] = 255; |
|
pCrosshair->SetCrosshair( GetWpnData().iconCrosshair, clr ); |
|
} |
|
else |
|
{ |
|
pCrosshair->ResetCrosshair(); |
|
} |
|
} |
|
else |
|
{ |
|
Color white( 255, 255, 255, 255 ); |
|
|
|
// zoomed crosshairs |
|
if (bOnTarget && GetWpnData().iconZoomedAutoaim) |
|
pCrosshair->SetCrosshair(GetWpnData().iconZoomedAutoaim, white); |
|
else if ( GetWpnData().iconZoomedCrosshair ) |
|
pCrosshair->SetCrosshair( GetWpnData().iconZoomedCrosshair, white ); |
|
else |
|
pCrosshair->ResetCrosshair(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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 ( GetOwner() == C_BasePlayer::GetLocalPlayer() ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns true if this client is carrying this weapon and is |
|
// using the view models |
|
//----------------------------------------------------------------------------- |
|
bool C_BaseCombatWeapon::ShouldDrawUsingViewModel( void ) |
|
{ |
|
return IsCarriedByLocalPlayer() && !C_BasePlayer::ShouldDrawLocalPlayer(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// 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(); |
|
} |
|
|
|
QAngle vDummy; |
|
if ( IsActiveByLocalPlayer() && ShouldDrawLocalPlayerViewModel() ) |
|
{ |
|
C_BasePlayer *player = ToBasePlayer( pEnt ); |
|
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; |
|
|
|
if ( !pOwner->ShouldDraw() ) |
|
{ |
|
// Our owner is invisible. |
|
// This also tests whether the player is zoomed in, in which case you don't want to draw the weapon. |
|
return false; |
|
} |
|
|
|
// 3rd person mode? |
|
if ( !ShouldDrawLocalPlayerViewModel() ) |
|
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; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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 ) |
|
{ |
|
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; |
|
} |
|
|
|
return BaseClass::DrawModel( flags ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Allows the client-side entity to override what the network tells it to use for |
|
// a model. This is used for third person mode, specifically in HL2 where the |
|
// the weapon timings are on the view model and not the world model. That means the |
|
// server needs to use the view model, but the client wants to use the world model. |
|
//----------------------------------------------------------------------------- |
|
int C_BaseCombatWeapon::CalcOverrideModelIndex() |
|
{ |
|
C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer(); |
|
if ( localplayer && |
|
localplayer == GetOwner() && |
|
ShouldDrawLocalPlayerViewModel() ) |
|
{ |
|
return BaseClass::CalcOverrideModelIndex(); |
|
} |
|
else |
|
{ |
|
return GetWorldModelIndex(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// 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 ); |
|
} |
|
}
|
|
|