From ccf9d55f84ba0f1ce4b802f05839a190652912a8 Mon Sep 17 00:00:00 2001
From: CREAsTIVE <62616308+CREAsTIVE@users.noreply.github.com>
Date: Mon, 2 Feb 2026 18:52:33 +0500
Subject: [PATCH 01/12] feat: UI update
---
.../Chemistry/UI/ReagentDispenserWindow.xaml | 73 ++++++++++---------
1 file changed, 37 insertions(+), 36 deletions(-)
diff --git a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml
index af778556c01..2c8961e38c8 100644
--- a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml
+++ b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml
@@ -23,56 +23,52 @@ SPDX-License-Identifier: AGPL-3.0-or-later
Title="{Loc 'reagent-dispenser-bound-user-interface-title'}"
MinSize="600 300"
SetSize="800 500">
-
-
-
+
+
+
-
+
+
+
+
+
+
+
+
+
+ Text=""/>
+
-
-
-
-
-
-
-
-
-
+
-
+
+
+
From 764d26192371316877d6859a2b6e3f887353a1d5 Mon Sep 17 00:00:00 2001
From: CREAsTIVE <62616308+CREAsTIVE@users.noreply.github.com>
Date: Mon, 2 Feb 2026 22:31:32 +0500
Subject: [PATCH 02/12] feat: removed container from dispenser
---
.../Chemistry/UI/ReagentCardControl.xaml | 16 ---
.../Chemistry/UI/ReagentCardControl.xaml.cs | 24 ++--
.../UI/ReagentDispenserBoundUserInterface.cs | 5 +-
.../Chemistry/UI/ReagentDispenserWindow.xaml | 50 ++++---
.../UI/ReagentDispenserWindow.xaml.cs | 25 ++--
.../Components/ReagentDispenserComponent.cs | 11 +-
.../EntitySystems/ReagentDispenserSystem.cs | 135 ++++++++++--------
.../ReagentDispenserInventoryPrototype.cs | 30 ++++
.../Chemistry/SharedReagentDispenser.cs | 24 +---
.../Dispensers/base_structuredispensers.yml | 7 +-
.../Entities/Structures/Dispensers/booze.yml | 5 -
.../Entities/Structures/Dispensers/chem.yml | 7 +-
.../Entities/Structures/Dispensers/soda.yml | 5 -
13 files changed, 176 insertions(+), 168 deletions(-)
create mode 100644 Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs
diff --git a/Content.Client/Chemistry/UI/ReagentCardControl.xaml b/Content.Client/Chemistry/UI/ReagentCardControl.xaml
index 41bf884b60c..81820fad833 100644
--- a/Content.Client/Chemistry/UI/ReagentCardControl.xaml
+++ b/Content.Client/Chemistry/UI/ReagentCardControl.xaml
@@ -9,10 +9,6 @@ SPDX-License-Identifier: AGPL-3.0-or-later
-
-
diff --git a/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs b/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs
index 93e24ac5e6a..e769f34ff33 100644
--- a/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs
+++ b/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs
@@ -4,6 +4,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
using Content.Shared.Chemistry;
+using Content.Shared.Chemistry.Reagent;
using Content.Shared.Storage;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
@@ -15,24 +16,15 @@ namespace Content.Client.Chemistry.UI;
[GenerateTypedNameReferences]
public sealed partial class ReagentCardControl : Control
{
- public ItemStorageLocation StorageLocation { get; }
- public Action? OnPressed;
- public Action? OnEjectButtonPressed;
+ public ReagentId ReagentId { get; private init; }
+ public Action? OnPressed;
- public ReagentCardControl(ReagentInventoryItem item)
+ public ReagentCardControl(ReagentId reagentId, ReagentPrototype? prototype)
{
RobustXamlLoader.Load(this);
- StorageLocation = item.StorageLocation;
- ColorPanel.PanelOverride = new StyleBoxFlat { BackgroundColor = item.ReagentColor };
- ReagentNameLabel.Text = item.ReagentLabel;
- FillLabel.Text = Loc.GetString("reagent-dispenser-window-quantity-label-text", ("quantity", item.Quantity));;
- EjectButtonIcon.Text = Loc.GetString("reagent-dispenser-window-eject-container-button");
-
- if (item.Quantity == 0.0)
- MainButton.Disabled = true;
-
- MainButton.OnPressed += args => OnPressed?.Invoke(StorageLocation);
- EjectButton.OnPressed += args => OnEjectButtonPressed?.Invoke(StorageLocation);
+ ReagentId = reagentId;
+ ReagentNameLabel.Text = prototype?.LocalizedName ?? Loc.GetString("reagent-dispenser-window-reagent-name-not-found-text");
+ MainButton.OnPressed += args => OnPressed?.Invoke(ReagentId);
}
-}
\ No newline at end of file
+}
diff --git a/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs b/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs
index 9d6ed87fa34..a13e2e6dbe6 100644
--- a/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs
+++ b/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs
@@ -68,8 +68,7 @@ protected override void Open()
_window.AmountGrid.OnButtonPressed += s => SendMessage(new ReagentDispenserSetDispenseAmountMessage(s));
- _window.OnDispenseReagentButtonPressed += (location) => SendMessage(new ReagentDispenserDispenseReagentMessage(location));
- _window.OnEjectJugButtonPressed += (location) => SendMessage(new ReagentDispenserEjectContainerMessage(location));
+ _window.OnDispenseReagentButtonPressed += (reagent) => SendMessage(new ReagentDispenserDispenseReagentMessage(reagent));
}
///
@@ -87,4 +86,4 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window?.UpdateState(castState); //Update window state
}
}
-}
\ No newline at end of file
+}
diff --git a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml
index 2c8961e38c8..5161672650f 100644
--- a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml
+++ b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml
@@ -46,26 +46,34 @@ SPDX-License-Identifier: AGPL-3.0-or-later
Access="Public"
Columns="3" />
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -87,10 +95,6 @@ SPDX-License-Identifier: AGPL-3.0-or-later
-
diff --git a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs
index 676781ad8f4..2b2c69fc8cd 100644
--- a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs
+++ b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs
@@ -41,6 +41,7 @@
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
+using System.Linq;
using static Robust.Client.UserInterface.Controls.BoxContainer;
namespace Content.Client.Chemistry.UI
@@ -53,8 +54,7 @@ public sealed partial class ReagentDispenserWindow : FancyWindow
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
- public event Action? OnDispenseReagentButtonPressed;
- public event Action? OnEjectJugButtonPressed;
+ public event Action? OnDispenseReagentButtonPressed;
///
/// Create and initialize the dispenser UI client-side. Creates the basic layout,
@@ -70,20 +70,27 @@ public ReagentDispenserWindow()
/// Update the button grid of reagents which can be dispensed.
///
/// Reagents which can be dispensed by this dispenser
- public void UpdateReagentsList(List inventory)
+ public void UpdateReagentsList(List inventory)
{
if (ReagentList == null)
return;
ReagentList.Children.Clear();
- //Sort inventory by reagentLabel
- inventory.Sort((x, y) => x.ReagentLabel.CompareTo(y.ReagentLabel));
- foreach (var item in inventory)
+ string untranslatedText = Loc.GetString("reagent-dispenser-window-reagent-name-not-found-text");
+
+ var reagentPrototypes = inventory
+ // Convert reagentId[] to (reagentId, prototype)[]
+ .Select(reagent => (reagent, prototype: _prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? prototype) ? prototype : null))
+ .ToList(); // Copy, but should be fine since there not that much reagents
+
+ // Sort reagents by name
+ reagentPrototypes.Sort((x, y) => x.prototype?.LocalizedName?.CompareTo(y.prototype?.LocalizedName ?? untranslatedText) ?? 0);
+
+ foreach (var item in reagentPrototypes)
{
- var card = new ReagentCardControl(item);
+ var card = new ReagentCardControl(item.reagent, item.prototype);
card.OnPressed += OnDispenseReagentButtonPressed;
- card.OnEjectButtonPressed += OnEjectJugButtonPressed;
ReagentList.Children.Add(card);
}
}
@@ -156,4 +163,4 @@ public void UpdateContainerInfo(ReagentDispenserBoundUserInterfaceState state)
}
}
}
-}
\ No newline at end of file
+}
diff --git a/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs b/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs
index a038cde3db7..77c2ff29121 100644
--- a/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs
+++ b/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs
@@ -60,6 +60,7 @@
using Content.Shared.Chemistry;
using Robust.Shared.Audio;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using Content.Shared.Chemistry.Dispenser;
namespace Content.Server.Chemistry.Components
{
@@ -78,5 +79,13 @@ public sealed partial class ReagentDispenserComponent : Component
[ViewVariables(VVAccess.ReadWrite)]
public ReagentDispenserDispenseAmount DispenseAmount = ReagentDispenserDispenseAmount.U10;
+
+ [DataField("pack", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ [ViewVariables(VVAccess.ReadWrite)]
+ public string? PackPrototypeId = default!;
+
+ [DataField("emagPack", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ [ViewVariables(VVAccess.ReadWrite)]
+ public string? EmagPackPrototypeId = default!;
}
-}
\ No newline at end of file
+}
diff --git a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
index c7a347a5c82..e96b44ea9ff 100644
--- a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
+++ b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
@@ -33,13 +33,22 @@
//
// SPDX-License-Identifier: AGPL-3.0-or-later
-using System.Linq;
+using Content.Goobstation.Maths.FixedPoint;
using Content.Server.Chemistry.Components;
+using Content.Server.Hands.Systems;
using Content.Shared.Chemistry;
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Chemistry.Components.SolutionManager;
+using Content.Shared.Chemistry.Dispenser;
using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.Chemistry.Reagent;
using Content.Shared.Containers.ItemSlots;
-using Content.Goobstation.Maths.FixedPoint;
+using Content.Shared.Database;
+using Content.Shared.Emag.Components;
+using Content.Shared.Labels.Components;
using Content.Shared.Nutrition.EntitySystems;
+using Content.Shared.Storage;
+using Content.Shared.Storage.Components;
using Content.Shared.Storage.EntitySystems;
using JetBrains.Annotations;
using Robust.Server.Audio;
@@ -47,9 +56,11 @@
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
-using Content.Shared.Labels.Components;
-using Content.Shared.Storage;
-using Content.Server.Hands.Systems;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Mapping;
+using Robust.Shared.Serialization.Markdown.Sequence;
+using Robust.Shared.Serialization.Markdown.Value;
+using System.Linq;
namespace Content.Server.Chemistry.EntitySystems
{
@@ -81,7 +92,6 @@ public override void Initialize()
SubscribeLocalEvent(OnSetDispenseAmountMessage);
SubscribeLocalEvent(OnDispenseReagentMessage);
- SubscribeLocalEvent(OnEjectReagentMessage);
SubscribeLocalEvent(OnClearContainerSolutionMessage);
SubscribeLocalEvent(OnMapInit, before: new[] { typeof(ItemSlotsSystem) });
@@ -97,7 +107,7 @@ private void UpdateUiState(Entity reagentDispenser)
var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenser, SharedReagentDispenser.OutputSlotName);
var outputContainerInfo = BuildOutputContainerInfo(outputContainer);
- var inventory = GetInventory(reagentDispenser);
+ var inventory = GetInventory(reagentDispenser).ToList(); // TODO: Another copy, optimize
var state = new ReagentDispenserBoundUserInterfaceState(outputContainerInfo, GetNetEntity(outputContainer), inventory, reagentDispenser.Comp.DispenseAmount);
_userInterfaceSystem.SetUiState(reagentDispenser.Owner, ReagentDispenserUiKey.Key, state);
@@ -119,33 +129,70 @@ private void UpdateUiState(Entity reagentDispenser)
return null;
}
- private List GetInventory(Entity reagentDispenser)
+ private HashSet GetInventory(Entity dispenserEnt)
{
- if (!TryComp(reagentDispenser.Owner, out var storage))
+ var inventory = new HashSet();
+ var dispenserComponent = dispenserEnt.Comp;
+ if (TryComp(dispenserEnt, out var storageFillComp))
{
- return [];
+ foreach (var item in storageFillComp.Contents)
+ {
+ if (!_prototypeManager.TryIndex(item.PrototypeId, out EntityPrototype? entityPrototype))
+ continue;
+
+ if (!entityPrototype.Components.TryGetValue("SolutionContainerManager", out var data))
+ continue;
+
+ if (!data.Mapping.TryGet("solutions", out var solutions))
+ continue;
+
+ foreach (var maybeSolution in solutions.Values)
+ {
+ if (!(maybeSolution is MappingDataNode solution))
+ continue;
+
+ if (!solution.TryGet("reagents", out var reagents))
+ continue;
+
+ foreach(var maybeReagent in reagents)
+ {
+ if (!(maybeReagent is MappingDataNode reagent))
+ continue;
+
+ if (!reagent.TryGet("ReagentId", out var reagentId))
+ continue;
+
+ // Finded!
+ inventory.Add(new(reagentId.Value, null));
+ }
+ }
+ }
+
}
- var inventory = new List();
+ // Collect reagents from packs:
- foreach (var (storedContainer, storageLocation) in storage.StoredItems)
+ if (
+ dispenserComponent.PackPrototypeId is not null
+ && _prototypeManager.TryIndex(dispenserComponent.PackPrototypeId, out ReagentDispenserInventoryPrototype? packPrototype)
+ )
{
- string reagentLabel;
- if (TryComp(storedContainer, out var label) && !string.IsNullOrEmpty(label.CurrentLabel))
- reagentLabel = label.CurrentLabel;
- else
- reagentLabel = Name(storedContainer);
-
- // Get volume remaining and color of solution
- FixedPoint2 quantity = 0f;
- var reagentColor = Color.White;
- if (_solutionContainerSystem.TryGetDrainableSolution(storedContainer, out _, out var sol))
+ foreach (var reagentId in packPrototype.Inventory)
{
- quantity = sol.Volume;
- reagentColor = sol.GetColor(_prototypeManager);
+ inventory.Add(new(reagentId, null));
}
+ }
- inventory.Add(new ReagentInventoryItem(storageLocation, reagentLabel, quantity, reagentColor));
+ if (
+ HasComp(dispenserEnt)
+ && dispenserComponent.EmagPackPrototypeId is not null
+ && _prototypeManager.TryIndex(dispenserComponent.EmagPackPrototypeId, out ReagentDispenserInventoryPrototype? emagPackPrototype)
+ )
+ {
+ foreach (var reagentId in emagPackPrototype.Inventory)
+ {
+ inventory.Add(new(reagentId, null));
+ }
}
return inventory;
@@ -160,51 +207,19 @@ private void OnSetDispenseAmountMessage(Entity reagen
private void OnDispenseReagentMessage(Entity reagentDispenser, ref ReagentDispenserDispenseReagentMessage message)
{
- if (!TryComp(reagentDispenser.Owner, out var storage))
- {
- return;
- }
-
- // Ensure that the reagent is something this reagent dispenser can dispense.
- var storageLocation = message.StorageLocation;
- var storedContainer = storage.StoredItems.FirstOrDefault(kvp => kvp.Value == storageLocation).Key;
- if (storedContainer == EntityUid.Invalid)
- return;
+ var reagentId = message.ReagentId;
var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenser, SharedReagentDispenser.OutputSlotName);
if (outputContainer is not { Valid: true } || !_solutionContainerSystem.TryGetFitsInDispenser(outputContainer.Value, out var solution, out _))
return;
- if (_solutionContainerSystem.TryGetDrainableSolution(storedContainer, out var src, out _) &&
- _solutionContainerSystem.TryGetRefillableSolution(outputContainer.Value, out var dst, out _))
- {
- // force open container, if applicable, to avoid confusing people on why it doesn't dispense
- _openable.SetOpen(storedContainer, true);
- _solutionTransferSystem.Transfer(reagentDispenser,
- storedContainer, src.Value,
- outputContainer.Value, dst.Value,
- (int)reagentDispenser.Comp.DispenseAmount);
- }
+ // sollution is not null because [NotNullWhen(true)]
+ _solutionContainerSystem.TryAddReagent(solution ?? throw new(), reagentId.Prototype, (int) reagentDispenser.Comp.DispenseAmount, out var dispensed);
UpdateUiState(reagentDispenser);
ClickSound(reagentDispenser);
}
- private void OnEjectReagentMessage(Entity reagentDispenser, ref ReagentDispenserEjectContainerMessage message)
- {
- if (!TryComp(reagentDispenser.Owner, out var storage))
- {
- return;
- }
-
- var storageLocation = message.StorageLocation;
- var storedContainer = storage.StoredItems.FirstOrDefault(kvp => kvp.Value == storageLocation).Key;
- if (storedContainer == EntityUid.Invalid)
- return;
-
- _handsSystem.TryPickupAnyHand(message.Actor, storedContainer);
- }
-
private void OnClearContainerSolutionMessage(Entity reagentDispenser, ref ReagentDispenserClearContainerSolutionMessage message)
{
var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenser, SharedReagentDispenser.OutputSlotName);
diff --git a/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs b/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs
new file mode 100644
index 00000000000..c5cab4ccfb6
--- /dev/null
+++ b/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs
@@ -0,0 +1,30 @@
+using Content.Shared.Chemistry.Reagent;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Content.Shared.Chemistry.Dispenser
+{
+ ///
+ /// Is simply a list of reagents defined in yaml. This can then be set as a
+ /// reagent despenser pack value (also in yaml),
+ /// to define which reagents it's able to dispense. Based off of how vending
+ /// machines define their inventory.
+ ///
+ [Prototype("reagentDispenserInventory")]
+ [DataDefinition]
+ public sealed partial class ReagentDispenserInventoryPrototype : IPrototype
+ {
+ [ViewVariables, IdDataField]
+ public string ID { get; private set; } = default!;
+
+ // TODO use ReagentId
+ [DataField("inventory", customTypeSerializer: typeof(PrototypeIdListSerializer))]
+ public List Inventory = new();
+ }
+}
diff --git a/Content.Shared/Chemistry/SharedReagentDispenser.cs b/Content.Shared/Chemistry/SharedReagentDispenser.cs
index 8f058e28e36..4ba85bdd8c3 100644
--- a/Content.Shared/Chemistry/SharedReagentDispenser.cs
+++ b/Content.Shared/Chemistry/SharedReagentDispenser.cs
@@ -81,25 +81,11 @@ public ReagentDispenserSetDispenseAmountMessage(String s)
[Serializable, NetSerializable]
public sealed class ReagentDispenserDispenseReagentMessage : BoundUserInterfaceMessage
{
- public readonly ItemStorageLocation StorageLocation;
+ public readonly ReagentId ReagentId;
- public ReagentDispenserDispenseReagentMessage(ItemStorageLocation storageLocation)
+ public ReagentDispenserDispenseReagentMessage(ReagentId storageLocation)
{
- StorageLocation = storageLocation;
- }
- }
-
- ///
- /// Message sent by the user interface to ask the reagent dispenser to eject a container
- ///
- [Serializable, NetSerializable]
- public sealed class ReagentDispenserEjectContainerMessage : BoundUserInterfaceMessage
- {
- public readonly ItemStorageLocation StorageLocation;
-
- public ReagentDispenserEjectContainerMessage(ItemStorageLocation storageLocation)
- {
- StorageLocation = storageLocation;
+ ReagentId = storageLocation;
}
}
@@ -141,11 +127,11 @@ public sealed class ReagentDispenserBoundUserInterfaceState : BoundUserInterface
///
/// A list of the reagents which this dispenser can dispense.
///
- public readonly List Inventory;
+ public readonly List Inventory;
public readonly ReagentDispenserDispenseAmount SelectedDispenseAmount;
- public ReagentDispenserBoundUserInterfaceState(ContainerInfo? outputContainer, NetEntity? outputContainerEntity, List inventory, ReagentDispenserDispenseAmount selectedDispenseAmount)
+ public ReagentDispenserBoundUserInterfaceState(ContainerInfo? outputContainer, NetEntity? outputContainerEntity, List inventory, ReagentDispenserDispenseAmount selectedDispenseAmount)
{
OutputContainer = outputContainer;
OutputContainerEntity = outputContainerEntity;
diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml b/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml
index 0602e51c937..cabc21ca510 100644
--- a/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml
+++ b/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml
@@ -102,10 +102,6 @@
- !type:PlaySoundBehavior
sound:
collection: MetalGlassBreak
- - type: Storage
- maxItemSize: Normal
- grid:
- - 0,0,19,5
- type: ReagentDispenser
beakerSlot:
whitelistFailPopup: reagent-dispenser-component-cannot-put-entity-message
@@ -119,7 +115,6 @@
machine_board: !type:Container
machine_parts: !type:Container
beakerSlot: !type:ContainerSlot
- storagebase: !type:Container
- type: StaticPrice
price: 1000
- - type: WiresPanel
\ No newline at end of file
+ - type: WiresPanel
diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml b/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml
index c10862baa5f..b8e3ab9069b 100644
--- a/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml
+++ b/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml
@@ -111,11 +111,6 @@
sprite: Structures/smalldispensers.rsi
drawdepth: SmallObjects
state: booze
- - type: Storage
- openOnActivate: false
- whitelist:
- tags:
- - DrinkBottle
- type: StorageFill
contents:
- id: DrinkAleBottleFullGrowler
diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml b/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml
index da48dc8506c..df0fd2543d4 100644
--- a/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml
+++ b/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml
@@ -51,15 +51,12 @@
parent: ReagentDispenserBase
description: An industrial grade chemical dispenser.
components:
+# - type: ReagentDispenser
+# pack: ChemDispenserStandardInventory
- type: Sprite
sprite: Structures/dispensers.rsi
state: industrial-working
snapCardinals: true
- - type: Storage
- openOnActivate: false
- whitelist:
- tags:
- - ChemDispensable
- type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: Destructible
diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml b/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml
index 80d7b4327c2..23ff7e04e7a 100644
--- a/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml
+++ b/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml
@@ -112,11 +112,6 @@
sprite: Structures/smalldispensers.rsi
drawdepth: SmallObjects
state: soda
- - type: Storage
- openOnActivate: false
- whitelist:
- tags:
- - DrinkBottle
- type: StorageFill
contents:
- id: DrinkCoconutWaterJug
From 6be78b688a3a50d18da079871ee8d6fd995dc466 Mon Sep 17 00:00:00 2001
From: CREAsTIVE <62616308+CREAsTIVE@users.noreply.github.com>
Date: Tue, 3 Feb 2026 00:23:47 +0500
Subject: [PATCH 03/12] feat: better reagent dispenser UI
---
.../Chemistry/UI/ReagentCardControl.xaml | 2 +-
.../Chemistry/UI/ReagentDispenserWindow.xaml | 26 +++++++------------
.../UI/ReagentDispenserWindow.xaml.cs | 3 +--
3 files changed, 12 insertions(+), 19 deletions(-)
diff --git a/Content.Client/Chemistry/UI/ReagentCardControl.xaml b/Content.Client/Chemistry/UI/ReagentCardControl.xaml
index 81820fad833..d30c6655429 100644
--- a/Content.Client/Chemistry/UI/ReagentCardControl.xaml
+++ b/Content.Client/Chemistry/UI/ReagentCardControl.xaml
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
VerticalExpand="True"
HorizontalExpand="True"
Margin="-5 0 0 0">
-
+
diff --git a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml
index 5161672650f..a29dcf40e78 100644
--- a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml
+++ b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml
@@ -21,10 +21,10 @@ SPDX-License-Identifier: AGPL-3.0-or-later
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
xmlns:ui="clr-namespace:Content.Client.Chemistry.UI"
Title="{Loc 'reagent-dispenser-bound-user-interface-title'}"
- MinSize="600 300"
+ MinSize="450 300"
SetSize="800 500">
-
+
-
-
-
+ Columns="4" />
-
-
+
+
+
-
+
+ Text="{Loc 'reagent-dispenser-window-no-container-loaded-text'}"/>
-
+ Scale="2 2"/>
-
diff --git a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs
index 2b2c69fc8cd..14270fe1948 100644
--- a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs
+++ b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs
@@ -127,9 +127,8 @@ public void UpdateContainerInfo(ReagentDispenserBoundUserInterfaceState state)
if (state.OutputContainer is null)
{
- ContainerInfoName.Text = "";
+ ContainerInfoName.Text = Loc.GetString("reagent-dispenser-window-no-container-loaded-text");
ContainerInfoFill.Text = "";
- ContainerInfo.Children.Add(new Label { Text = Loc.GetString("reagent-dispenser-window-no-container-loaded-text") });
return;
}
From 4d5ed50694a038cebc7a7934d1f93d0d0437110b Mon Sep 17 00:00:00 2001
From: CREAsTIVE <62616308+CREAsTIVE@users.noreply.github.com>
Date: Tue, 3 Feb 2026 01:17:39 +0500
Subject: [PATCH 04/12] fix: repeating reagents
---
.../EntitySystems/ReagentDispenserSystem.cs | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
index e96b44ea9ff..fa8f4d805c7 100644
--- a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
+++ b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
@@ -129,10 +129,12 @@ private void UpdateUiState(Entity reagentDispenser)
return null;
}
- private HashSet GetInventory(Entity dispenserEnt)
+ private IEnumerable GetInventory(Entity dispenserEnt)
{
- var inventory = new HashSet();
+ var inventory = new HashSet();
var dispenserComponent = dispenserEnt.Comp;
+
+ // Collect reagents from items provided by SolutionContainerManager; TODO: include parent
if (TryComp(dispenserEnt, out var storageFillComp))
{
foreach (var item in storageFillComp.Contents)
@@ -163,7 +165,7 @@ private HashSet GetInventory(Entity dispen
continue;
// Finded!
- inventory.Add(new(reagentId.Value, null));
+ inventory.Add(reagentId.Value);
}
}
}
@@ -179,7 +181,7 @@ dispenserComponent.PackPrototypeId is not null
{
foreach (var reagentId in packPrototype.Inventory)
{
- inventory.Add(new(reagentId, null));
+ inventory.Add(reagentId);
}
}
@@ -191,11 +193,11 @@ dispenserComponent.PackPrototypeId is not null
{
foreach (var reagentId in emagPackPrototype.Inventory)
{
- inventory.Add(new(reagentId, null));
+ inventory.Add(reagentId);
}
}
- return inventory;
+ return inventory.Select(id => new ReagentId(id, null));
}
private void OnSetDispenseAmountMessage(Entity reagentDispenser, ref ReagentDispenserSetDispenseAmountMessage message)
From b2411eaae92e234490690483204b1a1ff7190e12 Mon Sep 17 00:00:00 2001
From: CREAsTIVE <62616308+CREAsTIVE@users.noreply.github.com>
Date: Tue, 3 Feb 2026 01:57:24 +0500
Subject: [PATCH 05/12] refactor: catalogs instead of container fillers (+
optimization)
---
.../Prototypes/Catalog/reagent_dispenser.yml | 94 +++++++++++++++++++
.../Entities/Structures/Dispensers/booze.yml | 24 +----
.../Entities/Structures/Dispensers/chem.yml | 31 +-----
.../Entities/Structures/Dispensers/soda.yml | 26 +----
4 files changed, 106 insertions(+), 69 deletions(-)
create mode 100644 Resources/Prototypes/Catalog/reagent_dispenser.yml
diff --git a/Resources/Prototypes/Catalog/reagent_dispenser.yml b/Resources/Prototypes/Catalog/reagent_dispenser.yml
new file mode 100644
index 00000000000..7b004e614d2
--- /dev/null
+++ b/Resources/Prototypes/Catalog/reagent_dispenser.yml
@@ -0,0 +1,94 @@
+- type: reagentDispenserInventory
+ id: ChemDispenserStandardInventory
+ inventory:
+ - Aluminium
+ - Carbon
+ - Chlorine
+ - Copper
+ - Ethanol
+ - Fluorine
+ - Sugar
+ - Hydrogen
+ - Iodine
+ - Iron
+ - Lithium
+ - Mercury
+ - Nitrogen
+ - Oxygen
+ - Phosphorus
+ - Potassium
+ - Radium
+ - Silicon
+ - Sodium
+ - Sulfur
+
+- type: reagentDispenserInventory
+ id: ChemDispenserEmaggedInventory
+ inventory: ##Feel free to change this to something more interesting when more chems are added
+ - Napalm
+ - Toxin
+ - Epinephrine
+ - Ultravasculine
+
+
+- type: reagentDispenserInventory
+ id: SodaDispenserInventory
+ inventory:
+ - Ice
+ - Coffee
+ - Cream
+ - Tea
+ - GreenTea
+ - IcedTea
+ - IcedGreenTea
+ - Cola
+ - SpaceMountainWind
+ - DrGibb
+ - RootBeer
+ - SpaceUp
+ - TonicWater
+ - SodaWater
+ - LemonLime
+ - Sugar
+ - JuiceOrange
+ - JuiceLime
+ - JuiceWatermelon
+ ###Hacked
+ #- Fourteen Loko
+ #- GrapeSoda
+
+- type: reagentDispenserInventory
+ id: SodaDispenserEmagInventory
+ inventory:
+ - FourteenLoko
+ - Ephedrine
+ - Histamine
+
+- type: reagentDispenserInventory
+ id: BoozeDispenserInventory
+ inventory:
+ - Beer
+ - CoffeeLiqueur
+ - Whiskey
+ - Wine
+ - Vodka
+ - Gin
+ - Rum
+ - Tequila
+ - Vermouth
+ - Cognac
+ - Ale
+ - Mead
+ ###Hacked
+ #- Goldschlager
+ #- Patron
+ #- JuiceWatermelon
+ #- JuiceBerry
+
+- type: reagentDispenserInventory
+ id: BoozeDispenserEmagInventory
+ inventory:
+ - AtomicBomb
+ - Ethanol
+ - Iron
+
diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml b/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml
index b8e3ab9069b..99d3242d65c 100644
--- a/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml
+++ b/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml
@@ -111,20 +111,9 @@
sprite: Structures/smalldispensers.rsi
drawdepth: SmallObjects
state: booze
- - type: StorageFill
- contents:
- - id: DrinkAleBottleFullGrowler
- - id: DrinkBeerGrowler
- - id: DrinkCoffeeLiqueurBottleFull
- - id: DrinkCognacBottleFull
- - id: DrinkGinBottleFull
- - id: DrinkMeadJug
- - id: DrinkRumBottleFull
- - id: DrinkTequilaBottleFull
- - id: DrinkVermouthBottleFull
- - id: DrinkVodkaBottleFull
- - id: DrinkWhiskeyBottleFull
- - id: DrinkWineBottleFull
+ - type: ReagentDispenser
+ pack: BoozeDispenserInventory
+ emagPack: BoozeDispenserEmagInventory
- type: Transform
noRot: false
- type: Machine
@@ -137,11 +126,6 @@
stealGroup: BoozeDispenser
- type: entity
- id: BoozeDispenserEmpty
+ id: BoozeDispenserEmpty # Exists for backwards compatibility
suffix: Empty
parent: BoozeDispenser
- components:
- - type: Storage
- whitelist:
- tags:
- - DrinkBottle
diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml b/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml
index df0fd2543d4..e3fec905e5b 100644
--- a/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml
+++ b/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml
@@ -51,8 +51,9 @@
parent: ReagentDispenserBase
description: An industrial grade chemical dispenser.
components:
-# - type: ReagentDispenser
-# pack: ChemDispenserStandardInventory
+ - type: ReagentDispenser
+ pack: ChemDispenserStandardInventory
+ emagPack: ChemDispenserEmaggedInventory
- type: Sprite
sprite: Structures/dispensers.rsi
state: industrial-working
@@ -93,30 +94,6 @@
- MachineLayer
- type: entity
- id: ChemDispenser
+ id: ChemDispenser # Exists for backward compatibility
suffix: Filled
parent: ChemDispenserEmpty
- components:
- - type: ReagentDispenser
- - type: StorageFill
- contents:
- - id: JugAluminium
- - id: JugCarbon
- - id: JugChlorine
- - id: JugCopper
- - id: JugEthanol
- - id: JugFluorine
- - id: JugSugar
- - id: JugHydrogen
- - id: JugIodine
- - id: JugIron
- - id: JugLithium
- - id: JugMercury
- - id: JugNitrogen
- - id: JugOxygen
- - id: JugPhosphorus
- - id: JugPotassium
- - id: JugRadium
- - id: JugSilicon
- - id: JugSodium
- - id: JugSulfur
diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml b/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml
index 23ff7e04e7a..6e4789e4f59 100644
--- a/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml
+++ b/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml
@@ -112,27 +112,9 @@
sprite: Structures/smalldispensers.rsi
drawdepth: SmallObjects
state: soda
- - type: StorageFill
- contents:
- - id: DrinkCoconutWaterJug
- - id: DrinkCoffeeJug
- - id: DrinkColaBottleFull
- - id: DrinkCreamCartonXL
- - id: DrinkDrGibbJug
- - id: DrinkEnergyDrinkJug
- - id: DrinkGreenTeaJug
- - id: DrinkIceJug
- - id: DrinkJuiceLimeCartonXL
- - id: DrinkJuiceOrangeCartonXL
- - id: DrinkLemonLimeJug
- - id: DrinkRootBeerJug
- - id: DrinkSodaWaterBottleFull
- - id: DrinkSpaceMountainWindBottleFull
- - id: DrinkSpaceUpBottleFull
- - id: DrinkSugarJug
- - id: DrinkTeaJug
- - id: DrinkTonicWaterBottleFull
- - id: DrinkWaterMelonJuiceJug
+ - type: ReagentDispenser
+ pack: SodaDispenserInventory
+ emagPack: SodaDispenserEmagInventory
- type: Transform
noRot: false
- type: Machine
@@ -144,5 +126,5 @@
- type: entity
parent: SodaDispenser
- id: SodaDispenserEmpty
+ id: SodaDispenserEmpty # Exists for backwards compatibility
suffix: Empty
From 2196a7e1b87055c1509aad0629d8c494f4ddd0e6 Mon Sep 17 00:00:00 2001
From: CREAsTIVE <62616308+CREAsTIVE@users.noreply.github.com>
Date: Tue, 3 Feb 2026 02:23:12 +0500
Subject: [PATCH 06/12] refactor: removed unused and typo fixes
---
.../Chemistry/EntitySystems/ReagentDispenserSystem.cs | 3 ++-
.../Dispenser/ReagentDispenserInventoryPrototype.cs | 2 +-
Content.Shared/Chemistry/SharedReagentDispenser.cs | 9 ---------
.../Structures/Dispensers/base_structuredispensers.yml | 2 --
4 files changed, 3 insertions(+), 13 deletions(-)
diff --git a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
index fa8f4d805c7..ccacfac61aa 100644
--- a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
+++ b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
@@ -60,6 +60,7 @@
using Robust.Shared.Serialization.Markdown.Mapping;
using Robust.Shared.Serialization.Markdown.Sequence;
using Robust.Shared.Serialization.Markdown.Value;
+using System.Diagnostics;
using System.Linq;
namespace Content.Server.Chemistry.EntitySystems
@@ -216,7 +217,7 @@ private void OnDispenseReagentMessage(Entity reagentD
return;
// sollution is not null because [NotNullWhen(true)]
- _solutionContainerSystem.TryAddReagent(solution ?? throw new(), reagentId.Prototype, (int) reagentDispenser.Comp.DispenseAmount, out var dispensed);
+ _solutionContainerSystem.TryAddReagent(solution ?? throw new UnreachableException(), reagentId.Prototype, (int) reagentDispenser.Comp.DispenseAmount, out var dispensed);
UpdateUiState(reagentDispenser);
ClickSound(reagentDispenser);
diff --git a/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs b/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs
index c5cab4ccfb6..8306c17afd0 100644
--- a/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs
+++ b/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs
@@ -12,7 +12,7 @@ namespace Content.Shared.Chemistry.Dispenser
{
///
/// Is simply a list of reagents defined in yaml. This can then be set as a
- /// reagent despenser pack value (also in yaml),
+ /// reagent dispenser pack value (also in yaml),
/// to define which reagents it's able to dispense. Based off of how vending
/// machines define their inventory.
///
diff --git a/Content.Shared/Chemistry/SharedReagentDispenser.cs b/Content.Shared/Chemistry/SharedReagentDispenser.cs
index 4ba85bdd8c3..c1134b6cf54 100644
--- a/Content.Shared/Chemistry/SharedReagentDispenser.cs
+++ b/Content.Shared/Chemistry/SharedReagentDispenser.cs
@@ -108,15 +108,6 @@ public enum ReagentDispenserDispenseAmount
U100 = 100,
}
- [Serializable, NetSerializable]
- public sealed class ReagentInventoryItem(ItemStorageLocation storageLocation, string reagentLabel, FixedPoint2 quantity, Color reagentColor)
- {
- public ItemStorageLocation StorageLocation = storageLocation;
- public string ReagentLabel = reagentLabel;
- public FixedPoint2 Quantity = quantity;
- public Color ReagentColor = reagentColor;
- }
-
[Serializable, NetSerializable]
public sealed class ReagentDispenserBoundUserInterfaceState : BoundUserInterfaceState
{
diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml b/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml
index cabc21ca510..930db660979 100644
--- a/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml
+++ b/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml
@@ -78,8 +78,6 @@
interfaces:
enum.ReagentDispenserUiKey.Key:
type: ReagentDispenserBoundUserInterface
- enum.StorageUiKey.Key:
- type: StorageBoundUserInterface
- type: Anchorable
- type: Pullable
- type: Damageable
From 2e496f28ccacc0cb9e6875f6ad056c4fcb44ec1a Mon Sep 17 00:00:00 2001
From: CREAsTIVE <62616308+CREAsTIVE@users.noreply.github.com>
Date: Tue, 3 Feb 2026 23:23:23 +0500
Subject: [PATCH 07/12] feat: charges to dispense chem
---
.../Chemistry/UI/ReagentCardControl.xaml | 10 +-
.../Chemistry/UI/ReagentCardControl.xaml.cs | 4 +-
.../Chemistry/UI/ReagentDispenserWindow.xaml | 10 +-
.../UI/ReagentDispenserWindow.xaml.cs | 19 ++-
.../Components/ReagentDispenserComponent.cs | 6 +
.../EntitySystems/ReagentDispenserSystem.cs | 107 +++++++++++----
.../ReagentDispenserInventoryPrototype.cs | 4 +-
.../Chemistry/SharedReagentDispenser.cs | 22 +++-
.../reagent-dispenser-component.ftl | 1 +
.../reagent-dispenser-component.ftl | 1 +
.../Prototypes/Catalog/reagent_dispenser.yml | 122 +++++++++---------
.../Entities/Structures/Dispensers/booze.yml | 8 ++
.../Entities/Structures/Dispensers/chem.yml | 10 +-
.../Entities/Structures/Dispensers/soda.yml | 8 ++
14 files changed, 230 insertions(+), 102 deletions(-)
diff --git a/Content.Client/Chemistry/UI/ReagentCardControl.xaml b/Content.Client/Chemistry/UI/ReagentCardControl.xaml
index d30c6655429..1e3a5138136 100644
--- a/Content.Client/Chemistry/UI/ReagentCardControl.xaml
+++ b/Content.Client/Chemistry/UI/ReagentCardControl.xaml
@@ -15,11 +15,15 @@ SPDX-License-Identifier: AGPL-3.0-or-later
StyleClasses="ButtonSquare"
Margin="-1 0 0 0">
-
-
+ Margin="0 0 0 0">
+
+
+
+
+
diff --git a/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs b/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs
index e769f34ff33..34180e29a1e 100644
--- a/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs
+++ b/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs
@@ -17,14 +17,16 @@ namespace Content.Client.Chemistry.UI;
public sealed partial class ReagentCardControl : Control
{
public ReagentId ReagentId { get; private init; }
+ public int Cost { get; private set; }
public Action? OnPressed;
- public ReagentCardControl(ReagentId reagentId, ReagentPrototype? prototype)
+ public ReagentCardControl(ReagentId reagentId, ReagentPrototype? prototype, int cost)
{
RobustXamlLoader.Load(this);
ReagentId = reagentId;
ReagentNameLabel.Text = prototype?.LocalizedName ?? Loc.GetString("reagent-dispenser-window-reagent-name-not-found-text");
MainButton.OnPressed += args => OnPressed?.Invoke(ReagentId);
+ ReagentCostLabel.Text = cost.ToString();
}
}
diff --git a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml
index a29dcf40e78..6d2aeae79c5 100644
--- a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml
+++ b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml
@@ -44,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
-
+
+ StyleClasses="LabelSubText"/>
+
+
+
+
+
+
/// Reagents which can be dispensed by this dispenser
- public void UpdateReagentsList(List inventory)
+ public void UpdateReagentsList(List<(ReagentId reagent, int cost)> inventory, int amount)
{
if (ReagentList == null)
return;
@@ -81,7 +81,11 @@ public void UpdateReagentsList(List inventory)
var reagentPrototypes = inventory
// Convert reagentId[] to (reagentId, prototype)[]
- .Select(reagent => (reagent, prototype: _prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? prototype) ? prototype : null))
+ .Select(reagentPair => (
+ reagentPair.reagent,
+ prototype: _prototypeManager.TryIndex(reagentPair.reagent.Prototype, out ReagentPrototype? prototype) ? prototype : null,
+ reagentPair.cost
+ ))
.ToList(); // Copy, but should be fine since there not that much reagents
// Sort reagents by name
@@ -89,7 +93,7 @@ public void UpdateReagentsList(List inventory)
foreach (var item in reagentPrototypes)
{
- var card = new ReagentCardControl(item.reagent, item.prototype);
+ var card = new ReagentCardControl(item.reagent, item.prototype, item.cost * amount);
card.OnPressed += OnDispenseReagentButtonPressed;
ReagentList.Children.Add(card);
}
@@ -103,7 +107,7 @@ public void UpdateState(BoundUserInterfaceState state)
{
var castState = (ReagentDispenserBoundUserInterfaceState) state;
UpdateContainerInfo(castState);
- UpdateReagentsList(castState.Inventory);
+ UpdateReagentsList(castState.Inventory, (int) castState.SelectedDispenseAmount);
_entityManager.TryGetEntity(castState.OutputContainerEntity, out var outputContainerEnt);
View.SetEntity(outputContainerEnt);
@@ -113,6 +117,13 @@ public void UpdateState(BoundUserInterfaceState state)
EjectButton.Disabled = castState.OutputContainer is null;
AmountGrid.Selected = ((int)castState.SelectedDispenseAmount).ToString();
+
+ // Show charge amount
+ ChargeAmount.Visible = castState.Charges is not null;
+ if (castState.Charges is not null)
+ {
+ ChargeAmountLabel.Text = castState.Charges.ToString();
+ }
}
///
diff --git a/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs b/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs
index 77c2ff29121..3a9853efa35 100644
--- a/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs
+++ b/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs
@@ -80,6 +80,12 @@ public sealed partial class ReagentDispenserComponent : Component
[ViewVariables(VVAccess.ReadWrite)]
public ReagentDispenserDispenseAmount DispenseAmount = ReagentDispenserDispenseAmount.U10;
+ ///
+ /// Used for reagents that got collected from
+ ///
+ [DataField("defaultReagentCost")]
+ public int DefaultReagentCost = 30;
+
[DataField("pack", customTypeSerializer: typeof(PrototypeIdSerializer))]
[ViewVariables(VVAccess.ReadWrite)]
public string? PackPrototypeId = default!;
diff --git a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
index ccacfac61aa..05981e4bf01 100644
--- a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
+++ b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
@@ -34,8 +34,10 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
using Content.Goobstation.Maths.FixedPoint;
+using Content.Server.Charges;
using Content.Server.Chemistry.Components;
using Content.Server.Hands.Systems;
+using Content.Shared.Charges.Components;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
@@ -51,6 +53,7 @@
using Content.Shared.Storage.Components;
using Content.Shared.Storage.EntitySystems;
using JetBrains.Annotations;
+using JetBrains.FormatRipper.MachO;
using Robust.Server.Audio;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
@@ -60,6 +63,7 @@
using Robust.Shared.Serialization.Markdown.Mapping;
using Robust.Shared.Serialization.Markdown.Sequence;
using Robust.Shared.Serialization.Markdown.Value;
+using Robust.Shared.Utility;
using System.Diagnostics;
using System.Linq;
@@ -80,6 +84,7 @@ public sealed class ReagentDispenserSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly OpenableSystem _openable = default!;
[Dependency] private readonly HandsSystem _handsSystem = default!;
+ [Dependency] private readonly ChargesSystem _chargesSystem = default!;
public override void Initialize()
{
@@ -98,20 +103,51 @@ public override void Initialize()
SubscribeLocalEvent(OnMapInit, before: new[] { typeof(ItemSlotsSystem) });
}
+ private float _updateTimer = 0f;
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ _updateTimer += frameTime;
+ if (_updateTimer >= 1f)
+ {
+ _updateTimer = 0f;
+ var query = EntityQueryEnumerator();
+
+ while (query.MoveNext(out var uid, out var reagentDispenserComp, out var uiComp, out var charges))
+ {
+ UpdateUiState(uid, reagentDispenserComp);
+ }
+ }
+ }
+
private void SubscribeUpdateUiState(Entity ent, ref T ev)
{
- UpdateUiState(ent);
+ UpdateUiState(ent.Owner, ent.Comp);
}
- private void UpdateUiState(Entity reagentDispenser)
+ private void UpdateUiState(EntityUid reagentDispenserEnt, ReagentDispenserComponent reagentDispenserComp)
{
- var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenser, SharedReagentDispenser.OutputSlotName);
+ var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenserEnt, SharedReagentDispenser.OutputSlotName);
var outputContainerInfo = BuildOutputContainerInfo(outputContainer);
- var inventory = GetInventory(reagentDispenser).ToList(); // TODO: Another copy, optimize
+ var inventory = GetInventory(reagentDispenserEnt, reagentDispenserComp).ToList(); // TODO: Another copy, optimize
+
+ int? charges = null;
+ if (TryComp(reagentDispenserEnt, out LimitedChargesComponent? comp))
+ {
+ charges = _chargesSystem.GetCurrentCharges(reagentDispenserEnt);
+ }
+
+ var state = new ReagentDispenserBoundUserInterfaceState(
+ outputContainerInfo,
+ GetNetEntity(outputContainer),
+ inventory,
+ reagentDispenserComp.DispenseAmount,
+ charges
+ );
- var state = new ReagentDispenserBoundUserInterfaceState(outputContainerInfo, GetNetEntity(outputContainer), inventory, reagentDispenser.Comp.DispenseAmount);
- _userInterfaceSystem.SetUiState(reagentDispenser.Owner, ReagentDispenserUiKey.Key, state);
+ _userInterfaceSystem.SetUiState(reagentDispenserEnt, ReagentDispenserUiKey.Key, state);
}
private ContainerInfo? BuildOutputContainerInfo(EntityUid? container)
@@ -130,10 +166,9 @@ private void UpdateUiState(Entity reagentDispenser)
return null;
}
- private IEnumerable GetInventory(Entity dispenserEnt)
+ private IEnumerable<(ReagentId reagent, int cost)> GetInventory(EntityUid dispenserEnt, ReagentDispenserComponent dispenserComp)
{
- var inventory = new HashSet();
- var dispenserComponent = dispenserEnt.Comp;
+ var inventory = new Dictionary();
// Collect reagents from items provided by SolutionContainerManager; TODO: include parent
if (TryComp(dispenserEnt, out var storageFillComp))
@@ -157,7 +192,7 @@ private IEnumerable GetInventory(Entity di
if (!solution.TryGet("reagents", out var reagents))
continue;
- foreach(var maybeReagent in reagents)
+ foreach (var maybeReagent in reagents)
{
if (!(maybeReagent is MappingDataNode reagent))
continue;
@@ -166,7 +201,7 @@ private IEnumerable GetInventory(Entity di
continue;
// Finded!
- inventory.Add(reagentId.Value);
+ inventory.Add(reagentId.Value, dispenserComp.DefaultReagentCost);
}
}
}
@@ -176,50 +211,78 @@ private IEnumerable GetInventory(Entity di
// Collect reagents from packs:
if (
- dispenserComponent.PackPrototypeId is not null
- && _prototypeManager.TryIndex(dispenserComponent.PackPrototypeId, out ReagentDispenserInventoryPrototype? packPrototype)
+ dispenserComp.PackPrototypeId is not null
+ && _prototypeManager.TryIndex(dispenserComp.PackPrototypeId, out ReagentDispenserInventoryPrototype? packPrototype)
)
{
foreach (var reagentId in packPrototype.Inventory)
{
- inventory.Add(reagentId);
+ inventory.Add(reagentId.Key, reagentId.Value);
}
}
if (
HasComp(dispenserEnt)
- && dispenserComponent.EmagPackPrototypeId is not null
- && _prototypeManager.TryIndex(dispenserComponent.EmagPackPrototypeId, out ReagentDispenserInventoryPrototype? emagPackPrototype)
+ && dispenserComp.EmagPackPrototypeId is not null
+ && _prototypeManager.TryIndex(dispenserComp.EmagPackPrototypeId, out ReagentDispenserInventoryPrototype? emagPackPrototype)
)
{
foreach (var reagentId in emagPackPrototype.Inventory)
{
- inventory.Add(reagentId);
+ inventory.Add(reagentId.Key, reagentId.Value);
}
}
- return inventory.Select(id => new ReagentId(id, null));
+ return inventory.Select(pair => (new ReagentId(pair.Key, null), pair.Value));
}
private void OnSetDispenseAmountMessage(Entity reagentDispenser, ref ReagentDispenserSetDispenseAmountMessage message)
{
reagentDispenser.Comp.DispenseAmount = message.ReagentDispenserDispenseAmount;
- UpdateUiState(reagentDispenser);
+ UpdateUiState(reagentDispenser.Owner, reagentDispenser.Comp);
ClickSound(reagentDispenser);
}
private void OnDispenseReagentMessage(Entity reagentDispenser, ref ReagentDispenserDispenseReagentMessage message)
{
var reagentId = message.ReagentId;
+ var reagentDispenserComp = reagentDispenser.Comp;
var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenser, SharedReagentDispenser.OutputSlotName);
if (outputContainer is not { Valid: true } || !_solutionContainerSystem.TryGetFitsInDispenser(outputContainer.Value, out var solution, out _))
return;
+ // Check if we even can dispense that
+ var inventory = GetInventory(reagentDispenser.Owner, reagentDispenser.Comp);
+ if (!inventory.TryFirstOrNull(data => data.reagent == reagentId, out var inventoryElement))
+ return;
+
+ var singleCost = inventoryElement?.cost ?? 1; // "?? 1" should be unreachable
+
+ float requestedDispenseAmount = (int) reagentDispenserComp.DispenseAmount;
+
+ // How much would be dispensed (less then required, if there not enough charges)
+ float totalDispenseAmount = requestedDispenseAmount;
+
+ bool hasCharges;
+ if (hasCharges = TryComp(reagentDispenser.Owner, out LimitedChargesComponent? charges))
+ {
+ var avalaibleCharges = _chargesSystem.GetCurrentCharges(reagentDispenser.Owner);
+
+ totalDispenseAmount = requestedDispenseAmount;
+ if (requestedDispenseAmount * singleCost > avalaibleCharges)
+ totalDispenseAmount = avalaibleCharges / singleCost;
+ }
+
// sollution is not null because [NotNullWhen(true)]
- _solutionContainerSystem.TryAddReagent(solution ?? throw new UnreachableException(), reagentId.Prototype, (int) reagentDispenser.Comp.DispenseAmount, out var dispensed);
+ _solutionContainerSystem.TryAddReagent(solution ?? throw new UnreachableException(), reagentId.Prototype, totalDispenseAmount, out var dispensed);
+
+ if (hasCharges && dispensed > float.Epsilon)
+ {
+ _chargesSystem.AddCharges(reagentDispenser.Owner, -((int) (MathF.Ceiling((float) dispensed * singleCost) + float.Epsilon))); // Ceil dispensed amount up (should be safe)
+ }
- UpdateUiState(reagentDispenser);
+ UpdateUiState(reagentDispenser.Owner, reagentDispenser.Comp);
ClickSound(reagentDispenser);
}
@@ -230,7 +293,7 @@ private void OnClearContainerSolutionMessage(Entity r
return;
_solutionContainerSystem.RemoveAllSolution(solution.Value);
- UpdateUiState(reagentDispenser);
+ UpdateUiState(reagentDispenser.Owner, reagentDispenser.Comp);
ClickSound(reagentDispenser);
}
diff --git a/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs b/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs
index 8306c17afd0..63e3ee014b0 100644
--- a/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs
+++ b/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs
@@ -24,7 +24,7 @@ public sealed partial class ReagentDispenserInventoryPrototype : IPrototype
public string ID { get; private set; } = default!;
// TODO use ReagentId
- [DataField("inventory", customTypeSerializer: typeof(PrototypeIdListSerializer))]
- public List Inventory = new();
+ [DataField("inventory")]
+ public Dictionary Inventory = new();
}
}
diff --git a/Content.Shared/Chemistry/SharedReagentDispenser.cs b/Content.Shared/Chemistry/SharedReagentDispenser.cs
index c1134b6cf54..20cbd8205c0 100644
--- a/Content.Shared/Chemistry/SharedReagentDispenser.cs
+++ b/Content.Shared/Chemistry/SharedReagentDispenser.cs
@@ -13,9 +13,11 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
using Content.Goobstation.Maths.FixedPoint;
+using Content.Shared.Charges.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Storage;
using Robust.Shared.Serialization;
+using Robust.Shared.Toolshed.TypeParsers.Math;
namespace Content.Shared.Chemistry
{
@@ -83,9 +85,9 @@ public sealed class ReagentDispenserDispenseReagentMessage : BoundUserInterfaceM
{
public readonly ReagentId ReagentId;
- public ReagentDispenserDispenseReagentMessage(ReagentId storageLocation)
+ public ReagentDispenserDispenseReagentMessage(ReagentId reagentId)
{
- ReagentId = storageLocation;
+ ReagentId = reagentId;
}
}
@@ -112,22 +114,30 @@ public enum ReagentDispenserDispenseAmount
public sealed class ReagentDispenserBoundUserInterfaceState : BoundUserInterfaceState
{
public readonly ContainerInfo? OutputContainer;
-
public readonly NetEntity? OutputContainerEntity;
///
- /// A list of the reagents which this dispenser can dispense.
+ /// A list of the reagents which this dispenser can dispense and their cost.
///
- public readonly List Inventory;
+ public readonly List<(ReagentId reagent, int cost)> Inventory;
public readonly ReagentDispenserDispenseAmount SelectedDispenseAmount;
- public ReagentDispenserBoundUserInterfaceState(ContainerInfo? outputContainer, NetEntity? outputContainerEntity, List inventory, ReagentDispenserDispenseAmount selectedDispenseAmount)
+ public readonly int? Charges; // TODO: pass Charge Component instead, so we don't calculate charges
+
+ public ReagentDispenserBoundUserInterfaceState(
+ ContainerInfo? outputContainer,
+ NetEntity? outputContainerEntity,
+ List<(ReagentId reagent, int cost)> inventory,
+ ReagentDispenserDispenseAmount selectedDispenseAmount,
+ int? charges
+ )
{
OutputContainer = outputContainer;
OutputContainerEntity = outputContainerEntity;
Inventory = inventory;
SelectedDispenseAmount = selectedDispenseAmount;
+ Charges = charges;
}
}
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 f5b03b207d9..f1ffeb408b3 100644
--- a/Resources/Locale/en-US/chemistry/components/reagent-dispenser-component.ftl
+++ b/Resources/Locale/en-US/chemistry/components/reagent-dispenser-component.ftl
@@ -27,3 +27,4 @@ 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-charge-amount = charge
diff --git a/Resources/Locale/ru-RU/chemistry/components/reagent-dispenser-component.ftl b/Resources/Locale/ru-RU/chemistry/components/reagent-dispenser-component.ftl
index ba746c40940..fa53ad29910 100644
--- a/Resources/Locale/ru-RU/chemistry/components/reagent-dispenser-component.ftl
+++ b/Resources/Locale/ru-RU/chemistry/components/reagent-dispenser-component.ftl
@@ -17,3 +17,4 @@ reagent-dispenser-window-no-container-loaded-text = Контейнер не за
reagent-dispenser-window-reagent-name-not-found-text = Имя реагента не найдено
reagent-dispenser-window-unknown-reagent-text = Неизвестный реагент
reagent-dispenser-window-quantity-label-text = { $quantity } ед.
+reagent-dispenser-window-charge-amount = заряд
diff --git a/Resources/Prototypes/Catalog/reagent_dispenser.yml b/Resources/Prototypes/Catalog/reagent_dispenser.yml
index 7b004e614d2..91eca2c5f22 100644
--- a/Resources/Prototypes/Catalog/reagent_dispenser.yml
+++ b/Resources/Prototypes/Catalog/reagent_dispenser.yml
@@ -1,58 +1,58 @@
- type: reagentDispenserInventory
id: ChemDispenserStandardInventory
inventory:
- - Aluminium
- - Carbon
- - Chlorine
- - Copper
- - Ethanol
- - Fluorine
- - Sugar
- - Hydrogen
- - Iodine
- - Iron
- - Lithium
- - Mercury
- - Nitrogen
- - Oxygen
- - Phosphorus
- - Potassium
- - Radium
- - Silicon
- - Sodium
- - Sulfur
+ Aluminium: 10
+ Carbon: 10
+ Chlorine: 10
+ Copper: 10
+ Ethanol: 10
+ Fluorine: 10
+ Sugar: 10
+ Hydrogen: 10
+ Iodine: 10
+ Iron: 10
+ Lithium: 10
+ Mercury: 10
+ Nitrogen: 10
+ Oxygen: 10
+ Phosphorus: 10
+ Potassium: 10
+ Radium: 10
+ Silicon: 10
+ Sodium: 10
+ Sulfur: 10
- type: reagentDispenserInventory
id: ChemDispenserEmaggedInventory
inventory: ##Feel free to change this to something more interesting when more chems are added
- - Napalm
- - Toxin
- - Epinephrine
- - Ultravasculine
+ Napalm: 30
+ Toxin: 30
+ Epinephrine: 30
+ Ultravasculine: 30
- type: reagentDispenserInventory
id: SodaDispenserInventory
inventory:
- - Ice
- - Coffee
- - Cream
- - Tea
- - GreenTea
- - IcedTea
- - IcedGreenTea
- - Cola
- - SpaceMountainWind
- - DrGibb
- - RootBeer
- - SpaceUp
- - TonicWater
- - SodaWater
- - LemonLime
- - Sugar
- - JuiceOrange
- - JuiceLime
- - JuiceWatermelon
+ Ice: 5
+ Coffee: 5
+ Cream: 5
+ Tea: 5
+ GreenTea: 5
+ IcedTea: 5
+ IcedGreenTea: 5
+ Cola: 5
+ SpaceMountainWind: 5
+ DrGibb: 5
+ RootBeer: 5
+ SpaceUp: 5
+ TonicWater: 5
+ SodaWater: 5
+ LemonLime: 5
+ Sugar: 5
+ JuiceOrange: 5
+ JuiceLime: 5
+ JuiceWatermelon: 5
###Hacked
#- Fourteen Loko
#- GrapeSoda
@@ -60,25 +60,25 @@
- type: reagentDispenserInventory
id: SodaDispenserEmagInventory
inventory:
- - FourteenLoko
- - Ephedrine
- - Histamine
+ FourteenLoko: 15
+ Ephedrine: 15
+ Histamine: 15
- type: reagentDispenserInventory
id: BoozeDispenserInventory
inventory:
- - Beer
- - CoffeeLiqueur
- - Whiskey
- - Wine
- - Vodka
- - Gin
- - Rum
- - Tequila
- - Vermouth
- - Cognac
- - Ale
- - Mead
+ Beer: 5
+ CoffeeLiqueur: 5
+ Whiskey: 5
+ Wine: 5
+ Vodka: 5
+ Gin: 5
+ Rum: 5
+ Tequila: 5
+ Vermouth: 5
+ Cognac: 5
+ Ale: 5
+ Mead: 5
###Hacked
#- Goldschlager
#- Patron
@@ -88,7 +88,7 @@
- type: reagentDispenserInventory
id: BoozeDispenserEmagInventory
inventory:
- - AtomicBomb
- - Ethanol
- - Iron
+ AtomicBomb: 15
+ Ethanol: 15
+ Iron: 15
diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml b/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml
index 99d3242d65c..c3d76663a06 100644
--- a/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml
+++ b/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml
@@ -114,6 +114,11 @@
- type: ReagentDispenser
pack: BoozeDispenserInventory
emagPack: BoozeDispenserEmagInventory
+ - type: LimitedCharges
+ maxCharges: 1000
+ lastCharges: 1000
+ - type: AutoRecharge
+ rechargeDuration: 0.1
- type: Transform
noRot: false
- type: Machine
@@ -129,3 +134,6 @@
id: BoozeDispenserEmpty # Exists for backwards compatibility
suffix: Empty
parent: BoozeDispenser
+ components:
+ - type: LimitedCharges
+ lastCharges: 0 # Doesn't work
diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml b/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml
index e3fec905e5b..ce600122ea3 100644
--- a/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml
+++ b/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml
@@ -54,6 +54,11 @@
- type: ReagentDispenser
pack: ChemDispenserStandardInventory
emagPack: ChemDispenserEmaggedInventory
+ - type: LimitedCharges
+ maxCharges: 1000
+ lastCharges: 0 # Doesn't work
+ - type: AutoRecharge
+ rechargeDuration: 0.1
- type: Sprite
sprite: Structures/dispensers.rsi
state: industrial-working
@@ -94,6 +99,9 @@
- MachineLayer
- type: entity
- id: ChemDispenser # Exists for backward compatibility
+ id: ChemDispenser
suffix: Filled
parent: ChemDispenserEmpty
+ components:
+ - type: LimitedCharges
+ lastCharges: 1000
diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml b/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml
index 6e4789e4f59..9fd8db78b03 100644
--- a/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml
+++ b/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml
@@ -115,6 +115,11 @@
- type: ReagentDispenser
pack: SodaDispenserInventory
emagPack: SodaDispenserEmagInventory
+ - type: LimitedCharges
+ maxCharges: 1000
+ lastCharges: 1000
+ - type: AutoRecharge
+ rechargeDuration: 0.1
- type: Transform
noRot: false
- type: Machine
@@ -128,3 +133,6 @@
parent: SodaDispenser
id: SodaDispenserEmpty # Exists for backwards compatibility
suffix: Empty
+ components:
+ - type: LimitedCharges
+ lastCharges: 0 # Doesn't work
From 1930a9c3267417efdd4ee6cf7b9e89520a008901 Mon Sep 17 00:00:00 2001
From: CREAsTIVE <62616308+CREAsTIVE@users.noreply.github.com>
Date: Tue, 3 Feb 2026 23:59:13 +0500
Subject: [PATCH 08/12] fix: something
---
Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs | 1 -
.../Chemistry/EntitySystems/ReagentDispenserSystem.cs | 6 +++---
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs b/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs
index 34180e29a1e..d5a77a070d9 100644
--- a/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs
+++ b/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs
@@ -17,7 +17,6 @@ namespace Content.Client.Chemistry.UI;
public sealed partial class ReagentCardControl : Control
{
public ReagentId ReagentId { get; private init; }
- public int Cost { get; private set; }
public Action? OnPressed;
public ReagentCardControl(ReagentId reagentId, ReagentPrototype? prototype, int cost)
diff --git a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
index 05981e4bf01..218bf835811 100644
--- a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
+++ b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
@@ -201,7 +201,7 @@ private void UpdateUiState(EntityUid reagentDispenserEnt, ReagentDispenserCompon
continue;
// Finded!
- inventory.Add(reagentId.Value, dispenserComp.DefaultReagentCost);
+ inventory[reagentId.Value] = dispenserComp.DefaultReagentCost;
}
}
}
@@ -217,7 +217,7 @@ dispenserComp.PackPrototypeId is not null
{
foreach (var reagentId in packPrototype.Inventory)
{
- inventory.Add(reagentId.Key, reagentId.Value);
+ inventory[reagentId.Key] = reagentId.Value;
}
}
@@ -229,7 +229,7 @@ dispenserComp.PackPrototypeId is not null
{
foreach (var reagentId in emagPackPrototype.Inventory)
{
- inventory.Add(reagentId.Key, reagentId.Value);
+ inventory[reagentId.Key] = reagentId.Value;
}
}
From 0c669ee0b5184c808fe8943bc2960159a7f2f3bc Mon Sep 17 00:00:00 2001
From: CREAsTIVE <62616308+CREAsTIVE@users.noreply.github.com>
Date: Wed, 11 Feb 2026 16:01:27 +0500
Subject: [PATCH 09/12] refactor: Maid marks
---
.../Chemistry/UI/ReagentCardControl.xaml | 2 +
.../Chemistry/UI/ReagentCardControl.xaml.cs | 4 ++
.../UI/ReagentDispenserBoundUserInterface.cs | 6 +++
.../Chemistry/UI/ReagentDispenserWindow.xaml | 3 +-
.../UI/ReagentDispenserWindow.xaml.cs | 3 ++
.../Components/ReagentDispenserComponent.cs | 6 ++-
.../EntitySystems/ReagentDispenserSystem.cs | 12 ++----
.../ReagentDispenserInventoryPrototype.cs | 2 +
.../Chemistry/SharedReagentDispenser.cs | 10 ++---
.../reagent-dispenser-component.ftl | 1 +
.../reagent-dispenser-component.ftl | 1 +
.../Prototypes/Catalog/reagent_dispenser.yml | 2 +
.../Dispensers/base_structuredispensers.yml | 9 ++++
.../Entities/Structures/Dispensers/booze.yml | 31 +++++++++++++-
.../Entities/Structures/Dispensers/chem.yml | 42 ++++++++++++++++---
.../Entities/Structures/Dispensers/soda.yml | 34 ++++++++++++++-
16 files changed, 144 insertions(+), 24 deletions(-)
diff --git a/Content.Client/Chemistry/UI/ReagentCardControl.xaml b/Content.Client/Chemistry/UI/ReagentCardControl.xaml
index 1e3a5138136..fc08a1cc67d 100644
--- a/Content.Client/Chemistry/UI/ReagentCardControl.xaml
+++ b/Content.Client/Chemistry/UI/ReagentCardControl.xaml
@@ -5,6 +5,7 @@ SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com>
SPDX-License-Identifier: AGPL-3.0-or-later
-->
+
+
diff --git a/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs b/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs
index d5a77a070d9..93baf9eabb9 100644
--- a/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs
+++ b/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs
@@ -3,6 +3,8 @@
//
// SPDX-License-Identifier: AGPL-3.0-or-later
+// Maid START PR №21
+
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Storage;
@@ -29,3 +31,5 @@ public ReagentCardControl(ReagentId reagentId, ReagentPrototype? prototype, int
ReagentCostLabel.Text = cost.ToString();
}
}
+
+// Maid END
diff --git a/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs b/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs
index a13e2e6dbe6..99284980c7b 100644
--- a/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs
+++ b/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs
@@ -68,7 +68,13 @@ protected override void Open()
_window.AmountGrid.OnButtonPressed += s => SendMessage(new ReagentDispenserSetDispenseAmountMessage(s));
+ // Maid START PR №21
+ /*
+ _window.OnDispenseReagentButtonPressed += (location) => SendMessage(new ReagentDispenserDispenseReagentMessage(location));
+ _window.OnEjectJugButtonPressed += (location) => SendMessage(new ReagentDispenserEjectContainerMessage(location));
+ */
_window.OnDispenseReagentButtonPressed += (reagent) => SendMessage(new ReagentDispenserDispenseReagentMessage(reagent));
+ // Maid END
}
///
diff --git a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml
index 6d2aeae79c5..f6591866252 100644
--- a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml
+++ b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml
@@ -14,7 +14,7 @@ SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com>
SPDX-License-Identifier: AGPL-3.0-or-later
-->
-
+
+
diff --git a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs
index 6091e1fd52f..92be18471f3 100644
--- a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs
+++ b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs
@@ -44,6 +44,7 @@
using System.Linq;
using static Robust.Client.UserInterface.Controls.BoxContainer;
+// Maid START PR №21
namespace Content.Client.Chemistry.UI
{
///
@@ -174,3 +175,5 @@ public void UpdateContainerInfo(ReagentDispenserBoundUserInterfaceState state)
}
}
}
+
+// Maid END
diff --git a/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs b/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs
index 3a9853efa35..99ec778f554 100644
--- a/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs
+++ b/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs
@@ -60,7 +60,7 @@
using Content.Shared.Chemistry;
using Robust.Shared.Audio;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-using Content.Shared.Chemistry.Dispenser;
+using Content.Shared.Chemistry.Dispenser; // Maid PR №21
namespace Content.Server.Chemistry.Components
{
@@ -80,6 +80,8 @@ public sealed partial class ReagentDispenserComponent : Component
[ViewVariables(VVAccess.ReadWrite)]
public ReagentDispenserDispenseAmount DispenseAmount = ReagentDispenserDispenseAmount.U10;
+ // Maid START PR №21
+
///
/// Used for reagents that got collected from
///
@@ -93,5 +95,7 @@ public sealed partial class ReagentDispenserComponent : Component
[DataField("emagPack", customTypeSerializer: typeof(PrototypeIdSerializer))]
[ViewVariables(VVAccess.ReadWrite)]
public string? EmagPackPrototypeId = default!;
+
+ // Maid END
}
}
diff --git a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
index 218bf835811..f1de9c5f88b 100644
--- a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
+++ b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
@@ -33,33 +33,25 @@
//
// SPDX-License-Identifier: AGPL-3.0-or-later
-using Content.Goobstation.Maths.FixedPoint;
using Content.Server.Charges;
using Content.Server.Chemistry.Components;
using Content.Server.Hands.Systems;
using Content.Shared.Charges.Components;
using Content.Shared.Chemistry;
-using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.Dispenser;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Containers.ItemSlots;
-using Content.Shared.Database;
using Content.Shared.Emag.Components;
-using Content.Shared.Labels.Components;
using Content.Shared.Nutrition.EntitySystems;
-using Content.Shared.Storage;
using Content.Shared.Storage.Components;
using Content.Shared.Storage.EntitySystems;
using JetBrains.Annotations;
-using JetBrains.FormatRipper.MachO;
using Robust.Server.Audio;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.Markdown;
using Robust.Shared.Serialization.Markdown.Mapping;
using Robust.Shared.Serialization.Markdown.Sequence;
using Robust.Shared.Serialization.Markdown.Value;
@@ -67,6 +59,8 @@
using System.Diagnostics;
using System.Linq;
+// Maid START PR №21
+
namespace Content.Server.Chemistry.EntitySystems
{
///
@@ -311,3 +305,5 @@ private void OnMapInit(Entity ent, ref MapInitEvent a
}
}
}
+
+// Maid END
diff --git a/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs b/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs
index 63e3ee014b0..6fa93651386 100644
--- a/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs
+++ b/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs
@@ -1,3 +1,5 @@
+// Maid PR №21
+
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
diff --git a/Content.Shared/Chemistry/SharedReagentDispenser.cs b/Content.Shared/Chemistry/SharedReagentDispenser.cs
index 20cbd8205c0..2390ee03d46 100644
--- a/Content.Shared/Chemistry/SharedReagentDispenser.cs
+++ b/Content.Shared/Chemistry/SharedReagentDispenser.cs
@@ -12,12 +12,10 @@
//
// SPDX-License-Identifier: AGPL-3.0-or-later
-using Content.Goobstation.Maths.FixedPoint;
-using Content.Shared.Charges.Components;
using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Storage;
using Robust.Shared.Serialization;
-using Robust.Shared.Toolshed.TypeParsers.Math;
+
+// Maid START PR №21
namespace Content.Shared.Chemistry
{
@@ -123,7 +121,7 @@ public sealed class ReagentDispenserBoundUserInterfaceState : BoundUserInterface
public readonly ReagentDispenserDispenseAmount SelectedDispenseAmount;
- public readonly int? Charges; // TODO: pass Charge Component instead, so we don't calculate charges
+ public readonly int? Charges;
public ReagentDispenserBoundUserInterfaceState(
ContainerInfo? outputContainer,
@@ -147,3 +145,5 @@ public enum ReagentDispenserUiKey
Key
}
}
+
+// Maid END
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 f1ffeb408b3..3dd78b21a44 100644
--- a/Resources/Locale/en-US/chemistry/components/reagent-dispenser-component.ftl
+++ b/Resources/Locale/en-US/chemistry/components/reagent-dispenser-component.ftl
@@ -27,4 +27,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
+# Maid PR №21
reagent-dispenser-window-charge-amount = charge
diff --git a/Resources/Locale/ru-RU/chemistry/components/reagent-dispenser-component.ftl b/Resources/Locale/ru-RU/chemistry/components/reagent-dispenser-component.ftl
index fa53ad29910..28fa338e98d 100644
--- a/Resources/Locale/ru-RU/chemistry/components/reagent-dispenser-component.ftl
+++ b/Resources/Locale/ru-RU/chemistry/components/reagent-dispenser-component.ftl
@@ -17,4 +17,5 @@ reagent-dispenser-window-no-container-loaded-text = Контейнер не за
reagent-dispenser-window-reagent-name-not-found-text = Имя реагента не найдено
reagent-dispenser-window-unknown-reagent-text = Неизвестный реагент
reagent-dispenser-window-quantity-label-text = { $quantity } ед.
+# Maid PR №21
reagent-dispenser-window-charge-amount = заряд
diff --git a/Resources/Prototypes/Catalog/reagent_dispenser.yml b/Resources/Prototypes/Catalog/reagent_dispenser.yml
index 91eca2c5f22..42a325922a9 100644
--- a/Resources/Prototypes/Catalog/reagent_dispenser.yml
+++ b/Resources/Prototypes/Catalog/reagent_dispenser.yml
@@ -1,3 +1,5 @@
+# Maid PR №21
+
- type: reagentDispenserInventory
id: ChemDispenserStandardInventory
inventory:
diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml b/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml
index 930db660979..c3e1ef67bc5 100644
--- a/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml
+++ b/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml
@@ -78,6 +78,8 @@
interfaces:
enum.ReagentDispenserUiKey.Key:
type: ReagentDispenserBoundUserInterface
+# enum.StorageUiKey.Key: # Maid PR №21
+# type: StorageBoundUserInterface # Maid PR №21
- type: Anchorable
- type: Pullable
- type: Damageable
@@ -100,6 +102,12 @@
- !type:PlaySoundBehavior
sound:
collection: MetalGlassBreak
+# Maid START PR №21
+# - type: Storage
+# maxItemSize: Normal
+# grid:
+# - 0,0,19,5
+# Maid END
- type: ReagentDispenser
beakerSlot:
whitelistFailPopup: reagent-dispenser-component-cannot-put-entity-message
@@ -113,6 +121,7 @@
machine_board: !type:Container
machine_parts: !type:Container
beakerSlot: !type:ContainerSlot
+ # storagebase: !type:Container # Maid PR №21
- type: StaticPrice
price: 1000
- type: WiresPanel
diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml b/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml
index c3d76663a06..ad7a452958b 100644
--- a/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml
+++ b/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml
@@ -111,6 +111,26 @@
sprite: Structures/smalldispensers.rsi
drawdepth: SmallObjects
state: booze
+## Maid START PR №21
+# - type: Storage
+# openOnActivate: false
+# whitelist:
+# tags:
+# - DrinkBottle
+# - type: StorageFill
+# contents:
+# - id: DrinkAleBottleFullGrowler
+# - id: DrinkBeerGrowler
+# - id: DrinkCoffeeLiqueurBottleFull
+# - id: DrinkCognacBottleFull
+# - id: DrinkGinBottleFull
+# - id: DrinkMeadJug
+# - id: DrinkRumBottleFull
+# - id: DrinkTequilaBottleFull
+# - id: DrinkVermouthBottleFull
+# - id: DrinkVodkaBottleFull
+# - id: DrinkWhiskeyBottleFull
+# - id: DrinkWineBottleFull
- type: ReagentDispenser
pack: BoozeDispenserInventory
emagPack: BoozeDispenserEmagInventory
@@ -119,6 +139,7 @@
lastCharges: 1000
- type: AutoRecharge
rechargeDuration: 0.1
+## Maid END
- type: Transform
noRot: false
- type: Machine
@@ -131,9 +152,15 @@
stealGroup: BoozeDispenser
- type: entity
- id: BoozeDispenserEmpty # Exists for backwards compatibility
+ id: BoozeDispenserEmpty
suffix: Empty
parent: BoozeDispenser
components:
+## Maid START PR №21
+# - type: Storage
+# whitelist:
+# tags:
+# - DrinkBottle
- type: LimitedCharges
- lastCharges: 0 # Doesn't work
+ lastCharges: 0 # Doesn't work, search for comment in PR 21 for more info
+## Maid END
diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml b/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml
index ce600122ea3..907e66e0f37 100644
--- a/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml
+++ b/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml
@@ -51,18 +51,25 @@
parent: ReagentDispenserBase
description: An industrial grade chemical dispenser.
components:
+ - type: Sprite
+ sprite: Structures/dispensers.rsi
+ state: industrial-working
+ snapCardinals: true
+## Maid START PR №21
+# - type: Storage
+# openOnActivate: false
+# whitelist:
+# tags:
+# - ChemDispensable
- type: ReagentDispenser
pack: ChemDispenserStandardInventory
emagPack: ChemDispenserEmaggedInventory
- type: LimitedCharges
maxCharges: 1000
- lastCharges: 0 # Doesn't work
+ lastCharges: 0 # Doesn't work, search for comment in PR 21 for more info
- type: AutoRecharge
rechargeDuration: 0.1
- - type: Sprite
- sprite: Structures/dispensers.rsi
- state: industrial-working
- snapCardinals: true
+## Maid END
- type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: Destructible
@@ -103,5 +110,30 @@
suffix: Filled
parent: ChemDispenserEmpty
components:
+## Maid START PR №21
+# - type: ReagentDispenser
+# - type: StorageFill
+# contents:
+# - id: JugAluminium
+# - id: JugCarbon
+# - id: JugChlorine
+# - id: JugCopper
+# - id: JugEthanol
+# - id: JugFluorine
+# - id: JugSugar
+# - id: JugHydrogen
+# - id: JugIodine
+# - id: JugIron
+# - id: JugLithium
+# - id: JugMercury
+# - id: JugNitrogen
+# - id: JugOxygen
+# - id: JugPhosphorus
+# - id: JugPotassium
+# - id: JugRadium
+# - id: JugSilicon
+# - id: JugSodium
+# - id: JugSulfur
- type: LimitedCharges
lastCharges: 1000
+## Maid END
diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml b/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml
index 9fd8db78b03..08e26289753 100644
--- a/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml
+++ b/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml
@@ -112,6 +112,33 @@
sprite: Structures/smalldispensers.rsi
drawdepth: SmallObjects
state: soda
+## Maid START PR №21
+# - type: Storage
+# openOnActivate: false
+# whitelist:
+# tags:
+# - DrinkBottle
+# - type: StorageFill
+# contents:
+# - id: DrinkCoconutWaterJug
+# - id: DrinkCoffeeJug
+# - id: DrinkColaBottleFull
+# - id: DrinkCreamCartonXL
+# - id: DrinkDrGibbJug
+# - id: DrinkEnergyDrinkJug
+# - id: DrinkGreenTeaJug
+# - id: DrinkIceJug
+# - id: DrinkJuiceLimeCartonXL
+# - id: DrinkJuiceOrangeCartonXL
+# - id: DrinkLemonLimeJug
+# - id: DrinkRootBeerJug
+# - id: DrinkSodaWaterBottleFull
+# - id: DrinkSpaceMountainWindBottleFull
+# - id: DrinkSpaceUpBottleFull
+# - id: DrinkSugarJug
+# - id: DrinkTeaJug
+# - id: DrinkTonicWaterBottleFull
+# - id: DrinkWaterMelonJuiceJug
- type: ReagentDispenser
pack: SodaDispenserInventory
emagPack: SodaDispenserEmagInventory
@@ -120,6 +147,7 @@
lastCharges: 1000
- type: AutoRecharge
rechargeDuration: 0.1
+## Maid END
- type: Transform
noRot: false
- type: Machine
@@ -131,8 +159,10 @@
- type: entity
parent: SodaDispenser
- id: SodaDispenserEmpty # Exists for backwards compatibility
+ id: SodaDispenserEmpty
suffix: Empty
components:
+## Maid START PR №21
- type: LimitedCharges
- lastCharges: 0 # Doesn't work
+ lastCharges: 0 # Doesn't work, search for comment in PR 21 for more info
+# Maid END
From a29ab9f91f1bda1123c58f3540360f219a970648 Mon Sep 17 00:00:00 2001
From: CREAsTIVE <62616308+CREAsTIVE@users.noreply.github.com>
Date: Tue, 24 Feb 2026 18:10:26 +0500
Subject: [PATCH 10/12] fix: removed searching reagents in storage fill
component + refactor
---
.../EntitySystems/ReagentDispenserSystem.cs | 62 ++-----------------
1 file changed, 5 insertions(+), 57 deletions(-)
diff --git a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
index f1de9c5f88b..3040b2b4fd3 100644
--- a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
+++ b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
@@ -33,6 +33,7 @@
//
// SPDX-License-Identifier: AGPL-3.0-or-later
+using Content.Goobstation.Maths.FixedPoint;
using Content.Server.Charges;
using Content.Server.Chemistry.Components;
using Content.Server.Hands.Systems;
@@ -96,23 +97,9 @@ public override void Initialize()
SubscribeLocalEvent(OnMapInit, before: new[] { typeof(ItemSlotsSystem) });
}
-
- private float _updateTimer = 0f;
public override void Update(float frameTime)
{
base.Update(frameTime);
-
- _updateTimer += frameTime;
- if (_updateTimer >= 1f)
- {
- _updateTimer = 0f;
- var query = EntityQueryEnumerator();
-
- while (query.MoveNext(out var uid, out var reagentDispenserComp, out var uiComp, out var charges))
- {
- UpdateUiState(uid, reagentDispenserComp);
- }
- }
}
private void SubscribeUpdateUiState(Entity ent, ref T ev)
@@ -163,45 +150,6 @@ private void UpdateUiState(EntityUid reagentDispenserEnt, ReagentDispenserCompon
private IEnumerable<(ReagentId reagent, int cost)> GetInventory(EntityUid dispenserEnt, ReagentDispenserComponent dispenserComp)
{
var inventory = new Dictionary();
-
- // Collect reagents from items provided by SolutionContainerManager; TODO: include parent
- if (TryComp(dispenserEnt, out var storageFillComp))
- {
- foreach (var item in storageFillComp.Contents)
- {
- if (!_prototypeManager.TryIndex(item.PrototypeId, out EntityPrototype? entityPrototype))
- continue;
-
- if (!entityPrototype.Components.TryGetValue("SolutionContainerManager", out var data))
- continue;
-
- if (!data.Mapping.TryGet("solutions", out var solutions))
- continue;
-
- foreach (var maybeSolution in solutions.Values)
- {
- if (!(maybeSolution is MappingDataNode solution))
- continue;
-
- if (!solution.TryGet("reagents", out var reagents))
- continue;
-
- foreach (var maybeReagent in reagents)
- {
- if (!(maybeReagent is MappingDataNode reagent))
- continue;
-
- if (!reagent.TryGet("ReagentId", out var reagentId))
- continue;
-
- // Finded!
- inventory[reagentId.Value] = dispenserComp.DefaultReagentCost;
- }
- }
- }
-
- }
-
// Collect reagents from packs:
if (
@@ -258,8 +206,8 @@ private void OnDispenseReagentMessage(Entity reagentD
// How much would be dispensed (less then required, if there not enough charges)
float totalDispenseAmount = requestedDispenseAmount;
- bool hasCharges;
- if (hasCharges = TryComp(reagentDispenser.Owner, out LimitedChargesComponent? charges))
+ bool hasChargesComponent = TryComp(reagentDispenser.Owner, out LimitedChargesComponent? charges);
+ if (hasChargesComponent)
{
var avalaibleCharges = _chargesSystem.GetCurrentCharges(reagentDispenser.Owner);
@@ -271,9 +219,9 @@ private void OnDispenseReagentMessage(Entity reagentD
// sollution is not null because [NotNullWhen(true)]
_solutionContainerSystem.TryAddReagent(solution ?? throw new UnreachableException(), reagentId.Prototype, totalDispenseAmount, out var dispensed);
- if (hasCharges && dispensed > float.Epsilon)
+ if (hasChargesComponent && dispensed > float.Epsilon)
{
- _chargesSystem.AddCharges(reagentDispenser.Owner, -((int) (MathF.Ceiling((float) dispensed * singleCost) + float.Epsilon))); // Ceil dispensed amount up (should be safe)
+ _chargesSystem.AddCharges(reagentDispenser.Owner, -((int) (MathF.Ceiling((float) dispensed * singleCost))));
}
UpdateUiState(reagentDispenser.Owner, reagentDispenser.Comp);
From c8d9a9e8fc5f7a5c734a36255a819e1c97bbd6cd Mon Sep 17 00:00:00 2001
From: CREAsTIVE <62616308+CREAsTIVE@users.noreply.github.com>
Date: Wed, 25 Feb 2026 00:07:27 +0500
Subject: [PATCH 11/12] feat: charge amount moved to client
---
.../UI/ReagentDispenserWindow.xaml.cs | 40 +++++++++++++++++--
.../EntitySystems/ReagentDispenserSystem.cs | 8 +---
.../Chemistry/SharedReagentDispenser.cs | 7 ++--
3 files changed, 41 insertions(+), 14 deletions(-)
diff --git a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs
index 92be18471f3..c318430c60d 100644
--- a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs
+++ b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs
@@ -32,15 +32,20 @@
//
// SPDX-License-Identifier: AGPL-3.0-or-later
+using Content.Client.Charges;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Controls;
+using Content.Shared.Charges.Components;
+using Content.Shared.Charges.Systems;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Storage;
+using JetBrains.Annotations;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
using System.Linq;
using static Robust.Client.UserInterface.Controls.BoxContainer;
@@ -55,7 +60,10 @@ public sealed partial class ReagentDispenserWindow : FancyWindow
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
+ private readonly ChargesSystem _chargesSystem = default!;
+
public event Action? OnDispenseReagentButtonPressed;
+ Entity? _chargesEntity;
///
/// Create and initialize the dispenser UI client-side. Creates the basic layout,
@@ -65,6 +73,18 @@ public ReagentDispenserWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
+
+ _chargesSystem = _entityManager.System();
+ }
+
+ protected override void FrameUpdate(FrameEventArgs args)
+ {
+ if (_chargesEntity?.Comp1 is not null)
+ {
+ ChargeAmountLabel.Text = _chargesSystem.GetCurrentCharges(_chargesEntity.Value).ToString();
+ }
+
+ base.FrameUpdate(args);
}
///
@@ -117,13 +137,25 @@ public void UpdateState(BoundUserInterfaceState state)
ClearButton.Disabled = castState.OutputContainer is null;
EjectButton.Disabled = castState.OutputContainer is null;
- AmountGrid.Selected = ((int)castState.SelectedDispenseAmount).ToString();
+ AmountGrid.Selected = ((int) castState.SelectedDispenseAmount).ToString();
// Show charge amount
- ChargeAmount.Visible = castState.Charges is not null;
- if (castState.Charges is not null)
+ ChargeAmount.Visible = false;
+
+ var ent = _entityManager.GetEntity(castState.ChargesContainerEntity);
+ if (ent is not null)
{
- ChargeAmountLabel.Text = castState.Charges.ToString();
+ _chargesEntity = new(
+ ent.Value,
+ _entityManager.GetComponentOrNull(ent),
+ _entityManager.GetComponentOrNull(ent)
+ );
+
+ if (_chargesEntity?.Comp1 is not null)
+ {
+ ChargeAmount.Visible = true;
+ ChargeAmountLabel.Text = _chargesSystem.GetCurrentCharges(_chargesEntity.Value).ToString();
+ }
}
}
diff --git a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
index 3040b2b4fd3..09b053454af 100644
--- a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
+++ b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
@@ -114,18 +114,12 @@ private void UpdateUiState(EntityUid reagentDispenserEnt, ReagentDispenserCompon
var inventory = GetInventory(reagentDispenserEnt, reagentDispenserComp).ToList(); // TODO: Another copy, optimize
- int? charges = null;
- if (TryComp(reagentDispenserEnt, out LimitedChargesComponent? comp))
- {
- charges = _chargesSystem.GetCurrentCharges(reagentDispenserEnt);
- }
-
var state = new ReagentDispenserBoundUserInterfaceState(
outputContainerInfo,
GetNetEntity(outputContainer),
inventory,
reagentDispenserComp.DispenseAmount,
- charges
+ GetNetEntity(reagentDispenserEnt)
);
_userInterfaceSystem.SetUiState(reagentDispenserEnt, ReagentDispenserUiKey.Key, state);
diff --git a/Content.Shared/Chemistry/SharedReagentDispenser.cs b/Content.Shared/Chemistry/SharedReagentDispenser.cs
index 2390ee03d46..bf2758ed22b 100644
--- a/Content.Shared/Chemistry/SharedReagentDispenser.cs
+++ b/Content.Shared/Chemistry/SharedReagentDispenser.cs
@@ -108,6 +108,7 @@ public enum ReagentDispenserDispenseAmount
U100 = 100,
}
+
[Serializable, NetSerializable]
public sealed class ReagentDispenserBoundUserInterfaceState : BoundUserInterfaceState
{
@@ -121,21 +122,21 @@ public sealed class ReagentDispenserBoundUserInterfaceState : BoundUserInterface
public readonly ReagentDispenserDispenseAmount SelectedDispenseAmount;
- public readonly int? Charges;
+ public readonly NetEntity? ChargesContainerEntity;
public ReagentDispenserBoundUserInterfaceState(
ContainerInfo? outputContainer,
NetEntity? outputContainerEntity,
List<(ReagentId reagent, int cost)> inventory,
ReagentDispenserDispenseAmount selectedDispenseAmount,
- int? charges
+ NetEntity? charges
)
{
OutputContainer = outputContainer;
OutputContainerEntity = outputContainerEntity;
Inventory = inventory;
SelectedDispenseAmount = selectedDispenseAmount;
- Charges = charges;
+ ChargesContainerEntity = charges;
}
}
From a3d80449edef07521a0c2a72f648f5b3ed467ca5 Mon Sep 17 00:00:00 2001
From: CREAsTIVE <62616308+CREAsTIVE@users.noreply.github.com>
Date: Wed, 25 Feb 2026 01:47:11 +0500
Subject: [PATCH 12/12] fix: price count disabled when charge component doesn't
exist
---
.../Chemistry/UI/ReagentCardControl.xaml | 6 +++--
.../Chemistry/UI/ReagentCardControl.xaml.cs | 5 ++++
.../UI/ReagentDispenserWindow.xaml.cs | 27 ++++++++++---------
3 files changed, 23 insertions(+), 15 deletions(-)
diff --git a/Content.Client/Chemistry/UI/ReagentCardControl.xaml b/Content.Client/Chemistry/UI/ReagentCardControl.xaml
index fc08a1cc67d..430adb01723 100644
--- a/Content.Client/Chemistry/UI/ReagentCardControl.xaml
+++ b/Content.Client/Chemistry/UI/ReagentCardControl.xaml
@@ -22,8 +22,10 @@ SPDX-License-Identifier: AGPL-3.0-or-later
Margin="0 0 0 0">
-
-
+
+
+
+
diff --git a/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs b/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs
index 93baf9eabb9..d90aa8d9a1b 100644
--- a/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs
+++ b/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs
@@ -28,7 +28,12 @@ public ReagentCardControl(ReagentId reagentId, ReagentPrototype? prototype, int
ReagentId = reagentId;
ReagentNameLabel.Text = prototype?.LocalizedName ?? Loc.GetString("reagent-dispenser-window-reagent-name-not-found-text");
MainButton.OnPressed += args => OnPressed?.Invoke(ReagentId);
+
ReagentCostLabel.Text = cost.ToString();
+ if (cost == 0)
+ {
+ ReagentCostControl.Visible = false;
+ }
}
}
diff --git a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs
index c318430c60d..008c4a33f54 100644
--- a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs
+++ b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs
@@ -91,7 +91,7 @@ protected override void FrameUpdate(FrameEventArgs args)
/// Update the button grid of reagents which can be dispensed.
///
/// Reagents which can be dispensed by this dispenser
- public void UpdateReagentsList(List<(ReagentId reagent, int cost)> inventory, int amount)
+ public void UpdateReagentsList(List<(ReagentId reagent, int cost)> inventory, int amount, bool showPrices)
{
if (ReagentList == null)
return;
@@ -114,7 +114,7 @@ public void UpdateReagentsList(List<(ReagentId reagent, int cost)> inventory, in
foreach (var item in reagentPrototypes)
{
- var card = new ReagentCardControl(item.reagent, item.prototype, item.cost * amount);
+ var card = new ReagentCardControl(item.reagent, item.prototype, showPrices ? item.cost * amount : 0);
card.OnPressed += OnDispenseReagentButtonPressed;
ReagentList.Children.Add(card);
}
@@ -127,17 +127,6 @@ public void UpdateReagentsList(List<(ReagentId reagent, int cost)> inventory, in
public void UpdateState(BoundUserInterfaceState state)
{
var castState = (ReagentDispenserBoundUserInterfaceState) state;
- UpdateContainerInfo(castState);
- UpdateReagentsList(castState.Inventory, (int) castState.SelectedDispenseAmount);
-
- _entityManager.TryGetEntity(castState.OutputContainerEntity, out var outputContainerEnt);
- View.SetEntity(outputContainerEnt);
-
- // Disable the Clear & Eject button if no beaker
- ClearButton.Disabled = castState.OutputContainer is null;
- EjectButton.Disabled = castState.OutputContainer is null;
-
- AmountGrid.Selected = ((int) castState.SelectedDispenseAmount).ToString();
// Show charge amount
ChargeAmount.Visible = false;
@@ -157,6 +146,18 @@ public void UpdateState(BoundUserInterfaceState state)
ChargeAmountLabel.Text = _chargesSystem.GetCurrentCharges(_chargesEntity.Value).ToString();
}
}
+
+ UpdateContainerInfo(castState);
+ UpdateReagentsList(castState.Inventory, (int) castState.SelectedDispenseAmount, _chargesEntity?.Comp1 is not null);
+
+ _entityManager.TryGetEntity(castState.OutputContainerEntity, out var outputContainerEnt);
+ View.SetEntity(outputContainerEnt);
+
+ // Disable the Clear & Eject button if no beaker
+ ClearButton.Disabled = castState.OutputContainer is null;
+ EjectButton.Disabled = castState.OutputContainer is null;
+
+ AmountGrid.Selected = ((int) castState.SelectedDispenseAmount).ToString();
}
///