_window.OnDispenseReagentButtonMouseEntered += (args, button) =>
{
if (_lastState is not null)
- _window.UpdateContainerInfo(_lastState, button.ReagentId);
+ _window.UpdateContainerInfo(_lastState);
};
_window.OnDispenseReagentButtonMouseExited += (args, button) =>
{
if (_lastState is not null)
_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>
-<DefaultWindow xmlns="https://spacestation14.io"
+<DefaultWindow xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
Title="{Loc 'reagent-dispenser-bound-user-interface-title'}"
- SetSize="620 450"
- MinSize="620 450">
+ MinSize="680 450">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'reagent-dispenser-window-amount-to-dispense-label'}"/>
<Button Name="DispenseButton100" Access="Public" Text="100" StyleClasses="OpenLeft"/>
</BoxContainer>
<Control MinSize="0 10"/>
- <ScrollContainer HScrollEnabled="False" HorizontalExpand="True" MinSize="0 170">
- <GridContainer Name="ChemicalList" Access="Public" Columns="4">
- </GridContainer>
- </ScrollContainer>
+ <GridContainer Name="ChemicalList" HorizontalExpand="True" VerticalExpand="True" Access="Public" Columns="6">
+ </GridContainer>
<Control MinSize="0 10"/>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'reagent-dispenser-window-container-label'}"/>
public event Action<GUIMouseHoverEventArgs, DispenseReagentButton>? OnDispenseReagentButtonMouseEntered;
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>
/// 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.
/// Update the button grid of reagents which can be dispensed.
/// </summary>
/// <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)
return;
ChemicalList.Children.Clear();
- foreach (var entry in inventory
- .OrderBy(r => {_prototypeManager.TryIndex(r.Prototype, out ReagentPrototype? p); return p?.LocalizedName;}))
+ foreach (KeyValuePair<string, KeyValuePair<string, string>> entry in inventory)
{
- var localizedName = _prototypeManager.TryIndex(entry.Prototype, out ReagentPrototype? p)
- ? p.LocalizedName
- : Loc.GetString("reagent-dispenser-window-reagent-name-not-found-text");
-
- var button = new DispenseReagentButton(entry, localizedName);
+ var button = new DispenseReagentButton(entry.Key, entry.Value.Key, entry.Value.Value);
button.OnPressed += args => OnDispenseReagentButtonPressed?.Invoke(args, button);
button.OnMouseEntered += args => OnDispenseReagentButtonMouseEntered?.Invoke(args, button);
button.OnMouseExited += args => OnDispenseReagentButtonMouseExited?.Invoke(args, 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);
}
}
/// <para>Also highlights a reagent if it's dispense button is being mouse hovered.</para>
/// </summary>
/// <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>
- public void UpdateContainerInfo(ReagentDispenserBoundUserInterfaceState state, ReagentId? highlightedReagentId = null)
+ public void UpdateContainerInfo(ReagentDispenserBoundUserInterfaceState state)
{
ContainerInfo.Children.Clear();
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
{
Orientation = LayoutOrientation.Horizontal,
}
}
- public sealed class DispenseReagentButton : Button {
- public ReagentId ReagentId { get; }
+ public sealed class DispenseReagentButton : Button
+ {
+ public string ReagentId { get; }
+
+ public DispenseReagentButton(string reagentId, string text, string amount)
+ {
+ AddStyleClass("OpenRight");
+ ReagentId = reagentId;
+ Text = text + " " + amount;
+ }
+ }
+
+ public sealed class EjectJugButton : Button
+ {
+ public string ReagentId { get; }
- public DispenseReagentButton(ReagentId reagentId, string text)
+ public EjectJugButton(string reagentId)
{
+ AddStyleClass("OpenLeft");
ReagentId = reagentId;
- Text = text;
+ Text = "⏏";
}
}
}
using Content.Client.Chemistry.UI;
using Content.IntegrationTests.Tests.Interaction;
using Content.Shared.Chemistry;
+using Content.Server.Chemistry.Components;
using Content.Shared.Containers.ItemSlots;
namespace Content.IntegrationTests.Tests.Chemistry;
await Interact();
// Eject beaker via BUI.
- var ev = new ItemSlotButtonPressedEvent(SharedChemMaster.InputSlotName);
+ var ev = new ItemSlotButtonPressedEvent(ReagentDispenserComponent.BeakerSlotId);
await SendBui(ReagentDispenserUiKey.Key, ev);
// Beaker is back in the player's hands
+using Content.Shared.Whitelist;
+using Content.Shared.Containers.ItemSlots;
using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Dispenser;
namespace Content.Server.Chemistry.Components
{
/// <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>
[RegisterComponent]
[Access(typeof(ReagentDispenserSystem))]
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>))]
[ViewVariables(VVAccess.ReadWrite)]
public string? PackPrototypeId = default!;
- [DataField("emagPack", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentDispenserInventoryPrototype>))]
- [ViewVariables(VVAccess.ReadWrite)]
- public string? EmagPackPrototypeId = default!;
+ /// <summary>
+ /// Maximum number of internal storage slots. Dispenser can't store (or dispense) more than
+ /// 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)]
public SoundSpecifier ClickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg");
using Content.Server.Administration.Logs;
using Content.Server.Chemistry.Components;
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.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.Emag.Systems;
+using Content.Shared.FixedPoint;
using JetBrains.Annotations;
using Robust.Server.Audio;
using Robust.Server.GameObjects;
{
[Dependency] private readonly AudioSystem _audioSystem = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
+ [Dependency] private readonly SolutionTransferSystem _solutionTransferSystem = default!;
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
+ [Dependency] private readonly OpenableSystem _openable = default!;
+
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ReagentDispenserComponent, EntInsertedIntoContainerMessage>(SubscribeUpdateUiState);
SubscribeLocalEvent<ReagentDispenserComponent, EntRemovedFromContainerMessage>(SubscribeUpdateUiState);
SubscribeLocalEvent<ReagentDispenserComponent, BoundUIOpenedEvent>(SubscribeUpdateUiState);
- SubscribeLocalEvent<ReagentDispenserComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<ReagentDispenserComponent, ReagentDispenserSetDispenseAmountMessage>(OnSetDispenseAmountMessage);
SubscribeLocalEvent<ReagentDispenserComponent, ReagentDispenserDispenseReagentMessage>(OnDispenseReagentMessage);
SubscribeLocalEvent<ReagentDispenserComponent, ReagentDispenserClearContainerSolutionMessage>(OnClearContainerSolutionMessage);
+
+ SubscribeLocalEvent<ReagentDispenserComponent, MapInitEvent>(OnMapInit, before: new []{typeof(ItemSlotsSystem)});
}
private void SubscribeUpdateUiState<T>(Entity<ReagentDispenserComponent> ent, ref T ev)
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<ReagentId>();
+ var inventory = new List<KeyValuePair<string, KeyValuePair<string, string>>>();
- if (reagentDispenser.PackPrototypeId is not null
- && _prototypeManager.TryIndex(reagentDispenser.PackPrototypeId, out ReagentDispenserInventoryPrototype? packPrototype))
+ for (var i = 0; i < reagentDispenser.NumSlots; i++)
{
- 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)
- && reagentDispenser.EmagPackPrototypeId is not null
- && _prototypeManager.TryIndex(reagentDispenser.EmagPackPrototypeId, out ReagentDispenserInventoryPrototype? emagPackPrototype))
- {
- inventory.AddRange(emagPackPrototype.Inventory.Select(x => new ReagentId(x, null)));
+ inventory.Add(new KeyValuePair<string, KeyValuePair<string, string>>(storageSlotId, new KeyValuePair<string, string>(reagentLabel, storedAmount)));
}
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)
{
reagentDispenser.Comp.DispenseAmount = message.ReagentDispenserDispenseAmount;
private void OnDispenseReagentMessage(Entity<ReagentDispenserComponent> reagentDispenser, ref ReagentDispenserDispenseReagentMessage message)
{
// 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;
var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenser, SharedReagentDispenser.OutputSlotName);
if (outputContainer is not { Valid: true } || !_solutionContainerSystem.TryGetFitsInDispenser(outputContainer.Value, out var solution, out _))
return;
- if (_solutionContainerSystem.TryAddReagent(solution.Value, message.ReagentId, (int) reagentDispenser.Comp.DispenseAmount, out var dispensedAmount)
- && message.Session.AttachedEntity is not null)
+ if (_solutionContainerSystem.TryGetDrainableSolution(storedContainer.Value, out var src, out _) &&
+ _solutionContainerSystem.TryGetRefillableSolution(outputContainer.Value, out var dst, out _))
{
- _adminLogger.Add(LogType.ChemicalReaction, LogImpact.Medium,
- $"{ToPrettyString(message.Session.AttachedEntity.Value):player} dispensed {dispensedAmount}u of {message.ReagentId} into {ToPrettyString(outputContainer.Value):entity}");
+ // force open container, if applicable, to avoid confusing people on why it doesn't dispense
+ _openable.SetOpen(storedContainer.Value, true);
+ _solutionTransferSystem.Transfer(reagentDispenser,
+ storedContainer.Value, src.Value,
+ outputContainer.Value, dst.Value,
+ (int)reagentDispenser.Comp.DispenseAmount);
}
UpdateUiState(reagentDispenser);
{
_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);
+ }
}
}
-using Content.Shared.Chemistry.Reagent;
+using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
[Serializable, NetSerializable, Prototype("reagentDispenserInventory")]
public sealed partial class ReagentDispenserInventoryPrototype : IPrototype
{
- // TODO use ReagentId
- [DataField("inventory", customTypeSerializer: typeof(PrototypeIdListSerializer<ReagentPrototype>))]
+ [DataField("inventory", customTypeSerializer: typeof(PrototypeIdListSerializer<EntityPrototype>))]
public List<string> Inventory = new();
[ViewVariables, IdDataField]
/// </summary>
public sealed class SharedReagentDispenser
{
- public const string OutputSlotName = "beakerSlot";
+ public const string OutputSlotName = "ReagentDispenser-beakerSlot";
}
[Serializable, NetSerializable]
[Serializable, NetSerializable]
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;
}
}
/// <summary>
/// A list of the reagents which this dispenser can dispense.
/// </summary>
- public readonly List<ReagentId> Inventory;
+ public readonly List<KeyValuePair<string, KeyValuePair<string, string>>> Inventory;
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;
Inventory = inventory;
CopyFrom(other);
}
-
[DataField("whitelist")]
+ [Access(typeof(ItemSlotsSystem), Other = AccessPermissions.ReadWriteExecute)]
public EntityWhitelist? Whitelist;
[DataField("blacklist")]
/// The actual deconstruction logic is handled by the server-side EmptyOnMachineDeconstructSystem.
/// </remarks>
[DataField("ejectOnDeconstruct")]
+ [Access(typeof(ItemSlotsSystem), Other = AccessPermissions.ReadWriteExecute)]
[NonSerialized]
public bool EjectOnDeconstruct = true;
/// ejected when it is broken or destroyed?
/// </summary>
[DataField("ejectOnBreak")]
+ [Access(typeof(ItemSlotsSystem), Other = AccessPermissions.ReadWriteExecute)]
[NonSerialized]
public bool EjectOnBreak = false;
/// want to insert more than one item that matches the same whitelist.
/// </remarks>
[DataField("swap")]
+ [Access(typeof(ItemSlotsSystem), Other = AccessPermissions.ReadWriteExecute)]
public bool Swap = true;
public string? ID => ContainerSlot?.ID;
- 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
+ - DrinkIceJug
+ - DrinkCoffeeJug
+ - DrinkCreamCartonXL
+ - DrinkTeaJug
+ - DrinkGreenTeaJug
+ - DrinkIcedTeaJug
+ - DrinkColaBottleFull
+ - DrinkSpaceMountainWindBottleFull
+ - DrinkDrGibbJug
+ - DrinkRootBeerJug
+ - DrinkSpaceUpBottleFull
+ - DrinkTonicWaterBottleFull
+ - DrinkSodaWaterBottleFull
+ - DrinkLemonLimeJug
+ - DrinkSugarJug
+ - DrinkJuiceOrangeCartonXL
+ - DrinkJuiceLimeCartonXL
+ - DrinkWaterMelonJuiceJug
- 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: SodaDispenserEmagInventory
- inventory:
- - FourteenLoko
- - Ephedrine
- - Histamine
-
-- type: reagentDispenserInventory
- id: BoozeDispenserEmagInventory
- inventory:
- - AtomicBomb
- - Ethanol
- - Iron
+ - DrinkLemonLimeJug
+ - DrinkSugarJug
+ - DrinkJuiceOrangeCartonXL
+ - DrinkJuiceLimeCartonXL
+ - DrinkTonicWaterBottleFull
+ - DrinkSodaWaterBottleFull
+ - DrinkBeerGrowler
+ - DrinkCoffeeLiqueurBottleFull
+ - DrinkWhiskeyBottleFull
+ - DrinkWineBottleFull
+ - DrinkVodkaBottleFull
+ - DrinkGinBottleFull
+ - DrinkRumBottleFull
+ - DrinkTequilaBottleFull
+ - DrinkVermouthBottleFull
+ - DrinkCognacBottleFull
+ - DrinkAleBottleFullGrowler
+ - DrinkMeadJug
- 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
+ - JugAluminium
+ - JugCarbon
+ - JugChlorine
+ - JugCopper
+ - JugEthanol
+ - JugFluorine
+ - JugSugar
+ - JugHydrogen
+ - JugIodine
+ - JugIron
+ - JugLithium
+ - JugMercury
+ - JugNitrogen
+ - JugOxygen
+ - JugPhosphorus
+ - JugPotassium
+ - JugRadium
+ - JugSilicon
+ - JugSodium
+ - JugSulfur
- type: reagentDispenserInventory
- id: ChemDispenserEmaggedInventory
- inventory: ##Feel free to change this to something more interesting when more chems are added
- - Napalm
- - Toxin
- - Epinephrine
- - Ultravasculine
+ id: EmptyInventory
id: DrinkBottlePlasticBaseFull
abstract: true
components:
+ - type: Tag
+ tags:
+ - DrinkBottle
- type: Openable
sound:
collection: bottleOpenSounds
maxVol: 100
- type: Sprite
state: icon
+ sprite: Objects/Consumable/Drinks/water.rsi # fallback to boring water jug
- type: Item
size: Normal
- type: Damageable
reagents:
- ReagentId: Rum
Quantity: 100
+ - type: Label
+ currentLabel: rum
- type: Sprite
sprite: Objects/Consumable/Drinks/rumbottle.rsi
reagents:
- ReagentId: Tequila
Quantity: 100
+ - type: Label
+ currentLabel: tequila
- type: Sprite
sprite: Objects/Consumable/Drinks/tequillabottle.rsi
reagents:
- ReagentId: Vermouth
Quantity: 100
+ - type: Label
+ currentLabel: vermouth
- type: Sprite
sprite: Objects/Consumable/Drinks/vermouthbottle.rsi
reagents:
- ReagentId: Whiskey
Quantity: 100
+ - type: Label
+ currentLabel: whiskey
- type: Sprite
sprite: Objects/Consumable/Drinks/whiskeybottle.rsi
reagents:
- ReagentId: Wine
Quantity: 100
+ - type: Label
+ currentLabel: wine
- type: Sprite
sprite: Objects/Consumable/Drinks/winebottle.rsi
- type: entity
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
name: Magm-Ale
description: A true dorf's drink of choice.
- type: Sprite
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
parent: DrinkBottlePlasticBaseFull
id: DrinkWaterBottleFull
- type: Drink
- type: Sprite
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
- type: Sprite
state: medical
- type: MachineBoard
- prototype: ChemDispenser
+ prototype: ChemDispenserEmpty
requirements:
Capacitor: 1
materialRequirements:
- type: Sprite
state: service
- type: MachineBoard
- prototype: BoozeDispenser
+ prototype: BoozeDispenserEmpty
materialRequirements:
Steel: 5
tagRequirements:
- type: Sprite
state: service
- type: MachineBoard
- prototype: soda_dispenser
+ prototype: SodaDispenserEmpty
materialRequirements:
Steel: 5
tagRequirements:
price: 60
- type: Label
originalName: jug
+ - type: Tag
+ tags:
+ - ChemDispensable
- type: entity
parent: Jug
-- type: entity
+- type: entity
abstract: true
id: ReagentDispenserBase
parent: ConstructibleMachine
sound:
path: /Audio/Effects/metalbreak.ogg
- type: ReagentDispenser
+ storageWhitelist:
+ tags:
+ - Bottle
+ beakerSlot:
+ whitelistFailPopup: reagent-dispenser-component-cannot-put-entity-message
+ whitelist:
+ components:
+ - FitsInDispenser
- type: ItemSlots
- slots:
- beakerSlot:
- whitelistFailPopup: reagent-dispenser-component-cannot-put-entity-message
- whitelist:
- components:
- - FitsInDispenser
- type: ContainerContainer
containers:
machine_board: !type:Container
machine_parts: !type:Container
- beakerSlot: !type:ContainerSlot
+ ReagentDispenser-beakerSlot: !type:ContainerSlot
- type: StaticPrice
price: 1000
- type: Wires
- type: entity
id: BoozeDispenser
name: booze dispenser
+ suffix: Filled
description: A booze dispenser with a single slot for a container to be filled.
parent: ReagentDispenserBase
components:
drawdepth: SmallObjects
state: booze
- type: ReagentDispenser
+ storageWhitelist:
+ tags:
+ - DrinkBottle
pack: BoozeDispenserInventory
- emagPack: BoozeDispenserEmagInventory
- type: Transform
noRot: false
- type: Machine
- Bartender
- type: StealTarget
stealGroup: BoozeDispenser
+
+- type: entity
+ id: BoozeDispenserEmpty
+ suffix: Empty
+ parent: BoozeDispenser
+ components:
+ - type: ReagentDispenser
+ storageWhitelist:
+ tags:
+ - DrinkBottle
+ pack: EmptyInventory
-- type: entity
+- type: entity
id: ChemDispenser
name: chemical dispenser
+ suffix: Filled
parent: ReagentDispenserBase
- description: An industrial grade chemical dispenser with a sizeable chemical supply.
+ description: An industrial grade chemical dispenser.
components:
- type: Sprite
sprite: Structures/dispensers.rsi
state: industrial-working
snapCardinals: true
- type: ReagentDispenser
+ storageWhitelist:
+ tags:
+ - ChemDispensable
pack: ChemDispenserStandardInventory
- emagPack: ChemDispenserEmaggedInventory
- type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: Destructible
- Chemist
- type: StealTarget
stealGroup: ChemDispenser
+
+- type: entity
+ id: ChemDispenserEmpty
+ name: chemical dispenser
+ suffix: Empty
+ parent: ChemDispenser
+ components:
+ - type: ReagentDispenser
+ pack: EmptyInventory
- type: entity
id: soda_dispenser
name: soda dispenser
+ suffix: Filled
parent: ReagentDispenserBase
description: A beverage dispenser with a selection of soda and several other common beverages. Has a single fill slot for containers.
components:
drawdepth: SmallObjects
state: soda
- type: ReagentDispenser
+ storageWhitelist:
+ tags:
+ - DrinkBottle
pack: SodaDispenserInventory
- emagPack: SodaDispenserEmagInventory
- type: Transform
noRot: false
- type: Machine
- type: GuideHelp
guides:
- Bartender
+
+- type: entity
+ id: SodaDispenserEmpty
+ suffix: Empty
+ parent: soda_dispenser
+ components:
+ - type: ReagentDispenser
+ storageWhitelist:
+ tags:
+ - DrinkBottle
+ pack: EmptyInventory
- type: Tag
id: Chicken
+- type: Tag
+ id: ChemDispensable # container that can go into the chem dispenser
+
- type: Tag
id: Cigarette
- type: Tag
id: DrinkSpaceGlue
+- type: Tag
+ id: DrinkBottle
+
- type: Tag
id: DroneUsable