From 21b68d1938e3843a4a80353272de02818d0098c5 Mon Sep 17 00:00:00 2001 From: Marco Cawthorne Date: Mon, 5 Aug 2024 18:06:18 -0700 Subject: [PATCH] NSWeapon: - add "chargeTime" key rework state machine in NSWeapon reload timing separation in NSWeapon --- src/shared/NSClientPlayer.h | 1 + src/shared/NSClientPlayer.qc | 15 +- src/shared/NSSoundScape.qc | 15 +- src/shared/NSWeapon.h | 52 ++++- src/shared/NSWeapon.qc | 403 ++++++++++++++++++++--------------- src/shared/player_pmove.qc | 1 + 6 files changed, 305 insertions(+), 182 deletions(-) diff --git a/src/shared/NSClientPlayer.h b/src/shared/NSClientPlayer.h index 8f4566cc..25e08fd3 100644 --- a/src/shared/NSClientPlayer.h +++ b/src/shared/NSClientPlayer.h @@ -142,6 +142,7 @@ private: PREDICTED_FLOAT(w_attack_next) PREDICTED_FLOAT(w_idle_next) + PREDICTED_FLOAT(w_reload_next) PREDICTED_FLOAT(teleport_time) PREDICTED_FLOAT(weapontime) PREDICTED_FLOAT(m_flStamina) diff --git a/src/shared/NSClientPlayer.qc b/src/shared/NSClientPlayer.qc index 69d749a8..283a1faa 100644 --- a/src/shared/NSClientPlayer.qc +++ b/src/shared/NSClientPlayer.qc @@ -250,11 +250,9 @@ NSClientPlayer::ProcessInput(void) /* weapon system */ if (input_buttons & INPUT_SECONDARY) { - m_activeWeapon._WeaponStartedFiring(); - m_activeWeapon.SecondaryAttack(); + m_activeWeapon._SecondaryAttack(); } else if (input_buttons & INPUT_PRIMARY) { - m_activeWeapon._WeaponStartedFiring(); - m_activeWeapon.PrimaryAttack(); + m_activeWeapon._PrimaryAttack(); } else if (input_buttons & INPUT_RELOAD) { m_activeWeapon.Reload(); } else { @@ -734,6 +732,7 @@ NSClientPlayer::ReceiveEntity(float new, float flChanged) READENTITY_FLOAT(weapontime, PLAYER_TIMINGS) READENTITY_FLOAT(w_attack_next, PLAYER_TIMINGS) READENTITY_FLOAT(w_idle_next, PLAYER_TIMINGS) + READENTITY_FLOAT(w_reload_next, PLAYER_TIMINGS) READENTITY_ENTNUM(vehicle_entnum, PLAYER_VEHICLE) READENTITY_BYTE(spec_ent, PLAYER_SPECTATE) READENTITY_BYTE(spec_mode, PLAYER_SPECTATE) @@ -825,6 +824,7 @@ NSClientPlayer::PredictPreFrame(void) SAVE_STATE(weapontime) SAVE_STATE(w_attack_next) SAVE_STATE(w_idle_next) + SAVE_STATE(w_reload_next) SAVE_STATE(vehicle_entnum) SAVE_STATE(spec_ent) SAVE_STATE(spec_mode) @@ -878,6 +878,7 @@ NSClientPlayer::PredictPostFrame(void) ROLL_BACK(weapontime) ROLL_BACK(w_attack_next) ROLL_BACK(w_idle_next) + ROLL_BACK(w_reload_next) ROLL_BACK(vehicle_entnum) ROLL_BACK(spec_ent) ROLL_BACK(spec_mode) @@ -929,6 +930,7 @@ NSClientPlayer::Save(float handle) SaveFloat(handle, "pmove_flags", pmove_flags); SaveFloat(handle, "w_attack_next", w_attack_next); SaveFloat(handle, "w_idle_next", w_idle_next); + SaveFloat(handle, "w_reload_next", w_reload_next); SaveFloat(handle, "teleport_time", teleport_time); SaveFloat(handle, "m_flStamina", m_flStamina); SaveInt(handle, "weaponframe", weaponframe); @@ -1000,6 +1002,9 @@ NSClientPlayer::Restore(string strKey, string strValue) case "w_idle_next": w_idle_next = ReadFloat(strValue); break; + case "w_reload_next": + w_reload_next = ReadFloat(strValue); + break; case "teleport_time": teleport_time = ReadFloat(strValue); break; @@ -1255,6 +1260,7 @@ NSClientPlayer::EvaluateEntity(void) EVALUATE_FIELD(weapontime, PLAYER_TIMINGS) EVALUATE_FIELD(w_attack_next, PLAYER_TIMINGS) EVALUATE_FIELD(w_idle_next, PLAYER_TIMINGS) + EVALUATE_FIELD(w_reload_next, PLAYER_TIMINGS) EVALUATE_FIELD(vehicle, PLAYER_VEHICLE) EVALUATE_FIELD(spec_ent, PLAYER_SPECTATE) EVALUATE_FIELD(spec_mode, PLAYER_SPECTATE) @@ -1344,6 +1350,7 @@ NSClientPlayer::SendEntity(entity ePEnt, float flChanged) SENDENTITY_FLOAT(weapontime, PLAYER_TIMINGS) SENDENTITY_FLOAT(w_attack_next, PLAYER_TIMINGS) SENDENTITY_FLOAT(w_idle_next, PLAYER_TIMINGS) + SENDENTITY_FLOAT(w_reload_next, PLAYER_TIMINGS) SENDENTITY_ENTITY(vehicle, PLAYER_VEHICLE) SENDENTITY_BYTE(spec_ent, PLAYER_SPECTATE) SENDENTITY_BYTE(spec_mode, PLAYER_SPECTATE) diff --git a/src/shared/NSSoundScape.qc b/src/shared/NSSoundScape.qc index 4685bb3f..cac0f0ed 100644 --- a/src/shared/NSSoundScape.qc +++ b/src/shared/NSSoundScape.qc @@ -853,12 +853,15 @@ void EFX_UpdateListener(NSView playerView) { static NSSoundScape lastScape; - vector vecPlayer; + vector camPos = playerView.GetCameraOrigin(); + vector camAngle = playerView.GetCameraAngle(); NSSoundScape bestScape = __NULL__; - vecPlayer = playerView.GetCameraOrigin(); - makevectors(playerView.GetCameraAngle()); - SetListener(vecPlayer, v_forward, v_right, v_up, 12); + if (lastScape) { + SetListener(camPos, anglesToForward(camAngle), anglesToRight(camAngle), anglesToUp(camAngle), 12); + } else { + SetListener(camPos, anglesToForward(camAngle), anglesToRight(camAngle), anglesToUp(camAngle), 0); + } if (autocvar_s_al_use_reverb == false) { return; @@ -869,12 +872,12 @@ EFX_UpdateListener(NSView playerView) NSSoundScape scape = (NSSoundScape)e; other = world; - traceline(scape.origin, vecPlayer, MOVE_OTHERONLY, scape); + traceline(scape.origin, camPos, MOVE_OTHERONLY, scape); if (trace_fraction < 1.0f) { continue; } - float dist = vlen(e.origin - vecPlayer); + float dist = vlen(e.origin - camPos); if (dist > scape.m_radius) { continue; } diff --git a/src/shared/NSWeapon.h b/src/shared/NSWeapon.h index cb6f7ad4..5503a30b 100644 --- a/src/shared/NSWeapon.h +++ b/src/shared/NSWeapon.h @@ -44,8 +44,22 @@ typedef enum WEAPONSTATE_RELOAD_START, WEAPONSTATE_RELOAD, WEAPONSTATE_RELOAD_END, + WEAPONSTATE_CHARGING, + WEAPONSTATE_FIRELOOP, + WEAPONSTATE_RELEASED } nsweapon_state_t; +string nsweapon_state_s[] = +{ + "WEAPONSTATE_IDLE", + "WEAPONSTATE_RELOAD_START", + "WEAPONSTATE_RELOAD", + "WEAPONSTATE_RELOAD_END", + "WEAPONSTATE_CHARGING", + "WEAPONSTATE_FIRELOOP", + "WEAPONSTATE_RELEASED" +}; + typedef enum { WEPEVENT_FIRED, @@ -63,9 +77,6 @@ related keys get forwarded only to items of this class. - "targetname" : Name - "weapon_scriptobject" : mapC progs with the weapon code within. -- "ammoType" : name of the ammo type def entry which the gun uses -- "ammoRequired" : set to 1 if we require ammo. -- "ammoPerShot" : Amount of ammo to deduct per shot. - "clipSize" : maximum clip size - "mtr_flashShader" : muzzleflash material to Use. - "model_flash" : muzzleflash model/sprite to use. @@ -79,6 +90,25 @@ related keys get forwarded only to items of this class. - "continuousSmoke" : whether the particle effect is continous - "clipSizeDefault" : CUSTOM: Default clip size on pickup. +## Attack related keys +- "def_onFire" : Def to spawn when the weapon is fired. +- "def_onRelease" : Def to spawn when the weapon has been released. + +## Ammo management related keys +- "ammoType" : name of the ammo type def entry which the weapon uses +- "ammoRequired" : set to 1 if we require ammo. +- "ammoPerShot" : Amount of ammo to deduct per successful shot. + +### Charging/Overcharging +For an overcharge effect, spawn a self-destructive explosion on "def_onFire", and rely on "def_onRelease" +for delivering a lethal charge to other enemies. +- "chargeTime" : Amount of time the weapon has to charge before "def_onFire" is spawned. + +### Overheating weapons +Overheating of the weapon is done when both keys are set. +- "overheatLength" : Time in which it takes for the weapon to cool down. +- "overheatPerShot" : Time added against "overheat_length" when a shot is fired. + @ingroup baseclass */ class @@ -111,7 +141,7 @@ public: virtual bool IsWeapon(void); virtual bool HasReserveAmmo(void); - /** Overridable: Called when we switch to this weapon */ + /** Overridable: Called when we switch to this weapon from another. */ virtual void Draw(void); /** Overridable: Called when we are about to switch to another weapon */ virtual void Holster(void); @@ -121,8 +151,10 @@ public: virtual void SecondaryAttack(void); /** Overridable: On +reload execution. */ virtual void Reload(void); - /** Overridable: When no buttons are held. */ + /** Overridable: When no buttons are held, or you forcefully want to stop firing. */ virtual void Release(void); + /** Overridable: When the weapon is supposed to be doing something on its own. */ + virtual void Idle(void); /** Overridable: When the HUD is requested to be drawn. */ virtual void UpdateGUI(void); @@ -130,11 +162,18 @@ public: nonvirtual void SetWorldModel(string); nonvirtual void SetPlayerModel(string); nonvirtual void SetWeaponFrame(float); + nonvirtual void PlaySound(string, bool); + + /* state */ + nonvirtual void SetWeaponState(nsweapon_state_t); + nonvirtual nsweapon_state_t GetWeaponState(void); virtual void SetAttackNext(float); + virtual void SetReloadNext(float); virtual void SetIdleNext(float); virtual bool CanFire(void); virtual bool CanIdle(void); + virtual bool CanReload(void); virtual bool UseAmmo(string); /** Overridable: Called once when the weapon started firing. */ @@ -172,6 +211,8 @@ private: nonvirtual void _CacheWeaponDefVariables(void); nonvirtual void _WeaponStartedFiring(void); nonvirtual void _WeaponStoppedFiring(void); + nonvirtual void _PrimaryAttack(void); + nonvirtual void _SecondaryAttack(void); #ifdef SERVER nonvirtual void _ReloadFinished(void); @@ -230,6 +271,7 @@ private: bool m_fiSemiAuto; string m_fiSndFireLoop; float m_flReloadSpeed; + float m_fiChargeTime; NETWORKED_INT(m_iClip) NETWORKED_INT(m_iClipSize) diff --git a/src/shared/NSWeapon.qc b/src/shared/NSWeapon.qc index f6b99313..f98b1deb 100644 --- a/src/shared/NSWeapon.qc +++ b/src/shared/NSWeapon.qc @@ -64,6 +64,26 @@ NSWeapon::RemovedFromInventory(void) { } +void +NSWeapon::SetWeaponState(nsweapon_state_t newState) +{ + if (m_dState == newState) { + return; + } + + m_dState = newState; + +#ifdef SERVER + printf("State changed to %s\n", nsweapon_state_s[newState]); +#endif +} + +nsweapon_state_t +NSWeapon::GetWeaponState(void) +{ + return (m_dState); +} + void NSWeapon::UpdateFireInfoCache(void) { @@ -77,10 +97,14 @@ NSWeapon::UpdateFireInfoCache(void) m_fiAmmoType = ammoNumForName(GetSubDefString(m_strLastFireInfo, "ammoType")); m_fiAmmoRequired = GetSubDefBool(m_strLastFireInfo, "ammoRequired"); m_fiFireRate = GetSubDefFloat(m_strLastFireInfo, "fireRate"); - m_fiOnFire = GetSubDefString(m_strLastFireInfo, "def_onFire"); - m_fiOnRelease = GetSubDefString(m_strLastFireInfo, "def_onRelease"); m_fiSemiAuto = GetSubDefBool(m_strLastFireInfo, "semiAuto"); + /* defs to spawn */ + m_fiOnFire = GetSubDefString(m_strLastFireInfo, "def_onFire"); + m_fiOnRelease = GetSubDefString(m_strLastFireInfo, "def_onRelease"); + m_fiChargeTime = GetSubDefFloat(m_strLastFireInfo, "chargeTime"); + + /* when -1.0 we'll pull it from the animation. */ if (reloadSpeed != "") { m_flReloadSpeed = stof(reloadSpeed); } else { @@ -96,10 +120,11 @@ NSWeapon::UpdateFireInfoCache(void) #endif /* validate */ - if (ammoPerShot != __NULL__) + if (ammoPerShot != __NULL__) { m_fiAmmoPerShot = (int)stoi(ammoPerShot); - else + } else { m_fiAmmoPerShot = 1i; + } } void @@ -184,59 +209,6 @@ NSWeapon::SpawnKey(string keyName, string setValue) case "continuousSmoke": m_bSmokeContinous = ReadBool(setValue); break; - - case "actAltFire": - case "actAltFireLast": - case "actDraw": - case "actDrawEmpty": - case "actFire": - case "actFireLast": - case "actHolster": - case "actHolsterEmpty": - case "actLoop": - case "actIdle": - case "actIdleEmpty": - case "actMeleeHit": - case "actMeleeMiss": - case "actPull": - case "actReload": - case "actReloadEmpty": - case "actReloadEnd": - case "actReloadStart": - case "actThrow": - case "ammoRequired": - case "ammoPerShot": - case "def_altFireInfo": - case "def_damage_inhand": - case "def_explode_inhand": - case "def_fireInfo": - case "def_melee": - case "def_plant": - case "def_projectile": - case "detonateOnFire": - case "fireRate": - case "hudSlot": - case "hudSlotPos": - case "inv_name": - case "meleeRateHit": - case "meleeRateMiss": - case "melee_distance": - case "primed_fuse": - case "punchAngle": - case "powerAmmo": - case "removeOnEmpty": - case "silent_fire": - case "snd_altfire": - case "snd_empty": - case "snd_fire": - case "snd_reload": - case "snd_reload_start": - case "snd_reload_end": - case "weight": - case "trigger_delay": - case "zoomFov": - break; - default: super::SpawnKey(keyName, setValue); break; @@ -585,7 +557,6 @@ NSWeapon::_CacheWeaponDefVariables(void) muzzleModel = GetSubDefString(m_primaryFireInfo, "model_flash"); m_muzzleModelIndex = getmodelindex(muzzleModel, false); - m_flTriggerDelay = GetSubDefFloat(m_primaryFireInfo, "trigger_delay"); m_flPrimedFuse = GetDefFloat("primed_fuse"); m_flZoomFOV = GetDefFloat("zoomFov") / 90; m_bPowerAmmo = GetDefBool("powerAmmo"); @@ -748,6 +719,12 @@ NSWeapon::FiredWeaponAttack(string fireInfo) { NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); ourOwner.AttackByDef(fireInfo, false); + SetWeaponState(WEAPONSTATE_IDLE); + + /* prevent release from firing it again */ + if (m_bPowerAmmo) { + m_fiWillRelease = false; + } } void @@ -755,6 +732,7 @@ NSWeapon::ReleasedWeaponAttack(string fireInfo) { NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); ourOwner.AttackByDef(fireInfo, true); + SetWeaponState(WEAPONSTATE_RELEASED); } void @@ -768,7 +746,7 @@ NSWeapon::Attack(string fireInfo) ourOwner.gflags |= GF_SEMI_TOGGLED; } - /* satchels, pipe bombs, etc. */ + /* no real attack, detonate named satchels, pipe bombs, etc. */ if (m_fiDetonateOnFire != __NULL__) { if (DetonateDef(m_fiDetonateOnFire) == true) { SetWeaponFrame(GetSubDefAct(fireInfo, "actDetonate")); @@ -789,6 +767,8 @@ NSWeapon::Attack(string fireInfo) NSError("Will release."); } + /* melee attacks go through their own routine because we have to test + against objects and walls and if we can even hit them. */ if (m_meleeDef != "") { vector eyePos = ourOwner.GetEyePos(); vector eyeAngles = ourOwner.v_angle; @@ -818,7 +798,7 @@ NSWeapon::Attack(string fireInfo) SetIdleNext(meleeRate + 1.0f); #ifdef SERVER - Sound_Play(ourOwner, CHAN_WEAPON, GetSubDefString(m_meleeDef, "snd_miss")); + PlaySound(GetSubDefString(m_meleeDef, "snd_miss"), false); if (trace_fraction >= 1.0) { return; @@ -837,10 +817,10 @@ NSWeapon::Attack(string fireInfo) //Damage_Apply(trace_ent, ourOwner, meleeDamage, 0, DMG_BLUNT); if (hitEnt.iBleeds) { - Sound_Play(ourOwner, CHAN_WEAPON, GetSubDefString(m_meleeDef, "snd_flesh")); + PlaySound(GetSubDefString(m_meleeDef, "snd_flesh"), false); } } else { - Sound_Play(ourOwner, CHAN_WEAPON, GetSubDefString(m_meleeDef, "snd_hit")); + PlaySound(GetSubDefString(m_meleeDef, "snd_hit"), false); DecalGroups_Place("Impact.Shot", hitLoc + (anglesToForward(eyeAngles) * -2)); } #endif @@ -866,18 +846,19 @@ NSWeapon::Attack(string fireInfo) return; } - /* this weapon has a timed delay before something shoots out. */ - if (m_flTriggerDelay > 0.0f) { - if (ourOwner.vv_flags & VFL_PRIMEDFUSE) { - Release(); + /* this weapon has a delay before something shoots out. */ + if (m_fiChargeTime > 0.0f) { + /* we haven't yet tried firing while charging */ + if (GetWeaponState() != WEAPONSTATE_CHARGING) { + shotAnim = GetSubDefAct(fireInfo, "actDelay"); + SetAttackNext(m_fiChargeTime); + + /* mark as charging, play loop anim in Idle() next */ + SetIdleNext(frameduration(m_viewModel, shotAnim)); + SetWeaponFrame(shotAnim); + SetWeaponState(WEAPONSTATE_CHARGING); return; } - shotAnim = GetSubDefAct(fireInfo, "actDelay"); - SetAttackNext(m_flTriggerDelay); - SetWeaponFrame(shotAnim); - ourOwner.vv_flags |= VFL_PRIMEDFUSE; - //ourOwner.gflags |= GF_SEMI_TOGGLED; - return; } ourOwner.punchangle += m_fiPunchAngle; @@ -912,10 +893,43 @@ NSWeapon::Attack(string fireInfo) m_bFiring = true; } +void +NSWeapon::_PrimaryAttack(void) +{ + SwitchFireInfo(m_primaryFireInfo); + _WeaponStartedFiring(); + PrimaryAttack(); +} + +void +NSWeapon::_SecondaryAttack(void) +{ + SwitchFireInfo(m_secondaryFireInfo); + _WeaponStartedFiring(); + SecondaryAttack(); +} + +void +NSWeapon::PlaySound(string soundDef, bool clientOnly) +{ +#ifdef CLIENT + if (clientOnly) { + Sound_Play(owner, CHAN_WEAPON, soundDef); + } +#endif + +#ifdef SERVER + if (!clientOnly) { + Sound_Play(owner, CHAN_WEAPON, soundDef); + } +#endif +} + void NSWeapon::PrimaryAttack(void) { NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); + if (CanFire() == false) { if (!(ourOwner.gflags & GF_SEMI_TOGGLED)) { Release(); @@ -924,10 +938,99 @@ NSWeapon::PrimaryAttack(void) return; } - SwitchFireInfo(m_primaryFireInfo); Attack(m_primaryFireInfo); } +void +NSWeapon::Idle(void) +{ + NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); + float idleAnim = 0; + + if (CanIdle() == false) { + return; + } + + /* handle shotgun style reloads */ + switch (m_dState) { + case WEAPONSTATE_RELOAD_START: + idleAnim = GetDefAct("actReloadStart"); + SetWeaponState(WEAPONSTATE_RELOAD); + PlaySound(GetDefString("snd_reload_start"), false); + break; + case WEAPONSTATE_RELOAD: + float reloadTime; + idleAnim = GetDefAct("actReload"); + m_iClip++; + ourOwner.UseAmmo(m_primaryAmmoType, 1); + + if (HasReserveAmmo() == false || m_iClip >= m_iClipSize) { + SetWeaponState(WEAPONSTATE_RELOAD_END); + } + +#ifdef SERVER + PlaySound(GetDefString("snd_reload"), false); +#endif + + if (m_flReloadSpeed == -1.0f) { + reloadTime = frameduration(m_viewModel, idleAnim); + } else { + reloadTime = m_flReloadSpeed; + } + + SetWeaponFrame(idleAnim); + SetIdleNext(reloadTime); + return; + break; + case WEAPONSTATE_RELOAD_END: + idleAnim = GetDefAct("actReloadEnd"); + SetWeaponState(WEAPONSTATE_IDLE); +#ifdef SERVER + PlaySound(GetDefString("snd_reload_end"), false); +#endif + break; + case WEAPONSTATE_FIRELOOP: + idleAnim = GetDefAct("actLoop"); + break; + case WEAPONSTATE_CHARGING: + idleAnim = GetDefAct("actLoop"); + break; + case WEAPONSTATE_RELEASED: + //breakpoint(); + idleAnim = GetDefAct("actRelease"); + SetWeaponState(WEAPONSTATE_IDLE); + break; + case WEAPONSTATE_IDLE: + default: + if (!m_iMode) { + if (m_iClipSize > 0 && m_iClip == 0) { + idleAnim = GetDefAct("actIdleEmpty"); + + /* this will mess with us otherwise */ + if (!idleAnim) + return; + } + + if (!idleAnim) + idleAnim = GetDefAct("actIdle"); + } else { + if (m_iClipSize > 0 && m_iClip == 0) { + idleAnim = GetDefAct("actAltIdleEmpty"); + + /* this will mess with us otherwise */ + if (!idleAnim) + return; + } + + if (!idleAnim) + idleAnim = GetDefAct("actAltIdle"); + } + } + + SetWeaponFrame(idleAnim); + SetIdleNext(frameduration(m_viewModel, idleAnim)); +} + void NSWeapon::SecondaryAttack(void) { @@ -945,9 +1048,9 @@ NSWeapon::SecondaryAttack(void) #ifdef SERVER if (ourOwner.viewzoom == 1.0) { - Sound_Play(ourOwner, CHAN_WEAPON, GetDefString("snd_lower_scope")); + PlaySound(GetDefString("snd_lower_scope"), false); } else { - Sound_Play(ourOwner, CHAN_WEAPON, GetDefString("snd_raise_scope")); + PlaySound(GetDefString("snd_raise_scope"), false); } #endif @@ -961,10 +1064,12 @@ NSWeapon::SecondaryAttack(void) return; } - if (CanFire() == false) + /* don't run any further logic */ + if (CanFire() == false) { + Idle(); return; + } - SwitchFireInfo(m_secondaryFireInfo); Attack(m_secondaryFireInfo); } @@ -978,8 +1083,9 @@ NSWeapon::_ReloadFinished(void) ammoToDeduct = m_iClipSize - m_iClip; /* not enough ammo. assign whatever is left. */ - if (ourOwner.HasAmmo(m_primaryAmmoType, ammoToDeduct) == false) + if (ourOwner.HasAmmo(m_primaryAmmoType, ammoToDeduct) == false) { ammoToDeduct = ourOwner.m_iAmmoTypes[m_primaryAmmoType]; + } m_iClip = m_iClipSize; ourOwner.UseAmmo(m_primaryAmmoType, ammoToDeduct); @@ -990,7 +1096,6 @@ void NSWeapon::Reload(void) { NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); - string fireInfo; float reloadAnimation = 0; float reloadTime; string ammoType; @@ -1008,17 +1113,11 @@ NSWeapon::Reload(void) return; } - if (CanFire() == false) { + if (CanReload() == false) { return; } - fireInfo = m_strLastFireInfo; - - /* fall back to main weapon def */ - if (!fireInfo) - fireInfo = classname; - - ammoType = GetSubDefString(fireInfo, "ammoType"); + ammoType = GetSubDefString(m_strLastFireInfo, "ammoType"); ammoTypeID = ammoNumForName(ammoType); if (m_iClip == m_iClipSize) { @@ -1032,7 +1131,7 @@ NSWeapon::Reload(void) /* we have a start-reload, so this is a shotgun styled weapon reload */ if (reloadStartAct) { - m_dState = WEAPONSTATE_RELOAD_START; + SetWeaponState(WEAPONSTATE_RELOAD_START); Release(); return; } @@ -1061,13 +1160,9 @@ NSWeapon::Reload(void) reloadTime = m_flReloadSpeed; } + PlaySound(GetDefString("snd_reload"), false); + #ifdef SERVER - string soundDef = GetDefString("snd_reload"); - - if (soundDef) { - ourOwner.StartSoundDef(soundDef, CHAN_WEAPON, true); - } - ScheduleThink(_ReloadFinished, reloadTime - 0.1f); #endif @@ -1106,6 +1201,19 @@ NSWeapon::_WeaponStartedFiring(void) /* hasn't been fired yet. */ if (!(owner.vv_flags & VFL_FIRING)) { + float actFireStart = GetSubDefAct(m_strLastFireInfo, "actFireStart"); + +#ifdef SERVER + printf("actFireStart %d %S\n", actFireStart, m_strLastFireInfo); +#endif + + if (actFireStart) { + SetWeaponFrame(actFireStart); + SetAttackNext(frameduration(m_viewModel, actFireStart)); + SetIdleNext(frameduration(m_viewModel, actFireStart)); + SetWeaponState(WEAPONSTATE_FIRELOOP); + } + WeaponStartedFiring(); } @@ -1121,6 +1229,19 @@ NSWeapon::_WeaponStoppedFiring(void) /* was still registed as firing */ if (owner.vv_flags & VFL_FIRING) { + float actFireStop = GetSubDefAct(m_strLastFireInfo, "actFireStop"); + +#ifdef SERVER + printf("actFireStop %d %S\n", actFireStop, m_strLastFireInfo); +#endif + + if (actFireStop) { + SetWeaponFrame(actFireStop); + SetAttackNext(frameduration(m_viewModel, actFireStop)); + SetIdleNext(frameduration(m_viewModel, actFireStop)); + SetWeaponState(WEAPONSTATE_RELEASED); + } + WeaponStoppedFiring(); } @@ -1189,10 +1310,6 @@ NSWeapon::Release(void) } #endif - if (CanIdle() == false) { - return; - } - m_bFiring = false; if (ourOwner.vv_flags & VFL_REDRAW) { @@ -1201,75 +1318,7 @@ NSWeapon::Release(void) return; } - /* handle shotgun style reloads */ - switch (m_dState) { - case WEAPONSTATE_RELOAD_START: - idleAnim = GetDefAct("actReloadStart"); - m_dState = WEAPONSTATE_RELOAD; -#ifdef SERVER - Sound_Play(ourOwner, CHAN_WEAPON, GetDefString("snd_reload_start")); -#endif - break; - case WEAPONSTATE_RELOAD: - float reloadTime; - idleAnim = GetDefAct("actReload"); - m_iClip++; - ourOwner.UseAmmo(m_primaryAmmoType, 1); - - if (HasReserveAmmo() == false || m_iClip >= m_iClipSize) { - m_dState = WEAPONSTATE_RELOAD_END; - } - -#ifdef SERVER - Sound_Play(ourOwner, CHAN_WEAPON, GetDefString("snd_reload")); -#endif - - if (m_flReloadSpeed == -1.0f) { - reloadTime = frameduration(m_viewModel, idleAnim); - } else { - reloadTime = m_flReloadSpeed; - } - - SetWeaponFrame(idleAnim); - SetIdleNext(reloadTime); - return; - break; - case WEAPONSTATE_RELOAD_END: - idleAnim = GetDefAct("actReloadEnd"); - m_dState = WEAPONSTATE_IDLE; -#ifdef SERVER - Sound_Play(ourOwner, CHAN_WEAPON, GetDefString("snd_reload_end")); -#endif - break; - case WEAPONSTATE_IDLE: - default: - if (!m_iMode) { - if (m_iClipSize > 0 && m_iClip == 0) { - idleAnim = GetDefAct("actIdleEmpty"); - - /* this will mess with us otherwise */ - if (!idleAnim) - return; - } - - if (!idleAnim) - idleAnim = GetDefAct("actIdle"); - } else { - if (m_iClipSize > 0 && m_iClip == 0) { - idleAnim = GetDefAct("actAltIdleEmpty"); - - /* this will mess with us otherwise */ - if (!idleAnim) - return; - } - - if (!idleAnim) - idleAnim = GetDefAct("actAltIdle"); - } - } - - SetWeaponFrame(idleAnim); - SetIdleNext(frameduration(m_viewModel, idleAnim)); + Idle(); } void @@ -1332,6 +1381,14 @@ NSWeapon::SetAttackNext(float newDelay) { NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); ourOwner.w_attack_next = newDelay; + SetReloadNext(newDelay); +} + +void +NSWeapon::SetReloadNext(float newDelay) +{ + NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); + ourOwner.w_reload_next = newDelay; } void @@ -1369,6 +1426,18 @@ NSWeapon::CanFire(void) return (true); } +bool +NSWeapon::CanReload(void) +{ + NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); + + if (ourOwner.w_reload_next > 0.0f) { + return (false); + } + + return (true); +} + /* required because we might need to look it up inside the info for the primary attack mode. */ static string diff --git a/src/shared/player_pmove.qc b/src/shared/player_pmove.qc index c2e9b241..1758359c 100644 --- a/src/shared/player_pmove.qc +++ b/src/shared/player_pmove.qc @@ -514,6 +514,7 @@ NSClientPlayer::Physics_InputPostMove(void) float punch; /* timers, these are predicted and shared across client and server */ w_attack_next = max(0, w_attack_next - input_timelength); + w_reload_next = max(0, w_reload_next - input_timelength); w_idle_next = max(0, w_idle_next - input_timelength); weapontime += input_timelength;