diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs index e7de1297b4..73087f5694 100644 --- a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs @@ -163,7 +163,7 @@ public override void Update(float frameTime) var useKey = gun.UseKey ? EngineKeyFunctions.Use : EngineKeyFunctions.UseSecondary; - if (_inputSystem.CmdStates.GetState(useKey) != BoundKeyState.Down && !gun.BurstActivated) + if (_inputSystem.CmdStates.GetState(useKey) != BoundKeyState.Down) { if (gun.ShotCounter != 0) EntityManager.RaisePredictiveEvent(new RequestStopShootEvent { Gun = GetNetEntity(gunUid) }); diff --git a/Content.Client/_RMC14/Weapons/Ranged/Prediction/GunPredictionSystem.cs b/Content.Client/_RMC14/Weapons/Ranged/Prediction/GunPredictionSystem.cs index 9f700146a0..ac65948252 100644 --- a/Content.Client/_RMC14/Weapons/Ranged/Prediction/GunPredictionSystem.cs +++ b/Content.Client/_RMC14/Weapons/Ranged/Prediction/GunPredictionSystem.cs @@ -27,6 +27,7 @@ public sealed class GunPredictionSystem : SharedGunPredictionSystem private EntityQuery _ignorePredictionHideQuery; private EntityQuery _spriteQuery; + public override void Initialize() { base.Initialize(); diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.AutoFire.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.AutoFire.cs index e5439cdb06..39cd2486ed 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.AutoFire.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.AutoFire.cs @@ -1,6 +1,4 @@ -using Content.Shared.Damage; using Content.Shared.Weapons.Ranged.Components; -using Robust.Shared.Map; namespace Content.Server.Weapons.Ranged.Systems; @@ -15,28 +13,17 @@ public override void Update(float frameTime) */ // Automatic firing without stopping if the AutoShootGunComponent component is exist and enabled - var query = EntityQueryEnumerator(); + var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var gun)) + while (query.MoveNext(out var uid, out var autoShoot, out var gun)) { - if (gun.NextFire > Timing.CurTime) + if (!autoShoot.Enabled) continue; - if (TryComp(uid, out AutoShootGunComponent? autoShoot)) - { - if (!autoShoot.Enabled) - continue; + if (gun.NextFire > Timing.CurTime) + continue; - AttemptShoot(uid, gun); - } - else if (gun.BurstActivated) - { - var parent = _transform.GetParentUid(uid); - if (HasComp(parent)) - AttemptShoot(parent, uid, gun, gun.ShootCoordinates ?? new EntityCoordinates(uid, gun.DefaultDirection)); - else - AttemptShoot(uid, gun); - } + AttemptShoot(uid, gun); } } } diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index cdd1e0582a..dd5ced7f78 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -1,15 +1,19 @@ using System.Numerics; using Content.Server.Cargo.Systems; using Content.Server.Power.EntitySystems; +using Content.Server.Stunnable; using Content.Shared.Damage.Systems; +using Content.Shared.Effects; using Content.Shared.Weapons.Ranged; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Weapons.Ranged.Systems; +using Robust.Shared.Containers; using Robust.Shared.Map; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Utility; +using Robust.Shared.Containers; namespace Content.Server.Weapons.Ranged.Systems; @@ -19,7 +23,6 @@ public sealed partial class GunSystem : SharedGunSystem [Dependency] private readonly BatterySystem _battery = default!; [Dependency] private readonly DamageExamineSystem _damageExamine = default!; [Dependency] private readonly PricingSystem _pricing = default!; - [Dependency] private readonly SharedTransformSystem _transform = default!; public override void Initialize() { diff --git a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs index 98b1d2fe2a..b404221abf 100644 --- a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs +++ b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs @@ -1,4 +1,3 @@ -using System.Numerics; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Weapons.Ranged.Systems; using Robust.Shared.Audio; @@ -157,30 +156,6 @@ public sealed partial class GunComponent : Component [AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] public int ShotsPerBurstModified = 3; - /// - /// How long time must pass between burstfire shots. - /// - [DataField, AutoNetworkedField] - public float BurstCooldown = 0.25f; - - /// - /// The fire rate of the weapon in burst fire mode. - /// - [DataField, AutoNetworkedField] - public float BurstFireRate = 8f; - - /// - /// Whether the burst fire mode has been activated. - /// - [AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] - public bool BurstActivated = false; - - /// - /// The burst fire bullet count. - /// - [AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] - public int BurstShotsCount = 0; - /// /// Used for tracking semi-auto / burst /// @@ -257,12 +232,6 @@ public sealed partial class GunComponent : Component /// [DataField] public bool ClumsyProof = false; - - /// - /// Firing direction for an item not being held (e.g. shuttle cannons, thrown guns still firing). - /// - [DataField] - public Vector2 DefaultDirection = new Vector2(0, -1); } [Flags] diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs index 2c0204d946..38f427429f 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs @@ -130,7 +130,7 @@ public bool TryRevolverInsert(EntityUid revolverUid, RevolverAmmoProviderCompone return false; } - for (var i = Math.Min(ev.Ammo.Count - 1, component.Capacity - 1); i >= 0; i--) + for (var i = 0; i < component.Capacity; i++) { var index = (component.CurrentIndex + i) % component.Capacity; diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index 26ca3f2cf1..cd6704758d 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -219,7 +219,7 @@ public void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun, Ent /// public void AttemptShoot(EntityUid gunUid, GunComponent gun) { - var coordinates = new EntityCoordinates(gunUid, gun.DefaultDirection); + var coordinates = new EntityCoordinates(gunUid, new Vector2(0, -1)); gun.ShootCoordinates = coordinates; AttemptShoot(gunUid, gunUid, gun); gun.ShotCounter = 0; @@ -259,9 +259,6 @@ public void AttemptShoot(EntityUid gunUid, GunComponent gun) var fireRate = TimeSpan.FromSeconds(1f / gun.FireRateModified); - if (gun.SelectedMode == SelectiveFire.Burst || gun.BurstActivated) - fireRate = TimeSpan.FromSeconds(1f / gun.BurstFireRate); - // First shot // Previously we checked shotcounter but in some cases all the bullets got dumped at once // curTime - fireRate is insufficient because if you time it just right you can get a 3rd shot out slightly quicker. @@ -282,24 +279,18 @@ public void AttemptShoot(EntityUid gunUid, GunComponent gun) // Get how many shots we're actually allowed to make, due to clip size or otherwise. // Don't do this in the loop so we still reset NextFire. - if (!gun.BurstActivated) + switch (gun.SelectedMode) { - switch (gun.SelectedMode) - { - case SelectiveFire.SemiAuto: - shots = Math.Min(shots, 1 - gun.ShotCounter); - break; - case SelectiveFire.Burst: - shots = Math.Min(shots, gun.ShotsPerBurstModified - gun.ShotCounter); - break; - case SelectiveFire.FullAuto: - break; - default: - throw new ArgumentOutOfRangeException($"No implemented shooting behavior for {gun.SelectedMode}!"); - } - } else - { - shots = Math.Min(shots, gun.ShotsPerBurstModified - gun.ShotCounter); + case SelectiveFire.SemiAuto: + shots = Math.Min(shots, 1 - gun.ShotCounter); + break; + case SelectiveFire.Burst: + shots = Math.Min(shots, gun.ShotsPerBurstModified - gun.ShotCounter); + break; + case SelectiveFire.FullAuto: + break; + default: + throw new ArgumentOutOfRangeException($"No implemented shooting behavior for {gun.SelectedMode}!"); } var attemptEv = new AttemptShootEvent(user, null); @@ -311,15 +302,11 @@ public void AttemptShoot(EntityUid gunUid, GunComponent gun) { PopupSystem.PopupClient(attemptEv.Message, gunUid, user); } - gun.BurstActivated = false; - gun.BurstShotsCount = 0; + gun.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.NextFire.TotalSeconds)); return null; } - if (!Timing.IsFirstTimePredicted) - return null; - var fromCoordinates = Transform(user).Coordinates; // Remove ammo var ev = new TakeAmmoEvent(shots, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoordinates, user); @@ -335,6 +322,25 @@ public void AttemptShoot(EntityUid gunUid, GunComponent gun) // Even if we don't actually shoot update the ShotCounter. This is to avoid spamming empty sounds // where the gun may be SemiAuto or Burst. gun.ShotCounter += shots; + Dirty(gunUid, gun); + + void CleanupClient() + { + foreach (var (ent, _) in ev.Ammo) + { + if (ent == null) + continue; + + if (_netManager.IsServer || IsClientSide(ent.Value)) + Del(ent); + } + } + + if (!Timing.IsFirstTimePredicted) + { + CleanupClient(); + return null; + } if (ev.Ammo.Count <= 0) { @@ -342,10 +348,6 @@ public void AttemptShoot(EntityUid gunUid, GunComponent gun) var emptyGunShotEvent = new OnEmptyGunShotEvent(); RaiseLocalEvent(gunUid, ref emptyGunShotEvent); - gun.BurstActivated = false; - gun.BurstShotsCount = 0; - gun.NextFire += TimeSpan.FromSeconds(gun.BurstCooldown); - // Play empty gun sounds if relevant // If they're firing an existing clip then don't play anything. if (shots > 0) @@ -365,21 +367,11 @@ public void AttemptShoot(EntityUid gunUid, GunComponent gun) return null; } - // Handle burstfire - if (gun.SelectedMode == SelectiveFire.Burst) - { - gun.BurstActivated = true; - } - if (gun.BurstActivated) - { - gun.BurstShotsCount += shots; - if (gun.BurstShotsCount >= gun.ShotsPerBurstModified) - { - gun.NextFire += TimeSpan.FromSeconds(gun.BurstCooldown); - gun.BurstActivated = false; - gun.BurstShotsCount = 0; - } - } + // if (_netManager.IsClient && HasComp(gunUid)) + // { + // CleanupClient(); + // return null; + // } // Shoot confirmed - sounds also played here in case it's invalid (e.g. cartridge already spent). var projectiles = Shoot(gunUid, gun, ev.Ammo, fromCoordinates, toCoordinates.Value, out var userImpulse, user, throwItems: attemptEv.ThrowItems, predictedProjectiles, userSession); @@ -495,9 +487,14 @@ void MarkPredicted(EntityUid projectile, int index) if (_netManager.IsServer || GunPrediction) { var uid = Spawn(cartridge.Prototype, fromEnt); - shotProjectiles.Add(uid); CreateAndFireProjectiles(uid, cartridge); + if (_netManager.IsClient && HasComp(gunUid)) + { + predictedProjectiles?.RemoveAll(i => i == uid.Id); + QueueDel(uid); + } + RaiseLocalEvent(ent!.Value, new AmmoShotEvent() { FiredProjectiles = shotProjectiles, @@ -538,8 +535,7 @@ void MarkPredicted(EntityUid projectile, int index) case AmmoComponent newAmmo: if (_netManager.IsServer || GunPrediction) { - shotProjectiles.Add(ent!.Value); - CreateAndFireProjectiles(ent.Value, newAmmo); + CreateAndFireProjectiles(ent!.Value, newAmmo); } else { @@ -549,10 +545,8 @@ void MarkPredicted(EntityUid projectile, int index) Recoil(user, mapDirection, gun.CameraRecoilScalarModified); - if (IsClientSide(ent!.Value)) - Del(ent.Value); - else if (_netManager.IsClient) - RemoveShootable(ent.Value); + if (_netManager.IsClient) + RemoveShootable(ent!.Value); MarkPredicted(ent!.Value, 0); break; case HitscanPrototype hitscan: @@ -689,7 +683,7 @@ void CreateAndFireProjectiles(EntityUid ammoEnt, AmmoComponent ammoComp) var newuid = Spawn(ammoSpreadComp.Proto, fromEnt); ShootOrThrow(newuid, angles[i].ToVec(), gunVelocity, gun, gunUid, user); shotProjectiles.Add(newuid); - MarkPredicted(newuid, i + 1); + MarkPredicted(newuid, i); } } else @@ -716,7 +710,6 @@ private Angle GetRecoilAngle(TimeSpan curTime, GunComponent component, Angle dir long tick = Timing.CurTick.Value; tick = tick << 32; tick = tick | (uint) GetNetEntity(component.Owner).Id; - Logger.Info(Timing.CurTick.ToString()); var random = new Xoroshiro64S(tick).NextFloat(-0.5f, 0.5f); var spread = component.CurrentAngle.Theta * random; var angle = new Angle(direction.Theta + component.CurrentAngle.Theta * random); diff --git a/Content.Shared/_RMC14/Weapons/Ranged/Prediction/GunIgnorePredictionComponent.cs b/Content.Shared/_RMC14/Weapons/Ranged/Prediction/GunIgnorePredictionComponent.cs new file mode 100644 index 0000000000..f148a41a7a --- /dev/null +++ b/Content.Shared/_RMC14/Weapons/Ranged/Prediction/GunIgnorePredictionComponent.cs @@ -0,0 +1,7 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._RMC14.Weapons.Ranged.Prediction; + +[RegisterComponent, NetworkedComponent] +[Access(typeof(SharedGunPredictionSystem))] +public sealed partial class GunIgnorePredictionComponent : Component; diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml index d23e34a1ec..b2c063f504 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml @@ -19,7 +19,6 @@ minAngle: 2 maxAngle: 16 fireRate: 8 - burstFireRate: 8 angleIncrease: 3 angleDecay: 16 selectedMode: FullAuto @@ -28,7 +27,6 @@ - FullAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/smg.ogg - defaultDirection: 1, 0 - type: ChamberMagazineAmmoProvider soundRack: path: /Audio/Weapons/Guns/Cock/smg_cock.ogg @@ -142,15 +140,12 @@ - type: Gun minAngle: 21 maxAngle: 32 - fireRate: 12 - burstFireRate: 12 - selectedMode: Burst + fireRate: 6 + selectedMode: FullAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/atreides.ogg availableModes: - - Burst - shotsPerBurst: 3 - burstCooldown: 0.25 + - FullAuto - type: ItemSlots slots: gun_magazine: @@ -255,8 +250,6 @@ angleDecay: 6 selectedMode: FullAuto shotsPerBurst: 5 - burstCooldown: 0.2 - burstFireRate: 7 availableModes: - SemiAuto - Burst