Delete DrinkComponent, migrate prototypes to EdibleComponent (#40308)

This commit is contained in:
slarticodefast
2025-09-13 00:26:56 +02:00
committed by GitHub
parent 928e6c8079
commit 82e7cb020c
25 changed files with 139 additions and 376 deletions

View File

@@ -1,7 +0,0 @@
using Content.Shared.Nutrition.EntitySystems;
namespace Content.Client.Nutrition.EntitySystems;
public sealed class DrinkSystem : SharedDrinkSystem
{
}

View 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;

View File

@@ -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);
}
}

View File

@@ -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;
}

View 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);
}

View 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}");
}

View File

@@ -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);
}
}

View 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

View File

@@ -320,6 +320,7 @@
solution: drink
delay: 0.5
forceFeedDelay: 1.5
utensil: Spoon
- type: FlavorProfile
flavors:
- water

View File

@@ -206,6 +206,7 @@
delay: 3
transferAmount: 1
solution: puddle
utensil: None
- type: ExaminableSolution
solution: puddle
locVolume: "examinable-solution-on-examine-volume-puddle"

View File

@@ -2122,7 +2122,6 @@
id: DrinkTomatoJuice
suffix: tomato juice
components:
- type: Drink
- type: SolutionContainerManager
solutions:
drink:

View File

@@ -27,6 +27,7 @@
edible: Drink
solution: food
destroyOnEmpty: false
utensil: Spoon
- type: DamageOnLand
damage:
types:

View File

@@ -32,8 +32,11 @@
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
@@ -74,6 +77,14 @@
- 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
@@ -103,7 +114,7 @@
fillBaseName: packet-trans-
- type: entity
parent: BaseFoodCondimentPacket
parent: BaseFoodCondimentPacketDrink
id: FoodCondimentPacketBbq
name: BBQ sauce
description: Hand wipes not included.
@@ -123,7 +134,7 @@
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.
@@ -143,7 +154,7 @@
fillBaseName: packet-trans-
- type: entity
parent: BaseFoodCondimentPacket
parent: BaseFoodCondimentPacketDrink
id: FoodCondimentPacketColdsauce
name: coldsauce
description: Coldsauce. Leaves the tongue numb in its passage.
@@ -163,7 +174,7 @@
fillBaseName: packet-trans-
- type: entity
parent: BaseFoodCondimentPacket
parent: BaseFoodCondimentPacketDrink
id: FoodCondimentPacketHorseradish
name: horseradish sauce
description: A packet of smelly horseradish sauce.
@@ -183,7 +194,7 @@
fillBaseName: packet-solid-
- type: entity
parent: BaseFoodCondimentPacket
parent: BaseFoodCondimentPacketDrink
id: FoodCondimentPacketHotsauce
name: hotsauce
description: You can almost TASTE the stomach ulcers now!
@@ -203,7 +214,7 @@
fillBaseName: packet-trans-
- type: entity
parent: BaseFoodCondimentPacket
parent: BaseFoodCondimentPacketDrink
id: FoodCondimentPacketKetchup
name: ketchup
description: You feel more American already.
@@ -223,7 +234,7 @@
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.
@@ -289,7 +300,7 @@
fillBaseName: packet-solid-
- type: entity
parent: BaseFoodCondimentPacket
parent: BaseFoodCondimentPacketDrink
id: FoodCondimentPacketSoy
name: soy sauce
description: A salty soy-based flavoring.
@@ -335,8 +346,11 @@
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
@@ -526,7 +540,7 @@
# 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
@@ -534,8 +548,11 @@
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:

View File

@@ -25,10 +25,10 @@
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
@@ -224,6 +224,8 @@
reagents:
- ReagentId: OilOlive
Quantity: 20
- type: Edible
edible: Drink # slurping sounds!
- type: entity
parent: ReagentPacketBase

View 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
@@ -114,7 +120,7 @@
name: sugarcane
description: Sickly sweet.
id: Sugarcane
parent: ProduceBase
parent: ProduceBaseRuminant
components:
- type: Sprite
sprite: Objects/Specific/Hydroponics/sugarcane.rsi
@@ -226,7 +232,7 @@
name: nettle
description: Stingy little prick.
id: Nettle
parent: ProduceBase
parent: ProduceBaseRuminant
components:
- type: Sprite
sprite: Objects/Specific/Hydroponics/nettle.rsi
@@ -1964,7 +1970,7 @@
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
@@ -1983,7 +1989,7 @@
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
@@ -2047,7 +2053,7 @@
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

View File

@@ -2,7 +2,7 @@
- type: entity
name: cannabis leaves
parent: ProduceBase
parent: ProduceBaseRuminant
id: LeavesCannabis
description: "Recently legalized in most galaxies."
components:
@@ -167,7 +167,7 @@
- type: entity
name: tea leaves
parent: ProduceBase
parent: ProduceBaseRuminant
id: LeavesTea
description: "Can be dried out to make tea."
components:
@@ -184,7 +184,7 @@
- type: entity
name: dried tea leaves
parent: ProduceBase
parent: ProduceBaseRuminant
id: LeavesTeaDried
description: "Dried tea leaves, ready to be ground."
components:
@@ -200,7 +200,7 @@
- type: entity
name: tobacco leaves
parent: ProduceBase
parent: ProduceBaseRuminant
id: LeavesTobacco
description: "Dry them out to make some smokes."
components:
@@ -208,6 +208,12 @@
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

View 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

View File

@@ -39,8 +39,11 @@
interfaces:
enum.TransferAmountUiKey.Key:
type: TransferAmountBoundUserInterface
- type: Drink
- type: Edible
edible: Drink
solution: beaker
destroyOnEmpty: false
utensil: Spoon
- type: Spillable
solution: beaker
- type: Appearance

View File

@@ -24,7 +24,11 @@
- 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

View File

@@ -29,8 +29,11 @@
fillBaseName: vial-1-
inHandsMaxFillLevels: 4
inHandsFillBaseName: -fill-
- type: Drink
- type: Edible
edible: Drink
solution: beaker
destroyOnEmpty: false
utensil: None
- type: SolutionContainerManager
solutions:
beaker:

View File

@@ -48,8 +48,11 @@
interfaces:
enum.TransferAmountUiKey.Key:
type: TransferAmountBoundUserInterface
- type: Drink
- type: Edible
edible: Drink
solution: beaker
destroyOnEmpty: false
utensil: None
- type: Appearance
- type: SolutionContainerVisuals
maxFillLevels: 6
@@ -145,8 +148,11 @@
interfaces:
enum.TransferAmountUiKey.Key:
type: TransferAmountBoundUserInterface
- type: Drink
- type: Edible
edible: Drink
solution: beaker
destroyOnEmpty: false
utensil: Spoon
- type: Appearance
- type: SolutionContainerVisuals
maxFillLevels: 6

View File

@@ -9,6 +9,7 @@
edible: Drink
solution: bucket
destroyOnEmpty: false
utensil: Spoon
- type: Sprite
sprite: Objects/Tools/bucket.rsi
layers:

View File

@@ -971,8 +971,11 @@
- Honk
- Carpetium
- JuiceThatMakesYouWeh
- type: Drink
- type: Edible
edible: Drink
solution: anomaly
destroyOnEmpty: false
utensil: Spoon
- type: DrainableSolution
solution: anomaly
- type: DrawableSolution

View File

@@ -81,8 +81,11 @@
- 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
@@ -358,8 +361,11 @@
interfaces:
enum.StorageUiKey.Key:
type: StorageBoundUserInterface
- type: Drink
- type: Edible
edible: Drink
solution: bucket
destroyOnEmpty: false
utensil: Spoon
- type: ContainerContainer
containers:
storagebase: !type:Container

View File

@@ -267,8 +267,11 @@
solution: beaker
- type: SolutionTransfer
canChangeTransferAmount: true
- type: Drink
- type: Edible
edible: Drink
solution: beaker
destroyOnEmpty: false
utensil: None
- type: entity
id: XenoArtifactSpeedUp