Add limited-reagent dispensers (#23907)

* Add limited-reagent dispensers

* Add empty versions for all dispensers

* Fix lint

* Set initial window size so all buttons are visible

* Simplify logic, add parenthesis

* Use localized name for initial labels

* Adjust button style

* Avoid touching items before MapInit

* Remove pre-labeling

* Reduce diff

* Clean up YAML

* Fix test

* Really fix test

* Document

* Adjust based on review

* Add labels for obnoxiously long bottles

---------

Co-authored-by: AWF <you@example.com>
This commit is contained in:
Kevin Zheng
2024-01-17 13:43:48 -08:00
committed by GitHub
parent 47159d1c23
commit 9394a26245
19 changed files with 609 additions and 168 deletions

View File

@@ -57,13 +57,23 @@ namespace Content.Client.Chemistry.UI
_window.OnDispenseReagentButtonMouseEntered += (args, button) => _window.OnDispenseReagentButtonMouseEntered += (args, button) =>
{ {
if (_lastState is not null) if (_lastState is not null)
_window.UpdateContainerInfo(_lastState, button.ReagentId); _window.UpdateContainerInfo(_lastState);
}; };
_window.OnDispenseReagentButtonMouseExited += (args, button) => _window.OnDispenseReagentButtonMouseExited += (args, button) =>
{ {
if (_lastState is not null) if (_lastState is not null)
_window.UpdateContainerInfo(_lastState); _window.UpdateContainerInfo(_lastState);
}; };
_window.OnEjectJugButtonPressed += (args, button) => SendMessage(new ItemSlotButtonPressedEvent(button.ReagentId));
_window.OnEjectJugButtonMouseEntered += (args, button) => {
if (_lastState is not null)
_window.UpdateContainerInfo(_lastState);
};
_window.OnEjectJugButtonMouseExited += (args, button) => {
if (_lastState is not null)
_window.UpdateContainerInfo(_lastState);
};
} }
/// <summary> /// <summary>

View File

@@ -1,8 +1,7 @@
<DefaultWindow xmlns="https://spacestation14.io" <DefaultWindow xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client" xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
Title="{Loc 'reagent-dispenser-bound-user-interface-title'}" Title="{Loc 'reagent-dispenser-bound-user-interface-title'}"
SetSize="620 450" MinSize="680 450">
MinSize="620 450">
<BoxContainer Orientation="Vertical"> <BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal"> <BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'reagent-dispenser-window-amount-to-dispense-label'}"/> <Label Text="{Loc 'reagent-dispenser-window-amount-to-dispense-label'}"/>
@@ -18,10 +17,8 @@
<Button Name="DispenseButton100" Access="Public" Text="100" StyleClasses="OpenLeft"/> <Button Name="DispenseButton100" Access="Public" Text="100" StyleClasses="OpenLeft"/>
</BoxContainer> </BoxContainer>
<Control MinSize="0 10"/> <Control MinSize="0 10"/>
<ScrollContainer HScrollEnabled="False" HorizontalExpand="True" MinSize="0 170"> <GridContainer Name="ChemicalList" HorizontalExpand="True" VerticalExpand="True" Access="Public" Columns="6">
<GridContainer Name="ChemicalList" Access="Public" Columns="4">
</GridContainer> </GridContainer>
</ScrollContainer>
<Control MinSize="0 10"/> <Control MinSize="0 10"/>
<BoxContainer Orientation="Horizontal"> <BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'reagent-dispenser-window-container-label'}"/> <Label Text="{Loc 'reagent-dispenser-window-container-label'}"/>

View File

@@ -23,6 +23,10 @@ namespace Content.Client.Chemistry.UI
public event Action<GUIMouseHoverEventArgs, DispenseReagentButton>? OnDispenseReagentButtonMouseEntered; public event Action<GUIMouseHoverEventArgs, DispenseReagentButton>? OnDispenseReagentButtonMouseEntered;
public event Action<GUIMouseHoverEventArgs, DispenseReagentButton>? OnDispenseReagentButtonMouseExited; public event Action<GUIMouseHoverEventArgs, DispenseReagentButton>? OnDispenseReagentButtonMouseExited;
public event Action<BaseButton.ButtonEventArgs, EjectJugButton>? OnEjectJugButtonPressed;
public event Action<GUIMouseHoverEventArgs, EjectJugButton>? OnEjectJugButtonMouseEntered;
public event Action<GUIMouseHoverEventArgs, EjectJugButton>? OnEjectJugButtonMouseExited;
/// <summary> /// <summary>
/// Create and initialize the dispenser UI client-side. Creates the basic layout, /// Create and initialize the dispenser UI client-side. Creates the basic layout,
/// actual data isn't filled in until the server sends data about the dispenser. /// actual data isn't filled in until the server sends data about the dispenser.
@@ -48,25 +52,25 @@ namespace Content.Client.Chemistry.UI
/// Update the button grid of reagents which can be dispensed. /// Update the button grid of reagents which can be dispensed.
/// </summary> /// </summary>
/// <param name="inventory">Reagents which can be dispensed by this dispenser</param> /// <param name="inventory">Reagents which can be dispensed by this dispenser</param>
public void UpdateReagentsList(List<ReagentId> inventory) public void UpdateReagentsList(List<KeyValuePair<string, KeyValuePair<string,string>>> inventory)
{ {
if (ChemicalList == null) if (ChemicalList == null)
return; return;
ChemicalList.Children.Clear(); ChemicalList.Children.Clear();
foreach (var entry in inventory foreach (KeyValuePair<string, KeyValuePair<string, string>> entry in inventory)
.OrderBy(r => {_prototypeManager.TryIndex(r.Prototype, out ReagentPrototype? p); return p?.LocalizedName;}))
{ {
var localizedName = _prototypeManager.TryIndex(entry.Prototype, out ReagentPrototype? p) var button = new DispenseReagentButton(entry.Key, entry.Value.Key, entry.Value.Value);
? p.LocalizedName
: Loc.GetString("reagent-dispenser-window-reagent-name-not-found-text");
var button = new DispenseReagentButton(entry, localizedName);
button.OnPressed += args => OnDispenseReagentButtonPressed?.Invoke(args, button); button.OnPressed += args => OnDispenseReagentButtonPressed?.Invoke(args, button);
button.OnMouseEntered += args => OnDispenseReagentButtonMouseEntered?.Invoke(args, button); button.OnMouseEntered += args => OnDispenseReagentButtonMouseEntered?.Invoke(args, button);
button.OnMouseExited += args => OnDispenseReagentButtonMouseExited?.Invoke(args, button); button.OnMouseExited += args => OnDispenseReagentButtonMouseExited?.Invoke(args, button);
ChemicalList.AddChild(button); ChemicalList.AddChild(button);
var ejectButton = new EjectJugButton(entry.Key);
ejectButton.OnPressed += args => OnEjectJugButtonPressed?.Invoke(args, ejectButton);
ejectButton.OnMouseEntered += args => OnEjectJugButtonMouseEntered?.Invoke(args, ejectButton);
ejectButton.OnMouseExited += args => OnEjectJugButtonMouseExited?.Invoke(args, ejectButton);
ChemicalList.AddChild(ejectButton);
} }
} }
@@ -121,9 +125,8 @@ namespace Content.Client.Chemistry.UI
/// <para>Also highlights a reagent if it's dispense button is being mouse hovered.</para> /// <para>Also highlights a reagent if it's dispense button is being mouse hovered.</para>
/// </summary> /// </summary>
/// <param name="state">State data for the dispenser.</param> /// <param name="state">State data for the dispenser.</param>
/// <param name="highlightedReagentId">Prototype ID of the reagent whose dispense button is currently being mouse hovered,
/// or null if no button is being hovered.</param> /// or null if no button is being hovered.</param>
public void UpdateContainerInfo(ReagentDispenserBoundUserInterfaceState state, ReagentId? highlightedReagentId = null) public void UpdateContainerInfo(ReagentDispenserBoundUserInterfaceState state)
{ {
ContainerInfo.Children.Clear(); ContainerInfo.Children.Clear();
@@ -161,12 +164,6 @@ namespace Content.Client.Chemistry.UI
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}, StyleClasses = {StyleNano.StyleClassLabelSecondaryColor},
}; };
// Check if the reagent is being moused over. If so, color it green.
if (reagent == highlightedReagentId) {
nameLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateGood);
quantityLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateGood);
}
ContainerInfo.Children.Add(new BoxContainer ContainerInfo.Children.Add(new BoxContainer
{ {
Orientation = LayoutOrientation.Horizontal, Orientation = LayoutOrientation.Horizontal,
@@ -180,13 +177,27 @@ namespace Content.Client.Chemistry.UI
} }
} }
public sealed class DispenseReagentButton : Button { public sealed class DispenseReagentButton : Button
public ReagentId ReagentId { get; }
public DispenseReagentButton(ReagentId reagentId, string text)
{ {
public string ReagentId { get; }
public DispenseReagentButton(string reagentId, string text, string amount)
{
AddStyleClass("OpenRight");
ReagentId = reagentId; ReagentId = reagentId;
Text = text; Text = text + " " + amount;
}
}
public sealed class EjectJugButton : Button
{
public string ReagentId { get; }
public EjectJugButton(string reagentId)
{
AddStyleClass("OpenLeft");
ReagentId = reagentId;
Text = "⏏";
} }
} }
} }

View File

@@ -1,6 +1,7 @@
using Content.Client.Chemistry.UI; using Content.Client.Chemistry.UI;
using Content.IntegrationTests.Tests.Interaction; using Content.IntegrationTests.Tests.Interaction;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Server.Chemistry.Components;
using Content.Shared.Containers.ItemSlots; using Content.Shared.Containers.ItemSlots;
namespace Content.IntegrationTests.Tests.Chemistry; namespace Content.IntegrationTests.Tests.Chemistry;
@@ -24,7 +25,7 @@ public sealed class DispenserTest : InteractionTest
await Interact(); await Interact();
// Eject beaker via BUI. // Eject beaker via BUI.
var ev = new ItemSlotButtonPressedEvent(SharedChemMaster.InputSlotName); var ev = new ItemSlotButtonPressedEvent(ReagentDispenserComponent.BeakerSlotId);
await SendBui(ReagentDispenserUiKey.Key, ev); await SendBui(ReagentDispenserUiKey.Key, ev);
// Beaker is back in the player's hands // Beaker is back in the player's hands

View File

@@ -1,3 +1,5 @@
using Content.Shared.Whitelist;
using Content.Shared.Containers.ItemSlots;
using Content.Server.Chemistry.EntitySystems; using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Dispenser; using Content.Shared.Chemistry.Dispenser;
@@ -7,20 +9,57 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy
namespace Content.Server.Chemistry.Components namespace Content.Server.Chemistry.Components
{ {
/// <summary> /// <summary>
/// A machine that dispenses reagents into a solution container. /// A machine that dispenses reagents into a solution container from containers in its storage slots.
/// </summary> /// </summary>
[RegisterComponent] [RegisterComponent]
[Access(typeof(ReagentDispenserSystem))] [Access(typeof(ReagentDispenserSystem))]
public sealed partial class ReagentDispenserComponent : Component public sealed partial class ReagentDispenserComponent : Component
{ {
/// <summary>
/// String with the pack name that stores the initial fill of the dispenser. The initial
/// fill is added to the dispenser on MapInit. Note that we don't use ContainerFill because
/// we have to generate the storage slots at MapInit first, then fill them.
/// </summary>
[DataField("pack", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentDispenserInventoryPrototype>))] [DataField("pack", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentDispenserInventoryPrototype>))]
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public string? PackPrototypeId = default!; public string? PackPrototypeId = default!;
[DataField("emagPack", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentDispenserInventoryPrototype>))] /// <summary>
[ViewVariables(VVAccess.ReadWrite)] /// Maximum number of internal storage slots. Dispenser can't store (or dispense) more than
public string? EmagPackPrototypeId = default!; /// this many chemicals (without unloading and reloading).
/// </summary>
[DataField("numStorageSlots")]
public int NumSlots = 25;
/// <summary>
/// For each created storage slot for the reagent containers being dispensed, apply this
/// entity whitelist. Makes sure weird containers don't fit in the dispenser and that beakers
/// don't accidentally get slotted into the source slots.
/// </summary>
[DataField]
public EntityWhitelist? StorageWhitelist;
/// <summary>
/// Slot for container to dispense into.
/// </summary>
public static string BeakerSlotId = "ReagentDispenser-beakerSlot";
[DataField]
public ItemSlot BeakerSlot = new();
/// <summary>
/// Prefix for automatically-generated slot name for storage, up to NumSlots.
/// </summary>
public static string BaseStorageSlotId = "ReagentDispenser-storageSlot";
/// <summary>
/// List of storage slots that were created at MapInit.
/// </summary>
[DataField]
public List<string> StorageSlotIds = new List<string>();
[DataField]
public List<ItemSlot> StorageSlots = new List<ItemSlot>();
[DataField("clickSound"), ViewVariables(VVAccess.ReadWrite)] [DataField("clickSound"), ViewVariables(VVAccess.ReadWrite)]
public SoundSpecifier ClickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg"); public SoundSpecifier ClickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg");

View File

@@ -1,14 +1,18 @@
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems; using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Nutrition.Components;
using Content.Server.Nutrition.EntitySystems;
using Content.Server.Labels.Components;
using Content.Server.Chemistry;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.Dispenser; using Content.Shared.Chemistry.Dispenser;
using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.Containers.ItemSlots; using Content.Shared.Containers.ItemSlots;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Emag.Components; using Content.Shared.FixedPoint;
using Content.Shared.Emag.Systems;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.Audio; using Robust.Server.Audio;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
@@ -28,10 +32,13 @@ namespace Content.Server.Chemistry.EntitySystems
{ {
[Dependency] private readonly AudioSystem _audioSystem = default!; [Dependency] private readonly AudioSystem _audioSystem = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly SolutionTransferSystem _solutionTransferSystem = default!;
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly OpenableSystem _openable = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -41,11 +48,12 @@ namespace Content.Server.Chemistry.EntitySystems
SubscribeLocalEvent<ReagentDispenserComponent, EntInsertedIntoContainerMessage>(SubscribeUpdateUiState); SubscribeLocalEvent<ReagentDispenserComponent, EntInsertedIntoContainerMessage>(SubscribeUpdateUiState);
SubscribeLocalEvent<ReagentDispenserComponent, EntRemovedFromContainerMessage>(SubscribeUpdateUiState); SubscribeLocalEvent<ReagentDispenserComponent, EntRemovedFromContainerMessage>(SubscribeUpdateUiState);
SubscribeLocalEvent<ReagentDispenserComponent, BoundUIOpenedEvent>(SubscribeUpdateUiState); SubscribeLocalEvent<ReagentDispenserComponent, BoundUIOpenedEvent>(SubscribeUpdateUiState);
SubscribeLocalEvent<ReagentDispenserComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<ReagentDispenserComponent, ReagentDispenserSetDispenseAmountMessage>(OnSetDispenseAmountMessage); SubscribeLocalEvent<ReagentDispenserComponent, ReagentDispenserSetDispenseAmountMessage>(OnSetDispenseAmountMessage);
SubscribeLocalEvent<ReagentDispenserComponent, ReagentDispenserDispenseReagentMessage>(OnDispenseReagentMessage); SubscribeLocalEvent<ReagentDispenserComponent, ReagentDispenserDispenseReagentMessage>(OnDispenseReagentMessage);
SubscribeLocalEvent<ReagentDispenserComponent, ReagentDispenserClearContainerSolutionMessage>(OnClearContainerSolutionMessage); SubscribeLocalEvent<ReagentDispenserComponent, ReagentDispenserClearContainerSolutionMessage>(OnClearContainerSolutionMessage);
SubscribeLocalEvent<ReagentDispenserComponent, MapInitEvent>(OnMapInit, before: new []{typeof(ItemSlotsSystem)});
} }
private void SubscribeUpdateUiState<T>(Entity<ReagentDispenserComponent> ent, ref T ev) private void SubscribeUpdateUiState<T>(Entity<ReagentDispenserComponent> ent, ref T ev)
@@ -80,35 +88,38 @@ namespace Content.Server.Chemistry.EntitySystems
return null; return null;
} }
private List<ReagentId> GetInventory(Entity<ReagentDispenserComponent> ent) private List<KeyValuePair<string, KeyValuePair<string, string>>> GetInventory(ReagentDispenserComponent reagentDispenser)
{ {
var reagentDispenser = ent.Comp; var inventory = new List<KeyValuePair<string, KeyValuePair<string, string>>>();
var inventory = new List<ReagentId>();
if (reagentDispenser.PackPrototypeId is not null for (var i = 0; i < reagentDispenser.NumSlots; i++)
&& _prototypeManager.TryIndex(reagentDispenser.PackPrototypeId, out ReagentDispenserInventoryPrototype? packPrototype))
{ {
inventory.AddRange(packPrototype.Inventory.Select(x => new ReagentId(x, null))); var storageSlotId = ReagentDispenserComponent.BaseStorageSlotId + i;
var storedContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenser.Owner, storageSlotId);
// Set label from manually-applied label, or metadata if unavailable
string reagentLabel;
if (TryComp<LabelComponent>(storedContainer, out var label) && !string.IsNullOrEmpty(label.CurrentLabel))
reagentLabel = label.CurrentLabel;
else if (storedContainer != null)
reagentLabel = Name(storedContainer.Value);
else
continue;
// Add volume remaining label
FixedPoint2 quantity = 0f;
if (storedContainer != null && _solutionContainerSystem.TryGetDrainableSolution(storedContainer.Value, out _, out var sol))
{
quantity = sol.Volume;
} }
var storedAmount = Loc.GetString("reagent-dispenser-window-quantity-label-text", ("quantity", quantity));
if (HasComp<EmaggedComponent>(ent) inventory.Add(new KeyValuePair<string, KeyValuePair<string, string>>(storageSlotId, new KeyValuePair<string, string>(reagentLabel, storedAmount)));
&& reagentDispenser.EmagPackPrototypeId is not null
&& _prototypeManager.TryIndex(reagentDispenser.EmagPackPrototypeId, out ReagentDispenserInventoryPrototype? emagPackPrototype))
{
inventory.AddRange(emagPackPrototype.Inventory.Select(x => new ReagentId(x, null)));
} }
return inventory; return inventory;
} }
private void OnEmagged(Entity<ReagentDispenserComponent> reagentDispenser, ref GotEmaggedEvent args)
{
// adding component manually to have correct state
EntityManager.AddComponent<EmaggedComponent>(reagentDispenser);
UpdateUiState(reagentDispenser);
args.Handled = true;
}
private void OnSetDispenseAmountMessage(Entity<ReagentDispenserComponent> reagentDispenser, ref ReagentDispenserSetDispenseAmountMessage message) private void OnSetDispenseAmountMessage(Entity<ReagentDispenserComponent> reagentDispenser, ref ReagentDispenserSetDispenseAmountMessage message)
{ {
reagentDispenser.Comp.DispenseAmount = message.ReagentDispenserDispenseAmount; reagentDispenser.Comp.DispenseAmount = message.ReagentDispenserDispenseAmount;
@@ -119,18 +130,23 @@ namespace Content.Server.Chemistry.EntitySystems
private void OnDispenseReagentMessage(Entity<ReagentDispenserComponent> reagentDispenser, ref ReagentDispenserDispenseReagentMessage message) private void OnDispenseReagentMessage(Entity<ReagentDispenserComponent> reagentDispenser, ref ReagentDispenserDispenseReagentMessage message)
{ {
// Ensure that the reagent is something this reagent dispenser can dispense. // Ensure that the reagent is something this reagent dispenser can dispense.
if (!GetInventory(reagentDispenser).Contains(message.ReagentId)) var storedContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenser, message.SlotId);
if (storedContainer == null)
return; return;
var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenser, SharedReagentDispenser.OutputSlotName); var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenser, SharedReagentDispenser.OutputSlotName);
if (outputContainer is not { Valid: true } || !_solutionContainerSystem.TryGetFitsInDispenser(outputContainer.Value, out var solution, out _)) if (outputContainer is not { Valid: true } || !_solutionContainerSystem.TryGetFitsInDispenser(outputContainer.Value, out var solution, out _))
return; return;
if (_solutionContainerSystem.TryAddReagent(solution.Value, message.ReagentId, (int) reagentDispenser.Comp.DispenseAmount, out var dispensedAmount) if (_solutionContainerSystem.TryGetDrainableSolution(storedContainer.Value, out var src, out _) &&
&& message.Session.AttachedEntity is not null) _solutionContainerSystem.TryGetRefillableSolution(outputContainer.Value, out var dst, out _))
{ {
_adminLogger.Add(LogType.ChemicalReaction, LogImpact.Medium, // force open container, if applicable, to avoid confusing people on why it doesn't dispense
$"{ToPrettyString(message.Session.AttachedEntity.Value):player} dispensed {dispensedAmount}u of {message.ReagentId} into {ToPrettyString(outputContainer.Value):entity}"); _openable.SetOpen(storedContainer.Value, true);
_solutionTransferSystem.Transfer(reagentDispenser,
storedContainer.Value, src.Value,
outputContainer.Value, dst.Value,
(int)reagentDispenser.Comp.DispenseAmount);
} }
UpdateUiState(reagentDispenser); UpdateUiState(reagentDispenser);
@@ -152,5 +168,41 @@ namespace Content.Server.Chemistry.EntitySystems
{ {
_audioSystem.PlayPvs(reagentDispenser.Comp.ClickSound, reagentDispenser, AudioParams.Default.WithVolume(-2f)); _audioSystem.PlayPvs(reagentDispenser.Comp.ClickSound, reagentDispenser, AudioParams.Default.WithVolume(-2f));
} }
/// <summary>
/// Automatically generate storage slots for all NumSlots, and fill them with their initial chemicals.
/// The actual spawning of entities happens in ItemSlotsSystem's MapInit.
/// </summary>
private void OnMapInit(EntityUid uid, ReagentDispenserComponent component, MapInitEvent args)
{
// Get list of pre-loaded containers
List<string> preLoad = new List<string>();
if (component.PackPrototypeId is not null
&& _prototypeManager.TryIndex(component.PackPrototypeId, out ReagentDispenserInventoryPrototype? packPrototype))
{
preLoad.AddRange(packPrototype.Inventory);
}
// Populate storage slots with base storage slot whitelist
for (var i = 0; i < component.NumSlots; i++)
{
var storageSlotId = ReagentDispenserComponent.BaseStorageSlotId + i;
ItemSlot storageComponent = new();
storageComponent.Whitelist = component.StorageWhitelist;
storageComponent.Swap = false;
storageComponent.EjectOnBreak = true;
// Check corresponding index in pre-loaded container (if exists) and set starting item
if (i < preLoad.Count)
storageComponent.StartingItem = preLoad[i];
component.StorageSlotIds.Add(storageSlotId);
component.StorageSlots.Add(storageComponent);
component.StorageSlots[i].Name = "Storage Slot " + (i+1);
_itemSlotsSystem.AddItemSlot(uid, component.StorageSlotIds[i], component.StorageSlots[i]);
}
_itemSlotsSystem.AddItemSlot(uid, ReagentDispenserComponent.BeakerSlotId, component.BeakerSlot);
}
} }
} }

View File

@@ -1,4 +1,4 @@
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
@@ -14,8 +14,7 @@ namespace Content.Shared.Chemistry.Dispenser
[Serializable, NetSerializable, Prototype("reagentDispenserInventory")] [Serializable, NetSerializable, Prototype("reagentDispenserInventory")]
public sealed partial class ReagentDispenserInventoryPrototype : IPrototype public sealed partial class ReagentDispenserInventoryPrototype : IPrototype
{ {
// TODO use ReagentId [DataField("inventory", customTypeSerializer: typeof(PrototypeIdListSerializer<EntityPrototype>))]
[DataField("inventory", customTypeSerializer: typeof(PrototypeIdListSerializer<ReagentPrototype>))]
public List<string> Inventory = new(); public List<string> Inventory = new();
[ViewVariables, IdDataField] [ViewVariables, IdDataField]

View File

@@ -8,7 +8,7 @@ namespace Content.Shared.Chemistry
/// </summary> /// </summary>
public sealed class SharedReagentDispenser public sealed class SharedReagentDispenser
{ {
public const string OutputSlotName = "beakerSlot"; public const string OutputSlotName = "ReagentDispenser-beakerSlot";
} }
[Serializable, NetSerializable] [Serializable, NetSerializable]
@@ -25,11 +25,11 @@ namespace Content.Shared.Chemistry
[Serializable, NetSerializable] [Serializable, NetSerializable]
public sealed class ReagentDispenserDispenseReagentMessage : BoundUserInterfaceMessage public sealed class ReagentDispenserDispenseReagentMessage : BoundUserInterfaceMessage
{ {
public readonly ReagentId ReagentId; public readonly string SlotId;
public ReagentDispenserDispenseReagentMessage(ReagentId reagentId) public ReagentDispenserDispenseReagentMessage(string slotId)
{ {
ReagentId = reagentId; SlotId = slotId;
} }
} }
@@ -59,11 +59,11 @@ namespace Content.Shared.Chemistry
/// <summary> /// <summary>
/// A list of the reagents which this dispenser can dispense. /// A list of the reagents which this dispenser can dispense.
/// </summary> /// </summary>
public readonly List<ReagentId> Inventory; public readonly List<KeyValuePair<string, KeyValuePair<string, string>>> Inventory;
public readonly ReagentDispenserDispenseAmount SelectedDispenseAmount; public readonly ReagentDispenserDispenseAmount SelectedDispenseAmount;
public ReagentDispenserBoundUserInterfaceState(ContainerInfo? outputContainer, List<ReagentId> inventory, ReagentDispenserDispenseAmount selectedDispenseAmount) public ReagentDispenserBoundUserInterfaceState(ContainerInfo? outputContainer, List<KeyValuePair<string, KeyValuePair<string, string>>> inventory, ReagentDispenserDispenseAmount selectedDispenseAmount)
{ {
OutputContainer = outputContainer; OutputContainer = outputContainer;
Inventory = inventory; Inventory = inventory;

View File

@@ -67,8 +67,8 @@ namespace Content.Shared.Containers.ItemSlots
CopyFrom(other); CopyFrom(other);
} }
[DataField("whitelist")] [DataField("whitelist")]
[Access(typeof(ItemSlotsSystem), Other = AccessPermissions.ReadWriteExecute)]
public EntityWhitelist? Whitelist; public EntityWhitelist? Whitelist;
[DataField("blacklist")] [DataField("blacklist")]
@@ -179,6 +179,7 @@ namespace Content.Shared.Containers.ItemSlots
/// The actual deconstruction logic is handled by the server-side EmptyOnMachineDeconstructSystem. /// The actual deconstruction logic is handled by the server-side EmptyOnMachineDeconstructSystem.
/// </remarks> /// </remarks>
[DataField("ejectOnDeconstruct")] [DataField("ejectOnDeconstruct")]
[Access(typeof(ItemSlotsSystem), Other = AccessPermissions.ReadWriteExecute)]
[NonSerialized] [NonSerialized]
public bool EjectOnDeconstruct = true; public bool EjectOnDeconstruct = true;
@@ -187,6 +188,7 @@ namespace Content.Shared.Containers.ItemSlots
/// ejected when it is broken or destroyed? /// ejected when it is broken or destroyed?
/// </summary> /// </summary>
[DataField("ejectOnBreak")] [DataField("ejectOnBreak")]
[Access(typeof(ItemSlotsSystem), Other = AccessPermissions.ReadWriteExecute)]
[NonSerialized] [NonSerialized]
public bool EjectOnBreak = false; public bool EjectOnBreak = false;
@@ -205,6 +207,7 @@ namespace Content.Shared.Containers.ItemSlots
/// want to insert more than one item that matches the same whitelist. /// want to insert more than one item that matches the same whitelist.
/// </remarks> /// </remarks>
[DataField("swap")] [DataField("swap")]
[Access(typeof(ItemSlotsSystem), Other = AccessPermissions.ReadWriteExecute)]
public bool Swap = true; public bool Swap = true;
public string? ID => ContainerSlot?.ID; public string? ID => ContainerSlot?.ID;

View File

@@ -1,61 +1,43 @@
- type: reagentDispenserInventory - type: reagentDispenserInventory
id: SodaDispenserInventory id: SodaDispenserInventory
inventory: inventory:
- Ice - DrinkIceJug
- Coffee - DrinkCoffeeJug
- Cream - DrinkCreamCartonXL
- Tea - DrinkTeaJug
- GreenTea - DrinkGreenTeaJug
- IcedTea - DrinkIcedTeaJug
- IcedGreenTea - DrinkColaBottleFull
- Cola - DrinkSpaceMountainWindBottleFull
- SpaceMountainWind - DrinkDrGibbJug
- DrGibb - DrinkRootBeerJug
- RootBeer - DrinkSpaceUpBottleFull
- SpaceUp - DrinkTonicWaterBottleFull
- TonicWater - DrinkSodaWaterBottleFull
- SodaWater - DrinkLemonLimeJug
- LemonLime - DrinkSugarJug
- Sugar - DrinkJuiceOrangeCartonXL
- JuiceOrange - DrinkJuiceLimeCartonXL
- JuiceLime - DrinkWaterMelonJuiceJug
- JuiceWatermelon
###Hacked
#- Fourteen Loko
#- GrapeSoda
- type: reagentDispenserInventory - type: reagentDispenserInventory
id: BoozeDispenserInventory id: BoozeDispenserInventory
inventory: inventory:
- Beer - DrinkLemonLimeJug
- CoffeeLiqueur - DrinkSugarJug
- Whiskey - DrinkJuiceOrangeCartonXL
- Wine - DrinkJuiceLimeCartonXL
- Vodka - DrinkTonicWaterBottleFull
- Gin - DrinkSodaWaterBottleFull
- Rum - DrinkBeerGrowler
- Tequila - DrinkCoffeeLiqueurBottleFull
- Vermouth - DrinkWhiskeyBottleFull
- Cognac - DrinkWineBottleFull
- Ale - DrinkVodkaBottleFull
- Mead - DrinkGinBottleFull
###Hacked - DrinkRumBottleFull
#- Goldschlager - DrinkTequilaBottleFull
#- Patron - DrinkVermouthBottleFull
#- JuiceWatermelon - DrinkCognacBottleFull
#- JuiceBerry - DrinkAleBottleFullGrowler
- DrinkMeadJug
- type: reagentDispenserInventory
id: SodaDispenserEmagInventory
inventory:
- FourteenLoko
- Ephedrine
- Histamine
- type: reagentDispenserInventory
id: BoozeDispenserEmagInventory
inventory:
- AtomicBomb
- Ethanol
- Iron

View File

@@ -1,31 +1,26 @@
- type: reagentDispenserInventory - type: reagentDispenserInventory
id: ChemDispenserStandardInventory id: ChemDispenserStandardInventory
inventory: inventory:
- Aluminium - JugAluminium
- Carbon - JugCarbon
- Chlorine - JugChlorine
- Copper - JugCopper
- Ethanol - JugEthanol
- Fluorine - JugFluorine
- Sugar - JugSugar
- Hydrogen - JugHydrogen
- Iodine - JugIodine
- Iron - JugIron
- Lithium - JugLithium
- Mercury - JugMercury
- Nitrogen - JugNitrogen
- Oxygen - JugOxygen
- Phosphorus - JugPhosphorus
- Potassium - JugPotassium
- Radium - JugRadium
- Silicon - JugSilicon
- Sodium - JugSodium
- Sulfur - JugSulfur
- type: reagentDispenserInventory - type: reagentDispenserInventory
id: ChemDispenserEmaggedInventory id: EmptyInventory
inventory: ##Feel free to change this to something more interesting when more chems are added
- Napalm
- Toxin
- Epinephrine
- Ultravasculine

View File

@@ -4,6 +4,9 @@
id: DrinkBottlePlasticBaseFull id: DrinkBottlePlasticBaseFull
abstract: true abstract: true
components: components:
- type: Tag
tags:
- DrinkBottle
- type: Openable - type: Openable
sound: sound:
collection: bottleOpenSounds collection: bottleOpenSounds
@@ -13,6 +16,7 @@
maxVol: 100 maxVol: 100
- type: Sprite - type: Sprite
state: icon state: icon
sprite: Objects/Consumable/Drinks/water.rsi # fallback to boring water jug
- type: Item - type: Item
size: Normal size: Normal
- type: Damageable - type: Damageable
@@ -285,6 +289,8 @@
reagents: reagents:
- ReagentId: Rum - ReagentId: Rum
Quantity: 100 Quantity: 100
- type: Label
currentLabel: rum
- type: Sprite - type: Sprite
sprite: Objects/Consumable/Drinks/rumbottle.rsi sprite: Objects/Consumable/Drinks/rumbottle.rsi
@@ -332,6 +338,8 @@
reagents: reagents:
- ReagentId: Tequila - ReagentId: Tequila
Quantity: 100 Quantity: 100
- type: Label
currentLabel: tequila
- type: Sprite - type: Sprite
sprite: Objects/Consumable/Drinks/tequillabottle.rsi sprite: Objects/Consumable/Drinks/tequillabottle.rsi
@@ -347,6 +355,8 @@
reagents: reagents:
- ReagentId: Vermouth - ReagentId: Vermouth
Quantity: 100 Quantity: 100
- type: Label
currentLabel: vermouth
- type: Sprite - type: Sprite
sprite: Objects/Consumable/Drinks/vermouthbottle.rsi sprite: Objects/Consumable/Drinks/vermouthbottle.rsi
@@ -377,6 +387,8 @@
reagents: reagents:
- ReagentId: Whiskey - ReagentId: Whiskey
Quantity: 100 Quantity: 100
- type: Label
currentLabel: whiskey
- type: Sprite - type: Sprite
sprite: Objects/Consumable/Drinks/whiskeybottle.rsi sprite: Objects/Consumable/Drinks/whiskeybottle.rsi
@@ -392,6 +404,8 @@
reagents: reagents:
- ReagentId: Wine - ReagentId: Wine
Quantity: 100 Quantity: 100
- type: Label
currentLabel: wine
- type: Sprite - type: Sprite
sprite: Objects/Consumable/Drinks/winebottle.rsi sprite: Objects/Consumable/Drinks/winebottle.rsi
@@ -417,6 +431,22 @@
- type: entity - type: entity
parent: DrinkBottleGlassBaseFull parent: DrinkBottleGlassBaseFull
id: DrinkBeerGrowler # Needs to be renamed DrinkBeerBottleFull
name: Beer Growler # beer it is. coffee. beer? coff-ee? be-er? c-o... b-e
description: An alcoholic beverage made from malted grains, hops, yeast, and water. XL growler bottle.
components:
- type: SolutionContainerManager
solutions:
drink:
maxVol: 150
reagents:
- ReagentId: Beer
Quantity: 150
- type: Sprite
sprite: Objects/Consumable/Drinks/beer.rsi
- type: entity
parent: DrinkBottlePlasticBaseFull
id: DrinkAleBottleFull id: DrinkAleBottleFull
name: Magm-Ale name: Magm-Ale
description: A true dorf's drink of choice. description: A true dorf's drink of choice.
@@ -433,6 +463,22 @@
- type: Sprite - type: Sprite
sprite: Objects/Consumable/Drinks/alebottle.rsi sprite: Objects/Consumable/Drinks/alebottle.rsi
- type: entity
parent: DrinkBottlePlasticBaseFull
id: DrinkAleBottleFullGrowler
name: Magm-Ale Growler
description: A true dorf's drink of choice. XL growler bottle.
components:
- type: SolutionContainerManager
solutions:
drink:
maxVol: 150
reagents:
- ReagentId: Ale
Quantity: 150
- type: Sprite
sprite: Objects/Consumable/Drinks/alebottle.rsi
- type: entity - type: entity
parent: DrinkBottlePlasticBaseFull parent: DrinkBottlePlasticBaseFull
id: DrinkWaterBottleFull id: DrinkWaterBottleFull
@@ -451,3 +497,258 @@
- type: Drink - type: Drink
- type: Sprite - type: Sprite
sprite: Objects/Consumable/Drinks/waterbottle.rsi sprite: Objects/Consumable/Drinks/waterbottle.rsi
- type: entity
parent: DrinkBottlePlasticBaseFull
id: DrinkSodaWaterBottleFull
name: soda water bottle
description: Like water, but angry!
components:
- type: SolutionContainerManager
solutions:
drink:
maxVol: 150
reagents:
- ReagentId: SodaWater
Quantity: 150
- type: Drink
- type: Sprite
sprite: Objects/Consumable/Drinks/waterbottle.rsi
- type: entity
parent: DrinkBottlePlasticBaseFull
id: DrinkTonicWaterBottleFull
name: tonic water bottle
description: Like soda water, but angrier maybe? Often sweeter.
components:
- type: SolutionContainerManager
solutions:
drink:
maxVol: 150
reagents:
- ReagentId: TonicWater
Quantity: 150
- type: Drink
- type: Sprite
sprite: Objects/Consumable/Drinks/waterbottle.rsi
# Cartons, TODO: this needs to be moved elsewhere eventually, since cartons shouldnt smash into glass shards
- type: entity
parent: DrinkBottlePlasticBaseFull
id: DrinkJuiceLimeCartonXL
name: lime juice XL
description: Sweet-sour goodness.
components:
- type: SolutionContainerManager
solutions:
drink:
maxVol: 150
reagents:
- ReagentId: JuiceLime
Quantity: 150
- type: Drink
- type: Sprite
sprite: Objects/Consumable/Drinks/limejuice.rsi
- type: entity
parent: DrinkBottlePlasticBaseFull
id: DrinkJuiceOrangeCartonXL
name: orange juice XL
description: Full of vitamins and deliciousness!
components:
- type: SolutionContainerManager
solutions:
drink:
maxVol: 150
reagents:
- ReagentId: JuiceOrange
Quantity: 150
- type: Drink
- type: Sprite
sprite: Objects/Consumable/Drinks/orangejuice.rsi
- type: entity
parent: DrinkBottlePlasticBaseFull
id: DrinkCreamCartonXL
name: Milk Cream XL
description: It's cream. Made from milk. What else did you think you'd find in there?
components:
- type: SolutionContainerManager
solutions:
drink:
maxVol: 150
reagents:
- ReagentId: Cream
Quantity: 150
- type: Drink
- type: Sprite
sprite: Objects/Consumable/Drinks/cream.rsi
#boring jugs some more sprites are made
- type: entity
parent: DrinkBottlePlasticBaseFull
id: DrinkSugarJug
name: sugar
suffix: for drinks
description: some people put this in their coffee...
components:
- type: SolutionContainerManager
solutions:
drink:
maxVol: 300
reagents:
- ReagentId: Sugar
Quantity: 300
- type: Drink
- type: entity
parent: DrinkBottlePlasticBaseFull
id: DrinkLemonLimeJug
name: lemon lime
description: a dual citrus sensation.
components:
- type: SolutionContainerManager
solutions:
drink:
maxVol: 300
reagents:
- ReagentId: LemonLime
Quantity: 300
- type: Drink
- type: entity
parent: DrinkBottlePlasticBaseFull
id: DrinkMeadJug
name: mead jug
description: storing mead in a plastic jug should be a crime.
components:
- type: SolutionContainerManager
solutions:
drink:
maxVol: 150
reagents:
- ReagentId: Mead
Quantity: 150
- type: Drink
- type: entity
parent: DrinkBottlePlasticBaseFull
id: DrinkIceJug
name: ice jug
description: stubborn water. pretty cool.
components:
- type: SolutionContainerManager
solutions:
drink:
maxVol: 300
reagents:
- ReagentId: Ice
Quantity: 300
- type: Drink
- type: entity
parent: DrinkBottlePlasticBaseFull
id: DrinkCoffeeJug
name: coffee jug
description: wake up juice, of the heated kind.
components:
- type: SolutionContainerManager
solutions:
drink:
maxVol: 300
reagents:
- ReagentId: Coffee
Quantity: 300
- type: Drink
- type: entity
parent: DrinkBottlePlasticBaseFull
id: DrinkTeaJug
name: tea jug
description: the drink of choice for the Bri'ish and hipsters.
components:
- type: SolutionContainerManager
solutions:
drink:
maxVol: 300
reagents:
- ReagentId: Tea
Quantity: 300
- type: Drink
- type: entity
parent: DrinkBottlePlasticBaseFull
id: DrinkGreenTeaJug
name: green tea jug
description: its like tea... but green! great for settling the stomach.
components:
- type: SolutionContainerManager
solutions:
drink:
maxVol: 300
reagents:
- ReagentId: GreenTea
Quantity: 300
- type: Drink
- type: entity
parent: DrinkBottlePlasticBaseFull
id: DrinkIcedTeaJug
name: iced tea jug
description: for when the regular tea is too hot for you boohoo
components:
- type: SolutionContainerManager
solutions:
drink:
maxVol: 300
reagents:
- ReagentId: IcedTea
Quantity: 300
- type: Drink
- type: entity
parent: DrinkBottlePlasticBaseFull
id: DrinkDrGibbJug
name: dr gibb jug
description: yeah I don't know either...
components:
- type: SolutionContainerManager
solutions:
drink:
maxVol: 300
reagents:
- ReagentId: DrGibb
Quantity: 300
- type: Drink
- type: entity
parent: DrinkBottlePlasticBaseFull
id: DrinkRootBeerJug
name: root beer jug
description: this drink makes Australians giggle
components:
- type: SolutionContainerManager
solutions:
drink:
maxVol: 300
reagents:
- ReagentId: RootBeer
Quantity: 300
- type: Drink
- type: entity
parent: DrinkBottlePlasticBaseFull
id: DrinkWaterMelonJuiceJug
name: watermelon juice jug
description: May include leftover seeds
components:
- type: SolutionContainerManager
solutions:
drink:
maxVol: 300
reagents:
- ReagentId: JuiceWatermelon
Quantity: 300
- type: Drink

View File

@@ -553,7 +553,7 @@
- type: Sprite - type: Sprite
state: medical state: medical
- type: MachineBoard - type: MachineBoard
prototype: ChemDispenser prototype: ChemDispenserEmpty
requirements: requirements:
Capacitor: 1 Capacitor: 1
materialRequirements: materialRequirements:
@@ -1145,7 +1145,7 @@
- type: Sprite - type: Sprite
state: service state: service
- type: MachineBoard - type: MachineBoard
prototype: BoozeDispenser prototype: BoozeDispenserEmpty
materialRequirements: materialRequirements:
Steel: 5 Steel: 5
tagRequirements: tagRequirements:
@@ -1178,7 +1178,7 @@
- type: Sprite - type: Sprite
state: service state: service
- type: MachineBoard - type: MachineBoard
prototype: soda_dispenser prototype: SodaDispenserEmpty
materialRequirements: materialRequirements:
Steel: 5 Steel: 5
tagRequirements: tagRequirements:

View File

@@ -47,6 +47,9 @@
price: 60 price: 60
- type: Label - type: Label
originalName: jug originalName: jug
- type: Tag
tags:
- ChemDispensable
- type: entity - type: entity
parent: Jug parent: Jug

View File

@@ -1,4 +1,4 @@
- type: entity - type: entity
abstract: true abstract: true
id: ReagentDispenserBase id: ReagentDispenserBase
parent: ConstructibleMachine parent: ConstructibleMachine
@@ -55,18 +55,20 @@
sound: sound:
path: /Audio/Effects/metalbreak.ogg path: /Audio/Effects/metalbreak.ogg
- type: ReagentDispenser - type: ReagentDispenser
- type: ItemSlots storageWhitelist:
slots: tags:
- Bottle
beakerSlot: beakerSlot:
whitelistFailPopup: reagent-dispenser-component-cannot-put-entity-message whitelistFailPopup: reagent-dispenser-component-cannot-put-entity-message
whitelist: whitelist:
components: components:
- FitsInDispenser - FitsInDispenser
- type: ItemSlots
- type: ContainerContainer - type: ContainerContainer
containers: containers:
machine_board: !type:Container machine_board: !type:Container
machine_parts: !type:Container machine_parts: !type:Container
beakerSlot: !type:ContainerSlot ReagentDispenser-beakerSlot: !type:ContainerSlot
- type: StaticPrice - type: StaticPrice
price: 1000 price: 1000
- type: Wires - type: Wires

View File

@@ -1,6 +1,7 @@
- type: entity - type: entity
id: BoozeDispenser id: BoozeDispenser
name: booze dispenser name: booze dispenser
suffix: Filled
description: A booze dispenser with a single slot for a container to be filled. description: A booze dispenser with a single slot for a container to be filled.
parent: ReagentDispenserBase parent: ReagentDispenserBase
components: components:
@@ -10,8 +11,10 @@
drawdepth: SmallObjects drawdepth: SmallObjects
state: booze state: booze
- type: ReagentDispenser - type: ReagentDispenser
storageWhitelist:
tags:
- DrinkBottle
pack: BoozeDispenserInventory pack: BoozeDispenserInventory
emagPack: BoozeDispenserEmagInventory
- type: Transform - type: Transform
noRot: false noRot: false
- type: Machine - type: Machine
@@ -24,3 +27,14 @@
- Bartender - Bartender
- type: StealTarget - type: StealTarget
stealGroup: BoozeDispenser stealGroup: BoozeDispenser
- type: entity
id: BoozeDispenserEmpty
suffix: Empty
parent: BoozeDispenser
components:
- type: ReagentDispenser
storageWhitelist:
tags:
- DrinkBottle
pack: EmptyInventory

View File

@@ -1,16 +1,19 @@
- type: entity - type: entity
id: ChemDispenser id: ChemDispenser
name: chemical dispenser name: chemical dispenser
suffix: Filled
parent: ReagentDispenserBase parent: ReagentDispenserBase
description: An industrial grade chemical dispenser with a sizeable chemical supply. description: An industrial grade chemical dispenser.
components: components:
- type: Sprite - type: Sprite
sprite: Structures/dispensers.rsi sprite: Structures/dispensers.rsi
state: industrial-working state: industrial-working
snapCardinals: true snapCardinals: true
- type: ReagentDispenser - type: ReagentDispenser
storageWhitelist:
tags:
- ChemDispensable
pack: ChemDispenserStandardInventory pack: ChemDispenserStandardInventory
emagPack: ChemDispenserEmaggedInventory
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver - type: ExtensionCableReceiver
- type: Destructible - type: Destructible
@@ -37,3 +40,12 @@
- Chemist - Chemist
- type: StealTarget - type: StealTarget
stealGroup: ChemDispenser stealGroup: ChemDispenser
- type: entity
id: ChemDispenserEmpty
name: chemical dispenser
suffix: Empty
parent: ChemDispenser
components:
- type: ReagentDispenser
pack: EmptyInventory

View File

@@ -1,6 +1,7 @@
- type: entity - type: entity
id: soda_dispenser id: soda_dispenser
name: soda dispenser name: soda dispenser
suffix: Filled
parent: ReagentDispenserBase parent: ReagentDispenserBase
description: A beverage dispenser with a selection of soda and several other common beverages. Has a single fill slot for containers. description: A beverage dispenser with a selection of soda and several other common beverages. Has a single fill slot for containers.
components: components:
@@ -10,8 +11,10 @@
drawdepth: SmallObjects drawdepth: SmallObjects
state: soda state: soda
- type: ReagentDispenser - type: ReagentDispenser
storageWhitelist:
tags:
- DrinkBottle
pack: SodaDispenserInventory pack: SodaDispenserInventory
emagPack: SodaDispenserEmagInventory
- type: Transform - type: Transform
noRot: false noRot: false
- type: Machine - type: Machine
@@ -22,3 +25,14 @@
- type: GuideHelp - type: GuideHelp
guides: guides:
- Bartender - Bartender
- type: entity
id: SodaDispenserEmpty
suffix: Empty
parent: soda_dispenser
components:
- type: ReagentDispenser
storageWhitelist:
tags:
- DrinkBottle
pack: EmptyInventory

View File

@@ -305,6 +305,9 @@
- type: Tag - type: Tag
id: Chicken id: Chicken
- type: Tag
id: ChemDispensable # container that can go into the chem dispenser
- type: Tag - type: Tag
id: Cigarette id: Cigarette
@@ -458,6 +461,9 @@
- type: Tag - type: Tag
id: DrinkSpaceGlue id: DrinkSpaceGlue
- type: Tag
id: DrinkBottle
- type: Tag - type: Tag
id: DroneUsable id: DroneUsable