// share as much code as possible
if (!Timing.IsFirstTimePredicted ||
!TryComp<AmmoCounterComponent>(uid, out var clientComp))
+ {
return;
+ }
UpdateAmmoCount(uid, clientComp);
}
+++ /dev/null
-using Robust.Shared.Audio;
-
-namespace Content.Server.Weapons.Ranged.Components;
-
-/// <summary>
-/// Responsible for handling recharging a basic entity ammo provider over time.
-/// </summary>
-[RegisterComponent]
-public sealed class RechargeBasicEntityAmmoComponent : Component
-{
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("minRechargeCooldown")]
- public float MinRechargeCooldown = 30f;
-
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("maxRechargeCooldown")]
- public float MaxRechargeCooldown = 45f;
-
- [DataField("rechargeSound")]
- public SoundSpecifier RechargeSound = new SoundPathSpecifier("/Audio/Magic/forcewall.ogg")
- {
- Params = AudioParams.Default.WithVolume(-5f)
- };
-
- [DataField("accumulatedFrametime")]
- public float AccumulatedFrameTime;
- /// <summary>
- /// Number of seconds until the next recharge.
- /// </summary>
- [ViewVariables(VVAccess.ReadWrite)]
- public float NextRechargeTime = 0f;
-}
+++ /dev/null
-using Content.Server.Weapons.Ranged.Components;
-using Content.Shared.Examine;
-using Content.Shared.Weapons.Ranged.Components;
-using Content.Shared.Weapons.Ranged.Systems;
-using Robust.Shared.Audio;
-using Robust.Shared.Player;
-using Robust.Shared.Random;
-
-namespace Content.Server.Weapons.Ranged.Systems;
-
-public sealed class RechargeBasicEntityAmmoSystem : EntitySystem
-{
- [Dependency] private readonly IRobustRandom _random = default!;
- [Dependency] private readonly SharedGunSystem _gun = default!;
-
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent<RechargeBasicEntityAmmoComponent, ComponentInit>(OnInit);
- SubscribeLocalEvent<RechargeBasicEntityAmmoComponent, ExaminedEvent>(OnExamined);
- }
-
- public override void Update(float frameTime)
- {
- base.Update(frameTime);
-
- foreach (var (recharge, ammo) in
- EntityQuery<RechargeBasicEntityAmmoComponent, BasicEntityAmmoProviderComponent>())
- {
- if (ammo.Count is null || ammo.Count == ammo.Capacity)
- continue;
-
- recharge.AccumulatedFrameTime += frameTime;
-
- if (recharge.AccumulatedFrameTime < recharge.NextRechargeTime)
- continue;
-
- recharge.AccumulatedFrameTime -= recharge.NextRechargeTime;
- UpdateCooldown(recharge);
-
-
- if (_gun.UpdateBasicEntityAmmoCount(ammo.Owner, ammo.Count.Value + 1, ammo))
- {
- SoundSystem.Play(recharge.RechargeSound.GetSound(), Filter.Pvs(recharge.Owner), recharge.Owner,
- recharge.RechargeSound.Params);
- }
- }
- }
-
- private void OnInit(EntityUid uid, RechargeBasicEntityAmmoComponent component, ComponentInit args)
- {
- UpdateCooldown(component);
- }
-
- private void OnExamined(EntityUid uid, RechargeBasicEntityAmmoComponent component, ExaminedEvent args)
- {
- if (!TryComp<BasicEntityAmmoProviderComponent>(uid, out var ammo)
- || ammo.Count == ammo.Capacity)
- {
- args.PushMarkup(Loc.GetString("recharge-basic-entity-ammo-full"));
- return;
- }
-
- var timeLeft = component.NextRechargeTime - component.AccumulatedFrameTime;
- args.PushMarkup(Loc.GetString("recharge-basic-entity-ammo-can-recharge", ("seconds", Math.Round(timeLeft, 1))));
- }
-
- private void UpdateCooldown(RechargeBasicEntityAmmoComponent component)
- {
- component.NextRechargeTime = _random.NextFloat(component.MinRechargeCooldown, component.MaxRechargeCooldown);
- }
-}
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Weapons.Ranged.Components;
/// Simply provides a certain capacity of entities that cannot be reloaded through normal means and have
/// no special behavior like cycling, magazine
/// </summary>
-[RegisterComponent, AutoGenerateComponentState]
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class BasicEntityAmmoProviderComponent : AmmoProviderComponent
{
[ViewVariables(VVAccess.ReadWrite)]
--- /dev/null
+using Robust.Shared.Audio;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Shared.Weapons.Ranged.Components;
+
+/// <summary>
+/// Responsible for handling recharging a basic entity ammo provider over time.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class RechargeBasicEntityAmmoComponent : Component
+{
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("rechargeCooldown")]
+ [AutoNetworkedField]
+ public float RechargeCooldown = 1.5f;
+
+ [DataField("rechargeSound")]
+ [AutoNetworkedField]
+ public SoundSpecifier RechargeSound = new SoundPathSpecifier("/Audio/Magic/forcewall.ogg")
+ {
+ Params = AudioParams.Default.WithVolume(-5f)
+ };
+
+ [ViewVariables(VVAccess.ReadWrite),
+ DataField("nextCharge", customTypeSerializer:typeof(TimeOffsetSerializer)),
+ AutoNetworkedField]
+ public TimeSpan? NextCharge;
+}
--- /dev/null
+using Content.Shared.Examine;
+using Content.Shared.Weapons.Ranged.Components;
+using Robust.Shared.Network;
+using Robust.Shared.Player;
+using Robust.Shared.Timing;
+using Robust.Shared.Utility;
+
+namespace Content.Shared.Weapons.Ranged.Systems;
+
+public sealed class RechargeBasicEntityAmmoSystem : EntitySystem
+{
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly INetManager _netManager = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly SharedGunSystem _gun = default!;
+ [Dependency] private readonly MetaDataSystem _metadata = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<RechargeBasicEntityAmmoComponent, EntityUnpausedEvent>(OnUnpaused);
+ SubscribeLocalEvent<RechargeBasicEntityAmmoComponent, MapInitEvent>(OnInit);
+ SubscribeLocalEvent<RechargeBasicEntityAmmoComponent, ExaminedEvent>(OnExamined);
+ }
+
+ private void OnUnpaused(EntityUid uid, RechargeBasicEntityAmmoComponent component, ref EntityUnpausedEvent args)
+ {
+ if (component.NextCharge == null)
+ return;
+
+ component.NextCharge = component.NextCharge.Value + args.PausedTime;
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+ var query = EntityQueryEnumerator<RechargeBasicEntityAmmoComponent, BasicEntityAmmoProviderComponent>();
+
+ while (query.MoveNext(out var uid, out var recharge, out var ammo))
+ {
+ if (ammo.Count is null || ammo.Count == ammo.Capacity || recharge.NextCharge == null)
+ continue;
+
+ if (recharge.NextCharge > _timing.CurTime)
+ continue;
+
+ if (_gun.UpdateBasicEntityAmmoCount(uid, ammo.Count.Value + 1, ammo))
+ {
+ if (_netManager.IsClient && _timing.IsFirstTimePredicted)
+ _audio.Play(recharge.RechargeSound, Filter.Local(), uid, true);
+ }
+
+ if (ammo.Count == ammo.Capacity)
+ {
+ recharge.NextCharge = null;
+ Dirty(recharge);
+ continue;
+ }
+
+ recharge.NextCharge = recharge.NextCharge.Value + TimeSpan.FromSeconds(recharge.RechargeCooldown);
+ Dirty(recharge);
+ }
+ }
+
+ private void OnInit(EntityUid uid, RechargeBasicEntityAmmoComponent component, MapInitEvent args)
+ {
+ component.NextCharge = _timing.CurTime;
+ Dirty(component);
+ }
+
+ private void OnExamined(EntityUid uid, RechargeBasicEntityAmmoComponent component, ExaminedEvent args)
+ {
+ if (!TryComp<BasicEntityAmmoProviderComponent>(uid, out var ammo)
+ || ammo.Count == ammo.Capacity ||
+ component.NextCharge == null)
+ {
+ args.PushMarkup(Loc.GetString("recharge-basic-entity-ammo-full"));
+ return;
+ }
+
+ var timeLeft = component.NextCharge + _metadata.GetPauseTime(uid) - _timing.CurTime;
+ args.PushMarkup(Loc.GetString("recharge-basic-entity-ammo-can-recharge", ("seconds", Math.Round(timeLeft.Value.TotalSeconds, 1))));
+ }
+
+ public void Reset(EntityUid uid, RechargeBasicEntityAmmoComponent? recharge = null)
+ {
+ if (!Resolve(uid, ref recharge, false))
+ return;
+
+ if (recharge.NextCharge == null || recharge.NextCharge < _timing.CurTime)
+ {
+ recharge.NextCharge = _timing.CurTime + TimeSpan.FromSeconds(recharge.RechargeCooldown);
+ Dirty(recharge);
+ }
+ }
+}
{
protected virtual void InitializeBasicEntity()
{
- SubscribeLocalEvent<BasicEntityAmmoProviderComponent, ComponentInit>(OnBasicEntityInit);
+ SubscribeLocalEvent<BasicEntityAmmoProviderComponent, MapInitEvent>(OnBasicEntityMapInit);
SubscribeLocalEvent<BasicEntityAmmoProviderComponent, TakeAmmoEvent>(OnBasicEntityTakeAmmo);
SubscribeLocalEvent<BasicEntityAmmoProviderComponent, GetAmmoCountEvent>(OnBasicEntityAmmoCount);
}
- private void OnBasicEntityInit(EntityUid uid, BasicEntityAmmoProviderComponent component, ComponentInit args)
+ private void OnBasicEntityMapInit(EntityUid uid, BasicEntityAmmoProviderComponent component, MapInitEvent args)
{
if (component.Count is null)
{
private void OnBasicEntityTakeAmmo(EntityUid uid, BasicEntityAmmoProviderComponent component, TakeAmmoEvent args)
{
- for (int i = 0; i < args.Shots; i++)
+ for (var i = 0; i < args.Shots; i++)
{
if (component.Count <= 0)
return;
args.Ammo.Add((ent, EnsureComp<AmmoComponent>(ent)));
}
+ _recharge.Reset(uid);
UpdateBasicEntityAppearance(uid, component);
Dirty(component);
}
component.Count = count;
Dirty(component);
UpdateBasicEntityAppearance(uid, component);
+ UpdateAmmoCount(uid);
return true;
}
[Dependency] protected readonly DamageableSystem Damageable = default!;
[Dependency] protected readonly ExamineSystemShared Examine = default!;
[Dependency] private readonly ItemSlotsSystem _slots = default!;
+ [Dependency] private readonly RechargeBasicEntityAmmoSystem _recharge = default!;
[Dependency] protected readonly SharedActionsSystem Actions = default!;
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
[Dependency] private readonly SharedCombatModeSystem _combatMode = default!;
shots++;
}
+ // NextFire has been touched regardless so need to dirty the gun.
+ Dirty(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.
switch (gun.SelectedMode)
// May cause prediction issues? Needs more tweaking
gun.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.NextFire.TotalSeconds));
Audio.PlayPredicted(gun.SoundEmpty, gunUid, user);
- Dirty(gun);
return;
}
sprite: Objects/Weapons/Guns/Basic/kinetic_accelerator.rsi
size: 30
- type: Gun
- fireRate: 1
+ fireRate: 0.75
selectedMode: SemiAuto
availableModes:
- SemiAuto
True: { visible: False }
False: { visible: True }
- type: RechargeBasicEntityAmmo
- minRechargeCooldown: 1.5
- maxRechargeCooldown: 1.5
+ rechargeCooldown: 1
rechargeSound:
path: /Audio/Weapons/Guns/MagIn/kinetic_reload.ogg
- type: BasicEntityAmmoProvider
- type: AmmoCounter
# All staves recharge. Wands are not.
- type: RechargeBasicEntityAmmo
+ rechargeCooldown: 30
- type: Tag
tags:
- WizardStaff