From 60ce8dc2a9f3dc8b9e2dfe9734dec9606ab35afc Mon Sep 17 00:00:00 2001 From: Lily Autumn Date: Sun, 24 May 2026 05:06:23 +1200 Subject: [PATCH 1/4] Chemistan Rising... --- .../UI/ChemMasterBoundUserInterface.cs | 1 + .../Chemistry/UI/ChemMasterWindow.xaml | 5 +- .../Chemistry/UI/ChemMasterWindow.xaml.cs | 11 + .../UI/ReagentDispenserBoundUserInterface.cs | 1 + .../Chemistry/UI/ReagentDispenserWindow.xaml | 6 + .../UI/ReagentDispenserWindow.xaml.cs | 7 + .../Medical/Cryogenics/BeakerBarChart.cs | 285 +++++++ Content.Client/Medical/Cryogenics/Ruler.cs | 36 + .../RCD/RCDMenuBoundUserInterface.cs | 2 + .../Controls/LabelledSplitBar.cs | 75 ++ .../PlumbingConnectorAppearanceSystem.cs | 184 +++++ .../UI/PlumbingFilterBoundUserInterface.cs | 57 ++ .../UI/PlumbingPillPressBoundUserInterface.cs | 50 ++ .../Plumbing/UI/PlumbingPillPressWindow.xaml | 61 ++ .../UI/PlumbingPillPressWindow.xaml.cs | 202 +++++ .../UI/PlumbingReactorBoundUserInterface.cs | 64 ++ .../Plumbing/UI/PlumbingReactorWindow.xaml | 39 + .../Plumbing/UI/PlumbingReactorWindow.xaml.cs | 186 +++++ .../PlumbingSmartFridgeBoundUserInterface.cs | 31 + .../UI/PlumbingSmartFridgeWindow.xaml | 23 + .../UI/PlumbingSmartFridgeWindow.xaml.cs | 60 ++ .../PlumbingSynthesizerBoundUserInterface.cs | 45 ++ .../UI/PlumbingSynthesizerWindow.xaml | 22 + .../UI/PlumbingSynthesizerWindow.xaml.cs | 82 ++ .../UI/StarlightPlumbingFilterWindow.xaml | 29 + .../UI/StarlightPlumbingFilterWindow.xaml.cs | 143 ++++ .../EntitySystems/ChemMasterSystem.cs | 19 +- .../EntitySystems/ReagentDispenserSystem.cs | 27 +- .../Power/EntitySystems/BatterySystem.cs | 1 + .../Components/PlumbingDeviceComponent.cs | 50 ++ .../PlumbingConnectorAppearanceSystem.cs | 194 +++++ .../EntitySystems/PlumbingDeviceSystem.cs | 136 ++++ .../EntitySystems/PlumbingFilterSystem.cs | 134 +++ .../EntitySystems/PlumbingInletSystem.cs | 64 ++ .../EntitySystems/PlumbingInputSystem.cs | 59 ++ .../EntitySystems/PlumbingOutputSystem.cs | 60 ++ .../EntitySystems/PlumbingPillPressSystem.cs | 307 +++++++ .../EntitySystems/PlumbingPullSystem.cs | 317 ++++++++ .../EntitySystems/PlumbingReactorSystem.cs | 336 ++++++++ .../PlumbingSmartFridgeSystem.cs | 366 +++++++++ .../PlumbingSynthesizerSystem.cs | 216 +++++ .../EntitySystems/PlumbingTankSystem.cs | 47 ++ .../Plumbing/NodeGroups/PlumbingNet.cs | 18 + .../_Starlight/Plumbing/Nodes/PlumbingNode.cs | 20 + .../Plumbing/PlumbingPullAttemptEvent.cs | 41 + .../Plumbing/PlumbingPullIntoAttemptEvent.cs | 48 ++ .../SharedSolutionContainerSystem.Relays.cs | 12 + .../SharedSolutionContainerSystem.cs | 18 + Content.Shared/Chemistry/SharedChemMaster.cs | 11 +- .../Chemistry/SharedReagentDispenser.cs | 15 +- .../NodeContainer/NodeGroups/NodeGroupID.cs | 5 + Content.Shared/RCD/Components/RCDComponent.cs | 7 + .../Components/RCDDeconstructibleComponent.cs | 10 +- Content.Shared/RCD/Systems/RCDSystem.cs | 2 +- .../PlumbingConnectorAppearanceComponent.cs | 36 + .../Components/PlumbingInletComponent.cs | 40 + .../Components/PlumbingInputComponent.cs | 16 + .../Components/PlumbingOutletComponent.cs | 40 + .../Components/PlumbingOutputComponent.cs | 15 + .../Components/PlumbingPillPressComponent.cs | 94 +++ .../Components/PlumbingReactorComponent.cs | 67 ++ .../PlumbingSmartFridgeComponent.cs | 24 + .../PlumbingSynthesizerComponent.cs | 46 ++ .../Components/PlumbingTankComponent.cs | 42 + .../Components/PlungeableComponent.cs | 17 + .../StarlightPlumbingFilterComponent.cs | 48 ++ .../_Starlight/Plumbing/PlumbingVisuals.cs | 55 ++ .../Plumbing/SharedPlumbingFilter.cs | 84 ++ .../Plumbing/SharedPlumbingPillPress.cs | 163 ++++ .../Plumbing/SharedPlumbingReactor.cs | 131 +++ .../Plumbing/SharedPlumbingSmartFridge.cs | 47 ++ .../Plumbing/SharedPlumbingSynthesizer.cs | 91 +++ .../en-US/_Starlight/plumbing/pill-press.ftl | 14 + .../en-US/_Starlight/plumbing/plumbing.ftl | 56 ++ .../_Starlight/plumbing/smart-fridge.ftl | 10 + .../en-US/_Starlight/rpld/rpld-components.ftl | 2 + .../components/chem-master-component.ftl | 2 + .../reagent-dispenser-component.ftl | 2 + .../Dispensers/base_structuredispensers.yml | 18 + .../Structures/Machines/chem_master.yml | 27 +- .../Structures/Machines/reagent_grinder.yml | 15 + .../Prototypes/Guidebook/engineering.yml | 1 + .../Entities/Guidebook/plumbing_ducts.yml | 55 ++ .../Entities/Objects/Tools/tools.yml | 64 +- .../Structures/Piping/Plumbing/ducts.yml | 189 +++++ .../Structures/Piping/Plumbing/machines.yml | 760 ++++++++++++++++++ .../_StarLight/Guidebook/engineering.yml | 18 + Resources/Prototypes/_StarLight/RPLD/rpld.yml | 176 ++++ Resources/Prototypes/_StarLight/tags.yml | 3 + .../Guidebook/Engineering/Plumbing.xml | 41 + .../Guidebook/Engineering/PlumbingFlow.xml | 41 + .../Engineering/PlumbingMachines.xml | 108 +++ .../ServerInfo/Guidebook/Medical/Chemist.xml | 2 + .../Interface/Radial/RPLD/category_ducts.png | Bin 0 -> 361 bytes .../Radial/RPLD/category_machines.png | Bin 0 -> 599 bytes .../Interface/Radial/RPLD/disposal.png | Bin 0 -> 389 bytes .../Interface/Radial/RPLD/drain.png | Bin 0 -> 874 bytes .../Interface/Radial/RPLD/duct_bend.png | Bin 0 -> 273 bytes .../Interface/Radial/RPLD/duct_fourway.png | Bin 0 -> 361 bytes .../Interface/Radial/RPLD/duct_straight.png | Bin 0 -> 230 bytes .../Interface/Radial/RPLD/duct_tjunction.png | Bin 0 -> 303 bytes .../Interface/Radial/RPLD/filter.png | Bin 0 -> 381 bytes .../Interface/Radial/RPLD/pill_press.png | Bin 0 -> 507 bytes .../Interface/Radial/RPLD/pipe_input.png | Bin 0 -> 416 bytes .../Interface/Radial/RPLD/pipe_output.png | Bin 0 -> 414 bytes .../Radial/RPLD/reaction_chamber.png | Bin 0 -> 593 bytes .../_Starlight/Interface/Radial/RPLD/sink.png | Bin 0 -> 701 bytes .../Interface/Radial/RPLD/smart_fridge.png | Bin 0 -> 1250 bytes .../Interface/Radial/RPLD/synthesizer.png | Bin 0 -> 734 bytes .../_Starlight/Interface/Radial/RPLD/tank.png | Bin 0 -> 599 bytes .../Interface/Radial/RPLD/tap_output.png | Bin 0 -> 441 bytes .../Objects/Tools/rpld.rsi/icon.png | Bin 0 -> 711 bytes .../Objects/Tools/rpld.rsi/inhand-left.png | Bin 0 -> 869 bytes .../Objects/Tools/rpld.rsi/inhand-right.png | Bin 0 -> 865 bytes .../Objects/Tools/rpld.rsi/meta.json | 22 + .../Plumbing/fluid_ducts.rsi/ductBend.png | Bin 0 -> 577 bytes .../fluid_ducts.rsi/ductConnector.png | Bin 0 -> 386 bytes .../Plumbing/fluid_ducts.rsi/ductFourway.png | Bin 0 -> 597 bytes .../Plumbing/fluid_ducts.rsi/ductStraight.png | Bin 0 -> 386 bytes .../fluid_ducts.rsi/ductTJunction.png | Bin 0 -> 588 bytes .../Piping/Plumbing/fluid_ducts.rsi/meta.json | 52 ++ .../Plumbing/fluid_ducts.rsi/storageBend.png | Bin 0 -> 300 bytes .../fluid_ducts.rsi/storageFourway.png | Bin 0 -> 368 bytes .../fluid_ducts.rsi/storageStraight.png | Bin 0 -> 239 bytes .../fluid_ducts.rsi/storageTjunction.png | Bin 0 -> 336 bytes .../Piping/Plumbing/plumbers.rsi/bottler.png | Bin 0 -> 1993 bytes .../Piping/Plumbing/plumbers.rsi/disposal.png | Bin 0 -> 374 bytes .../plumbers.rsi/disposal_working.png | Bin 0 -> 1036 bytes .../Plumbing/plumbers.rsi/ductConnector.png | Bin 0 -> 559 bytes .../plumbers.rsi/ductConnector_connected.png | Bin 0 -> 556 bytes .../Piping/Plumbing/plumbers.rsi/filter.png | Bin 0 -> 909 bytes .../Piping/Plumbing/plumbers.rsi/filterF.png | Bin 0 -> 892 bytes .../Piping/Plumbing/plumbers.rsi/meta.json | 114 +++ .../Plumbing/plumbers.rsi/pill_press.png | Bin 0 -> 1074 bytes .../Plumbing/plumbers.rsi/pill_press_off.png | Bin 0 -> 604 bytes .../Plumbing/plumbers.rsi/pipe_input.png | Bin 0 -> 487 bytes .../Plumbing/plumbers.rsi/pipe_output.png | Bin 0 -> 490 bytes .../plumbers.rsi/reaction_chamber.png | Bin 0 -> 713 bytes .../plumbers.rsi/reaction_chamber_on.png | Bin 0 -> 2534 bytes .../Plumbing/plumbers.rsi/synthesizer.png | Bin 0 -> 831 bytes .../plumbers.rsi/synthesizer_inactive.png | Bin 0 -> 935 bytes .../plumbers.rsi/synthesizer_overlay.png | Bin 0 -> 215 bytes .../Piping/Plumbing/plumbers.rsi/tank.png | Bin 0 -> 721 bytes .../Plumbing/plumbers.rsi/tap_output.png | Bin 0 -> 428 bytes 144 files changed, 7082 insertions(+), 13 deletions(-) create mode 100644 Content.Client/Medical/Cryogenics/BeakerBarChart.cs create mode 100644 Content.Client/Medical/Cryogenics/Ruler.cs create mode 100644 Content.Client/UserInterface/Controls/LabelledSplitBar.cs create mode 100644 Content.Client/_Starlight/Plumbing/PlumbingConnectorAppearanceSystem.cs create mode 100644 Content.Client/_Starlight/Plumbing/UI/PlumbingFilterBoundUserInterface.cs create mode 100644 Content.Client/_Starlight/Plumbing/UI/PlumbingPillPressBoundUserInterface.cs create mode 100644 Content.Client/_Starlight/Plumbing/UI/PlumbingPillPressWindow.xaml create mode 100644 Content.Client/_Starlight/Plumbing/UI/PlumbingPillPressWindow.xaml.cs create mode 100644 Content.Client/_Starlight/Plumbing/UI/PlumbingReactorBoundUserInterface.cs create mode 100644 Content.Client/_Starlight/Plumbing/UI/PlumbingReactorWindow.xaml create mode 100644 Content.Client/_Starlight/Plumbing/UI/PlumbingReactorWindow.xaml.cs create mode 100644 Content.Client/_Starlight/Plumbing/UI/PlumbingSmartFridgeBoundUserInterface.cs create mode 100644 Content.Client/_Starlight/Plumbing/UI/PlumbingSmartFridgeWindow.xaml create mode 100644 Content.Client/_Starlight/Plumbing/UI/PlumbingSmartFridgeWindow.xaml.cs create mode 100644 Content.Client/_Starlight/Plumbing/UI/PlumbingSynthesizerBoundUserInterface.cs create mode 100644 Content.Client/_Starlight/Plumbing/UI/PlumbingSynthesizerWindow.xaml create mode 100644 Content.Client/_Starlight/Plumbing/UI/PlumbingSynthesizerWindow.xaml.cs create mode 100644 Content.Client/_Starlight/Plumbing/UI/StarlightPlumbingFilterWindow.xaml create mode 100644 Content.Client/_Starlight/Plumbing/UI/StarlightPlumbingFilterWindow.xaml.cs create mode 100644 Content.Server/_Starlight/Plumbing/Components/PlumbingDeviceComponent.cs create mode 100644 Content.Server/_Starlight/Plumbing/EntitySystems/PlumbingConnectorAppearanceSystem.cs create mode 100644 Content.Server/_Starlight/Plumbing/EntitySystems/PlumbingDeviceSystem.cs create mode 100644 Content.Server/_Starlight/Plumbing/EntitySystems/PlumbingFilterSystem.cs create mode 100644 Content.Server/_Starlight/Plumbing/EntitySystems/PlumbingInletSystem.cs create mode 100644 Content.Server/_Starlight/Plumbing/EntitySystems/PlumbingInputSystem.cs create mode 100644 Content.Server/_Starlight/Plumbing/EntitySystems/PlumbingOutputSystem.cs create mode 100644 Content.Server/_Starlight/Plumbing/EntitySystems/PlumbingPillPressSystem.cs create mode 100644 Content.Server/_Starlight/Plumbing/EntitySystems/PlumbingPullSystem.cs create mode 100644 Content.Server/_Starlight/Plumbing/EntitySystems/PlumbingReactorSystem.cs create mode 100644 Content.Server/_Starlight/Plumbing/EntitySystems/PlumbingSmartFridgeSystem.cs create mode 100644 Content.Server/_Starlight/Plumbing/EntitySystems/PlumbingSynthesizerSystem.cs create mode 100644 Content.Server/_Starlight/Plumbing/EntitySystems/PlumbingTankSystem.cs create mode 100644 Content.Server/_Starlight/Plumbing/NodeGroups/PlumbingNet.cs create mode 100644 Content.Server/_Starlight/Plumbing/Nodes/PlumbingNode.cs create mode 100644 Content.Server/_Starlight/Plumbing/PlumbingPullAttemptEvent.cs create mode 100644 Content.Server/_Starlight/Plumbing/PlumbingPullIntoAttemptEvent.cs create mode 100644 Content.Shared/_Starlight/Plumbing/Components/PlumbingConnectorAppearanceComponent.cs create mode 100644 Content.Shared/_Starlight/Plumbing/Components/PlumbingInletComponent.cs create mode 100644 Content.Shared/_Starlight/Plumbing/Components/PlumbingInputComponent.cs create mode 100644 Content.Shared/_Starlight/Plumbing/Components/PlumbingOutletComponent.cs create mode 100644 Content.Shared/_Starlight/Plumbing/Components/PlumbingOutputComponent.cs create mode 100644 Content.Shared/_Starlight/Plumbing/Components/PlumbingPillPressComponent.cs create mode 100644 Content.Shared/_Starlight/Plumbing/Components/PlumbingReactorComponent.cs create mode 100644 Content.Shared/_Starlight/Plumbing/Components/PlumbingSmartFridgeComponent.cs create mode 100644 Content.Shared/_Starlight/Plumbing/Components/PlumbingSynthesizerComponent.cs create mode 100644 Content.Shared/_Starlight/Plumbing/Components/PlumbingTankComponent.cs create mode 100644 Content.Shared/_Starlight/Plumbing/Components/PlungeableComponent.cs create mode 100644 Content.Shared/_Starlight/Plumbing/Components/StarlightPlumbingFilterComponent.cs create mode 100644 Content.Shared/_Starlight/Plumbing/PlumbingVisuals.cs create mode 100644 Content.Shared/_Starlight/Plumbing/SharedPlumbingFilter.cs create mode 100644 Content.Shared/_Starlight/Plumbing/SharedPlumbingPillPress.cs create mode 100644 Content.Shared/_Starlight/Plumbing/SharedPlumbingReactor.cs create mode 100644 Content.Shared/_Starlight/Plumbing/SharedPlumbingSmartFridge.cs create mode 100644 Content.Shared/_Starlight/Plumbing/SharedPlumbingSynthesizer.cs create mode 100644 Resources/Locale/en-US/_Starlight/plumbing/pill-press.ftl create mode 100644 Resources/Locale/en-US/_Starlight/plumbing/plumbing.ftl create mode 100644 Resources/Locale/en-US/_Starlight/plumbing/smart-fridge.ftl create mode 100644 Resources/Locale/en-US/_Starlight/rpld/rpld-components.ftl create mode 100644 Resources/Prototypes/_StarLight/Entities/Guidebook/plumbing_ducts.yml create mode 100644 Resources/Prototypes/_StarLight/Entities/Structures/Piping/Plumbing/ducts.yml create mode 100644 Resources/Prototypes/_StarLight/Entities/Structures/Piping/Plumbing/machines.yml create mode 100644 Resources/Prototypes/_StarLight/Guidebook/engineering.yml create mode 100644 Resources/Prototypes/_StarLight/RPLD/rpld.yml create mode 100644 Resources/ServerInfo/Guidebook/Engineering/Plumbing.xml create mode 100644 Resources/ServerInfo/Guidebook/Engineering/PlumbingFlow.xml create mode 100644 Resources/ServerInfo/Guidebook/Engineering/PlumbingMachines.xml create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/category_ducts.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/category_machines.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/disposal.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/drain.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/duct_bend.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/duct_fourway.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/duct_straight.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/duct_tjunction.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/filter.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/pill_press.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/pipe_input.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/pipe_output.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/reaction_chamber.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/sink.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/smart_fridge.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/synthesizer.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/tank.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/tap_output.png create mode 100644 Resources/Textures/_Starlight/Objects/Tools/rpld.rsi/icon.png create mode 100644 Resources/Textures/_Starlight/Objects/Tools/rpld.rsi/inhand-left.png create mode 100644 Resources/Textures/_Starlight/Objects/Tools/rpld.rsi/inhand-right.png create mode 100644 Resources/Textures/_Starlight/Objects/Tools/rpld.rsi/meta.json create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/ductBend.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/ductConnector.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/ductFourway.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/ductStraight.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/ductTJunction.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/meta.json create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/storageBend.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/storageFourway.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/storageStraight.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/storageTjunction.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/bottler.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/disposal.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/disposal_working.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/ductConnector.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/ductConnector_connected.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/filter.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/filterF.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/meta.json create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/pill_press.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/pill_press_off.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/pipe_input.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/pipe_output.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/reaction_chamber.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/reaction_chamber_on.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/synthesizer.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/synthesizer_inactive.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/synthesizer_overlay.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/tank.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/tap_output.png diff --git a/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs b/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs index a669a8da4c1..eff37603a6a 100644 --- a/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs +++ b/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs @@ -56,6 +56,7 @@ protected override void Open() } _window.OnReagentButtonPressed += (args, button) => SendMessage(new ChemMasterReagentAmountButtonMessage(button.Id, button.Amount, button.IsBuffer)); + _window.OnToggleValveButtonPressed += () => SendMessage(new ChemMasterToggleValveMessage()); // Starlight-edit: Plumbing valve } /// diff --git a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml index d67906f6b1a..2efa2be25b3 100644 --- a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml +++ b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml @@ -10,7 +10,10 @@ [DataField, ViewVariables(VVAccess.ReadWrite)] public bool Deconstructable = true; + + // Starlight: RPLD + /// + /// Toggles whether this entity is deconstructable by the RPLD (plumbing) or not + /// + [DataField("rpld"), ViewVariables(VVAccess.ReadWrite)] + public bool RpldDeconstructable = false; + // Starlight End: RPLD } diff --git a/Content.Shared/RCD/Systems/RCDSystem.cs b/Content.Shared/RCD/Systems/RCDSystem.cs index 8df22e171c3..0bab6fdaa07 100644 --- a/Content.Shared/RCD/Systems/RCDSystem.cs +++ b/Content.Shared/RCD/Systems/RCDSystem.cs @@ -681,7 +681,7 @@ private bool IsDeconstructionStillValid(EntityUid uid, TileRef tile, EntityUid? else { // The object is not in the whitelist - if (!TryComp(target, out var deconstructible) || !deconstructible.Deconstructable) + if (!TryComp(target, out var deconstructible) || !deconstructible.Deconstructable || TryComp(uid, out var rcd) && !deconstructible.RpldDeconstructable && rcd.IsRPLD) // Starlight: RPLD { if (popMsgs) _popup.PopupClient(Loc.GetString("rcd-component-deconstruct-target-not-on-whitelist-message"), uid, user); diff --git a/Content.Shared/_Starlight/Plumbing/Components/PlumbingConnectorAppearanceComponent.cs b/Content.Shared/_Starlight/Plumbing/Components/PlumbingConnectorAppearanceComponent.cs new file mode 100644 index 00000000000..2d1b0f65eee --- /dev/null +++ b/Content.Shared/_Starlight/Plumbing/Components/PlumbingConnectorAppearanceComponent.cs @@ -0,0 +1,36 @@ +using Robust.Shared.Utility; + +namespace Content.Shared._Starlight.Plumbing.Components; + +/// +/// Adds dynamic connector sprites to plumbing machines. +/// Shows jagged connectors where nodes exist and switches layer to smooth ends version when connected. +/// +[RegisterComponent] +public sealed partial class PlumbingConnectorAppearanceComponent : Component +{ + /// + /// Sprite for disconnected (jagged) connectors. + /// + [DataField] + public SpriteSpecifier.Rsi Disconnected = new(new("_Starlight/Structures/Piping/Plumbing/plumbers.rsi"), "ductConnector"); + + /// + /// Sprite for connected (smooth) connectors - overlays disconnected state. + /// + [DataField] + public SpriteSpecifier.Rsi Connected = new(new("_Starlight/Structures/Piping/Plumbing/plumbers.rsi"), "ductConnector_connected"); + + /// + /// Offset from center for connector sprites. Used so jagged ends stick out from under a machine to be visible under big sprites. + /// + [DataField] + public float Offset; + + /// + /// Node names that should be colored as mixing inlets (green). + /// Configured per-entity in YAML, matching how inlets/outlets are classified by component data. + /// + [DataField] + public HashSet MixingInletNames = new(); +} diff --git a/Content.Shared/_Starlight/Plumbing/Components/PlumbingInletComponent.cs b/Content.Shared/_Starlight/Plumbing/Components/PlumbingInletComponent.cs new file mode 100644 index 00000000000..8ea89f63589 --- /dev/null +++ b/Content.Shared/_Starlight/Plumbing/Components/PlumbingInletComponent.cs @@ -0,0 +1,40 @@ +using Content.Shared.FixedPoint; + +namespace Content.Shared._Starlight.Plumbing.Components; + +/// +/// A plumbing inlet that pulls reagents from the network. +/// Actively pulls reagents from its inlet nodes each update tick into the specified solution. +/// Each inlet node connects to its own plumbing network, preventing network bridging +/// through multi-connector machines. +/// Other machines can pull from this entity via . +/// +[RegisterComponent] +public sealed partial class PlumbingInletComponent : Component +{ + /// + /// The name of the solution on this entity to pull reagents into. + /// + [DataField] + public string SolutionName = "tank"; + + /// + /// The names of the inlet nodes to pull from. + /// Each node should be a single-direction PlumbingNode so that each direction + /// has its own isolated network. + /// + [DataField] + public List InletNames = new() { "inlet" }; + + /// + /// Amount to transfer per update, shared across all inlets. + /// + [DataField] + public FixedPoint2 TransferAmount = FixedPoint2.New(20); + + /// + /// Round-robin indices for fair outlet selection. + /// Tracks which outlet to start from when pulling from multiple sources on each network. + /// + public Dictionary RoundRobinIndices = new(); +} diff --git a/Content.Shared/_Starlight/Plumbing/Components/PlumbingInputComponent.cs b/Content.Shared/_Starlight/Plumbing/Components/PlumbingInputComponent.cs new file mode 100644 index 00000000000..6a78ab9ddcd --- /dev/null +++ b/Content.Shared/_Starlight/Plumbing/Components/PlumbingInputComponent.cs @@ -0,0 +1,16 @@ +namespace Content.Shared._Starlight.Plumbing.Components; + +/// +/// A plumbing input that players can pour reagents into using containers. +/// Paired with to allow other machines +/// to pull reagents from its solution via the plumbing network. +/// +[RegisterComponent] +public sealed partial class PlumbingInputComponent : Component +{ + /// + /// The name of the solution on this entity that receives input. + /// + [DataField] + public string SolutionName = "input"; +} diff --git a/Content.Shared/_Starlight/Plumbing/Components/PlumbingOutletComponent.cs b/Content.Shared/_Starlight/Plumbing/Components/PlumbingOutletComponent.cs new file mode 100644 index 00000000000..133d7994c4d --- /dev/null +++ b/Content.Shared/_Starlight/Plumbing/Components/PlumbingOutletComponent.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using Robust.Shared.GameStates; + +namespace Content.Shared._Starlight.Plumbing.Components; + +/// +/// Marks an entity as a reagent source on the plumbing network. +/// discovers entities with this component +/// when other machines request reagents from the network. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class PlumbingOutletComponent : Component +{ + /// + /// The name of the solution to provide to the network. + /// + [DataField] + public string SolutionName = "tank"; + + /// + /// The outlet node names that serve this solution. + /// Defaults to a single "outlet" node. Devices with multiple outlets + /// (e.g. the filter) can list several names. + /// + [DataField] + public List OutletNames = new() { "outlet" }; + + /// + /// If true, this outlet can be pulled from. If false, it's blocked. + /// + [DataField, AutoNetworkedField] + public bool Enabled = true; + + /// + /// If set, look for the solution on the entity in this container slot instead of on this entity. + /// Useful for machines like dispensers where the solution is in a beaker. + /// + [DataField] + public string? ContainerSlotId; +} diff --git a/Content.Shared/_Starlight/Plumbing/Components/PlumbingOutputComponent.cs b/Content.Shared/_Starlight/Plumbing/Components/PlumbingOutputComponent.cs new file mode 100644 index 00000000000..2b2b639342f --- /dev/null +++ b/Content.Shared/_Starlight/Plumbing/Components/PlumbingOutputComponent.cs @@ -0,0 +1,15 @@ +namespace Content.Shared._Starlight.Plumbing.Components; + +/// +/// A plumbing output that players can draw reagents from using containers. +/// Pulling from the network is handled by . +/// +[RegisterComponent] +public sealed partial class PlumbingOutputComponent : Component +{ + /// + /// The name of the solution on this entity that players can draw from. + /// + [DataField] + public string SolutionName = "output"; +} diff --git a/Content.Shared/_Starlight/Plumbing/Components/PlumbingPillPressComponent.cs b/Content.Shared/_Starlight/Plumbing/Components/PlumbingPillPressComponent.cs new file mode 100644 index 00000000000..640a3386690 --- /dev/null +++ b/Content.Shared/_Starlight/Plumbing/Components/PlumbingPillPressComponent.cs @@ -0,0 +1,94 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.GameStates; + +namespace Content.Shared._Starlight.Plumbing.Components; + +/// +/// A plumbing pill press that automatically creates pills or patches +/// from reagents pulled through its inlet network. +/// When the buffer accumulates enough reagents to meet the dosage, +/// it spawns a pill or patch entity on the ground containing those reagents. +/// Supports optional mixing mode with two configurable ratio inlets (E/W) +/// in addition to the normal north inlet. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true)] +public sealed partial class PlumbingPillPressComponent : Component +{ + /// + /// Name of the buffer solution that holds incoming reagents. + /// + [DataField] + public string BufferSolutionName = "buffer"; + + /// + /// The dosage per pill/patch in units. Clamped 1–20. + /// + [DataField, AutoNetworkedField] + public uint Dosage = 10; + + /// + /// Whether we're producing pills or patches. + /// + [DataField, AutoNetworkedField] + public PillPressOutputMode OutputMode = PillPressOutputMode.Pill; + + /// + /// The pill visual type (0–19). Only relevant for pills. + /// + [DataField, AutoNetworkedField] + public uint PillType; + + /// + /// Whether the press is enabled and will pull/produce. + /// + [DataField, AutoNetworkedField] + public bool Enabled = true; + + // ========================================================================= + // Mixing mode fields + // ========================================================================= + + /// + /// Whether ratio-controlled mixing is active. + /// When enabled, E/W inlets pull proportionally into staging solutions + /// and combine into the buffer when targets are met. + /// + [DataField, AutoNetworkedField] + public bool MixingEnabled; + + /// + /// Ratio percentage for the east inlet (0–100). + /// + [DataField, AutoNetworkedField] + public float InletRatioEast; + + /// + /// Ratio percentage for the west inlet (0–100). + /// + [DataField, AutoNetworkedField] + public float InletRatioWest; + + /// + /// Name of the east staging solution. + /// + [DataField] + public string StagingEastSolutionName = "stagingEast"; + + /// + /// Name of the west staging solution. + /// + [DataField] + public string StagingWestSolutionName = "stagingWest"; + + /// + /// Node name for the east inlet. + /// + [DataField] + public string InletEastNodeName = "mixingInletEast"; + + /// + /// Node name for the west inlet. + /// + [DataField] + public string InletWestNodeName = "mixingInletWest"; +} diff --git a/Content.Shared/_Starlight/Plumbing/Components/PlumbingReactorComponent.cs b/Content.Shared/_Starlight/Plumbing/Components/PlumbingReactorComponent.cs new file mode 100644 index 00000000000..21b509251df --- /dev/null +++ b/Content.Shared/_Starlight/Plumbing/Components/PlumbingReactorComponent.cs @@ -0,0 +1,67 @@ +using Content.Shared.Atmos; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.FixedPoint; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._Starlight.Plumbing.Components; + +/// +/// A plumbing reactor that accumulates reagents and triggers chemical reactions. +/// Pulls specific reagents from the inlet until target quantities are met, +/// then triggers reactions in the buffer at a configurable temperature. +/// Products are moved to an output solution for other machines to pull. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true)] +public sealed partial class PlumbingReactorComponent : Component +{ + /// + /// Name of the inlet node for pulling reagents from the network. + /// + [DataField] + public string InletName = "inlet"; + + /// + /// Name of the buffer solution that holds reagents waiting to react. + /// + [DataField] + public string BufferSolutionName = "buffer"; + + /// + /// Name of the output solution that holds reacted products. + /// + [DataField] + public string OutputSolutionName = "output"; + + /// + /// Amount to pull from tanks per update. + /// + [DataField] + public FixedPoint2 TransferAmount = FixedPoint2.New(20); + + /// + /// Whether the reactor is currently enabled. + /// + [DataField, AutoNetworkedField] + public bool Enabled = true; + + /// + /// The reagent targets to accumulate. Key is reagent prototype ID, value is target quantity. + /// When all targets are met, the buffer triggers reactions and moves products to output. + /// + [DataField, AutoNetworkedField] + public Dictionary, FixedPoint2> ReagentTargets = new(); + + /// + /// Target temperature for the buffer solution in Kelvin. + /// The reactor will heat or cool the buffer to reach this temperature. + /// + [DataField, AutoNetworkedField] + public float TargetTemperature = Atmospherics.T20C; + + /// + /// Maximum heat transfer rate when powered, in watts. + /// + [DataField] + public float HeatTransferPower = 500f; +} diff --git a/Content.Shared/_Starlight/Plumbing/Components/PlumbingSmartFridgeComponent.cs b/Content.Shared/_Starlight/Plumbing/Components/PlumbingSmartFridgeComponent.cs new file mode 100644 index 00000000000..a7a2f455b0a --- /dev/null +++ b/Content.Shared/_Starlight/Plumbing/Components/PlumbingSmartFridgeComponent.cs @@ -0,0 +1,24 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.GameStates; + +namespace Content.Shared._Starlight.Plumbing.Components; + +/// +/// A plumbing-connected smart fridge that stores reagents pulled from the network +/// and fills labeled jugs on interaction. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class PlumbingSmartFridgeComponent : Component +{ + /// + /// The solution container name for the fridge's reagent storage. + /// + [DataField] + public string SolutionName = "fridge"; + + /// + /// Maximum amount of any single reagent the fridge can store. + /// + [DataField] + public FixedPoint2 MaxPerReagent = FixedPoint2.New(200); +} diff --git a/Content.Shared/_Starlight/Plumbing/Components/PlumbingSynthesizerComponent.cs b/Content.Shared/_Starlight/Plumbing/Components/PlumbingSynthesizerComponent.cs new file mode 100644 index 00000000000..60d9b3a0b66 --- /dev/null +++ b/Content.Shared/_Starlight/Plumbing/Components/PlumbingSynthesizerComponent.cs @@ -0,0 +1,46 @@ +using Content.Shared.Chemistry.Reagent; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._Starlight.Plumbing.Components; + +/// +/// A plumbing synthesizer that generates reagents from electrical power. +/// Drains power from its battery to produce a selected reagent into +/// its buffer. Other machines can pull the generated reagents via +/// the on this entity. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true)] +public sealed partial class PlumbingSynthesizerComponent : Component +{ + /// + /// Name of the outlet node for the plumbing network. + /// + [DataField] + public string OutletName = "outlet"; + + /// + /// Name of the buffer solution that holds generated reagents. + /// + [DataField] + public string BufferSolutionName = "buffer"; + + /// + /// Available reagents that can be generated. + /// Key is reagent prototype ID, value is power drain per unit. + /// + [DataField, AutoNetworkedField] + public Dictionary, float> GeneratableReagents = new(); + + /// + /// Currently selected reagent to generate. Null if none selected. + /// + [DataField, AutoNetworkedField] + public ProtoId? SelectedReagent; + + /// + /// Whether the synthesizer is currently enabled. + /// + [DataField, AutoNetworkedField] + public bool Enabled = true; +} diff --git a/Content.Shared/_Starlight/Plumbing/Components/PlumbingTankComponent.cs b/Content.Shared/_Starlight/Plumbing/Components/PlumbingTankComponent.cs new file mode 100644 index 00000000000..3f0c63416a7 --- /dev/null +++ b/Content.Shared/_Starlight/Plumbing/Components/PlumbingTankComponent.cs @@ -0,0 +1,42 @@ +using Content.Shared.FixedPoint; + +namespace Content.Shared._Starlight.Plumbing.Components; + +/// +/// A plumbing tank that stores reagents from the network. +/// Actively pulls reagents from its inlet network each update tick. +/// Other machines can pull from this tank via . +/// +[RegisterComponent] +public sealed partial class PlumbingTankComponent : Component +{ + /// + /// The name of the solution on this entity that the tank uses. + /// + [DataField] + public string SolutionName = "tank"; + + /// + /// Name of the inlet node for receiving reagents. + /// + [DataField] + public string InletName = "inlet"; + + /// + /// Name of the outlet node for providing reagents. + /// + [DataField] + public string OutletName = "outlet"; + + /// + /// Amount to transfer per update when pushing to outputs. + /// + [DataField] + public FixedPoint2 TransferAmount = FixedPoint2.New(10); + + /// + /// Round-robin index for fair outlet selection. + /// Tracks which outlet to start from when pulling from multiple sources. + /// + public int RoundRobinIndex; +} diff --git a/Content.Shared/_Starlight/Plumbing/Components/PlungeableComponent.cs b/Content.Shared/_Starlight/Plumbing/Components/PlungeableComponent.cs new file mode 100644 index 00000000000..f1d2d9cf706 --- /dev/null +++ b/Content.Shared/_Starlight/Plumbing/Components/PlungeableComponent.cs @@ -0,0 +1,17 @@ +using Robust.Shared.Audio; + +namespace Content.Shared._Starlight.Plumbing.Components; + +/// +/// Marker component for plumbing machines that can be plunged. +/// When a plunger is used on this entity, all solution containers are emptied onto the floor. +/// +[RegisterComponent] +public sealed partial class PlungeableComponent : Component +{ + /// + /// Sound played when the machine is successfully drained. + /// + [DataField] + public SoundSpecifier DrainSound = new SoundPathSpecifier("/Audio/Effects/Fluids/slosh.ogg"); +} diff --git a/Content.Shared/_Starlight/Plumbing/Components/StarlightPlumbingFilterComponent.cs b/Content.Shared/_Starlight/Plumbing/Components/StarlightPlumbingFilterComponent.cs new file mode 100644 index 00000000000..93ef8093719 --- /dev/null +++ b/Content.Shared/_Starlight/Plumbing/Components/StarlightPlumbingFilterComponent.cs @@ -0,0 +1,48 @@ +using Content.Shared.Chemistry.Reagent; +using Content.Shared.FixedPoint; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._Starlight.Plumbing.Components; + +/// +/// A plumbing filter that separates reagents into two outputs. +/// Pulls reagents from the inlet into a buffer, then restricts which reagents +/// can be pulled from each outlet via . +/// Filter outlet only provides reagents in the filter list. +/// Passthrough outlet only provides reagents NOT in the filter list. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true)] +public sealed partial class StarlightPlumbingFilterComponent : Component // Hardlight: Goob also has a PlumbingFilterComponent which causes RT to throw up if both has the same name +{ + /// + /// Name of the filter outlet node - only filtered reagents can be pulled here. + /// + [DataField] + public string FilterNodeName = "outletFilter"; + + /// + /// Name of the passthrough outlet node - only non-filtered reagents can be pulled here. + /// + [DataField] + public string PassthroughNodeName = "outletPassthrough"; + + /// + /// Name of the solution buffer that holds the reagents. + /// + [DataField] + public string BufferSolutionName = "buffer"; + + /// + /// Whether the filter is currently enabled. + /// + [DataField, AutoNetworkedField] + public bool Enabled = true; + + /// + /// The reagent IDs to filter out to the filter port. + /// Multiple reagents can be filtered simultaneously. + /// + [DataField, AutoNetworkedField] + public HashSet> FilteredReagents = new(); +} diff --git a/Content.Shared/_Starlight/Plumbing/PlumbingVisuals.cs b/Content.Shared/_Starlight/Plumbing/PlumbingVisuals.cs new file mode 100644 index 00000000000..041b3b3e2b8 --- /dev/null +++ b/Content.Shared/_Starlight/Plumbing/PlumbingVisuals.cs @@ -0,0 +1,55 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared._Starlight.Plumbing; + +[Serializable, NetSerializable] +public enum PlumbingVisuals : byte +{ + /// + /// Whether the plumbing machine is actively running. + /// + Running, + + /// + /// Directions that have PlumbingNodes defined. + /// Used to show jagged connectors in those directions. + /// + NodeDirections, + + /// + /// Directions that are connected. + /// Used to show smooth connector layer instead when connected. + /// + ConnectedDirections, + + /// + /// Directions that have inlet nodes. + /// Used to color inlet connectors red. + /// + InletDirections, + + /// + /// Directions that have outlet nodes. + /// Used to color outlet connectors blue. + /// + OutletDirections, + + /// + /// Whether the entity is covered by a floor tile. + /// Used to hide connector layers under floors without using SubFloorHide. + /// + CoveredByFloor, + + /// + /// Directions that have mixing inlet nodes. + /// Used to color mixing inlet connectors green. + /// + MixingInletDirections +} + +[Serializable, NetSerializable] +public enum PlumbingVisualLayers : byte +{ + Base, + Overlay +} diff --git a/Content.Shared/_Starlight/Plumbing/SharedPlumbingFilter.cs b/Content.Shared/_Starlight/Plumbing/SharedPlumbingFilter.cs new file mode 100644 index 00000000000..7e161b833d3 --- /dev/null +++ b/Content.Shared/_Starlight/Plumbing/SharedPlumbingFilter.cs @@ -0,0 +1,84 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.Serialization; + +namespace Content.Shared._Starlight.Plumbing; + +/// +/// UI key for the plumbing filter interface. +/// +[Serializable, NetSerializable] +public enum PlumbingFilterUiKey : byte +{ + Key, +} + +/// +/// State sent to the client to update the filter UI. +/// +[Serializable, NetSerializable] +public sealed class PlumbingFilterBoundUserInterfaceState : BoundUserInterfaceState +{ + /// + /// The reagent IDs currently being filtered. + /// + public HashSet FilteredReagents { get; } + + /// + /// Whether the filter is enabled. + /// + public bool Enabled { get; } + + public PlumbingFilterBoundUserInterfaceState(HashSet filteredReagents, bool enabled) + { + FilteredReagents = filteredReagents; + Enabled = enabled; + } +} + +/// +/// Message to toggle the filter on/off. +/// +[Serializable, NetSerializable] +public sealed class PlumbingFilterToggleMessage : BoundUserInterfaceMessage +{ + public bool Enabled { get; } + + public PlumbingFilterToggleMessage(bool enabled) + { + Enabled = enabled; + } +} + +/// +/// Message to add a reagent to the filter list. +/// +[Serializable, NetSerializable] +public sealed class PlumbingFilterAddReagentMessage : BoundUserInterfaceMessage +{ + public string ReagentId { get; } + + public PlumbingFilterAddReagentMessage(string reagentId) + { + ReagentId = reagentId; + } +} + +/// +/// Message to remove a reagent from the filter list. +/// +[Serializable, NetSerializable] +public sealed class PlumbingFilterRemoveReagentMessage : BoundUserInterfaceMessage +{ + public string ReagentId { get; } + + public PlumbingFilterRemoveReagentMessage(string reagentId) + { + ReagentId = reagentId; + } +} + +/// +/// Message to clear all filtered reagents. +/// +[Serializable, NetSerializable] +public sealed class PlumbingFilterClearMessage : BoundUserInterfaceMessage; diff --git a/Content.Shared/_Starlight/Plumbing/SharedPlumbingPillPress.cs b/Content.Shared/_Starlight/Plumbing/SharedPlumbingPillPress.cs new file mode 100644 index 00000000000..94976fc172b --- /dev/null +++ b/Content.Shared/_Starlight/Plumbing/SharedPlumbingPillPress.cs @@ -0,0 +1,163 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.Serialization; + +namespace Content.Shared._Starlight.Plumbing; + +/// +/// Whether the pill press produces pills or patches. +/// +[Serializable, NetSerializable] +public enum PillPressOutputMode : byte +{ + Pill, + // Patch, // HL: no patches +} + +/// +/// UI key for the plumbing pill press interface. +/// +[Serializable, NetSerializable] +public enum PlumbingPillPressUiKey : byte +{ + Key, +} + +/// +/// State sent to the client to update the pill press UI. +/// +[Serializable, NetSerializable] +public sealed class PlumbingPillPressBoundUserInterfaceState : BoundUserInterfaceState +{ + public FixedPoint2 BufferVolume { get; } + public uint Dosage { get; } + public PillPressOutputMode OutputMode { get; } + public uint PillType { get; } + public bool Enabled { get; } + + // Mixing mode fields + public bool MixingEnabled { get; } + public float InletRatioEast { get; } + public float InletRatioWest { get; } + public FixedPoint2 StagingEastVolume { get; } + public FixedPoint2 StagingWestVolume { get; } + + public PlumbingPillPressBoundUserInterfaceState( + FixedPoint2 bufferVolume, + uint dosage, + PillPressOutputMode outputMode, + uint pillType, + bool enabled, + bool mixingEnabled, + float inletRatioEast, + float inletRatioWest, + FixedPoint2 stagingEastVolume, + FixedPoint2 stagingWestVolume) + { + BufferVolume = bufferVolume; + Dosage = dosage; + OutputMode = outputMode; + PillType = pillType; + Enabled = enabled; + MixingEnabled = mixingEnabled; + InletRatioEast = inletRatioEast; + InletRatioWest = inletRatioWest; + StagingEastVolume = stagingEastVolume; + StagingWestVolume = stagingWestVolume; + } +} + +/// +/// Message to toggle the pill press on/off. +/// +[Serializable, NetSerializable] +public sealed class PlumbingPillPressToggleMessage : BoundUserInterfaceMessage +{ + public bool Enabled { get; } + + public PlumbingPillPressToggleMessage(bool enabled) + { + Enabled = enabled; + } +} + +/// +/// Message to set the dosage (1–20). +/// +[Serializable, NetSerializable] +public sealed class PlumbingPillPressSetDosageMessage : BoundUserInterfaceMessage +{ + public uint Dosage { get; } + + public PlumbingPillPressSetDosageMessage(uint dosage) + { + Dosage = dosage; + } +} + +/// +/// Message to switch between pill and patch output. +/// +[Serializable, NetSerializable] +public sealed class PlumbingPillPressSetOutputModeMessage : BoundUserInterfaceMessage +{ + public PillPressOutputMode OutputMode { get; } + + public PlumbingPillPressSetOutputModeMessage(PillPressOutputMode outputMode) + { + OutputMode = outputMode; + } +} + +/// +/// Message to set the pill type visual (0–19). +/// +[Serializable, NetSerializable] +public sealed class PlumbingPillPressSetPillTypeMessage : BoundUserInterfaceMessage +{ + public uint PillType { get; } + + public PlumbingPillPressSetPillTypeMessage(uint pillType) + { + PillType = pillType; + } +} + +/// +/// Message to toggle mixing mode on/off. +/// +[Serializable, NetSerializable] +public sealed class PlumbingPillPressSetMixingMessage : BoundUserInterfaceMessage +{ + public bool MixingEnabled { get; } + + public PlumbingPillPressSetMixingMessage(bool mixingEnabled) + { + MixingEnabled = mixingEnabled; + } +} + +/// +/// Identifies which mixing inlet is being configured. +/// +[Serializable, NetSerializable] +public enum PillPressInlet : byte +{ + East, + West, +} + +/// +/// Message to set the ratio for a mixing inlet. +/// +[Serializable, NetSerializable] +public sealed class PlumbingPillPressSetInletRatioMessage : BoundUserInterfaceMessage +{ + public PillPressInlet Inlet { get; } + public float Ratio { get; } + + public PlumbingPillPressSetInletRatioMessage(PillPressInlet inlet, float ratio) + { + Inlet = inlet; + Ratio = ratio; + } +} diff --git a/Content.Shared/_Starlight/Plumbing/SharedPlumbingReactor.cs b/Content.Shared/_Starlight/Plumbing/SharedPlumbingReactor.cs new file mode 100644 index 00000000000..17abf48de30 --- /dev/null +++ b/Content.Shared/_Starlight/Plumbing/SharedPlumbingReactor.cs @@ -0,0 +1,131 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.Serialization; + +namespace Content.Shared._Starlight.Plumbing; + +/// +/// UI key for the plumbing reactor interface. +/// +[Serializable, NetSerializable] +public enum PlumbingReactorUiKey : byte +{ + Key, +} + +/// +/// State sent to the client to update the reactor UI. +/// +[Serializable, NetSerializable] +public sealed class PlumbingReactorBoundUserInterfaceState : BoundUserInterfaceState +{ + /// + /// The reagent targets the reactor is trying to accumulate. + /// Key is reagent prototype ID, value is target quantity. + /// + public Dictionary ReagentTargets { get; } + + /// + /// Current quantities of reagents in the buffer. + /// + public Dictionary BufferContents { get; } + + /// + /// Current quantities of reagents in the output. + /// + public Dictionary OutputContents { get; } + + /// + /// Whether the reactor is enabled. + /// + public bool Enabled { get; } + + /// + /// Target temperature for the buffer in Kelvin. + /// + public float TargetTemperature { get; } + + /// + /// Current temperature of the buffer in Kelvin. + /// + public float CurrentTemperature { get; } + + public PlumbingReactorBoundUserInterfaceState( + Dictionary reagentTargets, + Dictionary bufferContents, + Dictionary outputContents, + bool enabled, + float targetTemperature, + float currentTemperature) + { + ReagentTargets = reagentTargets; + BufferContents = bufferContents; + OutputContents = outputContents; + Enabled = enabled; + TargetTemperature = targetTemperature; + CurrentTemperature = currentTemperature; + } +} + +/// +/// Message to toggle the reactor on/off. +/// +[Serializable, NetSerializable] +public sealed class PlumbingReactorToggleMessage : BoundUserInterfaceMessage +{ + public bool Enabled { get; } + + public PlumbingReactorToggleMessage(bool enabled) + { + Enabled = enabled; + } +} + +/// +/// Message to set a reagent target quantity. +/// +[Serializable, NetSerializable] +public sealed class PlumbingReactorSetTargetMessage : BoundUserInterfaceMessage +{ + public string ReagentId { get; } + public FixedPoint2 Quantity { get; } + + public PlumbingReactorSetTargetMessage(string reagentId, FixedPoint2 quantity) + { + ReagentId = reagentId; + Quantity = quantity; + } +} + +/// +/// Message to remove a reagent target. +/// +[Serializable, NetSerializable] +public sealed class PlumbingReactorRemoveTargetMessage : BoundUserInterfaceMessage +{ + public string ReagentId { get; } + + public PlumbingReactorRemoveTargetMessage(string reagentId) + { + ReagentId = reagentId; + } +} + +/// +/// Message to clear all reagent targets. +/// +[Serializable, NetSerializable] +public sealed class PlumbingReactorClearTargetsMessage : BoundUserInterfaceMessage; + +/// +/// Message to set the target temperature. +/// +[Serializable, NetSerializable] +public sealed class PlumbingReactorSetTemperatureMessage : BoundUserInterfaceMessage +{ + public float Temperature { get; } + + public PlumbingReactorSetTemperatureMessage(float temperature) + { + Temperature = temperature; + } +} diff --git a/Content.Shared/_Starlight/Plumbing/SharedPlumbingSmartFridge.cs b/Content.Shared/_Starlight/Plumbing/SharedPlumbingSmartFridge.cs new file mode 100644 index 00000000000..70cadbf0ee2 --- /dev/null +++ b/Content.Shared/_Starlight/Plumbing/SharedPlumbingSmartFridge.cs @@ -0,0 +1,47 @@ +using System.Linq; +using Content.Shared.FixedPoint; +using Robust.Shared.Serialization; + +namespace Content.Shared._Starlight.Plumbing; + +[Serializable, NetSerializable] +public enum PlumbingSmartFridgeUiKey : byte +{ + Key, +} + +/// +/// A single reagent entry for the plumbing smart fridge UI. +/// +[Serializable, NetSerializable] +public sealed class PlumbingSmartFridgeReagentEntry +{ + public string ReagentId; + public string LocalizedName; + public FixedPoint2 Quantity; + public Color Color; + + public PlumbingSmartFridgeReagentEntry(string reagentId, string localizedName, FixedPoint2 quantity, Color color) + { + ReagentId = reagentId; + LocalizedName = localizedName; + Quantity = quantity; + Color = color; + } +} + +/// +/// BUI state sent to the client containing the current reagent inventory. +/// +[Serializable, NetSerializable] +public sealed class PlumbingSmartFridgeBoundUserInterfaceState : BoundUserInterfaceState +{ + public List Entries; + public float MaxPerReagent; + + public PlumbingSmartFridgeBoundUserInterfaceState(List entries, float maxPerReagent) + { + Entries = entries; + MaxPerReagent = maxPerReagent; + } +} diff --git a/Content.Shared/_Starlight/Plumbing/SharedPlumbingSynthesizer.cs b/Content.Shared/_Starlight/Plumbing/SharedPlumbingSynthesizer.cs new file mode 100644 index 00000000000..36a57c6064c --- /dev/null +++ b/Content.Shared/_Starlight/Plumbing/SharedPlumbingSynthesizer.cs @@ -0,0 +1,91 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.Serialization; + +namespace Content.Shared._Starlight.Plumbing; + +/// +/// UI key for the plumbing synthesizer interface. +/// +[Serializable, NetSerializable] +public enum PlumbingSynthesizerUiKey : byte +{ + Key, +} + +/// +/// State sent to the client to update the synthesizer UI. +/// +[Serializable, NetSerializable] +public sealed class PlumbingSynthesizerBoundUserInterfaceState : BoundUserInterfaceState +{ + /// + /// Available reagents that can be generated. + /// Key is reagent prototype ID, value is power drain per unit. + /// + public Dictionary GeneratableReagents { get; } + + /// + /// Currently selected reagent ID, or null if none selected. + /// + public string? SelectedReagent { get; } + + /// + /// Current contents of the buffer. + /// + public Dictionary BufferContents { get; } + + /// + /// Whether the synthesizer is enabled. + /// + public bool Enabled { get; } + + /// + /// Current battery charge (0-1 ratio). + /// + public float BatteryCharge { get; } + + public PlumbingSynthesizerBoundUserInterfaceState( + Dictionary generatableReagents, + string? selectedReagent, + Dictionary bufferContents, + bool enabled, + float batteryCharge) + { + GeneratableReagents = generatableReagents; + SelectedReagent = selectedReagent; + BufferContents = bufferContents; + Enabled = enabled; + BatteryCharge = batteryCharge; + } +} + +/// +/// Message to toggle the synthesizer on/off. +/// +[Serializable, NetSerializable] +public sealed class PlumbingSynthesizerToggleMessage : BoundUserInterfaceMessage +{ + public bool Enabled { get; } + + public PlumbingSynthesizerToggleMessage(bool enabled) + { + Enabled = enabled; + } +} + +/// +/// Message to select which reagent to generate. +/// +[Serializable, NetSerializable] +public sealed class PlumbingSynthesizerSelectReagentMessage : BoundUserInterfaceMessage +{ + /// + /// The reagent ID to select, or null to deselect. + /// + public string? ReagentId { get; } + + public PlumbingSynthesizerSelectReagentMessage(string? reagentId) + { + ReagentId = reagentId; + } +} diff --git a/Resources/Locale/en-US/_Starlight/plumbing/pill-press.ftl b/Resources/Locale/en-US/_Starlight/plumbing/pill-press.ftl new file mode 100644 index 00000000000..4784ce207fa --- /dev/null +++ b/Resources/Locale/en-US/_Starlight/plumbing/pill-press.ftl @@ -0,0 +1,14 @@ +# Pill Press UI +plumbing-pill-press-window-title = Plumbing Pill Press +plumbing-pill-press-status = Status: +plumbing-pill-press-enabled = Enabled +plumbing-pill-press-disabled = Disabled +plumbing-pill-press-output-mode = Output: +plumbing-pill-press-mode-pill = Pills +plumbing-pill-press-mode-patch = Patches +plumbing-pill-press-dosage = Dosage (u): +plumbing-pill-press-set = Set +plumbing-pill-press-pill-type = Pill Type +plumbing-pill-press-mixing-mode = Mixing Mode (E/W Inlets) +plumbing-pill-press-east-ratio = East: +plumbing-pill-press-west-ratio = West: diff --git a/Resources/Locale/en-US/_Starlight/plumbing/plumbing.ftl b/Resources/Locale/en-US/_Starlight/plumbing/plumbing.ftl new file mode 100644 index 00000000000..a52daa6b324 --- /dev/null +++ b/Resources/Locale/en-US/_Starlight/plumbing/plumbing.ftl @@ -0,0 +1,56 @@ +# Plumbing system localization + +plumbing-input-full = The input buffer is full! +plumbing-input-poured = Poured {$amount}u into the plumbing network. + +plumbing-output-empty = The output buffer is empty! +plumbing-output-filled = Filled container with {$amount}u from the plumbing network. + +# Reactor UI +plumbing-reactor-window-title = Plumbing Reactor +plumbing-reactor-status = Status: +plumbing-reactor-enabled = Enabled +plumbing-reactor-disabled = Disabled +plumbing-reactor-temperature = Target Temp: +plumbing-reactor-set = Set +plumbing-reactor-reset = Reset +plumbing-reactor-add-reagent = Add Reagent +plumbing-reactor-reagent-id = Reagent ID +plumbing-reactor-quantity = Quantity +plumbing-reactor-add = Add +plumbing-reactor-targets = Reaction Recipe +plumbing-reactor-clear = Clear All +plumbing-reactor-remove-selected = Remove Selected +plumbing-reactor-output = Output Contents +plumbing-reactor-invalid-reagent = Unknown reagent: {$reagent} + +# Filter UI +plumbing-filter-window-title = Plumbing Filter +plumbing-filter-status = Status: +plumbing-filter-enabled = Enabled +plumbing-filter-disabled = Disabled +plumbing-filter-add-reagent = Add Reagent to Filter +plumbing-filter-reagent-id = Reagent ID +plumbing-filter-add = Add +plumbing-filter-filtered = Filtered Reagents +plumbing-filter-clear = Clear All +plumbing-filter-remove-selected = Remove Selected +plumbing-filter-invalid-reagent = Unknown reagent: {$reagent} + +# Synthesizer UI +plumbing-synthesizer-window-title = Plumbing Synthesizer +plumbing-synthesizer-status = Status: +plumbing-synthesizer-enabled = Enabled +plumbing-synthesizer-disabled = Disabled +plumbing-synthesizer-battery = Battery: +plumbing-synthesizer-power = Power: +plumbing-synthesizer-power-on = Connected +plumbing-synthesizer-power-off = No Power +plumbing-synthesizer-select-reagent = Select Reagent +plumbing-synthesizer-none = None +plumbing-synthesizer-buffer = Buffer Contents +plumbing-synthesizer-buffer-empty = Empty + +# Plunger drain +plumbing-drain-success = Drained {$amount}u onto the floor. +plumbing-drain-empty = Nothing to drain. diff --git a/Resources/Locale/en-US/_Starlight/plumbing/smart-fridge.ftl b/Resources/Locale/en-US/_Starlight/plumbing/smart-fridge.ftl new file mode 100644 index 00000000000..463d9e7b908 --- /dev/null +++ b/Resources/Locale/en-US/_Starlight/plumbing/smart-fridge.ftl @@ -0,0 +1,10 @@ +plumbing-smart-fridge-window-title = Smart Fridge +plumbing-smart-fridge-header = Chemical Storage +plumbing-smart-fridge-reagent-entry = {$reagent}: {$amount}u +plumbing-smart-fridge-empty = No reagents stored. +plumbing-smart-fridge-total = {$count} reagent(s) — {$total}u total +plumbing-smart-fridge-no-label = Container has no label. +plumbing-smart-fridge-no-match = Could not match label to any known reagent. +plumbing-smart-fridge-not-in-stock = {$reagent} is not in stock. +plumbing-smart-fridge-jug-full = Container is already full. +plumbing-smart-fridge-filled = Filled {$amount}u of {$reagent}. diff --git a/Resources/Locale/en-US/_Starlight/rpld/rpld-components.ftl b/Resources/Locale/en-US/_Starlight/rpld/rpld-components.ftl new file mode 100644 index 00000000000..476e562ce82 --- /dev/null +++ b/Resources/Locale/en-US/_Starlight/rpld/rpld-components.ftl @@ -0,0 +1,2 @@ +rpld-component-ducts = Plumbing Ducts +rpld-component-machines = Plumbing Machines diff --git a/Resources/Locale/en-US/chemistry/components/chem-master-component.ftl b/Resources/Locale/en-US/chemistry/components/chem-master-component.ftl index c000811e77d..5a7afa7d68a 100644 --- a/Resources/Locale/en-US/chemistry/components/chem-master-component.ftl +++ b/Resources/Locale/en-US/chemistry/components/chem-master-component.ftl @@ -12,6 +12,8 @@ chem-master-window-input-tab = Input chem-master-window-output-tab = Output chem-master-window-container-label = Container chem-master-window-eject-button = Eject +chem-master-window-valve-open = Valve: Open +chem-master-window-valve-closed = Valve: Closed chem-master-window-no-container-loaded-text = No container loaded. chem-master-window-buffer-text = Buffer chem-master-window-buffer-label = buffer: diff --git a/Resources/Locale/en-US/chemistry/components/reagent-dispenser-component.ftl b/Resources/Locale/en-US/chemistry/components/reagent-dispenser-component.ftl index 37697c45176..6d8fae66613 100644 --- a/Resources/Locale/en-US/chemistry/components/reagent-dispenser-component.ftl +++ b/Resources/Locale/en-US/chemistry/components/reagent-dispenser-component.ftl @@ -17,3 +17,5 @@ reagent-dispenser-window-no-container-loaded-text = No container loaded. reagent-dispenser-window-reagent-name-not-found-text = Reagent name not found reagent-dispenser-window-unknown-reagent-text = Unknown reagent reagent-dispenser-window-quantity-label-text = {$quantity}u +reagent-dispenser-window-valve-open = Close Valve +reagent-dispenser-window-valve-closed = Open Valve diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml b/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml index 31e233625a3..ad5fcaa7e07 100644 --- a/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml +++ b/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml @@ -10,6 +10,12 @@ noRot: true - type: Clickable - type: InteractionOutline + - type: Appearance + # Starlight-start: Plumbing connector visuals + - type: PlumbingConnectorAppearance + offset: 0.09375 + - type: AtmosPipeColor + # Starlight-end - type: Physics bodyType: Static - type: Fixtures @@ -73,3 +79,15 @@ - type: StaticPrice price: 1000 - type: WiresPanel + # Starlight-start: Plumbing outlet + - type: NodeContainer + nodes: + outlet: # Provides reagents TO the network (all directions) + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: Fourway + - type: PlumbingOutlet + solutionName: beaker + containerSlotId: beakerSlot + enabled: false + # Starlight-end diff --git a/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml b/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml index a6096b7a480..b91a628935e 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml @@ -14,6 +14,11 @@ - state: mixer_screens shader: unshaded map: ["enum.PowerDeviceVisualLayers.Powered"] + # Starlight-start: Plumbing connector visuals + - type: PlumbingConnectorAppearance + offset: 0.09375 + - type: AtmosPipeColor + # Starlight-end - type: Icon sprite: Structures/Machines/mixer.rsi state: mixer_empty @@ -114,8 +119,28 @@ maxVol: 1000000 - type: DumpableSolution solution: buffer - unlimited: true + unlimited: true # Starlight plumbing normally turns this off but maybe not for us lmao - type: GuideHelp guides: - Chemicals - Chemist + # Starlight - Plumbing integration + - type: PlumbingDevice + updateInterval: 2 + - type: PlumbingInlet + solutionName: buffer + - type: PlumbingOutlet + solutionName: beaker + containerSlotId: beakerSlot + enabled: false + - type: NodeContainer + nodes: + inlet: # Pulls reagents FROM the network (north, east, west sides) + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: TNorth + outlet: # Provides reagents TO the network (south side) + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: South + # Starlight-end diff --git a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml index 73ef70f7516..b6279ed0a95 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml @@ -78,6 +78,12 @@ name: industrial reagent grinder description: An industrial reagent grinder. components: + - type: Appearance + # Starlight-start: Plumbing connector visuals + - type: PlumbingConnectorAppearance + offset: 0.09375 + - type: AtmosPipeColor + # Starlight-end - type: SolutionContainerManager solutions: output: @@ -125,3 +131,12 @@ solution: output - type: ExaminableSolution solution: output + # Starlight - Plumbing integration + - type: PlumbingOutlet + solutionName: output + - type: NodeContainer + nodes: + outlet: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: Fourway diff --git a/Resources/Prototypes/Guidebook/engineering.yml b/Resources/Prototypes/Guidebook/engineering.yml index a61e6c4a728..cce81640394 100644 --- a/Resources/Prototypes/Guidebook/engineering.yml +++ b/Resources/Prototypes/Guidebook/engineering.yml @@ -6,6 +6,7 @@ - Construction - Power - Atmospherics + - StarlightPlumbing # Starlight - ShuttleCraft - Networking - Automation diff --git a/Resources/Prototypes/_StarLight/Entities/Guidebook/plumbing_ducts.yml b/Resources/Prototypes/_StarLight/Entities/Guidebook/plumbing_ducts.yml new file mode 100644 index 00000000000..fc183dbb511 --- /dev/null +++ b/Resources/Prototypes/_StarLight/Entities/Guidebook/plumbing_ducts.yml @@ -0,0 +1,55 @@ +# ============================================================================ +# Guidebook Duct Entities +# ============================================================================ +# Duct variants specifically for GuideEntityEmbed display. +# Real ducts have SubFloorHide + visible: false, which prevents proper +# rendering in guidebook embeds +# ============================================================================ + +- type: entity + id: GuidebookPlumbingDuctStraight + name: fluid duct + suffix: Straight, Guidebook + categories: [ HideSpawnMenu ] + components: + - type: Sprite + sprite: _Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi + drawdepth: ThinPipe + layers: + - state: storageStraight + +- type: entity + id: GuidebookPlumbingDuctBend + name: fluid duct + suffix: Bend, Guidebook + categories: [ HideSpawnMenu ] + components: + - type: Sprite + sprite: _Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi + drawdepth: ThinPipe + layers: + - state: storageBend + +- type: entity + id: GuidebookPlumbingDuctTJunction + name: fluid duct + suffix: T-Junction, Guidebook + categories: [ HideSpawnMenu ] + components: + - type: Sprite + sprite: _Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi + drawdepth: ThinPipe + layers: + - state: storageTjunction + +- type: entity + id: GuidebookPlumbingDuctFourway + name: fluid duct + suffix: Fourway, Guidebook + categories: [ HideSpawnMenu ] + components: + - type: Sprite + sprite: _Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi + drawdepth: ThinPipe + layers: + - state: storageFourway diff --git a/Resources/Prototypes/_StarLight/Entities/Objects/Tools/tools.yml b/Resources/Prototypes/_StarLight/Entities/Objects/Tools/tools.yml index f80dc19112e..51caa2bec1c 100644 --- a/Resources/Prototypes/_StarLight/Entities/Objects/Tools/tools.yml +++ b/Resources/Prototypes/_StarLight/Entities/Objects/Tools/tools.yml @@ -880,4 +880,66 @@ - Wrench - PlantSampleTaker - Wirecutter - - Screwdriver \ No newline at end of file + - Screwdriver + +# ============================================================================= +# RPLD (Rapid Plumbing Device) - places plumbing ducts and machines +# ============================================================================= +- type: entity + id: RPLD + parent: RCD + name: RPLD + description: A device used to rapidly deploy plumbing ducts and machines. + components: + - type: RCD + isRPLD: true + availablePrototypes: + - PlumbingRpldDuctStraight + - PlumbingRpldDuctBend + - PlumbingRpldDuctTJunction + - PlumbingRpldDuctFourway + - PlumbingRpldTank + - PlumbingRpldInput + - PlumbingRpldOutput + - PlumbingRpldSink + - PlumbingRpldReactor + - PlumbingRpldPillPress + - PlumbingRpldFilter + - PlumbingRpldSynthesizer + - PlumbingRpldSmartFridge + - PlumbingRpldDrain + - Deconstruct + - type: LimitedCharges + maxCharges: 45 + - type: Sprite + sprite: _Starlight/Objects/Tools/rpld.rsi + +- type: entity + id: RPLDEmpty + parent: RPLD + suffix: Empty + components: + - type: LimitedCharges + lastCharges: -1 + +- type: entity + id: RPLDRecharging + parent: RPLD + name: experimental RPLD + description: Cyborg-mounted Rapid Plumbing Device which creates compressed matter on the fly using an internal fabricator. + suffix: AutoRecharge + components: + - type: LimitedCharges + maxCharges: 20 + - type: AutoRecharge + rechargeDuration: 10 + +- type: entity + id: RPLDExperimental + parent: RPLD + suffix: Admeme + name: experimental RPLD + description: A bluespace-enhanced rapid plumbing device that passively generates its own compressed matter. + components: + - type: AutoRecharge + rechargeDuration: 1 diff --git a/Resources/Prototypes/_StarLight/Entities/Structures/Piping/Plumbing/ducts.yml b/Resources/Prototypes/_StarLight/Entities/Structures/Piping/Plumbing/ducts.yml new file mode 100644 index 00000000000..8de7c5f5869 --- /dev/null +++ b/Resources/Prototypes/_StarLight/Entities/Structures/Piping/Plumbing/ducts.yml @@ -0,0 +1,189 @@ +# ============================================================================ +# Plumbing Ducts +# ============================================================================ +# Ducts allow transport of reagents through the plumbing network. +# All ducts inherit from PlumbingDuctBase for common components. +# ============================================================================ + +# ============================================================================ +# BASE ENTITY +# ============================================================================ + +- type: entity + abstract: true + parent: BaseItem + id: PlumbingDuctBase + name: fluid duct + description: A duct for transporting reagents through the plumbing network. + placement: + mode: SnapgridCenter + components: + - type: Visibility + layer: 1 + - type: Item + size: Normal + - type: Transform + anchored: true + - type: Sprite + sprite: _Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi + drawdepth: ThinPipe + visible: false + - type: Appearance + - type: PipeAppearance + sprite: + - sprite: _Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi + state: ductConnector + - type: PipeColorVisuals + - type: AtmosPipeColor + - type: Clickable + - type: InteractionOutline + - type: SubFloorHide + - type: CollideOnAnchor + - type: Anchorable + - type: Pullable + - type: Rotatable + - type: NodeContainer + - type: Tag + tags: + - PlumbingDuct + - type: Physics + canCollide: false + bodyType: static + - type: Damageable + damageContainer: StructuralInorganic + damageModifierSet: Metallic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 200 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + params: + volume: -8 + - !type:DoActsBehavior + acts: [Breakage] + - type: PipeRestrictOverlap + - type: RCDDeconstructable + cost: 1 + delay: 2 + fx: EffectRCDDeconstruct2 + rpld: true + - type: StaticPrice + price: 20 + +# ============================================================================ +# DUCT VARIANTS +# ============================================================================ + +# -------------------------------- +# Straight Duct +# -------------------------------- +- type: entity + parent: PlumbingDuctBase + id: PlumbingDuctStraight + name: fluid duct + suffix: Straight + components: + - type: NodeContainer + nodes: + duct: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: Longitudinal + - type: Sprite + layers: + - state: ductStraight + map: [ "enum.PipeVisualLayers.Pipe" ] + color: "#4169E1" + - type: Item + storedSprite: + sprite: _Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi + state: storageStraight + shape: + - 0,0,0,2 + +# -------------------------------- +# Bend Duct +# -------------------------------- +- type: entity + parent: PlumbingDuctBase + id: PlumbingDuctBend + name: fluid duct + suffix: Bend + components: + - type: NodeContainer + nodes: + duct: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: SWBend + - type: Sprite + layers: + - state: ductBend + map: [ "enum.PipeVisualLayers.Pipe" ] + color: "#4169E1" + - type: Item + size: Small + storedSprite: + sprite: _Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi + state: storageBend + +# -------------------------------- +# T-Junction Duct +# -------------------------------- +- type: entity + parent: PlumbingDuctBase + id: PlumbingDuctTJunction + name: fluid duct + suffix: T-Junction + components: + - type: NodeContainer + nodes: + duct: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: TSouth + - type: Sprite + layers: + - state: ductTJunction + map: [ "enum.PipeVisualLayers.Pipe" ] + color: "#4169E1" + - type: Item + storedSprite: + sprite: _Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi + state: storageTjunction + +# -------------------------------- +# Fourway Duct +# -------------------------------- +- type: entity + parent: PlumbingDuctBase + id: PlumbingDuctFourway + name: fluid duct + suffix: Fourway + components: + - type: NodeContainer + nodes: + duct: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: Fourway + - type: Sprite + layers: + - state: ductFourway + map: [ "enum.PipeVisualLayers.Pipe" ] + color: "#4169E1" + - type: Item + size: Small + storedSprite: + sprite: _Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi + state: storageFourway diff --git a/Resources/Prototypes/_StarLight/Entities/Structures/Piping/Plumbing/machines.yml b/Resources/Prototypes/_StarLight/Entities/Structures/Piping/Plumbing/machines.yml new file mode 100644 index 00000000000..9bf27fe1a7f --- /dev/null +++ b/Resources/Prototypes/_StarLight/Entities/Structures/Piping/Plumbing/machines.yml @@ -0,0 +1,760 @@ +# Base plumbing machine entity - all plumbing machines inherit from this +- type: entity + abstract: true + id: PlumbingMachineBase + placement: + mode: SnapgridCenter + components: + - type: Transform + anchored: true + noRot: false + - type: Sprite + noRot: false # Allow rotation so connectors face correct direction + - type: Clickable + - type: Appearance + - type: PlumbingConnectorAppearance + offset: 0.09375 + - type: InteractionOutline + - type: Anchorable + - type: Pullable + - type: Rotatable + rotateWhileAnchored: true + - type: Physics + bodyType: Static + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: -0.25,-0.25,0.25,0.25 + density: 190 + mask: + - MachineMask + layer: + - MachineLayer + - type: AtmosPipeColor + - type: RCDDeconstructable + cost: 2 + delay: 2 + fx: EffectRCDDeconstruct2 + rpld: true + - type: Damageable + damageContainer: StructuralInorganic + damageModifierSet: Metallic + +# ============================================================================= +# Plumbing Tank - stores reagents in the network +# ============================================================================= +- type: entity + parent: PlumbingMachineBase + id: PlumbingTank + name: plumbing tank + description: A tank that pulls reagents from its inlet (north) and stores up to 400u. Other machines can pull from its outlets (south, east, west). + components: + - type: Sprite + sprite: _Starlight/Structures/Piping/Plumbing/plumbers.rsi + layers: + - state: tank + - type: PlumbingDevice + updateInterval: 2 + - type: PlumbingInlet + solutionName: tank + - type: Plungeable + - type: PlumbingOutlet + solutionName: tank + - type: SolutionContainerManager + solutions: + tank: + maxVol: 400 + canReact: false + - type: ExaminableSolution + solution: tank + - type: NodeContainer + nodes: + inlet: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: North + outlet: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: TSouth + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:SpillBehavior + solution: tank + - !type:DoActsBehavior + acts: ["Destruction"] + - type: StaticPrice + price: 100 + +# ============================================================================= +# Plumbing Input - pour reagents into the network +# ============================================================================= +- type: entity + parent: PlumbingMachineBase + id: PlumbingInput + name: plumbing input + description: A small tank you pour reagents into. Other machines pull from this. Connects in all directions. + components: + - type: Sprite + sprite: _Starlight/Structures/Piping/Plumbing/plumbers.rsi + layers: + - state: pipe_input + - type: PlumbingInput + solutionName: input + - type: Plungeable + - type: PlumbingOutlet + solutionName: input + - type: SolutionContainerManager + solutions: + input: + maxVol: 100 + canReact: false + - type: ExaminableSolution + solution: input + - type: NodeContainer + nodes: + outlet: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: Fourway + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:SpillBehavior + solution: input + - !type:DoActsBehavior + acts: ["Destruction"] + - type: StaticPrice + price: 75 + +# ============================================================================= +# Plumbing Sink - draws reagents from the network, has an outlet for solutions dumped into it +# ============================================================================= +- type: entity + parent: PlumbingMachineBase + id: PlumbingSink + name: plumbing sink + description: A sink connected to the plumbing network. Draw reagents from it with a container, or dump reagents into it to send them down the drain. Connects in all directions. + components: + - type: Sprite + sprite: Structures/Furniture/sink.rsi + drawdepth: FloorObjects + layers: + - state: sink_wide + - type: Rotatable + rotateWhileAnchored: false + - type: PlumbingDevice + updateInterval: 2 + - type: PlumbingInlet + solutionName: output + inletNames: + - inletNorth + - inletEast + - inletWest + - type: PlumbingOutput + solutionName: output + - type: PlumbingOutlet + solutionName: drain + - type: DumpableSolution + solution: drain + - type: Plungeable + - type: SolutionContainerManager + solutions: + output: + maxVol: 100 + canReact: false + drain: + maxVol: 100 + canReact: false + - type: ExaminableSolution + solution: output + - type: NodeContainer + nodes: + inletNorth: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: North + inletEast: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: East + inletWest: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: West + outlet: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: South + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:SpillBehavior + solution: output + - !type:SpillBehavior + solution: drain + - !type:DoActsBehavior + acts: ["Destruction"] + - type: StaticPrice + price: 100 + +# ============================================================================= +# Plumbing Output - draw reagents from the network +# ============================================================================= +- type: entity + parent: PlumbingMachineBase + id: PlumbingOutput + name: plumbing output + description: A small tank that pulls reagents from the network. Draw from it with a container. Connects in all directions. + components: + - type: Sprite + sprite: _Starlight/Structures/Piping/Plumbing/plumbers.rsi + layers: + - state: pipe_output + - type: PlumbingDevice + updateInterval: 2 + - type: PlumbingInlet + solutionName: output + inletNames: + - inletNorth + - inletEast + - inletSouth + - inletWest + - type: PlumbingOutput + solutionName: output + - type: Plungeable + - type: SolutionContainerManager + solutions: + output: + maxVol: 100 + canReact: false + - type: ExaminableSolution + solution: output + - type: NodeContainer + nodes: + inletNorth: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: North + inletEast: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: East + inletSouth: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: South + inletWest: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: West + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:SpillBehavior + solution: output + - !type:DoActsBehavior + acts: ["Destruction"] + - type: StaticPrice + price: 75 + +# ============================================================================= +# Plumbing Reactor - accumulates reagents and triggers reactions +# ============================================================================= +- type: entity + parent: PlumbingMachineBase + id: PlumbingReactor + name: plumbing reactor + description: Pulls target reagents from inlet (north) and triggers reactions when targets are met. Products available at outlet (south). + components: + - type: Sprite + sprite: _Starlight/Structures/Piping/Plumbing/plumbers.rsi + layers: + - state: reaction_chamber + map: [ "enum.PlumbingVisualLayers.Base" ] + - type: GenericVisualizer + visuals: + enum.PlumbingVisuals.Running: + enum.PlumbingVisualLayers.Base: + True: { state: reaction_chamber_on } + False: { state: reaction_chamber } + - type: PlumbingDevice + updateInterval: 2 + - type: PlumbingReactor + bufferSolutionName: buffer + outputSolutionName: output + inletName: inlet + - type: Plungeable + - type: PlumbingOutlet + solutionName: output + - type: SolutionContainerManager + solutions: + buffer: + maxVol: 100 + canReact: false # Reactions triggered manually when targets met + output: + maxVol: 100 + canReact: false + - type: ExaminableSolution + solution: output + - type: NodeContainer + nodes: + inlet: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: North + outlet: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: South + - type: UserInterface + interfaces: + enum.PlumbingReactorUiKey.Key: + type: PlumbingReactorBoundUserInterface + - type: ActivatableUI + key: enum.PlumbingReactorUiKey.Key + - type: ApcPowerReceiver + powerLoad: 750 + - type: ExtensionCableReceiver + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:SpillBehavior + solution: buffer + - !type:SpillBehavior + solution: output + - !type:DoActsBehavior + acts: ["Destruction"] + - type: StaticPrice + price: 150 + +# ============================================================================= +# Plumbing Smart Fridge - stores reagents from the network, fills labeled jugs +# ============================================================================= +- type: entity + parent: PlumbingMachineBase + id: PlumbingSmartFridge + name: plumbing smart fridge + description: A plumbing-connected smart fridge. Pulls all reagents from the network and stores up to 200u of each. Insert a labeled jug to fill it with the matching reagent. + components: + - type: Sprite + sprite: Structures/Machines/smartfridge.rsi + layers: + - state: smartfridge + map: ["enum.StorageVisualLayers.Base"] + - state: smartfridge_door + map: ["enum.StorageVisualLayers.Door"] + shader: unshaded + - type: PointLight + radius: 1.2 + energy: 3.0 + color: "#9dc5c9" + - type: PlumbingDevice + updateInterval: 2 + - type: PlumbingInlet + solutionName: fridge + inletNames: + - inletNorth + - inletEast + - inletSouth + - inletWest + - type: PlumbingSmartFridge + solutionName: fridge + maxPerReagent: 200 + - type: SolutionContainerManager + solutions: + fridge: + maxVol: 50000 + canReact: false + - type: ExaminableSolution + solution: fridge + - type: NodeContainer + nodes: + inletNorth: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: North + inletEast: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: East + inletSouth: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: South + inletWest: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: West + - type: UserInterface + interfaces: + enum.PlumbingSmartFridgeUiKey.Key: + type: PlumbingSmartFridgeBoundUserInterface + - type: ActivatableUI + key: enum.PlumbingSmartFridgeUiKey.Key + - type: ApcPowerReceiver + powerLoad: 200 + - type: ExtensionCableReceiver + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 200 + behaviors: + - !type:SpillBehavior + solution: fridge + - !type:DoActsBehavior + acts: ["Destruction"] + - type: StaticPrice + price: 200 + +# ============================================================================= +# Plumbing Pill Press - automatically creates pills or patches from reagents +# ============================================================================= +- type: entity + parent: PlumbingMachineBase + id: PlumbingPillPress + name: plumbing pill press + description: Pulls reagents from the network and automatically presses them into pills or patches at a configurable dosage. Has optional mixing inlets (E/W) for ratio-controlled mixing. + components: + - type: Sprite + sprite: _Starlight/Structures/Piping/Plumbing/plumbers.rsi + layers: + - state: pill_press_off + map: [ "enum.PlumbingVisualLayers.Base" ] + - type: GenericVisualizer + visuals: + enum.PlumbingVisuals.Running: + enum.PlumbingVisualLayers.Base: + True: { state: pill_press } + False: { state: pill_press_off } + - type: PlumbingDevice + updateInterval: 2 + - type: PlumbingPillPress + bufferSolutionName: buffer + dosage: 10 + - type: PlumbingConnectorAppearance + offset: 0.09375 + mixingInletNames: + - mixingInletEast + - mixingInletWest + - type: PlumbingInlet + solutionName: buffer + inletNames: + - inlet + - type: SolutionContainerManager + solutions: + buffer: + maxVol: 20 + canReact: false + stagingEast: + maxVol: 20 + canReact: false + stagingWest: + maxVol: 20 + canReact: false + - type: ExaminableSolution + solution: buffer + - type: NodeContainer + nodes: + inlet: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: North + mixingInletEast: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: East + mixingInletWest: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: West + - type: UserInterface + interfaces: + enum.PlumbingPillPressUiKey.Key: + type: PlumbingPillPressBoundUserInterface + - type: ActivatableUI + key: enum.PlumbingPillPressUiKey.Key + - type: ApcPowerReceiver + powerLoad: 200 + - type: ExtensionCableReceiver + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:SpillBehavior + solution: buffer + - !type:DoActsBehavior + acts: ["Destruction"] + - type: StaticPrice + price: 150 +# ============================================================================= +# Plumbing Filter - separates specific reagents into two outputs +# ============================================================================= +- type: entity + parent: PlumbingMachineBase + id: PlumbingFilter + name: plumbing filter + description: Filters specific reagents. Pulls from inlet (north). Filtered reagents at west output, non-filtered at south output. + components: + - type: Sprite + sprite: _Starlight/Structures/Piping/Plumbing/plumbers.rsi + layers: + - state: filter + - type: PlumbingDevice + updateInterval: 2 + - type: PlumbingInlet + solutionName: buffer + - type: StarlightPlumbingFilter + filterNodeName: outletFilter + passthroughNodeName: outletPassthrough + bufferSolutionName: buffer + - type: Plungeable + - type: PlumbingOutlet + solutionName: buffer + outletNames: + - outletFilter + - outletPassthrough + - type: SolutionContainerManager + solutions: + buffer: + maxVol: 40 + canReact: false + - type: ExaminableSolution + solution: buffer + - type: NodeContainer + nodes: + inlet: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: North + outletFilter: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: West + outletPassthrough: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: South + - type: AtmosPipeColor + color: "#8B4513" + - type: UserInterface + interfaces: + enum.PlumbingFilterUiKey.Key: + type: PlumbingFilterBoundUserInterface + - type: ActivatableUI + key: enum.PlumbingFilterUiKey.Key + - type: Flippable + mirrorEntity: PlumbingFilterFlipped + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:SpillBehavior + solution: buffer + - !type:DoActsBehavior + acts: ["Destruction"] + - type: StaticPrice + price: 125 + +- type: entity + parent: PlumbingFilter + id: PlumbingFilterFlipped + suffix: Flipped + description: Filters specific reagents. Pulls from inlet (south). Filtered reagents at west output, non-filtered at north output. + components: + - type: Sprite + sprite: _Starlight/Structures/Piping/Plumbing/plumbers.rsi + layers: + - state: filterF + - type: NodeContainer + nodes: + inlet: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: South + outletFilter: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: West + outletPassthrough: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: North + - type: Flippable + mirrorEntity: PlumbingFilter + +# ============================================================================= +# Plumbing Synthesizer - generates reagents from power +# ============================================================================= +- type: entity + parent: PlumbingMachineBase + id: PlumbingSynthesizer + name: plumbing synthesizer + description: Generates reagents using power. Select a reagent and it fills its buffer for other machines to pull from. Output at south. + components: + - type: Sprite + sprite: _Starlight/Structures/Piping/Plumbing/plumbers.rsi + noRot: false + layers: + - state: synthesizer + - state: synthesizer_overlay + map: [ "enum.PlumbingVisualLayers.Overlay" ] + visible: false + - type: GenericVisualizer + visuals: + enum.PlumbingVisuals.Running: + enum.PlumbingVisualLayers.Overlay: + True: { visible: true } + False: { visible: false } + - type: PlumbingDevice + updateInterval: 2 + - type: PlumbingSynthesizer + bufferSolutionName: buffer + generatableReagents: + Iron: 2 + Carbon: 2 + Copper: 2 + Hydrogen: 2 + Oxygen: 0 + Sugar: 2 + Silicon: 5 + Phosphorus: 5 + - type: Plungeable + - type: PlumbingOutlet + solutionName: buffer + - type: SolutionContainerManager + solutions: + buffer: + maxVol: 10 + canReact: false + - type: ExaminableSolution + solution: buffer + - type: NodeContainer + nodes: + outlet: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: South + - type: UserInterface + interfaces: + enum.PlumbingSynthesizerUiKey.Key: + type: PlumbingSynthesizerBoundUserInterface + - type: ActivatableUI + key: enum.PlumbingSynthesizerUiKey.Key + - type: PowerCellSlot + cellSlotId: cell_slot + - type: ItemSlots + slots: + cell_slot: + name: power-cell-slot-component-slot-name-default + startingItem: PowerCellMedium + - type: ContainerContainer + containers: + cell_slot: !type:ContainerSlot + - type: Charger + slotId: cell_slot + chargeRate: 5 + passiveDraw: 1 + - type: ApcPowerReceiver + powerLoad: 1 + - type: ExtensionCableReceiver + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:SpillBehavior + solution: buffer + - !type:DoActsBehavior + acts: ["Destruction"] + - type: StaticPrice + price: 200 +# ============================================================================= +# Plumbing Drain - floor drain that outputs to plumbing network +# ============================================================================= +- type: entity + parent: PlumbingMachineBase + id: PlumbingDrain + name: plumbing drain + description: A floor drain that absorbs puddles and routes them into the plumbing network instead of disposing them. + placement: + mode: SnapgridCenter + components: + - type: Sprite + drawdepth: FloorObjects + sprite: Objects/Specific/Janitorial/drain.rsi + layers: + - state: icon + - map: [ "enum.SolutionContainerLayers.Fill" ] + state: fill-1 + visible: false + - type: Physics + bodyType: Static + canCollide: false + - type: Fixtures + fixtures: {} + - type: AmbientSound + enabled: false + volume: -8 + range: 8 + sound: + path: /Audio/Ambience/Objects/drain.ogg + - type: Drain + unitsDestroyedPerSecond: 0 + - type: DumpableSolution + solution: drainBuffer + - type: Appearance + - type: SolutionContainerVisuals + maxFillLevels: 1 + fillBaseName: fill- + solutionName: drainBuffer + - type: PlumbingOutlet + solutionName: drainBuffer + - type: SolutionContainerManager + solutions: + drainBuffer: + maxVol: 400 + - type: NodeContainer + nodes: + outlet: + !type:PlumbingNode + nodeGroupID: Plumbing + pipeDirection: Fourway + - type: ExaminableSolution + solution: drainBuffer + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:SpillBehavior + solution: drainBuffer + - !type:DoActsBehavior + acts: ["Destruction"] + - type: StaticPrice + price: 150 diff --git a/Resources/Prototypes/_StarLight/Guidebook/engineering.yml b/Resources/Prototypes/_StarLight/Guidebook/engineering.yml new file mode 100644 index 00000000000..73de6b43ba8 --- /dev/null +++ b/Resources/Prototypes/_StarLight/Guidebook/engineering.yml @@ -0,0 +1,18 @@ +# Plumbing guidebook entries +- type: guideEntry + id: StarlightPlumbing + name: guide-entry-plumbing + text: "/ServerInfo/_StarLight/Guidebook/Engineering/Plumbing.xml" + children: + - PlumbingFlow + - PlumbingMachines + +- type: guideEntry + id: PlumbingFlow + name: guide-entry-plumbingflow + text: "/ServerInfo/_StarLight/Guidebook/Engineering/PlumbingFlow.xml" + +- type: guideEntry + id: PlumbingMachines + name: guide-entry-plumbingmachines + text: "/ServerInfo/_StarLight/Guidebook/Engineering/PlumbingMachines.xml" diff --git a/Resources/Prototypes/_StarLight/RPLD/rpld.yml b/Resources/Prototypes/_StarLight/RPLD/rpld.yml new file mode 100644 index 00000000000..c2f254c4b4a --- /dev/null +++ b/Resources/Prototypes/_StarLight/RPLD/rpld.yml @@ -0,0 +1,176 @@ +# ============================================================================= +# RPLD (Rapid Plumbing Device) - RCD prototypes for plumbing placement +# ============================================================================= + +# ----------------------------------------------------------------------------- +# Category: PlumbingDucts +# ----------------------------------------------------------------------------- + +- type: rcd + id: PlumbingRpldDuctStraight + category: PlumbingDucts + sprite: /Textures/_Starlight/Interface/Radial/RPLD/duct_straight.png + mode: ConstructObject + prototype: PlumbingDuctStraight + cost: 1 + delay: 0 + rotation: User + fx: EffectRCDConstruct0 + +- type: rcd + id: PlumbingRpldDuctBend + category: PlumbingDucts + sprite: /Textures/_Starlight/Interface/Radial/RPLD/duct_bend.png + mode: ConstructObject + prototype: PlumbingDuctBend + cost: 1 + delay: 0 + rotation: User + fx: EffectRCDConstruct0 + +- type: rcd + id: PlumbingRpldDuctTJunction + category: PlumbingDucts + sprite: /Textures/_Starlight/Interface/Radial/RPLD/duct_tjunction.png + mode: ConstructObject + prototype: PlumbingDuctTJunction + cost: 1 + delay: 0 + rotation: User + fx: EffectRCDConstruct0 + +- type: rcd + id: PlumbingRpldDuctFourway + category: PlumbingDucts + sprite: /Textures/_Starlight/Interface/Radial/RPLD/duct_fourway.png + mode: ConstructObject + prototype: PlumbingDuctFourway + cost: 1 + delay: 0 + rotation: User + fx: EffectRCDConstruct0 + +# ----------------------------------------------------------------------------- +# Category: PlumbingMachines +# ----------------------------------------------------------------------------- + +- type: rcd + id: PlumbingRpldTank + category: PlumbingMachines + sprite: /Textures/_Starlight/Interface/Radial/RPLD/tank.png + mode: ConstructObject + prototype: PlumbingTank + cost: 2 + delay: 0 + collisionMask: Impassable + rotation: User + fx: EffectRCDConstruct0 + +- type: rcd + id: PlumbingRpldInput + category: PlumbingMachines + sprite: /Textures/_Starlight/Interface/Radial/RPLD/pipe_input.png + mode: ConstructObject + prototype: PlumbingInput + cost: 2 + delay: 0 + collisionMask: Impassable + rotation: User + fx: EffectRCDConstruct0 + +- type: rcd + id: PlumbingRpldOutput + category: PlumbingMachines + sprite: /Textures/_Starlight/Interface/Radial/RPLD/pipe_output.png + mode: ConstructObject + prototype: PlumbingOutput + cost: 2 + delay: 0 + collisionMask: Impassable + rotation: User + fx: EffectRCDConstruct0 + +- type: rcd + id: PlumbingRpldSink + category: PlumbingMachines + sprite: /Textures/_Starlight/Interface/Radial/RPLD/sink.png + mode: ConstructObject + prototype: PlumbingSink + cost: 2 + delay: 0 + collisionMask: Impassable + rotation: User + fx: EffectRCDConstruct0 + +- type: rcd + id: PlumbingRpldReactor + category: PlumbingMachines + sprite: /Textures/_Starlight/Interface/Radial/RPLD/reaction_chamber.png + mode: ConstructObject + prototype: PlumbingReactor + cost: 5 + delay: 0 + collisionMask: Impassable + rotation: User + fx: EffectRCDConstruct0 + +- type: rcd + id: PlumbingRpldPillPress + category: PlumbingMachines + sprite: /Textures/_Starlight/Interface/Radial/RPLD/pill_press.png + mode: ConstructObject + prototype: PlumbingPillPress + cost: 3 + delay: 0 + collisionMask: Impassable + rotation: User + fx: EffectRCDConstruct0 + +- type: rcd + id: PlumbingRpldFilter + category: PlumbingMachines + sprite: /Textures/_Starlight/Interface/Radial/RPLD/filter.png + mode: ConstructObject + prototype: PlumbingFilter + mirrorPrototype: PlumbingFilterFlipped + cost: 3 + delay: 0 + collisionMask: Impassable + rotation: User + fx: EffectRCDConstruct0 + +- type: rcd + id: PlumbingRpldSynthesizer + category: PlumbingMachines + sprite: /Textures/_Starlight/Interface/Radial/RPLD/synthesizer.png + mode: ConstructObject + prototype: PlumbingSynthesizer + cost: 3 + delay: 0 + collisionMask: Impassable + rotation: User + fx: EffectRCDConstruct0 + +- type: rcd + id: PlumbingRpldSmartFridge + category: PlumbingMachines + sprite: /Textures/_Starlight/Interface/Radial/RPLD/smart_fridge.png + mode: ConstructObject + prototype: PlumbingSmartFridge + cost: 5 + delay: 0 + collisionMask: Impassable + rotation: User + fx: EffectRCDConstruct0 + +- type: rcd + id: PlumbingRpldDrain + category: PlumbingMachines + sprite: /Textures/_Starlight/Interface/Radial/RPLD/drain.png + mode: ConstructObject + prototype: PlumbingDrain + cost: 2 + delay: 0 + collisionMask: Impassable + rotation: User + fx: EffectRCDConstruct0 diff --git a/Resources/Prototypes/_StarLight/tags.yml b/Resources/Prototypes/_StarLight/tags.yml index db020dd46a0..2d2598bbdc4 100644 --- a/Resources/Prototypes/_StarLight/tags.yml +++ b/Resources/Prototypes/_StarLight/tags.yml @@ -112,6 +112,9 @@ - type: Tag id: PatchPack +- type: Tag + id: PlumbingDuct + - type: Tag id: PowerCellTiny diff --git a/Resources/ServerInfo/Guidebook/Engineering/Plumbing.xml b/Resources/ServerInfo/Guidebook/Engineering/Plumbing.xml new file mode 100644 index 00000000000..8f0b8b37d8e --- /dev/null +++ b/Resources/ServerInfo/Guidebook/Engineering/Plumbing.xml @@ -0,0 +1,41 @@ + + # Plumbing + Plumbing is a reagent piping system that lets you build fluid supply systems and automated chemical factories. Fluid ducts connect machines together into networks, moving reagents between them without manual intervention. + + + + + + ## Getting Started + Plumbing is built using the [color=#a4885c]RPLD[/color] (Rapid Plumbing Device), which works like an RCD but for plumbing. It can place fluid ducts and all plumbing machines (but not all machines with plumbing connectors). Select what to build from its radial menu. + + All plumbing machines and ducts are [color=#a4885c]rotatable[/color]. Use the rotation key before or after placing to orient inlets and outlets correctly. Machines must be oriented so their connectors line up with adjacent ducts. Most plumbing machines are rotatable while anchored. + + ## Fluid Ducts + Fluid ducts are pipes that carry reagents between machines. They similar to atmos pipes, place them with the RPLD and they automatically connect to adjacent ducts and machines to form a [color=#a4885c]plumbing network[/color]. Ducts hide under floor tiles. There are four variants: + + + + + + + + + ## Connector Colors + Each machine's connectors are color-coded: + - [color=#ff5959]Red[/color] connectors are [bold]inlets[/bold]: the machine pulls reagents from the network through these. + - [color=#5999ff]Blue[/color] connectors are [bold]outlets[/bold]: other machines can pull reagents from this machine through these. + - [color=#59e659]Green[/color] connectors are [bold]mixing inlets[/bold]: specialized inlets for ratio-controlled mixing (pill press). + + Connected connectors appear as smooth joints. Disconnected connectors appear jagged. + + ## Plunger + Most machines with internal buffers are [color=#a4885c]plungeable[/color]. Use a plunger on them to flush their contents onto the floor. Useful for clearing stuck reagents. + + ## Reagent Flow + See [textlink="Reagent Flow" link="PlumbingFlow"] for details on how reagents move through networks, pull mechanics, and distribution. + + ## Machines + See [textlink="Plumbing Machines" link="PlumbingMachines"] for details on each machine. + + diff --git a/Resources/ServerInfo/Guidebook/Engineering/PlumbingFlow.xml b/Resources/ServerInfo/Guidebook/Engineering/PlumbingFlow.xml new file mode 100644 index 00000000000..ee000f73d4e --- /dev/null +++ b/Resources/ServerInfo/Guidebook/Engineering/PlumbingFlow.xml @@ -0,0 +1,41 @@ + + # Reagent Flow + Understanding how reagents move through plumbing networks is key to building efficient factories. + + ## Networks + A [color=#a4885c]plumbing network[/color] is a group of machines and ducts connected together through their connectors. Reagents can only flow between machines on the [bold]same[/bold] network. + + Each [color=#ff5959]inlet[/color] connector on a machine forms its own [bold]independent network[/bold]. This means if a machine has multiple inlet connectors, each one connects to a separate network. Pipes attached to different inlets on the same machine [bold]do not bridge[/bold]. + + [color=#5999ff]Outlet[/color] connectors on the other hand, [bold]share[/bold] a single network. A tank with three outlet connectors merges all three into one network, so any machine pulling from any of those sides sees the same pool of reagents. This can be used to save space and simplify layouts. + + ## Pull-Based Flow + Plumbing uses a [color=#a4885c]pull-based[/color] model, reagents don't flow on their own. A machine with an inlet [bold]actively pulls[/bold] reagents from the outlets of other machines on the same network. If nothing is pulling, nothing moves. + + This means: + - An [color=#a4885c]Input[/color] won't push reagents anywhere by itself. Another machine must pull from it. + - A [color=#a4885c]Synthesizer[/color] generates reagents into its buffer, but they sit there until something pulls. + - A [color=#a4885c]Tank[/color] pulls from its inlet network and stores reagents. Other machines then pull from the tank's outlet network. + + ## Round-Robin Distribution + When a machine pulls reagents and there are [bold]multiple outlets[/bold] on the same network (e.g., two synthesizers feeding a tank), it uses round-robin distribution. Each update tick, it starts pulling from a different outlet, cycling through them fairly. + + ## Transfer Rate + Each machine pulls a fixed amount per update tick ([color=#a4885c]20u[/color] every 2 seconds). This transfer budget is shared across all of the machine's inlets, a machine with four inlet connectors still pulls 20u total per tick, not 20u per inlet. + + ## Reactor: Targeted Pulling + The [color=#a4885c]Reactor[/color] is special, it pulls [bold]specific reagents[/bold] rather than everything. It fills each target reagent fully before moving to the next. If you set targets of 30u Iron and 20u Carbon, it will pull all 30u of Iron first, then pull Carbon. + + ## Filter: Selective Routing + The [color=#a4885c]Filter[/color] has one inlet and two outlets. It pulls everything from the inlet network into its buffer, then when other machines pull from it, the filter controls which reagents are available on which outlet: + - [bold]Filtered outlet[/bold]: Only provides the reagents you've selected. + - [bold]Passthrough outlet[/bold]: Only provides reagents you [italic]haven't[/italic] selected. + + ## Pill Press: Mixing Mode + In [color=#a4885c]mixing mode[/color], the pill press ignores its main inlet and instead pulls from its [color=#59e659]mixing inlets[/color]. Each mixing inlet pulls from its own separate network into a staging area. Once both staging areas have enough reagent, they combine and press a pill at the configured ratio. + + ## Tips + - Place [color=#a4885c]Tanks[/color] between production and consumption to buffer reagents and decouple supply from demand. + - Use the [color=#a4885c]plunger[/color] to clear stuck reagents from any machine's buffer. + - Machines only pull when they have space in their buffer. A full buffer stops pulling until reagents are pulled from it. + diff --git a/Resources/ServerInfo/Guidebook/Engineering/PlumbingMachines.xml b/Resources/ServerInfo/Guidebook/Engineering/PlumbingMachines.xml new file mode 100644 index 00000000000..429e000f507 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/Engineering/PlumbingMachines.xml @@ -0,0 +1,108 @@ + + # Plumbing Machines + All plumbing machines are placed with the RPLD. Machines that require [color=#a4885c]power[/color] must be within range of an APC. + + + + + + + ## Input and Output + + + [color=#999999][italic]Getting reagents into and out of the network[/italic][/color] + + + The [color=#a4885c]Plumbing Input[/color] is a small tank you pour reagents into by hand. Other machines pull from it via the network. Connects in all directions. + + The [color=#a4885c]Plumbing Output[/color] is a small tank you can draw reagents from using a container. It pulls reagents from the network into its buffer. Connects in all directions. + + + + + + + ## Sink and Drain + + + [color=#999999][italic]The plumbing classics.[/italic][/color] + + + The [color=#a4885c]Plumbing Sink[/color] is more or less a combined input and output. Reagents drawn in from its inlets can be used to fill containers from its facet, and dumping reagents down the drain sends them into the network via its outlet. + + The [color=#a4885c]Plumbing Drain[/color] is a floor drain that absorbs puddles and routes them into the plumbing network. Connects in all directions. + + + + + + ## Tank + + + [color=#999999][italic]Buffering reagents in the network[/italic][/color] + + + The [color=#a4885c]Plumbing Tank[/color] stores up to [color=#a4885c]400u[/color] of reagents. Has one inlet and three outlets. Use tanks to buffer reagents between production stages or store large amounts of reagents. + + + + + + ## Filter + + + [color=#999999][italic]Splitting reagents by type[/italic][/color] + + + The [color=#a4885c]Plumbing Filter[/color] separates specific reagents from the network. Set which reagents to filter via its UI. Has one inlet and two outlets, one outlet for filtered reagents and one for everything else. When disabled in the UI all reagents passthrough. Can be flipped via the RPLD. + + + + + + ## Reactor + + + [color=#999999][italic]Triggering chemical reactions automatically[/italic][/color] + + + The [color=#a4885c]Plumbing Reactor[/color] pulls specific reagents from its inlet in configured quantities, heats them, and triggers reactions. Products are moved to the outlet. Has one inlet and one outlet. Set target reagents and quantities via the UI. Requires power. Use a plunger to clear stuck reagents. + + + + + + ## Pill Press + + + [color=#999999][italic]Automated pill and patch production[/italic][/color] + + + The [color=#a4885c]Plumbing Pill Press[/color] pulls reagents and presses them into pills or patches at a configurable dosage. Has one main inlet that pulls from the network normally, plus two optional mixing inlets that allow ratio-controlled mixing — set a percentage for each side to blend reagents from two separate networks. Toggle between pill and patch output via the UI. Requires power. + + + + + + ## Synthesizer + + + [color=#999999][italic]Generating reagents from power[/italic][/color] + + + The [color=#a4885c]Plumbing Synthesizer[/color] generates basic reagents from an internal battery. Select a reagent from the UI and toggle it on. The battery charges while the machine is powered and drains as reagents are synthesized. Different reagents have different power costs. + + Available reagents: Iron, Carbon, Copper, Hydrogen, Oxygen, Sugar, Silicon, Phosphorus. + + + + + + ## Smart Fridge + + + [color=#999999][italic]Storing and dispensing reagents via labeled jugs[/italic][/color] + + + The [color=#a4885c]Plumbing Smart Fridge[/color] pulls all reagents from the network and stores up to [color=#a4885c]200u[/color] of each type. Use a [color=#a4885c]labeled jug[/color] on the fridge to fill it with the matching reagent. Connects in all directions. Requires power. + diff --git a/Resources/ServerInfo/Guidebook/Medical/Chemist.xml b/Resources/ServerInfo/Guidebook/Medical/Chemist.xml index 6712e15dcea..217f7418439 100644 --- a/Resources/ServerInfo/Guidebook/Medical/Chemist.xml +++ b/Resources/ServerInfo/Guidebook/Medical/Chemist.xml @@ -6,6 +6,8 @@ While chemists primarily make medications, there's a very wide variety of chemic [textlink="Click here to see a complete list of chemicals." link="Chemicals"] +For automated chemical production, see [textlink="Plumbing" link="Plumbing"]. + ## Equipment: diff --git a/Resources/Textures/_Starlight/Interface/Radial/RPLD/category_ducts.png b/Resources/Textures/_Starlight/Interface/Radial/RPLD/category_ducts.png new file mode 100644 index 0000000000000000000000000000000000000000..cb7b16d6565f4ee261a090c05bffce27004f01f0 GIT binary patch literal 361 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU^MV_aSW-L^Y+HZ-WCG^)(huf z6m70m5K8h=P<1wO@bY3_V$$@YK}dl20z=1yj;a2;nD)G$Jn`B76D-DNVr`NPN>K2t z-uLpI(9pYObDsa4yX2DEQ8nq7kpxC-vYMFbEkuUHx3vIVCg!02G#-fdBvi literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Interface/Radial/RPLD/category_machines.png b/Resources/Textures/_Starlight/Interface/Radial/RPLD/category_machines.png new file mode 100644 index 0000000000000000000000000000000000000000..ec46d796e70c247581611358268c05660d95796d GIT binary patch literal 599 zcmV-d0;v6oP)l1O8bHU^{7#wb{*1#K)e zZ5oNFa+;V<-f@?rZD_sMyY~B8-u-#McklbnukZb?M+5)>000000000`Vn~H}Xk%lC zn(1CRd@giOav?;!Tuo{F)YMe7Mips>M1sUK(M(`G{*+C;hTiAq=8n4wGMQ97yXJO( zWCTwWY-CdL`Z&b2lLV7^8uKp}fSics7n@Gr;fP zHcZGxyw~gHIzNf$;N9a#YT{yr?DldR8=Iug-gfP}XLT*3_|%Ni#aES2(Pd~}vZMhO zwUzX8x=ap-!w@{XxS+LX$APpNqmxX&k+XM`lt6%+0Wt}oVZ=wu!IE*VXFwv6$QsAQ z7tOms{iL5N>poL{V>n|!XaAOQoP2mK9s|Cc!LZ-|T=_dB%&Tw7o2LwccPJ7gv-zbcBM}KkkVT l4FCWD00000002Ox%saM24mEqn#bE#d002ovPDHLkV1mH@5m5jD literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Interface/Radial/RPLD/disposal.png b/Resources/Textures/_Starlight/Interface/Radial/RPLD/disposal.png new file mode 100644 index 0000000000000000000000000000000000000000..9d70555538bc278a64ad6ac72c8af49d522b363a GIT binary patch literal 389 zcmV;00eb$4P)~Tj+w6q3{Ml7xh}#O4xEn>HkSludb!b z*{R)H1ONa400000a5)OkWXt7qXYpcjb@o}?{v6}o?#=DK3Xde~^`^5Tt?GDs@2y}Hx19!O(@i}HK^*DK#)WP_-uu`4hsVAxeJVVZl8F|7 zeRJDiHV`?Ka^cEciS@Dn-hcI%#<>K~Wa%Op7jA9Z$8#+RlEKh|m+lH)LXKTPRn?39 z2f;I0{A+J)#%KQ(*hYK*P^{acr{J3HQ0!lE{FKac0NO)=y8YQ~$FaBInXIawj&akR z7Q{IAR?3A7Qzy`w9ozEa_OU*7Et4ud4nfkxkMw_k9H)JzJ{&2;`6S5b!WSD_1U*lN j0000000000koo%scR7Zm{4WJX00000NkvXXu0mjfJ94#d literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Interface/Radial/RPLD/drain.png b/Resources/Textures/_Starlight/Interface/Radial/RPLD/drain.png new file mode 100644 index 0000000000000000000000000000000000000000..b939e31b8b1587b687421a3216151dee7f7edbaa GIT binary patch literal 874 zcmV-w1C{)VP)4Tx0C=2zkvmAkKpe)uKBOWQ2Qx@<$WWau_(B||ibb$c+6t{Ym|XgTCJjl7 zi=*ILaPYBMb#QUk)xlK|1Ro%dPELw0QsV!TLW>yhIPS-H|L^1Oe}KPPWvUq*2UN{6 z(uug3%dd)|R|Fx@oIpfkrk>0!X5l%$?&0J6U5saW*Zn#Am4e9tpGZ8%bi*RvAfDN@ zbk6(40xL@j@j3CBK^G)`o)_Ex7y&}NK&xTf-^aGyIspRDz?IhV*P6iWC+Urj z7Ciz6wtA*K5QiP1Dly*=)9R8wKP{-H{&*23zH;)yl?Br-K-EyWL{ZX4jb@ zAZL6VzP-NK`2O*UnEiZz+1S6i^=dC5XX+ZbYu+9C;c&QBKbcHye7L`}@$u=obev+n z>(}-;^ZDGyv-1o15Rgl%DzYrIch6q0XP1}DW$76Z8+Qg2*L$&8=u=>?r-0+Gxni77 zr#6nq z6#=;<^lR|?>M|(faxBG+m3|F+5|9&+Goc+^F3#NpV#x=%_0oJmAcKZ9A8;ZBjf% A-v9sr literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Interface/Radial/RPLD/duct_bend.png b/Resources/Textures/_Starlight/Interface/Radial/RPLD/duct_bend.png new file mode 100644 index 0000000000000000000000000000000000000000..67ea1e04eed9b09fe120c470fc84c9391a8f09a7 GIT binary patch literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=H#}V&Ln`LHy|I?J)j*>4;bC!` z4IFIe*&<{T8Fjc5Hu@Zt$~nN+)A)*4I>uwN(=WSaruSd^&r(~<0@MrzKm0G7_{mTG z!J&Ej($&(xJL)3E^gmZ9eHEOPI&)cORY5IVadVsQ%is5UA6Kk?XX76l zx^zO2ykhM2ov%gKyslckE;qV$+AofsV)frS_z!>R>b_jl(z54B+(yRuw5~atMaxdi zR7!_46xruLk6fs_&@Q0w@r8ZwPhLI$`On2_CWw=Ey=ND5);^K-tK}$2z|+;wWt~$( F69AxeaVG!( literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Interface/Radial/RPLD/duct_fourway.png b/Resources/Textures/_Starlight/Interface/Radial/RPLD/duct_fourway.png new file mode 100644 index 0000000000000000000000000000000000000000..cb7b16d6565f4ee261a090c05bffce27004f01f0 GIT binary patch literal 361 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU^MV_aSW-L^Y+HZ-WCG^)(huf z6m70m5K8h=P<1wO@bY3_V$$@YK}dl20z=1yj;a2;nD)G$Jn`B76D-DNVr`NPN>K2t z-uLpI(9pYObDsa4yX2DEQ8nq7kpxC-vYMFbEkuUHx3vIVCg!02G#-fdBvi literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Interface/Radial/RPLD/duct_straight.png b/Resources/Textures/_Starlight/Interface/Radial/RPLD/duct_straight.png new file mode 100644 index 0000000000000000000000000000000000000000..a8ee8fba3ffdc87743f4d30b0dea0b3e10eed8b8 GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=Ydu{YLn`LHy=f@e;vnF1u|Ah! z$^!;YZASJbD->KM`3xFb_ul$kVCwk2yK2$AStSZeQ1CDR{`Y;k(ejnC*Z1jo=db;n zwpw}1wO_yY)jnBe>$32%#R|1EoW+Oc7^o9Nt=#m-#(!05 z1^h;@6jYr}9K5`MNWH{?Q;au&QAy?2wABl3r_b`pWF22njIs=wLgEezSVc0_B(h}CrH54)z4*}Q$iB}c8i3s literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Interface/Radial/RPLD/filter.png b/Resources/Textures/_Starlight/Interface/Radial/RPLD/filter.png new file mode 100644 index 0000000000000000000000000000000000000000..9f933c7e58dfe66bafd9d05c12b5c448d3908acb GIT binary patch literal 381 zcmV-@0fPRCP)J*lIS^NJ% zShI#?-xfBkmfx#!t`+QndEb?k#G%hi!qKY{o*RX{5QZibzerWbJRzx9+tZjEiQ;}kjp&%EFi(MIY0<3MNdU%pt z6{&*HVYw$yKo!iqzYgoUB*SP7>Vx;+{n0-M%-v(>+LxSyPqGPyN$|qpbJ!P5ib298 zgfqY26M9LS#2(;Pb+_I$H!_I?RK4c{&6Q9jAMwDxF!bK2{|n4}zYtoC+Q`a*M^8Xm zuB2)1PS&4ua$X6rwmsPa$9DTEXL%^PT;2{Ic7D9Sb?TQ_*Mt3RHZM^>CjbBd00000 b$W(j-!rpOnRG}BP00000NkvXXu0mjfX~L=M literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Interface/Radial/RPLD/pill_press.png b/Resources/Textures/_Starlight/Interface/Radial/RPLD/pill_press.png new file mode 100644 index 0000000000000000000000000000000000000000..5b437bf6960edcfdf9d06427ea25dfbe989f3f7c GIT binary patch literal 507 zcmVYE90%}Uk8=Z{ed z)OMeeG&0THpZjo}HW@~7*y&uSVi$l}+4jv?*|PGao1lovW>?*Vi=c!PlyHI)PEf*0 ziV*eko5!a!ashChHa$Mvht_Xx@3;@H+Kjg22!N?ayxmP)UrO&1(S>_{TrLwg z$uUT;_ev|v-opmhb5DKk0Wi%RS=J%h_LW;s7YddO*Ef zXQEwp&!o*k-VIg|b-h-r#cwxg7{#HN(ta(0lhC&P_xjZISIwqIqaN?gQ2LikKm}_w zaO&K@Ns46v_NOd?Zu6)7^4xWg?|RJ}BS}n}r@`y^$yd&-t&hzB-6Tb93H;j`usJvn zt?T--LQaArPAvmnzr)yOfcmu+_MP{roM90p#~@x(6lV0($4yYeNsdAMZ}uRIngNny x5bihoECeN-)Y&;lCIZ||& literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Interface/Radial/RPLD/pipe_input.png b/Resources/Textures/_Starlight/Interface/Radial/RPLD/pipe_input.png new file mode 100644 index 0000000000000000000000000000000000000000..a7bb070a4993752b92c5e16995fb068c9dc74510 GIT binary patch literal 416 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU`+RPaSW-L^Y;2iFJ?oD10UC? z9~Zl_u!7@jTa&Zw8WEPlqK~3??$)xf%R3%aFL?1Fd4)oVf5?iaW)ZKY&Z4(Mwc?+8 zDd)N#bb0)Ldy7iB%KhmvCpv+~gTcT4DXSa9xBFF1{&imab=b3MV(eYr?0a`U{pq;3 za8K?Rf6;Gi&Z_G2HJzX0ZRZ*uhINe()fAIx7T_376J?fs4k*$T0LRel8C-}{~O#j%ek zrk3v&X1dM%>Xf=;wq>4rO_~3X&72(9zp*=JuPmL;B;7pawDhqoHOGGLxbxh9Q|$IV zKg#E^Bd&7w_Xdrq3AfJvcCK}dU}&g#!nAt5OFm1wowr0xk}I3BN)ICucjmIru?z5Q-5+q#y_dY3ZPa z7Mx1twYH=|BNe>J_5Xu#4VQ4ecQ5Y}5dZ)H00000!2EDb4YYlD87B2#U+fzDFHs97 z2OB(9r_OT+XOrKhpcXzfj*JJvH91(P)15k^GUc+AinYWn`3sJzf%Z?HLvN$-ZU5c* z)v)acftbt$$J9W&y1L5x80hNzJEv3qE1NlK-@X_Rf-C=k21O^YL9n_Aer22yo>ZkR z$D>Tpj)sW!88G;qywY}@3Hk__;->iy5UmB;#&-tmGr*4(!uYur0jN~=LQQGi0cO2V z=4BV~Q&@RpyFj6^Dv$T0yi=eI)7TbvX)o&3_WpV$`H z)`!UU%0hpCF0mx?jUl+4pzm>`-G5`Po00000006{7A5;M1& literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Interface/Radial/RPLD/reaction_chamber.png b/Resources/Textures/_Starlight/Interface/Radial/RPLD/reaction_chamber.png new file mode 100644 index 0000000000000000000000000000000000000000..b2127e648b4e491eb186c27144b52d38d0f76688 GIT binary patch literal 593 zcmV-X0CG!A6L_Kxleule<0_2?>X;!&b{7y&LaW<0000000000004l0gvZIr zB9WNcik_|~9s6CHlg@`iV=ZdOOs9gNkwdWa(dcr^`npc!ssN7|n;M;xP z`pype{eul{#8^FBRcNFbcA%EOg3!#is}mt#-=-z`5Ik5Z(NcfGmf~O(I>m%SX)^YU?_{!~3k@kP3zU(}Yxn>7c48vY+mi%30dBRc7 z2t`&X`dFu)ttvdqo=im3;bXg;eaLQGbBWSePJ89Hs2uCrx(c84j-d8^0RR9100000 f00000fK>4fZI1BHR;K1v00000NkvXXu0mjfAgC3r literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Interface/Radial/RPLD/sink.png b/Resources/Textures/_Starlight/Interface/Radial/RPLD/sink.png new file mode 100644 index 0000000000000000000000000000000000000000..2728aafa2761558152887903080770ed91371532 GIT binary patch literal 701 zcmV;u0z&cs$YlI7LDc)JhBxW9LHNU^9R-Hg9T7~_d&=vuXcs}{_{-8^3$@kQ;waxf6DH) zvTF)?T>oU);R*tXMNgyI$W#d&9EWAi{M+ij&}&CWMkIgDM{C<0sdLv6^0;0%;?~~F zO7YjF|1^U8z|cQ`loj&j+gBkQXLgmq!Ero$7iKI%-{xqw7#;}VT&uPMnz%!(eP*@ zbHc%KJX?p^|G4b$4+f?kTBC>Re1KtSi_jIZqACHO2$PD;2nWaUY#sjIwp5E5Lca}q zkzA{*Dq}i>8unzy04Sk4j>h-p2@>-snU@_L9LKYDh><+lmv@1@m~!cy8xD@+Wpz9` zJryzxL)C*X`ja!YRw}6$y5}qV@IIWF*+fSVN-AE)gCICx0W9192Jmv{g~-Lm=9ZD7 zBxC7@_#F<155_GN3PzwL<3^P9?tgx9N#6sqz+gdiC5#e1d0(D#b(lJ%)c1r`Ao=9{ z;un1r%(VjMWoz@fNZI!GGoe>16(OT260+Ov3VC()TgcYU^-|`>!Erb^4hP5amNii8 z<{HNLfLV6Sf`j95a2yVf+y2)dz#ks547+K9QJ3edWjz)PO&L>Nt+pZsGb+#p{1K^%d&z{(*}1@!v*K2cMz- znakHkP~Y!E9VNNc{LJ3xkc3Zygm}jP+uS1o?k8O>00Z(5MVjd zWH3BsGHKC^|17BkQYoqZ&70#@d+9R_4EHL$45>7ynO?; zv-9za?Kuj({?fjx*O|MWs#@>m;aWY<2(TP!GT^w+lu9ZLl<3(#h=xdE88tFZaXxf^ z8kPOdpP*}t0p;(yX->F2M|pq=0hS|82BoqQKzb{9j}9F53{dRX zy*`B+dbPWz8+E_y>oq3`upDVJD7r>KStjRGWxtQ~Q9*mMdDk4UR^ARAri1hv8`4x{ zM1bW;lfl^c8P}N!{M&A5cVoB5+TfSOnlizzuFCnvdLh7aq{*PY{jpVb#2Fvsl^J?6 zuV;e2hhBtwAi#2@$)I&-t5$YYTu-Odn&)4Og;4{3s{dyAFVv&Qj-l@Pf_8j{Bdf~; z&VKMNtOr7nkI!GYRJk53N169`sWe5e{rjq~LIqfkG#U6C z=m|4L61GVQjIyPJJ{M9R0R&^upOpraC z%jHlzIyz9p;jq>}!*T?W<*Nq*EJxOw33B{zdtnc1>gPEv0}MPJMRk+| zvUh)2Qzlq11XzwV8FcpXK0lFPCYL5EFX~n(ah&&2y!P#+_+M08ieC zJ3k!9+4I?Kw&GfXSA9GZI72icz;dL?V59fsyjWVI=o62}|5raeGCb?Sa->&9z4qk1 z`26CRb-9iJ%W>*e2+(@*jO+cHP}3C9~a4-w#6G%)EKC@6FEJnIRGcK@bE%5ClOG z1VIo4;eWzPYM^qZ=IQ0u{tI7wVd;&p{CQ_Nk!KrAVvxDnEpKYRNc@BIi;Iuw;%7_! zAfry#L7ya^%{Dy%9LYJ3uMSSv>qJjhS5>)QKT>r%=rc!K-nYhcE7Ji8W98*O?QJg6 zZnfpB`xn?r4U~Ua@|7z)=dt?jFK09o0mwOyTGPkxKB@nHfb-C8qtM8B0J?{V*NzN0 ztI$Q?kyrxV1RVKWtRx1xlPh@0_|4`iHGX`jiOIsPtj~3`_a9KHbdPqcRqAwG)=0bo z6O#p>oX_9Ar4y`!UZ?91QYJS-3kxeAHaa}}05hyR3>kF*I?zQQ^v!Gl%9WZI-2Upg z?q#SvsGU>>pj@e`t?%!Xjo!>M`pjdN@e6AM=nEULWhw|`CXC{tb?{1Kmi1f^TmlSq zpo>0nHd7mb>FLMb_{?iMd%vz`kev@Z@rA)U90b^aE!c$Z)Cb@q@OfkUasE%bQMOb4 z6>#0lft+X1CoVarHUOQQ%}epwTXoEZBYei!;pM@vxen{2FKob;ZhM$mWB`1>`Ju!1 z`22O?M8`7LdDMXkdEnq+&&o^^i~|qa#HVt5zI{%xno5cNqAP9mW2!bF8f*=TjAkb)Y1p~$}>*V|R Q-2eap07*qoM6N<$g30$_@c;k- literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Interface/Radial/RPLD/tank.png b/Resources/Textures/_Starlight/Interface/Radial/RPLD/tank.png new file mode 100644 index 0000000000000000000000000000000000000000..ec46d796e70c247581611358268c05660d95796d GIT binary patch literal 599 zcmV-d0;v6oP)l1O8bHU^{7#wb{*1#K)e zZ5oNFa+;V<-f@?rZD_sMyY~B8-u-#McklbnukZb?M+5)>000000000`Vn~H}Xk%lC zn(1CRd@giOav?;!Tuo{F)YMe7Mips>M1sUK(M(`G{*+C;hTiAq=8n4wGMQ97yXJO( zWCTwWY-CdL`Z&b2lLV7^8uKp}fSics7n@Gr;fP zHcZGxyw~gHIzNf$;N9a#YT{yr?DldR8=Iug-gfP}XLT*3_|%Ni#aES2(Pd~}vZMhO zwUzX8x=ap-!w@{XxS+LX$APpNqmxX&k+XM`lt6%+0Wt}oVZ=wu!IE*VXFwv6$QsAQ z7tOms{iL5N>poL{V>n|!XaAOQoP2mK9s|Cc!LZ-|T=_dB%&Tw7o2LwccPJ7gv-zbcBM}KkkVT l4FCWD00000002Ox%saM24mEqn#bE#d002ovPDHLkV1mH@5m5jD literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Interface/Radial/RPLD/tap_output.png b/Resources/Textures/_Starlight/Interface/Radial/RPLD/tap_output.png new file mode 100644 index 0000000000000000000000000000000000000000..8dc7bfdb903c4e7302c409f6850bbf12e4ddea7a GIT binary patch literal 441 zcmV;q0Y?6bP)aPND#@I~Ax6m%)n2hfEzv|bj1RM3`Q zXhn;uxROYwSQUe`LNjIN|28?9%y5zuauPxW0000000000a#46DPelB-5tv|cruayy zT*>qE(TMZKFB$&t!?xE&(1^41Oo?lLl~>|I?WL_TVGHp2+`Gln6rdNVBsrBNr+ATM zdGo|}8-Go$)AZzUs{sxqj$6mhlB3~@MN!=Efa2j6^%BjCB+i}n zMZ+xaH$#+8Up5bn%uu0r%fgj?>*u1RKn2L{9#JJ0WAXd_13f>LX<_4_xu4snYPC*R zd#A1Wjck^@qLSn^-Y3u(*&#i`FTdLD1o8Nq?d7^osU$hA+X=$q5ZzpVH8$(5-d3Y? z)|sXNUxLA)Vf%cetkXN3H^A7q{^xdfZl2B0=QD~h1?ZG81sEFC|J}^2Mw#nBO8@`> j0000000000020XucpH4_!C^!V00000NkvXXu0mjfIkL_@ literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Objects/Tools/rpld.rsi/icon.png b/Resources/Textures/_Starlight/Objects/Tools/rpld.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6b420de2de1177219996381c448cc07d47bb32d5 GIT binary patch literal 711 zcmV;&0yzDNP)tN~y%nN~O}WHmE=^Qz_&Ce9UB!Qxw!{HD;}k@o@zF{>DN{Qv1_sc9bqfL;RVP zBvqT3JwL~jjSc)LmvMS<0Q=1ij88EzKMyIHY-}nB`b+s8E@LsSDWkIv?Z#QJm#yvJ z-Nn1PIeaY^u@0aXjhZ%*vRPe-CYaXU2jp5Moi@4ZU`rJRef7g9W@hkdYm0-_UQeZP z)^q@EL@!;w8wVqoe6N9l_~o_3_9Ex9R2@p4VL|?(f6r^Oa<6vp3aOrhmviU)&M zN52K!d|vKaWo1QvLJY(o93B009NN(j{cFb~o@_6dwTd@V0saFJ&j9S&=;$c*tgh1D z{yyOqHZw3FzkisWwQ8UEvokZaUoK0*`0lQ+Q=w2GR1+59)9$X3T!o3CNVd0Fl;R-* zVjv3QAhI6y#A7_-m=X_+1+IN2;Ip<8dUtlHzrUYs+qTetadAPHi;K6!1GJmYPnezn zA|U?r#6-39Baz6hKKL8g(T;xT-wN@dC6nM++^?%E3Uzl&vGI8PuX)$j*1Y56WAgiD zGMSd-7HGvJz8I9dU*31Yb=(%27VSL4Fbu;m48t%C!!Qh^i&pyrI_{J>S}JKhzi)K0 z8}$ze-AS471>A>!VpL0`{sEyoDf~Mdr$VNypk93V2i}B2^lWlcx>VY-0CUay0#tDv z>f6|;?zDe9H0mD+=)V7PlG}_gKo!LQmdnYV#=JO4t#ozCHXNa=B6KB$GyP_K0jlWs z4_KYx3sA*`e?U+N_ySZh;UBQ5jlKX?O!@}|wb>V-iYfnqMQ!p0G!n<8e?U+heF3VN v^bc6nW?z6Rru+i|9YO9x#TTH8DgVG9B3oT+0>&U=00000NkvXXu0mjf9e0^a literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Objects/Tools/rpld.rsi/inhand-right.png b/Resources/Textures/_Starlight/Objects/Tools/rpld.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..4e7c93af95925961dd1fac9ac00c1f1e0aea1500 GIT binary patch literal 865 zcmV-n1D^beP)6vy8k8KPzqwGt_c4%J04n?pwl?I=O0OS%a~2^lS&8|ZQbr6L$KP#mOnECeA0 z+DXBHOG`yKQxK}bL1N5ezViRzLXsMri#-j&~YE0Mg!vm1N1$cmFpi96&4rCt$f|sphuIF^y}z|^xIn*Z`m-YoXtud zwkn`lEQ&XVA%2{k$SMGsC@?)uZsclol%8#FO7Xjigp3pMN-`OO4(NfdyUy3Dci!*r zinG&GdcCkfFAIgr=g8*cQi%-1s5#zE#7iY{v9?Cnxg7oUdgb!9*z)=4?fN?ThllCa z?5x(X0@4Qu@*s#u7E$(iFQOV;PQ>fg()qmHM!ePWLl1O8Up<<2rwq=_&|W?-Pr!C@ zWra-Bl-b0DpLcd#d-dzVjT`qaRPD&f2z4(lQEG2b{#4HN^~vpz!JyPrserr0@k0mnKo|5uXEjXQ`^ z2n1wFVDQWMxIDNM!~^Jn9_X@4A9^h`Ya~t89T@StlRy=sC z>`@uM)zylN+&1cZ6_h8y^5Bp6_oWSuvd4PFRcXe9e<+y0T-B?fJONh^^x#{iQLh3% zbLu-Z6&`#msOnYFRe12NpsH6vQ{lneL3OW!uEK-2i^^UFO@#+za2rp4Hgd@9)@nH*Yh$;==3s2d`{qtlBFV zwpuiahmHC7hn-Rn>-Hy`&1StCx&He36C0CfKmHgI68hAcncMnjhKZBjwI7Dh@4qdZ z;kW$WyWbZXKL5OPJ9xwW_sx7Fw?3`^^7V*o`EB{w1nD0f%l8<^sV=>JvwQ!#*Zco7 z?SH8HZT;5w3`mZGxG`8OI^^rSt{a70&MIChjg8oO|GoQBVR?D^Kpm0omOR@1$CLAa zG8TQFb#CWz-{`)EoVo)_hUO36NjYf@ literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/ductConnector.png b/Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/ductConnector.png new file mode 100644 index 0000000000000000000000000000000000000000..597a7f2fe58f250b838a3e058938dfb67e6b46bc GIT binary patch literal 386 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1quc4dR|Ijv*CsZ*SXnwHSyT_-MX6xT7~AIW8+n__N#h+TI`)=`*rWM_uI@u*PQudz;ioc_qyMA{R8&Bm%k^kExaH)b5>j8 zj=A68|E^o@fBDOYe-V|er#9WutNrwAvvZ(G)V}?*?2ozHH~dnJ`FuTH6lB!{1||M` VJ5w!uYyYQ#_@1tQF6*2Ung9?(qZj}H literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/ductFourway.png b/Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/ductFourway.png new file mode 100644 index 0000000000000000000000000000000000000000..e920791d8909e66d7779783ad5508bedc613195a GIT binary patch literal 597 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEV3P54aSW-L^LCc)xxB-h z*k1i7nRzR=!Qz~H#?F{$@4rj8A3oT7{BhDe70z?dH(#&X_iO$2)fZ&=+AAmjviI~+ zoBZcv#S=64!!wp;M)N8D;_*z{8l`Bq&{E(LI=sMU?PsR@r?#ed<^7Eh>-PWm(nvYD z|7h3DNi%+(V_W^gZ~5fu9})}>eEzvcW_yHAU1^rIDk$%Vnw)z4*}Q$iB}dX^C( literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/ductStraight.png b/Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/ductStraight.png new file mode 100644 index 0000000000000000000000000000000000000000..00033a3df21c01a46f133ad8d2b8071fb07928a1 GIT binary patch literal 386 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEVD$8KaSW-L^Y+%kvrP^nEDsVN z#ab_5w%)+v#CnWv&#i{=btVZK2l=)Dg%}r$et14H!KV49>Hjn3H{PVqobKRJVdD^E z`1C^E{`c|g#dodKHk*BRgZ^ou=btMteO>mb^57L?rpW7mX^=lpM9;;-F7>7zR~?NVDPVX)v8TByZ$--Q=0Z;o1g7_ z>BHw1&-vLMv1n$2ja=}vU<5_v1gHswl-DXJyhAo4qtDnm{r-UW| DRd}%t literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/ductTJunction.png b/Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/ductTJunction.png new file mode 100644 index 0000000000000000000000000000000000000000..199b78590864b8a78df446e4d9210dbc5c562c2a GIT binary patch literal 588 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU=sFpaSW-L^LCbP-XRBp*7@@% z#5DOo;8u{z>T7uMLO{I0+uPCmG^>EDu6e_(V~?0(7@R$yX6RTdz1&l_M^S%%i>b)w z{H^u3Zb~}LVaWHGr1JCe#{+-sjz28mU~6`iJ6@RR+^XXF>Gs=c{l_2Q$+=$EVV=G* z;>suWBZ_EnVeK2Mo7+CT{(5zJ-Eq6?ufKY?-#XAVbCTMZ)xW0ylKow>J9bT&w)#Vj zt_i7;sr!G{{L5UPDRrc9&h9&}=bu+!b~#gZ{@1F%l9p`iPjAR#_o=avc~rF1qI|)X zdwJV4vOn~mOli9OQba^M!EE-^ZCu}e*DiHCHF5o`^o_fZelYv|ZU6Pxi92HsT^DOP z{m*#*GRhwW|!S9n6vNm=Y3l_3pvo>3-24{ zwGN)kSGL6f6}%Fbl5EiEy72yc_x%1}4BqFfWgB!(7ud}gY+E?}S3{A|p1ei_p1oJu zBWjyYzxqAVI3f6>?S(X3-^+)}D*ba_ol&s)xQn&MZkf;{0W`QFkJ&DpW!440S6A3? q$llXhcRK0ywsl+nf91^R!lvI6;>1s;*b3=DjSL74G){)!Z!;6YCp$B+p3w^ujvwixiR1{8A(eCxn*Dm$ zvSYHxBrZgWvnpIRxog*Av3Xs#U7vFCGkyJ-u1n&J82l$O2sto$G%R9JY}x!MIriX> sqIbXfZf^^X&|b!lvI6;>1s;*b3=DjSL74G){)!Z!23AiO$B+p3x6=&yTpUE&=I>>ArzmA=e&N*- z)-6%u7j`uYcSu__?P%Z@SekXs{@As&oE)>tc*B2^iT^70$p2NXoY!sQ%UUAQcIbPp z;PH)f?Ad%0E?3S2YQFYtX`h>j>%u9P+j(l3%?_~LV33}`Ciniab^7mZ1`0llG@7m` zc?x~9=|8n8MEiQ=^{KO#ZCt{r5_Nmqrxv|+uP<%V$ts(xb9%+=U56PQBufsb-ZtZE zYB*L1RJ>x9S00}(d&=(I+Y_ShXgc2iZo+f;BZJQ;nd@)N4gbko#Mk^3y;AyD^JwrK z)dy}zKz9GgXXco?TViX%myemVQf4?<_c?QHj^FZSw!mJ-52-1Ow(kh+1cnlWr>mdK II;Vst0C5D9EC2ui literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/storageStraight.png b/Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts.rsi/storageStraight.png new file mode 100644 index 0000000000000000000000000000000000000000..5806fd083c4d97e954c0b0387d3b9e25c79aec4d GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSL74G){)!Z!V2h`VV@QPi+v|>k4GIFS505fUXkl|vVB*~| zsY&}}h6AJ4n$73L7-VJB@6|ti=(vCNm(xY(<^(0(RAl*fuej&quKm*#o-8i*o3^%p zy7iYktEMc<-XGnd&IB|X3G859vGvuh^DlNz`6JwCxZ-!lvI6;>1s;*b3=DjSL74G){)!Z!;8RZ*$B+p3w^KLrwixiZp4An&!F(~`Fx!@r z4%Up!NCjPK1I8p~HwT>qObTAB3d|n9a!}cEX#IvdA=RH<%F+^Ti4xEBJtnzi{*-VH zyfBxqO@Sjs=D3D@a%_0UtYeDIfg&rn(>FVdQ&MBb@007{7&j0`b literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/bottler.png b/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/bottler.png new file mode 100644 index 0000000000000000000000000000000000000000..f902a47a5a21eaa70d27402c1f508c77893deeb9 GIT binary patch literal 1993 zcmY+F2~?8V9>6~YK}f+Dwv2mC9<-F~n5kueW`#2*Xxe1v($v$MGEK)F1I^OpxJ0Ir z89Gm2Gc9f-E>BE+F65Ri7CD%rqC^aeTcQZO&*p7&&$;)1m+#(l{=f74pYJBs_oxxd z6a@f)kvG}PAB+sJml?ppv6FR90|sOaIVcVQ(4FfYa%UIX3;+g%7(vyDCW#0+fi4LV($ zAx54u$gndJdSWI1u!fUv*ZPLNT)h^%0>2e6`nWJ>>Fem=VonrY2>0=3q_ z_nvs*qAAtrauro!j?{!81ZmaiW9Z6j_l5Sy)6+|%?@zKHb=W=M54?^hCDrUUy`N$J zR|`Pq!dO(@CbGHQw=t=4zdA4O9&GVTKW;2Al++s((xBuG8#bI339Hil?=>zOK6i$? z>67M-3W~nWlhZ6nr&vzbLc>hJl^hF~+)FnG^1Jpx-Krqd{cIzM_t_uk7*o!a6j0weggh$&C?hvtCTq%7^p4$&%Up8;G>XMf0fL4i1>BZOeCF zz0X%f24Rkj5Qk!{@=~9X>8s4YUC`Mw4xBNVBpxrM&%ZzV%`-&MniGOSNbq@aEZ1`R zx7OOYaMyQowru*V1$8&U; zc4kp%bu%J8?jlFJ8uAX}>iRM3#_ijf%zRnbo0CBqx^m*N6}%Qapvw>oOO+1=J{O+Y zw{rwzva{mh%0ZucNAJ)g`$Nja<-tvitjNebi*E^oX;ksxEXNM0zgJPAiCt3H&IOA^ zf2T*rn~vpq>2UYU%&Tv8w7KehAO`g0i7OQ@)omtP^^Im%(|kf5Rd14bahGa~G@1*) zn_W5cVfb;@wGKu7VDDiAElumoAj(GEsx{-1*|zQ?1!@<3x1Y-WBR08zp0!C<6BN<{6fPZ<>JF?3Pkq8aHCl*11u zn;>a=?Fli|^gu@yEJGpgM%jkE-WY{Q+*N~mhtK=!CL5m z$3?#=5>f+RRb3Buid2LlMf_;vI7*@5Svl{5ln_3G z(N;oK0lk`5DfD;Z>KWCnM#NMKpCj(BD!3g{P`VN#<^+qOw#(r4=C;N#)b*rHz;=sX z*=Ykt>^7kH`uaj#8&G`caoK()(ir$w-+3Hs4HW$_lu2O#Cm#i43F*S681KMxQ`{-E z(M~D`!^dqE;ko8J)6j-|T==cVB_({FdxPbuK8JDGmr3f06VF(xVCHpbK&X*N=@TFr z4nGIOPu8WF(Ur)V>~uBC&L1VdoN{`VWJG1LkCpRN11g6^AvzvKiWNnT}57WNKTNM4(I+4pSF**|-_ zIRPBfWh3j57a*eoeJ^sclbIO%TyHy>r4ZvmT(Ky|j%|lh;Gf=QQp8K~fD$><)s3iW zr5`K?nIzOrKnt2?!lEZJ1c=o-y!E86QTT~#Pgj5)Jy+dTFn7UNijnnjK7vzrjzGGj zo)gR=bLIzoapHOUT=#*9nFs2ALEKngcbz%?(SRoIQ$wnYdb6E!qIM$ur$e4A6S6A z?q+UYYX{|nSOHo??Ydvft7@R2<^Kxv4@7PO%~W(CzPhixpoQm<#fm}27B@~&?`I@Q zqTK2s1PBw{O=!oxG`h(m=sW-LPn>ceutSN2ZSz|%>Wl*0pDk&64}qAGw$T$}tWt@5 zvI3NE1fW!atEX}TM93J?TQ zj!7`F@6Uv>5QFiIZIvKO09Xu@VU_TlB0Mwh1CPp8`==4IP9S}8KeJYFq;1qzuaN~5LMn?dW zRc!nSXyb`&98F^YwDCkX-6bOcnBaE3@M@{8D=mO9TyZ-|%7&!2Q2-NC0zhT5k)wWD z3}Y<;;&?9{Ip_uSQ@b9ck`mzkcT@fep`h{LQh(bL;M0X3&x_MU|KEfbzyg@o3%X^J U$L+4_q5uE@07*qoM6N<$f>BMHzyJUM literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/disposal_working.png b/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/disposal_working.png new file mode 100644 index 0000000000000000000000000000000000000000..994262ec863209d16ec466bcd43f026e61fb023b GIT binary patch literal 1036 zcmV+n1oQieP)u(GtL82j`Xemv{0|e-1cMJIfVQmfBd37vh5C%AJ^KKUy_$J_IhbN(05&1)wpeRe zhx))PwR7Gkw9+VOn^3ZZ^?L)x;BED-o&X4;6VbY~NK)VG6ab~tV(7hUL$%>Q1(MV^ zUIL1Cdv{tqC zr-`=xGCHB~%m3b^BjQBY$G}I`H#R^$hKE1)Y5LUZ|H|AXt#4#NsdOPIx7$;}TGPv% zaOle?!MFx+HhSbJ^!h<30pvJh06C5rK#n8%0mC#Jr~H7o z&rW)Em0DO|eM7yp`YWCMfTxenhT5&IQ)>AE2!r+0S7Ceh`~W4c$LCRpPH;ztF!M?3 zTW^BV_&hq1CeVNK_`6rWbJt#9P668sqw)E)fhJ~tRDG-b0IW5vIbC>d{aY&e0od&a z>0=YbDUeEj06Gz^OA8D%svpf782XmsZX0XPFPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0mMl}K~#8N?b<(X z!Y~*G@GpdRsRW1~fE&OCfE%!|BQC(e!psg6QYLP|%ndjKoPY~JSt^xMT%K_xRH-US zT!HcXNzrHX-;X2;HsYdQuctbljzYW9XsBwns#>jo0j0U{#0CCtJs7K_X?GPjocIzTBU zhr>bFTu#4Cstb`@%XA?W1m`g$1QgwIjLiU?l=)IP4#8Ea1Hiz&-UH%6qk4M?xW4;C zrb7T+{`MjBNznU4z{>!J!y%q`9F0a2hN0y1`STqPN_7^)s_c5bmgRC8pj|4Jq*yE- zqbLf^Lx_loh=_=Yei8rQ*L1I8eGbm*3npD6+wFF;TCJpBufLtTm}7`H{H(t02UvZI zr{N}(iM7vdH2|y=n&wry-L7o6Tba#fdQt@g{JCAuHld9m%H{HX27tv<)BMwTJeK)< zuHV1iZh!Rq{bzm06`IZFeFl*95I6-UwOZ}7P$)d%+djebj~Cn5k3KeuqUaUX2CD%i xg%e<Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0l`T`K~#8N?b<<# z!Y~vD;MaBpaWR4;xN_mrTL^BvfTvJAf(vgTcpevC!li2!3&J3Rj+y?M)ahadwW(x0nDEbBhTuF}GeR0r`0iCL$uD$LjTZ zYC4^&&1Rzpg8|AiPQHj(y$QzE7*m!y*QQ*OWdfXJy%w;o$vs-?1YqE~z60VxBj)xI zaQ*fNt49D_{`bLp63q1@06`Gw=BiW)=ytn*jo8TvA=O1M77JA>l_U&9DHe-TE|+Dw zTuQUqbbnSPA|fIpBBDELwOVgjRbu0Fba?G5GV|4D=09jQE?!19I2?4uza7DMPl<*t@X6!TWHR}P u;!E%_0b(NTbUN?m91#%_5fKsHpU5}Xzo|%KzD`;I0000Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D10qR8K~#8N?V2%b z6hRcnPr*(>EdoJ^>C{S;<2oBZ!;#8H?EDZGeh;y+3g=g_u{(lbrFJnOffOoeXL0hM z>>nSKnb|jQ_V#wq`N1%=o6DQ`{_oA~WwMh>rBeByxzzMQrqjo(W*Fq?=$-q0?dNi7 zv#TXFfAj~Ke1HFTa~0iG&40P+O~Ao6C)WjnfAIXR zYZSslwgAjJdf9ld4;VZt5g}UuW}MlVRlt7T$lyuno`#|&0M$#%&&rjfm#z{2ItP^i zu^@MLuDdoAq@UWvvO!z4hcytE}(iu}V%Hr~s{Bmj^B z_Z2)~$+P_BGGL#BSpa~*`wZS^Hhbh+3DHxU`TXgs)Hqo#-%l2c*I6e*n)we8-?;wZ z;EC&Fq~?!0wYT?hg=c;joSprgoSb~f5j*=CYo5&QW6e2T%pAD{&X6nBfLFwj1V1wliKcon$%xit_yaM1H9V`_=22Z=b2r;xiN1x3+l*EKECKU}$&S&wT z=?m9OzAc`az6aczziO`j!HlPT0Q4Ds>QL0Q2|^gZ^-WhkL}*#I-*uglmP z)!^Ha*S-(*!w2_M;uLLy2pWx(_+?oVJh9q{`Rr4)31Wjv?a*Cev8CMvO%=e*6JAq< zA<$_B_{huyzY@lTNq|sEc3JX$ViEwzfX5m_?UwS(QjvCct1eP*1jL0WozFL jkkt1BDwRqlo5|!acGvTBv->ir00000NkvXXu0mjfq|C0m literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/filterF.png b/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/filterF.png new file mode 100644 index 0000000000000000000000000000000000000000..d00df89c66a26145341db760bbbe7613732d4d5f GIT binary patch literal 892 zcmV-?1B3jDP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0})9?K~#8N?V7)e zR529BuY$c=EQqjfr&gj2)7kh8!&Ww8=R;Wd9%5s)jIUs0Hw=Q6+N}#BY@zlRBVW89 z92&^QE{(O5+3e9~Y! z0T?`~DFSC5wJdy=N|pdJc;b4t3pnGPeVGNUQ^*qF%KC*V#O~JuSy!5QDgopmwNhQf z!NFBiTxk7_3IIbgy)ht-?`!5YX!oU{05W*AQLxUW2(3XsfkuR&0CMKhW?zcXf)LUN z=_inF7g?Jipa{M-SOfS_nvp>PTG;6|ND*v5V?IYSpw*y(T!IuqqVZj|1X>lrUhfcr zXnoG!0|3`xZRE@YyPral0Qh2^Yfp>+1dLf0W{-l35_3g3Qmm83;g*0ZEXGF+VvY_>msSa zlYNih>2x}sPN&oPmkGWiwthcA%g*ixXu(0rF75~L7_8)e!X%Hx0D|XHSnhcsn4|y< zo|K3%Nda63psr^cCMke@6ejzrnV=FNC%v|LLaem~KY5Cz^*Jg44CSQPHcyCsw(OZl z@cgWHUkC~ygGXCqkkrfo8NBpUBSP>!;O64x*!xRpt;z3WI1PtTmmG@HhRlM{*uQ4a zO)dGle7gPj061rtT5NOdpa7h0V6QiTS&_vBp657--wD~Y3z2>T*>(}l0NVh9r*mk# z(h*3fO3Aeq3m&MJ01e6D0X7-mH$=@%(1j*=khBN*X5N(vEo6X(BmsOgAIgN*NfLm# z(o36it&t@FkO5CMc#tgsK;Tmj9(do*YW;uS$0Y6tXzBL@`u%`Tr_*VXVfYQHRPS>_ STTxp80000|)5S5QV$R#y`?G};W!m?9 zadC;RbvBrCEJsZ!x_%0em-zHDkrutOi-8wh3qG8maLMaJ>@!S+sVDhtDpaVn(?ku;ll3kv(8sn-v3_o{aLLG!=*Et!3V#5xyu}` zsIC3J*D~|``If)Q3k!bkKRo+v{-pEwZEoC&>HVW95;Q^n7DM32J?D>a)1In1J>P!H z&*ibT6O`FC4 z|LNs@us?CBk@CFn3(FTB-VmGS@&j+ z&yD1*^>1g-kAIQVuX|_Wx9QIg6y7XyRQtBvYAWxc`}=q7kl2|N7ys{j&raqk-@>nc zzP>7-x&QWx>(;@1g`azcL;kS@eV_R2zUqsGAAQTdtyV!+-46oT{+^A30$^L39nia5XTb9meo4lX48_kdW{3AEPAWV6||Jk-2XXSR79{Cx@yPz~Z z@wCD7Vot9a7X%-C@-*UmJLNeu!-0Zk`2+@ghY!p_8}06^{$Om9XAn%(QcBEdXj#Z8 z#Abg$@dqRCABKxc?eY!s2~745jDHyT?HTM9y5%o018Ik*1N<6~*&nc9;d5uHa}czD z;Ij7ob)Y`RAB=K!3>uB{35+0NhA9X5IT-DMS;OQYzd?h%!Vkuk5%b>*{b4xB-_Wta h%4!(d=rH9cH}j%`eD|if*+367c)I$ztaD0e0sv<0>|X!? literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/pill_press_off.png b/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/pill_press_off.png new file mode 100644 index 0000000000000000000000000000000000000000..b462921a6ccd8fa83a444f8b2242aa5c07c90685 GIT binary patch literal 604 zcmV-i0;BzjP);( zI%uGRgN4dPs%@GJmwHc%=Kq60E|=c_`1AC+w=f0(h%XzWigNjEs!F8Wy%y>~f4%-| zK|jprbH5hv_XjMSElr2R+t5J({T#XffdLQ}%jzwZ4Wp>CdFT`PzsVGnPAA=KT|hq> z%75hn0R9#TfS7{-h&c#=n4@$Rz$YInmTu6G&VNmIE&25LAgn()EV#n_o%#1v1q1X4 zWd2KMfb4#QeZ1W-J>NqbWz)3&~vO7QJ*B9HpzUvho3_-s_;=gnNbT0kJk!MdiwY#?vfvSMM zN#eiqA%H3aKwl^EUpfFXlcuoF_UuYg0R1M3|Kx{3JPob#IT-Y#@n0$f@K1|G>&zK^ z%}NFIi)8+5VK!w%%r8ACfPRn6e|!%RbEGnWcV_7aWd?x0P3FH;08q{BWr2Qy%v%fr qF()tssNBh01?W3#^&bGh{yCq2KN+RGYE9LG=1*n&C)L!r%3aL~b&f*?*^d0OQ!<;_rxE<)kN(jbHp7aAm3r=)2ndms$bBg-G~XH_&fO`yU&CX7kkNC50o`ezXI8(RHO8~{;N2LSqFC<{QTXr(UZrn8}R0HAN>>OTMg00000 g000000D%4gKgI%A*YJ>*@c;k-07*qoM6N<$f`yyr$N&HU literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/reaction_chamber.png b/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/reaction_chamber.png new file mode 100644 index 0000000000000000000000000000000000000000..df30be70209447a713184be17855f21388cc83b6 GIT binary patch literal 713 zcmV;)0yh1LP)(CBeT?Kasafltd3*Cxqmkxz4E*Ud~ zlxskV0U^l{6FSr(fp(F|@8lgXn#IC{_BmM_203lvIt594(zoFg#@i0l*%}rWbUZJ^1Nv)HCe)Rq; ztpEc2CpPZQO6a=+EH19~W~P&UuOEZNxgupahjT}rAoSOa`tMr+m%m%o>9%}dDQ-`S zb2xWYpx-j;|InX+Hy>-g{D*aCv_HLdE6wYDRNkS=x!-o5gz5=Ezh>Nj&jR4Y#PgJM z&Ew1Muf&rWR#jYuyfNWeIX6rZK))7=|Gt~R_g^mDYZhk#uMG%4lP4(Wh6VZ;M*a6J zfcDR(TS320>dt2tNF-)Gzf3$El&cE#o00hMy8`eRcnO8cRR#KWqy8&3f@Y?+oRK#n z2d^*b)x(xMij9egF#+_qwEM5z37mMEb9S4@g};O3*1H8dw{7J~KtDeJL!AX!dxgF}G+*<*sDOS!oByHC0%YMhfqrsi z50zGc&Yz>cSBgw#)%g?fGmbF@^pCXrue<_Wo$phda(7yfEY8nN1O0ui{(G(fUE6l6 zjYs}0x=ccdJ5r)6CGK&|pj=g;Z%5+)(Ej|hr9<~P@wB?*#EPRyx$P)li<7HCxvD__ vFjoJEBA-#;KCg}f=qF?K9{>OV{p(x<6pg}=aJie500000NkvXXu0mjfk?2=a literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/reaction_chamber_on.png b/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/reaction_chamber_on.png new file mode 100644 index 0000000000000000000000000000000000000000..794e9d7e64c52e148ce71d91b70aa6279aea75ac GIT binary patch literal 2534 zcmeHJ|2GqQAOCEV4GqbuPztdlB!#%mwnUpvR7$n(Gqfa0h@QvLHW5j_6j%CY^3gL}^B{eGYK`@Da8zrLJL#$G=>QrA=$ z0D$x%cANllwCYqTVa?2jkJo}u-70s7-rDqS^)X%%^@i?^OP^Mfcf44qW!Aowh_|18> zql(hJ;?p3Xvw5_Z+8(tIqxc$Ea3WDI9PYiJchHQdj8y#PIWwsKG7Uv<-+f5eYjS?x zptYPdcrX%38^pJu!J+|px?YO{-Td%YSOdkRbG`DE+rnr->$}SSTh6>D*$brWYlf7& z9b%Ebx)?rtF;w6q*!?-NfPF%l2`vr_Bk{UkGWmid73V6k*(8{y8Ca7rkFJU8XRQy> zA}+(3mfi8o%vg-&&d+Tfa$A0R&wYj~s8*h%J&Kw8S}pWhlWast;l^exy6*?|^PB)> zIp~$;t-RQeRZaKx;qhsuqB|y?=fCoqK8Q<758}21zMhnLd1i8V5nU=SL#4?-%1f=l zXd=sfi#auC`BkSAC5Ap`16J<-nNRpDVKbtUE(&x&rs$d!sD}k5>C!CA@Yckn_ru|& z7{*bCGpJ5dx5vdL5*Ec@;`JeE7E|R}1Fd0K(}rl_!?ai80J0rPjKYJCjaruBd6&jY zyorvDwHIHVOF1ZWO6rpIlyBHQZ^YB9nfQHV#5^z8AgI~(Nt}A@j>jHf!M5Ntu++oK zSRFIYGnSBP+)K|-Z}Sg7dp;M*%uOs>2)-SiL>p%f*I7wtro^TLl^MP9fsEJsfZS3K zbDl+W=~ew}K@M6ia2sCu_EFe+3spV!pG63nQK|n%77=gFr8~R;#k)t>f!PLY8#AqJ zu_>ycKDEZlMHRRXII9YJzg5~K*YZ;fSEuHYj;7?dz?&7dj{7<|$}cxIL$<3hjY5B2 z1tu$tm$=|-7dXb|a~C46B;~`;XV}w;d3NbE{aF!5Br=AsG&l`7!_BK^i6@l~KY^3V zKlKR~Sw&>5#n>Mi+Pv4X6MWx&kf|6yb*SYE8}s*|IjxHjUbWYJND($zs3gF~7h2H| zB=7_Ov_t2((}`wQi&kwtaQ`WV!6I8vbcI#UvWb~4fr_cJF(NUdO<5^Hm0SSyPkK^L zpXi4ymU8EKh|FMLret!}Cs7x?u(SKoAj@Eg&cHC`?CaMGp`gLMrz^*Ti+QaXcfn8U zCxw_K7&Yya7LDCMqP`0RA3hk(&TTDrN+<~b{8!FMH}knRNGqC^j&FgMLpLD1{n%~K z6z*g>%_MrQc3VX+)(kxANuAkh@f4AdGi@S;>N{cay1YKsxnEn1nPkSQN9u%7PbsQ22rC@!PafI>rDo|kIU-Qk>$Pne1__JQpu71&^0saN zfZ7Xtj@E3#x>U3UQAbPB0@87Dz|EiCU&R9ZT@{|eUy_kSF66sEn^APCTD;+)D(EA` zw(M*Y6R<~&1ltT-5G{04D%N-kdlZX6voOwET~LB5 zF*_$ZQgl<|sLL$6;{d@r!c0rUZuC{D+%_XGod&@I9Y&PC)!t2@d&6`b+4mKzw2pNh z(jKz+G*S5*gt=pnVDn1LD>$99lE(+e4d-IZ!0G8Awu}V1Cqbcf4+kc(IXV8dT8ua? z=Ixo2ZRRGH)K{Js*Ib|A&latsWudf}c)I3?;|S`HF%w_L{MT$2l>)S*!*@YMgxab` z5x5(vxQ&rUwP-G3(t3M+ZsVC6A~sFbNN+NJ_M8HZhddCDOt|7My~AFw5fc@S`h=a+ zc-mn_zD+;;13O_wSqtRD~!n>ympiRZsV4|Lc_TW(PzUWa=%K8uVd$}iHmhX=pJj{Lrn9s%C7-b zvchFIrX%HduT|Lc7r(Tk;k>QKH)$=&pHgbwQ8)a{8`;q~@J3d*F4xo4qmJ&`k+KpA z$kc4z;GljjV1yK}D$jCJ~dQf6M-F*?*UPXWF*qYRKdRm`N2h{FLTz O27J8zysABeFZ>%&Vc;PE literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/synthesizer.png b/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/synthesizer.png new file mode 100644 index 0000000000000000000000000000000000000000..e89dfeac9dbbcfca1a1d2c1ccb9f88b68eaa5739 GIT binary patch literal 831 zcmV-F1Hk-=P)4e-6n=@85!B*vIfz#XQ6lJt5Fw0iS;RfA(7+v7$;!9-mf79e-PzpjY?1fDk=fbz-Zy;rZuafG5D5eVfj}TQkC-zx zytj4Wsn>4jY3RPQ{wj3+VQ0fQw(ysy|GbIO@>0{gcDG9W0r}e69s2dt^*#dz1srJL8lQS(y`0eVSnE=>I@J#vi=Dq*d1R#g) z5Ep$T7vV2S|I7rK^m_jS<@{B{7QWL$_Et; z3!k)@X%PNxYx>6$U~zH9yKv(LoxFYQXAqO?I!-#U4KD&Q5DPI8JIi1ASFGwEOMvL> zMKrJpl5CS=3x9d~XTAj>lk$+Oi%2U@OdaYDm@czh7x-(|3PPx$FF`hF?4GTPn4iEQ@P6ma)VNL(UA}|Rc zz7sND#@b6mFKb8Rh3k8pR&uKkqUI1)Ndeu3KK;b{KqrY|s82@e& z8ML5>Ir{Z}OhX8N%bNakN&R;!0H*&RApB0L`U?aCfq>2f{sMbWzT==V9jyQW002ov JPDHLkV1n?ik5vEw literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/synthesizer_inactive.png b/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/synthesizer_inactive.png new file mode 100644 index 0000000000000000000000000000000000000000..0a740e719d68b58d396d0c80472de5cf8dd3bd81 GIT binary patch literal 935 zcmV;Y16cftP)?IFTmku5}7h8?J?+44i_vZV)-{UvmzIpS$C1Nlb z3q$o)d$!nfV{i=JZ=6g zm>;w;CfmSACpPm3tN&3Q^7@Vr%NR)0X`*X=eP%zMUNXmIz^9EyjL+Ho2U-c3f25}R zdnSMi$ww)gdD25ulLgC|Uts=J)%4d+fJh>4>GzL+ceUSG%eEU+CnisBPCN##&m>Q9OQzrY0w8Yr9aDvjFCg)=Yn`6#&8Ib|iAb zW>Y=}yRB{OB=-yEkJe0o&x4?@uB3s329bRLGE62!8)E=$U<)7kdd`6P1Jyt5cJx?& z{3xSX{H1JH8i)CV)nAzaaKLNC?&?q}vjpVK+qrz227jz8uL1By48*D=37CJ*H~p0f z04Et(QjlfxvlwL0KP}Pd%}W+y*uWM(ZZX}!{6oI!Z%crtrcUEX^DyOK-!(HxkgFK>lo_=y3dkgGa`_vG0VWx(tCT28fQ0c8x>&>U&KHx;Njn0^Sk|Qoo!OgKOKnv%9A3zKME(+ z8MD}(ij9V7<2mD0u)uR@VPPipCJ{GoEazNLsF*+LoBp;NfsKO5Iwy!pfDjOEFCYAC zWo_+)>&<8WPG9v8t2PP%) zbMBex$701E`ZO|zYO1Jin$Z6~>hgjQK7xHfVVwmAk<#>#bST`v1>A z8VKn)mIVz|NRMhkVOqYO8M_zxUhfL8Rc6bzNf37 J%Q~loCIAiSTSWi> literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/tank.png b/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/tank.png new file mode 100644 index 0000000000000000000000000000000000000000..7b500de2dc6f73a7ac9cf66389c0f23970022858 GIT binary patch literal 721 zcmV;?0xtcDP)(9=+=&6 zyR>mp6I?VH4ndlxo`FtR-qj1SCih<6y^kxs|9`pMUB34_{_o@NeP1p_P^1;p2^tv4 zI_=Jt%ipxd1V0@A<2ulxp~05l>FZ02jl+=);AdI{_uml^1idbe`q8L zz|XSzALlY(92*_|laWD^0Q@Yg|6N?>d;22Z@0kUDxc>VlfH#;f+=ReSv-&ShfG{T? zd}8-sngE<|U4frA=D%+ORKHs^HFZCb`66D7qa*MwcK@Y!fPDUK<05q6xm-?$06#?k zeao}4hnr4q`voPFJv2KzPj@D6yT?~vzG@3^&R7rL+p|DBr6(Pig5Ynm`!7v^D_0YA z`sF#LQmF>;(bHvj`*o|(GGjf}>%S;7u}XHKpq&8VCs_SgY6V}PUMKtGv&O!cfwfxA zH%`D!2>f+6|CN6W=%3G1;@bHw=goum(8;yN=laQ+>hR!)9lu{xDns1(^Ey;l;IFaz z-=$iI>&Rpu0Q}B{J^=aSv#m_OKamNETU(DaC935Pk zJeBdm56^$C2^tx>>;BQ8RNT9C&eWuX|L^(_(f_00jQThB=h~X500000NkvXXu0mjf De)(wq literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/tap_output.png b/Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/tap_output.png new file mode 100644 index 0000000000000000000000000000000000000000..0a0702ba1d364256f1b3ecbeed67901a421dfd66 GIT binary patch literal 428 zcmV;d0aN~oP)o87BK6-$S_I5K_w)_J_B+oQ>E-~K<)%hb z$in|gbp*vxaQfUciu9B1fXA;tF~EW!S&VKD00B_EBc}m!z|hVGBgX;e5l8-G6Mwwj z1HlF+KyJ?+4CcVB2;!eyVMA1QBsv5n45Tr{?rz7f=Ea4l#2QO_6g*v%%s@>*lmkFM z6Vp)uvsur`Gn_hg5hB5_1`PUJV5a5L�PKP|*Q*=5JwmVQ2_u|9$X~;r+Yk3|c;` zA^baa46j~&V7Rp8C|v!Q^XD0e1w$hj-~-GmO$?GMG<1N8iPQh@pBYF7Abn@f>?4>J z2Y(bOD9AHhzl=y&pTBHu1UQ3s4VVAKHv?f?K_ Wj(}NGUn$%G0000 Date: Sun, 24 May 2026 05:33:54 +1200 Subject: [PATCH 2/4] Content Update --- .../RCD/RCDMenuBoundUserInterface.cs | 4 +- .../PlumbingConnectorAppearanceSystem.cs | 267 ++++++++++++++-- .../UI/PlumbingPillPressBoundUserInterface.cs | 3 + .../Plumbing/UI/PlumbingPillPressWindow.xaml | 6 + .../UI/PlumbingPillPressWindow.xaml.cs | 7 + ...umbingSmartDispenserBoundUserInterface.cs} | 10 +- ...xaml => PlumbingSmartDispenserWindow.xaml} | 4 +- ...s => PlumbingSmartDispenserWindow.xaml.cs} | 12 +- .../Components/PlumbingManifoldComponent.cs | 17 + .../PlumbingConnectorAppearanceSystem.cs | 301 +++++++++++++++--- .../EntitySystems/PlumbingDisposalSystem.cs | 34 ++ .../EntitySystems/PlumbingInletSystem.cs | 4 + .../EntitySystems/PlumbingManifoldSystem.cs | 85 +++++ .../EntitySystems/PlumbingPillPressSystem.cs | 16 + .../EntitySystems/PlumbingPullSystem.cs | 184 ++++++++++- ...tem.cs => PlumbingSmartDispenserSystem.cs} | 41 +-- .../_Starlight/Plumbing/Nodes/PlumbingNode.cs | 186 +++++++++++ Content.Shared/RCD/Components/RCDComponent.cs | 9 + Content.Shared/RCD/RCDEvents.cs | 14 + Content.Shared/RCD/Systems/RCDSystem.cs | 35 +- .../PlumbingConnectorAppearanceComponent.cs | 7 - .../Components/PlumbingOutletComponent.cs | 8 + .../Components/PlumbingPillPressComponent.cs | 6 + ....cs => PlumbingSmartDispenserComponent.cs} | 6 +- .../StarlightPlumbingFilterComponent.cs | 13 +- .../_Starlight/Plumbing/PlumbingVisuals.cs | 19 +- .../Plumbing/SharedPlumbingPillPress.cs | 17 + ...dge.cs => SharedPlumbingSmartDispenser.cs} | 14 +- .../en-US/_Starlight/plumbing/pill-press.ftl | 1 + .../en-US/_Starlight/plumbing/plumbing.ftl | 1 + .../_Starlight/plumbing/smart-dispenser.ftl | 10 + .../_Starlight/plumbing/smart-fridge.ftl | 10 - .../en-US/_Starlight/rpld/rpld-components.ftl | 3 +- .../Dispensers/base_structuredispensers.yml | 22 +- .../Structures/Machines/chem_master.yml | 18 +- .../Structures/Machines/reagent_grinder.yml | 22 +- .../Entities/Guidebook/plumbing_ducts.yml | 20 +- .../Entities/Objects/Tools/tools.yml | 5 +- .../Structures/Piping/Plumbing/ducts.yml | 252 ++++++++++++++- .../{machines.yml => plumbing_machines.yml} | 221 +++++++++++-- Resources/Prototypes/_StarLight/RPLD/rpld.yml | 128 +++++--- .../Guidebook/Engineering/Plumbing.xml | 12 +- .../Guidebook/Engineering/PlumbingFlow.xml | 12 +- .../Engineering/PlumbingMachines.xml | 136 ++++++++ .../Interface/Radial/RPLD/buffer_tank.png | Bin 0 -> 611 bytes .../Interface/Radial/RPLD/disposer.png | Bin 0 -> 439 bytes .../Interface/Radial/RPLD/duct_manifold.png | Bin 0 -> 464 bytes .../fluid_ducts_alt1.rsi/ductBend.png | Bin 0 -> 651 bytes .../fluid_ducts_alt1.rsi/ductConnector.png | Bin 0 -> 354 bytes .../fluid_ducts_alt1.rsi/ductFourway.png | Bin 0 -> 724 bytes .../fluid_ducts_alt1.rsi/ductStraight.png | Bin 0 -> 444 bytes .../fluid_ducts_alt1.rsi/ductTJunction.png | Bin 0 -> 704 bytes .../Plumbing/fluid_ducts_alt1.rsi/meta.json | 36 +++ .../fluid_ducts_alt2.rsi/ductBend.png | Bin 0 -> 654 bytes .../fluid_ducts_alt2.rsi/ductConnector.png | Bin 0 -> 355 bytes .../fluid_ducts_alt2.rsi/ductFourway.png | Bin 0 -> 702 bytes .../fluid_ducts_alt2.rsi/ductStraight.png | Bin 0 -> 447 bytes .../fluid_ducts_alt2.rsi/ductTJunction.png | Bin 0 -> 704 bytes .../Plumbing/fluid_ducts_alt2.rsi/meta.json | 36 +++ .../Plumbing/plumbers.rsi/buffer_tank.png | Bin 0 -> 630 bytes .../Piping/Plumbing/plumbers.rsi/disposal.png | Bin 374 -> 542 bytes .../plumbers.rsi/disposal_working.png | Bin 1036 -> 1836 bytes .../Piping/Plumbing/plumbers.rsi/meta.json | 18 +- .../plumbing_manifold.rsi/duct_manifold.png | Bin 0 -> 1155 bytes .../Plumbing/plumbing_manifold.rsi/meta.json | 16 + 65 files changed, 2078 insertions(+), 230 deletions(-) rename Content.Client/_Starlight/Plumbing/UI/{PlumbingSmartFridgeBoundUserInterface.cs => PlumbingSmartDispenserBoundUserInterface.cs} (52%) rename Content.Client/_Starlight/Plumbing/UI/{PlumbingSmartFridgeWindow.xaml => PlumbingSmartDispenserWindow.xaml} (85%) rename Content.Client/_Starlight/Plumbing/UI/{PlumbingSmartFridgeWindow.xaml.cs => PlumbingSmartDispenserWindow.xaml.cs} (77%) create mode 100644 Content.Server/_Starlight/Plumbing/Components/PlumbingManifoldComponent.cs create mode 100644 Content.Server/_Starlight/Plumbing/EntitySystems/PlumbingDisposalSystem.cs create mode 100644 Content.Server/_Starlight/Plumbing/EntitySystems/PlumbingManifoldSystem.cs rename Content.Server/_Starlight/Plumbing/EntitySystems/{PlumbingSmartFridgeSystem.cs => PlumbingSmartDispenserSystem.cs} (85%) rename Content.Shared/_Starlight/Plumbing/Components/{PlumbingSmartFridgeComponent.cs => PlumbingSmartDispenserComponent.cs} (69%) rename Content.Shared/_Starlight/Plumbing/{SharedPlumbingSmartFridge.cs => SharedPlumbingSmartDispenser.cs} (58%) create mode 100644 Resources/Locale/en-US/_Starlight/plumbing/smart-dispenser.ftl delete mode 100644 Resources/Locale/en-US/_Starlight/plumbing/smart-fridge.ftl rename Resources/Prototypes/_StarLight/Entities/Structures/Piping/Plumbing/{machines.yml => plumbing_machines.yml} (76%) rename Resources/ServerInfo/{ => _StarLight}/Guidebook/Engineering/Plumbing.xml (72%) rename Resources/ServerInfo/{ => _StarLight}/Guidebook/Engineering/PlumbingFlow.xml (69%) create mode 100644 Resources/ServerInfo/_StarLight/Guidebook/Engineering/PlumbingMachines.xml create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/buffer_tank.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/disposer.png create mode 100644 Resources/Textures/_Starlight/Interface/Radial/RPLD/duct_manifold.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts_alt1.rsi/ductBend.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts_alt1.rsi/ductConnector.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts_alt1.rsi/ductFourway.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts_alt1.rsi/ductStraight.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts_alt1.rsi/ductTJunction.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts_alt1.rsi/meta.json create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts_alt2.rsi/ductBend.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts_alt2.rsi/ductConnector.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts_alt2.rsi/ductFourway.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts_alt2.rsi/ductStraight.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts_alt2.rsi/ductTJunction.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/fluid_ducts_alt2.rsi/meta.json create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbers.rsi/buffer_tank.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbing_manifold.rsi/duct_manifold.png create mode 100644 Resources/Textures/_Starlight/Structures/Piping/Plumbing/plumbing_manifold.rsi/meta.json diff --git a/Content.Client/RCD/RCDMenuBoundUserInterface.cs b/Content.Client/RCD/RCDMenuBoundUserInterface.cs index 98fbc21102b..5ff4848b23f 100644 --- a/Content.Client/RCD/RCDMenuBoundUserInterface.cs +++ b/Content.Client/RCD/RCDMenuBoundUserInterface.cs @@ -25,8 +25,10 @@ public sealed class RCDMenuBoundUserInterface : BoundUserInterface ["Airlocks"] = ("rcd-component-airlocks", new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Radial/RCD/airlocks.png"))), ["Electrical"] = ("rcd-component-electrical", new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Radial/RCD/multicoil.png"))), ["Lighting"] = ("rcd-component-lighting", new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Radial/RCD/lighting.png"))), + // Starlight start: RPLD ["PlumbingDucts"] = ("rpld-component-ducts", new SpriteSpecifier.Texture(new ResPath("/Textures/_Starlight/Interface/Radial/RPLD/category_ducts.png"))), - ["PlumbingMachines"] = ("rpld-component-machines", new SpriteSpecifier.Texture(new ResPath("/Textures/_Starlight/Interface/Radial/RPLD/category_machines.png"))), + ["PlumbingSupply"] = ("rpld-component-supply", new SpriteSpecifier.Texture(new ResPath("/Textures/_Starlight/Interface/Radial/RPLD/tank.png"))), + ["PlumbingProduction"] = ("rpld-component-production", new SpriteSpecifier.Texture(new ResPath("/Textures/_Starlight/Interface/Radial/RPLD/reaction_chamber.png"))), }; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; diff --git a/Content.Client/_Starlight/Plumbing/PlumbingConnectorAppearanceSystem.cs b/Content.Client/_Starlight/Plumbing/PlumbingConnectorAppearanceSystem.cs index 7c150a70edc..a701bf41acc 100644 --- a/Content.Client/_Starlight/Plumbing/PlumbingConnectorAppearanceSystem.cs +++ b/Content.Client/_Starlight/Plumbing/PlumbingConnectorAppearanceSystem.cs @@ -3,6 +3,7 @@ using Content.Shared._Starlight.Plumbing.Components; using Content.Client.SubFloor; using Content.Shared.Atmos; +using Content.Shared.Atmos.Components; using Content.Shared.Atmos.Piping; using JetBrains.Annotations; using Robust.Client.GameObjects; @@ -11,8 +12,9 @@ namespace Content.Client._Starlight.Plumbing; /// /// Client system that creates and updates plumbing connector sprite layers. -/// Single layer per direction that switches between disconnected/connected sprites -/// Layers hide when covered by floor tiles (server sends CoveredByFloor state) +/// Regular machines use one connector layer per cardinal direction. +/// Manifolds use slot-based connector layers per side. +/// Layers hide when covered by floor tiles (server sends CoveredByFloor state). /// [UsedImplicitly] public sealed class PlumbingConnectorAppearanceSystem : EntitySystem @@ -24,6 +26,21 @@ public sealed class PlumbingConnectorAppearanceSystem : EntitySystem private static readonly Color OutletColor = new(0.35f, 0.6f, 1.0f); // Vibrant Blue private static readonly Color MixingInletColor = new(0.35f, 0.9f, 0.35f); // Vibrant Green private static readonly PlumbingConnectionLayer[] ConnectionLayers = Enum.GetValues(); + private static readonly PipeDirection[] CardinalDirections = + [ + PipeDirection.North, + PipeDirection.South, + PipeDirection.East, + PipeDirection.West, + ]; + + private static readonly ManifoldSideSpec[] ManifoldSides = + [ + new(PipeDirection.North, 3), + new(PipeDirection.South, 3), + ]; + + private const float ManifoldSlotSpacing = 0.0625f; private EntityQuery _xformQuery; @@ -46,7 +63,8 @@ private void OnInit(EntityUid uid, PlumbingConnectorAppearanceComponent componen // Insert at layer 0 so connectors render UNDER the plumbing machine sprite foreach (var layerKey in ConnectionLayers) { - var offset = GetDirectionOffset(layerKey, component.Offset); + var direction = (PipeDirection) layerKey; + var baseOffset = GetDirectionOffset(direction, component.Offset); // Each insertion at 0 pushes previous layers up, so we use index 0 for all operations var layerName = layerKey.ToString(); @@ -56,16 +74,15 @@ private void OnInit(EntityUid uid, PlumbingConnectorAppearanceComponent componen // Disconnected connectors are offset from center to show under big machine sprites. _sprite.LayerSetRsi((uid, sprite), 0, component.Disconnected.RsiPath); _sprite.LayerSetRsiState((uid, sprite), 0, component.Disconnected.RsiState); - _sprite.LayerSetDirOffset((uid, sprite), 0, ToOffset(layerKey)); + _sprite.LayerSetDirOffset((uid, sprite), 0, ToOffset(direction)); + _sprite.LayerSetOffset((uid, sprite), 0, baseOffset); _sprite.LayerSetVisible((uid, sprite), 0, false); - if (offset != Vector2.Zero) - _sprite.LayerSetOffset((uid, sprite), 0, offset); } } - private static Vector2 GetDirectionOffset(PlumbingConnectionLayer layer, float offset) + private static Vector2 GetDirectionOffset(PipeDirection direction, float offset) { - return ((PipeDirection)layer).ToDirection().ToVec() * offset; + return direction.ToDirection().ToVec() * offset; } private void OnAppearanceChanged(EntityUid uid, PlumbingConnectorAppearanceComponent component, ref AppearanceChangeEvent args) @@ -74,7 +91,13 @@ private void OnAppearanceChanged(EntityUid uid, PlumbingConnectorAppearanceCompo return; if (!args.Sprite.Visible) + return; + + if (IsManifoldAppearance(uid, args.Component)) { + HideAllLayers(uid, args.Sprite); + EnsureManifoldLayers(uid, component, args.Sprite); + UpdateManifoldAppearance(uid, component, ref args); return; } @@ -88,6 +111,9 @@ private void OnAppearanceChanged(EntityUid uid, PlumbingConnectorAppearanceCompo if (!_appearance.TryGetData(uid, PlumbingVisuals.ConnectedDirections, out var connectedDirectionsInt, args.Component)) connectedDirectionsInt = 0; + if (!_appearance.TryGetData(uid, PlumbingVisuals.ConnectedLayerByDirection, out var connectedLayersPacked, args.Component)) + connectedLayersPacked = 0; + if (!_appearance.TryGetData(uid, PlumbingVisuals.InletDirections, out var inletDirectionsInt, args.Component)) inletDirectionsInt = 0; if (!_appearance.TryGetData(uid, PlumbingVisuals.OutletDirections, out var outletDirectionsInt, args.Component)) @@ -124,8 +150,7 @@ private void OnAppearanceChanged(EntityUid uid, PlumbingConnectorAppearanceCompo var isOutlet = outletDirectionsLocal.HasDirection(dir); var isMixingInlet = mixingInletDirectionsLocal.HasDirection(dir); - // Determine color based on inlet/outlet/mixing - var color = isMixingInlet ? MixingInletColor : isInlet ? InletColor : isOutlet ? OutletColor : Color.White; + var color = GetConnectorColor(isInlet, isOutlet, isMixingInlet); var layerName = layerKey.ToString(); if (_sprite.LayerMapTryGet((uid, args.Sprite), layerName, out var layerKey2, false)) @@ -139,12 +164,14 @@ private void OnAppearanceChanged(EntityUid uid, PlumbingConnectorAppearanceCompo if (isConnected) { _sprite.LayerSetRsiState((uid, args.Sprite), layerKey2, component.Connected.RsiState); - _sprite.LayerSetOffset((uid, args.Sprite), layerKey2, Vector2.Zero); + var worldDirection = dir.RotatePipeDirection(localRotation); + var connectedLayer = GetConnectedLayer(connectedLayersPacked, worldDirection); + _sprite.LayerSetOffset((uid, args.Sprite), layerKey2, GetConnectedLayerOffset(worldDirection, localRotation, connectedLayer, component.Offset)); } else { _sprite.LayerSetRsiState((uid, args.Sprite), layerKey2, component.Disconnected.RsiState); - _sprite.LayerSetOffset((uid, args.Sprite), layerKey2, GetDirectionOffset(layerKey, component.Offset)); + _sprite.LayerSetOffset((uid, args.Sprite), layerKey2, GetDirectionOffset(dir, component.Offset)); } layer.Color = color; } @@ -152,6 +179,79 @@ private void OnAppearanceChanged(EntityUid uid, PlumbingConnectorAppearanceCompo } } + private void UpdateManifoldAppearance(EntityUid uid, PlumbingConnectorAppearanceComponent component, ref AppearanceChangeEvent args) + { + if (args.Sprite is not { } sprite) + return; + + if (!_appearance.TryGetData(uid, PlumbingVisuals.ManifoldConnectedSlotsByDirection, out var connectedSlotsPacked, args.Component)) + connectedSlotsPacked = 0; + + if (!_appearance.TryGetData(uid, PlumbingVisuals.CoveredByFloor, out var coveredByFloor, args.Component)) + coveredByFloor = false; + + if (!_xformQuery.TryGetComponent(uid, out var xform)) + return; + + var localRotation = xform.LocalRotation; + var localPacked = RotateManifoldSlotsToLocal(connectedSlotsPacked, localRotation); + + foreach (var (direction, slotIndex, slotCount, layerName) in EnumerateManifoldLayers()) + { + if (!_sprite.LayerMapTryGet((uid, sprite), layerName, out var layerKey, false)) + continue; + + var layer = sprite[layerKey]; + var slotMask = ReadPackedDirectionNibble(localPacked, direction); + var isConnected = (slotMask & (1 << slotIndex)) != 0; + layer.Visible = isConnected && !coveredByFloor; + if (!layer.Visible) + continue; + + _sprite.LayerSetRsiState((uid, sprite), layerKey, component.Connected.RsiState); + const float forwardOffset = 0f; + _sprite.LayerSetOffset((uid, sprite), layerKey, + GetManifoldSlotOffset(direction, slotIndex, slotCount, forwardOffset, ManifoldSlotSpacing, component.Offset, localRotation)); + layer.Color = Color.White; + } + } + + private bool IsManifoldAppearance(EntityUid uid, AppearanceComponent? appearance) + { + if (_appearance.TryGetData(uid, PlumbingVisuals.ManifoldMode, out var manifoldMode, appearance)) + return manifoldMode; + + return _appearance.TryGetData(uid, PlumbingVisuals.ManifoldConnectedSlotsByDirection, out _, appearance); + } + + private void EnsureManifoldLayers(EntityUid uid, PlumbingConnectorAppearanceComponent component, SpriteComponent sprite) + { + foreach (var (direction, slotIndex, slotCount, layerName) in EnumerateManifoldLayers()) + { + if (_sprite.LayerMapTryGet((uid, sprite), layerName, out _, false)) + continue; + + _sprite.AddBlankLayer((uid, sprite), 0); + _sprite.LayerMapSet((uid, sprite), layerName, 0); + _sprite.LayerSetRsi((uid, sprite), 0, component.Disconnected.RsiPath); + _sprite.LayerSetRsiState((uid, sprite), 0, component.Disconnected.RsiState); + _sprite.LayerSetDirOffset((uid, sprite), 0, ToOffset(direction)); + _sprite.LayerSetOffset((uid, sprite), 0, GetManifoldSlotOffset(direction, slotIndex, slotCount, 0f, ManifoldSlotSpacing, component.Offset, Angle.Zero)); + _sprite.LayerSetVisible((uid, sprite), 0, false); + } + } + + private static IEnumerable<(PipeDirection Direction, int SlotIndex, int SlotCount, string LayerName)> EnumerateManifoldLayers() + { + foreach (var side in ManifoldSides) + { + for (var slotIndex = 0; slotIndex < side.SlotCount; slotIndex++) + { + yield return (side.Direction, slotIndex, side.SlotCount, $"{side.Direction}_slot_{slotIndex}"); + } + } + } + private void HideAllLayers(EntityUid uid, SpriteComponent sprite) { foreach (var layerKey in ConnectionLayers) @@ -162,15 +262,144 @@ private void HideAllLayers(EntityUid uid, SpriteComponent sprite) } } + private static AtmosPipeLayer GetConnectedLayer(int packedData, PipeDirection direction) + { + var encoded = ReadPackedDirectionNibble(packedData, direction); + if (encoded == 0) + return AtmosPipeLayer.Primary; + + return (AtmosPipeLayer) Math.Clamp(encoded - 1, 0, (int) AtmosPipeLayer.Tertiary); // HL TODO: update for 5 layers later + } - private SpriteComponent.DirectionOffset ToOffset(PlumbingConnectionLayer layer) + private static Vector2 GetConnectedLayerOffset(PipeDirection worldDirection, Angle localRotation, AtmosPipeLayer layer, float offset) { - return layer switch + var sidewaysOffset = offset + (1f / 32f); + + var worldOffset = layer switch { - PlumbingConnectionLayer.NorthConnection => SpriteComponent.DirectionOffset.Flip, - PlumbingConnectionLayer.EastConnection => SpriteComponent.DirectionOffset.CounterClockwise, - PlumbingConnectionLayer.WestConnection => SpriteComponent.DirectionOffset.Clockwise, - _ => SpriteComponent.DirectionOffset.None, // SouthConnection + AtmosPipeLayer.Secondary => GetPerpendicularOffset(worldDirection, sidewaysOffset), + AtmosPipeLayer.Tertiary => GetPerpendicularOffset(worldDirection, -sidewaysOffset), + // AtmosPipeLayer.Quaternary => GetPerpendicularOffset(worldDirection, sidewaysOffset), // HL TODO: update for 5 layers later + // AtmosPipeLayer.Quinary => GetPerpendicularOffset(worldDirection, -sidewaysOffset), + _ => Vector2.Zero, + }; + + return (-localRotation).RotateVec(worldOffset); + } + + private static Vector2 GetPerpendicularOffset(PipeDirection direction, float magnitude) + { + return direction switch + { + PipeDirection.North or PipeDirection.South => new Vector2(magnitude, 0f), + PipeDirection.East or PipeDirection.West => new Vector2(0f, magnitude), + _ => Vector2.Zero, + }; + } + + private static int RotateManifoldSlotsToLocal(int packedData, Angle localRotation) + { + var rotated = 0; + foreach (var worldDirection in CardinalDirections) + { + var mask = ReadPackedDirectionNibble(packedData, worldDirection); + if (mask == 0) + continue; + + var localDirection = worldDirection.RotatePipeDirection(-localRotation); + rotated = WritePackedDirectionNibble(rotated, localDirection, mask); + } + + return rotated; + } + + private static int ReadPackedDirectionNibble(int packedData, PipeDirection direction) + { + var shift = GetDirectionNibbleShift(direction); + + if (shift < 0) + return 0; + + return (packedData >> shift) & 0xF; + } + + private static int WritePackedDirectionNibble(int packedData, PipeDirection direction, int nibble) + { + var shift = GetDirectionNibbleShift(direction); + + if (shift < 0) + return packedData; + + var clearMask = ~(0xF << shift); + return (packedData & clearMask) | ((nibble & 0xF) << shift); + } + + private static int GetDirectionNibbleShift(PipeDirection direction) + { + return direction switch + { + PipeDirection.North => 0, + PipeDirection.South => 4, + PipeDirection.East => 8, + PipeDirection.West => 12, + _ => -1, + }; + } + + private static Vector2 GetManifoldSlotOffset(PipeDirection direction, int slotIndex, int slotCount, float baseOffset, float slotSpacing, float connectorOffset, Angle localRotation) + { + var baseDirectionOffset = direction.ToDirection().ToVec() * baseOffset; + + if (slotCount == 3) + { + var slotLayer = slotIndex switch + { + 0 => AtmosPipeLayer.Tertiary, + 1 => AtmosPipeLayer.Primary, + 2 => AtmosPipeLayer.Secondary, + _ => AtmosPipeLayer.Primary, + }; + + var worldDirection = direction.RotatePipeDirection(localRotation); + var layerOffset = GetConnectedLayerOffset(worldDirection, localRotation, slotLayer, connectorOffset); + return baseDirectionOffset + layerOffset; + } + + var centeredIndex = slotIndex - (slotCount - 1) / 2f; + var spread = centeredIndex * slotSpacing; + + var perpendicularOffset = direction switch + { + PipeDirection.North or PipeDirection.South => new Vector2(spread, 0f), + PipeDirection.East or PipeDirection.West => new Vector2(0f, spread), + _ => Vector2.Zero, + }; + + return baseDirectionOffset + perpendicularOffset; + } + + private static Color GetConnectorColor(bool isInlet, bool isOutlet, bool isMixingInlet) + { + if (isMixingInlet) + return MixingInletColor; + + if (isInlet) + return InletColor; + + if (isOutlet) + return OutletColor; + + return Color.White; + } + + private SpriteComponent.DirectionOffset ToOffset(PipeDirection direction) + { + return direction switch + { + PipeDirection.North => SpriteComponent.DirectionOffset.Flip, + PipeDirection.East => SpriteComponent.DirectionOffset.CounterClockwise, + PipeDirection.West => SpriteComponent.DirectionOffset.Clockwise, + _ => SpriteComponent.DirectionOffset.None, }; } @@ -181,4 +410,6 @@ private enum PlumbingConnectionLayer : byte EastConnection = PipeDirection.East, WestConnection = PipeDirection.West, } + + private readonly record struct ManifoldSideSpec(PipeDirection Direction, int SlotCount); } diff --git a/Content.Client/_Starlight/Plumbing/UI/PlumbingPillPressBoundUserInterface.cs b/Content.Client/_Starlight/Plumbing/UI/PlumbingPillPressBoundUserInterface.cs index 3d377e0de0c..f215edceef3 100644 --- a/Content.Client/_Starlight/Plumbing/UI/PlumbingPillPressBoundUserInterface.cs +++ b/Content.Client/_Starlight/Plumbing/UI/PlumbingPillPressBoundUserInterface.cs @@ -25,6 +25,9 @@ protected override void Open() _window.OnSetDosage += dosage => SendMessage(new PlumbingPillPressSetDosageMessage(dosage)); + _window.OnSetLabel += label => + SendMessage(new PlumbingPillPressSetLabelMessage(label)); + _window.OnSetOutputMode += mode => SendMessage(new PlumbingPillPressSetOutputModeMessage(mode)); diff --git a/Content.Client/_Starlight/Plumbing/UI/PlumbingPillPressWindow.xaml b/Content.Client/_Starlight/Plumbing/UI/PlumbingPillPressWindow.xaml index 3f4e55b058f..971c8a38a60 100644 --- a/Content.Client/_Starlight/Plumbing/UI/PlumbingPillPressWindow.xaml +++ b/Content.Client/_Starlight/Plumbing/UI/PlumbingPillPressWindow.xaml @@ -24,6 +24,12 @@