]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Move solution examine subscription from DrinkComponent to ExaminableSolutionComponent...
authorāda <ss.adasts@gmail.com>
Wed, 6 Aug 2025 16:11:19 +0000 (11:11 -0500)
committerGitHub <noreply@github.com>
Wed, 6 Aug 2025 16:11:19 +0000 (09:11 -0700)
* initial it works

* clean it up

* yml

* datafield the LocIds

* move from the other branch

* no max vol on puddles and anoms

* closed

* Changes inspired by #39008

* small bug and more color range

* puddle changes and more examinable solutions

* lint

* small change

* requested changes

* un-delete

* tiny comment

* 1 less loc id in this world

* request and last second change

---------

Co-authored-by: iaada <iaada@users.noreply.github.com>
25 files changed:
Content.Shared/Chemistry/Components/SolutionManager/ExaminableSolutionComponent.cs
Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.Capabilities.cs
Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs
Content.Shared/Nutrition/Components/DrinkComponent.cs
Content.Shared/Nutrition/EntitySystems/SharedDrinkSystem.cs
Resources/Locale/en-US/chemistry/solution/components/shared-solution-container-component.ftl
Resources/Locale/en-US/nutrition/components/drink-component.ftl
Resources/Prototypes/Entities/Clothing/Back/specific.yml
Resources/Prototypes/Entities/Effects/puddle.yml
Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks-cartons.yml
Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml
Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml
Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml
Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cups.yml
Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_flasks.yml
Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_special.yml
Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/condiments.yml
Resources/Prototypes/Entities/Objects/Misc/arabianlamp.yml
Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml
Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.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/Structures/Specific/Anomaly/anomalies.yml

index 1abe81180c846c4c363eab99e3ccb580f0966c0c..913622e19d4fd910c28b89740e6ca9f622e76d6d 100644 (file)
@@ -1,14 +1,70 @@
-namespace Content.Shared.Chemistry.Components.SolutionManager;
+using Content.Shared.Nutrition.Components;
+using Robust.Shared.Serialization;
 
+namespace Content.Shared.Chemistry.Components.SolutionManager;
+
+/// <summary>
+///     Component for examining a solution with shift click or through <see cref="SolutionScanEvent"/>.
+/// </summary>
 [RegisterComponent]
 public sealed partial class ExaminableSolutionComponent : Component
 {
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    /// <summary>
+    ///     The solution being examined.
+    /// </summary>
+    [DataField]
     public string Solution = "default";
 
     /// <summary>
-    /// If false then the hidden solution is always visible.
+    ///     If true, the solution must be held to be examined.
     /// </summary>
     [DataField]
     public bool HeldOnly;
+
+    /// <summary>
+    ///     If false, the examine text will give an approximation of the remaining solution.
+    ///     If true, the exact unit count will be shown.
+    /// </summary>
+    [DataField]
+    public bool ExactVolume;
+
+    /// <summary>
+    ///     If false, the solution can't be examined when this entity is closed by <see cref="OpenableComponent"/>.
+    /// </summary>
+    [DataField]
+    public bool ExaminableWhileClosed = true;
+
+    /// <summary>
+    ///     Examine text for the amount of solution.
+    /// </summary>
+    /// <seealso cref="ExaminedVolumeDisplay"/>
+    [DataField]
+    public LocId LocVolume = "examinable-solution-on-examine-volume";
+
+    /// <summary>
+    ///     Examine text for the physical description of the primary reagent.
+    /// </summary>
+    [DataField]
+    public LocId LocPhysicalQuality = "shared-solution-container-component-on-examine-main-text";
+
+    /// <summary>
+    ///     Examine text for reagents that are obvious like water.
+    /// </summary>
+    [DataField]
+    public LocId LocRecognizableReagents = "examinable-solution-has-recognizable-chemicals";
+}
+
+/// <summary>
+///     Used to choose how to display a volume.
+/// </summary>
+[Serializable, NetSerializable]
+public enum ExaminedVolumeDisplay
+{
+    Exact,
+    Full,
+    MostlyFull,
+    HalfFull,
+    HalfEmpty,
+    MostlyEmpty,
+    Empty,
 }
index ffdb6088759da1084f5ef54655848ea3f86cbbe6..cc511f8db92bb0c53a09e3eea95ecd4fb1530b0d 100644 (file)
@@ -1,9 +1,7 @@
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Kitchen.Components;
 using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.Reaction;
 using Content.Shared.FixedPoint;
-using Robust.Shared.Utility;
 using System.Diagnostics.CodeAnalysis;
 using System.Text;
 
@@ -139,12 +137,13 @@ public abstract partial class SharedSolutionContainerSystem
 
     #endregion Solution Modifiers
 
+    /// <returns>A value between 0 and 100 inclusive.</returns>
     public float PercentFull(EntityUid uid)
     {
-        if (!TryGetDrainableSolution(uid, out _, out var solution) || solution.MaxVolume.Equals(FixedPoint2.Zero))
+        if (!TryGetDrainableSolution(uid, out _, out var solution))
             return 0;
 
-        return solution.FillFraction * 100;
+        return PercentFull(solution);
     }
 
     #region Static Methods
@@ -175,5 +174,14 @@ public abstract partial class SharedSolutionContainerSystem
         return sb.ToString();
     }
 
+    /// <returns>A value between 0 and 100 inclusive.</returns>
+    public static float PercentFull(Solution sol)
+    {
+        if (sol.MaxVolume.Equals(FixedPoint2.Zero))
+            return 0;
+
+        return sol.FillFraction * 100;
+    }
+
     #endregion Static Methods
 }
index 5cc12a9b695e00d58302f86d81e77d31b78ed8e8..54210cf195f4585964b129cd7c72dde45d99cb10 100644 (file)
@@ -12,6 +12,9 @@ using Content.Shared.Examine;
 using Content.Shared.FixedPoint;
 using Content.Shared.Hands.Components;
 using Content.Shared.Hands.EntitySystems;
+using Content.Shared.Localizations;
+using Content.Shared.Nutrition.Components;
+using Content.Shared.Nutrition.EntitySystems;
 using Content.Shared.Verbs;
 using JetBrains.Annotations;
 using Robust.Shared.Containers;
@@ -61,14 +64,15 @@ public partial record struct SolutionAccessAttemptEvent(string SolutionName)
 [UsedImplicitly]
 public abstract partial class SharedSolutionContainerSystem : EntitySystem
 {
-    [Robust.Shared.IoC.Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
-    [Robust.Shared.IoC.Dependency] protected readonly ChemicalReactionSystem ChemicalReactionSystem = default!;
-    [Robust.Shared.IoC.Dependency] protected readonly ExamineSystemShared ExamineSystem = default!;
-    [Robust.Shared.IoC.Dependency] protected readonly SharedAppearanceSystem AppearanceSystem = default!;
-    [Robust.Shared.IoC.Dependency] protected readonly SharedHandsSystem Hands = default!;
-    [Robust.Shared.IoC.Dependency] protected readonly SharedContainerSystem ContainerSystem = default!;
-    [Robust.Shared.IoC.Dependency] protected readonly MetaDataSystem MetaDataSys = default!;
-    [Robust.Shared.IoC.Dependency] protected readonly INetManager NetManager = default!;
+    [Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
+    [Dependency] protected readonly ChemicalReactionSystem ChemicalReactionSystem = default!;
+    [Dependency] protected readonly ExamineSystemShared ExamineSystem = default!;
+    [Dependency] protected readonly OpenableSystem Openable = default!;
+    [Dependency] protected readonly SharedAppearanceSystem AppearanceSystem = default!;
+    [Dependency] protected readonly SharedHandsSystem Hands = default!;
+    [Dependency] protected readonly SharedContainerSystem ContainerSystem = default!;
+    [Dependency] protected readonly MetaDataSystem MetaDataSys = default!;
+    [Dependency] protected readonly INetManager NetManager = default!;
 
     public override void Initialize()
     {
@@ -791,52 +795,55 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
         }
     }
 
+    /// <summary>
+    ///     Shift click examine.
+    /// </summary>
     private void OnExamineSolution(Entity<ExaminableSolutionComponent> entity, ref ExaminedEvent args)
     {
-        if (!TryGetSolution(entity.Owner, entity.Comp.Solution, out _, out var solution))
-        {
+        if (!args.IsInDetailsRange ||
+            !CanSeeHiddenSolution(entity, args.Examiner) ||
+            !TryGetSolution(entity.Owner, entity.Comp.Solution, out _, out var solution))
             return;
-        }
 
-        if (!CanSeeHiddenSolution(entity, args.Examiner))
-            return;
+        using (args.PushGroup(nameof(ExaminableSolutionComponent)))
+        {
 
-        var primaryReagent = solution.GetPrimaryReagentId();
+            var primaryReagent = solution.GetPrimaryReagentId();
 
-        if (string.IsNullOrEmpty(primaryReagent?.Prototype))
-        {
-            args.PushText(Loc.GetString("shared-solution-container-component-on-examine-empty-container"));
-            return;
-        }
+            // If there's no primary reagent, assume the solution is empty and exit early
+            if (string.IsNullOrEmpty(primaryReagent?.Prototype) ||
+                !PrototypeManager.Resolve<ReagentPrototype>(primaryReagent.Value.Prototype, out var primary))
+            {
+                args.PushMarkup(Loc.GetString(entity.Comp.LocVolume, ("fillLevel", ExaminedVolumeDisplay.Empty)));
+                return;
+            }
 
-        if (!PrototypeManager.TryIndex(primaryReagent.Value.Prototype, out ReagentPrototype? primary))
-        {
-            Log.Error($"{nameof(Solution)} could not find the prototype associated with {primaryReagent}.");
-            return;
-        }
+            // Push amount of reagent
 
-        var colorHex = solution.GetColor(PrototypeManager)
-            .ToHexNoAlpha(); //TODO: If the chem has a dark color, the examine text becomes black on a black background, which is unreadable.
-        var messageString = "shared-solution-container-component-on-examine-main-text";
+            args.PushMarkup(Loc.GetString(entity.Comp.LocVolume,
+                                ("fillLevel", ExaminedVolume(entity, solution, args.Examiner)),
+                                ("current", solution.Volume),
+                                ("max", solution.MaxVolume)));
 
-        using (args.PushGroup(nameof(ExaminableSolutionComponent)))
-        {
-            args.PushMarkup(Loc.GetString(messageString,
-                ("color", colorHex),
-                ("wordedAmount", Loc.GetString(solution.Contents.Count == 1
-                    ? "shared-solution-container-component-on-examine-worded-amount-one-reagent"
-                    : "shared-solution-container-component-on-examine-worded-amount-multiple-reagents")),
-                ("desc", primary.LocalizedPhysicalDescription)));
+            // Push the physical description of the primary reagent
 
-            var reagentPrototypes = solution.GetReagentPrototypes(PrototypeManager);
+            var colorHex = solution.GetColor(PrototypeManager)
+                .ToHexNoAlpha(); //TODO: If the chem has a dark color, the examine text becomes black on a black background, which is unreadable.
+
+            args.PushMarkup(Loc.GetString(entity.Comp.LocPhysicalQuality,
+                                        ("color", colorHex),
+                                        ("desc", primary.LocalizedPhysicalDescription),
+                                        ("chemCount", solution.Contents.Count) ));
+
+            // Push the recognizable reagents
 
             // Sort the reagents by amount, descending then alphabetically
-            var sortedReagentPrototypes = reagentPrototypes
+            var sortedReagentPrototypes = solution.GetReagentPrototypes(PrototypeManager)
                 .OrderByDescending(pair => pair.Value.Value)
                 .ThenBy(pair => pair.Key.LocalizedName);
 
-            // Add descriptions of immediately recognizable reagents, like water or beer
-            var recognized = new List<ReagentPrototype>();
+            // Collect recognizable reagents, like water or beer
+            var recognized = new List<string>();
             foreach (var keyValuePair in sortedReagentPrototypes)
             {
                 var proto = keyValuePair.Key;
@@ -845,41 +852,58 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
                     continue;
                 }
 
-                recognized.Add(proto);
+                recognized.Add(Loc.GetString("examinable-solution-recognized",
+                                            ("color", proto.SubstanceColor.ToHexNoAlpha()),
+                                            ("chemical", proto.LocalizedName)));
             }
 
-            // Skip if there's nothing recognizable
             if (recognized.Count == 0)
                 return;
 
-            var msg = new StringBuilder();
-            foreach (var reagent in recognized)
-            {
-                string part;
-                if (reagent == recognized[0])
-                {
-                    part = "examinable-solution-recognized-first";
-                }
-                else if (reagent == recognized[^1])
-                {
-                    // this loc specifically  requires space to be appended, fluent doesnt support whitespace
-                    msg.Append(' ');
-                    part = "examinable-solution-recognized-last";
-                }
-                else
-                {
-                    part = "examinable-solution-recognized-next";
-                }
+            var msg = ContentLocalizationManager.FormatList(recognized);
 
-                msg.Append(Loc.GetString(part, ("color", reagent.SubstanceColor.ToHexNoAlpha()),
-                    ("chemical", reagent.LocalizedName)));
-            }
-
-            args.PushMarkup(Loc.GetString("examinable-solution-has-recognizable-chemicals",
-                ("recognizedString", msg.ToString())));
+            // Finally push the full message
+            args.PushMarkup(Loc.GetString(entity.Comp.LocRecognizableReagents,
+                ("recognizedString", msg)));
         }
     }
 
+    /// <returns>An enum for how to display the solution.</returns>
+    public ExaminedVolumeDisplay ExaminedVolume(Entity<ExaminableSolutionComponent> ent, Solution sol, EntityUid? examiner = null)
+    {
+        // Exact measurement
+        if (ent.Comp.ExactVolume)
+            return ExaminedVolumeDisplay.Exact;
+
+        // General approximation
+        return (int)PercentFull(sol) switch
+        {
+            100 => ExaminedVolumeDisplay.Full,
+            > 66 => ExaminedVolumeDisplay.MostlyFull,
+            > 33 => HalfEmptyOrHalfFull(examiner),
+            > 0 => ExaminedVolumeDisplay.MostlyEmpty,
+            _ => ExaminedVolumeDisplay.Empty,
+        };
+    }
+
+    // Some spessmen see half full, some see half empty, but always the same one.
+    private ExaminedVolumeDisplay HalfEmptyOrHalfFull(EntityUid? examiner = null)
+    {
+        // Optimistic when un-observed
+        if (examiner == null)
+            return ExaminedVolumeDisplay.HalfFull;
+
+        var meta = MetaData(examiner.Value);
+        if (meta.EntityName.Length > 0 &&
+            string.Compare(meta.EntityName.Substring(0, 1), "m", StringComparison.InvariantCultureIgnoreCase) > 0)
+            return ExaminedVolumeDisplay.HalfFull;
+
+        return ExaminedVolumeDisplay.HalfEmpty;
+    }
+
+    /// <summary>
+    ///     Full reagent scan, such as with chemical analysis goggles.
+    /// </summary>
     private void OnSolutionExaminableVerb(Entity<ExaminableSolutionComponent> entity, ref GetVerbsEvent<ExamineVerb> args)
     {
         if (!args.CanInteract || !args.CanAccess)
@@ -953,15 +977,18 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
     }
 
     /// <summary>
-    /// Check if examinable solution requires you to hold the item in hand.
+    ///     Check if an examinable solution is hidden by something.
     /// </summary>
     private bool CanSeeHiddenSolution(Entity<ExaminableSolutionComponent> entity, EntityUid examiner)
     {
         // If not held-only then it's always visible.
-        if (!entity.Comp.HeldOnly)
-            return true;
+        if (entity.Comp.HeldOnly && !Hands.IsHolding(examiner, entity, out _))
+            return false;
 
-        return Hands.IsHolding(examiner, entity, out _);
+        if (!entity.Comp.ExaminableWhileClosed && Openable.IsClosed(entity.Owner, predicted: true))
+            return false;
+
+        return true;
     }
 
     private void OnMapInit(Entity<SolutionContainerManagerComponent> entity, ref MapInitEvent args)
index 17baaef5a37ca982f9b19962857cac036ad45791..2211d58071af25ff8b1a4f37b3cc4de8c0cff983 100644 (file)
@@ -24,9 +24,6 @@ public sealed partial class DrinkComponent : Component
     [DataField, AutoNetworkedField]
     public float Delay = 1;
 
-    [DataField, AutoNetworkedField]
-    public bool Examinable = true;
-
     /// <summary>
     /// If true, trying to drink when empty will not handle the event.
     /// This means other systems such as equipping on use can run.
index a4b8e13b2fa16b19d7effd08d1cd80e55f1e5fd6..66e4834d0d1f81725659e70ae2a431fe05ef45d4 100644 (file)
@@ -37,7 +37,6 @@ public abstract partial class SharedDrinkSystem : EntitySystem
         base.Initialize();
 
         SubscribeLocalEvent<DrinkComponent, AttemptShakeEvent>(OnAttemptShake);
-        SubscribeLocalEvent<DrinkComponent, ExaminedEvent>(OnExamined);
         SubscribeLocalEvent<DrinkComponent, GetVerbsEvent<AlternativeVerb>>(AddDrinkVerb);
     }
 
@@ -47,38 +46,6 @@ public abstract partial class SharedDrinkSystem : EntitySystem
             args.Cancelled = true;
     }
 
-    protected void OnExamined(Entity<DrinkComponent> entity, ref ExaminedEvent args)
-    {
-        TryComp<OpenableComponent>(entity, out var openable);
-        if (_openable.IsClosed(entity.Owner, null, openable, true) || !args.IsInDetailsRange || !entity.Comp.Examinable)
-            return;
-
-        var empty = IsEmpty(entity, entity.Comp);
-        if (empty)
-        {
-            args.PushMarkup(Loc.GetString("drink-component-on-examine-is-empty"));
-            return;
-        }
-
-        if (HasComp<ExaminableSolutionComponent>(entity))
-        {
-            //provide exact measurement for beakers
-            args.PushText(Loc.GetString("drink-component-on-examine-exact-volume", ("amount", DrinkVolume(entity, entity.Comp))));
-        }
-        else
-        {
-            //general approximation
-            var remainingString = (int) _solutionContainer.PercentFull(entity) switch
-            {
-                100 => "drink-component-on-examine-is-full",
-                > 66 => "drink-component-on-examine-is-mostly-full",
-                > 33 => HalfEmptyOrHalfFull(args),
-                _ => "drink-component-on-examine-is-mostly-empty",
-            };
-            args.PushMarkup(Loc.GetString(remainingString));
-        }
-    }
-
     private void AddDrinkVerb(Entity<DrinkComponent> entity, ref GetVerbsEvent<AlternativeVerb> ev)
     {
         if (entity.Owner == ev.User ||
@@ -130,18 +97,6 @@ public abstract partial class SharedDrinkSystem : EntitySystem
         return DrinkVolume(uid, component) <= 0;
     }
 
-    // some see half empty, and others see half full
-    private string HalfEmptyOrHalfFull(ExaminedEvent args)
-    {
-        string remainingString = "drink-component-on-examine-is-half-full";
-
-        if (TryComp(args.Examiner, out MetaDataComponent? examiner) && examiner.EntityName.Length > 0
-            && string.Compare(examiner.EntityName.Substring(0, 1), "m", StringComparison.InvariantCultureIgnoreCase) > 0)
-            remainingString = "drink-component-on-examine-is-half-empty";
-
-        return remainingString;
-    }
-
     /// <summary>
     /// Tries to feed the drink item to the target entity
     /// </summary>
index 6f1ba911f7d7e06465050f9d0ed035726db99f2f..dd78281b89e9b9931455dc4ff9699a1148be832d 100644 (file)
@@ -1,9 +1,37 @@
-shared-solution-container-component-on-examine-empty-container = Contains no chemicals.
-shared-solution-container-component-on-examine-main-text = It contains {INDEFINITE($desc)} [color={$color}]{$desc}[/color] {$wordedAmount}
-shared-solution-container-component-on-examine-worded-amount-one-reagent = chemical.
-shared-solution-container-component-on-examine-worded-amount-multiple-reagents = mixture of chemicals.
+shared-solution-container-component-on-examine-main-text = It contains {INDEFINITE($desc)} [color={$color}]{$desc}[/color] { $chemCount ->
+    [1] chemical.
+   *[other] mixture of chemicals.
+    }
 
 examinable-solution-has-recognizable-chemicals = You can recognize {$recognizedString} in the solution.
-examinable-solution-recognized-first = [color={$color}]{$chemical}[/color]
-examinable-solution-recognized-next = , [color={$color}]{$chemical}[/color]
-examinable-solution-recognized-last = and [color={$color}]{$chemical}[/color]
+examinable-solution-recognized = [color={$color}]{$chemical}[/color]
+
+examinable-solution-on-examine-volume = The contained solution is { $fillLevel ->
+    [exact] holding [color=white]{$current}/{$max}u[/color].
+   *[other] [bold]{ -solution-vague-fill-level(fillLevel: $fillLevel) }[/bold].
+}
+
+examinable-solution-on-examine-volume-no-max = The contained solution is { $fillLevel ->
+    [exact] holding [color=white]{$current}u[/color].
+   *[other] [bold]{ -solution-vague-fill-level(fillLevel: $fillLevel) }[/bold].
+}
+
+examinable-solution-on-examine-volume-puddle = The puddle is { $fillLevel ->
+    [exact] [color=white]{$current}u[/color].
+    [full] huge and overflowing!
+    [mostlyfull] huge and overflowing!
+    [halffull] deep and flowing.
+    [halfempty] very deep.
+   *[mostlyempty] pooling together.
+    [empty] forming multiple small pools.
+}
+
+-solution-vague-fill-level =
+    { $fillLevel ->
+        [full] [color=white]Full[/color]
+        [mostlyfull] [color=#DFDFDF]Mostly Full[/color]
+        [halffull] [color=#C8C8C8]Half Full[/color]
+        [halfempty] [color=#C8C8C8]Half Empty[/color]
+        [mostlyempty] [color=#A4A4A4]Mostly Empty[/color]
+       *[empty] [color=gray]Empty[/color]
+    }
index e80787c8d5beee54e95a172173d661091349e5f1..ab458746ddfb2dfa028f6cba0340fae768dd2980 100644 (file)
@@ -1,14 +1,7 @@
 drink-component-on-use-is-empty = {CAPITALIZE(THE($owner))} is empty!
-drink-component-on-examine-is-empty = [color=gray]Empty[/color]
 drink-component-on-examine-is-opened = [color=yellow]Opened[/color]
 drink-component-on-examine-is-sealed = The seal is intact.
 drink-component-on-examine-is-unsealed = The seal is broken.
-drink-component-on-examine-is-full = Full
-drink-component-on-examine-is-mostly-full = Mostly Full
-drink-component-on-examine-is-half-full = Halfway Full
-drink-component-on-examine-is-half-empty = Halfway Empty
-drink-component-on-examine-is-mostly-empty = Mostly Empty
-drink-component-on-examine-exact-volume = It contains {$amount}u.
 drink-component-try-use-drink-not-open = Open {$owner} first!
 drink-component-try-use-drink-is-empty = {CAPITALIZE(THE($entity))} is empty!
 drink-component-try-use-drink-cannot-drink = You can't drink anything!
index d303aa166c5dbd89536efc876277ea4ed0e51296..005dfedba507d591f104cfe94419060963f1e628 100644 (file)
@@ -75,6 +75,7 @@
     solution: tank
   - type: ExaminableSolution
     solution: tank
+    exactVolume: true
 
 - type: entity
   parent: ClothingBackpack
index a426e7aa1c9012499bbf5946963f77aaea4f2614..1b84dcd7d0871bf08dbadc00009188ea4580ed45 100644 (file)
     delay: 3
     transferAmount: 1
     solution: puddle
-    examinable: false
   - type: ExaminableSolution
     solution: puddle
+    locVolume: "examinable-solution-on-examine-volume-puddle"
   - type: DrawableSolution
     solution: puddle
   - type: BadDrink
index c33e24ad3e49cc6f6e0cb6fc3dc6e73f2114ad93..834a2c3a8e5b210281e583a839e3a9a75e9d5284 100644 (file)
@@ -17,6 +17,8 @@
         maxVol: 50
   - type: PressurizedSolution
     solution: drink
+  - type: ExaminableSolution
+    examinableWhileClosed: false
   - type: Shakeable
   - type: Sprite
     state: icon
           Quantity: 100
   - type: Sprite
     sprite: Objects/Consumable/Drinks/oatmilk.rsi
-    
+
 - type: entity
   parent: [DrinkCartonVisualsOpenable, DrinkCartonBaseFull]
   id: DrinkJuiceLemonCarton
           Quantity: 50
   - type: Sprite
     sprite: Objects/Consumable/Drinks/lemonjuice.rsi
-    
+
 - type: entity
   parent: [DrinkCartonVisualsOpenable, DrinkCartonBaseFull]
   id: DrinkJuicePineappleCarton
index aff0aa7a05d4255e0975abc5df0411f0767de8b0..2d8fadfa43e1f49f82c0e09c12306f07c056105a 100644 (file)
@@ -31,6 +31,8 @@
     solution: drink
   - type: DrainableSolution
     solution: drink
+  - type: ExaminableSolution
+    solution: drink
   - type: UserInterface
     interfaces:
       enum.TransferAmountUiKey.Key:
@@ -75,8 +77,6 @@
   - type: PhysicalComposition
     materialComposition:
       Glass: 25
-  - type: ExaminableSolution
-    solution: drink
   - type: FitsInDispenser
     solution: drink
   - type: Tag
index c2ac4c606fa5b65326180af64d39fc0023e9cbcf..d432c6d9d518e9ec2a338015a34b05dc5be40da7 100644 (file)
     layers:
       - state: icon
         map: ["enum.OpenableVisuals.Layer"]
+  - type: ExaminableSolution
+    examinableWhileClosed: false # If you can't see fill levels, it's probably opaque
 
 - type: entity
   id: DrinkBottleVisualsAll
     fillBaseName: fill-
     inHandsMaxFillLevels: 3
     inHandsFillBaseName: -fill-
+  - type: ExaminableSolution
 
 # Large Glass Bottles
 
index d933cbcd1b1d0657294864163ef6e31c9ab2034b..e6ba0d002e5fa9f5ab85e41e5b1f5e042cd8ef83 100644 (file)
@@ -46,6 +46,9 @@
     solution: drink
   - type: PressurizedSolution
     solution: drink
+  - type: ExaminableSolution
+    solution: drink
+    examinableWhileClosed: false
   - type: Appearance
   - type: GenericVisualizer
     visuals:
index 842cf13641f3c2d5ec0d7ad521c358171e1a2250..d5bfdefe489bf60f3fd81f3f9c81cc2e2975f1cd 100644 (file)
@@ -20,6 +20,8 @@
     solution: drink
   - type: DrainableSolution
     solution: drink
+  - type: ExaminableSolution
+    solution: drink
   - type: SolutionTransfer
     canChangeTransferAmount: true
     maxTransferAmount: 10
index 9e1b100dbfcf83b8281e30ecb8c084fb87e5e756..8f3b9381f7a76b501290c80a6e49d35a646f99bb 100644 (file)
@@ -15,6 +15,8 @@
       Steel: 300
   - type: FitsInDispenser
     solution: drink
+  - type: ExaminableSolution
+    examinableWhileClosed: false
 
 - type: entity
   id: DrinkFlaskVisualsOpenable
index 02a4346ebca8511626b9796b92623449a9fa8eac..e7aa6c125258e2f95ab07201f79ea9ef94f04454 100644 (file)
     mixOnInteract: false
     reactionTypes:
     - Shake
+  - type: ExaminableSolution
+    solution: drink
+    heldOnly: true
+    examinableWhileClosed: false
 
 - type: entity
   parent: DrinkGlassBase
index c7de98b4a44df68d0abeeaa7faccb2a693586e18..7c7df07f7891ea5ca102ecb7c53ce543c12b783a 100644 (file)
@@ -9,6 +9,8 @@
     solution: food
   - type: DrainableSolution
     solution: food
+  - type: ExaminableSolution
+    solution: food
   - type: Sprite
     sprite: Objects/Consumable/Food/condiments.rsi
   - type: Icon
@@ -69,6 +71,8 @@
   - type: Tag
     tags:
       - Packet
+  - type: ExaminableSolution
+    exactVolume: true
 
 - type: entity
   parent: BaseFoodCondimentPacket
index 2b1c2940fe7f1eb5db3d3ddbf98cab6e8b30394b..2a289a95f8c4d8817962c0ce428a9a560591fda7 100644 (file)
     solution: Welder
   - type: ExaminableSolution
     solution: Welder
+    heldOnly: true
+    exactVolume: true
   - type: SolutionRegeneration
     solution: Welder
     generated:
index b692f20dd91e27ce61943a030a822b8e53be58ba..9ed597275428851662f06f12901c9d9601f66595 100644 (file)
@@ -42,6 +42,7 @@
     solution: spray
   - type: ExaminableSolution
     solution: spray
+    exactVolume: true
 
 - type: entity
   name: mega spray bottle
index bd8e78dd9abf025f17fddae5e5f351cf0bbd13b9..bc0cf16e8e5a4b8b945e5b4c15a2fdc716b1ca23 100644 (file)
@@ -22,6 +22,7 @@
     solution: hypospray
   - type: ExaminableSolution
     solution: hypospray
+    exactVolume: true
   - type: Hypospray
     onlyAffectsMobs: false
   - type: UseDelay
@@ -63,6 +64,7 @@
     solution: hypospray
   - type: ExaminableSolution
     solution: hypospray
+    exactVolume: true
   - type: Hypospray
     onlyAffectsMobs: false
   - type: UseDelay
         maxVol: 15
   - type: ExaminableSolution
     solution: pen
+    exactVolume: true
   - type: Hypospray
     solutionName: pen
     transferAmount: 15
   - type: ExaminableSolution
     solution: hypospray
     heldOnly: true # Allow examination only when held in hand.
+    exactVolume: true
   - type: Hypospray
     onlyAffectsMobs: false
   - type: UseDelay
index 1bbe05944fa488ff3b16c5c72c65a9be4d8e5141..84990b10be5d93f5ec235c0b8c32a902e7e036f5 100644 (file)
@@ -26,6 +26,7 @@
       solution: beaker
     - type: ExaminableSolution
       solution: beaker
+      exactVolume: true
     - type: DrawableSolution
       solution: beaker
     - type: InjectableSolution
index a892ee6395d99bad5a39622419424fa25f34cde5..0c0c1cf1725dcf8a676e2c6172ef28b59da0d2ab 100644 (file)
@@ -37,6 +37,7 @@
     solution: drink
   - type: ExaminableSolution
     solution: drink
+    exactVolume: true
   - type: DrawableSolution
     solution: drink
   - type: SolutionTransfer
index 60441bb4750ddb3584ccad2e6ac3bfd18434bdce..bc8207b673bc1401ab648cb5e1117ebdaab9b226 100644 (file)
@@ -43,6 +43,7 @@
     solution: beaker
   - type: ExaminableSolution
     solution: beaker
+    exactVolume: true
   - type: DrawableSolution
     solution: beaker
   - type: SolutionTransfer
     solution: beaker
   - type: ExaminableSolution
     solution: beaker
+    exactVolume: true
   - type: DrawableSolution
     solution: beaker
   - type: SolutionTransfer
index ebd785ab7e3028ffa6b27a332f32e1cd68c13c00..3a7e44775f3e5722646829c8d4094dc16e3fb12c 100644 (file)
@@ -35,6 +35,7 @@
     solution: beaker
   - type: ExaminableSolution
     solution: beaker
+    exactVolume: true
   - type: DrawableSolution
     solution: beaker
   - type: InjectableSolution
     solution: beaker
   - type: ExaminableSolution
     solution: beaker
+    exactVolume: true
   - type: DrawableSolution
     solution: beaker
   - type: InjectableSolution
     solution: beaker
   - type: ExaminableSolution
     solution: beaker
+    exactVolume: true
   - type: DrawableSolution
     solution: beaker
   - type: InjectableSolution
     toggleState: 1 # draw
   - type: ExaminableSolution
     solution: dropper
+    exactVolume: true
   - type: UserInterface
     interfaces:
       enum.TransferAmountUiKey.Key:
     injectOnly: false
   - type: ExaminableSolution
     solution: injector
+    exactVolume: true
   - type: Spillable
     solution: injector
   - type: TrashOnSolutionEmpty
index 638947af7500bd8a0b7e74584754c4688c0aee96..cb25f9199b82dc45ec7a2a60cd94887fcbc247cb 100644 (file)
     solution: anomaly
   - type: ExaminableSolution
     solution: anomaly
+    exactVolume: true
+    locVolume: "examinable-solution-on-examine-volume-no-max"
   - type: RefillableSolution
     solution: anomaly
   - type: InjectableSolution