diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs index 710ee7c7cbf736..0ad22bf1d87878 100644 --- a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs @@ -77,6 +77,7 @@ public override void Initialize() base.Initialize(); UpdatesOutsidePrediction = true; SubscribeLocalEvent(OnAmmoCounterCollect); + SubscribeLocalEvent(OnUpdateClientAmmo); SubscribeAllEvent(OnMuzzleFlash); // Plays animated effects on the client. @@ -86,6 +87,11 @@ public override void Initialize() InitializeSpentAmmo(); } + private void OnUpdateClientAmmo(EntityUid uid, AmmoCounterComponent ammoComp, ref UpdateClientAmmoEvent args) + { + UpdateAmmoCount(uid, ammoComp); + } + private void OnMuzzleFlash(MuzzleFlashEvent args) { var gunUid = GetEntity(args.Uid); diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs index 70fcfccc4eb9f1..8dccaabad6088f 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs @@ -204,6 +204,7 @@ private void AddTricksVerbs(GetVerbsEvent args) var recharger = EnsureComp(args.Target); recharger.AutoRecharge = true; recharger.AutoRechargeRate = battery.MaxCharge; // Instant refill. + recharger.AutoRechargePause = false; // No delay. }, Impact = LogImpact.Medium, Message = Loc.GetString("admin-trick-infinite-battery-object-description"), @@ -603,6 +604,7 @@ private void AddTricksVerbs(GetVerbsEvent args) recharger.AutoRecharge = true; recharger.AutoRechargeRate = battery.MaxCharge; // Instant refill. + recharger.AutoRechargePause = false; // No delay. } }, Impact = LogImpact.Extreme, diff --git a/Content.Server/Power/Components/BatterySelfRechargerComponent.cs b/Content.Server/Power/Components/BatterySelfRechargerComponent.cs index 4798beb7573976..1cb92d9cd6311d 100644 --- a/Content.Server/Power/Components/BatterySelfRechargerComponent.cs +++ b/Content.Server/Power/Components/BatterySelfRechargerComponent.cs @@ -1,3 +1,5 @@ +using System; + namespace Content.Server.Power.Components { /// @@ -6,8 +8,29 @@ namespace Content.Server.Power.Components [RegisterComponent] public sealed partial class BatterySelfRechargerComponent : Component { - [ViewVariables(VVAccess.ReadWrite)] [DataField("autoRecharge")] public bool AutoRecharge { get; set; } + /// + /// Does the entity auto recharge? + /// + [DataField] public bool AutoRecharge; + + /// + /// At what rate does the entity automatically recharge? + /// + [DataField] public float AutoRechargeRate; + + /// + /// Should this entity stop automatically recharging if a charge is used? + /// + [DataField] public bool AutoRechargePause = false; + + /// + /// How long should the entity stop automatically recharging if a charge is used? + /// + [DataField] public float AutoRechargePauseTime = 0f; - [ViewVariables(VVAccess.ReadWrite)] [DataField("autoRechargeRate")] public float AutoRechargeRate { get; set; } + /// + /// Do not auto recharge if this timestamp has yet to happen, set for the auto recharge pause system. + /// + [DataField] public TimeSpan NextAutoRecharge = TimeSpan.FromSeconds(0f); } } diff --git a/Content.Server/Power/EntitySystems/BatterySystem.cs b/Content.Server/Power/EntitySystems/BatterySystem.cs index ed2d54ffaa60f0..eac31ecef789d1 100644 --- a/Content.Server/Power/EntitySystems/BatterySystem.cs +++ b/Content.Server/Power/EntitySystems/BatterySystem.cs @@ -3,14 +3,18 @@ using Content.Server.Power.Components; using Content.Shared.Examine; using Content.Shared.Rejuvenate; +using Content.Shared.Timing; using JetBrains.Annotations; using Robust.Shared.Utility; +using Robust.Shared.Timing; namespace Content.Server.Power.EntitySystems { [UsedImplicitly] public sealed class BatterySystem : EntitySystem { + [Dependency] protected readonly IGameTiming Timing = default!; + public override void Initialize() { base.Initialize(); @@ -84,6 +88,14 @@ public override void Update(float frameTime) while (query.MoveNext(out var uid, out var comp, out var batt)) { if (!comp.AutoRecharge) continue; + if (batt.IsFullyCharged) continue; + + if (comp.AutoRechargePause) + { + if (comp.NextAutoRecharge > Timing.CurTime) + continue; + } + SetCharge(uid, batt.CurrentCharge + comp.AutoRechargeRate * frameTime, batt); } } @@ -100,6 +112,8 @@ private void OnEmpPulse(EntityUid uid, BatteryComponent component, ref EmpPulseE { args.Affected = true; UseCharge(uid, args.EnergyConsumption, component); + // Apply a cooldown to the entity's self recharge if needed to avoid it immediately self recharging after an EMP. + TrySetChargeCooldown(uid); } public float UseCharge(EntityUid uid, float value, BatteryComponent? battery = null) @@ -110,6 +124,10 @@ public float UseCharge(EntityUid uid, float value, BatteryComponent? battery = n var newValue = Math.Clamp(0, battery.CurrentCharge - value, battery.MaxCharge); var delta = newValue - battery.CurrentCharge; battery.CurrentCharge = newValue; + + // Apply a cooldown to the entity's self recharge if needed. + TrySetChargeCooldown(uid); + var ev = new ChargeChangedEvent(battery.CurrentCharge, battery.MaxCharge); RaiseLocalEvent(uid, ref ev); return delta; @@ -139,11 +157,47 @@ public void SetCharge(EntityUid uid, float value, BatteryComponent? battery = nu battery.CurrentCharge = MathHelper.Clamp(value, 0, battery.MaxCharge); if (MathHelper.CloseTo(battery.CurrentCharge, old) && !(old != battery.CurrentCharge && battery.CurrentCharge == battery.MaxCharge)) + { return; + } var ev = new ChargeChangedEvent(battery.CurrentCharge, battery.MaxCharge); RaiseLocalEvent(uid, ref ev); } + /// + /// Checks if the entity has a self recharge and puts it on cooldown if applicable. + /// + public void TrySetChargeCooldown(EntityUid uid, float value = -1) + { + if (!TryComp(uid, out var batteryself)) + return; + + if (!batteryself.AutoRechargePause) + return; + + // If no answer or a negative is given for value, use the default from AutoRechargePauseTime. + if (value < 0) + value = batteryself.AutoRechargePauseTime; + + if (Timing.CurTime + TimeSpan.FromSeconds(value) <= batteryself.NextAutoRecharge) + return; + + SetChargeCooldown(uid, batteryself.AutoRechargePauseTime, batteryself); + } + + /// + /// Puts the entity's self recharge on cooldown for the specified time. + /// + public void SetChargeCooldown(EntityUid uid, float value, BatterySelfRechargerComponent? batteryself = null) + { + if (!Resolve(uid, ref batteryself)) + return; + + if (value >= 0) + batteryself.NextAutoRecharge = Timing.CurTime + TimeSpan.FromSeconds(value); + else + batteryself.NextAutoRecharge = Timing.CurTime; + } /// /// If sufficient charge is available on the battery, use it. Otherwise, don't. diff --git a/Content.Shared/Weapons/Ranged/Events/UpdateClientAmmoEvent.cs b/Content.Shared/Weapons/Ranged/Events/UpdateClientAmmoEvent.cs new file mode 100644 index 00000000000000..57f731889a4d2e --- /dev/null +++ b/Content.Shared/Weapons/Ranged/Events/UpdateClientAmmoEvent.cs @@ -0,0 +1,4 @@ +namespace Content.Shared.Weapons.Ranged.Events; + +[ByRefEvent] +public readonly record struct UpdateClientAmmoEvent(); \ No newline at end of file diff --git a/Content.Shared/Weapons/Ranged/Systems/BatteryWeaponFireModesSystem.cs b/Content.Shared/Weapons/Ranged/Systems/BatteryWeaponFireModesSystem.cs index 8e44576d283458..bae5b95a193db1 100644 --- a/Content.Shared/Weapons/Ranged/Systems/BatteryWeaponFireModesSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/BatteryWeaponFireModesSystem.cs @@ -5,6 +5,7 @@ using Content.Shared.Popups; using Content.Shared.Verbs; using Content.Shared.Weapons.Ranged.Components; +using Content.Shared.Weapons.Ranged.Events; using Robust.Shared.Prototypes; namespace Content.Shared.Weapons.Ranged.Systems; @@ -99,13 +100,21 @@ private void SetFireMode(EntityUid uid, BatteryWeaponFireModesComponent componen component.CurrentFireMode = index; Dirty(uid, component); - if (TryComp(uid, out ProjectileBatteryAmmoProviderComponent? projectileBatteryAmmoProvider)) + if (TryComp(uid, out ProjectileBatteryAmmoProviderComponent? projectileBatteryAmmoProviderComponent)) { if (!_prototypeManager.TryIndex(fireMode.Prototype, out var prototype)) return; - projectileBatteryAmmoProvider.Prototype = fireMode.Prototype; - projectileBatteryAmmoProvider.FireCost = fireMode.FireCost; + // TODO: Have this get the info directly from the batteryComponent when power is moved to shared. + var OldFireCost = projectileBatteryAmmoProviderComponent.FireCost; + projectileBatteryAmmoProviderComponent.Prototype = fireMode.Prototype; + projectileBatteryAmmoProviderComponent.FireCost = fireMode.FireCost; + float FireCostDiff = (float)fireMode.FireCost / (float)OldFireCost; + projectileBatteryAmmoProviderComponent.Shots = (int)Math.Round(projectileBatteryAmmoProviderComponent.Shots/FireCostDiff); + projectileBatteryAmmoProviderComponent.Capacity = (int)Math.Round(projectileBatteryAmmoProviderComponent.Capacity/FireCostDiff); + Dirty(uid, projectileBatteryAmmoProviderComponent); + var updateClientAmmoEvent = new UpdateClientAmmoEvent(); + RaiseLocalEvent(uid, ref updateClientAmmoEvent); if (user != null) { diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml index e7fef78f809afa..46c3d36ebcfadc 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml @@ -740,15 +740,15 @@ path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg - type: ProjectileBatteryAmmoProvider proto: BulletLaserSpread - fireCost: 100 + fireCost: 150 - type: BatteryWeaponFireModes fireModes: - proto: BulletLaserSpread - fireCost: 100 + fireCost: 150 - proto: BulletLaserSpreadNarrow - fireCost: 100 + fireCost: 200 - proto: BulletDisablerSmgSpread - fireCost: 100 + fireCost: 120 - type: Item size: Large shape: @@ -762,5 +762,10 @@ stealGroup: WeaponEnergyShotgun - type: GunRequiresWield #remove when inaccuracy on spreads is fixed - type: Battery - maxCharge: 800 - startingCharge: 800 + maxCharge: 1200 + startingCharge: 1200 + - type: BatterySelfRecharger + autoRecharge: true + autoRechargeRate: 24 + autoRechargePause: true + autoRechargePauseTime: 10 \ No newline at end of file