--- /dev/null
+using Content.Server.Chemistry.EntitySystems;
+using Content.Shared.Chemistry.Components;
+using Content.Shared.FixedPoint;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Server.Chemistry.Components;
+
+/// <summary>
+/// Passively increases a solution's quantity of a reagent.
+/// </summary>
+[RegisterComponent]
+[Access(typeof(SolutionRegenerationSystem))]
+public sealed class SolutionRegenerationComponent : Component
+{
+ /// <summary>
+ /// The name of the solution to add to.
+ /// </summary>
+ [DataField("solution", required: true), ViewVariables(VVAccess.ReadWrite)]
+ public string Solution = string.Empty;
+
+ /// <summary>
+ /// The reagent(s) to be regenerated in the solution.
+ /// </summary>
+ [DataField("generated", required: true), ViewVariables(VVAccess.ReadWrite)]
+ public Solution Generated = default!;
+
+ /// <summary>
+ /// How long it takes to regenerate once.
+ /// </summary>
+ [DataField("duration"), ViewVariables(VVAccess.ReadWrite)]
+ public TimeSpan Duration = TimeSpan.FromSeconds(1);
+
+ /// <summary>
+ /// The time when the next regeneration will occur.
+ /// </summary>
+ [DataField("nextChargeTime", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
+ public TimeSpan NextRegenTime = TimeSpan.FromSeconds(0);
+}
--- /dev/null
+using Content.Server.Chemistry.Components;
+using Content.Server.Chemistry.Components.SolutionManager;
+using Robust.Shared.Timing;
+
+namespace Content.Server.Chemistry.EntitySystems;
+
+public sealed class SolutionRegenerationSystem : EntitySystem
+{
+ [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<SolutionRegenerationComponent, EntityUnpausedEvent>(OnUnpaused);
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ var query = EntityQueryEnumerator<SolutionRegenerationComponent, SolutionContainerManagerComponent>();
+ while (query.MoveNext(out var uid, out var regen, out var manager))
+ {
+ if (_timing.CurTime < regen.NextRegenTime)
+ continue;
+
+ // timer ignores if its full, it's just a fixed cycle
+ regen.NextRegenTime = _timing.CurTime + regen.Duration;
+ if (_solutionContainer.TryGetSolution(uid, regen.Solution, out var solution, manager))
+ _solutionContainer.TryAddSolution(uid, solution, regen.Generated);
+ }
+ }
+
+ private void OnUnpaused(EntityUid uid, SolutionRegenerationComponent comp, ref EntityUnpausedEvent args)
+ {
+ comp.NextRegenTime += args.PausedTime;
+ }
+}