From f7db03182a5ff596f2146937575b6dca1b892ff0 Mon Sep 17 00:00:00 2001 From: AlgisAlphonse <94876543+AlgisAlphonse@users.noreply.github.com> Date: Sun, 27 Jul 2025 14:31:36 +0200 Subject: [PATCH] Port goobstation factorio (#4035) * Initial port of goobstation factorio, missing disposals integration and faxing. Also ports impstations modification for robotic arms to have static power draw. Also adds automation slots to silos and advanced microwave. * Ports goobstation factorio fax automation, adds to the guidebook entry info about gas canisters. * Ported Goob Disposals. Removed part about taking mats out of storage silo cuz it ain't implemented yet. Seems to work. * Adds constructor circuitboard to research cuz I forgor --- .../_Goobstation/Factory/ConstructorSystem.cs | 10 + .../_Goobstation/Factory/InteractorSystem.cs | 10 + .../Factory/RoboticArmAnimationSystem.cs | 57 +++ .../_Goobstation/Factory/UI/ConstructorBUI.cs | 193 ++++++++ .../_Goobstation/Factory/UI/LabelFilterBUI.cs | 27 ++ .../Factory/UI/LabelFilterWindow.xaml | 6 + .../Factory/UI/LabelFilterWindow.xaml.cs | 37 ++ .../_Goobstation/Factory/UI/NameFilterBUI.cs | 28 ++ .../Factory/UI/NameFilterWindow.xaml | 9 + .../Factory/UI/NameFilterWindow.xaml.cs | 50 ++ .../Factory/UI/PressureFilterBUI.cs | 27 ++ .../Factory/UI/PressureFilterWindow.xaml | 19 + .../Factory/UI/PressureFilterWindow.xaml.cs | 64 +++ .../_Goobstation/Factory/UI/StackFilterBUI.cs | 28 ++ .../Factory/UI/StackFilterWindow.xaml | 17 + .../Factory/UI/StackFilterWindow.xaml.cs | 59 +++ .../Controls/GuideAutomationSlotsEmbed.cs | 52 ++ .../ConstructionSystem.Initial.cs | 172 ++++--- Content.Server/Fax/FaxSystem.cs | 11 +- .../Materials/MaterialStorageSystem.cs | 3 + .../EntitySystems/PowerReceiverSystem.cs | 4 +- Content.Server/Stack/StackSystem.cs | 2 +- .../EntitySystems/GasCanisterSignalSystem.cs | 45 ++ .../Construction/FlatpackSignalSystem.cs | 33 ++ .../Disposals/DisposalSignalSystem.cs | 41 ++ .../_Goobstation/Factory/ConstructorSystem.cs | 63 +++ .../Factory/Filters/PressureFilterSystem.cs | 37 ++ .../_Goobstation/Factory/InteractorSystem.cs | 65 +++ .../_Goobstation/Fax/FaxSignalSystem.cs | 34 ++ .../Kitchen/MicrowaveEventsSystem.cs | 28 ++ .../RadCollectorSignalComponent.cs | 26 + .../Singularity/RadCollectorSignalSystem.cs | 59 +++ .../Containers/ItemSlot/ItemSlotsSystem.cs | 4 +- .../DeviceLinking/SharedDeviceLinkSystem.cs | 26 + .../Disposal/Unit/SharedDisposalUnitSystem.cs | 5 + Content.Shared/DoAfter/DoAfterComponent.cs | 6 + Content.Shared/DoAfter/SharedDoAfterSystem.cs | 10 + .../Interaction/SharedInteractionSystem.cs | 14 +- .../Mobs/Systems/MobThresholdSystem.cs | 2 +- .../SharedPowerReceiverSystem.cs | 11 +- Content.Shared/Stacks/SharedStackSystem.cs | 10 + .../Construction/ConstructedEvent.cs | 12 + .../_Goobstation/DoAfter/DoAfterEndedEvent.cs | 12 + .../Factory/AutomatedComponent.cs | 15 + .../Factory/AutomationSlotsComponent.cs | 23 + .../_Goobstation/Factory/AutomationSystem.cs | 140 ++++++ .../Factory/ConstructorComponent.cs | 38 ++ .../Filters/AutomationFilterComponent.cs | 29 ++ .../Factory/Filters/AutomationFilterSystem.cs | 389 +++++++++++++++ .../Filters/CombinedFilterComponent.cs | 46 ++ .../Factory/Filters/FilterSlotComponent.cs | 33 ++ .../Factory/Filters/LabelFilterComponent.cs | 43 ++ .../Factory/Filters/NameFilterComponent.cs | 66 +++ .../Filters/PressureFilterComponent.cs | 48 ++ .../Factory/Filters/StackFilterComponent.cs | 50 ++ .../Factory/InteractorComponent.cs | 58 +++ .../Factory/RoboticArmComponent.cs | 172 +++++++ .../_Goobstation/Factory/RoboticArmSystem.cs | 453 ++++++++++++++++++ .../Factory/SharedConstructorSystem.cs | 66 +++ .../Factory/SharedInteractorSystem.cs | 180 +++++++ .../Factory/Slots/AutomatedContainer.cs | 59 +++ .../Factory/Slots/AutomatedHand.cs | 70 +++ .../Factory/Slots/AutomatedItemSlot.cs | 67 +++ .../Factory/Slots/AutomatedMaterialStorage.cs | 57 +++ .../Factory/Slots/AutomatedPorts.cs | 43 ++ .../Factory/Slots/AutomatedStorage.cs | 48 ++ .../Factory/Slots/AutomationSlot.cs | 117 +++++ .../Factory/StartableMachineComponent.cs | 62 +++ .../Factory/StartableMachineSystem.cs | 144 ++++++ .../Factory/StorageBinComponent.cs | 35 ++ .../_Goobstation/Factory/StorageBinSystem.cs | 47 ++ .../construction/upgrade-kits.ftl | 1 + .../_Goobstation/factory/constructor.ftl | 2 + .../en-US/_Goobstation/factory/filters.ftl | 22 + .../_Goobstation/factory/robotic-arm.ftl | 4 + .../en-US/_Goobstation/guidebook/guides.ftl | 11 + .../_Goobstation/machines/automation.ftl | 102 ++++ .../Structures/Machines/fax_machine.yml | 9 + .../Structures/Machines/flatpacker.yml | 12 + .../Entities/Structures/Machines/lathe.yml | 6 + .../Structures/Machines/microwave.yml | 7 + .../Structures/Machines/reagent_grinder.yml | 14 + .../Entities/Structures/Machines/silo.yml | 5 + .../Structures/Piping/Disposal/units.yml | 14 + .../Generation/Singularity/collector.yml | 12 + .../Storage/Canisters/gas_canisters.yml | 11 + .../Prototypes/Guidebook/engineering.yml | 1 + Resources/Prototypes/Research/industrial.yml | 9 +- .../Machines/advanced_microwave.yml | 7 + .../Entities/Structures/Machines/lathe.yml | 1 + .../_Goobstation/DeviceLink/sink_ports.yml | 108 +++++ .../_Goobstation/DeviceLink/source_ports.yml | 133 +++++ .../Objects/Devices/Circuitboards/machine.yml | 79 +++ .../Entities/Objects/Misc/filters.yml | 114 +++++ .../Entities/Objects/Tools/upgrade_kits.yml | 46 ++ .../Structures/Machines/constructor.yml | 46 ++ .../Structures/Machines/interactor.yml | 134 ++++++ .../Structures/Machines/robotic_arm.yml | 121 +++++ .../Structures/Machines/storage_bin.yml | 88 ++++ .../_Goobstation/Guidebook/engineering.yml | 11 + .../Recipes/Crafting/Graphs/filters.yml | 70 +++ .../_Goobstation/Recipes/Crafting/filters.yml | 45 ++ .../Recipes/Lathes/Packs/circuits.yml | 18 + .../Recipes/Lathes/Packs/engineering.yml | 16 + .../Recipes/Lathes/electronics.yml | 41 ++ .../Recipes/Lathes/upgrade_kits.yml | 8 + .../Entities/Structures/Machines/autodoc.yml | 9 + .../Guidebook/Engineering/Automation.xml | 146 ++++++ .../Objects/Misc/filter.rsi/icon.png | Bin 0 -> 348 bytes .../Objects/Misc/filter.rsi/meta.json | 14 + .../Machines/constructor.rsi/icon.png | Bin 0 -> 1260 bytes .../Machines/constructor.rsi/meta.json | 19 + .../Machines/constructor.rsi/powered.png | Bin 0 -> 336 bytes .../interactor.rsi/active-powered.png | Bin 0 -> 342 bytes .../Machines/interactor.rsi/active.png | Bin 0 -> 430 bytes .../Machines/interactor.rsi/base.png | Bin 0 -> 766 bytes .../Machines/interactor.rsi/empty-powered.png | Bin 0 -> 356 bytes .../Machines/interactor.rsi/empty.png | Bin 0 -> 430 bytes .../Machines/interactor.rsi/icon.png | Bin 0 -> 634 bytes .../interactor.rsi/inactive-powered.png | Bin 0 -> 336 bytes .../Machines/interactor.rsi/inactive.png | Bin 0 -> 419 bytes .../Machines/interactor.rsi/meta.json | 42 ++ .../Machines/robotic_arm.rsi/arm-long.png | Bin 0 -> 1310 bytes .../Machines/robotic_arm.rsi/arm-short.png | Bin 0 -> 1152 bytes .../Machines/robotic_arm.rsi/base.png | Bin 0 -> 1489 bytes .../Machines/robotic_arm.rsi/icon.png | Bin 0 -> 993 bytes .../Machines/robotic_arm.rsi/meta.json | 30 ++ .../Machines/robotic_arm.rsi/powered.png | Bin 0 -> 211 bytes .../Machines/storage_bin.rsi/icon.png | Bin 0 -> 621 bytes .../Machines/storage_bin.rsi/meta.json | 19 + .../Machines/storage_bin.rsi/powered.png | Bin 0 -> 256 bytes 131 files changed, 5550 insertions(+), 83 deletions(-) create mode 100644 Content.Client/_Goobstation/Factory/ConstructorSystem.cs create mode 100644 Content.Client/_Goobstation/Factory/InteractorSystem.cs create mode 100644 Content.Client/_Goobstation/Factory/RoboticArmAnimationSystem.cs create mode 100644 Content.Client/_Goobstation/Factory/UI/ConstructorBUI.cs create mode 100644 Content.Client/_Goobstation/Factory/UI/LabelFilterBUI.cs create mode 100644 Content.Client/_Goobstation/Factory/UI/LabelFilterWindow.xaml create mode 100644 Content.Client/_Goobstation/Factory/UI/LabelFilterWindow.xaml.cs create mode 100644 Content.Client/_Goobstation/Factory/UI/NameFilterBUI.cs create mode 100644 Content.Client/_Goobstation/Factory/UI/NameFilterWindow.xaml create mode 100644 Content.Client/_Goobstation/Factory/UI/NameFilterWindow.xaml.cs create mode 100644 Content.Client/_Goobstation/Factory/UI/PressureFilterBUI.cs create mode 100644 Content.Client/_Goobstation/Factory/UI/PressureFilterWindow.xaml create mode 100644 Content.Client/_Goobstation/Factory/UI/PressureFilterWindow.xaml.cs create mode 100644 Content.Client/_Goobstation/Factory/UI/StackFilterBUI.cs create mode 100644 Content.Client/_Goobstation/Factory/UI/StackFilterWindow.xaml create mode 100644 Content.Client/_Goobstation/Factory/UI/StackFilterWindow.xaml.cs create mode 100644 Content.Client/_Goobstation/Guidebook/Controls/GuideAutomationSlotsEmbed.cs create mode 100644 Content.Server/_Goobstation/Atmos/EntitySystems/GasCanisterSignalSystem.cs create mode 100644 Content.Server/_Goobstation/Construction/FlatpackSignalSystem.cs create mode 100644 Content.Server/_Goobstation/Disposals/DisposalSignalSystem.cs create mode 100644 Content.Server/_Goobstation/Factory/ConstructorSystem.cs create mode 100644 Content.Server/_Goobstation/Factory/Filters/PressureFilterSystem.cs create mode 100644 Content.Server/_Goobstation/Factory/InteractorSystem.cs create mode 100644 Content.Server/_Goobstation/Fax/FaxSignalSystem.cs create mode 100644 Content.Server/_Goobstation/Kitchen/MicrowaveEventsSystem.cs create mode 100644 Content.Server/_Goobstation/Singularity/RadCollectorSignalComponent.cs create mode 100644 Content.Server/_Goobstation/Singularity/RadCollectorSignalSystem.cs create mode 100644 Content.Shared/_Goobstation/Construction/ConstructedEvent.cs create mode 100644 Content.Shared/_Goobstation/DoAfter/DoAfterEndedEvent.cs create mode 100644 Content.Shared/_Goobstation/Factory/AutomatedComponent.cs create mode 100644 Content.Shared/_Goobstation/Factory/AutomationSlotsComponent.cs create mode 100644 Content.Shared/_Goobstation/Factory/AutomationSystem.cs create mode 100644 Content.Shared/_Goobstation/Factory/ConstructorComponent.cs create mode 100644 Content.Shared/_Goobstation/Factory/Filters/AutomationFilterComponent.cs create mode 100644 Content.Shared/_Goobstation/Factory/Filters/AutomationFilterSystem.cs create mode 100644 Content.Shared/_Goobstation/Factory/Filters/CombinedFilterComponent.cs create mode 100644 Content.Shared/_Goobstation/Factory/Filters/FilterSlotComponent.cs create mode 100644 Content.Shared/_Goobstation/Factory/Filters/LabelFilterComponent.cs create mode 100644 Content.Shared/_Goobstation/Factory/Filters/NameFilterComponent.cs create mode 100644 Content.Shared/_Goobstation/Factory/Filters/PressureFilterComponent.cs create mode 100644 Content.Shared/_Goobstation/Factory/Filters/StackFilterComponent.cs create mode 100644 Content.Shared/_Goobstation/Factory/InteractorComponent.cs create mode 100644 Content.Shared/_Goobstation/Factory/RoboticArmComponent.cs create mode 100644 Content.Shared/_Goobstation/Factory/RoboticArmSystem.cs create mode 100644 Content.Shared/_Goobstation/Factory/SharedConstructorSystem.cs create mode 100644 Content.Shared/_Goobstation/Factory/SharedInteractorSystem.cs create mode 100644 Content.Shared/_Goobstation/Factory/Slots/AutomatedContainer.cs create mode 100644 Content.Shared/_Goobstation/Factory/Slots/AutomatedHand.cs create mode 100644 Content.Shared/_Goobstation/Factory/Slots/AutomatedItemSlot.cs create mode 100644 Content.Shared/_Goobstation/Factory/Slots/AutomatedMaterialStorage.cs create mode 100644 Content.Shared/_Goobstation/Factory/Slots/AutomatedPorts.cs create mode 100644 Content.Shared/_Goobstation/Factory/Slots/AutomatedStorage.cs create mode 100644 Content.Shared/_Goobstation/Factory/Slots/AutomationSlot.cs create mode 100644 Content.Shared/_Goobstation/Factory/StartableMachineComponent.cs create mode 100644 Content.Shared/_Goobstation/Factory/StartableMachineSystem.cs create mode 100644 Content.Shared/_Goobstation/Factory/StorageBinComponent.cs create mode 100644 Content.Shared/_Goobstation/Factory/StorageBinSystem.cs create mode 100644 Resources/Locale/en-US/_Goobstation/construction/upgrade-kits.ftl create mode 100644 Resources/Locale/en-US/_Goobstation/factory/constructor.ftl create mode 100644 Resources/Locale/en-US/_Goobstation/factory/filters.ftl create mode 100644 Resources/Locale/en-US/_Goobstation/factory/robotic-arm.ftl create mode 100644 Resources/Locale/en-US/_Goobstation/guidebook/guides.ftl create mode 100644 Resources/Locale/en-US/_Goobstation/machines/automation.ftl create mode 100644 Resources/Prototypes/_Goobstation/DeviceLink/sink_ports.yml create mode 100644 Resources/Prototypes/_Goobstation/DeviceLink/source_ports.yml create mode 100644 Resources/Prototypes/_Goobstation/Entities/Objects/Devices/Circuitboards/machine.yml create mode 100644 Resources/Prototypes/_Goobstation/Entities/Objects/Misc/filters.yml create mode 100644 Resources/Prototypes/_Goobstation/Entities/Objects/Tools/upgrade_kits.yml create mode 100644 Resources/Prototypes/_Goobstation/Entities/Structures/Machines/constructor.yml create mode 100644 Resources/Prototypes/_Goobstation/Entities/Structures/Machines/interactor.yml create mode 100644 Resources/Prototypes/_Goobstation/Entities/Structures/Machines/robotic_arm.yml create mode 100644 Resources/Prototypes/_Goobstation/Entities/Structures/Machines/storage_bin.yml create mode 100644 Resources/Prototypes/_Goobstation/Guidebook/engineering.yml create mode 100644 Resources/Prototypes/_Goobstation/Recipes/Crafting/Graphs/filters.yml create mode 100644 Resources/Prototypes/_Goobstation/Recipes/Crafting/filters.yml create mode 100644 Resources/Prototypes/_Goobstation/Recipes/Lathes/Packs/circuits.yml create mode 100644 Resources/Prototypes/_Goobstation/Recipes/Lathes/Packs/engineering.yml create mode 100644 Resources/Prototypes/_Goobstation/Recipes/Lathes/electronics.yml create mode 100644 Resources/Prototypes/_Goobstation/Recipes/Lathes/upgrade_kits.yml create mode 100644 Resources/ServerInfo/_Goobstation/Guidebook/Engineering/Automation.xml create mode 100644 Resources/Textures/_Goobstation/Objects/Misc/filter.rsi/icon.png create mode 100644 Resources/Textures/_Goobstation/Objects/Misc/filter.rsi/meta.json create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/constructor.rsi/icon.png create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/constructor.rsi/meta.json create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/constructor.rsi/powered.png create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/interactor.rsi/active-powered.png create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/interactor.rsi/active.png create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/interactor.rsi/base.png create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/interactor.rsi/empty-powered.png create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/interactor.rsi/empty.png create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/interactor.rsi/icon.png create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/interactor.rsi/inactive-powered.png create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/interactor.rsi/inactive.png create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/interactor.rsi/meta.json create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/robotic_arm.rsi/arm-long.png create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/robotic_arm.rsi/arm-short.png create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/robotic_arm.rsi/base.png create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/robotic_arm.rsi/icon.png create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/robotic_arm.rsi/meta.json create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/robotic_arm.rsi/powered.png create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/storage_bin.rsi/icon.png create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/storage_bin.rsi/meta.json create mode 100644 Resources/Textures/_Goobstation/Structures/Machines/storage_bin.rsi/powered.png diff --git a/Content.Client/_Goobstation/Factory/ConstructorSystem.cs b/Content.Client/_Goobstation/Factory/ConstructorSystem.cs new file mode 100644 index 0000000000..3ec5a6136c --- /dev/null +++ b/Content.Client/_Goobstation/Factory/ConstructorSystem.cs @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 deltanedas <@deltanedas:kde.org> +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Shared._Goobstation.Factory; + +namespace Content.Client._Goobstation.Factory; + +public sealed class ConstructorSystem : SharedConstructorSystem; diff --git a/Content.Client/_Goobstation/Factory/InteractorSystem.cs b/Content.Client/_Goobstation/Factory/InteractorSystem.cs new file mode 100644 index 0000000000..416328db89 --- /dev/null +++ b/Content.Client/_Goobstation/Factory/InteractorSystem.cs @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 deltanedas <@deltanedas:kde.org> +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Shared._Goobstation.Factory; + +namespace Content.Client._Goobstation.Factory; + +public sealed class InteractorSystem : SharedInteractorSystem; diff --git a/Content.Client/_Goobstation/Factory/RoboticArmAnimationSystem.cs b/Content.Client/_Goobstation/Factory/RoboticArmAnimationSystem.cs new file mode 100644 index 0000000000..26b0f25a14 --- /dev/null +++ b/Content.Client/_Goobstation/Factory/RoboticArmAnimationSystem.cs @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 deltanedas <@deltanedas:kde.org> +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Shared._Goobstation.Factory; +using Robust.Client.GameObjects; +using Robust.Shared.Timing; + +namespace Content.Client._Goobstation.Factory; + +/// +/// Animations robotic arm's arm layer swinging. +/// Can't be done with engine AnimationPlayer as it can't animate individual layers. +/// +public sealed class RoboticArmAnimationSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + + public override void FrameUpdate(float frameTime) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp)) + { + if (comp.ItemSlot == null) + continue; + + if (comp.NextMove is {} nextMove) + Animate((uid, comp), nextMove); + else + Reset((uid, comp)); + } + } + + private void Animate(Entity ent, TimeSpan nextMove) + { + if (!TryComp(ent, out var sprite)) + return; + + var started = nextMove - ent.Comp.MoveDelay; + // 0-1 unless something weird happens + var progress = (_timing.CurTime - started) / ent.Comp.MoveDelay; + if (!ent.Comp.HasItem) // returning to the resting position when emptied + progress = 1f - progress; + var angle = Angle.FromDegrees(progress * 180f); + sprite.LayerSetRotation(RoboticArmLayers.Arm, angle); + } + + private void Reset(Entity ent) + { + if (!TryComp(ent, out var sprite)) + return; + + var angle = ent.Comp.HasItem ? new Angle(Math.PI) : Angle.Zero; + sprite.LayerSetRotation(RoboticArmLayers.Arm, angle); + } +} diff --git a/Content.Client/_Goobstation/Factory/UI/ConstructorBUI.cs b/Content.Client/_Goobstation/Factory/UI/ConstructorBUI.cs new file mode 100644 index 0000000000..ec96ff4e1a --- /dev/null +++ b/Content.Client/_Goobstation/Factory/UI/ConstructorBUI.cs @@ -0,0 +1,193 @@ +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 deltanedas <39013340+deltanedas@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 deltanedas <@deltanedas:kde.org> +// SPDX-FileCopyrightText: 2025 gluesniffler <159397573+gluesniffler@users.noreply.github.com> +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Client.Construction; +using Content.Client.Construction.UI; +using Content.Shared._Goobstation.Factory; +using Content.Shared.Construction.Prototypes; +using Content.Shared.Whitelist; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.Prototypes; +using System.Linq; + +namespace Content.Client._Goobstation.Factory.UI; + +public sealed class ConstructorBUI : BoundUserInterface +{ + [Dependency] private readonly IPrototypeManager _proto = default!; + private readonly ConstructionSystem _construction; + private readonly EntityWhitelistSystem _whitelist; + private readonly SpriteSystem _sprite; + + private ConstructionMenu? _menu; + private string? _id; + private List _recipes = new(); + private readonly LocId _favoriteCatName = "construction-category-favorites"; + private readonly LocId _forAllCategoryName = "construction-category-all"; + + public ConstructorBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + _construction = EntMan.System(); + _whitelist = EntMan.System(); + _sprite = EntMan.System(); + + _id = EntMan.GetComponentOrNull(owner)?.Construction; + } + + protected override void Open() + { + base.Open(); + + // god BLESS whoever made construction ui for having it so decoupled <3 + _menu = this.CreateWindow(); + PopulateCategories(); + PopulateRecipes(string.Empty, string.Empty); + _menu.PopulateRecipes += (_, args) => PopulateRecipes(args.Item1, args.Item2); + _menu.RecipeSelected += (_, item) => + { + _menu.ClearRecipeInfo(); + if (item != null && item.Prototype != null) + { + _id = item.Prototype.ID; + _menu.SetRecipeInfo(item.Prototype.Name ?? "", item.Prototype.Description ?? "", item?.TargetPrototype, + item!.Prototype.Type != ConstructionType.Item, true); // TODO: favourites + + GenerateStepList(item.Prototype); + } + else + { + _id = null; + } + }; + _menu.BuildButtonToggled += (_, _) => + { + SendPredictedMessage(new ConstructorSetProtoMessage(_id)); + _menu.Close(); + }; + } + + private void PopulateCategories(string? selected = null) + { + if (_menu is not {} menu) + return; + + var categories = new HashSet(); + + foreach (var prototype in _proto.EnumeratePrototypes()) + { + var category = prototype.Category; + + if (!string.IsNullOrEmpty(category)) + categories.Add(category); + } + + var categoriesArray = new string[categories.Count + 1]; + + // hard-coded to show all recipes + var idx = 0; + categoriesArray[idx++] = _forAllCategoryName; + + foreach (var cat in categories.OrderBy(Loc.GetString)) + { + categoriesArray[idx++] = cat; + } + + menu.OptionCategories.Clear(); + + for (var i = 0; i < categoriesArray.Length; i++) + { + menu.OptionCategories.AddItem(Loc.GetString(categoriesArray[i]), i); + + if (!string.IsNullOrEmpty(selected) && selected == categoriesArray[i]) + menu.OptionCategories.SelectId(i); + } + + menu.Categories = categoriesArray; + } + + // copypasted and optimised from ConstructionMenuPresenter + private void PopulateRecipes(string search, string category) + { + if (PlayerManager.LocalEntity is not { } user + || _menu is not { } menu) + return; + + search = search.Trim().ToLowerInvariant(); + var searching = !string.IsNullOrEmpty(search); + var isEmptyCategory = string.IsNullOrEmpty(category) || category == _forAllCategoryName; + + _recipes.Clear(); + foreach (var recipe in _proto.EnumeratePrototypes()) + { + if (recipe.Hide) + continue; + + if (_whitelist.IsWhitelistFail(recipe.EntityWhitelist, user)) + continue; + + if (searching + && recipe.Name != null + && !recipe.Name.ToLowerInvariant().Contains(search)) + continue; + + if (!isEmptyCategory) + { + // TODO: when favourites get sent from server do this + // currently its specific to the G menu + //if (!_favoritedRecipes.Contains(recipe)) + if (category == _favoriteCatName) + continue; + else if (recipe.Category != category) + continue; + } + + if (!_construction!.TryGetRecipePrototype(recipe.ID, out var targetProtoId)) + continue; + + if (!_proto.TryIndex(targetProtoId, out EntityPrototype? proto)) + continue; + + _recipes.Add(new(recipe, proto)); + } + + _recipes.Sort((a, b) => string.Compare(a.Prototype.Name, b.Prototype.Name, StringComparison.InvariantCulture)); + + var recipesList = menu.Recipes; + recipesList.PopulateList(_recipes); + + menu.RecipesGridScrollContainer.Visible = false; + menu.Recipes.Visible = true; + } + + private void GenerateStepList(ConstructionPrototype proto) + { + if (_construction.GetGuide(proto) is not { } guide + || _menu is not { } menu) + return; + + var list = menu.RecipeStepList; + foreach (var entry in guide.Entries) + { + var text = entry.Arguments != null + ? Loc.GetString(entry.Localization, entry.Arguments) + : Loc.GetString(entry.Localization); + + if (entry.EntryNumber is { } number) + text = Loc.GetString("construction-presenter-step-wrapper", + ("step-number", number), ("text", text)); + + // The padding needs to be applied regardless of text length... (See PadLeft documentation) + text = text.PadLeft(text.Length + entry.Padding); + + var icon = entry.Icon != null ? _sprite.Frame0(entry.Icon) : Texture.Transparent; + list.AddItem(text, icon, false); + } + } +} diff --git a/Content.Client/_Goobstation/Factory/UI/LabelFilterBUI.cs b/Content.Client/_Goobstation/Factory/UI/LabelFilterBUI.cs new file mode 100644 index 0000000000..3575a71cfe --- /dev/null +++ b/Content.Client/_Goobstation/Factory/UI/LabelFilterBUI.cs @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 deltanedas <@deltanedas:kde.org> +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Shared._Goobstation.Factory.Filters; +using Robust.Client.UserInterface; + +namespace Content.Client._Goobstation.Factory.UI; + +public sealed class LabelFilterBUI : BoundUserInterface +{ + private LabelFilterWindow? _window; + + public LabelFilterBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + _window = this.CreateWindow(); + _window.SetEntity(Owner); + _window.OnSetLabel += label => SendPredictedMessage(new LabelFilterSetLabelMessage(label)); + } +} diff --git a/Content.Client/_Goobstation/Factory/UI/LabelFilterWindow.xaml b/Content.Client/_Goobstation/Factory/UI/LabelFilterWindow.xaml new file mode 100644 index 0000000000..fcd5c77795 --- /dev/null +++ b/Content.Client/_Goobstation/Factory/UI/LabelFilterWindow.xaml @@ -0,0 +1,6 @@ + + + diff --git a/Content.Client/_Goobstation/Factory/UI/LabelFilterWindow.xaml.cs b/Content.Client/_Goobstation/Factory/UI/LabelFilterWindow.xaml.cs new file mode 100644 index 0000000000..9f590e6b3a --- /dev/null +++ b/Content.Client/_Goobstation/Factory/UI/LabelFilterWindow.xaml.cs @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 deltanedas <@deltanedas:kde.org> +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Client.UserInterface.Controls; +using Content.Shared._Goobstation.Factory.Filters; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client._Goobstation.Factory.UI; + +[GenerateTypedNameReferences] +public sealed partial class LabelFilterWindow : FancyWindow +{ + [Dependency] private readonly EntityManager _entMan = default!; + + public event Action? OnSetLabel; + + public LabelFilterWindow() + { + IoCManager.InjectDependencies(this); + RobustXamlLoader.Load(this); + + LabelEdit.OnTextChanged += _ => OnSetLabel?.Invoke(LabelEdit.Text); + } + + public void SetEntity(EntityUid uid) + { + if (!_entMan.TryGetComponent(uid, out var comp)) + return; + + var max = comp.MaxLength; + LabelEdit.IsValid = label => label.Length < max; + LabelEdit.Text = comp.Label; + } +} diff --git a/Content.Client/_Goobstation/Factory/UI/NameFilterBUI.cs b/Content.Client/_Goobstation/Factory/UI/NameFilterBUI.cs new file mode 100644 index 0000000000..5e902beebb --- /dev/null +++ b/Content.Client/_Goobstation/Factory/UI/NameFilterBUI.cs @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 deltanedas <@deltanedas:kde.org> +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Shared._Goobstation.Factory.Filters; +using Robust.Client.UserInterface; + +namespace Content.Client._Goobstation.Factory.UI; + +public sealed class NameFilterBUI : BoundUserInterface +{ + private NameFilterWindow? _window; + + public NameFilterBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + _window = this.CreateWindow(); + _window.SetEntity(Owner); + _window.OnSetName += name => SendPredictedMessage(new NameFilterSetNameMessage(name)); + _window.OnSetMode += mode => SendPredictedMessage(new NameFilterSetModeMessage(mode)); + } +} diff --git a/Content.Client/_Goobstation/Factory/UI/NameFilterWindow.xaml b/Content.Client/_Goobstation/Factory/UI/NameFilterWindow.xaml new file mode 100644 index 0000000000..8b17039fc4 --- /dev/null +++ b/Content.Client/_Goobstation/Factory/UI/NameFilterWindow.xaml @@ -0,0 +1,9 @@ + + + + + + diff --git a/Content.Client/_Goobstation/Factory/UI/NameFilterWindow.xaml.cs b/Content.Client/_Goobstation/Factory/UI/NameFilterWindow.xaml.cs new file mode 100644 index 0000000000..b0efa656a4 --- /dev/null +++ b/Content.Client/_Goobstation/Factory/UI/NameFilterWindow.xaml.cs @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 deltanedas <@deltanedas:kde.org> +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Client.UserInterface.Controls; +using Content.Shared._Goobstation.Factory.Filters; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client._Goobstation.Factory.UI; + +[GenerateTypedNameReferences] +public sealed partial class NameFilterWindow : FancyWindow +{ + [Dependency] private readonly EntityManager _entMan = default!; + + public event Action? OnSetName; + public event Action? OnSetMode; + + public NameFilterWindow() + { + IoCManager.InjectDependencies(this); + RobustXamlLoader.Load(this); + + foreach (var mode in Enum.GetValues()) + { + ModeButton.AddItem(Loc.GetString($"name-filter-mode-{mode}"), (int) mode); + } + + ModeButton.OnItemSelected += args => + { + ModeButton.SelectId(args.Id); + OnSetMode?.Invoke((NameFilterMode) args.Id); + }; + + NameEdit.OnTextChanged += _ => OnSetName?.Invoke(NameEdit.Text); + } + + public void SetEntity(EntityUid uid) + { + if (!_entMan.TryGetComponent(uid, out var comp)) + return; + + ModeButton.SelectId((int) comp.Mode); + var max = comp.MaxLength; + NameEdit.IsValid = name => name.Length < max; + NameEdit.Text = comp.Name; + } +} diff --git a/Content.Client/_Goobstation/Factory/UI/PressureFilterBUI.cs b/Content.Client/_Goobstation/Factory/UI/PressureFilterBUI.cs new file mode 100644 index 0000000000..7d8e172ba8 --- /dev/null +++ b/Content.Client/_Goobstation/Factory/UI/PressureFilterBUI.cs @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2025 deltanedas <@deltanedas:kde.org> +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Shared._Goobstation.Factory.Filters; +using Robust.Client.UserInterface; + +namespace Content.Client._Goobstation.Factory.UI; + +public sealed class PressureFilterBUI : BoundUserInterface +{ + private PressureFilterWindow? _window; + + public PressureFilterBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + _window = this.CreateWindow(); + _window.SetEntity(Owner); + _window.OnSetMin += min => SendPredictedMessage(new PressureFilterSetMinMessage(min)); + _window.OnSetMax += max => SendPredictedMessage(new PressureFilterSetMaxMessage(max)); + } +} diff --git a/Content.Client/_Goobstation/Factory/UI/PressureFilterWindow.xaml b/Content.Client/_Goobstation/Factory/UI/PressureFilterWindow.xaml new file mode 100644 index 0000000000..206508a937 --- /dev/null +++ b/Content.Client/_Goobstation/Factory/UI/PressureFilterWindow.xaml @@ -0,0 +1,19 @@ + + + +