Predict passive welding fuel consumption (#38876)

* predict welding fuel consumption

* autopaused

* review

Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>

---------

Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>
This commit is contained in:
slarticodefast
2025-07-31 00:38:38 +02:00
committed by GitHub
parent 68ba22548d
commit 271e271cc9
4 changed files with 82 additions and 62 deletions

View File

@@ -1,47 +1,5 @@
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Tools.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Tools.Components;
using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem;
namespace Content.Server.Tools; namespace Content.Server.Tools;
public sealed class ToolSystem : SharedToolSystem public sealed class ToolSystem : SharedToolSystem;
{
public override void Update(float frameTime)
{
base.Update(frameTime);
UpdateWelders(frameTime);
}
//todo move to shared once you can remove reagents from shared without it freaking out.
private void UpdateWelders(float frameTime)
{
var query = EntityQueryEnumerator<WelderComponent, SolutionContainerManagerComponent>();
while (query.MoveNext(out var uid, out var welder, out var solutionContainer))
{
if (!welder.Enabled)
continue;
welder.WelderTimer += frameTime;
if (welder.WelderTimer < welder.WelderUpdateTimer)
continue;
if (!SolutionContainerSystem.TryGetSolution((uid, solutionContainer), welder.FuelSolutionName, out var solutionComp, out var solution))
continue;
SolutionContainerSystem.RemoveReagent(solutionComp.Value, welder.FuelReagent, welder.FuelConsumption * welder.WelderTimer);
if (solution.GetTotalPrototypeQuantity(welder.FuelReagent) <= FixedPoint2.Zero)
{
ItemToggle.Toggle(uid, predicted: false);
}
Dirty(uid, welder);
welder.WelderTimer -= welder.WelderUpdateTimer;
}
}
}

View File

@@ -1,58 +1,76 @@
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Tools.Systems; using Content.Shared.Tools.Systems;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared.Tools.Components; namespace Content.Shared.Tools.Components;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), Access(typeof(SharedToolSystem))] /// <summary>
/// Handles fuel consumption for the tool and allows it to explode welding fuel tanks.
/// </summary>
/// <summary>
/// TODO: De-hardcode welder bombing.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), AutoGenerateComponentPause]
[Access(typeof(SharedToolSystem))]
public sealed partial class WelderComponent : Component public sealed partial class WelderComponent : Component
{ {
/// <summary>
/// Is the welder currently enabled?
/// </summary>
[DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public bool Enabled; public bool Enabled;
[DataField] /// <summary>
public float WelderTimer; /// Timestamp for the next update loop update.
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
[AutoNetworkedField, AutoPausedField]
public TimeSpan NextUpdate;
/// <summary> /// <summary>
/// Name of <see cref="FuelSolution"/>. /// Delay between updates.
/// </summary>
[DataField]
public TimeSpan WelderUpdateTimer = TimeSpan.FromSeconds(1);
/// <summary>
/// Name of the fuel solution.
/// </summary> /// </summary>
[DataField] [DataField]
public string FuelSolutionName = "Welder"; public string FuelSolutionName = "Welder";
/// <summary> /// <summary>
/// Reagent that will be used as fuel for welding. /// Reagent that will be used as fuel for welding.
/// </summary> /// </summary>
[DataField] [DataField]
public ProtoId<ReagentPrototype> FuelReagent = "WeldingFuel"; public ProtoId<ReagentPrototype> FuelReagent = "WeldingFuel";
/// <summary> /// <summary>
/// Fuel consumption per second while the welder is active. /// Fuel consumption per second while the welder is active.
/// In u/s
/// </summary> /// </summary>
[DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public FixedPoint2 FuelConsumption = FixedPoint2.New(1.0f); public FixedPoint2 FuelConsumption = FixedPoint2.New(1.0f);
/// <summary> /// <summary>
/// A fuel amount to be consumed when the welder goes from being unlit to being lit. /// A fuel amount to be consumed when the welder goes from being unlit to being lit.
/// </summary> /// </summary>
[DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public FixedPoint2 FuelLitCost = FixedPoint2.New(0.5f); public FixedPoint2 FuelLitCost = FixedPoint2.New(0.5f);
/// <summary> /// <summary>
/// Sound played when refilling the welder. /// Sound played when refilling the welder.
/// </summary> /// </summary>
[DataField] [DataField]
public SoundSpecifier WelderRefill = new SoundPathSpecifier("/Audio/Effects/refill.ogg"); public SoundSpecifier WelderRefill = new SoundPathSpecifier("/Audio/Effects/refill.ogg");
/// <summary> /// <summary>
/// Whether the item is safe to refill while lit without exploding the tank. /// Whether the item is safe to refill while lit without exploding the tank.
/// </summary> /// </summary>
[DataField] [DataField]
public bool TankSafe; public bool TankSafe;
[DataField]
public float WelderUpdateTimer = 1f;
} }

View File

@@ -17,13 +17,16 @@ public abstract partial class SharedToolSystem
public void InitializeWelder() public void InitializeWelder()
{ {
SubscribeLocalEvent<WelderComponent, MapInitEvent>(OnWelderInit);
SubscribeLocalEvent<WelderComponent, ExaminedEvent>(OnWelderExamine); SubscribeLocalEvent<WelderComponent, ExaminedEvent>(OnWelderExamine);
SubscribeLocalEvent<WelderComponent, AfterInteractEvent>(OnWelderAfterInteract); SubscribeLocalEvent<WelderComponent, AfterInteractEvent>(OnWelderAfterInteract);
SubscribeLocalEvent<WelderComponent, ToolUseAttemptEvent>((uid, comp, ev) => { SubscribeLocalEvent<WelderComponent, ToolUseAttemptEvent>((uid, comp, ev) =>
{
CanCancelWelderUse((uid, comp), ev.User, ev.Fuel, ev); CanCancelWelderUse((uid, comp), ev.User, ev.Fuel, ev);
}); });
SubscribeLocalEvent<WelderComponent, DoAfterAttemptEvent<ToolDoAfterEvent>>((uid, comp, ev) => { SubscribeLocalEvent<WelderComponent, DoAfterAttemptEvent<ToolDoAfterEvent>>((uid, comp, ev) =>
{
CanCancelWelderUse((uid, comp), ev.Event.User, ev.Event.Fuel, ev); CanCancelWelderUse((uid, comp), ev.Event.User, ev.Event.Fuel, ev);
}); });
SubscribeLocalEvent<WelderComponent, ToolDoAfterEvent>(OnWelderDoAfter); SubscribeLocalEvent<WelderComponent, ToolDoAfterEvent>(OnWelderDoAfter);
@@ -71,6 +74,12 @@ public abstract partial class SharedToolSystem
return (fuelSolution.GetTotalPrototypeQuantity(welder.FuelReagent), fuelSolution.MaxVolume); return (fuelSolution.GetTotalPrototypeQuantity(welder.FuelReagent), fuelSolution.MaxVolume);
} }
private void OnWelderInit(Entity<WelderComponent> ent, ref MapInitEvent args)
{
ent.Comp.NextUpdate = _timing.CurTime + ent.Comp.WelderUpdateTimer;
Dirty(ent);
}
private void OnWelderExamine(Entity<WelderComponent> entity, ref ExaminedEvent args) private void OnWelderExamine(Entity<WelderComponent> entity, ref ExaminedEvent args)
{ {
using (args.PushGroup(nameof(WelderComponent))) using (args.PushGroup(nameof(WelderComponent)))
@@ -169,7 +178,8 @@ public abstract partial class SharedToolSystem
private void OnActivateAttempt(Entity<WelderComponent> entity, ref ItemToggleActivateAttemptEvent args) private void OnActivateAttempt(Entity<WelderComponent> entity, ref ItemToggleActivateAttemptEvent args)
{ {
if (args.User != null && !_actionBlocker.CanComplexInteract(args.User.Value)) { if (args.User != null && !_actionBlocker.CanComplexInteract(args.User.Value))
{
args.Cancelled = true; args.Cancelled = true;
return; return;
} }
@@ -191,9 +201,34 @@ public abstract partial class SharedToolSystem
private void OnDeactivateAttempt(Entity<WelderComponent> entity, ref ItemToggleDeactivateAttemptEvent args) private void OnDeactivateAttempt(Entity<WelderComponent> entity, ref ItemToggleDeactivateAttemptEvent args)
{ {
if (args.User != null && !_actionBlocker.CanComplexInteract(args.User.Value)) { if (args.User != null && !_actionBlocker.CanComplexInteract(args.User.Value))
{
args.Cancelled = true; args.Cancelled = true;
return; }
}
private void UpdateWelders()
{
var query = EntityQueryEnumerator<WelderComponent, SolutionContainerManagerComponent>();
var curTime = _timing.CurTime;
while (query.MoveNext(out var uid, out var welder, out var solutionContainer))
{
if (curTime < welder.NextUpdate)
continue;
welder.NextUpdate += welder.WelderUpdateTimer;
Dirty(uid, welder);
if (!welder.Enabled)
continue;
if (!SolutionContainerSystem.TryGetSolution((uid, solutionContainer), welder.FuelSolutionName, out var solutionComp, out var solution))
continue;
SolutionContainerSystem.RemoveReagent(solutionComp.Value, welder.FuelReagent, welder.FuelConsumption * welder.WelderUpdateTimer.TotalSeconds);
if (solution.GetTotalPrototypeQuantity(welder.FuelReagent) <= FixedPoint2.Zero)
ItemToggle.Toggle(uid);
} }
} }
} }

View File

@@ -12,12 +12,14 @@ using Robust.Shared.Audio.Systems;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Shared.Tools.Systems; namespace Content.Shared.Tools.Systems;
public abstract partial class SharedToolSystem : EntitySystem public abstract partial class SharedToolSystem : EntitySystem
{ {
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPrototypeManager _protoMan = default!; [Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!; [Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!;
@@ -263,6 +265,13 @@ public abstract partial class SharedToolSystem : EntitySystem
return !beforeAttempt.Cancelled; return !beforeAttempt.Cancelled;
} }
public override void Update(float frameTime)
{
base.Update(frameTime);
UpdateWelders();
}
#region DoAfterEvents #region DoAfterEvents
[Serializable, NetSerializable] [Serializable, NetSerializable]