1477 lines
41 KiB
C++
1477 lines
41 KiB
C++
#include "cbase.h"
|
|
#include "in_buttons.h"
|
|
|
|
#ifdef CLIENT_DLL
|
|
#define CASW_Weapon C_ASW_Weapon
|
|
#define CASW_Marine C_ASW_Marine
|
|
#define CBasePlayer C_BasePlayer
|
|
#include "c_asw_weapon.h"
|
|
#include "c_asw_marine.h"
|
|
#include "c_asw_player.h"
|
|
#include "c_asw_marine_resource.h"
|
|
#include "c_basecombatcharacter.h"
|
|
#include "c_baseplayer.h"
|
|
#include "c_asw_fx.h"
|
|
#include "prediction.h"
|
|
#include "igameevents.h"
|
|
#define CASW_Marine_Resource C_ASW_Marine_Resource
|
|
#define CRecipientFilter C_RecipientFilter
|
|
#else
|
|
#include "asw_lag_compensation.h"
|
|
#include "asw_weapon.h"
|
|
#include "asw_marine.h"
|
|
#include "asw_marine_resource.h"
|
|
#include "asw_player.h"
|
|
#include "asw_marine_speech.h"
|
|
#include "asw_gamestats.h"
|
|
#include "ammodef.h"
|
|
#include "asw_achievements.h"
|
|
#endif
|
|
#include "asw_equipment_list.h"
|
|
#include "asw_weapon_parse.h"
|
|
#include "asw_marine_skills.h"
|
|
#include "asw_weapon_ammo_bag_shared.h"
|
|
#include "asw_gamerules.h"
|
|
#include "asw_melee_system.h"
|
|
#include "SoundEmitterSystem/isoundemittersystembase.h"
|
|
#include "particle_parse.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
ConVar asw_weapon_max_shooting_distance( "asw_weapon_max_shooting_distance", "1500", FCVAR_REPLICATED, "Maximum distance of the hitscan weapons." );
|
|
ConVar asw_weapon_force_scale("asw_weapon_force_scale", "1.0f", FCVAR_REPLICATED, "Force of weapon shots");
|
|
ConVar asw_fast_reload_enabled( "asw_fast_reload_enabled", "1", FCVAR_CHEAT | FCVAR_REPLICATED, "Use fast reload mechanic?" );
|
|
|
|
#ifndef CLIENT_DLL
|
|
extern ConVar asw_debug_marine_damage;
|
|
extern ConVar asw_DebugAutoAim;
|
|
#endif
|
|
|
|
BEGIN_DEFINE_LOGGING_CHANNEL( LOG_ASW_Weapons, "ASWWeapons", 0, LS_MESSAGE );
|
|
ADD_LOGGING_CHANNEL_TAG( "AlienSwarm" );
|
|
ADD_LOGGING_CHANNEL_TAG( "Weapons" );
|
|
END_DEFINE_LOGGING_CHANNEL();
|
|
|
|
#ifdef ASW_VERBOSE_MESSAGES
|
|
BEGIN_DEFINE_LOGGING_CHANNEL( LOG_ASW_WeaponDeveloper, "ASWVerboseWeapons", 0, LS_ERROR );
|
|
ADD_LOGGING_CHANNEL_TAG( "AlienSwarmVerbose" );
|
|
ADD_LOGGING_CHANNEL_TAG( "Weapons" );
|
|
END_DEFINE_LOGGING_CHANNEL();
|
|
#endif // #ifdef ASW_VERBOSE_MESSAGES
|
|
|
|
BEGIN_DEFINE_LOGGING_CHANNEL( LOG_ASW_Damage, "ASWDamage", 0, LS_MESSAGE );
|
|
ADD_LOGGING_CHANNEL_TAG( "AlienSwarm" );
|
|
ADD_LOGGING_CHANNEL_TAG( "Damage" );
|
|
END_DEFINE_LOGGING_CHANNEL();
|
|
|
|
void CASW_Weapon::Spawn()
|
|
{
|
|
//InitializeAttributes();
|
|
|
|
BaseClass::Spawn();
|
|
|
|
SetModel( GetWorldModel() );
|
|
|
|
m_nSkin = GetWeaponInfo()->m_iPlayerModelSkin;
|
|
}
|
|
|
|
// get the player owner of this weapon (either the marine's commander if the weapon is
|
|
// being held by marine, or the player directly if a player is holding this weapon)
|
|
CASW_Player* CASW_Weapon::GetCommander()
|
|
{
|
|
CASW_Player *pOwner = NULL;
|
|
CASW_Marine *pMarine = NULL;
|
|
|
|
pMarine = dynamic_cast<CASW_Marine*>( GetOwner() );
|
|
if ( pMarine )
|
|
{
|
|
pOwner = pMarine->GetCommander();
|
|
}
|
|
else
|
|
{
|
|
pOwner = ToASW_Player( dynamic_cast<CBasePlayer*>( GetOwner() ) );
|
|
}
|
|
|
|
return pOwner;
|
|
}
|
|
|
|
CASW_Marine* CASW_Weapon::GetMarine()
|
|
{
|
|
return dynamic_cast<CASW_Marine*>(GetOwner());
|
|
}
|
|
|
|
#if PREDICTION_ERROR_CHECK_LEVEL > 0
|
|
extern void DiffPrint( bool bServer, int nCommandNumber, char const *fmt, ... );
|
|
void CASW_Weapon::DiffPrint( char const *fmt, ... )
|
|
{
|
|
CASW_Player *pPlayer = GetCommander();
|
|
if ( !pPlayer )
|
|
return;
|
|
|
|
va_list argptr;
|
|
char string[1024];
|
|
va_start (argptr,fmt);
|
|
Q_vsnprintf(string, sizeof( string ), fmt,argptr);
|
|
va_end (argptr);
|
|
|
|
::DiffPrint( CBaseEntity::IsServer(), pPlayer->CurrentCommandNumber(), "%s", string );
|
|
}
|
|
#else
|
|
void CASW_Weapon::DiffPrint(char const *fmt, ...)
|
|
{
|
|
// nothing
|
|
}
|
|
#endif
|
|
|
|
void CASW_Weapon::ItemBusyFrame( void )
|
|
{
|
|
CASW_Marine* pMarine = GetMarine();
|
|
if ( !pMarine )
|
|
return;
|
|
|
|
bool bAttack1, bAttack2, bReload, bOldReload, bOldAttack1;
|
|
GetButtons(bAttack1, bAttack2, bReload, bOldReload, bOldAttack1 );
|
|
|
|
// check for clearing our weapon switching bool
|
|
if (m_bSwitchingWeapons && gpGlobals->curtime > m_flNextPrimaryAttack)
|
|
{
|
|
m_bSwitchingWeapons = false;
|
|
}
|
|
|
|
// check for clearing our firing bool from reloading
|
|
if (m_bInReload && gpGlobals->curtime > m_fReloadClearFiringTime)
|
|
{
|
|
ClearIsFiring();
|
|
}
|
|
|
|
if ( (bReload && !bOldReload) && UsesClipsForAmmo1() && asw_fast_reload_enabled.GetBool() )
|
|
{
|
|
if ( m_bInReload )
|
|
{
|
|
// check for a fast reload
|
|
//Msg("%f Check for fast reload while busy\n", gpGlobals->curtime);
|
|
if (gpGlobals->curtime >= m_fFastReloadStart && gpGlobals->curtime <= m_fFastReloadEnd)
|
|
{
|
|
// todo: reduce next attack time
|
|
m_fFastReloadEnd = 0;
|
|
m_fFastReloadStart = 0;
|
|
|
|
CBaseCombatCharacter *pOwner = GetOwner();
|
|
if ( pOwner )
|
|
{
|
|
float flSucceedDelay = gpGlobals->curtime + 0.5f;
|
|
pOwner->SetNextAttack( flSucceedDelay );
|
|
m_flNextPrimaryAttack = m_flNextSecondaryAttack = flSucceedDelay;
|
|
}
|
|
|
|
// TODO: hook up anim
|
|
//pMarine->DoAnimationEvent( PLAYERANIMEVENT_RELOAD_SUCCEED );
|
|
|
|
DispatchParticleEffect( "fast_reload", PATTACH_POINT_FOLLOW, this, "muzzle" );
|
|
pMarine->m_flPreventLaserSightTime = gpGlobals->curtime + 2.5f;
|
|
|
|
#ifdef GAME_DLL
|
|
pMarine->m_nFastReloadsInARow++;
|
|
|
|
IGameEvent * event = gameeventmanager->CreateEvent( "fast_reload" );
|
|
if ( event )
|
|
{
|
|
event->SetInt( "marine", pMarine->entindex() );
|
|
event->SetInt( "reloads", pMarine->m_nFastReloadsInARow );
|
|
gameeventmanager->FireEvent( event );
|
|
}
|
|
|
|
if ( pMarine->m_nFastReloadsInARow >= 4 && pMarine->IsInhabited() )
|
|
{
|
|
if ( pMarine->GetMarineResource() )
|
|
{
|
|
pMarine->GetMarineResource()->m_bDidFastReloadsInARow = true;
|
|
}
|
|
|
|
if ( pMarine->GetCommander() )
|
|
{
|
|
pMarine->GetCommander()->AwardAchievement( ACHIEVEMENT_ASW_FAST_RELOADS_IN_A_ROW );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
EmitSound( "FastReload.Success" );
|
|
//Msg("%f RELOAD SUCCESS! - bAttack1 = %d, bOldAttack1 = %d\n", gpGlobals->curtime, bAttack1, bOldAttack1 );
|
|
//Msg( "S: %f - %f - %f RELOAD SUCCESS! -- Progress = %f\n", gpGlobals->curtime, fFastStart, fFastEnd, flProgress );
|
|
#ifdef GAME_DLL
|
|
pMarine->GetMarineSpeech()->PersonalChatter(CHATTER_SELECTION);
|
|
#endif
|
|
m_bFastReloadSuccess = true;
|
|
m_bFastReloadFailure = false;
|
|
}
|
|
else if (m_fFastReloadStart != 0)
|
|
{
|
|
EmitSound( "FastReload.Miss" );
|
|
//Msg("%f RELOAD MISSED! - bAttack1 = %d, bOldAttack1 = %d\n", gpGlobals->curtime, bAttack1, bOldAttack1 );
|
|
//Msg( "S: %f - %f - %f RELOAD MISSED! -- Progress = %f\n", gpGlobals->curtime, fFastStart, fFastEnd, flProgress );
|
|
m_fFastReloadEnd = 0;
|
|
m_fFastReloadStart = 0;
|
|
|
|
CBaseCombatCharacter *pOwner = GetOwner();
|
|
if ( pOwner )
|
|
{
|
|
float flMissDelay = MAX( gpGlobals->curtime + 2.0f, m_flNextPrimaryAttack + 1.0f );
|
|
pOwner->SetNextAttack( flMissDelay );
|
|
m_flNextPrimaryAttack = m_flNextSecondaryAttack = flMissDelay;
|
|
m_flReloadFailTime = m_flNextPrimaryAttack - gpGlobals->curtime;
|
|
}
|
|
|
|
// TODO: hook up anim
|
|
//pMarine->DoAnimationEvent( PLAYERANIMEVENT_RELOAD_FAIL );
|
|
|
|
#ifdef GAME_DLL
|
|
pMarine->m_nFastReloadsInARow = 0;
|
|
#endif
|
|
|
|
DispatchParticleEffect( "reload_fail", PATTACH_POINT_FOLLOW, this, "muzzle" );
|
|
|
|
#ifdef GAME_DLL
|
|
pMarine->GetMarineSpeech()->PersonalChatter(CHATTER_PAIN_SMALL);
|
|
#endif
|
|
m_bFastReloadSuccess = false;
|
|
m_bFastReloadFailure = true;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef CLIENT_DLL
|
|
if ( m_bInReload )
|
|
{
|
|
float fStart = m_fReloadStart;
|
|
float fNext = MAX( m_flNextPrimaryAttack, GetOwner() ? GetOwner()->GetNextAttack() : 0 );
|
|
float fTotalTime = fNext - fStart;
|
|
if (fTotalTime <= 0)
|
|
fTotalTime = 0.1f;
|
|
|
|
m_fReloadProgress = (gpGlobals->curtime - fStart) / fTotalTime;
|
|
}
|
|
else
|
|
{
|
|
m_fReloadProgress = 0;
|
|
}
|
|
//Msg( "S: %f Reload Progress = %f\n", gpGlobals->curtime, m_fReloadProgress );
|
|
#endif //CLIENT_DLL
|
|
}
|
|
|
|
// check player or marine commander's buttons for firing/reload/etc
|
|
void CASW_Weapon::ItemPostFrame( void )
|
|
{
|
|
//CBasePlayer *pOwner = GetCommander();
|
|
CASW_Marine* pOwner = GetMarine();
|
|
if (!pOwner)
|
|
return;
|
|
|
|
bool bThisActive = ( pOwner && pOwner->GetActiveWeapon() == this );
|
|
|
|
bool bAttack1, bAttack2, bReload, bOldReload, bOldAttack1;
|
|
GetButtons(bAttack1, bAttack2, bReload, bOldReload, bOldAttack1 );
|
|
|
|
// check for clearing our weapon switching bool
|
|
if (m_bSwitchingWeapons && gpGlobals->curtime > m_flNextPrimaryAttack)
|
|
{
|
|
m_bSwitchingWeapons = false;
|
|
}
|
|
|
|
// check for clearing our firing bool from reloading
|
|
if (m_bInReload && gpGlobals->curtime > m_fReloadClearFiringTime)
|
|
{
|
|
ClearIsFiring();
|
|
}
|
|
|
|
if ( m_bShotDelayed && gpGlobals->curtime > m_flDelayedFire )
|
|
{
|
|
DelayedAttack();
|
|
}
|
|
|
|
if ( UsesClipsForAmmo1() )
|
|
{
|
|
CheckReload();
|
|
}
|
|
|
|
bool bFired = false;
|
|
if ( bThisActive )
|
|
{
|
|
//Track the duration of the fire
|
|
//FIXME: Check for IN_ATTACK2 as well?
|
|
//FIXME: What if we're calling ItemBusyFrame?
|
|
m_fFireDuration = bAttack1 ? ( m_fFireDuration + gpGlobals->frametime ) : 0.0f;
|
|
|
|
if (bAttack2 && (m_flNextSecondaryAttack <= gpGlobals->curtime) && gpGlobals->curtime > pOwner->m_fFFGuardTime)
|
|
{
|
|
if ( SecondaryAttackUsesPrimaryAmmo() )
|
|
{
|
|
if ( !IsMeleeWeapon() &&
|
|
(( UsesClipsForAmmo1() && !(this->PrimaryAmmoLoaded())) || ( !UsesClipsForAmmo1() && pOwner->GetAmmoCount(m_iPrimaryAmmoType)<=0 )) )
|
|
{
|
|
HandleFireOnEmpty();
|
|
}
|
|
else
|
|
{
|
|
bFired = true;
|
|
SecondaryAttack();
|
|
|
|
#ifndef CLIENT_DLL
|
|
if ( pOwner->IsInhabited() )
|
|
{
|
|
IGameEvent * event = gameeventmanager->CreateEvent( "player_alt_fire" );
|
|
if ( event )
|
|
{
|
|
CASW_Player *pPlayer = pOwner->GetCommander();
|
|
event->SetInt( "userid", ( pPlayer ? pPlayer->GetUserID() : 0 ) );
|
|
gameeventmanager->FireEvent( event );
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else if ( UsesSecondaryAmmo() &&
|
|
( ( UsesClipsForAmmo2() && m_iClip2 <= 0 ) ||
|
|
( !UsesClipsForAmmo2() && pOwner->GetAmmoCount( m_iSecondaryAmmoType ) <= 0 ) ) )
|
|
{
|
|
if ( m_flNextEmptySoundTime < gpGlobals->curtime )
|
|
{
|
|
WeaponSound( EMPTY );
|
|
m_flNextSecondaryAttack = m_flNextEmptySoundTime = gpGlobals->curtime + 0.5;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bFired = true;
|
|
SecondaryAttack();
|
|
|
|
#ifndef CLIENT_DLL
|
|
if ( pOwner->IsInhabited() )
|
|
{
|
|
IGameEvent * event = gameeventmanager->CreateEvent( "player_alt_fire" );
|
|
if ( event )
|
|
{
|
|
CASW_Player *pPlayer = pOwner->GetCommander();
|
|
event->SetInt( "userid", ( pPlayer ? pPlayer->GetUserID() : 0 ) );
|
|
gameeventmanager->FireEvent( event );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Secondary ammo doesn't have a reload animation
|
|
// this code makes secondary ammo come from nowhere!
|
|
/*
|
|
if ( UsesClipsForAmmo2() )
|
|
{
|
|
// reload clip2 if empty
|
|
if (m_iClip2 < 1)
|
|
{
|
|
pOwner->RemoveAmmo( 1, m_iSecondaryAmmoType );
|
|
m_iClip2 = m_iClip2 + 1;
|
|
}
|
|
}*/
|
|
}
|
|
}
|
|
|
|
if ( !bFired && bAttack1 && (m_flNextPrimaryAttack <= gpGlobals->curtime) && gpGlobals->curtime > pOwner->m_fFFGuardTime)
|
|
{
|
|
// Clip empty? Or out of ammo on a no-clip weapon?
|
|
if ( !IsMeleeWeapon() &&
|
|
(( UsesClipsForAmmo1() && !(this->PrimaryAmmoLoaded())) || ( !UsesClipsForAmmo1() && pOwner->GetAmmoCount(m_iPrimaryAmmoType)<=0 )) )
|
|
{
|
|
HandleFireOnEmpty();
|
|
}
|
|
else if (pOwner->GetWaterLevel() == 3 && 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, reset the firing time
|
|
if ( pOwner && bAttack1 )
|
|
{
|
|
#ifdef CLIENT_DLL
|
|
//Msg("[Client] setting nextprimaryattack to now %f\n", gpGlobals->curtime);
|
|
#else
|
|
//Msg("[Server] setting nextprimaryattack to now %f\n", gpGlobals->curtime);
|
|
#endif
|
|
m_flNextPrimaryAttack = gpGlobals->curtime;
|
|
}
|
|
PrimaryAttack();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bAttack1) // clear our firing var if we don't have the attack button held down (not totally accurate since firing could continue for some time after pulling the trigger, but it's good enough for our purposes)
|
|
{
|
|
m_bIsFiring = false; // NOTE: Only want to clear primary fire IsFiring bool here (i.e. don't call ClearIsFiring as that'll clear secondary fire too, in subclasses that have it)
|
|
if ( bOldAttack1 )
|
|
{
|
|
OnStoppedFiring();
|
|
}
|
|
}
|
|
|
|
// -----------------------
|
|
// Reload pressed / Clip Empty
|
|
// -----------------------
|
|
if ( bReload && UsesClipsForAmmo1())
|
|
{
|
|
if ( m_bInReload )
|
|
{
|
|
// todo: check for a fast reload
|
|
//Msg("Check for fast reload\n");
|
|
}
|
|
else
|
|
{
|
|
// reload when reload is pressed, or if no buttons are down and weapon is empty.
|
|
Reload();
|
|
m_fFireDuration = 0.0f;
|
|
}
|
|
}
|
|
|
|
// -----------------------
|
|
// No buttons down
|
|
// -----------------------
|
|
if (!(bAttack1 || bAttack2 || bReload))
|
|
{
|
|
// no fire buttons down or reloading
|
|
if ( !ReloadOrSwitchWeapons() && ( m_bInReload == false ) )
|
|
{
|
|
WeaponIdle();
|
|
}
|
|
}
|
|
}
|
|
|
|
// just dry fire by default
|
|
void CASW_Weapon::SecondaryAttack( void )
|
|
{
|
|
// Only the player fires this way so we can cast
|
|
CASW_Player *pPlayer = GetCommander();
|
|
if (!pPlayer)
|
|
return;
|
|
|
|
CASW_Marine *pMarine = GetMarine();
|
|
if (!pMarine)
|
|
return;
|
|
|
|
SendWeaponAnim( ACT_VM_DRYFIRE );
|
|
BaseClass::WeaponSound( EMPTY );
|
|
m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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 CASW_Weapon::ReloadOrSwitchWeapons( void )
|
|
{
|
|
CASW_Player *pPlayer = NULL;
|
|
CASW_Marine *pMarine = NULL;
|
|
bool bAutoReload = true;
|
|
|
|
pMarine = dynamic_cast<CASW_Marine*>(GetOwner());
|
|
if (pMarine)
|
|
{
|
|
if (pMarine->GetCommander() && pMarine->IsInhabited())
|
|
{
|
|
pPlayer = pMarine->GetCommander();
|
|
bAutoReload = pPlayer->ShouldAutoReload();
|
|
}
|
|
}
|
|
|
|
// if (HasAnyAmmo()) // asw add later!
|
|
m_bFireOnEmpty = false;
|
|
if ( UsesClipsForAmmo1() &&
|
|
(m_iClip1 == 0) &&
|
|
(GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD) == false &&
|
|
bAutoReload &&
|
|
m_flNextPrimaryAttack < gpGlobals->curtime &&
|
|
m_flNextSecondaryAttack < gpGlobals->curtime && !m_bInReload)
|
|
{
|
|
// if we're successfully reloading, we're done
|
|
if ( Reload() )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
ClearIsFiring();
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Return true if this weapon has some ammo
|
|
//-----------------------------------------------------------------------------
|
|
bool CASW_Weapon::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;
|
|
|
|
CBaseCombatCharacter *pOwner = GetOwner();
|
|
if ( !pOwner )
|
|
return false;
|
|
return ( m_iClip1 > 0 || pOwner->GetAmmoCount( m_iPrimaryAmmoType ) || m_iClip2 > 0 || pOwner->GetAmmoCount( m_iSecondaryAmmoType ) );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Return true if this weapon has some ammo loaded in the primary clip
|
|
//
|
|
// Useful because some weapons (like the ammo bag) may have special conditions
|
|
// other than how many bullets are in the clip - They can specialize this fn
|
|
//-----------------------------------------------------------------------------
|
|
bool CASW_Weapon::PrimaryAmmoLoaded( void )
|
|
{
|
|
return (m_iClip1 > 0);
|
|
}
|
|
|
|
|
|
void CASW_Weapon::PrimaryAttack( void )
|
|
{
|
|
// If my clip is empty (and I use clips) start reload
|
|
if ( UsesClipsForAmmo1() && !m_iClip1 )
|
|
{
|
|
Reload();
|
|
return;
|
|
}
|
|
|
|
CASW_Player *pPlayer = GetCommander();
|
|
CASW_Marine *pMarine = GetMarine();
|
|
if ( !pMarine )
|
|
return;
|
|
|
|
m_bIsFiring = true;
|
|
// MUST call sound before removing a round from the clip of a CMachineGun
|
|
WeaponSound(SINGLE);
|
|
|
|
if (m_iClip1 <= AmmoClickPoint())
|
|
{
|
|
LowAmmoSound();
|
|
}
|
|
|
|
// tell the marine to tell its weapon to draw the muzzle flash
|
|
pMarine->DoMuzzleFlash();
|
|
|
|
// sets the animation on the weapon model itself
|
|
SendWeaponAnim( GetPrimaryAttackActivity() );
|
|
|
|
// sets the animation on the marine holding this weapon
|
|
//pMarine->SetAnimation( PLAYER_ATTACK1 );
|
|
|
|
#ifdef GAME_DLL // check for turning on lag compensation
|
|
if (pPlayer && pMarine->IsInhabited())
|
|
{
|
|
CASW_Lag_Compensation::RequestLagCompensation( pPlayer, pPlayer->GetCurrentUserCommand() );
|
|
}
|
|
#endif
|
|
|
|
FireBulletsInfo_t info;
|
|
info.m_vecSrc = pMarine->Weapon_ShootPosition( );
|
|
if ( pPlayer && pMarine->IsInhabited() )
|
|
{
|
|
info.m_vecDirShooting = pPlayer->GetAutoaimVectorForMarine(pMarine, GetAutoAimAmount(), GetVerticalAdjustOnlyAutoAimAmount()); // 45 degrees = 0.707106781187
|
|
}
|
|
else
|
|
{
|
|
#ifdef CLIENT_DLL
|
|
Msg("Error, clientside firing of a weapon that's being controlled by an AI marine\n");
|
|
#else
|
|
info.m_vecDirShooting = pMarine->GetActualShootTrajectory( info.m_vecSrc );
|
|
#endif
|
|
}
|
|
|
|
// 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 )
|
|
{
|
|
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 );
|
|
m_iClip1 -= info.m_iShots;
|
|
|
|
#ifdef GAME_DLL
|
|
CASW_Marine *pMarine = GetMarine();
|
|
if (pMarine && m_iClip1 <= 0 && pMarine->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
|
|
{
|
|
// check he doesn't have ammo in an ammo bay
|
|
CASW_Weapon_Ammo_Bag* pAmmoBag = dynamic_cast<CASW_Weapon_Ammo_Bag*>(pMarine->GetASWWeapon(0));
|
|
if (!pAmmoBag)
|
|
pAmmoBag = dynamic_cast<CASW_Weapon_Ammo_Bag*>(pMarine->GetASWWeapon(1));
|
|
if (!pAmmoBag || !pAmmoBag->CanGiveAmmoToWeapon(this))
|
|
pMarine->OnWeaponOutOfAmmo(true);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
info.m_iShots = MIN( info.m_iShots, pMarine->GetAmmoCount( m_iPrimaryAmmoType ) );
|
|
pMarine->RemoveAmmo( info.m_iShots, m_iPrimaryAmmoType );
|
|
}
|
|
|
|
info.m_flDistance = asw_weapon_max_shooting_distance.GetFloat();
|
|
info.m_iAmmoType = m_iPrimaryAmmoType;
|
|
info.m_iTracerFreq = 1; // asw tracer test everytime
|
|
info.m_flDamageForceScale = asw_weapon_force_scale.GetFloat();
|
|
|
|
info.m_vecSpread = pMarine->GetActiveWeapon()->GetBulletSpread();
|
|
info.m_flDamage = GetWeaponDamage();
|
|
#ifndef CLIENT_DLL
|
|
if (asw_debug_marine_damage.GetBool())
|
|
Msg("Weapon dmg = %f\n", info.m_flDamage);
|
|
info.m_flDamage *= pMarine->GetMarineResource()->OnFired_GetDamageScale();
|
|
if (asw_DebugAutoAim.GetBool())
|
|
{
|
|
NDebugOverlay::Line(info.m_vecSrc, info.m_vecSrc + info.m_vecDirShooting * info.m_flDistance, 64, 0, 64, true, 1.0);
|
|
}
|
|
#endif
|
|
|
|
pMarine->FireBullets( info );
|
|
|
|
// increment shooting stats
|
|
#ifndef CLIENT_DLL
|
|
if (pMarine && pMarine->GetMarineResource())
|
|
{
|
|
pMarine->GetMarineResource()->UsedWeapon(this, info.m_iShots);
|
|
pMarine->OnWeaponFired( this, info.m_iShots );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool CASW_Weapon::IsPredicted(void) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// ========================
|
|
// reloading
|
|
// ========================
|
|
|
|
float CASW_Weapon::GetReloadTime()
|
|
{
|
|
// can adjust for marine's weapon skill here
|
|
float fReloadTime = GetWeaponInfo()->flReloadTime;
|
|
if (GetMarine())
|
|
{
|
|
float fSpeedScale = MarineSkills()->GetSkillBasedValueByMarine(GetMarine(), ASW_MARINE_SKILL_RELOADING, ASW_MARINE_SUBSKILL_RELOADING_SPEED_SCALE);
|
|
fReloadTime *= fSpeedScale;
|
|
}
|
|
|
|
//CALL_ATTRIB_HOOK_FLOAT( fReloadTime, mod_reload_time );
|
|
|
|
return fReloadTime;
|
|
}
|
|
|
|
float CASW_Weapon::GetReloadFailTime()
|
|
{
|
|
return m_flReloadFailTime;
|
|
}
|
|
|
|
bool CASW_Weapon::Reload( void )
|
|
{
|
|
bool bReloaded = ASWReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD );
|
|
|
|
if ( bReloaded )
|
|
{
|
|
#ifdef GAME_DLL
|
|
CASW_Marine *pMarine = GetMarine();
|
|
if ( pMarine )
|
|
{
|
|
if ( pMarine->IsAlienNear() )
|
|
{
|
|
pMarine->GetMarineSpeech()->Chatter(CHATTER_RELOADING);
|
|
}
|
|
|
|
CASW_Marine_Resource *pMR = pMarine->GetMarineResource();
|
|
if ( pMR )
|
|
{
|
|
pMR->m_TimelineAmmo.RecordValue( pMarine->GetAllAmmoCount() );
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
return bReloaded;
|
|
}
|
|
|
|
bool CASW_Weapon::ASWReload( int iClipSize1, int iClipSize2, int iActivity )
|
|
{
|
|
if ( m_bInReload ) // we're already reloading!
|
|
{
|
|
Msg("ASWReload already reloading\n");
|
|
Assert(false);
|
|
return true;
|
|
}
|
|
|
|
CASW_Marine *pMarine = GetMarine();
|
|
if ( !pMarine || !ASWGameRules() )
|
|
return false;
|
|
|
|
bool bReload = false;
|
|
if ( m_bIsFiring )
|
|
{
|
|
OnStoppedFiring();
|
|
}
|
|
|
|
// 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, pMarine->GetAmmoCount( m_iPrimaryAmmoType ) );
|
|
if ( primary != 0 )
|
|
{
|
|
bReload = true;
|
|
}
|
|
else
|
|
{
|
|
// check if we have an ammo bag we can take a clip from instead
|
|
CASW_Weapon_Ammo_Bag* pAmmoBag = dynamic_cast<CASW_Weapon_Ammo_Bag*>( pMarine->GetASWWeapon( 0 ) );
|
|
if ( !pAmmoBag )
|
|
{
|
|
pAmmoBag = dynamic_cast<CASW_Weapon_Ammo_Bag*>( pMarine->GetASWWeapon( 1 ) );
|
|
}
|
|
|
|
if ( pAmmoBag && pAmmoBag->CanGiveAmmoToWeapon( this ) )
|
|
{
|
|
#ifdef CLIENT_DLL
|
|
bReload = true;
|
|
#else
|
|
pAmmoBag->GiveClipTo(pMarine, m_iPrimaryAmmoType, true);
|
|
|
|
// now we've given a clip, check if we can reload
|
|
primary = MIN(iClipSize1 - m_iClip1, pMarine->GetAmmoCount(m_iPrimaryAmmoType));
|
|
if ( primary != 0 )
|
|
{
|
|
bReload = true;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( UsesClipsForAmmo2() )
|
|
{
|
|
// need to reload secondary clip?
|
|
int secondary = MIN( iClipSize2 - m_iClip2, pMarine->GetAmmoCount( m_iSecondaryAmmoType ) );
|
|
if ( secondary != 0 )
|
|
{
|
|
bReload = true;
|
|
}
|
|
}
|
|
|
|
if ( !bReload )
|
|
return false;
|
|
|
|
m_bFastReloadSuccess = false;
|
|
m_bFastReloadFailure = false;
|
|
|
|
#ifndef CLIENT_DLL
|
|
if ( GetMaxClip1() > 1 )
|
|
{
|
|
// Fire event when a player reloads a weapon with more than a bullet per clip
|
|
IGameEvent * event = gameeventmanager->CreateEvent( "weapon_reload" );
|
|
if ( event )
|
|
{
|
|
CASW_Player *pPlayer = NULL;
|
|
pPlayer = pMarine->GetCommander();
|
|
|
|
int nClipSize = GetMaxClip1();
|
|
|
|
int nClips = pMarine->GetAmmoCount( m_iPrimaryAmmoType ) / nClipSize;
|
|
CASW_Weapon_Ammo_Bag *pAmmoBag = dynamic_cast< CASW_Weapon_Ammo_Bag* >( pMarine->GetASWWeapon( 0 ) );
|
|
if ( !pAmmoBag )
|
|
{
|
|
pAmmoBag = dynamic_cast< CASW_Weapon_Ammo_Bag* >( pMarine->GetASWWeapon( 1 ) );
|
|
}
|
|
if ( pAmmoBag && this != pAmmoBag )
|
|
{
|
|
nClips += pAmmoBag->NumClipsForWeapon( this );
|
|
}
|
|
|
|
event->SetInt( "userid", ( pPlayer ? pPlayer->GetUserID() : 0 ) );
|
|
event->SetInt( "marine", pMarine->entindex() );
|
|
event->SetInt( "lost", m_iClip1 );
|
|
event->SetInt( "clipsize", nClipSize );
|
|
event->SetInt( "clipsremaining", nClips - 1 );
|
|
event->SetInt( "clipsmax", GetAmmoDef()->MaxCarry( m_iPrimaryAmmoType, pMarine ) / nClipSize );
|
|
|
|
gameeventmanager->FireEvent( event );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
m_fReloadClearFiringTime = gpGlobals->curtime + GetFireRate();
|
|
|
|
float fReloadTime = GetReloadTime();
|
|
float flSequenceEndTime = gpGlobals->curtime + fReloadTime;
|
|
pMarine->SetNextAttack( flSequenceEndTime );
|
|
m_flNextPrimaryAttack = m_flNextSecondaryAttack = flSequenceEndTime;
|
|
//Msg(" Setting nextprimary attack time to %f from aswreload\n", m_flNextPrimaryAttack);
|
|
|
|
m_bInReload = true;
|
|
|
|
// set fast reload timings
|
|
// assuming 2.8 base reload time
|
|
// ~0.29
|
|
RandomSeed( CBaseEntity::GetPredictionRandomSeed() & 255 );
|
|
|
|
float flStartFraction = random->RandomFloat( 0.29f, 0.35f );
|
|
|
|
// set width by difficulty
|
|
float flFastReloadWidth = 0.12f;
|
|
switch( ASWGameRules()->GetSkillLevel() )
|
|
{
|
|
default:
|
|
case 1:
|
|
case 2: flFastReloadWidth = random->RandomFloat( 0.10f, 0.1f ); break; // easy/normal
|
|
case 3: flFastReloadWidth = random->RandomFloat( 0.08f, 0.12f ); break; // hard
|
|
case 4: flFastReloadWidth = random->RandomFloat( 0.06f, 0.10f ); break; // insane
|
|
}
|
|
// scale by marine skills
|
|
flFastReloadWidth *= MarineSkills()->GetSkillBasedValueByMarine( pMarine, ASW_MARINE_SKILL_RELOADING, ASW_MARINE_SUBSKILL_RELOADING_FAST_WIDTH_SCALE );
|
|
|
|
m_fReloadStart = gpGlobals->curtime;
|
|
m_fFastReloadStart = gpGlobals->curtime + flStartFraction * fReloadTime;
|
|
m_fFastReloadEnd = m_fFastReloadStart + flFastReloadWidth * fReloadTime;
|
|
|
|
SendReloadEvents();
|
|
|
|
#ifdef GAME_DLL
|
|
pMarine->RemoveWeaponPowerup( this );
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void CASW_Weapon::SendReloadEvents()
|
|
{
|
|
CASW_Marine *marine = dynamic_cast<CASW_Marine*>(GetOwner());
|
|
if (!marine)
|
|
return;
|
|
|
|
#ifdef CLIENT_DLL
|
|
if (marine->IsAnimatingReload()) // don't play the anim twice
|
|
return;
|
|
#else
|
|
CASW_GameStats.Event_MarineReloading( marine, this );
|
|
#endif
|
|
|
|
// Make the player play his reload animation. (and send to clients)
|
|
marine->DoAnimationEvent( PLAYERANIMEVENT_RELOAD );
|
|
}
|
|
|
|
// function unused (done by CASW_Marine::Weapon_Switch instead?)
|
|
void CASW_Weapon::SendWeaponSwitchEvents()
|
|
{
|
|
CASW_Marine *marine = dynamic_cast<CASW_Marine*>(GetOwner());
|
|
if (!marine)
|
|
return;
|
|
|
|
// Make the player play his reload animation. (and send to clients)
|
|
marine->DoAnimationEvent( PLAYERANIMEVENT_WEAPON_SWITCH );
|
|
}
|
|
|
|
bool CASW_Weapon::IsReloading() const
|
|
{
|
|
return m_bInReload;
|
|
}
|
|
|
|
// fixme: technically this returns if we're firing or anything else that stops firing (except reloading, which is checked for)
|
|
bool CASW_Weapon::IsFiring()// const
|
|
{
|
|
return m_bIsFiring;
|
|
}
|
|
|
|
float CASW_Weapon::GetFireRate()
|
|
{
|
|
float flRate = GetWeaponInfo()->m_flFireRate;
|
|
|
|
//CALL_ATTRIB_HOOK_FLOAT( flRate, mod_fire_rate );
|
|
|
|
return flRate;
|
|
}
|
|
|
|
void CASW_Weapon::GetButtons(bool& bAttack1, bool& bAttack2, bool& bReload, bool& bOldReload, bool& bOldAttack1 )
|
|
{
|
|
CASW_Marine *pMarine = GetMarine();
|
|
|
|
if (!pMarine)
|
|
{
|
|
CBasePlayer *pOwner = dynamic_cast<CBasePlayer*>(GetOwner());
|
|
if (pOwner)
|
|
{
|
|
bAttack1 = !!(pOwner->m_nButtons & IN_ATTACK);
|
|
bAttack2 = !!(pOwner->m_nButtons & IN_ATTACK2);
|
|
bReload = !!(pOwner->m_nButtons & IN_RELOAD);
|
|
bOldReload = false;
|
|
bOldAttack1 = false;
|
|
return;
|
|
}
|
|
bAttack1 = false;
|
|
bAttack2 = false;
|
|
bReload = false;
|
|
bOldReload = false;
|
|
bOldAttack1 = false;
|
|
return;
|
|
}
|
|
|
|
// don't allow firing when frozen/stopped from a pickup/kick
|
|
if ( pMarine->IsControllingTurret() || ( pMarine->GetFlags() & FL_FROZEN )
|
|
|| ( gpGlobals->curtime < pMarine->GetStopTime() ) || pMarine->GetCurrentMeleeAttack() )
|
|
{
|
|
bAttack1 = false;
|
|
bAttack2 = false;
|
|
bReload = false;
|
|
bOldReload = false;
|
|
bOldAttack1 = false;
|
|
return;
|
|
}
|
|
|
|
if (pMarine->IsInhabited() && pMarine->GetCommander())
|
|
{
|
|
bAttack1 = !!(pMarine->GetCommander()->m_nButtons & IN_ATTACK);
|
|
bAttack2 = !!(pMarine->GetCommander()->m_nButtons & IN_ATTACK2);
|
|
bReload = !!(pMarine->GetCommander()->m_nButtons & IN_RELOAD);
|
|
bOldReload = !!(pMarine->m_nOldButtons & IN_RELOAD);
|
|
bOldAttack1 = !!(pMarine->m_nOldButtons & IN_ATTACK);
|
|
return;
|
|
}
|
|
// does our uninhabited marine want to fire?
|
|
#ifdef GAME_DLL
|
|
bAttack1 = pMarine->AIWantsToFire();
|
|
bAttack2 = pMarine->AIWantsToFire2();
|
|
bReload = pMarine->AIWantsToReload();
|
|
bOldReload = false;
|
|
bOldAttack1 = false;
|
|
return;
|
|
#endif
|
|
|
|
bAttack1 = false;
|
|
bAttack2 = false;
|
|
bReload = false;
|
|
bOldReload = false;
|
|
bOldAttack1 = false;
|
|
return;
|
|
}
|
|
|
|
bool CASW_Weapon::ShouldMarineMoveSlow()
|
|
{
|
|
return (IsReloading() || IsFiring());
|
|
}
|
|
|
|
void CASW_Weapon::ClearIsFiring()
|
|
{
|
|
m_bIsFiring = false;
|
|
}
|
|
|
|
|
|
bool CASW_Weapon::Holster( CBaseCombatWeapon *pSwitchingTo )
|
|
{
|
|
if ( m_bIsFiring )
|
|
{
|
|
OnStoppedFiring();
|
|
}
|
|
ClearIsFiring();
|
|
|
|
/* UNDONE: This was causing trouble in the offhand welder. It seems like this was to enforce the grenade toss
|
|
animation completes, but it's probably better that pressing the offhand key guarantees item use
|
|
rather than having it misfire because of an rare condition.
|
|
|
|
CASW_Marine* pMarine = dynamic_cast<CASW_Marine*>(GetOwner());
|
|
|
|
// check if our offhand item is doing an offhand delayed attack, cancel it if so
|
|
if (pMarine && pMarine->GetASWWeapon(2) && pMarine->GetASWWeapon(2)->m_bShotDelayed) // cancel throwing if we're switching out
|
|
{
|
|
pMarine->GetASWWeapon(2)->m_bShotDelayed = false;
|
|
}
|
|
*/
|
|
|
|
return BaseClass::Holster( pSwitchingTo );
|
|
|
|
m_bFastReloadSuccess = false;
|
|
m_bFastReloadFailure = false;
|
|
}
|
|
|
|
void CASW_Weapon::FinishReload( void )
|
|
{
|
|
CASW_Marine *pOwner = GetMarine();
|
|
|
|
if (pOwner)
|
|
{
|
|
// If I use primary clips, reload primary
|
|
if ( UsesClipsForAmmo1() )
|
|
{
|
|
// asw: throw away what's in the clip currently
|
|
m_iClip1 = 0;
|
|
int primary = MIN( GetMaxClip1() - m_iClip1, pOwner->GetAmmoCount(m_iPrimaryAmmoType));
|
|
m_iClip1 += primary;
|
|
pOwner->RemoveAmmo( primary, m_iPrimaryAmmoType);
|
|
}
|
|
|
|
// If I use secondary clips, reload secondary
|
|
if ( UsesClipsForAmmo2() )
|
|
{
|
|
int secondary = MIN( GetMaxClip2() - m_iClip2, pOwner->GetAmmoCount(m_iSecondaryAmmoType));
|
|
m_iClip2 += secondary;
|
|
pOwner->RemoveAmmo( secondary, m_iSecondaryAmmoType );
|
|
}
|
|
|
|
if ( m_bReloadsSingly )
|
|
{
|
|
m_bInReload = false;
|
|
}
|
|
|
|
#ifdef GAME_DLL
|
|
if ( !m_bFastReloadSuccess )
|
|
{
|
|
pOwner->m_nFastReloadsInARow = 0;
|
|
}
|
|
#endif
|
|
m_bFastReloadSuccess = false;
|
|
m_bFastReloadFailure = false;
|
|
}
|
|
}
|
|
|
|
void CASW_Weapon::SetWeaponVisible( bool visible )
|
|
{
|
|
#ifdef CLIENT_DLL
|
|
// Msg("[C] %s SetWeaponVisible %d\n", GetClassname(), visible);
|
|
#else
|
|
//Msg("[S] %s SetWeaponVisible %d\n", GetClassname(), visible);
|
|
#endif
|
|
BaseClass::SetWeaponVisible(visible);
|
|
/*
|
|
if ( visible )
|
|
{
|
|
RemoveEffects( EF_NODRAW );
|
|
}
|
|
else
|
|
{
|
|
AddEffects( EF_NODRAW );
|
|
}*/
|
|
}
|
|
|
|
#define ASW_WEAPON_SWITCH_TIME 0.5
|
|
void CASW_Weapon::ApplyWeaponSwitchTime()
|
|
{
|
|
// play weaponswitch sound
|
|
if (ASWGameRules() && ASWGameRules()->GetGameState() >= ASW_GS_INGAME)
|
|
{
|
|
WeaponSound(SPECIAL3);
|
|
}
|
|
float flSequenceEndTime = gpGlobals->curtime + ASW_WEAPON_SWITCH_TIME;
|
|
CBaseCombatCharacter *pOwner = GetOwner();
|
|
if (pOwner)
|
|
pOwner->SetNextAttack( flSequenceEndTime );
|
|
m_flNextPrimaryAttack = m_flNextSecondaryAttack = flSequenceEndTime;
|
|
|
|
m_bSwitchingWeapons = true;
|
|
}
|
|
|
|
const float CASW_Weapon::GetAutoAimAmount()
|
|
{
|
|
return 0.0f;
|
|
//return AUTOAIM_2DEGREES;
|
|
}
|
|
|
|
// dot has to be lower than this to count
|
|
const float CASW_Weapon::GetVerticalAdjustOnlyAutoAimAmount()
|
|
{
|
|
return 0.66f;
|
|
}
|
|
|
|
void CASW_Weapon::Precache()
|
|
{
|
|
BaseClass::Precache();
|
|
|
|
PrecacheModel( "models/swarm/Bayonet/bayonet.mdl" );
|
|
PrecacheScriptSound("ASW_Rifle.ReloadA");
|
|
PrecacheScriptSound("ASW_Rifle.ReloadB");
|
|
PrecacheScriptSound("ASW_Rifle.ReloadC");
|
|
PrecacheScriptSound("FastReload.Success");
|
|
PrecacheScriptSound("FastReload.Miss");
|
|
|
|
const CASW_WeaponInfo* pWeaponInfo = GetWeaponInfo();
|
|
|
|
if ( pWeaponInfo )
|
|
{
|
|
// find equipment list index
|
|
if ( ASWEquipmentList() )
|
|
{
|
|
if ( pWeaponInfo->m_bExtra )
|
|
m_iEquipmentListIndex = ASWEquipmentList()->GetExtraIndex(GetClassname());
|
|
else
|
|
m_iEquipmentListIndex = ASWEquipmentList()->GetRegularIndex(GetClassname());
|
|
}
|
|
if ( pWeaponInfo->szDisplayModel && pWeaponInfo->szDisplayModel[0] )
|
|
{
|
|
CBaseEntity::PrecacheModel( pWeaponInfo->szDisplayModel );
|
|
}
|
|
if ( pWeaponInfo->szDisplayModel2 && pWeaponInfo->szDisplayModel2[0] )
|
|
{
|
|
CBaseEntity::PrecacheModel( pWeaponInfo->szDisplayModel2 );
|
|
}
|
|
}
|
|
}
|
|
|
|
const CASW_WeaponInfo* CASW_Weapon::GetWeaponInfo() const
|
|
{
|
|
return dynamic_cast<const CASW_WeaponInfo*>(&GetWpnData());
|
|
}
|
|
|
|
bool CASW_Weapon::SendWeaponAnim(int iActivity)
|
|
{
|
|
// no animations in 3rd person unless subclasses decide to have them
|
|
return false;
|
|
}
|
|
|
|
void CASW_Weapon::Equip(CBaseCombatCharacter *pOwner)
|
|
{
|
|
BaseClass::Equip(pOwner);
|
|
|
|
SetModel( GetViewModel() );
|
|
|
|
//IHasAttributes *pOwnerAttribInterface = dynamic_cast<IHasAttributes *>( pOwner );
|
|
//if ( pOwnerAttribInterface )
|
|
//{
|
|
//pOwnerAttribInterface->GetAttributeManager()->AddProvider( this );
|
|
//}
|
|
}
|
|
|
|
|
|
float CASW_Weapon::GetTurnRateModifier()
|
|
{
|
|
if (IsFiring())
|
|
return 0.5f;
|
|
|
|
return 1.0f;
|
|
}
|
|
|
|
int CASW_Weapon::ASW_SelectWeaponActivity(int idealActivity)
|
|
{
|
|
switch( idealActivity )
|
|
{
|
|
//case ACT_IDLE: idealActivity = ACT_DOD_STAND_IDLE; break;
|
|
//case ACT_CROUCHIDLE: idealActivity = ACT_DOD_CROUCH_IDLE; break;
|
|
//case ACT_RUN_CROUCH: idealActivity = ACT_DOD_CROUCHWALK_IDLE; break;
|
|
case ACT_WALK: idealActivity = ACT_WALK_AIM_RIFLE; break;
|
|
case ACT_RUN: idealActivity = ACT_RUN_AIM_RIFLE; break;
|
|
case ACT_IDLE: idealActivity = ACT_IDLE_RIFLE; break;
|
|
default: break;
|
|
}
|
|
return idealActivity;
|
|
}
|
|
|
|
bool CASW_Weapon::SupportsBayonet()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
float CASW_Weapon::GetMovementScale()
|
|
{
|
|
return ShouldMarineMoveSlow() ? 0.5f : 1.0f;
|
|
}
|
|
|
|
float CASW_Weapon::GetWeaponDamage()
|
|
{
|
|
float flDamage = GetWeaponInfo()->m_flBaseDamage;
|
|
|
|
if ( GetMarine() )
|
|
{
|
|
flDamage += MarineSkills()->GetSkillBasedValueByMarine(GetMarine(), ASW_MARINE_SKILL_ACCURACY, ASW_MARINE_SUBSKILL_ACCURACY_RIFLE_DMG);
|
|
}
|
|
|
|
//CALL_ATTRIB_HOOK_FLOAT( flDamage, mod_damage_done );
|
|
|
|
return flDamage;
|
|
}
|
|
|
|
const char *CASW_Weapon::GetASWShootSound( int iIndex, int &iPitch )
|
|
{
|
|
if ( iIndex == SINGLE || iIndex == SINGLE_NPC )
|
|
{
|
|
iIndex = IsCarriedByLocalPlayer() ? SINGLE : SINGLE_NPC;
|
|
}
|
|
|
|
if ( iIndex == WPN_DOUBLE || iIndex == DOUBLE_NPC )
|
|
{
|
|
iIndex = IsCarriedByLocalPlayer() ? WPN_DOUBLE : DOUBLE_NPC;
|
|
}
|
|
|
|
if ( iIndex == RELOAD || iIndex == RELOAD_NPC )
|
|
{
|
|
iIndex = IsCarriedByLocalPlayer() ? RELOAD : RELOAD_NPC;
|
|
|
|
// play the weapon sound according to marine skill
|
|
int iSkill = MarineSkills()->GetSkillBasedValueByMarine(GetMarine(), ASW_MARINE_SKILL_RELOADING, ASW_MARINE_SUBSKILL_RELOADING_SOUND);
|
|
switch (iSkill)
|
|
{
|
|
case 5: return GetWpnData().aShootSounds[ FAST_RELOAD ]; break;
|
|
case 4: iPitch = 120; return GetWpnData().aShootSounds[ iIndex ]; break;
|
|
case 3: iPitch = 115; return GetWpnData().aShootSounds[ iIndex ]; break;
|
|
case 2: iPitch = 110; return GetWpnData().aShootSounds[ iIndex ]; break;
|
|
case 1: iPitch = 105; return GetWpnData().aShootSounds[ iIndex ]; break;
|
|
default: return GetWpnData().aShootSounds[ iIndex ]; break;
|
|
};
|
|
}
|
|
return GetShootSound( iIndex );
|
|
}
|
|
|
|
void CASW_Weapon::WeaponSound( WeaponSound_t sound_type, float soundtime /* = 0.0f */ )
|
|
{
|
|
//asw hack - don't allow normal reloading sounds to be triggered, since we fire them from the marine's reloading animation anim event
|
|
if (sound_type == RELOAD)
|
|
return;
|
|
|
|
if (sound_type == SPECIAL2)
|
|
sound_type = RELOAD;
|
|
// If we have some sounds from the weapon classname.txt file, play a random one of them
|
|
int iPitch = 100;
|
|
const char *shootsound = GetASWShootSound( sound_type, iPitch );
|
|
//Msg("%s:%f WeaponSound %d %s\n", IsServer() ? "S" : "C", gpGlobals->curtime, sound_type, shootsound);
|
|
if ( !shootsound || !shootsound[0] )
|
|
return;
|
|
|
|
CSoundParameters params;
|
|
if ( !GetParametersForSound( shootsound, params, NULL ) )
|
|
return;
|
|
|
|
EmitSound_t playparams(params);
|
|
if (soundtime != 0)
|
|
playparams.m_flSoundTime = soundtime;
|
|
playparams.m_nPitch = params.pitch;
|
|
|
|
CASW_Player *pPlayer = GetCommander();
|
|
|
|
if ( params.play_to_owner_only )
|
|
{
|
|
// Am I only to play to my owner?
|
|
if ( GetOwner() && pPlayer && pPlayer->GetMarine() == GetOwner() )
|
|
{
|
|
CSingleUserRecipientFilter filter( pPlayer );
|
|
if ( IsPredicted() && CBaseEntity::GetPredictionPlayer() )
|
|
{
|
|
filter.UsePredictionRules();
|
|
}
|
|
EmitSound(filter, GetOwner()->entindex(), playparams);
|
|
//EmitSound( filter, GetOwner()->entindex(), shootsound, NULL, soundtime );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Play weapon sound from the owner
|
|
if ( GetOwner() )
|
|
{
|
|
CPASAttenuationFilter filter( GetOwner(), params.soundlevel );
|
|
if ( IsPredicted() && CBaseEntity::GetPredictionPlayer() )
|
|
{
|
|
filter.UsePredictionRules();
|
|
}
|
|
EmitSound(filter, GetOwner()->entindex(), playparams);
|
|
|
|
#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
|
|
{
|
|
CPASAttenuationFilter filter( this, params.soundlevel );
|
|
if ( IsPredicted() && CBaseEntity::GetPredictionPlayer() )
|
|
{
|
|
filter.UsePredictionRules();
|
|
}
|
|
EmitSound( filter, entindex(), shootsound, NULL, soundtime );
|
|
}
|
|
}
|
|
}
|
|
|
|
void CASW_Weapon::PlaySoundDirectlyToOwner( const char *szSoundName )
|
|
{
|
|
CASW_Player *pPlayer = GetCommander();
|
|
if ( !pPlayer )
|
|
return;
|
|
|
|
CSoundParameters params;
|
|
if ( !GetParametersForSound( szSoundName, params, NULL ) )
|
|
return;
|
|
|
|
EmitSound_t playparams( params );
|
|
|
|
CSingleUserRecipientFilter filter( pPlayer );
|
|
if ( IsPredicted() && CBaseEntity::GetPredictionPlayer() )
|
|
{
|
|
filter.UsePredictionRules();
|
|
}
|
|
EmitSound( filter, GetOwner()->entindex(), playparams );
|
|
}
|
|
|
|
void CASW_Weapon::PlaySoundToOthers( const char *szSoundName )
|
|
{
|
|
CASW_Player *pPlayer = GetCommander();
|
|
CSoundParameters params;
|
|
if ( !GetParametersForSound( szSoundName, params, NULL ) )
|
|
return;
|
|
|
|
EmitSound_t playparams( params );
|
|
|
|
// Play weapon sound from the owner
|
|
if ( GetOwner() )
|
|
{
|
|
CPASAttenuationFilter filter( GetOwner(), params.soundlevel );
|
|
if ( IsPredicted() && CBaseEntity::GetPredictionPlayer() )
|
|
{
|
|
filter.UsePredictionRules();
|
|
}
|
|
if ( pPlayer )
|
|
{
|
|
filter.RemoveRecipient( pPlayer );
|
|
}
|
|
EmitSound(filter, GetOwner()->entindex(), playparams);
|
|
}
|
|
// If no owner play from the weapon (this is used for thrown items)
|
|
else
|
|
{
|
|
CPASAttenuationFilter filter( this, params.soundlevel );
|
|
if ( IsPredicted() && CBaseEntity::GetPredictionPlayer() )
|
|
{
|
|
filter.UsePredictionRules();
|
|
}
|
|
if ( pPlayer )
|
|
{
|
|
filter.RemoveRecipient( pPlayer );
|
|
}
|
|
EmitSound(filter, entindex(), playparams);
|
|
}
|
|
}
|
|
|
|
|
|
void CASW_Weapon::LowAmmoSound()
|
|
{
|
|
CASW_Player *pPlayer = GetCommander();
|
|
if ( GetOwner() && pPlayer && pPlayer->GetMarine() == GetOwner() )
|
|
{
|
|
CSingleUserRecipientFilter filter( pPlayer );
|
|
if ( IsPredicted() && CBaseEntity::GetPredictionPlayer() )
|
|
{
|
|
filter.UsePredictionRules();
|
|
}
|
|
CSoundParameters params;
|
|
if ( !GetParametersForSound( "ASW_Weapon.LowAmmoClick", params, NULL ) )
|
|
return;
|
|
|
|
EmitSound_t playparams(params);
|
|
EmitSound( filter, GetOwner()->entindex(), playparams );
|
|
}
|
|
}
|
|
|
|
// no autoswitching our weapons
|
|
bool CASW_Weapon::AllowsAutoSwitchFrom( void ) const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int CASW_Weapon::AmmoClickPoint()
|
|
{
|
|
return 6;
|
|
}
|
|
|
|
// user message based tracer type
|
|
const char* CASW_Weapon::GetUTracerType()
|
|
{
|
|
return "ASWUTracer";
|
|
}
|
|
|
|
void CASW_Weapon::UpdateOnRemove( void )
|
|
{
|
|
#ifdef CLIENT_DLL
|
|
if ( m_hLaserSight.Get() )
|
|
{
|
|
UTIL_Remove( m_hLaserSight );
|
|
}
|
|
#endif
|
|
//IHasAttributes *pOwnerAttribInterface = dynamic_cast<IHasAttributes *>( GetOwnerEntity() );
|
|
//if ( pOwnerAttribInterface )
|
|
//{
|
|
//pOwnerAttribInterface->GetAttributeManager()->RemoveProvider( this );
|
|
//}
|
|
|
|
BaseClass::UpdateOnRemove();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Dropped weapon functions
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool CASW_Weapon::AllowedToPickup(CASW_Marine *pMarine)
|
|
{
|
|
if (!pMarine || !ASWGameRules() || !pMarine->GetMarineResource())
|
|
return false;
|
|
|
|
// check if we're swapping for an existing item
|
|
int index = pMarine->GetWeaponPositionForPickup(GetClassname());
|
|
CASW_Weapon* pWeapon = pMarine->GetASWWeapon(index);
|
|
const char* szSwappingClass = pWeapon ? pWeapon->GetClassname() : "";
|
|
|
|
// first check if the gamerules will allow it
|
|
bool bAllowed = ASWGameRules()->MarineCanPickup(pMarine->GetMarineResource(), GetClassname(), szSwappingClass);
|
|
#ifdef CLIENT_DLL
|
|
m_bSwappingWeapon = ( pWeapon != NULL );
|
|
#endif
|
|
|
|
return bAllowed;
|
|
}
|
|
|
|
bool CASW_Weapon::IsUsable(CBaseEntity *pUser)
|
|
{
|
|
return (!IsBeingCarried() && pUser && pUser->GetAbsOrigin().DistTo(GetAbsOrigin()) < ASW_MARINE_USE_RADIUS); // near enough?
|
|
}
|
|
|
|
int CASW_Weapon::LookupAttachment( const char *pAttachmentName )
|
|
{
|
|
// skip over the special basecombatweapon lookup that always uses the world model instead of the current model
|
|
return BaseClass::BaseClass::LookupAttachment( pAttachmentName );
|
|
}
|
|
|
|
void CASW_Weapon::OnStoppedFiring()
|
|
{
|
|
// used by child classes
|
|
}
|
|
|
|
// marine has started doing a diving roll
|
|
void CASW_Weapon::OnStartedRoll()
|
|
{
|
|
if ( m_bFastReloadSuccess )
|
|
return;
|
|
|
|
// cancel reloading
|
|
m_bFastReloadSuccess = false;
|
|
m_bFastReloadFailure = false;
|
|
m_bInReload = false;
|
|
} |