Fixed containment fields dying even when one side still had power (#41006)

* Containment fields only die if both sides lose power

Currently, if either side of a containment field loses power,
the field dies, even if the other side is still powered. This commit
changes the behavior to only destroy the field if neither side
of the field has enough power.

* discard

---------

Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>
This commit is contained in:
Matt Idzik 2026-02-07 06:39:07 -06:00 committed by Coryler
parent 73c9c02539
commit 60ce323e5e
1 changed files with 49 additions and 16 deletions

View File

@ -12,6 +12,7 @@ using Robust.Server.GameObjects;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Events;
using Robust.Shared.Utility;
namespace Content.Server.Singularity.EntitySystems;
@ -152,32 +153,62 @@ public sealed class ContainmentFieldGeneratorSystem : EntitySystem
/// <summary>
/// Deletes the fields and removes the respective connections for the generators.
/// If a predicate is provided, only remove fields and connections if the predicate returns true.
/// </summary>
private void RemoveConnections(Entity<ContainmentFieldGeneratorComponent> generator)
/// <param name="generator">The field generator component</param>
/// <param name="removePredicate">An optional predicate that takes in this generator entity and the other generator entity.
/// It should return true if the connection should be removed, and false otherwise.
/// If a predicate isn't provided, all connections will be removed.</param>
private void RemoveConnections(
Entity<ContainmentFieldGeneratorComponent> generator,
Func<Entity<ContainmentFieldGeneratorComponent>, Entity<ContainmentFieldGeneratorComponent>, bool>? removePredicate = null)
{
var (uid, component) = generator;
foreach (var (direction, value) in component.Connections)
var anyFieldsRemoved = false;
foreach (var (direction, (otherGen, fields)) in component.Connections)
{
foreach (var field in value.Item2)
if (removePredicate is not null && !removePredicate(generator, otherGen))
{
// Do not delete a connection only if the provided predicate says not to.
continue;
}
anyFieldsRemoved = true;
foreach (var field in fields)
{
QueueDel(field);
}
value.Item1.Comp.Connections.Remove(direction.GetOpposite());
if (value.Item1.Comp.Connections.Count == 0) //Change isconnected only if there's no more connections
component.Connections.Remove(direction);
otherGen.Comp.Connections.Remove(direction.GetOpposite());
if (otherGen.Comp.Connections.Count == 0) //Change isconnected only if there's no more connections
{
value.Item1.Comp.IsConnected = false;
ChangeOnLightVisualizer(value.Item1);
otherGen.Comp.IsConnected = false;
ChangeOnLightVisualizer(otherGen);
}
ChangeFieldVisualizer(value.Item1);
ChangeFieldVisualizer(otherGen);
}
if (!anyFieldsRemoved)
{
// No fields were removed, so no logging or other updates are necessary.
return;
}
_popupSystem.PopupEntity(Loc.GetString("comp-containment-disconnected"), uid, PopupType.LargeCaution);
if (component.Connections.Count == 0)
{
component.IsConnected = false;
ChangeOnLightVisualizer(generator);
}
component.Connections.Clear();
if (component.IsConnected)
_popupSystem.PopupEntity(Loc.GetString("comp-containment-disconnected"), uid, PopupType.LargeCaution);
component.IsConnected = false;
ChangeOnLightVisualizer(generator);
ChangeFieldVisualizer(generator);
_adminLogger.Add(LogType.FieldGeneration, LogImpact.Medium, $"{ToPrettyString(uid)} lost field connections"); // Ideally LogImpact would depend on if there is a singulo nearby
}
@ -199,7 +230,7 @@ public sealed class ContainmentFieldGeneratorSystem : EntitySystem
if (component.PowerBuffer >= component.PowerMinimum)
{
var directions = Enum.GetValues<Direction>().Length;
for (int i = 0; i < directions-1; i+=2)
for (int i = 0; i < directions - 1; i += 2)
{
var dir = (Direction)i;
@ -220,7 +251,9 @@ public sealed class ContainmentFieldGeneratorSystem : EntitySystem
if (component.PowerBuffer < component.PowerMinimum && component.Connections.Count != 0)
{
RemoveConnections(generator);
// Only remove connections if the generators on BOTH sides of the field don't have enough power.
// Since we only run this code if we know this gen doesn't have enough power, we only have to check the other gen.
RemoveConnections(generator, (_, otherGen) => otherGen.Comp.PowerBuffer < otherGen.Comp.PowerMinimum);
}
ChangePowerVisualizer(power, generator);
@ -393,7 +426,7 @@ public sealed class ContainmentFieldGeneratorSystem : EntitySystem
{
_visualizer.SetData(generator, ContainmentFieldGeneratorVisuals.FieldLight, generator.Comp.Connections.Count switch
{
>1 => FieldLevelVisuals.MultipleFields,
> 1 => FieldLevelVisuals.MultipleFields,
1 => FieldLevelVisuals.OneField,
_ => generator.Comp.Enabled ? FieldLevelVisuals.On : FieldLevelVisuals.NoLevel
});