From 9394a262458df76d9c4156f147bed902e3feac75 Mon Sep 17 00:00:00 2001 From: Kevin Zheng Date: Wed, 17 Jan 2024 13:43:48 -0800 Subject: [PATCH] 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 --- .../UI/ReagentDispenserBoundUserInterface.cs | 12 +- .../Chemistry/UI/ReagentDispenserWindow.xaml | 11 +- .../UI/ReagentDispenserWindow.xaml.cs | 51 +-- .../Tests/Chemistry/DispenserTest.cs | 3 +- .../Components/ReagentDispenserComponent.cs | 49 ++- .../EntitySystems/ReagentDispenserSystem.cs | 108 +++++-- .../ReagentDispenserInventoryPrototype.cs | 5 +- .../Chemistry/SharedReagentDispenser.cs | 12 +- .../Containers/ItemSlot/ItemSlotsComponent.cs | 5 +- .../Catalog/ReagentDispensers/beverage.yml | 90 +++--- .../Catalog/ReagentDispensers/chemical.yml | 47 ++- .../Consumable/Drinks/drinks_bottles.yml | 301 ++++++++++++++++++ .../Circuitboards/Machine/production.yml | 6 +- .../Objects/Specific/chemical-containers.yml | 3 + .../Dispensers/base_structuredispensers.yml | 18 +- .../Entities/Structures/Dispensers/booze.yml | 16 +- .../Entities/Structures/Dispensers/chem.yml | 18 +- .../Entities/Structures/Dispensers/soda.yml | 16 +- Resources/Prototypes/tags.yml | 6 + 19 files changed, 609 insertions(+), 168 deletions(-) diff --git a/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs b/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs index c94759a6c1..8244e3e6ed 100644 --- a/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs +++ b/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs @@ -57,13 +57,23 @@ namespace Content.Client.Chemistry.UI _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); + }; } /// diff --git a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml index e17586db14..d9e480f132 100644 --- a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml +++ b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml @@ -1,8 +1,7 @@ - + MinSize="680 450"> /// State data for the dispenser. - /// Prototype ID of the reagent whose dispense button is currently being mouse hovered, /// or null if no button is being hovered. - public void UpdateContainerInfo(ReagentDispenserBoundUserInterfaceState state, ReagentId? highlightedReagentId = null) + public void UpdateContainerInfo(ReagentDispenserBoundUserInterfaceState state) { ContainerInfo.Children.Clear(); @@ -161,12 +164,6 @@ namespace Content.Client.Chemistry.UI 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, @@ -180,13 +177,27 @@ namespace Content.Client.Chemistry.UI } } - 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 = "⏏"; } } } diff --git a/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs b/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs index 13a0bee28e..98bb7da6b7 100644 --- a/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs +++ b/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs @@ -1,6 +1,7 @@ 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; @@ -24,7 +25,7 @@ public sealed class DispenserTest : InteractionTest 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 diff --git a/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs b/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs index 7229010228..4cf0d2e29e 100644 --- a/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs +++ b/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs @@ -1,3 +1,5 @@ +using Content.Shared.Whitelist; +using Content.Shared.Containers.ItemSlots; using Content.Server.Chemistry.EntitySystems; using Content.Shared.Chemistry; using Content.Shared.Chemistry.Dispenser; @@ -7,20 +9,57 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy namespace Content.Server.Chemistry.Components { /// - /// A machine that dispenses reagents into a solution container. + /// A machine that dispenses reagents into a solution container from containers in its storage slots. /// [RegisterComponent] [Access(typeof(ReagentDispenserSystem))] public sealed partial class ReagentDispenserComponent : Component { - + /// + /// 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. + /// [DataField("pack", customTypeSerializer:typeof(PrototypeIdSerializer))] [ViewVariables(VVAccess.ReadWrite)] public string? PackPrototypeId = default!; - [DataField("emagPack", customTypeSerializer:typeof(PrototypeIdSerializer))] - [ViewVariables(VVAccess.ReadWrite)] - public string? EmagPackPrototypeId = default!; + /// + /// Maximum number of internal storage slots. Dispenser can't store (or dispense) more than + /// this many chemicals (without unloading and reloading). + /// + [DataField("numStorageSlots")] + public int NumSlots = 25; + + /// + /// 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. + /// + [DataField] + public EntityWhitelist? StorageWhitelist; + + /// + /// Slot for container to dispense into. + /// + public static string BeakerSlotId = "ReagentDispenser-beakerSlot"; + + [DataField] + public ItemSlot BeakerSlot = new(); + + /// + /// Prefix for automatically-generated slot name for storage, up to NumSlots. + /// + public static string BaseStorageSlotId = "ReagentDispenser-storageSlot"; + + /// + /// List of storage slots that were created at MapInit. + /// + [DataField] + public List StorageSlotIds = new List(); + + [DataField] + public List StorageSlots = new List(); [DataField("clickSound"), ViewVariables(VVAccess.ReadWrite)] public SoundSpecifier ClickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg"); diff --git a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs index aeb141fe35..a0d6a66bb3 100644 --- a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs @@ -1,14 +1,18 @@ 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; @@ -28,10 +32,13 @@ namespace Content.Server.Chemistry.EntitySystems { [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(); @@ -41,11 +48,12 @@ namespace Content.Server.Chemistry.EntitySystems SubscribeLocalEvent(SubscribeUpdateUiState); SubscribeLocalEvent(SubscribeUpdateUiState); SubscribeLocalEvent(SubscribeUpdateUiState); - SubscribeLocalEvent(OnEmagged); SubscribeLocalEvent(OnSetDispenseAmountMessage); SubscribeLocalEvent(OnDispenseReagentMessage); SubscribeLocalEvent(OnClearContainerSolutionMessage); + + SubscribeLocalEvent(OnMapInit, before: new []{typeof(ItemSlotsSystem)}); } private void SubscribeUpdateUiState(Entity ent, ref T ev) @@ -80,35 +88,38 @@ namespace Content.Server.Chemistry.EntitySystems return null; } - private List GetInventory(Entity ent) + private List>> GetInventory(ReagentDispenserComponent reagentDispenser) { - var reagentDispenser = ent.Comp; - var inventory = new List(); + var inventory = new List>>(); - 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(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(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>(storageSlotId, new KeyValuePair(reagentLabel, storedAmount))); } return inventory; } - private void OnEmagged(Entity reagentDispenser, ref GotEmaggedEvent args) - { - // adding component manually to have correct state - EntityManager.AddComponent(reagentDispenser); - UpdateUiState(reagentDispenser); - args.Handled = true; - } - private void OnSetDispenseAmountMessage(Entity reagentDispenser, ref ReagentDispenserSetDispenseAmountMessage message) { reagentDispenser.Comp.DispenseAmount = message.ReagentDispenserDispenseAmount; @@ -119,18 +130,23 @@ namespace Content.Server.Chemistry.EntitySystems private void OnDispenseReagentMessage(Entity 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); @@ -152,5 +168,41 @@ namespace Content.Server.Chemistry.EntitySystems { _audioSystem.PlayPvs(reagentDispenser.Comp.ClickSound, reagentDispenser, AudioParams.Default.WithVolume(-2f)); } + + /// + /// Automatically generate storage slots for all NumSlots, and fill them with their initial chemicals. + /// The actual spawning of entities happens in ItemSlotsSystem's MapInit. + /// + private void OnMapInit(EntityUid uid, ReagentDispenserComponent component, MapInitEvent args) + { + // Get list of pre-loaded containers + List preLoad = new List(); + 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); + } } } diff --git a/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs b/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs index c362535e4f..5cdc8aed80 100644 --- a/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs +++ b/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs @@ -1,4 +1,4 @@ -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; @@ -14,8 +14,7 @@ namespace Content.Shared.Chemistry.Dispenser [Serializable, NetSerializable, Prototype("reagentDispenserInventory")] public sealed partial class ReagentDispenserInventoryPrototype : IPrototype { - // TODO use ReagentId - [DataField("inventory", customTypeSerializer: typeof(PrototypeIdListSerializer))] + [DataField("inventory", customTypeSerializer: typeof(PrototypeIdListSerializer))] public List Inventory = new(); [ViewVariables, IdDataField] diff --git a/Content.Shared/Chemistry/SharedReagentDispenser.cs b/Content.Shared/Chemistry/SharedReagentDispenser.cs index 1ecb0993f5..be71bf8a77 100644 --- a/Content.Shared/Chemistry/SharedReagentDispenser.cs +++ b/Content.Shared/Chemistry/SharedReagentDispenser.cs @@ -8,7 +8,7 @@ namespace Content.Shared.Chemistry /// public sealed class SharedReagentDispenser { - public const string OutputSlotName = "beakerSlot"; + public const string OutputSlotName = "ReagentDispenser-beakerSlot"; } [Serializable, NetSerializable] @@ -25,11 +25,11 @@ namespace Content.Shared.Chemistry [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; } } @@ -59,11 +59,11 @@ namespace Content.Shared.Chemistry /// /// 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, List inventory, ReagentDispenserDispenseAmount selectedDispenseAmount) + public ReagentDispenserBoundUserInterfaceState(ContainerInfo? outputContainer, List>> inventory, ReagentDispenserDispenseAmount selectedDispenseAmount) { OutputContainer = outputContainer; Inventory = inventory; diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs index 9310617634..f02cdc48db 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs @@ -67,8 +67,8 @@ namespace Content.Shared.Containers.ItemSlots CopyFrom(other); } - [DataField("whitelist")] + [Access(typeof(ItemSlotsSystem), Other = AccessPermissions.ReadWriteExecute)] public EntityWhitelist? Whitelist; [DataField("blacklist")] @@ -179,6 +179,7 @@ namespace Content.Shared.Containers.ItemSlots /// The actual deconstruction logic is handled by the server-side EmptyOnMachineDeconstructSystem. /// [DataField("ejectOnDeconstruct")] + [Access(typeof(ItemSlotsSystem), Other = AccessPermissions.ReadWriteExecute)] [NonSerialized] public bool EjectOnDeconstruct = true; @@ -187,6 +188,7 @@ namespace Content.Shared.Containers.ItemSlots /// ejected when it is broken or destroyed? /// [DataField("ejectOnBreak")] + [Access(typeof(ItemSlotsSystem), Other = AccessPermissions.ReadWriteExecute)] [NonSerialized] 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. /// [DataField("swap")] + [Access(typeof(ItemSlotsSystem), Other = AccessPermissions.ReadWriteExecute)] public bool Swap = true; public string? ID => ContainerSlot?.ID; diff --git a/Resources/Prototypes/Catalog/ReagentDispensers/beverage.yml b/Resources/Prototypes/Catalog/ReagentDispensers/beverage.yml index 1af7f4cef6..62b166ebc1 100644 --- a/Resources/Prototypes/Catalog/ReagentDispensers/beverage.yml +++ b/Resources/Prototypes/Catalog/ReagentDispensers/beverage.yml @@ -1,61 +1,43 @@ - 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 diff --git a/Resources/Prototypes/Catalog/ReagentDispensers/chemical.yml b/Resources/Prototypes/Catalog/ReagentDispensers/chemical.yml index 6d9c729fc9..2b0fdfae6c 100644 --- a/Resources/Prototypes/Catalog/ReagentDispensers/chemical.yml +++ b/Resources/Prototypes/Catalog/ReagentDispensers/chemical.yml @@ -1,31 +1,26 @@ - 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 diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml index 06dc47ce43..7837ae4e76 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml @@ -4,6 +4,9 @@ id: DrinkBottlePlasticBaseFull abstract: true components: + - type: Tag + tags: + - DrinkBottle - type: Openable sound: collection: bottleOpenSounds @@ -13,6 +16,7 @@ maxVol: 100 - type: Sprite state: icon + sprite: Objects/Consumable/Drinks/water.rsi # fallback to boring water jug - type: Item size: Normal - type: Damageable @@ -285,6 +289,8 @@ reagents: - ReagentId: Rum Quantity: 100 + - type: Label + currentLabel: rum - type: Sprite sprite: Objects/Consumable/Drinks/rumbottle.rsi @@ -332,6 +338,8 @@ reagents: - ReagentId: Tequila Quantity: 100 + - type: Label + currentLabel: tequila - type: Sprite sprite: Objects/Consumable/Drinks/tequillabottle.rsi @@ -347,6 +355,8 @@ reagents: - ReagentId: Vermouth Quantity: 100 + - type: Label + currentLabel: vermouth - type: Sprite sprite: Objects/Consumable/Drinks/vermouthbottle.rsi @@ -377,6 +387,8 @@ reagents: - ReagentId: Whiskey Quantity: 100 + - type: Label + currentLabel: whiskey - type: Sprite sprite: Objects/Consumable/Drinks/whiskeybottle.rsi @@ -392,6 +404,8 @@ reagents: - ReagentId: Wine Quantity: 100 + - type: Label + currentLabel: wine - type: Sprite sprite: Objects/Consumable/Drinks/winebottle.rsi @@ -417,6 +431,22 @@ - 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. @@ -433,6 +463,22 @@ - 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 @@ -451,3 +497,258 @@ - 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 diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml index e175bbe728..672d6488e3 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml @@ -553,7 +553,7 @@ - type: Sprite state: medical - type: MachineBoard - prototype: ChemDispenser + prototype: ChemDispenserEmpty requirements: Capacitor: 1 materialRequirements: @@ -1145,7 +1145,7 @@ - type: Sprite state: service - type: MachineBoard - prototype: BoozeDispenser + prototype: BoozeDispenserEmpty materialRequirements: Steel: 5 tagRequirements: @@ -1178,7 +1178,7 @@ - type: Sprite state: service - type: MachineBoard - prototype: soda_dispenser + prototype: SodaDispenserEmpty materialRequirements: Steel: 5 tagRequirements: diff --git a/Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml b/Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml index 139eecfe83..01f5e45c47 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml @@ -47,6 +47,9 @@ price: 60 - type: Label originalName: jug + - type: Tag + tags: + - ChemDispensable - type: entity parent: Jug diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml b/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml index e170ce8255..4ce88c1582 100644 --- a/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml +++ b/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity abstract: true id: ReagentDispenserBase parent: ConstructibleMachine @@ -55,18 +55,20 @@ 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 diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml b/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml index 15e40f79c3..1583bc451d 100644 --- a/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml +++ b/Resources/Prototypes/Entities/Structures/Dispensers/booze.yml @@ -1,6 +1,7 @@ - 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: @@ -10,8 +11,10 @@ drawdepth: SmallObjects state: booze - type: ReagentDispenser + storageWhitelist: + tags: + - DrinkBottle pack: BoozeDispenserInventory - emagPack: BoozeDispenserEmagInventory - type: Transform noRot: false - type: Machine @@ -24,3 +27,14 @@ - Bartender - type: StealTarget stealGroup: BoozeDispenser + +- type: entity + id: BoozeDispenserEmpty + suffix: Empty + parent: BoozeDispenser + components: + - type: ReagentDispenser + storageWhitelist: + tags: + - DrinkBottle + pack: EmptyInventory diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml b/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml index 916b2b748c..2d98b9ff35 100644 --- a/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml +++ b/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml @@ -1,16 +1,19 @@ -- 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 @@ -37,3 +40,12 @@ - Chemist - type: StealTarget stealGroup: ChemDispenser + +- type: entity + id: ChemDispenserEmpty + name: chemical dispenser + suffix: Empty + parent: ChemDispenser + components: + - type: ReagentDispenser + pack: EmptyInventory diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml b/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml index 4322d56947..323480506f 100644 --- a/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml +++ b/Resources/Prototypes/Entities/Structures/Dispensers/soda.yml @@ -1,6 +1,7 @@ - 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: @@ -10,8 +11,10 @@ drawdepth: SmallObjects state: soda - type: ReagentDispenser + storageWhitelist: + tags: + - DrinkBottle pack: SodaDispenserInventory - emagPack: SodaDispenserEmagInventory - type: Transform noRot: false - type: Machine @@ -22,3 +25,14 @@ - type: GuideHelp guides: - Bartender + +- type: entity + id: SodaDispenserEmpty + suffix: Empty + parent: soda_dispenser + components: + - type: ReagentDispenser + storageWhitelist: + tags: + - DrinkBottle + pack: EmptyInventory diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 88186ef84b..8fb58db7c1 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -305,6 +305,9 @@ - type: Tag id: Chicken +- type: Tag + id: ChemDispensable # container that can go into the chem dispenser + - type: Tag id: Cigarette @@ -458,6 +461,9 @@ - type: Tag id: DrinkSpaceGlue +- type: Tag + id: DrinkBottle + - type: Tag id: DroneUsable -- 2.51.2