]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Delete DrinkComponent, migrate prototypes to EdibleComponent (#40308)
authorslarticodefast <161409025+slarticodefast@users.noreply.github.com>
Fri, 12 Sep 2025 22:26:56 +0000 (00:26 +0200)
committerGitHub <noreply@github.com>
Fri, 12 Sep 2025 22:26:56 +0000 (15:26 -0700)
25 files changed:
Content.Client/Nutrition/EntitySystems/DrinkSystem.cs [deleted file]
Content.Server/Nutrition/Components/BadDrinkComponent.cs
Content.Server/Nutrition/EntitySystems/DrinkSystem.cs [deleted file]
Content.Shared/Nutrition/Components/DrinkComponent.cs [deleted file]
Content.Shared/Nutrition/EntitySystems/IngestionSystem.Utensils.cs
Content.Shared/Nutrition/EntitySystems/IngestionSystem.cs
Content.Shared/Nutrition/EntitySystems/SharedDrinkSystem.cs [deleted file]
Resources/Maps/plasma.yml
Resources/Prototypes/Entities/Clothing/Head/misc.yml
Resources/Prototypes/Entities/Effects/puddle.yml
Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_metamorphic.yml
Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/bowl.yml
Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/condiments.yml
Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml
Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml
Resources/Prototypes/Entities/Objects/Specific/Hydroponics/leaves.yml
Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml
Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml
Resources/Prototypes/Entities/Objects/Specific/chemistry-bottles.yml
Resources/Prototypes/Entities/Objects/Specific/chemistry-vials.yml
Resources/Prototypes/Entities/Objects/Specific/chemistry.yml
Resources/Prototypes/Entities/Objects/Tools/bucket.yml
Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml
Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml
Resources/Prototypes/XenoArch/effects.yml

diff --git a/Content.Client/Nutrition/EntitySystems/DrinkSystem.cs b/Content.Client/Nutrition/EntitySystems/DrinkSystem.cs
deleted file mode 100644 (file)
index 16dbecb..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-using Content.Shared.Nutrition.EntitySystems;
-
-namespace Content.Client.Nutrition.EntitySystems;
-
-public sealed class DrinkSystem : SharedDrinkSystem
-{
-}
index 5b9e5a6297142802a364ac397b1c2b1ec0393088..f114a104d029e2ed2a49524babc0f763d03a3197 100644 (file)
@@ -6,7 +6,5 @@ namespace Content.Server.Nutrition.Components;
 /// This component prevents NPC mobs like mice or cows from wanting to drink something that shouldn't be drank from.
 /// Including but not limited to: puddles
 /// </summary>
-[RegisterComponent, Access(typeof(DrinkSystem))]
-public sealed partial class BadDrinkComponent : Component
-{
-}
+[RegisterComponent]
+public sealed partial class BadDrinkComponent : Component;
diff --git a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs
deleted file mode 100644 (file)
index 1677f1d..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
-using Content.Shared.Nutrition.Components;
-using Content.Shared.Nutrition.EntitySystems;
-
-
-namespace Content.Server.Nutrition.EntitySystems;
-
-public sealed class DrinkSystem : SharedDrinkSystem
-{
-    [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
-    [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
-
-    public override void Initialize()
-    {
-        base.Initialize();
-
-        // TODO add InteractNoHandEvent for entities like mice.
-        SubscribeLocalEvent<DrinkComponent, SolutionContainerChangedEvent>(OnSolutionChange);
-        SubscribeLocalEvent<DrinkComponent, ComponentInit>(OnDrinkInit);
-        // run before inventory so for bucket it always tries to drink before equipping (when empty)
-        // run after openable so its always open -> drink
-    }
-
-    private void OnDrinkInit(Entity<DrinkComponent> entity, ref ComponentInit args)
-    {
-        if (TryComp<DrainableSolutionComponent>(entity, out var existingDrainable))
-        {
-            // Beakers have Drink component but they should use the existing Drainable
-            entity.Comp.Solution = existingDrainable.Solution;
-        }
-        else
-        {
-            _solutionContainer.EnsureSolution(entity.Owner, entity.Comp.Solution, out _);
-        }
-
-        UpdateAppearance(entity, entity.Comp);
-
-        if (TryComp(entity, out RefillableSolutionComponent? refillComp))
-            refillComp.Solution = entity.Comp.Solution;
-
-        if (TryComp(entity, out DrainableSolutionComponent? drainComp))
-            drainComp.Solution = entity.Comp.Solution;
-    }
-
-    private void OnSolutionChange(Entity<DrinkComponent> entity, ref SolutionContainerChangedEvent args)
-    {
-        UpdateAppearance(entity, entity.Comp);
-    }
-
-    public void UpdateAppearance(EntityUid uid, DrinkComponent component)
-    {
-        if (!TryComp<AppearanceComponent>(uid, out var appearance) ||
-            !HasComp<SolutionContainerManagerComponent>(uid))
-        {
-            return;
-        }
-
-        var drainAvailable = DrinkVolume(uid, component);
-        _appearance.SetData(uid, FoodVisuals.Visual, drainAvailable.Float(), appearance);
-    }
-}
diff --git a/Content.Shared/Nutrition/Components/DrinkComponent.cs b/Content.Shared/Nutrition/Components/DrinkComponent.cs
deleted file mode 100644 (file)
index a4d1114..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-using Content.Shared.Nutrition.EntitySystems;
-using Content.Shared.FixedPoint;
-using Robust.Shared.Audio;
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Nutrition.Components;
-
-[Obsolete("Migration to Content.Shared.Nutrition.Components.EdibleComponent is required")]
-[NetworkedComponent, AutoGenerateComponentState]
-[RegisterComponent, Access(typeof(SharedDrinkSystem))]
-public sealed partial class DrinkComponent : Component
-{
-    [DataField]
-    public string Solution = "drink";
-
-    [DataField, AutoNetworkedField]
-    public SoundSpecifier UseSound = new SoundPathSpecifier("/Audio/Items/drink.ogg");
-
-    [DataField, AutoNetworkedField]
-    public FixedPoint2 TransferAmount = FixedPoint2.New(5);
-
-    /// <summary>
-    /// How long it takes to drink this yourself.
-    /// </summary>
-    [DataField, AutoNetworkedField]
-    public float Delay = 1;
-
-    /// <summary>
-    /// If true, trying to drink when empty will not handle the event.
-    /// This means other systems such as equipping on use can run.
-    /// Example usecase is the bucket.
-    /// </summary>
-    [DataField]
-    public bool IgnoreEmpty;
-
-    /// <summary>
-    ///     This is how many seconds it takes to force feed someone this drink.
-    /// </summary>
-    [DataField, AutoNetworkedField]
-    public float ForceFeedDelay = 3;
-}
index 670fdc8dfb1dc8914942d761b29af30c2b6e1542..dfdc03cfe6d98106afe5240d48d666e94c4efed4 100644 (file)
@@ -45,7 +45,7 @@ public sealed partial class IngestionSystem
         //Prevents food usage with a wrong utensil
         if ((ev.Types & utensil.Comp.Types) == 0)
         {
-            _popup.PopupClient(Loc.GetString("ingestion-try-use-wrong-utensil", ("verb", GetEdibleVerb(target)),("food", target), ("utensil", utensil.Owner)), user, user);
+            _popup.PopupClient(Loc.GetString("ingestion-try-use-wrong-utensil", ("verb", GetEdibleVerb(target)), ("food", target), ("utensil", utensil.Owner)), user, user);
             return true;
         }
 
@@ -66,14 +66,13 @@ public sealed partial class IngestionSystem
             return;
 
         // TODO: Once we have predicted randomness delete this for something sane...
-        var seed = SharedRandomExtensions.HashCodeCombine(new() {(int)_timing.CurTick.Value, GetNetEntity(entity).Id, GetNetEntity(userUid).Id });
+        var seed = SharedRandomExtensions.HashCodeCombine(new() { (int)_timing.CurTick.Value, GetNetEntity(entity).Id, GetNetEntity(userUid).Id });
         var rand = new System.Random(seed);
 
         if (!rand.Prob(entity.Comp.BreakChance))
             return;
 
         _audio.PlayPredicted(entity.Comp.BreakSound, userUid, userUid, AudioParams.Default.WithVolume(-2f));
-        // Not prediced because no random predicted
         PredictedDel(entity.Owner);
     }
 
index 284bb866f77a5a30d374ded6999eaa7a295b2cae..caecc2797e607a4ace234584e6fe92fb0421cdce 100644 (file)
@@ -137,12 +137,12 @@ public sealed partial class IngestionSystem : EntitySystem
 
     private void OnEdibleInit(Entity<EdibleComponent> entity, ref ComponentInit args)
     {
-        // TODO: When Food and Drink component are kill make sure to nuke both TryComps and just have it update appearance...
-        // Beakers, Soap and other items have drainable, and we should be able to eat that solution...
-        // If I could make drainable properly support sound effects and such I'd just have it use TryIngest itself
-        // Does this exist just to make tests fail? That way you have the proper yaml???
+        // Beakers, Soap and other items have drainable, and we should be able to eat that solution.
+        // This ensures that tests fail when you configured the yaml from and EdibleComponent uses the wrong solution,
         if (TryComp<DrainableSolutionComponent>(entity, out var existingDrainable))
             entity.Comp.Solution = existingDrainable.Solution;
+        else
+            _solutionContainer.EnsureSolution(entity.Owner, entity.Comp.Solution, out _);
 
         UpdateAppearance(entity);
 
@@ -339,7 +339,7 @@ public sealed partial class IngestionSystem : EntitySystem
             if (!forceFed)
                 return;
 
-            _popup.PopupClient(Loc.GetString("ingestion-other-cannot-ingest-any-more", ("target", entity), ("verb", GetEdibleVerb(food))),  args.Target.Value, args.User);
+            _popup.PopupClient(Loc.GetString("ingestion-other-cannot-ingest-any-more", ("target", entity), ("verb", GetEdibleVerb(food))), args.Target.Value, args.User);
             return;
         }
 
@@ -354,7 +354,7 @@ public sealed partial class IngestionSystem : EntitySystem
             if (!forceFed)
                 return;
 
-            _popup.PopupClient(Loc.GetString("ingestion-other-cannot-ingest-any-more", ("target", entity), ("verb", GetEdibleVerb(food))),  args.Target.Value, args.User);
+            _popup.PopupClient(Loc.GetString("ingestion-other-cannot-ingest-any-more", ("target", entity), ("verb", GetEdibleVerb(food))), args.Target.Value, args.User);
             return;
         }
 
@@ -462,6 +462,7 @@ public sealed partial class IngestionSystem : EntitySystem
             _popup.PopupClient(Loc.GetString("edible-force-feed-success-user", ("target", targetName), ("verb", edible.Verb)), args.User, args.User);
 
             // log successful forced feeding
+            // TODO: Use correct verb
             _adminLogger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(entity):user} forced {ToPrettyString(args.User):target} to eat {ToPrettyString(entity):food}");
         }
         else
@@ -472,6 +473,9 @@ public sealed partial class IngestionSystem : EntitySystem
                 args.User);
 
             // log successful voluntary eating
+            // TODO: Use correct verb
+            // the past tense is tricky here
+            // localized admin logs when?
             _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(args.User):target} ate {ToPrettyString(entity):food}");
         }
 
diff --git a/Content.Shared/Nutrition/EntitySystems/SharedDrinkSystem.cs b/Content.Shared/Nutrition/EntitySystems/SharedDrinkSystem.cs
deleted file mode 100644 (file)
index fe804dd..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-using Content.Shared.Administration.Logs;
-using Content.Shared.Chemistry.EntitySystems;
-using Content.Shared.Database;
-using Content.Shared.FixedPoint;
-using Content.Shared.Forensics;
-using Content.Shared.IdentityManagement;
-using Content.Shared.Interaction;
-using Content.Shared.Interaction.Events;
-using Content.Shared.Inventory;
-using Content.Shared.Nutrition.Components;
-using Content.Shared.Popups;
-using Content.Shared.Verbs;
-using Robust.Shared.Audio;
-using Robust.Shared.Audio.Systems;
-using Robust.Shared.Player;
-
-namespace Content.Shared.Nutrition.EntitySystems;
-
-[Obsolete("Migration to Content.Shared.Nutrition.EntitySystems.IngestionSystem is required")]
-public abstract partial class SharedDrinkSystem : EntitySystem
-{
-    [Dependency] private readonly SharedAudioSystem _audio = default!;
-    [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
-    [Dependency] private readonly FlavorProfileSystem _flavorProfile = default!;
-    [Dependency] private readonly IngestionSystem _ingestion = default!;
-    [Dependency] private readonly SharedPopupSystem _popup = default!;
-    [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
-
-    public override void Initialize()
-    {
-        base.Initialize();
-
-        SubscribeLocalEvent<DrinkComponent, UseInHandEvent>(OnUseDrinkInHand, after: new[] { typeof(OpenableSystem), typeof(InventorySystem) });
-        SubscribeLocalEvent<DrinkComponent, AfterInteractEvent>(OnUseDrink);
-
-        SubscribeLocalEvent<DrinkComponent, AttemptShakeEvent>(OnAttemptShake);
-
-        SubscribeLocalEvent<DrinkComponent, GetVerbsEvent<AlternativeVerb>>(AddDrinkVerb);
-
-        SubscribeLocalEvent<DrinkComponent, BeforeIngestedEvent>(OnBeforeDrinkEaten);
-        SubscribeLocalEvent<DrinkComponent, IngestedEvent>(OnDrinkEaten);
-
-        SubscribeLocalEvent<DrinkComponent, EdibleEvent>(OnDrink);
-
-        SubscribeLocalEvent<DrinkComponent, IsDigestibleEvent>(OnIsDigestible);
-
-        SubscribeLocalEvent<DrinkComponent, GetEdibleTypeEvent>(OnGetEdibleType);
-    }
-
-    protected void OnAttemptShake(Entity<DrinkComponent> entity, ref AttemptShakeEvent args)
-    {
-        if (IsEmpty(entity, entity.Comp))
-            args.Cancelled = true;
-    }
-
-    protected FixedPoint2 DrinkVolume(EntityUid uid, DrinkComponent? component = null)
-    {
-        if (!Resolve(uid, ref component))
-            return FixedPoint2.Zero;
-
-        if (!_solutionContainer.TryGetSolution(uid, component.Solution, out _, out var sol))
-            return FixedPoint2.Zero;
-
-        return sol.Volume;
-    }
-
-    protected bool IsEmpty(EntityUid uid, DrinkComponent? component = null)
-    {
-        if (!Resolve(uid, ref component))
-            return true;
-
-        return DrinkVolume(uid, component) <= 0;
-    }
-
-    /// <summary>
-    /// Eat or drink an item
-    /// </summary>
-    private void OnUseDrinkInHand(Entity<DrinkComponent> entity, ref UseInHandEvent ev)
-    {
-        if (ev.Handled)
-            return;
-
-        ev.Handled = _ingestion.TryIngest(ev.User, ev.User, entity);
-    }
-
-    /// <summary>
-    /// Feed someone else
-    /// </summary>
-    private void OnUseDrink(Entity<DrinkComponent> entity, ref AfterInteractEvent args)
-    {
-        if (args.Handled || args.Target == null || !args.CanReach)
-            return;
-
-        args.Handled = _ingestion.TryIngest(args.User, args.Target.Value, entity);
-    }
-
-    private void AddDrinkVerb(Entity<DrinkComponent> entity, ref GetVerbsEvent<AlternativeVerb> args)
-    {
-        var user = args.User;
-
-        if (entity.Owner == user || !args.CanInteract || !args.CanAccess)
-            return;
-
-        if (!_ingestion.TryGetIngestionVerb(user, entity, IngestionSystem.Drink, out var verb))
-            return;
-
-        args.Verbs.Add(verb);
-    }
-
-    private void OnBeforeDrinkEaten(Entity<DrinkComponent> food, ref BeforeIngestedEvent args)
-    {
-        if (args.Cancelled)
-            return;
-
-        // Set it to transfer amount if it exists, otherwise eat the whole volume if possible.
-        args.Transfer = food.Comp.TransferAmount;
-    }
-
-    private void OnDrinkEaten(Entity<DrinkComponent> entity, ref IngestedEvent args)
-    {
-        if (args.Handled)
-            return;
-
-        args.Handled = true;
-
-        _audio.PlayPredicted(entity.Comp.UseSound, args.Target, args.User, AudioParams.Default.WithVolume(-2f).WithVariation(0.25f));
-
-        var flavors = _flavorProfile.GetLocalizedFlavorsMessage(entity.Owner, args.Target, args.Split);
-
-        if (args.ForceFed)
-        {
-            var targetName = Identity.Entity(args.Target, EntityManager);
-            var userName = Identity.Entity(args.User, EntityManager);
-
-            _popup.PopupEntity(Loc.GetString("edible-force-feed-success", ("user", userName), ("verb", _ingestion.GetProtoVerb(IngestionSystem.Drink)), ("flavors", flavors)), entity, entity);
-
-            _popup.PopupClient(Loc.GetString("edible-force-feed-success-user", ("target", targetName), ("verb", _ingestion.GetProtoVerb(IngestionSystem.Drink))), args.User, args.User);
-
-            // log successful forced drinking
-            _adminLogger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(entity.Owner):user} forced {ToPrettyString(args.User):target} to drink {ToPrettyString(entity.Owner):drink}");
-        }
-        else
-        {
-            _popup.PopupPredicted(Loc.GetString("edible-slurp", ("flavors", flavors)),
-                Loc.GetString("edible-slurp-other"),
-                args.User,
-                args.User);
-
-            // log successful voluntary drinking
-            _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(args.User):target} drank {ToPrettyString(entity.Owner):drink}");
-        }
-
-        if (_ingestion.GetUsesRemaining(entity, entity.Comp.Solution, args.Split.Volume) <= 0)
-            return;
-
-        // Leave some of the consumer's DNA on the consumed item...
-        var ev = new TransferDnaEvent
-        {
-            Donor = args.Target,
-            Recipient = entity,
-            CanDnaBeCleaned = false,
-        };
-        RaiseLocalEvent(args.Target, ref ev);
-
-        args.Repeat = !args.ForceFed;
-    }
-
-    private void OnDrink(Entity<DrinkComponent> drink, ref EdibleEvent args)
-    {
-        if (args.Cancelled || args.Solution != null)
-            return;
-
-        if (!_solutionContainer.TryGetSolution(drink.Owner, drink.Comp.Solution, out args.Solution) || IsEmpty(drink))
-        {
-            args.Cancelled = true;
-
-            _popup.PopupClient(Loc.GetString("ingestion-try-use-is-empty", ("entity", drink)), drink, args.User);
-            return;
-        }
-
-        args.Time += TimeSpan.FromSeconds(drink.Comp.Delay);
-    }
-
-    private void OnIsDigestible(Entity<DrinkComponent> ent, ref IsDigestibleEvent args)
-    {
-        // Anyone can drink from puddles on the floor!
-        args.UniversalDigestion();
-    }
-
-    private void OnGetEdibleType(Entity<DrinkComponent> ent, ref GetEdibleTypeEvent args)
-    {
-        if (args.Type != null)
-            return;
-
-        args.SetPrototype(IngestionSystem.Drink);
-    }
-}
index b5d76a75e0be6b798d1ce0c54f086443980ed9bc..b2ae035e0409d5848f5c26f4ff8b23432aa152cc 100644 (file)
@@ -83601,37 +83601,41 @@ entities:
     - type: Transform
       pos: -45.5,-61.5
       parent: 2
-    - type: Drink
-      useSound: !type:SoundPathSpecifier
-        path: /Audio/Items/drink.ogg
+    - type: Edible
+      edible: Drink
       solution: pool
+      destroyOnEmpty: false
+      utensil: Spoon
   - uid: 16868
     components:
     - type: Transform
       pos: -44.5,-61.5
       parent: 2
-    - type: Drink
-      useSound: !type:SoundPathSpecifier
-        path: /Audio/Items/drink.ogg
+    - type: Edible
+      edible: Drink
       solution: pool
+      destroyOnEmpty: false
+      utensil: Spoon
   - uid: 16872
     components:
     - type: Transform
       pos: -46.5,-61.5
       parent: 2
-    - type: Drink
-      useSound: !type:SoundPathSpecifier
-        path: /Audio/Items/drink.ogg
+    - type: Edible
+      edible: Drink
       solution: pool
+      destroyOnEmpty: false
+      utensil: Spoon
   - uid: 16907
     components:
     - type: Transform
       pos: -44.5,-60.5
       parent: 2
-    - type: Drink
-      useSound: !type:SoundPathSpecifier
-        path: /Audio/Items/drink.ogg
+    - type: Edible
+      edible: Drink
       solution: pool
+      destroyOnEmpty: false
+      utensil: Spoon
   - uid: 17621
     components:
     - type: Transform
index da0f17f32486ccb51c994572c591d013c7a846b5..511f1e198034ec0b4a90beaebdb95bc249edcb54 100644 (file)
     solution: drink
     delay: 0.5
     forceFeedDelay: 1.5
+    utensil: Spoon
   - type: FlavorProfile
     flavors:
       - water
index cc3df59c55c48b56e973692252245e380a064ea7..4e758e4e1c6d245e85ceb702b24119f0236b6561 100644 (file)
     delay: 3
     transferAmount: 1
     solution: puddle
+    utensil: None
   - type: ExaminableSolution
     solution: puddle
     locVolume: "examinable-solution-on-examine-volume-puddle"
index 22054741e3b4a1e275ee27fab0757307e9578015..3b7569022b9e034d30ce303a34dcb700ead25736 100644 (file)
   id: DrinkTomatoJuice
   suffix: tomato juice
   components:
-  - type: Drink
   - type: SolutionContainerManager
     solutions:
       drink:
index 1ac5dce0e8aac25686b66a85dc0addbf4c4252ee..ce82ba1e1b05d3fea18870e2c378b3cab811c639 100644 (file)
@@ -27,6 +27,7 @@
     edible: Drink
     solution: food
     destroyOnEmpty: false
+    utensil: Spoon
   - type: DamageOnLand
     damage:
       types:
index 7c7df07f7891ea5ca102ecb7c53ce543c12b783a..5cfe12d74a66feccdcf5c6ed086b23ca69bfc3bc 100644 (file)
   components:
   - type: Item
     size: Tiny
-  - type: Drink
+  - type: Edible
+    edible: Food # usually contains powders like flour or condiments like ketchup
     solution: food
+    destroyOnEmpty: false
+    utensil: Spoon
   - type: Openable
     sound:
       collection: packetOpenSounds
   - type: ExaminableSolution
     exactVolume: true
 
+- type: entity
+  parent: BaseFoodCondimentPacket
+  id: BaseFoodCondimentPacketDrink
+  abstract: true
+  components:
+  - type: Edible
+    edible: Drink # slurping sounds!
+
 - type: entity
   parent: BaseFoodCondimentPacket
   id: FoodCondimentPacketAstrotame
     fillBaseName: packet-trans-
 
 - type: entity
-  parent: BaseFoodCondimentPacket
+  parent: BaseFoodCondimentPacketDrink
   id: FoodCondimentPacketBbq
   name: BBQ sauce
   description: Hand wipes not included.
     fillBaseName: packet-trans-
 
 - type: entity
-  parent: BaseFoodCondimentPacket
+  parent: BaseFoodCondimentPacketDrink
   id: FoodCondimentPacketCornoil
   name: corn oil
   description: Corn oil. A delicious oil used in cooking. Made from corn.
     fillBaseName: packet-trans-
 
 - type: entity
-  parent: BaseFoodCondimentPacket
+  parent: BaseFoodCondimentPacketDrink
   id: FoodCondimentPacketColdsauce
   name: coldsauce
   description: Coldsauce. Leaves the tongue numb in its passage.
     fillBaseName: packet-trans-
 
 - type: entity
-  parent: BaseFoodCondimentPacket
+  parent: BaseFoodCondimentPacketDrink
   id: FoodCondimentPacketHorseradish
   name: horseradish sauce
   description: A packet of smelly horseradish sauce.
     fillBaseName: packet-solid-
 
 - type: entity
-  parent: BaseFoodCondimentPacket
+  parent: BaseFoodCondimentPacketDrink
   id: FoodCondimentPacketHotsauce
   name: hotsauce
   description: You can almost TASTE the stomach ulcers now!
     fillBaseName: packet-trans-
 
 - type: entity
-  parent: BaseFoodCondimentPacket
+  parent: BaseFoodCondimentPacketDrink
   id: FoodCondimentPacketKetchup
   name: ketchup
   description: You feel more American already.
     fillBaseName: packet-solid-
 
 - type: entity
-  parent: BaseFoodCondimentPacket
+  parent: BaseFoodCondimentPacketDrink
   id: FoodCondimentPacketMustard
   name: mustard
   description: A condiment made from the ground-up seeds of the Mustard plant.
     fillBaseName: packet-solid-
 
 - type: entity
-  parent: BaseFoodCondimentPacket
+  parent: BaseFoodCondimentPacketDrink
   id: FoodCondimentPacketSoy
   name: soy sauce
   description: A salty soy-based flavoring.
   name: condiment bottle
   description: A thin glass bottle used to store condiments.
   components:
-  - type: Drink
+  - type: Edible
+    edible: Drink
     solution: food
+    destroyOnEmpty: false
+    utensil: None
   - type: Openable
     sound:
       collection: pop
 # Shakers
 
 - type: entity
-  parent: BaseFoodCondiment
+  parent: BaseFoodCondiment # TODO: This should not inherit TrashOnSolutionEmpty, SpaceGarbage and the price of 0
   id: BaseFoodShaker
   abstract: true
   name: empty shaker
   components:
   - type: Item
     size: Tiny
-  - type: Drink
+  - type: Edible
+    edible: Drink
     solution: food
+    destroyOnEmpty: false
+    utensil: None # don't conflict with stirring
   - type: SolutionContainerManager
     solutions:
       food:
index 76ea3073e5faae7f45746dff3aaa19797cb6d0c1..fda43d65fe2d8f5b1a9ce5daf1f46c3da3c55165 100644 (file)
     solution: food
   - type: DrainableSolution
     solution: food
-  - type: Drink
+  - type: Edible
+    edible: Food # usually contains powders like flour or condiments like ketchup
     solution: food
-    useSound:
-      collection: eating
+    utensil: Spoon
   - type: Damageable
     damageContainer: Inorganic
   - type: Spillable
         reagents:
         - ReagentId: OilOlive
           Quantity: 20
+  - type: Edible
+    edible: Drink # slurping sounds!
 
 - type: entity
   parent: ReagentPacketBase
index 551ea802ca7316bc97f72a81dd44d7323ed1d882..563a7810d01eeb57cb30badecffa6da2c47306a1 100644 (file)
@@ -9,14 +9,20 @@
   - type: SolutionContainerManager
   - type: Sprite
     state: produce
-  # let cows eat raw produce like wheat and oats
-  - type: Edible
-    requiresSpecialDigestion: true
   - type: Produce
   - type: PotencyVisuals
   - type: Appearance
   - type: Extractable
     grindableSolutionName: food
+
+- type: entity
+  parent: ProduceBase
+  id: ProduceBaseRuminant
+  abstract: true
+  components:
+  # let cows eat raw produce like wheat and oats
+  - type: Edible
+    requiresSpecialDigestion: true
   - type: Tag
     tags:
     - Ruminant
@@ -43,7 +49,7 @@
   name: wheat bushel
   description: Sigh... wheat... a-grain?
   id: WheatBushel
-  parent: ProduceBase
+  parent: ProduceBaseRuminant
   components:
   - type: Sprite
     sprite: Objects/Specific/Hydroponics/wheat.rsi
@@ -66,7 +72,7 @@
   name: meatwheat bushel
   description: Some blood-drenched wheat stalks. You can crush them into what passes for meat if you squint hard enough.
   id: MeatwheatBushel
-  parent: ProduceBase
+  parent: ProduceBaseRuminant
   components:
   - type: Sprite
     sprite: Objects/Specific/Hydroponics/meatwheat.rsi
@@ -90,7 +96,7 @@
   name: oat bushel
   description: Eat oats, do squats.
   id: OatBushel
-  parent: ProduceBase
+  parent: ProduceBaseRuminant
   components:
   - type: Sprite
     sprite: Objects/Specific/Hydroponics/oat.rsi
   name: sugarcane
   description: Sickly sweet.
   id: Sugarcane
-  parent: ProduceBase
+  parent: ProduceBaseRuminant
   components:
   - type: Sprite
     sprite: Objects/Specific/Hydroponics/sugarcane.rsi
   name: nettle
   description: Stingy little prick.
   id: Nettle
-  parent: ProduceBase
+  parent: ProduceBaseRuminant
   components:
   - type: Sprite
     sprite: Objects/Specific/Hydroponics/nettle.rsi
   name: rice bushel
   description: Can be ground into rice, perfect for pudding or sake.
   id: RiceBushel
-  parent: ProduceBase
+  parent: ProduceBaseRuminant
   components:
   - type: Sprite
     sprite: Objects/Specific/Hydroponics/rice.rsi
   name: soybeans
   description: For those who can't stand seeing good old meat.
   id: FoodSoybeans
-  parent: ProduceBase
+  parent: ProduceBaseRuminant
   components:
   - type: Sprite
     sprite: Objects/Specific/Hydroponics/soybeans.rsi
   name: koibean
   description: These beans seem a little bit fishy.
   id: FoodKoibean
-  parent: ProduceBase
+  parent: ProduceBaseRuminant
   components:
   - type: Sprite
     sprite: Objects/Specific/Hydroponics/koibean.rsi
index 3ef9e99cd2e1ecc06c3b2f4e0affb01f8b9e9e2a..de3f8e1003e83bcf9e849372a7e1c198cf373329 100644 (file)
@@ -2,7 +2,7 @@
 
 - type: entity
   name: cannabis leaves
-  parent: ProduceBase
+  parent: ProduceBaseRuminant
   id: LeavesCannabis
   description: "Recently legalized in most galaxies."
   components:
 
 - type: entity
   name: tea leaves
-  parent: ProduceBase
+  parent: ProduceBaseRuminant
   id: LeavesTea
   description: "Can be dried out to make tea."
   components:
 
 - type: entity
   name: dried tea leaves
-  parent: ProduceBase
+  parent: ProduceBaseRuminant
   id: LeavesTeaDried
   description: "Dried tea leaves, ready to be ground."
   components:
 
 - type: entity
   name: tobacco leaves
-  parent: ProduceBase
+  parent: ProduceBaseRuminant
   id: LeavesTobacco
   description: "Dry them out to make some smokes."
   components:
     sprite: Objects/Specific/Hydroponics/tobacco.rsi
   - type: Produce
     seedId: tobacco
+  - type: SolutionContainerManager
+    solutions:
+      food:
+        reagents:
+        - ReagentId: Nicotine
+          Quantity: 2
 
 - type: entity
   name: dried tobacco leaves
index 9ed597275428851662f06f12901c9d9601f66595..f335244806569a141bb5001ba5d0521602a58cd4 100644 (file)
@@ -5,12 +5,16 @@
   suffix: Empty
   description: A spray bottle with an unscrewable top.
   components:
-  - type: Drink
+  - type: Edible
+    edible: Drink
     solution: spray
-    ignoreEmpty: true
+    destroyOnEmpty: false
+    utensil: None
+    transferAmount: 10
     useSound:
       path: /Audio/Effects/spray3.ogg
-    transferAmount: 10
+      params:
+        variation: 0.2
   - type: Tag
     tags:
     - Spray
index 84990b10be5d93f5ec235c0b8c32a902e7e036f5..bf3501d6047e71629e1ebb770a0a48dafe3f7c32 100644 (file)
       interfaces:
         enum.TransferAmountUiKey.Key:
           type: TransferAmountBoundUserInterface
-    - type: Drink
+    - type: Edible
+      edible: Drink
       solution: beaker
+      destroyOnEmpty: false
+      utensil: Spoon
     - type: Spillable
       solution: beaker
     - type: Appearance
index c8c275ac238b287bd33027488fda88c3e62aed09..8f573077602d4acdff2a48a5a60fc16d3bc301a0 100644 (file)
   - type: SolutionContainerVisuals
     maxFillLevels: 6
     fillBaseName: bottle-1-
-  - type: Drink
+  - type: Edible
+    edible: Drink
+    solution: drink
+    destroyOnEmpty: false
+    utensil: None
   - type: SolutionContainerManager
     solutions:
       drink: # This solution name and target volume is hard-coded in ChemMasterComponent
index 21b3742d0244c3aa2a68af7bd42efe936b5fd0b3..2422d7d7120050795f7c7dad51a594e042539aed 100644 (file)
     fillBaseName: vial-1-
     inHandsMaxFillLevels: 4
     inHandsFillBaseName: -fill-
-  - type: Drink
+  - type: Edible
+    edible: Drink
     solution: beaker
+    destroyOnEmpty: false
+    utensil: None
   - type: SolutionContainerManager
     solutions:
       beaker:
index cd4cbb3834442405acdf4022418641b9aa90c499..6a0a23eb65cd3d35bb80150897c15b2bdc71b944 100644 (file)
     interfaces:
       enum.TransferAmountUiKey.Key:
         type: TransferAmountBoundUserInterface
-  - type: Drink
+  - type: Edible
+    edible: Drink
     solution: beaker
+    destroyOnEmpty: false
+    utensil: None
   - type: Appearance
   - type: SolutionContainerVisuals
     maxFillLevels: 6
     interfaces:
       enum.TransferAmountUiKey.Key:
         type: TransferAmountBoundUserInterface
-  - type: Drink
+  - type: Edible
+    edible: Drink
     solution: beaker
+    destroyOnEmpty: false
+    utensil: Spoon
   - type: Appearance
   - type: SolutionContainerVisuals
     maxFillLevels: 6
index d77e6cd2b80d99506f0bf78a3b535c025a52be2f..c62b178366b0b36c0b00fb1c9a3f108a0744cc0d 100644 (file)
@@ -9,6 +9,7 @@
     edible: Drink
     solution: bucket
     destroyOnEmpty: false
+    utensil: Spoon
   - type: Sprite
     sprite: Objects/Tools/bucket.rsi
     layers:
index cb25f9199b82dc45ec7a2a60cd94887fcbc247cb..c1976ba7a8b89a3e5af6180aba6d182470daf5c6 100644 (file)
     - Honk
     - Carpetium
     - JuiceThatMakesYouWeh
-  - type: Drink
+  - type: Edible
+    edible: Drink
     solution: anomaly
+    destroyOnEmpty: false
+    utensil: Spoon
   - type: DrainableSolution
     solution: anomaly
   - type: DrawableSolution
index 3be6fb7d6ff52ca5de309909ab4ea8b76084e922..a24a03da3e791f9910445a4a1303e644b8ca51fd 100644 (file)
   - type: ReactiveContainer
     solution: bucket
     container: item_slot
-  - type: Drink
+  - type: Edible
+    edible: Drink
     solution: bucket
+    destroyOnEmpty: false
+    utensil: Spoon
   - type: Appearance
   - type: SolutionContainerVisuals
     maxFillLevels: 3
       interfaces:
         enum.StorageUiKey.Key:
           type: StorageBoundUserInterface
-    - type: Drink
+    - type: Edible
+      edible: Drink
       solution: bucket
+      destroyOnEmpty: false
+      utensil: Spoon
     - type: ContainerContainer
       containers:
         storagebase: !type:Container
index 027c79550ccbc7924e278f14276004a83e1ee278..1506d94aa04bd7a6417116fcd7ace9a9915a8f1a 100644 (file)
       solution: beaker
     - type: SolutionTransfer
       canChangeTransferAmount: true
-    - type: Drink
+    - type: Edible
+      edible: Drink
       solution: beaker
+      destroyOnEmpty: false
+      utensil: None
 
 - type: entity
   id: XenoArtifactSpeedUp