From 6aa401c0e689f8728f009f71c588b38d9fba66a8 Mon Sep 17 00:00:00 2001 From: BombasterDS Date: Fri, 12 Jun 2026 23:26:56 +1000 Subject: [PATCH 1/3] Strandard gas reactions, GasReactionChamberSystem --- .../EntitySystems/AtmosphereSystem.Gases.cs | 3 +- .../Atmos/Reactions/GasReactionPrototype.cs | 7 ++++ .../Atmos/Components/GasDevourerComponent.cs | 14 +++++++ .../Components/GasReactionChamberComponent.cs | 17 ++++++++ .../Atmos/Systems/GasDevourerSystem.cs | 41 +++++++++++++++++++ .../Atmos/Systems/GasReactionChamberSystem.cs | 0 6 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 Modules/GoobStation/Content.Goobstation.Common/Atmos/Components/GasDevourerComponent.cs create mode 100644 Modules/GoobStation/Content.Goobstation.Server/Atmos/Components/GasReactionChamberComponent.cs create mode 100644 Modules/GoobStation/Content.Goobstation.Server/Atmos/Systems/GasDevourerSystem.cs create mode 100644 Modules/GoobStation/Content.Goobstation.Server/Atmos/Systems/GasReactionChamberSystem.cs diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs index d3c51086ca1..20bf3907c29 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs @@ -284,7 +284,8 @@ public override ReactionResult React(GasMixture mixture, IGasMixtureHolder? hold var temperature = mixture.Temperature; var energy = GetThermalEnergy(mixture); - foreach (var prototype in GasReactions) + // Goobstation - Added check for standard reactions + foreach (var prototype in GasReactions.Where(x => x.IsStandardReaction)) { if (energy < prototype.MinimumEnergyRequirement || temperature < prototype.MinimumTemperatureRequirement || diff --git a/Content.Server/Atmos/Reactions/GasReactionPrototype.cs b/Content.Server/Atmos/Reactions/GasReactionPrototype.cs index 9e00d38eded..729ace7b129 100644 --- a/Content.Server/Atmos/Reactions/GasReactionPrototype.cs +++ b/Content.Server/Atmos/Reactions/GasReactionPrototype.cs @@ -43,6 +43,13 @@ public sealed partial class GasReactionPrototype : IPrototype [DataField("priority")] public int Priority { get; private set; } = int.MinValue; + /// + /// Goobstation + /// True if it's standard reaction that happens in atmosphere and not require conditions to try React. + /// + [DataField] + public bool IsStandardReaction { get; private set; } = true; + /// /// A list of effects this will produce. /// diff --git a/Modules/GoobStation/Content.Goobstation.Common/Atmos/Components/GasDevourerComponent.cs b/Modules/GoobStation/Content.Goobstation.Common/Atmos/Components/GasDevourerComponent.cs new file mode 100644 index 00000000000..1df91b4519c --- /dev/null +++ b/Modules/GoobStation/Content.Goobstation.Common/Atmos/Components/GasDevourerComponent.cs @@ -0,0 +1,14 @@ +namespace Content.Goobstation.Common.Atmos; + +/// +/// Allows entity to devour nearby gases and put them inside GasMixtureHolderComponent +/// +[RegisterComponent] +public sealed partial class GasDevourerComponent : Component +{ + /// + /// Devouring speed in L/s + /// + [DataField] + public float TransferRate = 100; +} diff --git a/Modules/GoobStation/Content.Goobstation.Server/Atmos/Components/GasReactionChamberComponent.cs b/Modules/GoobStation/Content.Goobstation.Server/Atmos/Components/GasReactionChamberComponent.cs new file mode 100644 index 00000000000..8b55aac369b --- /dev/null +++ b/Modules/GoobStation/Content.Goobstation.Server/Atmos/Components/GasReactionChamberComponent.cs @@ -0,0 +1,17 @@ +using Content.Server.Atmos.Reactions; +using Robust.Shared.Prototypes; + +namespace Content.Goobstation.Server.Atmos; + +/// +/// Gas chamber that allows only specific gas reactions inside it +/// +[RegisterComponent] +public sealed partial class GasReactionChamberComponent : Component +{ + /// + /// List of allowed reactions. Reactions are not allowed if empty. + /// + [DataField] + public HashSet> Reactions = new(); +} diff --git a/Modules/GoobStation/Content.Goobstation.Server/Atmos/Systems/GasDevourerSystem.cs b/Modules/GoobStation/Content.Goobstation.Server/Atmos/Systems/GasDevourerSystem.cs new file mode 100644 index 00000000000..a92ab89315c --- /dev/null +++ b/Modules/GoobStation/Content.Goobstation.Server/Atmos/Systems/GasDevourerSystem.cs @@ -0,0 +1,41 @@ +using Content.Goobstation.Common.Atmos; +using Content.Server.Atmos.Components; +using Content.Server.Atmos.EntitySystems; +using Content.Server.Atmos.Piping.Unary.EntitySystems; +using Content.Shared.Atmos.Components; +using Content.Shared.Atmos.Piping.Unary.Components; +using Robust.Server.GameObjects; + +namespace Content.Goobstation.Server.Atmos; + +public sealed partial class GasDevourerSystem : EntitySystem +{ + [Dependency] private EntityQuery _gasHolderQuery = default!; + + [Dependency] private AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private GasVentScrubberSystem _scrubberSystem = default!; + [Dependency] private TransformSystem _transformSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAtmosUpdated); + } + + private void OnAtmosUpdated(Entity devourer, ref AtmosDeviceUpdateEvent args) + { + // Return if entity cannot hold gas or we are not on grid + if (!_gasHolderQuery.TryComp(devourer, out var gasHolder) || args.Grid is not { } grid) + return; + + var position = _transformSystem.GetGridTilePositionOrDefault(devourer.Owner); + var gasEnumerator = _atmosphereSystem.GetAdjacentTileMixtures(grid, position, false, true); + + while (gasEnumerator.MoveNext(out var tileMixture)) + { + _scrubberSystem.Scrub(args.dt, devourer.Comp.TransferRate * _atmosphereSystem.PumpSpeedup(), + ScrubberPumpDirection.Siphoning, [], tileMixture, gasHolder.Air); + } + } +} diff --git a/Modules/GoobStation/Content.Goobstation.Server/Atmos/Systems/GasReactionChamberSystem.cs b/Modules/GoobStation/Content.Goobstation.Server/Atmos/Systems/GasReactionChamberSystem.cs new file mode 100644 index 00000000000..e69de29bb2d From 13e672930ed1f6251c69e19a9c6015a963619fa0 Mon Sep 17 00:00:00 2001 From: BombasterDS Date: Fri, 12 Jun 2026 23:29:32 +1000 Subject: [PATCH 2/3] Standard Gas Reaction and Gas Reaction system logic --- .../EntitySystems/AtmosphereSystem.Gases.cs | 14 ++++++- .../Atmos/Reactions/GasReactionPrototype.cs | 2 +- .../Components/GasReactionChamberComponent.cs | 5 ++- .../Atmos/Systems/GasReactionChamberSystem.cs | 42 +++++++++++++++++++ 4 files changed, 58 insertions(+), 5 deletions(-) diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs index 20bf3907c29..c61833fcc0a 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs @@ -1,3 +1,4 @@ +using System.Collections; using System.Linq; using System.Runtime.CompilerServices; using Content.Server.Atmos.Reactions; @@ -278,14 +279,23 @@ public GasCompareResult CompareExchange(GasMixture sample, GasMixture otherSampl } [PublicAPI] + // Goobstation - Added override method without gasesToReact parameter to ensure inherence from shared system public override ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder) + { + // Goobstation - Sends list of standard reactions to react + var standardReactions = GasReactions.Where(x => x.IsStandardReaction); + return React(mixture, holder, standardReactions); + } + + // Goobstation - Added gasesToReact parameter to add specific set of reactions to react + public ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder, IEnumerable gasesToReact) { var reaction = ReactionResult.NoReaction; var temperature = mixture.Temperature; var energy = GetThermalEnergy(mixture); // Goobstation - Added check for standard reactions - foreach (var prototype in GasReactions.Where(x => x.IsStandardReaction)) + foreach (var prototype in gasesToReact) { if (energy < prototype.MinimumEnergyRequirement || temperature < prototype.MinimumTemperatureRequirement || @@ -308,7 +318,7 @@ public override ReactionResult React(GasMixture mixture, IGasMixtureHolder? hold continue; reaction = prototype.React(mixture, holder, this, HeatScale); - if(reaction.HasFlag(ReactionResult.StopReactions)) + if (reaction.HasFlag(ReactionResult.StopReactions)) break; } diff --git a/Content.Server/Atmos/Reactions/GasReactionPrototype.cs b/Content.Server/Atmos/Reactions/GasReactionPrototype.cs index 729ace7b129..f495b72c219 100644 --- a/Content.Server/Atmos/Reactions/GasReactionPrototype.cs +++ b/Content.Server/Atmos/Reactions/GasReactionPrototype.cs @@ -44,7 +44,7 @@ public sealed partial class GasReactionPrototype : IPrototype public int Priority { get; private set; } = int.MinValue; /// - /// Goobstation + /// Goobstation: /// True if it's standard reaction that happens in atmosphere and not require conditions to try React. /// [DataField] diff --git a/Modules/GoobStation/Content.Goobstation.Server/Atmos/Components/GasReactionChamberComponent.cs b/Modules/GoobStation/Content.Goobstation.Server/Atmos/Components/GasReactionChamberComponent.cs index 8b55aac369b..984390399c8 100644 --- a/Modules/GoobStation/Content.Goobstation.Server/Atmos/Components/GasReactionChamberComponent.cs +++ b/Modules/GoobStation/Content.Goobstation.Server/Atmos/Components/GasReactionChamberComponent.cs @@ -1,5 +1,6 @@ using Content.Server.Atmos.Reactions; using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; namespace Content.Goobstation.Server.Atmos; @@ -12,6 +13,6 @@ public sealed partial class GasReactionChamberComponent : Component /// /// List of allowed reactions. Reactions are not allowed if empty. /// - [DataField] - public HashSet> Reactions = new(); + [DataField(customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + public HashSet? Reactions = null; } diff --git a/Modules/GoobStation/Content.Goobstation.Server/Atmos/Systems/GasReactionChamberSystem.cs b/Modules/GoobStation/Content.Goobstation.Server/Atmos/Systems/GasReactionChamberSystem.cs index e69de29bb2d..c3591cae4bc 100644 --- a/Modules/GoobStation/Content.Goobstation.Server/Atmos/Systems/GasReactionChamberSystem.cs +++ b/Modules/GoobStation/Content.Goobstation.Server/Atmos/Systems/GasReactionChamberSystem.cs @@ -0,0 +1,42 @@ +using System.Linq; +using Content.Server.Atmos.Components; +using Content.Server.Atmos.EntitySystems; +using Content.Server.Atmos.Reactions; +using Content.Shared.Atmos.Components; +using Robust.Shared.Prototypes; + +namespace Content.Goobstation.Server.Atmos; + +public sealed partial class GasReactionChamberSystem : EntitySystem +{ + [Dependency] private AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private IPrototypeManager _prototypeMan = default!; + [Dependency] private EntityQuery _gasHolderQuery = default!; + + private GasReactionPrototype[] _gasReactions = []; + + /// + /// List of gas reactions ordered by priority. + /// + public IEnumerable GasReactions => _gasReactions; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAtmosUpdate); + + _gasReactions = _prototypeMan.EnumeratePrototypes().ToArray(); + Array.Sort(_gasReactions, (a, b) => b.Priority.CompareTo(a.Priority)); + } + + private void OnAtmosUpdate(Entity chamber, ref AtmosDeviceUpdateEvent args) + { + if (chamber.Comp.Reactions is null || !_gasHolderQuery.TryComp(chamber, out var gasHolderComp)) + return; + + var reactGases = GasReactions.Where(x => chamber.Comp.Reactions.Contains(x.ID)); + + _atmosphereSystem.React(gasHolderComp.Air, gasHolderComp, reactGases); + } +} \ No newline at end of file From 15b66a7bb92f98439a69d3b247ef2f424efe58b8 Mon Sep 17 00:00:00 2001 From: BombasterDS Date: Fri, 12 Jun 2026 22:09:24 +1000 Subject: [PATCH 3/3] GasEntitySystem --- .../Atmos/Systems/GasEntitySystem.cs | 64 +++++++++++++++++++ .../_Goobtstation/Entities/Atmos/gases.yml | 50 +++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 Modules/GoobStation/Content.Goobstation.Server/Atmos/Systems/GasEntitySystem.cs create mode 100644 Modules/GoobStation/Resources/Prototypes/_Goobtstation/Entities/Atmos/gases.yml diff --git a/Modules/GoobStation/Content.Goobstation.Server/Atmos/Systems/GasEntitySystem.cs b/Modules/GoobStation/Content.Goobstation.Server/Atmos/Systems/GasEntitySystem.cs new file mode 100644 index 00000000000..2aa16783209 --- /dev/null +++ b/Modules/GoobStation/Content.Goobstation.Server/Atmos/Systems/GasEntitySystem.cs @@ -0,0 +1,64 @@ +using System.Collections.Frozen; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Content.Server.GameTicking; +using Content.Shared.Atmos.Prototypes; +using Robust.Shared.Prototypes; + +/// +/// System that creates and store entities of specific gases just to allow gases work with components +/// +public sealed partial class GasEntitySystem : EntitySystem +{ + [Dependency] private IPrototypeManager _protoMan = default!; + private FrozenDictionary, EntityUid> _gasesDict = default!; + + public override void Initialize() + { + SubscribeLocalEvent(AfterMapLoad); + SubscribeLocalEvent(OnPrototypesReload); + } + + private void AfterMapLoad(PostGameMapLoad args) + { + if (_gasesDict == null || _gasesDict.Count == 0) + FillGasesDictionary(); + } + + private void OnPrototypesReload(PrototypesReloadedEventArgs args) + { + FillGasesDictionary(); + } + + private void FillGasesDictionary() + { + var gases = _protoMan.EnumeratePrototypes(); + Dictionary, EntityUid> gasesDict = []; + + foreach (var gas in gases) + { + if (!_protoMan.TryIndex(gas.ID, out var gasEntProto)) + { + Log.Error($"GasPrototype «{gas.ID}» does not have correlation entity prototype with same ID."); + continue; + } + + // Spawn gas entity with same gas ID and put it in dictionary + var gasEnt = Spawn(gasEntProto.ID); + gasesDict.Add(gas.ID, gasEnt); + } + + _gasesDict = gasesDict.ToFrozenDictionary(); + } + + public bool TryGetGasEntity(string gasId, [NotNullWhen(true)] out EntityUid? gasEnt) + { + if (_gasesDict.TryGetValue(gasId, out var outValue)) + { + gasEnt = outValue; + return true; + } + gasEnt = null; + return false; + } +} \ No newline at end of file diff --git a/Modules/GoobStation/Resources/Prototypes/_Goobtstation/Entities/Atmos/gases.yml b/Modules/GoobStation/Resources/Prototypes/_Goobtstation/Entities/Atmos/gases.yml new file mode 100644 index 00000000000..7c80d8d9b3b --- /dev/null +++ b/Modules/GoobStation/Resources/Prototypes/_Goobtstation/Entities/Atmos/gases.yml @@ -0,0 +1,50 @@ +#Contains the entity objects of gases +- type: entity + id: BaseGas + name: gas + description: Just a gas! + +- type: entity + parent: BaseGas + id: Oxygen + name: oxygen gas + +- type: entity + parent: BaseGas + id: Nitrogen + name: nitrogen gas + +- type: entity + parent: BaseGas + id: Frezon + name: frezon gas + +- type: entity + parent: BaseGas + id: NitrousOxide + name: nitrous oxide gas + +- type: entity + parent: BaseGas + id: Plasma + name: plasma gas + +- type: entity + parent: BaseGas + id: Ammonia + name: ammonia gas + +- type: entity + parent: BaseGas + id: Tritium + name: tritium gas + +- type: entity + parent: BaseGas + id: CarbonDioxide + name: carbon dioxide gas + +- type: entity + parent: BaseGas + id: WaterVapor + name: water vapor gas \ No newline at end of file