base.Initialize();
UpdatesOutsidePrediction = true;
SubscribeLocalEvent<AmmoCounterComponent, ItemStatusCollectMessage>(OnAmmoCounterCollect);
+ SubscribeLocalEvent<AmmoCounterComponent, UpdateClientAmmoEvent>(OnUpdateClientAmmo);
SubscribeAllEvent<MuzzleFlashEvent>(OnMuzzleFlash);
// Plays animated effects on the client.
InitializeSpentAmmo();
}
+ private void OnUpdateClientAmmo(EntityUid uid, AmmoCounterComponent ammoComp, ref UpdateClientAmmoEvent args)
+ {
+ UpdateAmmoCount(uid, ammoComp);
+ }
+
private void OnMuzzleFlash(MuzzleFlashEvent args)
{
var gunUid = GetEntity(args.Uid);
var recharger = EnsureComp<BatterySelfRechargerComponent>(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"),
recharger.AutoRecharge = true;
recharger.AutoRechargeRate = battery.MaxCharge; // Instant refill.
+ recharger.AutoRechargePause = false; // No delay.
}
},
Impact = LogImpact.Extreme,
+using System;
+
namespace Content.Server.Power.Components
{
/// <summary>
[RegisterComponent]
public sealed partial class BatterySelfRechargerComponent : Component
{
- [ViewVariables(VVAccess.ReadWrite)] [DataField("autoRecharge")] public bool AutoRecharge { get; set; }
+ /// <summary>
+ /// Does the entity auto recharge?
+ /// </summary>
+ [DataField] public bool AutoRecharge;
+
+ /// <summary>
+ /// At what rate does the entity automatically recharge?
+ /// </summary>
+ [DataField] public float AutoRechargeRate;
+
+ /// <summary>
+ /// Should this entity stop automatically recharging if a charge is used?
+ /// </summary>
+ [DataField] public bool AutoRechargePause = false;
+
+ /// <summary>
+ /// How long should the entity stop automatically recharging if a charge is used?
+ /// </summary>
+ [DataField] public float AutoRechargePauseTime = 0f;
- [ViewVariables(VVAccess.ReadWrite)] [DataField("autoRechargeRate")] public float AutoRechargeRate { get; set; }
+ /// <summary>
+ /// Do not auto recharge if this timestamp has yet to happen, set for the auto recharge pause system.
+ /// </summary>
+ [DataField] public TimeSpan NextAutoRecharge = TimeSpan.FromSeconds(0f);
}
}
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();
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);
}
}
{
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)
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;
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);
}
+ /// <summary>
+ /// Checks if the entity has a self recharge and puts it on cooldown if applicable.
+ /// </summary>
+ public void TrySetChargeCooldown(EntityUid uid, float value = -1)
+ {
+ if (!TryComp<BatterySelfRechargerComponent>(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);
+ }
+
+ /// <summary>
+ /// Puts the entity's self recharge on cooldown for the specified time.
+ /// </summary>
+ 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;
+ }
/// <summary>
/// If sufficient charge is available on the battery, use it. Otherwise, don't.
--- /dev/null
+namespace Content.Shared.Weapons.Ranged.Events;
+
+[ByRefEvent]
+public readonly record struct UpdateClientAmmoEvent();
\ No newline at end of file
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;
component.CurrentFireMode = index;
Dirty(uid, component);
- if (TryComp(uid, out ProjectileBatteryAmmoProviderComponent? projectileBatteryAmmoProvider))
+ if (TryComp(uid, out ProjectileBatteryAmmoProviderComponent? projectileBatteryAmmoProviderComponent))
{
if (!_prototypeManager.TryIndex<EntityPrototype>(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)
{
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:
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