diff --git a/src/game/shared/neo/weapons/weapon_neobasecombatweapon.cpp b/src/game/shared/neo/weapons/weapon_neobasecombatweapon.cpp index 4d75d319dc..b0cb08bdfb 100644 --- a/src/game/shared/neo/weapons/weapon_neobasecombatweapon.cpp +++ b/src/game/shared/neo/weapons/weapon_neobasecombatweapon.cpp @@ -416,6 +416,19 @@ void CNEOBaseCombatWeapon::Equip(CBaseCombatCharacter* pOwner) bool CNEOBaseCombatWeapon::Reload( void ) { + // an empty weapon reloads with its slide held back until the release, where the model has that reload + if (UsesClipsForAmmo1() && m_iClip1 <= 0 && SelectWeightedSequence(ACT_VM_RELOAD_EMPTY) != ACTIVITY_NOT_AVAILABLE) + { + if (!DefaultReload(GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD_EMPTY)) + { + return false; + } + + // others see the reload, as for a reload with rounds left + ToHL2MPPlayer(GetOwner())->DoAnimationEvent(PLAYERANIMEVENT_RELOAD); + return true; + } + return BaseClass::Reload(); #if(0) @@ -498,6 +511,16 @@ bool CNEOBaseCombatWeapon::Deploy(void) AddEffects(EF_BONEMERGE); m_bTriggerReset = true; + // An empty weapon draws with its slide held back where the model has that draw, + // which carries no activity, so it is picked by name + static constexpr const char *VM_SEQ_DRAW_EMPTY = "drawempty"; + const int iDrawEmpty = (UsesClipsForAmmo1() && m_iClip1 <= 0) ? LookupSequence(VM_SEQ_DRAW_EMPTY) : ACTIVITY_NOT_AVAILABLE; + if (iDrawEmpty != ACTIVITY_NOT_AVAILABLE) + { + SetSequence(iDrawEmpty); + SendViewModelAnim(iDrawEmpty); + } + #ifdef DEBUG CNEO_Player* pOwner = NULL; if (GetOwner()) @@ -941,6 +964,22 @@ void CNEOBaseCombatWeapon::DryFire() m_flNextPrimaryAttack = gpGlobals->curtime + GetFastestDryRefireTime(); // SequenceDuration(); } +// An empty weapon idles on its empty sequence where the model has one, such as a pistol with its slide held back +bool CNEOBaseCombatWeapon::SendWeaponAnim(int iActivity) +{ + if (iActivity == ACT_VM_IDLE && UsesClipsForAmmo1() && m_iClip1 <= 0 && HasEmptyIdle()) + { + iActivity = ACT_VM_IDLE_EMPTY; + } + + return BaseClass::SendWeaponAnim(iActivity); +} + +bool CNEOBaseCombatWeapon::HasEmptyIdle() +{ + return SelectWeightedSequence(ACT_VM_IDLE_EMPTY) != ACTIVITY_NOT_AVAILABLE; +} + void CNEOBaseCombatWeapon::PrimaryAttack(void) { if (ShootingIsPrevented()) @@ -1019,8 +1058,6 @@ void CNEOBaseCombatWeapon::PrimaryAttack(void) #endif // GAME_DLL } - SendWeaponAnim(GetPrimaryAttackActivity()); - SetWeaponIdleTime(gpGlobals->curtime + 2.0); if (IsSemiAuto()) { m_bTriggerReset = false; @@ -1064,6 +1101,22 @@ void CNEOBaseCombatWeapon::PrimaryAttack(void) m_iPrimaryAmmoCount -= info.m_iShots; } + // the last round's shot locks a pistol's slide back: + // the pistols' dry fire sequence is a shot's kick with the slide held back, + static constexpr NEO_WEP_BITS_UNDERLYING_TYPE NEO_WEP_SLIDE_LOCK = NEO_WEP_MILSO | NEO_WEP_TACHI; + if (UsesClipsForAmmo1() && m_iClip1 <= 0 && (GetNeoWepBits() & NEO_WEP_SLIDE_LOCK)) + { + SendWeaponAnim(ACT_VM_DRYFIRE); + } + else + { + SendWeaponAnim(GetPrimaryAttackActivity()); + } + + // and goes straight on to the empty idle when it ends + const bool bEmptyIdleNext = UsesClipsForAmmo1() && m_iClip1 <= 0 && HasEmptyIdle(); + SetWeaponIdleTime(gpGlobals->curtime + (bEmptyIdleNext ? SequenceDuration() : 2.0)); + info.m_flDistance = MAX_TRACE_LENGTH; info.m_iAmmoType = m_iPrimaryAmmoType; info.m_iTracerFreq = UsesTracers() ? 1 : 0; diff --git a/src/game/shared/neo/weapons/weapon_neobasecombatweapon.h b/src/game/shared/neo/weapons/weapon_neobasecombatweapon.h index b8a0afbefa..c3899d4ddc 100644 --- a/src/game/shared/neo/weapons/weapon_neobasecombatweapon.h +++ b/src/game/shared/neo/weapons/weapon_neobasecombatweapon.h @@ -191,6 +191,7 @@ class CNEOBaseCombatWeapon : public CBaseHL2MPCombatWeapon #endif virtual void DryFire(void); + virtual bool SendWeaponAnim(int iActivity) override; virtual Activity GetPrimaryAttackActivity(void) override; #ifdef CLIENT_DLL @@ -292,6 +293,7 @@ class CNEOBaseCombatWeapon : public CBaseHL2MPCombatWeapon private: CNEOBaseCombatWeapon(const CNEOBaseCombatWeapon &other); + bool HasEmptyIdle(); };