Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Content.Server._Starlight.Plumbing.Components;
using Content.Server._Starlight.Plumbing.Nodes;
using Content.Shared.NodeContainer;
using Content.Server.Popups;
using Content.Server.UserInterface;
using Content.Shared._Starlight.Plumbing;
Expand Down Expand Up @@ -27,6 +29,9 @@ public sealed class PlumbingFilterSystem : EntitySystem
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly SharedSolutionContainerSystem _solutionSystem = default!;
[Dependency] private readonly PlumbingPullSystem _pullSystem = default!;


public override void Initialize()
{
Expand All @@ -38,6 +43,54 @@ public override void Initialize()
SubscribeLocalEvent<StarlightPlumbingFilterComponent, PlumbingFilterRemoveReagentMessage>(OnRemoveReagent);
SubscribeLocalEvent<StarlightPlumbingFilterComponent, PlumbingFilterClearMessage>(OnClear);
SubscribeLocalEvent<StarlightPlumbingFilterComponent, BoundUIOpenedEvent>(OnUIOpened);
SubscribeLocalEvent<StarlightPlumbingFilterComponent, PlumbingDeviceUpdateEvent>(OnDeviceUpdate);
}

private void OnDeviceUpdate(Entity<StarlightPlumbingFilterComponent> ent, ref PlumbingDeviceUpdateEvent args)
{
// Use the existing PlumbingInlet component on the same entity for inlet names and transfer amount
if (!TryComp<PlumbingInletComponent>(ent.Owner, out var inletComp))
return;

if (!_solutionSystem.TryGetSolution(ent.Owner, ent.Comp.FilteredSolutionName, out var filteredEnt, out var filteredSol))
return;

if (!_solutionSystem.TryGetSolution(ent.Owner, ent.Comp.PassthroughSolutionName, out var passthroughEnt, out var passthroughSol))
return;

if (filteredEnt.Value.Comp.Solution.AvailableVolume <= 0 && passthroughEnt.Value.Comp.Solution.AvailableVolume <= 0)
return;

if (!TryComp<NodeContainerComponent>(ent.Owner, out var nodeContainer))
return;

var remaining = inletComp.TransferAmount;

foreach (var inletName in inletComp.InletNames)
{
if (remaining <= 0 || filteredEnt.Value.Comp.Solution.AvailableVolume <= 0 && passthroughEnt.Value.Comp.Solution.AvailableVolume <= 0)
break;

if (!nodeContainer.Nodes.TryGetValue(inletName, out var node))
continue;

if (node is not PlumbingNode plumbingNode || plumbingNode.PlumbingNet == null)
continue;

var roundRobinIndex = inletComp.RoundRobinIndices.GetValueOrDefault(inletName, 0);
var (pulled, nextIndex) = _pullSystem.PullFromNetworkSplit(
ent.Owner,
plumbingNode.PlumbingNet,
filteredEnt.Value,
passthroughEnt.Value,
remaining,
roundRobinIndex,
ent.Comp.Enabled,
ent.Comp.FilteredReagents);

inletComp.RoundRobinIndices[inletName] = nextIndex;
remaining -= pulled;
}
}

/// <summary>
Expand Down
169 changes: 127 additions & 42 deletions Content.Server/_Starlight/Plumbing/EntitySystems/PlumbingPullSystem.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Content.Server._Starlight.Plumbing.NodeGroups;
using Content.Server._Starlight.Plumbing.Nodes;
using Content.Shared._Starlight.Plumbing.Components;
using Content.Server.Chemistry.Components;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent;
Expand Down Expand Up @@ -35,6 +36,73 @@ public override void Initialize()
_outletQuery = GetEntityQuery<PlumbingOutletComponent>();
}

/// <summary>
/// Returns all solution entities that should be considered sources for the given outlet.
/// This handles containerSlotId pointing at a single slot as before, and expands
/// ReagentDispenser storage slots so each stored jug can be pulled from individually.
/// </summary>
private List<Entity<SolutionComponent>> GetOutletSolutionEntities(EntityUid outletOwner, string nodeName, PlumbingOutletComponent outlet)
{
var res = new List<Entity<SolutionComponent>>();

// If ContainerSlotId is set, default behavior is to return the entity in that slot.
if (outlet.ContainerSlotId != null)
{
// Special-case: if this outlet owner is a ReagentDispenser, expose all storage slot contents
if (TryComp<ReagentDispenserComponent>(outletOwner, out var dispenserComp))
{
foreach (var slotId in dispenserComp.StorageSlotIds)
{
var containerEntity = _itemSlots.GetItemOrNull(outletOwner, slotId);
if (containerEntity == null)
continue;

// Try drainable solution (any usable solution on the stored container)
if (_solutionSystem.TryGetDrainableSolution(containerEntity.Value, out var solEntEntity, out var sol))
{
if (solEntEntity != null)
res.Add(solEntEntity.Value);
}
}

return res;
}

// Fallback: original single-slot behavior
var containerEntity2 = _itemSlots.GetItemOrNull(outletOwner, outlet.ContainerSlotId);
if (containerEntity2 == null)
return res;

var solutionName = outlet.SolutionName;
if (_solutionSystem.TryGetSolution(containerEntity2.Value, solutionName, out var solutionEnt, out _))
{
if (solutionEnt != null)
res.Add(solutionEnt.Value);
}

return res;
}

// No ContainerSlotId: use the outletOwner itself, with plumbing filter handling
var targetEntity = outletOwner;
var solutionNameTop = outlet.SolutionName;
if (targetEntity == outletOwner && TryComp<StarlightPlumbingFilterComponent>(outletOwner, out var filterComp))
{
if (nodeName.Equals(filterComp.FilterNodeName, StringComparison.OrdinalIgnoreCase))
solutionNameTop = filterComp.FilteredSolutionName;
else if (nodeName.Equals(filterComp.PassthroughNodeName, StringComparison.OrdinalIgnoreCase))
solutionNameTop = filterComp.PassthroughSolutionName;
}

if (_solutionSystem.TryGetSolution(targetEntity, solutionNameTop, out var solEntTop, out _))
{
if (solEntTop != null)
res.Add(solEntTop.Value);
}

return res;
}

/// <summary>
/// Pulls all allowed reagents from outlets on a plumbing network into a destination solution.
/// </summary>
Expand All @@ -59,8 +127,8 @@ public override void Initialize()
if (remaining <= 0)
return (FixedPoint2.Zero, roundRobinIndex);

// Build list of valid outlets to pull from
var outlets = new List<(PlumbingNode Node, PlumbingOutletComponent Outlet)>();
// Build list of valid outlets to pull from. Each outlet may produce multiple source solutions.
var outlets = new List<(PlumbingNode Node, PlumbingOutletComponent Outlet, Entity<SolutionComponent> Source)>();
foreach (var node in network.Nodes)
{
if (node is not PlumbingNode plumbingNode || plumbingNode.Owner == puller)
Expand All @@ -84,7 +152,12 @@ public override void Initialize()
if (!isOutletNode)
continue;

outlets.Add((plumbingNode, outlet));
// Expand to one or more source solutions for this outlet
var solutions = GetOutletSolutionEntities(plumbingNode.Owner, plumbingNode.Name, outlet);
foreach (var sol in solutions)
{
outlets.Add((plumbingNode, outlet, sol));
}
}

if (outlets.Count == 0)
Expand All @@ -99,9 +172,9 @@ public override void Initialize()
for (var i = 0; i < outlets.Count && remaining > 0; i++)
{
var index = (startIndex + i) % outlets.Count;
var (plumbingNode, outlet) = outlets[index];
var (plumbingNode, outlet, sourceSol) = outlets[index];

var pulled = PullFromOutlet(puller, plumbingNode.Owner, plumbingNode.Name, outlet, destination, remaining);
var pulled = PullFromOutlet(puller, plumbingNode.Owner, plumbingNode.Name, outlet, destination, remaining, sourceSol);
totalPulled += pulled;
remaining -= pulled;
}
Expand Down Expand Up @@ -132,7 +205,7 @@ public override void Initialize()
&& passthroughDestination.Comp.Solution.AvailableVolume <= 0)
return (FixedPoint2.Zero, roundRobinIndex);

var outlets = new List<(PlumbingNode Node, PlumbingOutletComponent Outlet)>();
var outlets = new List<(PlumbingNode Node, PlumbingOutletComponent Outlet, Entity<SolutionComponent> Source)>();
foreach (var node in network.Nodes)
{
if (node is not PlumbingNode plumbingNode || plumbingNode.Owner == puller)
Expand All @@ -157,7 +230,11 @@ public override void Initialize()
if (!isOutletNode)
continue;

outlets.Add((plumbingNode, outlet));
var solutions = GetOutletSolutionEntities(plumbingNode.Owner, plumbingNode.Name, outlet);
foreach (var sol in solutions)
{
outlets.Add((plumbingNode, outlet, sol));
}
}

if (outlets.Count == 0)
Expand All @@ -169,7 +246,7 @@ public override void Initialize()
for (var i = 0; i < outlets.Count && remaining > 0; i++)
{
var index = (startIndex + i) % outlets.Count;
var (plumbingNode, outlet) = outlets[index];
var (plumbingNode, outlet, sourceSol) = outlets[index];

var pulled = PullFromOutletSplit(
puller,
Expand All @@ -180,7 +257,8 @@ public override void Initialize()
passthroughDestination,
remaining,
filterEnabled,
filteredReagents);
filteredReagents,
sourceSol);

totalPulled += pulled;
remaining -= pulled;
Expand Down Expand Up @@ -253,35 +331,36 @@ public Dictionary<string, FixedPoint2> PullSpecificReagents(
if (!isOutletNode)
continue;

if (GetOutletSolution(plumbingNode.Owner, plumbingNode.Name, outlet) is not { } sourceSoln)
continue;

var available = sourceSoln.Comp.Solution.GetReagentQuantity(new ReagentId(reagentId, null));
if (available <= 0)
continue;
var solutions = GetOutletSolutionEntities(plumbingNode.Owner, plumbingNode.Name, outlet);
foreach (var sourceSoln in solutions)
{
var available = sourceSoln.Comp.Solution.GetReagentQuantity(new ReagentId(reagentId, null));
if (available <= 0)
continue;

var attemptEv = new PlumbingPullAttemptEvent(puller, plumbingNode.Name, reagentId);
RaiseLocalEvent(plumbingNode.Owner, ref attemptEv);
var attemptEv = new PlumbingPullAttemptEvent(puller, plumbingNode.Name, reagentId);
RaiseLocalEvent(plumbingNode.Owner, ref attemptEv);

if (attemptEv.Cancelled)
continue;
if (attemptEv.Cancelled)
continue;

var toPull = FixedPoint2.Min(available, stillNeeded);
toPull = FixedPoint2.Min(toPull, remaining);
var toPull = FixedPoint2.Min(available, stillNeeded);
toPull = FixedPoint2.Min(toPull, remaining);

var actualPulled = _solutionSystem.RemoveReagentAndReturn(sourceSoln, new ReagentId(reagentId, null), toPull);
if (actualPulled > 0)
{
_solutionSystem.TryAddReagent(destination, new ReagentId(reagentId, null), actualPulled, out var actuallyAdded);
var actualPulled = _solutionSystem.RemoveReagentAndReturn(sourceSoln, new ReagentId(reagentId, null), toPull);
if (actualPulled > 0)
{
_solutionSystem.TryAddReagent(destination, new ReagentId(reagentId, null), actualPulled, out var actuallyAdded);

// Return any excess to source to prevent loss
var excess = actualPulled - actuallyAdded;
if (excess > 0)
_solutionSystem.TryAddReagent(sourceSoln, new ReagentId(reagentId, null), excess, out _);
// Return any excess to source to prevent loss
var excess = actualPulled - actuallyAdded;
if (excess > 0)
_solutionSystem.TryAddReagent(sourceSoln, new ReagentId(reagentId, null), excess, out _);

pulled[reagentId] = pulled.GetValueOrDefault(reagentId, FixedPoint2.Zero) + actuallyAdded;
stillNeeded -= actuallyAdded;
remaining -= actuallyAdded;
pulled[reagentId] = pulled.GetValueOrDefault(reagentId, FixedPoint2.Zero) + actuallyAdded;
stillNeeded -= actuallyAdded;
remaining -= actuallyAdded;
}
}
}
}
Expand All @@ -298,12 +377,15 @@ private FixedPoint2 PullFromOutlet(
string nodeName,
PlumbingOutletComponent outlet,
Entity<SolutionComponent> destination,
FixedPoint2 maxAmount)
FixedPoint2 maxAmount,
Entity<SolutionComponent>? sourceSolOverride = null)
{
if (GetOutletSolution(sourceOwner, nodeName, outlet) is not { } sourceSoln)
Entity<SolutionComponent>? sourceSoln = sourceSolOverride ?? GetOutletSolution(sourceOwner, nodeName, outlet);
if (sourceSoln is null)
return FixedPoint2.Zero;

var sourceSolution = sourceSoln.Comp.Solution;
var sourceSolNonNull = sourceSoln.Value;
var sourceSolution = sourceSolNonNull.Comp.Solution;
if (sourceSolution.Volume <= 0)
return FixedPoint2.Zero;

Expand Down Expand Up @@ -351,15 +433,15 @@ private FixedPoint2 PullFromOutlet(
if (toPull <= 0)
continue;

var pulled = _solutionSystem.RemoveReagentAndReturn(sourceSoln, reagent, toPull);
var pulled = _solutionSystem.RemoveReagentAndReturn(sourceSolNonNull, reagent, toPull);
if (pulled > 0)
{
_solutionSystem.TryAddReagent(destination, reagent, pulled, out var actuallyAdded);

// Return any excess to source to prevent loss
var excess = pulled - actuallyAdded;
if (excess > 0)
_solutionSystem.TryAddReagent(sourceSoln, reagent, excess, out _);
_solutionSystem.TryAddReagent(sourceSolNonNull, reagent, excess, out _);

totalPulled += actuallyAdded;
remaining -= actuallyAdded;
Expand All @@ -381,12 +463,15 @@ private FixedPoint2 PullFromOutletSplit(
Entity<SolutionComponent> passthroughDestination,
FixedPoint2 maxAmount,
bool filterEnabled,
HashSet<ProtoId<ReagentPrototype>> filteredReagents)
HashSet<ProtoId<ReagentPrototype>> filteredReagents,
Entity<SolutionComponent>? sourceSolOverride = null)
{
if (GetOutletSolution(sourceOwner, nodeName, outlet) is not { } sourceSoln)
Entity<SolutionComponent>? sourceSoln = sourceSolOverride ?? GetOutletSolution(sourceOwner, nodeName, outlet);
if (sourceSoln is null)
return FixedPoint2.Zero;

var sourceSolution = sourceSoln.Comp.Solution;
var sourceSolNonNull = sourceSoln.Value;
var sourceSolution = sourceSolNonNull.Comp.Solution;
if (sourceSolution.Volume <= 0)
return FixedPoint2.Zero;

Expand Down Expand Up @@ -435,15 +520,15 @@ private FixedPoint2 PullFromOutletSplit(
if (toPull <= 0)
continue;

var pulled = _solutionSystem.RemoveReagentAndReturn(sourceSoln, reagent, toPull);
var pulled = _solutionSystem.RemoveReagentAndReturn(sourceSolNonNull, reagent, toPull);
if (pulled <= 0)
continue;

_solutionSystem.TryAddReagent(destination, reagent, pulled, out var actuallyAdded);

var excess = pulled - actuallyAdded;
if (excess > 0)
_solutionSystem.TryAddReagent(sourceSoln, reagent, excess, out _);
_solutionSystem.TryAddReagent(sourceSolNonNull, reagent, excess, out _);

totalPulled += actuallyAdded;
remaining -= actuallyAdded;
Expand Down
Loading