diff --git a/Content.Client/Chemistry/Components/SolutionContainerComponent.cs b/Content.Client/Chemistry/Components/SolutionContainerComponent.cs deleted file mode 100644 index 98ecdfee9a..0000000000 --- a/Content.Client/Chemistry/Components/SolutionContainerComponent.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Content.Shared.Chemistry.Solution.Components; -using Robust.Shared.GameObjects; - -namespace Content.Client.Chemistry.Components -{ - [RegisterComponent] - [ComponentReference(typeof(SharedSolutionContainerComponent))] - public class SolutionContainerComponent : SharedSolutionContainerComponent - { - - } -} diff --git a/Content.Client/Chemistry/Visualizers/SolutionContainerVisualizer.cs b/Content.Client/Chemistry/Visualizers/SolutionContainerVisualizer.cs index 47b39c4c96..afc8e766e0 100644 --- a/Content.Client/Chemistry/Visualizers/SolutionContainerVisualizer.cs +++ b/Content.Client/Chemistry/Visualizers/SolutionContainerVisualizer.cs @@ -1,5 +1,5 @@ using System; -using Content.Shared.Chemistry.Solution.Components; +using Content.Shared.Chemistry.EntitySystems; using JetBrains.Annotations; using Robust.Client.GameObjects; using Robust.Shared.Maths; diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index 25ed24744d..ae17f9ae33 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -224,8 +224,7 @@ namespace Content.Client.Entry "DebugEquip", "InnateActions", "ReagentGrinder", - "Grindable", - "Juiceable", + "Extractable", "WelderRefinable", "ConveyorAssembly", "TwoWayLever", diff --git a/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs b/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs index 2698dab900..5e45ba25a7 100644 --- a/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs +++ b/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; +using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using Content.Shared.Kitchen.Components; using Robust.Client.AutoGenerated; using Robust.Client.GameObjects; diff --git a/Content.Client/Kitchen/UI/MicrowaveBoundUserInterface.cs b/Content.Client/Kitchen/UI/MicrowaveBoundUserInterface.cs index e336bb73a6..1308d66131 100644 --- a/Content.Client/Kitchen/UI/MicrowaveBoundUserInterface.cs +++ b/Content.Client/Kitchen/UI/MicrowaveBoundUserInterface.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; +using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using Content.Shared.Kitchen.Components; using JetBrains.Annotations; using Robust.Client.GameObjects; diff --git a/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs b/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs index c0398d7e6e..4a20eb1a27 100644 --- a/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs +++ b/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs @@ -4,7 +4,7 @@ using Robust.Client.UserInterface.Controls; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Prototypes; -using static Content.Shared.Chemistry.Solution.Solution; +using static Content.Shared.Chemistry.Components.Solution; namespace Content.Client.Kitchen.UI { diff --git a/Content.IntegrationTests/Tests/Body/LungTest.cs b/Content.IntegrationTests/Tests/Body/LungTest.cs index d1ac590adf..b529a2abe1 100644 --- a/Content.IntegrationTests/Tests/Body/LungTest.cs +++ b/Content.IntegrationTests/Tests/Body/LungTest.cs @@ -28,6 +28,7 @@ namespace Content.IntegrationTests.Tests.Body components: - type: Bloodstream max_volume: 100 + - type: SolutionContainerManager - type: Body template: HumanoidTemplate preset: HumanPreset diff --git a/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs b/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs index 9587897442..a80c35b10e 100644 --- a/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs +++ b/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs @@ -1,17 +1,13 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Content.Server.Chemistry.Components; -using Content.Server.Fluids.Components; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reaction; -using Content.Shared.Chemistry.Reagent; -using Content.Shared.Coordinates; using NUnit.Framework; using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Prototypes; -using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.IntegrationTests.Tests.Chemistry @@ -37,17 +33,19 @@ namespace Content.IntegrationTests.Tests.Chemistry Console.WriteLine($"Testing {reactionPrototype.ID}"); IEntity beaker; - SolutionContainerComponent component = null; + Solution component = null; server.Assert(() => { mapManager.CreateNewMapEntity(MapId.Nullspace); beaker = entityManager.SpawnEntity("BluespaceBeaker", MapCoordinates.Nullspace); - Assert.That(beaker.TryGetComponent(out component)); + Assert.That(EntitySystem.Get() + .TryGetSolution(beaker, "beaker", out component)); foreach (var (id, reactant) in reactionPrototype.Reactants) { - Assert.That(component.TryAddReagent(id, reactant.Amount, out var quantity)); + Assert.That(EntitySystem.Get() + .TryAddReagent(beaker.Uid, component, id, reactant.Amount, out var quantity)); Assert.That(reactant.Amount, Is.EqualTo(quantity)); } }); @@ -61,7 +59,7 @@ namespace Content.IntegrationTests.Tests.Chemistry var foundProductsMap = reactionPrototype.Products .Concat(reactionPrototype.Reactants.Where(x => x.Value.Catalyst).ToDictionary(x => x.Key, x => x.Value.Amount)) .ToDictionary(x => x, x => false); - foreach (var reagent in component.Solution.Contents) + foreach (var reagent in component.Contents) { Assert.That(foundProductsMap.TryFirstOrNull(x => x.Key.Key == reagent.ReagentId && x.Key.Value == reagent.Quantity, out var foundProduct)); foundProductsMap[foundProduct.Value.Key] = true; diff --git a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs index f3048f2c7f..8cbbe2ffdd 100644 --- a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs +++ b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs @@ -1,9 +1,8 @@ using System; using System.Threading.Tasks; using Content.Server.Fluids.Components; -using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using Content.Shared.Coordinates; using NUnit.Framework; using Robust.Shared.GameObjects; diff --git a/Content.Server/AI/Utility/Considerations/Nutrition/Drink/DrinkValueCon.cs b/Content.Server/AI/Utility/Considerations/Nutrition/Drink/DrinkValueCon.cs index 04b79925d2..83573a0b8c 100644 --- a/Content.Server/AI/Utility/Considerations/Nutrition/Drink/DrinkValueCon.cs +++ b/Content.Server/AI/Utility/Considerations/Nutrition/Drink/DrinkValueCon.cs @@ -1,6 +1,8 @@ using Content.Server.AI.WorldState; using Content.Server.AI.WorldState.States; -using Content.Server.Chemistry.Components; +using Content.Server.Nutrition.Components; +using Content.Shared.Chemistry.EntitySystems; +using Robust.Shared.GameObjects; namespace Content.Server.AI.Utility.Considerations.Nutrition.Drink { @@ -10,14 +12,16 @@ namespace Content.Server.AI.Utility.Considerations.Nutrition.Drink { var target = context.GetState().GetValue(); - if (target == null || target.Deleted || !target.TryGetComponent(out SolutionContainerComponent? drink)) + if (target == null + || target.Deleted + || !EntitySystem.Get().TryGetSolution(target, DrinkComponent.DefaultSolutionName, out var drink)) { return 0.0f; } var nutritionValue = 0; - foreach (var reagent in drink.ReagentList) + foreach (var reagent in drink.Contents) { // TODO nutritionValue += (reagent.Quantity * 30).Int(); diff --git a/Content.Server/AI/Utility/Considerations/Nutrition/Food/FoodValueCon.cs b/Content.Server/AI/Utility/Considerations/Nutrition/Food/FoodValueCon.cs index 38390e0ed6..e6d5858c04 100644 --- a/Content.Server/AI/Utility/Considerations/Nutrition/Food/FoodValueCon.cs +++ b/Content.Server/AI/Utility/Considerations/Nutrition/Food/FoodValueCon.cs @@ -1,6 +1,8 @@ using Content.Server.AI.WorldState; using Content.Server.AI.WorldState.States; -using Content.Server.Chemistry.Components; +using Content.Server.Nutrition.Components; +using Content.Shared.Chemistry.EntitySystems; +using Robust.Shared.GameObjects; namespace Content.Server.AI.Utility.Considerations.Nutrition.Food { @@ -10,14 +12,15 @@ namespace Content.Server.AI.Utility.Considerations.Nutrition.Food { var target = context.GetState().GetValue(); - if (target == null || target.Deleted || !target.TryGetComponent(out SolutionContainerComponent? food)) + if (target == null || target.Deleted || + !EntitySystem.Get().TryGetSolution(target, FoodComponent.SolutionName, out var food)) { return 0.0f; } var nutritionValue = 0; - foreach (var reagent in food.ReagentList) + foreach (var reagent in food.Contents) { // TODO nutritionValue += (reagent.Quantity * 30).Int(); diff --git a/Content.Server/Administration/Verbs/AdminAddReagentVerb.cs b/Content.Server/Administration/Verbs/AdminAddReagentVerb.cs index cd1375a45c..936ef2a5d8 100644 --- a/Content.Server/Administration/Verbs/AdminAddReagentVerb.cs +++ b/Content.Server/Administration/Verbs/AdminAddReagentVerb.cs @@ -1,10 +1,10 @@ using Content.Server.Administration.Managers; -using Content.Server.Chemistry.Components; using Content.Server.EUI; using Content.Shared.Administration; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; -using Content.Shared.Chemistry.Solution.Components; using Content.Shared.Eui; using Content.Shared.Verbs; using Robust.Server.GameObjects; @@ -13,7 +13,6 @@ using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Localization; - namespace Content.Server.Administration.Verbs { [GlobalVerb] @@ -34,9 +33,8 @@ namespace Content.Server.Administration.Verbs { // ISolutionInteractionsComponent doesn't exactly have an interface for "admin tries to refill this", so... // Still have a path for SolutionContainerComponent in case it doesn't allow direct refilling. - if (!target.HasComponent() - && !(target.TryGetComponent(out ISolutionInteractionsComponent? interactions) - && interactions.CanInject)) + if (!(target.HasComponent() + && target.HasComponent())) { data.Visibility = VerbVisibility.Invisible; return; @@ -87,7 +85,8 @@ namespace Content.Server.Administration.Verbs public override EuiStateBase GetNewState() { - if (_target.TryGetComponent(out SolutionContainerComponent? container)) + if (EntitySystem.Get() + .TryGetSolution(_target, "default", out var container)) { return new AdminAddReagentEuiState { @@ -96,16 +95,6 @@ namespace Content.Server.Administration.Verbs }; } - if (_target.TryGetComponent(out ISolutionInteractionsComponent? interactions)) - { - return new AdminAddReagentEuiState - { - // We don't exactly have an absolute total volume so good enough. - CurVolume = ReagentUnit.Zero, - MaxVolume = interactions.InjectSpaceAvailable - }; - } - return new AdminAddReagentEuiState { CurVolume = ReagentUnit.Zero, @@ -131,15 +120,21 @@ namespace Content.Server.Administration.Verbs var id = doAdd.ReagentId; var amount = doAdd.Amount; + var solutionsSys = EntitySystem.Get(); - if (_target.TryGetComponent(out SolutionContainerComponent? container)) - { - container.TryAddReagent(id, amount, out _); - } - else if (_target.TryGetComponent(out ISolutionInteractionsComponent? interactions)) + if (_target.TryGetComponent(out InjectableSolutionComponent? injectable) + && solutionsSys.TryGetSolution(_target, injectable.Name, out var targetSolution)) { var solution = new Solution(id, amount); - interactions.Inject(solution); + solutionsSys.Inject(_target.Uid, targetSolution, solution); + } + else + { + //TODO decide how to find the solution + if (solutionsSys.TryGetSolution(_target, "default", out var solution)) + { + solutionsSys.TryAddReagent(_target.Uid,solution, id, amount, out _); + } } StateDirty(); diff --git a/Content.Server/Atmos/Reactions/WaterVaporReaction.cs b/Content.Server/Atmos/Reactions/WaterVaporReaction.cs index 7e19ffafce..8161cc5739 100644 --- a/Content.Server/Atmos/Reactions/WaterVaporReaction.cs +++ b/Content.Server/Atmos/Reactions/WaterVaporReaction.cs @@ -1,10 +1,9 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Fluids.Components; +using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using Content.Shared.Maps; using JetBrains.Annotations; -using Robust.Server.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; namespace Content.Server.Atmos.Reactions diff --git a/Content.Server/Body/Behavior/StomachBehavior.cs b/Content.Server/Body/Behavior/StomachBehavior.cs index 813b8a3a5e..30366117e5 100644 --- a/Content.Server/Body/Behavior/StomachBehavior.cs +++ b/Content.Server/Body/Behavior/StomachBehavior.cs @@ -1,11 +1,10 @@ using System.Collections.Generic; using System.Linq; using Content.Server.Body.Circulatory; -using Content.Server.Chemistry.Components; using Content.Shared.Body.Networks; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; -using Content.Shared.Chemistry.Solution.Components; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; @@ -18,6 +17,7 @@ namespace Content.Server.Body.Behavior /// public class StomachBehavior : MechanismBehavior { + private const string DefaultSolutionName = "stomach"; private float _accumulatedFrameTime; /// @@ -30,7 +30,6 @@ namespace Content.Server.Body.Behavior /// public override void Update(float frameTime) { - // Do not metabolise if the organ does not have a body. if (Body == null) { @@ -49,8 +48,8 @@ namespace Content.Server.Body.Behavior // Note that "Owner" should be the organ that has this behaviour/mechanism, and it should have a dedicated // solution container. "Body.Owner" is something else, and may have more than one solution container. - if (!Owner.TryGetComponent(out SolutionContainerComponent? solution) || - !Body.Owner.TryGetComponent(out BloodstreamComponent? bloodstream)) + if (!Body.Owner.TryGetComponent(out BloodstreamComponent? bloodstream) + || !EntitySystem.Get().TryGetSolution(Owner, SharedBloodstreamComponent.DefaultSolutionName, out var solution)) { return; } @@ -68,13 +67,15 @@ namespace Content.Server.Body.Behavior // This reagent has been in the somach long enough, TRY to transfer it. // But first, check if the reagent still exists, and how much is left. // Some poor spessman may have washed down a potassium snack with some water. - if (solution.Solution.ContainsReagent(delta.ReagentId, out ReagentUnit quantity)){ - - if (quantity > delta.Quantity) { + if (solution.ContainsReagent(delta.ReagentId, out ReagentUnit quantity)) + { + if (quantity > delta.Quantity) + { quantity = delta.Quantity; } - solution.TryRemoveReagent(delta.ReagentId, quantity); + EntitySystem.Get() + .TryRemoveReagent(Owner.Uid, solution, delta.ReagentId, quantity); transferSolution.AddReagent(delta.ReagentId, quantity); } @@ -86,17 +87,28 @@ namespace Content.Server.Body.Behavior bloodstream.TryTransferSolution(transferSolution); } + public Solution? StomachSolution + { + get + { + EntitySystem.Get().TryGetSolution(Owner, DefaultSolutionName, out var solution); + return solution; + } + } + /// /// Max volume of internal solution storage /// public ReagentUnit MaxVolume { - get => Owner.TryGetComponent(out SharedSolutionContainerComponent? solution) ? solution.MaxVolume : ReagentUnit.Zero; + get => + StomachSolution?.MaxVolume ?? ReagentUnit.Zero; + set { - if (Owner.TryGetComponent(out SharedSolutionContainerComponent? solution)) + if (StomachSolution != null) { - solution.MaxVolume = value; + StomachSolution.MaxVolume = value; } } } @@ -118,27 +130,25 @@ namespace Content.Server.Body.Behavior /// /// Used to track how long each reagent has been in the stomach /// - [ViewVariables] - private readonly List _reagentDeltas = new(); + [ViewVariables] private readonly List _reagentDeltas = new(); public override void Startup() { base.Startup(); - Owner.EnsureComponentWarn(out SolutionContainerComponent solution); - + var solution = EntitySystem.Get().EnsureSolution(Owner, DefaultSolutionName); solution.MaxVolume = InitialMaxVolume; } public bool CanTransferSolution(Solution solution) { - if (!Owner.TryGetComponent(out SharedSolutionContainerComponent? solutionComponent)) + if (StomachSolution == null) { return false; } // TODO: For now no partial transfers. Potentially change by design - if (!solutionComponent.CanAddSolution(solution)) + if (!StomachSolution.CanAddSolution(solution)) { return false; } @@ -151,13 +161,13 @@ namespace Content.Server.Body.Behavior if (Owner == null || !CanTransferSolution(solution)) return false; - if (!Owner.TryGetComponent(out SolutionContainerComponent? solutionComponent)) + if (StomachSolution == null) { return false; } // Add solution to _stomachContents - solutionComponent.TryAddSolution(solution); + EntitySystem.Get().TryAddSolution(Owner.Uid, StomachSolution, solution); // Add each reagent to _reagentDeltas. Used to track how long each reagent has been in the stomach foreach (var reagent in solution.Contents) { diff --git a/Content.Server/Body/Circulatory/BloodstreamComponent.cs b/Content.Server/Body/Circulatory/BloodstreamComponent.cs index 998e3d97bb..9b1577abdf 100644 --- a/Content.Server/Body/Circulatory/BloodstreamComponent.cs +++ b/Content.Server/Body/Circulatory/BloodstreamComponent.cs @@ -1,13 +1,12 @@ using System; -using System.Linq; using Content.Server.Atmos; using Content.Server.Atmos.EntitySystems; using Content.Server.Body.Respiratory; -using Content.Server.Chemistry.Components; using Content.Shared.Atmos; using Content.Shared.Body.Networks; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; @@ -23,31 +22,33 @@ namespace Content.Server.Body.Circulatory /// /// Max volume of internal solution storage /// - [DataField("maxVolume")] - [ViewVariables] private ReagentUnit _initialMaxVolume = ReagentUnit.New(250); + [DataField("maxVolume")] [ViewVariables] + private ReagentUnit _initialMaxVolume = ReagentUnit.New(250); /// /// Internal solution for reagent storage /// - [ViewVariables] private SolutionContainerComponent _internalSolution = default!; + [ViewVariables] private Solution? _internalSolution; /// /// Empty volume of internal solution /// - [ViewVariables] public ReagentUnit EmptyVolume => _internalSolution.EmptyVolume; + [ViewVariables] + public ReagentUnit EmptyVolume => _internalSolution?.AvailableVolume ?? ReagentUnit.Zero; [ViewVariables] public GasMixture Air { get; set; } = new(6) - {Temperature = Atmospherics.NormalBodyTemperature}; - - [ViewVariables] public SolutionContainerComponent Solution => _internalSolution; + { Temperature = Atmospherics.NormalBodyTemperature }; protected override void Initialize() { base.Initialize(); - _internalSolution = Owner.EnsureComponent(); - _internalSolution.MaxVolume = _initialMaxVolume; + _internalSolution = EntitySystem.Get().EnsureSolution(Owner, DefaultSolutionName); + if (_internalSolution != null) + { + _internalSolution.MaxVolume = _initialMaxVolume; + } } /// @@ -59,12 +60,14 @@ namespace Content.Server.Body.Circulatory public override bool TryTransferSolution(Solution solution) { // For now doesn't support partial transfers - if (solution.TotalVolume + _internalSolution.CurrentVolume > _internalSolution.MaxVolume) + var current = _internalSolution?.CurrentVolume ?? ReagentUnit.Zero; + var max = _internalSolution?.MaxVolume ?? ReagentUnit.Zero; + if (solution.TotalVolume + current > max) { return false; } - _internalSolution.TryAddSolution(solution); + EntitySystem.Get().TryAddSolution(Owner.Uid, _internalSolution, solution); return true; } diff --git a/Content.Server/Body/Metabolism/MetabolizerComponent.cs b/Content.Server/Body/Metabolism/MetabolizerComponent.cs index b74926ff57..d1dccdc1a4 100644 --- a/Content.Server/Body/Metabolism/MetabolizerComponent.cs +++ b/Content.Server/Body/Metabolism/MetabolizerComponent.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using Content.Shared.Body.Networks; using Content.Shared.Chemistry.Reagent; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; @@ -25,11 +26,11 @@ namespace Content.Server.Body.Metabolism public float UpdateFrequency = 1.0f; /// - /// Whether this metabolizer should attempt to metabolize chemicals in its parent bodies' bloodstream, + /// From which solution will this metabolizer attempt to metabolize chemicals in its parent bodies' bloodstream, /// as opposed to a solution container on the metabolizing entity itself. /// - [DataField("takeFromBloodstream")] - public bool TakeFromBloodstream = true; + [DataField("solution")] + public string SolutionName { get; set; } = SharedBloodstreamComponent.DefaultSolutionName; /// /// A dictionary mapping reagent string IDs to a list of effects & associated metabolism rate. diff --git a/Content.Server/Body/Metabolism/MetabolizerSystem.cs b/Content.Server/Body/Metabolism/MetabolizerSystem.cs index 249ca13391..2967412fbd 100644 --- a/Content.Server/Body/Metabolism/MetabolizerSystem.cs +++ b/Content.Server/Body/Metabolism/MetabolizerSystem.cs @@ -1,20 +1,36 @@ using System.Collections.Generic; -using System.Linq; using Content.Server.Body.Circulatory; -using Content.Server.Chemistry.Components; using Content.Shared.Body.Components; using Content.Shared.Body.Mechanism; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; +using JetBrains.Annotations; using Robust.Shared.GameObjects; using Robust.Shared.IoC; -using Robust.Shared.Prototypes; namespace Content.Server.Body.Metabolism { // TODO mirror in the future working on mechanisms move updating here to BodySystem so it can be ordered? + [UsedImplicitly] public class MetabolizerSystem : EntitySystem { + [Dependency] + private readonly SolutionContainerSystem _solutionContainerSystem = default!; + + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMetabolizerInit); + } + + private void OnMetabolizerInit(EntityUid uid, MetabolizerComponent component, ComponentInit args) + { + _solutionContainerSystem.EnsureSolution(EntityManager.GetEntity(uid), component.SolutionName); + } + public override void Update(float frameTime) { base.Update(frameTime); @@ -35,37 +51,34 @@ namespace Content.Server.Body.Metabolism private void TryMetabolize(MetabolizerComponent comp) { var owner = comp.Owner; - var reagentList = new List(); - SolutionContainerComponent? solution = null; + IReadOnlyList reagentList = new List(); + Solution? solution = null; SharedBodyComponent? body = null; + var solutionsSys = Get(); // if this field is passed we should try and take from the bloodstream over anything else - if (comp.TakeFromBloodstream && owner.TryGetComponent(out var mech)) + if (owner.TryGetComponent(out var mech)) { body = mech.Body; if (body != null) { - if (body.Owner.TryGetComponent(out var bloodstream) - && bloodstream.Solution.CurrentVolume >= ReagentUnit.Zero) + if (body.Owner.HasComponent() + && solutionsSys.TryGetSolution(body.Owner, comp.SolutionName, out solution) + && solution.CurrentVolume >= ReagentUnit.Zero) { - solution = bloodstream.Solution; - reagentList = bloodstream.Solution.ReagentList.ToList(); + reagentList = solution.Contents; } } } - else if (owner.TryGetComponent(out var sol)) - { - // if we have no mechanism/body but a solution container instead, - // we'll just use that to metabolize from - solution = sol; - reagentList = sol.ReagentList.ToList(); - } + if (solution == null || reagentList.Count == 0) { // We're all outta ideas on where to metabolize from return; } + List removeReagents = new(5); + // Run metabolism for each reagent, remove metabolized reagents foreach (var reagent in reagentList) { @@ -100,8 +113,10 @@ namespace Content.Server.Body.Metabolism effect.Metabolize(ent, reagent); } - solution.TryRemoveReagent(reagent.ReagentId, metabolism.MetabolismRate); + removeReagents.Add(new Solution.ReagentQuantity(reagent.ReagentId, metabolism.MetabolismRate)); } + + solutionsSys.TryRemoveAllReagents(solution, removeReagents); } } } diff --git a/Content.Server/Botany/Components/PlantHolderComponent.cs b/Content.Server/Botany/Components/PlantHolderComponent.cs index 9b43f34f59..927be946eb 100644 --- a/Content.Server/Botany/Components/PlantHolderComponent.cs +++ b/Content.Server/Botany/Components/PlantHolderComponent.cs @@ -1,9 +1,7 @@ using System; -using System.Linq; using System.Threading.Tasks; using Content.Server.Atmos; using Content.Server.Atmos.EntitySystems; -using Content.Server.Chemistry.Components; using Content.Server.Fluids.Components; using Content.Server.Hands.Components; using Content.Server.Notification; @@ -11,11 +9,10 @@ using Content.Server.Plants; using Content.Shared.ActionBlocker; using Content.Shared.Audio; using Content.Shared.Botany; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution.Components; using Content.Shared.Examine; using Content.Shared.Interaction; -using Content.Shared.Interaction.Events; using Content.Shared.Notification.Managers; using Content.Shared.Random.Helpers; using Content.Shared.Tag; @@ -40,17 +37,23 @@ namespace Content.Server.Botany.Components { public const float HydroponicsSpeedMultiplier = 1f; public const float HydroponicsConsumptionMultiplier = 4f; + private const string SoilSolutionName = "soil"; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; + [ComponentDependency] private readonly AppearanceComponent? _appearanceComponent = default!; public override string Name => "PlantHolder"; [ViewVariables] private int _lastProduce; + [ViewVariables(VVAccess.ReadWrite)] private int _missingGas; + private readonly TimeSpan _cycleDelay = TimeSpan.FromSeconds(15f); + [ViewVariables] private TimeSpan _lastCycle = TimeSpan.Zero; + [ViewVariables(VVAccess.ReadWrite)] private bool _updateSpriteAfterUpdate; [ViewVariables(VVAccess.ReadWrite)] @@ -117,16 +120,6 @@ namespace Content.Server.Botany.Components [ViewVariables(VVAccess.ReadWrite)] public bool ForceUpdate { get; set; } - [ComponentDependency] private readonly SolutionContainerComponent? _solutionContainer = default!; - [ComponentDependency] private readonly AppearanceComponent? _appearanceComponent = default!; - - protected override void Initialize() - { - base.Initialize(); - - Owner.EnsureComponentWarn(); - } - public void WeedInvasion() { // TODO @@ -142,7 +135,7 @@ namespace Content.Server.Botany.Components ForceUpdate = false; else if (curTime < (_lastCycle + _cycleDelay)) { - if(_updateSpriteAfterUpdate) + if (_updateSpriteAfterUpdate) UpdateSprite(); return; } @@ -190,8 +183,8 @@ namespace Content.Server.Botany.Components SkipAging--; else { - if(_random.Prob(0.8f)) - Age += (int)(1 * HydroponicsSpeedMultiplier); + if (_random.Prob(0.8f)) + Age += (int) (1 * HydroponicsSpeedMultiplier); _updateSpriteAfterUpdate = true; } @@ -207,7 +200,8 @@ namespace Content.Server.Botany.Components // Water consumption. if (Seed.WaterConsumption > 0 && WaterLevel > 0 && _random.Prob(0.75f)) { - WaterLevel -= MathF.Max(0f, Seed.NutrientConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier); + WaterLevel -= MathF.Max(0f, + Seed.NutrientConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier); if (DrawWarnings) _updateSpriteAfterUpdate = true; } @@ -248,7 +242,8 @@ namespace Content.Server.Botany.Components _updateSpriteAfterUpdate = true; } - var environment = EntitySystem.Get().GetTileMixture(Owner.Transform.Coordinates, true)?? GasMixture.SpaceGas; + var environment = EntitySystem.Get().GetTileMixture(Owner.Transform.Coordinates, true) ?? + GasMixture.SpaceGas; if (Seed.ConsumeGasses.Count > 0) { @@ -306,7 +301,8 @@ namespace Content.Server.Botany.Components { foreach (var (gas, amount) in Seed.ExudeGasses) { - environment.AdjustMoles(gas, MathF.Max(1f, MathF.Round((amount * MathF.Round(Seed.Potency)) / exudeCount))); + environment.AdjustMoles(gas, + MathF.Max(1f, MathF.Round((amount * MathF.Round(Seed.Potency)) / exudeCount))); } } @@ -357,7 +353,7 @@ namespace Content.Server.Botany.Components if (DrawWarnings) _updateSpriteAfterUpdate = true; } - else if(Age < 0) // Revert back to seed packet! + else if (Age < 0) // Revert back to seed packet! { Seed.SpawnSeedPacket(Owner.Transform.Coordinates); RemovePlant(); @@ -393,7 +389,7 @@ namespace Content.Server.Botany.Components CheckLevelSanity(); - if(_updateSpriteAfterUpdate) + if (_updateSpriteAfterUpdate) UpdateSprite(); } @@ -428,8 +424,8 @@ namespace Content.Server.Botany.Components { if (!Seed.CheckHarvest(user, hands.GetActiveHand?.Owner)) return false; - - } else if (!Seed.CheckHarvest(user)) + } + else if (!Seed.CheckHarvest(user)) { return false; } @@ -460,7 +456,7 @@ namespace Content.Server.Botany.Components Harvest = false; _lastProduce = Age; - if(Seed?.HarvestRepeat == HarvestType.NoRepeat) + if (Seed?.HarvestRepeat == HarvestType.NoRepeat) RemovePlant(); CheckLevelSanity(); @@ -546,10 +542,11 @@ namespace Content.Server.Botany.Components public void UpdateReagents() { - if (_solutionContainer == null) + var solutionSystem = EntitySystem.Get(); + if (!solutionSystem.TryGetSolution(Owner, SoilSolutionName, out var solution)) return; - if (_solutionContainer.Solution.TotalVolume <= 0 || MutationLevel >= 25) + if (solution.TotalVolume <= 0 || MutationLevel >= 25) { if (MutationLevel >= 0) { @@ -560,12 +557,10 @@ namespace Content.Server.Botany.Components else { var one = ReagentUnit.New(1); - - foreach (var (reagent, amount) in _solutionContainer.ReagentList.ToArray()) + foreach (var reagent in solutionSystem.RemoveEachReagent(solution, one)) { var reagentProto = _prototypeManager.Index(reagent); reagentProto.ReactionPlant(Owner); - _solutionContainer.Solution.RemoveReagent(reagent, amount < one ? amount : one); } } @@ -586,26 +581,30 @@ namespace Content.Server.Botany.Components if (Seed != null) { - if(DrawWarnings) + if (DrawWarnings) _appearanceComponent.SetData(PlantHolderVisuals.HealthLight, Health <= (Seed.Endurance / 2f)); if (Dead) { - _appearanceComponent.SetData(PlantHolderVisuals.Plant, new SpriteSpecifier.Rsi(Seed.PlantRsi, "dead")); + _appearanceComponent.SetData(PlantHolderVisuals.Plant, + new SpriteSpecifier.Rsi(Seed.PlantRsi, "dead")); } else if (Harvest) { - _appearanceComponent.SetData(PlantHolderVisuals.Plant, new SpriteSpecifier.Rsi(Seed.PlantRsi, "harvest")); + _appearanceComponent.SetData(PlantHolderVisuals.Plant, + new SpriteSpecifier.Rsi(Seed.PlantRsi, "harvest")); } else if (Age < Seed.Maturation) { - var growthStage = Math.Max(1, (int)((Age * Seed.GrowthStages) / Seed.Maturation)); - _appearanceComponent.SetData(PlantHolderVisuals.Plant, new SpriteSpecifier.Rsi(Seed.PlantRsi,$"stage-{growthStage}")); + var growthStage = Math.Max(1, (int) ((Age * Seed.GrowthStages) / Seed.Maturation)); + _appearanceComponent.SetData(PlantHolderVisuals.Plant, + new SpriteSpecifier.Rsi(Seed.PlantRsi, $"stage-{growthStage}")); _lastProduce = Age; } else { - _appearanceComponent.SetData(PlantHolderVisuals.Plant, new SpriteSpecifier.Rsi(Seed.PlantRsi,$"stage-{Seed.GrowthStages}")); + _appearanceComponent.SetData(PlantHolderVisuals.Plant, + new SpriteSpecifier.Rsi(Seed.PlantRsi, $"stage-{Seed.GrowthStages}")); } } else @@ -617,7 +616,9 @@ namespace Content.Server.Botany.Components if (!DrawWarnings) return; _appearanceComponent.SetData(PlantHolderVisuals.WaterLight, WaterLevel <= 10); _appearanceComponent.SetData(PlantHolderVisuals.NutritionLight, NutritionLevel <= 2); - _appearanceComponent.SetData(PlantHolderVisuals.AlertLight, WeedLevel >= 5 || PestLevel >= 5 || Toxins >= 40 || ImproperHeat || ImproperLight || ImproperPressure || _missingGas > 0); + _appearanceComponent.SetData(PlantHolderVisuals.AlertLight, + WeedLevel >= 5 || PestLevel >= 5 || Toxins >= 40 || ImproperHeat || ImproperLight || ImproperPressure || + _missingGas > 0); _appearanceComponent.SetData(PlantHolderVisuals.HarvestLight, Harvest); } @@ -644,7 +645,7 @@ namespace Content.Server.Botany.Components var user = eventArgs.User; var usingItem = eventArgs.Using; - if (usingItem == null || usingItem.Deleted || !EntitySystem.Get().CanInteract(user)) + if (usingItem.Deleted || !EntitySystem.Get().CanInteract(user)) return false; if (usingItem.TryGetComponent(out SeedComponent? seeds)) @@ -659,8 +660,8 @@ namespace Content.Server.Botany.Components } user.PopupMessageCursor(Loc.GetString("plant-holder-component-plant-success-message", - ("seedName", seeds.Seed.SeedName), - ("seedNoun", seeds.Seed.SeedNoun))); + ("seedName", seeds.Seed.SeedName), + ("seedNoun", seeds.Seed.SeedNoun))); Seed = seeds.Seed; Dead = false; @@ -676,7 +677,8 @@ namespace Content.Server.Botany.Components return true; } - user.PopupMessageCursor(Loc.GetString("plant-holder-component-already-seeded-message", ("name", Owner.Name))); + user.PopupMessageCursor(Loc.GetString("plant-holder-component-already-seeded-message", + ("name", Owner.Name))); return false; } @@ -684,8 +686,10 @@ namespace Content.Server.Botany.Components { if (WeedLevel > 0) { - user.PopupMessageCursor(Loc.GetString("plant-holder-component-remove-weeds-message",("name", Owner.Name))); - user.PopupMessageOtherClients(Loc.GetString("plant-holder-component-remove-weeds-others-message",("otherName", user.Name))); + user.PopupMessageCursor(Loc.GetString("plant-holder-component-remove-weeds-message", + ("name", Owner.Name))); + user.PopupMessageOtherClients(Loc.GetString("plant-holder-component-remove-weeds-others-message", + ("otherName", user.Name))); WeedLevel = 0; UpdateSprite(); } @@ -701,8 +705,10 @@ namespace Content.Server.Botany.Components { if (Seed != null) { - user.PopupMessageCursor(Loc.GetString("plant-holder-component-remove-plant-message",("name", Owner.Name))); - user.PopupMessageOtherClients(Loc.GetString("plant-holder-component-remove-plant-others-message",("name", user.Name))); + user.PopupMessageCursor(Loc.GetString("plant-holder-component-remove-plant-message", + ("name", Owner.Name))); + user.PopupMessageOtherClients(Loc.GetString("plant-holder-component-remove-plant-others-message", + ("name", user.Name))); RemovePlant(); } else @@ -713,31 +719,38 @@ namespace Content.Server.Botany.Components return true; } - if (usingItem.TryGetComponent(out ISolutionInteractionsComponent? solution) && solution.CanDrain) + var solutionSystem = EntitySystem.Get(); + if (solutionSystem.TryGetDrainableSolution(usingItem.Uid, out var solution) + && solutionSystem.TryGetSolution(Owner, SoilSolutionName, out var targetSolution)) { var amount = ReagentUnit.New(5); var sprayed = false; + var targetEntity = Owner.Uid; + var solutionEntity = usingItem.Uid; if (usingItem.TryGetComponent(out SprayComponent? spray)) { sprayed = true; amount = ReagentUnit.New(1); - SoundSystem.Play(Filter.Pvs(usingItem), spray.SpraySound.GetSound(), usingItem, AudioHelpers.WithVariation(0.125f)); + SoundSystem.Play(Filter.Pvs(usingItem), spray.SpraySound.GetSound(), usingItem, + AudioHelpers.WithVariation(0.125f)); } - var split = solution.Drain(amount); + var split = solutionSystem.Drain(solutionEntity, solution, amount); if (split.TotalVolume == 0) { - user.PopupMessageCursor(Loc.GetString("plant-holder-component-empty-message",("owner", usingItem))); + user.PopupMessageCursor(Loc.GetString("plant-holder-component-empty-message", + ("owner", usingItem))); return true; } - user.PopupMessageCursor(Loc.GetString(sprayed ? "plant-holder-component-spray-message" : "plant-holder-component-transfer-message", - ("owner",Owner), - ("amount",split.TotalVolume))); + user.PopupMessageCursor(Loc.GetString( + sprayed ? "plant-holder-component-spray-message" : "plant-holder-component-transfer-message", + ("owner", Owner), + ("amount", split.TotalVolume))); - _solutionContainer?.TryAddSolution(split); + solutionSystem.TryAddSolution(targetEntity, targetSolution, split); ForceUpdateByExternalCause(); @@ -766,7 +779,8 @@ namespace Content.Server.Botany.Components var seed = Seed.SpawnSeedPacket(user.Transform.Coordinates); seed.RandomOffset(0.25f); - user.PopupMessageCursor(Loc.GetString("plant-holder-component-take-sample-message", ("seedName", Seed.DisplayName))); + user.PopupMessageCursor(Loc.GetString("plant-holder-component-take-sample-message", + ("seedName", Seed.DisplayName))); Health -= (_random.Next(3, 5) * 10); if (_random.Prob(0.3f)) @@ -787,17 +801,18 @@ namespace Content.Server.Botany.Components if (usingItem.HasComponent()) { user.PopupMessageCursor(Loc.GetString("plant-holder-component-compost-message", - ("owner", Owner), - ("usingItem", usingItem))); + ("owner", Owner), + ("usingItem", usingItem))); user.PopupMessageOtherClients(Loc.GetString("plant-holder-component-compost-others-message", - ("user",user), - ("usingItem", usingItem), - ("owner", Owner))); + ("user", user), + ("usingItem", usingItem), + ("owner", Owner))); - if (usingItem.TryGetComponent(out SolutionContainerComponent? solution2)) + if (solutionSystem.TryGetSolution(usingItem, ProduceComponent.SolutionName, out var solution2)) { // This deliberately discards overfill. - _solutionContainer?.TryAddSolution(solution2.SplitSolution(solution2.Solution.TotalVolume)); + solutionSystem.TryAddSolution(usingItem.Uid, solution2, + solutionSystem.SplitSolution(usingItem.Uid, solution2, solution2.TotalVolume)); ForceUpdateByExternalCause(); } @@ -834,14 +849,17 @@ namespace Content.Server.Botany.Components else if (!Dead) { message.AddMarkup(Loc.GetString("plant-holder-component-something-already-growing-message", - ("seedName", Seed.DisplayName), - ("toBeForm", Seed.DisplayName.EndsWith('s') ? "are" : "is")) + ("seedName", Seed.DisplayName), + ("toBeForm", Seed.DisplayName.EndsWith('s') ? "are" : "is")) + "\n"); - if(Health <= Seed.Endurance / 2) - message.AddMarkup(Loc.GetString("plant-holder-component-something-already-growing-low-health-message", - ("healthState", Loc.GetString(Age > Seed.Lifespan ? "plant-holder-component-plant-old-adjective" : - "plant-holder-component-plant-unhealthy-adjective"))) + if (Health <= Seed.Endurance / 2) + message.AddMarkup(Loc.GetString( + "plant-holder-component-something-already-growing-low-health-message", + ("healthState", + Loc.GetString(Age > Seed.Lifespan + ? "plant-holder-component-plant-old-adjective" + : "plant-holder-component-plant-unhealthy-adjective"))) + "\n"); } else @@ -849,30 +867,32 @@ namespace Content.Server.Botany.Components message.AddMarkup(Loc.GetString("plant-holder-component-dead-plant-matter-message") + "\n"); } - if(WeedLevel >= 5) + if (WeedLevel >= 5) message.AddMarkup(Loc.GetString("plant-holder-component-weed-high-level-message") + "\n"); - if(PestLevel >= 5) + if (PestLevel >= 5) message.AddMarkup(Loc.GetString("plant-holder-component-pest-high-level-message") + "\n"); - message.AddMarkup(Loc.GetString($"plant-holder-component-water-level-message",("waterLevel", (int)WaterLevel)) + "\n"); - message.AddMarkup(Loc.GetString($"plant-holder-component-nutrient-level-message", ("nutritionLevel", (int)NutritionLevel)) + "\n"); + message.AddMarkup(Loc.GetString($"plant-holder-component-water-level-message", + ("waterLevel", (int) WaterLevel)) + "\n"); + message.AddMarkup(Loc.GetString($"plant-holder-component-nutrient-level-message", + ("nutritionLevel", (int) NutritionLevel)) + "\n"); if (DrawWarnings) { - if(Toxins > 40f) + if (Toxins > 40f) message.AddMarkup(Loc.GetString("plant-holder-component-toxins-high-warning") + "\n"); - if(ImproperLight) + if (ImproperLight) message.AddMarkup(Loc.GetString("plant-holder-component-light-improper-warning") + "\n"); - if(ImproperHeat) + if (ImproperHeat) message.AddMarkup(Loc.GetString("plant-holder-component-heat-improper-warning") + "\n"); - if(ImproperPressure) + if (ImproperPressure) message.AddMarkup(Loc.GetString("plant-holder-component-pressure-improper-warning") + "\n"); - if(_missingGas > 0) + if (_missingGas > 0) message.AddMarkup(Loc.GetString("plant-holder-component-gas-missing-warning") + "\n"); } } diff --git a/Content.Server/Botany/Components/ProduceComponent.cs b/Content.Server/Botany/Components/ProduceComponent.cs index 88e456bb7c..520bf26014 100644 --- a/Content.Server/Botany/Components/ProduceComponent.cs +++ b/Content.Server/Botany/Components/ProduceComponent.cs @@ -1,8 +1,9 @@ -using Content.Server.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.IoC; +using Robust.Shared.Log; using Robust.Shared.Maths; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; @@ -15,9 +16,9 @@ namespace Content.Server.Botany.Components public class ProduceComponent : Component, ISerializationHooks { public override string Name => "Produce"; + public const string SolutionName = "produce"; - [DataField("seed")] - private string? _seedName; + [DataField("seed")] private string? _seedName; [ViewVariables] public Seed? Seed @@ -39,18 +40,22 @@ namespace Content.Server.Botany.Components sprite.LayerSetState(0, Seed.PlantIconState); } - var solutionContainer = Owner.EnsureComponent(); - - solutionContainer.RemoveAllSolution(); + EntitySystem.Get().RemoveAllSolution(Owner.Uid); + var solutionContainer = EntitySystem.Get().EnsureSolution(Owner, SolutionName); + if (solutionContainer == null) + { + Logger.Warning($"No solution container found in {nameof(ProduceComponent)}."); + return; + } foreach (var (chem, quantity) in Seed.Chemicals) { var amount = ReagentUnit.New(quantity.Min); - if(quantity.PotencyDivisor > 0 && Potency > 0) - amount += ReagentUnit.New(Potency/quantity.PotencyDivisor); + if (quantity.PotencyDivisor > 0 && Potency > 0) + amount += ReagentUnit.New(Potency / quantity.PotencyDivisor); amount = ReagentUnit.New((int) MathHelper.Clamp(amount.Float(), quantity.Min, quantity.Max)); solutionContainer.MaxVolume += amount; - solutionContainer.Solution.AddReagent(chem, amount); + solutionContainer.AddReagent(chem, amount); } } } diff --git a/Content.Server/Chemistry/Components/ChemMasterComponent.cs b/Content.Server/Chemistry/Components/ChemMasterComponent.cs index 940135fb21..8589397e88 100644 --- a/Content.Server/Chemistry/Components/ChemMasterComponent.cs +++ b/Content.Server/Chemistry/Components/ChemMasterComponent.cs @@ -6,10 +6,10 @@ using Content.Server.Items; using Content.Server.Power.Components; using Content.Server.UserInterface; using Content.Shared.ActionBlocker; -using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using Content.Shared.Interaction; using Content.Shared.Notification.Managers; using Content.Shared.Random.Helpers; @@ -35,19 +35,31 @@ namespace Content.Server.Chemistry.Components [RegisterComponent] [ComponentReference(typeof(IActivate))] [ComponentReference(typeof(IInteractUsing))] - public class ChemMasterComponent : SharedChemMasterComponent, IActivate, IInteractUsing, ISolutionChange + public class ChemMasterComponent : SharedChemMasterComponent, IActivate, IInteractUsing { - [ViewVariables] private ContainerSlot _beakerContainer = default!; - [ViewVariables] private bool HasBeaker => _beakerContainer.ContainedEntity != null; - [ViewVariables] private bool _bufferModeTransfer = true; + [ViewVariables] + private ContainerSlot _beakerContainer = default!; - [ViewVariables] private bool Powered => !Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || receiver.Powered; + [ViewVariables] + private bool HasBeaker => _beakerContainer.ContainedEntity != null; - [ViewVariables] private readonly Solution BufferSolution = new(); + [ViewVariables] + private bool _bufferModeTransfer = true; - [ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(ChemMasterUiKey.Key); + [ViewVariables] + private bool Powered => !Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || receiver.Powered; + + [ViewVariables] + private Solution BufferSolution => _bufferSolution ??= EntitySystem.Get().EnsureSolution(Owner, SolutionName); + + private Solution? _bufferSolution; + + [ViewVariables] + private BoundUserInterface? UserInterface => Owner.GetUIOrNull(ChemMasterUiKey.Key); + + [DataField("clickSound")] + private SoundSpecifier _clickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg"); - [DataField("clickSound")] private SoundSpecifier _clickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg"); /// /// Called once per instance of this component. Gets references to any other components needed @@ -65,8 +77,7 @@ namespace Content.Server.Chemistry.Components _beakerContainer = ContainerHelpers.EnsureContainer(Owner, $"{Name}-reagentContainerContainer"); - //BufferSolution = Owner.BufferSolution - BufferSolution.RemoveAllSolution(); + _bufferSolution = EntitySystem.Get().EnsureSolution(Owner, SolutionName); UpdateUserInterface(); } @@ -76,13 +87,13 @@ namespace Content.Server.Chemistry.Components base.HandleMessage(message, component); switch (message) { - case PowerChangedMessage powerChanged: - OnPowerChanged(powerChanged); + case PowerChangedMessage: + OnPowerChanged(); break; } } - private void OnPowerChanged(PowerChangedMessage e) + private void OnPowerChanged() { UpdateUserInterface(); } @@ -140,6 +151,7 @@ namespace Content.Server.Chemistry.Components /// Checks whether the player entity is able to use the chem master. /// /// The player entity. + /// whether the device requires power /// Returns true if the entity can use the chem master, and false if it cannot. private bool PlayerCanUseChemMaster(IEntity? playerEntity, bool needsPower = true) { @@ -166,25 +178,30 @@ namespace Content.Server.Chemistry.Components private ChemMasterBoundUserInterfaceState GetUserInterfaceState() { var beaker = _beakerContainer.ContainedEntity; - if (beaker == null) + EntitySystem.Get().TryGetSolution(beaker, SolutionName, out var beakerSolution); + // TODO this is just a guess + if (beaker == null || beakerSolution == null) { return new ChemMasterBoundUserInterfaceState(Powered, false, ReagentUnit.New(0), ReagentUnit.New(0), - "", Owner.Name, new List(), BufferSolution.Contents, _bufferModeTransfer, BufferSolution.TotalVolume); + "", Owner.Name, new List(), BufferSolution.Contents, _bufferModeTransfer, + BufferSolution.TotalVolume); } - var solution = beaker.GetComponent(); - return new ChemMasterBoundUserInterfaceState(Powered, true, solution.CurrentVolume, solution.MaxVolume, - beaker.Name, Owner.Name, solution.ReagentList, BufferSolution.Contents, _bufferModeTransfer, BufferSolution.TotalVolume); + + return new ChemMasterBoundUserInterfaceState(Powered, true, beakerSolution.CurrentVolume, + beakerSolution.MaxVolume, + beaker.Name, Owner.Name, beakerSolution.Contents, BufferSolution.Contents, _bufferModeTransfer, + BufferSolution.TotalVolume); } - private void UpdateUserInterface() + public void UpdateUserInterface() { var state = GetUserInterfaceState(); UserInterface?.SetState(state); } /// - /// If this component contains an entity with a , eject it. + /// If this component contains an entity with a , eject it. /// Tries to eject into user's hands first, then ejects onto chem master if both hands are full. /// private void TryEject(IEntity user) @@ -194,13 +211,14 @@ namespace Content.Server.Chemistry.Components var beaker = _beakerContainer.ContainedEntity; - if(beaker is null) + if (beaker is null) return; _beakerContainer.Remove(beaker); UpdateUserInterface(); - if(!user.TryGetComponent(out var hands) || !beaker.TryGetComponent(out var item)) + if (!user.TryGetComponent(out var hands) || + !beaker.TryGetComponent(out var item)) return; if (hands.CanPutInHand(item)) hands.PutInHand(item); @@ -211,10 +229,12 @@ namespace Content.Server.Chemistry.Components if (!HasBeaker && _bufferModeTransfer) return; var beaker = _beakerContainer.ContainedEntity; - if(beaker is null) + if (beaker is null) + return; + + if (!EntitySystem.Get().TryGetSolution(beaker, SolutionName, out var beakerSolution)) return; - var beakerSolution = beaker.GetComponent(); if (isBuffer) { foreach (var reagent in BufferSolution.Contents) @@ -222,30 +242,33 @@ namespace Content.Server.Chemistry.Components if (reagent.ReagentId == id) { ReagentUnit actualAmount; - if (amount == ReagentUnit.New(-1)) //amount is ReagentUnit.New(-1) when the client sends a message requesting to remove all solution from the container + if ( + amount == ReagentUnit + .New(-1)) //amount is ReagentUnit.New(-1) when the client sends a message requesting to remove all solution from the container { - actualAmount = ReagentUnit.Min(reagent.Quantity, beakerSolution.EmptyVolume); + actualAmount = ReagentUnit.Min(reagent.Quantity, beakerSolution.AvailableVolume); } else { - actualAmount = ReagentUnit.Min(reagent.Quantity, amount, beakerSolution.EmptyVolume); + actualAmount = ReagentUnit.Min(reagent.Quantity, amount, beakerSolution.AvailableVolume); } BufferSolution.RemoveReagent(id, actualAmount); if (_bufferModeTransfer) { - beakerSolution.TryAddReagent(id, actualAmount, out var _); + EntitySystem.Get() + .TryAddReagent(beaker.Uid, beakerSolution, id, actualAmount, out var _); // beakerSolution.Solution.AddReagent(id, actualAmount); } + break; } - } } else { - foreach (var reagent in beakerSolution.Solution.Contents) + foreach (var reagent in beakerSolution.Contents) { if (reagent.ReagentId == id) { @@ -258,7 +281,8 @@ namespace Content.Server.Chemistry.Components { actualAmount = ReagentUnit.Min(reagent.Quantity, amount); } - beakerSolution.TryRemoveReagent(id, actualAmount); + + EntitySystem.Get().TryRemoveReagent(beaker.Uid, beakerSolution, id, actualAmount); BufferSolution.AddReagent(id, actualAmount); break; } @@ -285,9 +309,9 @@ namespace Content.Server.Chemistry.Components var bottle = Owner.EntityManager.SpawnEntity("ChemistryEmptyBottle01", Owner.Transform.Coordinates); var bufferSolution = BufferSolution.SplitSolution(actualVolume); + var bottleSolution = EntitySystem.Get().EnsureSolution(bottle, "bottle"); - bottle.TryGetComponent(out var bottleSolution); - bottleSolution?.TryAddSolution(bufferSolution); + EntitySystem.Get().TryAddSolution(bottle.Uid, bottleSolution, bufferSolution); //Try to give them the bottle if (user.TryGetComponent(out var hands) && @@ -305,7 +329,6 @@ namespace Content.Server.Chemistry.Components //Give it an offset bottle.RandomOffset(0.2f); } - } else //Pills { @@ -320,8 +343,8 @@ namespace Content.Server.Chemistry.Components var bufferSolution = BufferSolution.SplitSolution(actualVolume); - pill.TryGetComponent(out var pillSolution); - pillSolution?.TryAddSolution(bufferSolution); + var pillSolution = EntitySystem.Get().EnsureSolution(pill, "pill"); + EntitySystem.Get().TryAddSolution(pill.Uid, pillSolution, bufferSolution); //Try to give them the bottle if (user.TryGetComponent(out var hands) && @@ -332,7 +355,6 @@ namespace Content.Server.Chemistry.Components hands.PutInHand(item); continue; } - } //Put it on the floor @@ -371,7 +393,7 @@ namespace Content.Server.Chemistry.Components /// /// Called when you click the owner entity with something in your active hand. If the entity in your hand - /// contains a , if you have hands, and if the chem master doesn't already + /// contains a , if you have hands, and if the chem master doesn't already /// hold a container, it will be added to the chem master. /// /// Data relevant to the event such as the actor which triggered it. @@ -391,16 +413,18 @@ namespace Content.Server.Chemistry.Components } var activeHandEntity = hands.GetActiveHand.Owner; - if (activeHandEntity.TryGetComponent(out var solution)) + if (activeHandEntity.HasComponent()) { if (HasBeaker) { Owner.PopupMessage(args.User, Loc.GetString("chem-master-component-has-beaker-already-message")); } - else if (!solution.CanUseWithChemDispenser) + else if (!activeHandEntity.HasComponent()) { //If it can't fit in the chem master, don't put it in. For example, buckets and mop buckets can't fit. - Owner.PopupMessage(args.User, Loc.GetString("chem-master-component-container-too-large-message",("container", activeHandEntity))); + Owner.PopupMessage(args.User, + Loc.GetString("chem-master-component-container-too-large-message", + ("container", activeHandEntity))); } else { @@ -410,14 +434,13 @@ namespace Content.Server.Chemistry.Components } else { - Owner.PopupMessage(args.User, Loc.GetString("chem-master-component-cannot-put-entity-message", ("entity", activeHandEntity))); + Owner.PopupMessage(args.User, + Loc.GetString("chem-master-component-cannot-put-entity-message", ("entity", activeHandEntity))); } return true; } - void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) => UpdateUserInterface(); - private void ClickSound() { SoundSystem.Play(Filter.Pvs(Owner), _clickSound.GetSound(), Owner, AudioParams.Default.WithVolume(-2f)); @@ -446,6 +469,5 @@ namespace Content.Server.Chemistry.Components component.TryEject(user); } } - } } diff --git a/Content.Server/Chemistry/Components/FoamSolutionAreaEffectComponent.cs b/Content.Server/Chemistry/Components/FoamSolutionAreaEffectComponent.cs index ad3d1d821b..9533834510 100644 --- a/Content.Server/Chemistry/Components/FoamSolutionAreaEffectComponent.cs +++ b/Content.Server/Chemistry/Components/FoamSolutionAreaEffectComponent.cs @@ -1,6 +1,8 @@ using Content.Server.Body.Circulatory; using Content.Server.Inventory.Components; using Content.Server.Items; +using Content.Shared.Chemistry; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; using Content.Shared.Foam; using Content.Shared.Inventory; @@ -15,21 +17,22 @@ namespace Content.Server.Chemistry.Components public class FoamSolutionAreaEffectComponent : SolutionAreaEffectComponent { public override string Name => "FoamSolutionAreaEffect"; + public static string SolutionName = "foam"; [DataField("foamedMetalPrototype")] private string? _foamedMetalPrototype; protected override void UpdateVisuals() { if (Owner.TryGetComponent(out AppearanceComponent? appearance) && - SolutionContainerComponent != null) + EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution)) { - appearance.SetData(FoamVisuals.Color, SolutionContainerComponent.Color.WithAlpha(0.80f)); + appearance.SetData(FoamVisuals.Color, solution.Color.WithAlpha(0.80f)); } } protected override void ReactWithEntity(IEntity entity, double solutionFraction) { - if (SolutionContainerComponent == null) + if (!EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution)) return; if (!entity.TryGetComponent(out BloodstreamComponent? bloodstream)) @@ -53,8 +56,9 @@ namespace Content.Server.Chemistry.Components } } - var cloneSolution = SolutionContainerComponent.Solution.Clone(); - var transferAmount = ReagentUnit.Min(cloneSolution.TotalVolume * solutionFraction * (1 - protection), bloodstream.EmptyVolume); + var cloneSolution = solution.Clone(); + var transferAmount = ReagentUnit.Min(cloneSolution.TotalVolume * solutionFraction * (1 - protection), + bloodstream.EmptyVolume); var transferSolution = cloneSolution.SplitSolution(transferAmount); bloodstream.TryTransferSolution(transferSolution); @@ -68,12 +72,14 @@ namespace Content.Server.Chemistry.Components { appearance.SetData(FoamVisuals.State, true); } + Owner.SpawnTimer(600, () => { if (!string.IsNullOrEmpty(_foamedMetalPrototype)) { Owner.EntityManager.SpawnEntity(_foamedMetalPrototype, Owner.Transform.Coordinates); } + Owner.QueueDelete(); }); } diff --git a/Content.Server/Chemistry/Components/HyposprayComponent.cs b/Content.Server/Chemistry/Components/HyposprayComponent.cs index 63f3520283..96b648a354 100644 --- a/Content.Server/Chemistry/Components/HyposprayComponent.cs +++ b/Content.Server/Chemistry/Components/HyposprayComponent.cs @@ -1,8 +1,8 @@ using Content.Server.Interaction.Components; using Content.Server.MobState.States; using Content.Server.Weapon.Melee; -using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; using Content.Shared.Notification.Managers; using Content.Shared.Sound; @@ -18,7 +18,7 @@ using Robust.Shared.ViewVariables; namespace Content.Server.Chemistry.Components { [RegisterComponent] - public sealed class HyposprayComponent : SharedHyposprayComponent, ISolutionChange + public sealed class HyposprayComponent : SharedHyposprayComponent { [DataField("ClumsyFailChance")] [ViewVariables(VVAccess.ReadWrite)] @@ -31,8 +31,6 @@ namespace Content.Server.Chemistry.Components [DataField("InjectSound")] private SoundSpecifier _injectSound = new SoundPathSpecifier("/Audio/Items/hypospray.ogg"); - [ComponentDependency] private readonly SolutionContainerComponent? _solution = default!; - protected override void Initialize() { base.Initialize(); @@ -57,13 +55,24 @@ namespace Content.Server.Chemistry.Components target = user; } - if (_solution == null || _solution.CurrentVolume == 0) + var solutionsSys = EntitySystem.Get(); + solutionsSys.TryGetSolution(Owner, SolutionName, out var hypoSpraySolution); + + if (hypoSpraySolution == null || hypoSpraySolution.CurrentVolume == 0) { user.PopupMessageCursor(Loc.GetString("hypospray-component-empty-message")); return true; } - user.PopupMessage(Loc.GetString(msgFormat ?? "hypospray-component-inject-other-message",("other", target))); + if (!solutionsSys.TryGetInjectableSolution(target.Uid, out var targetSolution)) + { + user.PopupMessage(user, + Loc.GetString("hypospray-cant-inject", ("target", target))); + return false; + } + + user.PopupMessage(Loc.GetString(msgFormat ?? "hypospray-component-inject-other-message", + ("other", target))); if (target != user) { target.PopupMessage(Loc.GetString("hypospray-component-feel-prick-message")); @@ -74,19 +83,21 @@ namespace Content.Server.Chemistry.Components SoundSystem.Play(Filter.Pvs(user), _injectSound.GetSound(), user); - var targetSolution = target.GetComponent(); - // Get transfer amount. May be smaller than _transferAmount if not enough room - var realTransferAmount = ReagentUnit.Min(TransferAmount, targetSolution.EmptyVolume); + var realTransferAmount = ReagentUnit.Min(TransferAmount, targetSolution.AvailableVolume); if (realTransferAmount <= 0) { - user.PopupMessage(user, Loc.GetString("hypospray-component-transfer-already-full-message ",("owner", targetSolution.Owner))); + user.PopupMessage(user, + Loc.GetString("hypospray-component-transfer-already-full-message", + ("owner", target))); return true; } // Move units from attackSolution to targetSolution - var removedSolution = _solution.SplitSolution(realTransferAmount); + var removedSolution = + EntitySystem.Get() + .SplitSolution(Owner.Uid, hypoSpraySolution, realTransferAmount); if (!targetSolution.CanAddSolution(removedSolution)) { @@ -95,29 +106,26 @@ namespace Content.Server.Chemistry.Components removedSolution.DoEntityReaction(target, ReactionMethod.Injection); - targetSolution.TryAddSolution(removedSolution); + EntitySystem.Get().TryAddSolution(target.Uid, targetSolution, removedSolution); static bool EligibleEntity(IEntity entity) { // TODO: Does checking for BodyComponent make sense as a "can be hypospray'd" tag? // In SS13 the hypospray ONLY works on mobs, NOT beakers or anything else. - return entity.HasComponent() && entity.HasComponent(); + + return entity.HasComponent() + && entity.HasComponent(); } return true; } - void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) - { - Dirty(); - } - public override ComponentState GetComponentState(ICommonSession player) { - if (_solution == null) - return new HyposprayComponentState(ReagentUnit.Zero, ReagentUnit.Zero); - - return new HyposprayComponentState(_solution.CurrentVolume, _solution.MaxVolume); + var solutionSys = Owner.EntityManager.EntitySysManager.GetEntitySystem(); + return solutionSys.TryGetSolution(Owner, SolutionName, out var solution) + ? new HyposprayComponentState(solution.CurrentVolume, solution.MaxVolume) + : new HyposprayComponentState(ReagentUnit.Zero, ReagentUnit.Zero); } } } diff --git a/Content.Server/Chemistry/Components/InjectorComponent.cs b/Content.Server/Chemistry/Components/InjectorComponent.cs index cd404aaa0d..4f6fa6e531 100644 --- a/Content.Server/Chemistry/Components/InjectorComponent.cs +++ b/Content.Server/Chemistry/Components/InjectorComponent.cs @@ -1,10 +1,11 @@ using System; using System.Threading.Tasks; using Content.Server.Body.Circulatory; -using Content.Shared.Chemistry; +using Content.Shared.Body.Networks; using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution.Components; using Content.Shared.Interaction; using Content.Shared.Interaction.Helpers; using Content.Shared.Notification.Managers; @@ -22,8 +23,10 @@ namespace Content.Server.Chemistry.Components /// containers, and can directly inject into a mobs bloodstream. /// [RegisterComponent] - public class InjectorComponent : SharedInjectorComponent, IAfterInteract, IUse, ISolutionChange + public class InjectorComponent : SharedInjectorComponent, IAfterInteract, IUse { + public const string SolutionName = "injector"; + /// /// Whether or not the injector is able to draw from containers or if it's a single use /// device that can only inject. @@ -109,47 +112,46 @@ namespace Content.Server.Chemistry.Components if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return false; + var solutionsSys = EntitySystem.Get(); //Make sure we have the attacking entity - if (eventArgs.Target == null || !Owner.HasComponent()) + if (eventArgs.Target == null || !Owner.HasComponent()) { return false; } var targetEntity = eventArgs.Target; + // Handle injecting/drawing for solutions - if (targetEntity.TryGetComponent(out var targetSolution)) + if (ToggleState == InjectorToggleMode.Inject) { - if (ToggleState == InjectorToggleMode.Inject) + if (solutionsSys.TryGetInjectableSolution(targetEntity.Uid, out var injectableSolution)) { - if (targetSolution.CanInject) - { - TryInject(targetSolution, eventArgs.User); - } - else - { - eventArgs.User.PopupMessage(eventArgs.User, - Loc.GetString("injector-component-cannot-transfer-message", ("owner", targetSolution.Owner))); - } + TryInject(targetEntity, injectableSolution, eventArgs.User); } - else if (ToggleState == InjectorToggleMode.Draw) + else if (targetEntity.TryGetComponent(out BloodstreamComponent? bloodstream)) { - if (targetSolution.CanDraw) - { - TryDraw(targetSolution, eventArgs.User); - } - else - { - eventArgs.User.PopupMessage(eventArgs.User, - Loc.GetString("injector-component-cannot-draw-message", ("owner", targetSolution.Owner))); - } + TryInjectIntoBloodstream(bloodstream, eventArgs.User); + } + else + { + eventArgs.User.PopupMessage(eventArgs.User, + Loc.GetString("injector-component-cannot-transfer-message", + ("owner", eventArgs.User))); } } - // Handle injecting into bloodstream - else if (targetEntity.TryGetComponent(out BloodstreamComponent? bloodstream) && - ToggleState == InjectorToggleMode.Inject) + else if (ToggleState == InjectorToggleMode.Draw) { - TryInjectIntoBloodstream(bloodstream, eventArgs.User); + if (solutionsSys.TryGetDrawableSolution(targetEntity, out var drawableSolution)) + { + TryDraw(targetEntity, drawableSolution, eventArgs.User); + } + else + { + eventArgs.User.PopupMessage(eventArgs.User, + Loc.GetString("injector-component-cannot-draw-message", + ("owner", eventArgs.User))); + } } return true; @@ -168,10 +170,10 @@ namespace Content.Server.Chemistry.Components private void TryInjectIntoBloodstream(BloodstreamComponent targetBloodstream, IEntity user) { - if (!Owner.TryGetComponent(out SolutionContainerComponent? solution) || solution.CurrentVolume == 0) - { + if (!EntitySystem.Get() + .TryGetSolution(user, SharedBloodstreamComponent.DefaultSolutionName, out var bloodstream) + || bloodstream.CurrentVolume == 0) return; - } // Get transfer amount. May be smaller than _transferAmount if not enough room var realTransferAmount = ReagentUnit.Min(_transferAmount, targetBloodstream.EmptyVolume); @@ -179,61 +181,65 @@ namespace Content.Server.Chemistry.Components if (realTransferAmount <= 0) { Owner.PopupMessage(user, - Loc.GetString("injector-component-cannot-inject-message",("owner", targetBloodstream.Owner))); + Loc.GetString("injector-component-cannot-inject-message", ("owner", targetBloodstream.Owner))); return; } // Move units from attackSolution to targetSolution - var removedSolution = solution.SplitSolution(realTransferAmount); + var removedSolution = + EntitySystem.Get().SplitSolution(user.Uid, bloodstream, realTransferAmount); - if (!solution.CanAddSolution(removedSolution)) + if (!bloodstream.CanAddSolution(removedSolution)) { return; } // TODO: Account for partial transfer. + var bloodsStreamEntity = Owner.EntityManager.GetEntity(user.Uid); + removedSolution.DoEntityReaction(bloodsStreamEntity, ReactionMethod.Injection); - removedSolution.DoEntityReaction(solution.Owner, ReactionMethod.Injection); - - solution.TryAddSolution(removedSolution); + EntitySystem.Get().TryAddSolution(user.Uid, bloodstream, removedSolution); removedSolution.DoEntityReaction(targetBloodstream.Owner, ReactionMethod.Injection); Owner.PopupMessage(user, Loc.GetString("injector-component-inject-success-message", - ("amount", removedSolution.TotalVolume), - ("target", targetBloodstream.Owner))); + ("amount", removedSolution.TotalVolume), + ("target", targetBloodstream.Owner))); Dirty(); AfterInject(); } - private void TryInject(ISolutionInteractionsComponent targetSolution, IEntity user) + private void TryInject(IEntity targetEntity, Solution targetSolution, IEntity user) { - if (!Owner.TryGetComponent(out SolutionContainerComponent? solution) || solution.CurrentVolume == 0) + if (!EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution) + || solution.CurrentVolume == 0) { return; } // Get transfer amount. May be smaller than _transferAmount if not enough room - var realTransferAmount = ReagentUnit.Min(_transferAmount, targetSolution.InjectSpaceAvailable); + var realTransferAmount = ReagentUnit.Min(_transferAmount, targetSolution.AvailableVolume); if (realTransferAmount <= 0) { - Owner.PopupMessage(user, Loc.GetString("injector-component-target-already-full-message", ("target", targetSolution.Owner))); + Owner.PopupMessage(user, + Loc.GetString("injector-component-target-already-full-message", ("target", targetEntity))); return; } // Move units from attackSolution to targetSolution - var removedSolution = solution.SplitSolution(realTransferAmount); + var removedSolution = EntitySystem.Get().SplitSolution(Owner.Uid, solution, realTransferAmount); - removedSolution.DoEntityReaction(targetSolution.Owner, ReactionMethod.Injection); + removedSolution.DoEntityReaction(targetEntity, ReactionMethod.Injection); - targetSolution.Inject(removedSolution); + EntitySystem.Get() + .Inject(targetEntity.Uid, targetSolution, removedSolution); Owner.PopupMessage(user, Loc.GetString("injector-component-transfer-success-message", - ("amount", removedSolution.TotalVolume), - ("target", targetSolution.Owner))); + ("amount", removedSolution.TotalVolume), + ("target", targetEntity))); Dirty(); AfterInject(); } @@ -241,15 +247,27 @@ namespace Content.Server.Chemistry.Components private void AfterInject() { // Automatically set syringe to draw after completely draining it. - if (Owner.GetComponent().CurrentVolume == 0) + if (EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution) + && solution.CurrentVolume == 0) { ToggleState = InjectorToggleMode.Draw; } } - private void TryDraw(ISolutionInteractionsComponent targetSolution, IEntity user) + private void AfterDraw() { - if (!Owner.TryGetComponent(out SolutionContainerComponent? solution) || solution.EmptyVolume == 0) + // Automatically set syringe to inject after completely filling it. + if (EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution) + && solution.AvailableVolume == 0) + { + ToggleState = InjectorToggleMode.Inject; + } + } + + private void TryDraw(IEntity targetEntity, Solution targetSolution, IEntity user) + { + if (!EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution) + || solution.AvailableVolume == 0) { return; } @@ -259,43 +277,33 @@ namespace Content.Server.Chemistry.Components if (realTransferAmount <= 0) { - Owner.PopupMessage(user, Loc.GetString("injector-component-target-is-empty-message",("target", targetSolution.Owner))); + Owner.PopupMessage(user, + Loc.GetString("injector-component-target-is-empty-message", ("target", targetEntity))); return; } // Move units from attackSolution to targetSolution - var removedSolution = targetSolution.Draw(realTransferAmount); + var removedSolution = EntitySystem.Get() + .Draw(targetEntity.Uid, targetSolution, realTransferAmount); - if (!solution.TryAddSolution(removedSolution)) + if (!EntitySystem.Get().TryAddSolution(targetEntity.Uid, solution, removedSolution)) { return; } Owner.PopupMessage(user, Loc.GetString("injector-component-draw-success-message", - ("amount", removedSolution.TotalVolume), - ("target", targetSolution.Owner))); + ("amount", removedSolution.TotalVolume), + ("target", targetEntity))); Dirty(); AfterDraw(); } - private void AfterDraw() - { - // Automatically set syringe to inject after completely filling it. - if (Owner.GetComponent().EmptyVolume == 0) - { - ToggleState = InjectorToggleMode.Inject; - } - } - - void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) - { - Dirty(); - } public override ComponentState GetComponentState(ICommonSession player) { - Owner.TryGetComponent(out SolutionContainerComponent? solution); + Owner.EntityManager.EntitySysManager.GetEntitySystem() + .TryGetSolution(Owner, SolutionName, out var solution); var currentVolume = solution?.CurrentVolume ?? ReagentUnit.Zero; var maxVolume = solution?.MaxVolume ?? ReagentUnit.Zero; diff --git a/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs b/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs index 3752c10a14..b4fb8c0858 100644 --- a/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs +++ b/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs @@ -7,10 +7,10 @@ using Content.Server.Items; using Content.Server.Power.Components; using Content.Server.UserInterface; using Content.Shared.ActionBlocker; -using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Dispenser; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using Content.Shared.Interaction; using Content.Shared.Notification.Managers; using Content.Shared.Sound; @@ -38,9 +38,10 @@ namespace Content.Server.Chemistry.Components [RegisterComponent] [ComponentReference(typeof(IActivate))] [ComponentReference(typeof(IInteractUsing))] - public class ReagentDispenserComponent : SharedReagentDispenserComponent, IActivate, IInteractUsing, ISolutionChange + public class ReagentDispenserComponent : SharedReagentDispenserComponent, IActivate, IInteractUsing { private static ReagentInventoryComparer _comparer = new(); + public static string SolutionName = "reagent"; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; @@ -52,9 +53,20 @@ namespace Content.Server.Chemistry.Components [ViewVariables] private bool HasBeaker => _beakerContainer.ContainedEntity != null; [ViewVariables] private ReagentUnit _dispenseAmount = ReagentUnit.New(10); - [UsedImplicitly] [ViewVariables] private SolutionContainerComponent? Solution => _beakerContainer.ContainedEntity?.GetComponent(); - [ViewVariables] private bool Powered => !Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || receiver.Powered; + [UsedImplicitly] + [ViewVariables] + private Solution? Solution + { + get + { + EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution); + return solution; + } + } + + [ViewVariables] + private bool Powered => !Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || receiver.Powered; [ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(ReagentDispenserUiKey.Key); @@ -134,7 +146,7 @@ namespace Content.Server.Chemistry.Components _ => true, }; - if(!PlayerCanUseDispenser(obj.Session.AttachedEntity, needsPower)) + if (!PlayerCanUseDispenser(obj.Session.AttachedEntity, needsPower)) return; switch (msg.Button) @@ -216,25 +228,27 @@ namespace Content.Server.Chemistry.Components private ReagentDispenserBoundUserInterfaceState GetUserInterfaceState() { var beaker = _beakerContainer.ContainedEntity; - if (beaker == null) + if (beaker == null || + !EntitySystem.Get().TryGetSolution(beaker, "beaker", out var solution)) { - return new ReagentDispenserBoundUserInterfaceState(Powered, false, ReagentUnit.New(0), ReagentUnit.New(0), + return new ReagentDispenserBoundUserInterfaceState(Powered, false, ReagentUnit.New(0), + ReagentUnit.New(0), string.Empty, Inventory, Owner.Name, null, _dispenseAmount); } - var solution = beaker.GetComponent(); - return new ReagentDispenserBoundUserInterfaceState(Powered, true, solution.CurrentVolume, solution.MaxVolume, - beaker.Name, Inventory, Owner.Name, solution.ReagentList.ToList(), _dispenseAmount); + return new ReagentDispenserBoundUserInterfaceState(Powered, true, solution.CurrentVolume, + solution.MaxVolume, + beaker.Name, Inventory, Owner.Name, solution.Contents.ToList(), _dispenseAmount); } - private void UpdateUserInterface() + public void UpdateUserInterface() { var state = GetUserInterfaceState(); UserInterface?.SetState(state); } /// - /// If this component contains an entity with a , eject it. + /// If this component contains an entity with a , eject it. /// Tries to eject into user's hands first, then ejects onto dispenser if both hands are full. /// private void TryEject(IEntity user) @@ -243,46 +257,48 @@ namespace Content.Server.Chemistry.Components return; var beaker = _beakerContainer.ContainedEntity; - if(beaker is null) + if (beaker is null) return; _beakerContainer.Remove(beaker); UpdateUserInterface(); - if(!user.TryGetComponent(out var hands) || !beaker.TryGetComponent(out var item)) + if (!user.TryGetComponent(out var hands) || + !beaker.TryGetComponent(out var item)) return; if (hands.CanPutInHand(item)) hands.PutInHand(item); } /// - /// If this component contains an entity with a , remove all of it's reagents / solutions. + /// If this component contains an entity with a , remove all of it's reagents / solutions. /// private void TryClear() { - if (!HasBeaker) return; - var solution = _beakerContainer.ContainedEntity?.GetComponent(); - if(solution is null) + if (!HasBeaker || + !EntitySystem.Get() + .TryGetSolution(_beakerContainer.ContainedEntity, "beaker", out var solution)) return; - solution.RemoveAllSolution(); + EntitySystem.Get().RemoveAllSolution(_beakerContainer.ContainedEntity!.Uid, solution); UpdateUserInterface(); } /// - /// If this component contains an entity with a , attempt to dispense the specified reagent to it. + /// If this component contains an entity with a , attempt to dispense the specified reagent to it. /// /// The index of the reagent in Inventory. private void TryDispense(int dispenseIndex) { if (!HasBeaker) return; - var solution = _beakerContainer.ContainedEntity?.GetComponent(); - if (solution is null) - return; + if (_beakerContainer.ContainedEntity == null + || !EntitySystem.Get() + .TryGetSolution(_beakerContainer.ContainedEntity, "beaker", out var solution)) return; - solution.TryAddReagent(Inventory[dispenseIndex].ID, _dispenseAmount, out _); + EntitySystem.Get() + .TryAddReagent(_beakerContainer.ContainedEntity.Uid, solution, Inventory[dispenseIndex].ID, _dispenseAmount, out _); UpdateUserInterface(); } @@ -313,7 +329,7 @@ namespace Content.Server.Chemistry.Components /// /// Called when you click the owner entity with something in your active hand. If the entity in your hand - /// contains a , if you have hands, and if the dispenser doesn't already + /// contains a , if you have hands, and if the dispenser doesn't already /// hold a container, it will be added to the dispenser. /// /// Data relevant to the event such as the actor which triggered it. @@ -328,18 +344,21 @@ namespace Content.Server.Chemistry.Components if (hands.GetActiveHand == null) { - Owner.PopupMessage(args.User, Loc.GetString("reagent-dispenser-component-interact-using-nothing-in-hands")); + Owner.PopupMessage(args.User, + Loc.GetString("reagent-dispenser-component-interact-using-nothing-in-hands")); return false; } + var solutionSys = EntitySystem.Get(); var activeHandEntity = hands.GetActiveHand.Owner; - if (activeHandEntity.TryGetComponent(out var solution)) + if (solutionSys.TryGetSolution(activeHandEntity, "beaker", out _)) { if (HasBeaker) { - Owner.PopupMessage(args.User, Loc.GetString("reagent-dispenser-component-has-container-already-message")); + Owner.PopupMessage(args.User, + Loc.GetString("reagent-dispenser-component-has-container-already-message")); } - else if ((solution.Capabilities & SolutionContainerCaps.FitsInDispenser) == 0) + else if (!solutionSys.HasFitsInDispenser(activeHandEntity)) { //If it can't fit in the dispenser, don't put it in. For example, buckets and mop buckets can't fit. Owner.PopupMessage(args.User, Loc.GetString("reagent-dispenser-component-cannot-fit-message")); @@ -352,14 +371,14 @@ namespace Content.Server.Chemistry.Components } else { - Owner.PopupMessage(args.User, Loc.GetString("reagent-dispenser-component-cannot-put-entity-message", ("entity", activeHandEntity))); + Owner.PopupMessage(args.User, + Loc.GetString("reagent-dispenser-component-cannot-put-entity-message", + ("entity", activeHandEntity))); } return true; } - void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) => UpdateUserInterface(); - private void ClickSound() { SoundSystem.Play(Filter.Pvs(Owner), _clickSound.GetSound(), Owner, AudioParams.Default.WithVolume(-2f)); diff --git a/Content.Server/Chemistry/Components/RehydratableComponent.cs b/Content.Server/Chemistry/Components/RehydratableComponent.cs index 6d900b26a5..291ae73481 100644 --- a/Content.Server/Chemistry/Components/RehydratableComponent.cs +++ b/Content.Server/Chemistry/Components/RehydratableComponent.cs @@ -1,8 +1,4 @@ -using Content.Server.Notification; -using Content.Shared.Chemistry; -using Content.Shared.Chemistry.Reagent; using Robust.Shared.GameObjects; -using Robust.Shared.Localization; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; @@ -13,44 +9,18 @@ namespace Content.Server.Chemistry.Components /// But specifically, this component deletes the entity and spawns in a new entity when the entity is exposed to a given reagent. /// [RegisterComponent] - [ComponentReference(typeof(ISolutionChange))] - public class RehydratableComponent : Component, ISolutionChange + public class RehydratableComponent : Component { public override string Name => "Rehydratable"; [ViewVariables] [DataField("catalyst")] - private string _catalystPrototype = "Water"; + internal string CatalystPrototype = "Water"; + [ViewVariables] [DataField("target")] - private string? _targetPrototype = default!; + internal string? TargetPrototype = default!; - private bool _expanding; - - void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) - { - var solution = eventArgs.Owner.GetComponent(); - if (solution.Solution.GetReagentQuantity(_catalystPrototype) > ReagentUnit.Zero) - { - Expand(); - } - } - - // Try not to make this public if you can help it. - private void Expand() - { - if (_expanding) - { - return; - } - _expanding = true; - Owner.PopupMessageEveryone(Loc.GetString("rehydratable-component-expands-message",("owner", Owner))); - if (!string.IsNullOrEmpty(_targetPrototype)) - { - var ent = Owner.EntityManager.SpawnEntity(_targetPrototype, Owner.Transform.Coordinates); - ent.Transform.AttachToGridOrMap(); - } - Owner.Delete(); - } + internal bool Expanding; } } diff --git a/Content.Server/Chemistry/Components/SmokeSolutionAreaEffectComponent.cs b/Content.Server/Chemistry/Components/SmokeSolutionAreaEffectComponent.cs index 461b9b25c1..162de541b4 100644 --- a/Content.Server/Chemistry/Components/SmokeSolutionAreaEffectComponent.cs +++ b/Content.Server/Chemistry/Components/SmokeSolutionAreaEffectComponent.cs @@ -1,7 +1,7 @@ -using System.Linq; -using Content.Server.Body.Circulatory; +using Content.Server.Body.Circulatory; using Content.Server.Body.Respiratory; using Content.Shared.Chemistry; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; using Content.Shared.Smoking; using Robust.Server.GameObjects; @@ -14,19 +14,20 @@ namespace Content.Server.Chemistry.Components public class SmokeSolutionAreaEffectComponent : SolutionAreaEffectComponent { public override string Name => "SmokeSolutionAreaEffect"; + public const string SolutionName = "smoke"; protected override void UpdateVisuals() { if (Owner.TryGetComponent(out AppearanceComponent? appearance) && - SolutionContainerComponent != null) + EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution)) { - appearance.SetData(SmokeVisuals.Color, SolutionContainerComponent.Color); + appearance.SetData(SmokeVisuals.Color, solution.Color); } } protected override void ReactWithEntity(IEntity entity, double solutionFraction) { - if (SolutionContainerComponent == null) + if (!EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution)) return; if (!entity.TryGetComponent(out BloodstreamComponent? bloodstream)) @@ -37,7 +38,7 @@ namespace Content.Server.Chemistry.Components return; var chemistry = EntitySystem.Get(); - var cloneSolution = SolutionContainerComponent.Solution.Clone(); + var cloneSolution = solution.Clone(); var transferAmount = ReagentUnit.Min(cloneSolution.TotalVolume * solutionFraction, bloodstream.EmptyVolume); var transferSolution = cloneSolution.SplitSolution(transferAmount); diff --git a/Content.Server/Chemistry/Components/SolutionAreaEffectComponent.cs b/Content.Server/Chemistry/Components/SolutionAreaEffectComponent.cs index 2932c2d1b0..801e5d791c 100644 --- a/Content.Server/Chemistry/Components/SolutionAreaEffectComponent.cs +++ b/Content.Server/Chemistry/Components/SolutionAreaEffectComponent.cs @@ -3,8 +3,9 @@ using System.Linq; using Content.Server.Atmos.Components; using Content.Server.Coordinates.Helpers; using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Log; @@ -20,10 +21,10 @@ namespace Content.Server.Chemistry.Components /// public abstract class SolutionAreaEffectComponent : Component { + public const string SolutionName = "solutionArea"; + [Dependency] protected readonly IMapManager MapManager = default!; [Dependency] protected readonly IPrototypeManager PrototypeManager = default!; - - [ComponentDependency] protected readonly SolutionContainerComponent? SolutionContainerComponent = default!; public int Amount { get; set; } public SolutionAreaEffectInceptionComponent? Inception { get; set; } @@ -67,10 +68,12 @@ namespace Content.Server.Chemistry.Components var coords = Owner.Transform.Coordinates; foreach (var neighbor in grid.GetInDir(coords, dir)) { - if (Owner.EntityManager.ComponentManager.TryGetComponent(neighbor, out SolutionAreaEffectComponent? comp) && comp.Inception == Inception) + if (Owner.EntityManager.ComponentManager.TryGetComponent(neighbor, + out SolutionAreaEffectComponent? comp) && comp.Inception == Inception) return; - if (Owner.EntityManager.ComponentManager.TryGetComponent(neighbor, out AirtightComponent? airtight) && airtight.AirBlocked) + if (Owner.EntityManager.ComponentManager.TryGetComponent(neighbor, + out AirtightComponent? airtight) && airtight.AirBlocked) return; } @@ -82,9 +85,9 @@ namespace Content.Server.Chemistry.Components return; } - if (SolutionContainerComponent != null) + if (EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution)) { - effectComponent.TryAddSolution(SolutionContainerComponent.Solution.Clone()); + effectComponent.TryAddSolution(solution.Clone()); } effectComponent.Amount = Amount - 1; @@ -95,7 +98,6 @@ namespace Content.Server.Chemistry.Components SpreadToDir(Direction.East); SpreadToDir(Direction.South); SpreadToDir(Direction.West); - } /// @@ -120,7 +122,7 @@ namespace Content.Server.Chemistry.Components /// with the other area effects from the inception. public void React(float averageExposures) { - if (SolutionContainerComponent == null) + if (!EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution)) return; var chemistry = EntitySystem.Get(); @@ -129,7 +131,7 @@ namespace Content.Server.Chemistry.Components var solutionFraction = 1 / Math.Floor(averageExposures); - foreach (var reagentQuantity in SolutionContainerComponent.ReagentList.ToArray()) + foreach (var reagentQuantity in solution.Contents) { if (reagentQuantity.Quantity == ReagentUnit.Zero) continue; var reagent = PrototypeManager.Index(reagentQuantity.ReagentId); @@ -140,7 +142,8 @@ namespace Content.Server.Chemistry.Components // Touch every entity on the tile foreach (var entity in tile.GetEntitiesInTileFast().ToArray()) { - chemistry.ReactionEntity(entity, ReactionMethod.Touch, reagent, reagentQuantity.Quantity * solutionFraction, SolutionContainerComponent.Solution); + chemistry.ReactionEntity(entity, ReactionMethod.Touch, reagent, + reagentQuantity.Quantity * solutionFraction, solution); } } @@ -157,13 +160,13 @@ namespace Content.Server.Chemistry.Components if (solution.TotalVolume == 0) return; - if (SolutionContainerComponent == null) + if (!EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solutionArea)) return; var addSolution = - solution.SplitSolution(ReagentUnit.Min(solution.TotalVolume, SolutionContainerComponent.EmptyVolume)); + solution.SplitSolution(ReagentUnit.Min(solution.TotalVolume, solutionArea.AvailableVolume)); - SolutionContainerComponent.TryAddSolution(addSolution); + EntitySystem.Get().TryAddSolution(Owner.Uid, solutionArea, addSolution); UpdateVisuals(); } diff --git a/Content.Server/Chemistry/Components/SolutionContainerComponent.cs b/Content.Server/Chemistry/Components/SolutionContainerComponent.cs deleted file mode 100644 index 33adde72b2..0000000000 --- a/Content.Server/Chemistry/Components/SolutionContainerComponent.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Content.Shared.Chemistry.Solution.Components; -using Robust.Shared.GameObjects; - -namespace Content.Server.Chemistry.Components -{ - [RegisterComponent] - [ComponentReference(typeof(SharedSolutionContainerComponent))] - [ComponentReference(typeof(ISolutionInteractionsComponent))] - public class SolutionContainerComponent : SharedSolutionContainerComponent - { - } -} diff --git a/Content.Server/Chemistry/Components/SolutionTransferComponent.cs b/Content.Server/Chemistry/Components/SolutionTransferComponent.cs index 421841fa67..d4bbd47846 100644 --- a/Content.Server/Chemistry/Components/SolutionTransferComponent.cs +++ b/Content.Server/Chemistry/Components/SolutionTransferComponent.cs @@ -3,12 +3,15 @@ using System.Threading.Tasks; using Content.Server.UserInterface; using Content.Shared.ActionBlocker; using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution.Components; using Content.Shared.Interaction; using Content.Shared.Interaction.Helpers; using Content.Shared.Notification.Managers; using Content.Shared.Verbs; +using Microsoft.EntityFrameworkCore.Metadata.Builders; using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.Localization; @@ -108,7 +111,8 @@ namespace Content.Server.Chemistry.Components var amount = Math.Clamp(sval, MinimumTransferAmount.Float(), MaximumTransferAmount.Float()); - serverMsg.Session.AttachedEntity?.PopupMessage(Loc.GetString("comp-solution-transfer-set-amount", ("amount", amount))); + serverMsg.Session.AttachedEntity?.PopupMessage(Loc.GetString("comp-solution-transfer-set-amount", + ("amount", amount))); SetTransferAmount(ReagentUnit.New(amount)); break; } @@ -116,50 +120,57 @@ namespace Content.Server.Chemistry.Components public void SetTransferAmount(ReagentUnit amount) { - amount = ReagentUnit.New(Math.Clamp(amount.Int(), MinimumTransferAmount.Int(), MaximumTransferAmount.Int())); + amount = ReagentUnit.New(Math.Clamp(amount.Int(), MinimumTransferAmount.Int(), + MaximumTransferAmount.Int())); TransferAmount = amount; } async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { + var solutionsSys = EntitySystem.Get(); + if (!eventArgs.InRangeUnobstructed() || eventArgs.Target == null) return false; - if (!Owner.TryGetComponent(out ISolutionInteractionsComponent? ownerSolution)) + if (!Owner.HasComponent()) return false; - var target = eventArgs.Target; - if (!target.TryGetComponent(out ISolutionInteractionsComponent? targetSolution)) + var target = eventArgs.Target!; + if (!target.HasComponent()) { return false; } + if (CanReceive && target.TryGetComponent(out ReagentTankComponent? tank) - && ownerSolution.CanRefill && targetSolution.CanDrain) + && solutionsSys.TryGetRefillableSolution(Owner.Uid, out var ownerRefill) + && solutionsSys.TryGetDrainableSolution(eventArgs.Target.Uid, out var targetDrain)) { - var transferred = DoTransfer(targetSolution, ownerSolution, tank.TransferAmount, eventArgs.User); + var transferred = DoTransfer(eventArgs.User, eventArgs.Target, targetDrain, Owner, ownerRefill, tank.TransferAmount); if (transferred > 0) { - var toTheBrim = ownerSolution.RefillSpaceAvailable == 0; + var toTheBrim = ownerRefill.AvailableVolume == 0; var msg = toTheBrim ? "comp-solution-transfer-fill-fully" : "comp-solution-transfer-fill-normal"; - target.PopupMessage(eventArgs.User, Loc.GetString(msg,("owner", Owner),("amount", transferred),("target", target))); + target.PopupMessage(eventArgs.User, + Loc.GetString(msg, ("owner", eventArgs.Target), ("amount", transferred), ("target", Owner))); return true; } } - if (CanSend && targetSolution.CanRefill && ownerSolution.CanDrain) + if (CanSend && solutionsSys.TryGetRefillableSolution(eventArgs.Target.Uid, out var targetRefill) + && solutionsSys.TryGetDrainableSolution(Owner.Uid, out var ownerDrain)) { - var transferred = DoTransfer(ownerSolution, targetSolution, TransferAmount, eventArgs.User); + var transferred = DoTransfer(eventArgs.User, Owner, ownerDrain, target, targetRefill, TransferAmount); if (transferred > 0) { Owner.PopupMessage(eventArgs.User, - Loc.GetString("comp-solution-transfer-transfer-solution", - ("amount",transferred), - ("target",target))); + Loc.GetString("comp-solution-transfer-transfer-solution", + ("amount", transferred), + ("target", target))); return true; } @@ -169,29 +180,33 @@ namespace Content.Server.Chemistry.Components } /// The actual amount transferred. - private static ReagentUnit DoTransfer( - ISolutionInteractionsComponent source, - ISolutionInteractionsComponent target, - ReagentUnit amount, - IEntity user) + private static ReagentUnit DoTransfer(IEntity user, + IEntity sourceEntity, + Solution source, + IEntity targetEntity, + Solution target, + ReagentUnit amount) { + if (source.DrainAvailable == 0) { - source.Owner.PopupMessage(user, Loc.GetString("comp-solution-transfer-is-empty", ("target", source.Owner))); + sourceEntity.PopupMessage(user, + Loc.GetString("comp-solution-transfer-is-empty", ("target", sourceEntity))); return ReagentUnit.Zero; } - if (target.RefillSpaceAvailable == 0) + if (target.AvailableVolume == 0) { - target.Owner.PopupMessage(user, Loc.GetString("comp-solution-transfer-is-full", ("target", target.Owner))); + targetEntity.PopupMessage(user, + Loc.GetString("comp-solution-transfer-is-full", ("target", targetEntity))); return ReagentUnit.Zero; } var actualAmount = - ReagentUnit.Min(amount, ReagentUnit.Min(source.DrainAvailable, target.RefillSpaceAvailable)); + ReagentUnit.Min(amount, ReagentUnit.Min(source.DrainAvailable, target.AvailableVolume)); - var solution = source.Drain(actualAmount); - target.Refill(solution); + var solution = EntitySystem.Get().Drain(sourceEntity.Uid, source, actualAmount); + EntitySystem.Get().Refill(targetEntity.Uid, target, solution); return actualAmount; } @@ -251,7 +266,8 @@ namespace Content.Server.Chemistry.Components protected override void Activate(IEntity user, SolutionTransferComponent component) { component.TransferAmount = component.SubjectiveBestTransferAmount(); - user.PopupMessage(Loc.GetString("comp-solution-transfer-set-amount", ("amount", component.TransferAmount.Int()))); + user.PopupMessage(Loc.GetString("comp-solution-transfer-set-amount", + ("amount", component.TransferAmount.Int()))); } } @@ -275,7 +291,8 @@ namespace Content.Server.Chemistry.Components protected override void Activate(IEntity user, SolutionTransferComponent component) { component.TransferAmount = component.MaximumTransferAmount; - user.PopupMessage(Loc.GetString("comp-solution-transfer-set-amount", ("amount", component.TransferAmount.Int()))); + user.PopupMessage(Loc.GetString("comp-solution-transfer-set-amount", + ("amount", component.TransferAmount.Int()))); } } @@ -303,6 +320,7 @@ namespace Content.Server.Chemistry.Components { return; } + component.UserInterface?.Open(actor.PlayerSession); } } diff --git a/Content.Server/Chemistry/Components/TransformableContainerComponent.cs b/Content.Server/Chemistry/Components/TransformableContainerComponent.cs index ad01057a58..00d6dda666 100644 --- a/Content.Server/Chemistry/Components/TransformableContainerComponent.cs +++ b/Content.Server/Chemistry/Components/TransformableContainerComponent.cs @@ -1,27 +1,22 @@ -using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using Robust.Server.GameObjects; using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Server.Chemistry.Components { [RegisterComponent] - public class TransformableContainerComponent : Component, ISolutionChange + public class TransformableContainerComponent : Component { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - public override string Name => "TransformableContainer"; - private SpriteSpecifier? _initialSprite; - private string _initialName = default!; - private string _initialDescription = default!; - private ReagentPrototype? _currentReagent; + public SpriteSpecifier? InitialSprite; + public string InitialName = default!; + public string InitialDescription = default!; + public ReagentPrototype? CurrentReagent; - public bool Transformed { get; private set; } + public bool Transformed { get; internal set; } protected override void Initialize() { @@ -30,72 +25,19 @@ namespace Content.Server.Chemistry.Components if (Owner.TryGetComponent(out SpriteComponent? sprite) && sprite.BaseRSIPath != null) { - _initialSprite = new SpriteSpecifier.Rsi(new ResourcePath(sprite.BaseRSIPath), "icon"); + InitialSprite = new SpriteSpecifier.Rsi(new ResourcePath(sprite.BaseRSIPath), "icon"); } - _initialName = Owner.Name; - _initialDescription = Owner.Description; + InitialName = Owner.Name; + InitialDescription = Owner.Description; } protected override void Startup() { base.Startup(); - Owner.EnsureComponentWarn(out SolutionContainerComponent solution); - - solution.Capabilities |= SolutionContainerCaps.FitsInDispenser; - } - - public void CancelTransformation() - { - _currentReagent = null; - Transformed = false; - - if (Owner.TryGetComponent(out SpriteComponent? sprite) && - _initialSprite != null) - { - sprite.LayerSetSprite(0, _initialSprite); - } - - Owner.Name = _initialName; - Owner.Description = _initialDescription; - } - - void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) - { - var solution = eventArgs.Owner.GetComponent(); - //Transform container into initial state when emptied - if (_currentReagent != null && solution.ReagentList.Count == 0) - { - CancelTransformation(); - } - - //the biggest reagent in the solution decides the appearance - var reagentId = solution.Solution.GetPrimaryReagentId(); - - //If biggest reagent didn't changed - don't change anything at all - if (_currentReagent != null && _currentReagent.ID == reagentId) - { - return; - } - - //Only reagents with spritePath property can change appearance of transformable containers! - if (!string.IsNullOrWhiteSpace(reagentId) && - _prototypeManager.TryIndex(reagentId, out ReagentPrototype? proto) && - !string.IsNullOrWhiteSpace(proto.SpriteReplacementPath)) - { - var spriteSpec = new SpriteSpecifier.Rsi(new ResourcePath("Objects/Consumable/Drinks/" + proto.SpriteReplacementPath),"icon"); - - if (Owner.TryGetComponent(out SpriteComponent? sprite)) - { - sprite?.LayerSetSprite(0, spriteSpec); - } - - Owner.Name = proto.Name + " glass"; - Owner.Description = proto.Description; - _currentReagent = proto; - Transformed = true; - } + Owner.EnsureComponentWarn(); + Owner.EnsureComponentWarn(); } } } diff --git a/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs b/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs new file mode 100644 index 0000000000..40f8f35753 --- /dev/null +++ b/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs @@ -0,0 +1,24 @@ +using Content.Server.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; +using JetBrains.Annotations; +using Robust.Shared.GameObjects; + +namespace Content.Server.Chemistry.EntitySystems +{ + [UsedImplicitly] + public class ChemMasterSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSolutionChange); + } + + private void OnSolutionChange(EntityUid uid, ChemMasterComponent component, + SolutionChangedEvent solutionChanged) + { + component.UpdateUserInterface(); + } + } +} diff --git a/Content.Server/Chemistry/EntitySystems/ChemicalReactionSystem.cs b/Content.Server/Chemistry/EntitySystems/ChemicalReactionSystem.cs index 5edece1a64..69cfde9ea1 100644 --- a/Content.Server/Chemistry/EntitySystems/ChemicalReactionSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/ChemicalReactionSystem.cs @@ -1,3 +1,4 @@ +using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Reagent; using Robust.Shared.Audio; @@ -8,9 +9,9 @@ namespace Content.Server.Chemistry.EntitySystems { public class ChemicalReactionSystem : SharedChemicalReactionSystem { - protected override void OnReaction(ReactionPrototype reaction, IEntity owner, ReagentUnit unitReactions) + protected override void OnReaction(Solution solution, ReactionPrototype reaction, IEntity owner, ReagentUnit unitReactions) { - base.OnReaction(reaction, owner, unitReactions); + base.OnReaction(solution, reaction, owner, unitReactions); SoundSystem.Play(Filter.Pvs(owner), reaction.Sound.GetSound(), owner); } diff --git a/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs index a6c8aaa54e..90728479f1 100644 --- a/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs +++ b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs @@ -1,10 +1,13 @@ using Content.Server.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Weapons.Melee; +using JetBrains.Annotations; using Robust.Shared.GameObjects; namespace Content.Server.Chemistry.EntitySystems { + [UsedImplicitly] public class HypospraySystem : EntitySystem { public override void Initialize() @@ -13,6 +16,12 @@ namespace Content.Server.Chemistry.EntitySystems SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(OnClickAttack); + SubscribeLocalEvent(OnSolutionChange); + } + + private void OnSolutionChange(EntityUid uid, HyposprayComponent component, SolutionChangedEvent args) + { + component.Dirty(); } public void OnAfterInteract(EntityUid uid, HyposprayComponent comp, AfterInteractEvent args) diff --git a/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs b/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs new file mode 100644 index 0000000000..760cb11102 --- /dev/null +++ b/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs @@ -0,0 +1,23 @@ +using Content.Server.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; +using JetBrains.Annotations; +using Robust.Shared.GameObjects; + +namespace Content.Server.Chemistry.EntitySystems +{ + [UsedImplicitly] + public class InjectorSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSolutionChange); + } + + private void OnSolutionChange(EntityUid uid, InjectorComponent component, SolutionChangedEvent args) + { + component.Dirty(); + } + } +} diff --git a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs new file mode 100644 index 0000000000..bcec570e1d --- /dev/null +++ b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs @@ -0,0 +1,23 @@ +using Content.Server.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; +using JetBrains.Annotations; +using Robust.Shared.GameObjects; + +namespace Content.Server.Chemistry.EntitySystems +{ + [UsedImplicitly] + public class ReagentDispenserSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSolutionChange); + } + + private void OnSolutionChange(EntityUid uid, ReagentDispenserComponent component, SolutionChangedEvent args) + { + component.UpdateUserInterface(); + } + } +} diff --git a/Content.Server/Chemistry/EntitySystems/RehydratableSystem.cs b/Content.Server/Chemistry/EntitySystems/RehydratableSystem.cs new file mode 100644 index 0000000000..3c86fa881b --- /dev/null +++ b/Content.Server/Chemistry/EntitySystems/RehydratableSystem.cs @@ -0,0 +1,51 @@ +using Content.Server.Chemistry.Components; +using Content.Server.Notification; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Chemistry.Reagent; +using JetBrains.Annotations; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; + +namespace Content.Server.Chemistry.EntitySystems +{ + [UsedImplicitly] + public class RehydratableSystem : EntitySystem + { + [Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!; + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSolutionChange); + } + + private void OnSolutionChange(EntityUid uid, RehydratableComponent component, SolutionChangedEvent args) + { + if (_solutionsSystem.GetReagentQuantity(uid, component.CatalystPrototype) > ReagentUnit.Zero) + { + Expand(component, component.Owner); + } + } + + // Try not to make this public if you can help it. + private void Expand(RehydratableComponent component, IEntity owner) + { + if (component.Expanding) + { + return; + } + + component.Expanding = true; + owner.PopupMessageEveryone(Loc.GetString("rehydratable-component-expands-message", ("owner", owner))); + if (!string.IsNullOrEmpty(component.TargetPrototype)) + { + var ent = component.Owner.EntityManager.SpawnEntity(component.TargetPrototype, + owner.Transform.Coordinates); + ent.Transform.AttachToGridOrMap(); + } + + owner.QueueDelete(); + } + } +} diff --git a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs index aa00ab6ac6..8c4dc6b977 100644 --- a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs @@ -1,7 +1,10 @@ using Content.Server.Body.Circulatory; using Content.Server.Chemistry.Components; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; using JetBrains.Annotations; using Robust.Shared.GameObjects; +using Robust.Shared.IoC; using Robust.Shared.Physics.Dynamics; namespace Content.Server.Chemistry.EntitySystems @@ -9,6 +12,7 @@ namespace Content.Server.Chemistry.EntitySystems [UsedImplicitly] internal sealed class SolutionInjectOnCollideSystem : EntitySystem { + [Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!; public override void Initialize() { base.Initialize(); @@ -18,15 +22,15 @@ namespace Content.Server.Chemistry.EntitySystems private void HandleInit(EntityUid uid, SolutionInjectOnCollideComponent component, ComponentInit args) { - component.Owner.EnsureComponentWarn($"{nameof(SolutionInjectOnCollideComponent)} requires a SolutionContainer on {component.Owner}!"); + component.Owner + .EnsureComponentWarn($"{nameof(SolutionInjectOnCollideComponent)} requires a SolutionContainerManager on {component.Owner}!"); } private void HandleInjection(EntityUid uid, SolutionInjectOnCollideComponent component, StartCollideEvent args) { if (!args.OtherFixture.Body.Owner.TryGetComponent(out var bloodstream) || - !ComponentManager.TryGetComponent(uid, out SolutionContainerComponent? solutionContainer)) return; + !_solutionsSystem.TryGetInjectableSolution(component.Owner.Uid, out var solution)) return; - var solution = solutionContainer.Solution; var solRemoved = solution.SplitSolution(component.TransferAmount); var solRemovedVol = solRemoved.TotalVolume; diff --git a/Content.Server/Chemistry/EntitySystems/TransformableContainerSystem.cs b/Content.Server/Chemistry/EntitySystems/TransformableContainerSystem.cs new file mode 100644 index 0000000000..e2721e6dae --- /dev/null +++ b/Content.Server/Chemistry/EntitySystems/TransformableContainerSystem.cs @@ -0,0 +1,82 @@ +using Content.Server.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Chemistry.Reagent; +using JetBrains.Annotations; +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + +namespace Content.Server.Chemistry.EntitySystems +{ + [UsedImplicitly] + public class TransformableContainerSystem : EntitySystem + { + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSolutionChange); + } + + private void OnSolutionChange(EntityUid uid, TransformableContainerComponent component, + SolutionChangedEvent args) + { + if (!_solutionsSystem.TryGetFitsInDispenser(uid, out var solution)) + return; + //Transform container into initial state when emptied + if (component.CurrentReagent != null && solution.Contents.Count == 0) + { + CancelTransformation(component); + } + + //the biggest reagent in the solution decides the appearance + var reagentId = solution.GetPrimaryReagentId(); + + //If biggest reagent didn't changed - don't change anything at all + if (component.CurrentReagent != null && component.CurrentReagent.ID == reagentId) + { + return; + } + + //Only reagents with spritePath property can change appearance of transformable containers! + if (!string.IsNullOrWhiteSpace(reagentId) + && _prototypeManager.TryIndex(reagentId, out ReagentPrototype? proto) + && !string.IsNullOrWhiteSpace(proto.SpriteReplacementPath)) + { + var spriteSpec = + new SpriteSpecifier.Rsi( + new ResourcePath("Objects/Consumable/Drinks/" + proto.SpriteReplacementPath), "icon"); + var ownerEntity = EntityManager.GetEntity(uid); + if (ownerEntity.TryGetComponent(out SpriteComponent? sprite)) + { + sprite?.LayerSetSprite(0, spriteSpec); + } + + ownerEntity.Name = proto.Name + " glass"; + ownerEntity.Description = proto.Description; + component.CurrentReagent = proto; + component.Transformed = true; + } + } + + private void CancelTransformation(TransformableContainerComponent component) + { + component.CurrentReagent = null; + component.Transformed = false; + + if (component.Owner.TryGetComponent(out SpriteComponent? sprite) && + component.InitialSprite != null) + { + sprite.LayerSetSprite(0, component.InitialSprite); + } + + component.Owner.Name = component.InitialName; + component.Owner.Description = component.InitialDescription; + } + } +} diff --git a/Content.Server/Chemistry/EntitySystems/VaporSystem.cs b/Content.Server/Chemistry/EntitySystems/VaporSystem.cs index f5e21c4adc..6580c5774e 100644 --- a/Content.Server/Chemistry/EntitySystems/VaporSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/VaporSystem.cs @@ -1,8 +1,10 @@ -using System.Linq; -using Content.Server.Chemistry.Components; +using Content.Server.Chemistry.Components; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using Content.Shared.Physics; +using Content.Shared.Vapor; using JetBrains.Annotations; using Robust.Shared.GameObjects; using Robust.Shared.IoC; @@ -18,6 +20,7 @@ namespace Content.Server.Chemistry.EntitySystems { [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IPrototypeManager _protoManager = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; private const float ReactTime = 0.125f; @@ -29,14 +32,17 @@ namespace Content.Server.Chemistry.EntitySystems private void HandleCollide(EntityUid uid, VaporComponent component, StartCollideEvent args) { - if (!ComponentManager.TryGetComponent(uid, out SolutionContainerComponent? contents)) return; + if (!ComponentManager.TryGetComponent(uid, out SolutionContainerManagerComponent? contents)) return; - contents.Solution.DoEntityReaction(args.OtherFixture.Body.Owner, ReactionMethod.Touch); + foreach (var (_, value) in contents.Solutions) + { + value.DoEntityReaction(args.OtherFixture.Body.Owner, ReactionMethod.Touch); + } // Check for collision with a impassable object (e.g. wall) and stop if ((args.OtherFixture.CollisionLayer & (int) CollisionGroup.Impassable) != 0 && args.OtherFixture.Hard) { - EntityManager.QueueDeleteEntity(uid); + EntityManager.QueueDeleteEntity(uid); } } @@ -60,14 +66,7 @@ namespace Content.Server.Chemistry.EntitySystems return false; } - if (!vapor.Owner.TryGetComponent(out SolutionContainerComponent? contents)) - { - return false; - } - - var result = contents.TryAddSolution(solution); - - if (!result) + if (!_solutionContainerSystem.TryGetSolution(vapor.Owner, SharedVaporComponent.SolutionName, out _)) { return false; } @@ -77,13 +76,17 @@ namespace Content.Server.Chemistry.EntitySystems public override void Update(float frameTime) { - foreach (var (vaporComp, solution) in ComponentManager.EntityQuery(true)) + foreach (var (vaporComp, solution) in ComponentManager + .EntityQuery(true)) { - Update(frameTime, vaporComp, solution); + foreach (var (_, value) in solution.Solutions) + { + Update(frameTime, vaporComp, value); + } } } - private void Update(float frameTime, VaporComponent vapor, SolutionContainerComponent contents) + private void Update(float frameTime, VaporComponent vapor, Solution contents) { if (!vapor.Active) return; @@ -99,16 +102,19 @@ namespace Content.Server.Chemistry.EntitySystems var mapGrid = _mapManager.GetGrid(entity.Transform.GridID); var tile = mapGrid.GetTileRef(entity.Transform.Coordinates.ToVector2i(EntityManager, _mapManager)); - foreach (var reagentQuantity in contents.ReagentList.ToArray()) + foreach (var reagentQuantity in contents.Contents.ToArray()) { if (reagentQuantity.Quantity == ReagentUnit.Zero) continue; var reagent = _protoManager.Index(reagentQuantity.ReagentId); - contents.TryRemoveReagent(reagentQuantity.ReagentId, reagent.ReactionTile(tile, (reagentQuantity.Quantity / vapor.TransferAmount) * 0.25f)); + _solutionContainerSystem.TryRemoveReagent(vapor.Owner.Uid, contents, reagentQuantity.ReagentId, + reagent.ReactionTile(tile, (reagentQuantity.Quantity / vapor.TransferAmount) * 0.25f)); } } // Check if we've reached our target. - if(!vapor.Reached && vapor.Target.TryDistance(EntityManager, entity.Transform.Coordinates, out var distance) && distance <= 0.5f) + if (!vapor.Reached && + vapor.Target.TryDistance(EntityManager, entity.Transform.Coordinates, out var distance) && + distance <= 0.5f) { vapor.Reached = true; } diff --git a/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs b/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs index f2d16600d1..5539d8868a 100644 --- a/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs +++ b/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs @@ -1,7 +1,9 @@ -using System; +using System; using Content.Server.Chemistry.Components; using Content.Server.Coordinates.Helpers; using Content.Shared.Audio; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reaction; using Content.Shared.Sound; using JetBrains.Annotations; @@ -87,12 +89,9 @@ namespace Content.Server.Chemistry.ReactionEffects IoCManager.InjectDependencies(this); } - public void React(IEntity solutionEntity, double intensity) + public void React(Solution solution, IEntity solutionEntity, double intensity) { - if (!solutionEntity.TryGetComponent(out SolutionContainerComponent? contents)) - return; - - var solution = contents.SplitSolution(contents.MaxVolume); + var splitSolution = EntitySystem.Get().SplitSolution(solutionEntity.Uid, solution, solution.MaxVolume); // We take the square root so it becomes harder to reach higher amount values var amount = (int) Math.Round(_rangeConstant + _rangeMultiplier*Math.Sqrt(intensity)); amount = Math.Min(amount, _maxRange); @@ -115,7 +114,7 @@ namespace Content.Server.Chemistry.ReactionEffects solutionFraction = amount * (1 - _reagentMaxConcentrationFactor) / _reagentDilutionStart + _reagentMaxConcentrationFactor; } - solution.RemoveSolution(solution.TotalVolume * solutionFraction); + splitSolution.RemoveSolution(splitSolution.TotalVolume * solutionFraction); } if (!_mapManager.TryFindGridAt(solutionEntity.Transform.MapPosition, out var grid)) return; @@ -133,7 +132,7 @@ namespace Content.Server.Chemistry.ReactionEffects return; } - areaEffectComponent.TryAddSolution(solution); + areaEffectComponent.TryAddSolution(splitSolution); areaEffectComponent.Start(amount, _duration, _spreadDelay, _removeDelay); SoundSystem.Play(Filter.Pvs(solutionEntity), _sound.GetSound(), solutionEntity, AudioHelpers.WithVariation(0.125f)); diff --git a/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs b/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs index 1b2dd70f80..15b08f0a29 100644 --- a/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs +++ b/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs @@ -1,6 +1,8 @@ using System; using Content.Server.Chemistry.Components; using Content.Server.Explosion; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Reaction; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; @@ -26,12 +28,11 @@ namespace Content.Server.Chemistry.ReactionEffects /// [DataField("maxScale")] private float _maxScale = 1; - public void React(IEntity? solutionEntity, double intensity) + public void React(Solution solution, IEntity solutionEntity, double intensity) { var floatIntensity = (float) intensity; - if (solutionEntity == null) - return; - if (!solutionEntity.HasComponent()) + + if (!solutionEntity.HasComponent()) return; //Handle scaling diff --git a/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs b/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs index cfc0177d41..c2419d82a4 100644 --- a/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs +++ b/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs @@ -1,6 +1,5 @@ -using Content.Shared.Body.Components; +using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; diff --git a/Content.Server/Chemistry/ReagentEffects/HealthChange.cs b/Content.Server/Chemistry/ReagentEffects/HealthChange.cs index 7484f9cdde..6dc85aeb2c 100644 --- a/Content.Server/Chemistry/ReagentEffects/HealthChange.cs +++ b/Content.Server/Chemistry/ReagentEffects/HealthChange.cs @@ -1,6 +1,5 @@ -using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; using Content.Shared.Damage; diff --git a/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs b/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs index 9031737443..d77446b9e6 100644 --- a/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs +++ b/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs @@ -1,12 +1,11 @@ -using Content.Shared.Chemistry.Reagent; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization.Manager.Attributes; -using Content.Shared.Movement.Components; -using Content.Shared.Chemistry.Components; -using Robust.Shared.Timing; -using Robust.Shared.IoC; using System; -using Content.Shared.Chemistry.Solution; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Movement.Components; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Timing; namespace Content.Server.Chemistry.ReagentEffects { diff --git a/Content.Server/Chemistry/ReagentEffects/SatiateHunger.cs b/Content.Server/Chemistry/ReagentEffects/SatiateHunger.cs index 983d30b565..aed1be8fda 100644 --- a/Content.Server/Chemistry/ReagentEffects/SatiateHunger.cs +++ b/Content.Server/Chemistry/ReagentEffects/SatiateHunger.cs @@ -1,7 +1,6 @@ using Content.Server.Nutrition.Components; -using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; diff --git a/Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs b/Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs index 637c56f235..ce2de5789d 100644 --- a/Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs +++ b/Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs @@ -1,7 +1,6 @@ using Content.Server.Nutrition.Components; -using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; diff --git a/Content.Server/Chemistry/ReagentEntityReactions/AddToSolutionReaction.cs b/Content.Server/Chemistry/ReagentEntityReactions/AddToSolutionReaction.cs index f509fa0d56..37e68ffb35 100644 --- a/Content.Server/Chemistry/ReagentEntityReactions/AddToSolutionReaction.cs +++ b/Content.Server/Chemistry/ReagentEntityReactions/AddToSolutionReaction.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using Content.Server.Chemistry.Components; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using JetBrains.Annotations; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; @@ -12,15 +12,19 @@ namespace Content.Server.Chemistry.ReagentEntityReactions [UsedImplicitly] public class AddToSolutionReaction : ReagentEntityReaction { - [DataField("reagents", true, customTypeSerializer:typeof(PrototypeIdHashSetSerializer))] + [DataField("reagents", true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] // ReSharper disable once CollectionNeverUpdated.Local - private readonly HashSet _reagents = new (); + private readonly HashSet _reagents = new(); protected override void React(IEntity entity, ReagentPrototype reagent, ReagentUnit volume, Solution? source) { - if (!entity.TryGetComponent(out SolutionContainerComponent? solutionContainer) || (_reagents.Count > 0 && !_reagents.Contains(reagent.ID))) return; + // TODO see if this is correct + if (!EntitySystem.Get() + .TryGetSolution(entity, "reagents", out var solutionContainer) + || (_reagents.Count > 0 && !_reagents.Contains(reagent.ID))) return; - if(solutionContainer.TryAddReagent(reagent.ID, volume, out var accepted)) + if (EntitySystem.Get() + .TryAddReagent(entity.Uid, solutionContainer, reagent.ID, volume, out var accepted)) source?.RemoveReagent(reagent.ID, accepted); } } diff --git a/Content.Server/Chemistry/ReagentEntityReactions/ExtinguishReaction.cs b/Content.Server/Chemistry/ReagentEntityReactions/ExtinguishReaction.cs index 860df2c423..d24504db76 100644 --- a/Content.Server/Chemistry/ReagentEntityReactions/ExtinguishReaction.cs +++ b/Content.Server/Chemistry/ReagentEntityReactions/ExtinguishReaction.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Content.Server.Atmos.Components; +using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using JetBrains.Annotations; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; diff --git a/Content.Server/Chemistry/ReagentEntityReactions/FlammableReaction.cs b/Content.Server/Chemistry/ReagentEntityReactions/FlammableReaction.cs index 8c71a8904d..a920a38e75 100644 --- a/Content.Server/Chemistry/ReagentEntityReactions/FlammableReaction.cs +++ b/Content.Server/Chemistry/ReagentEntityReactions/FlammableReaction.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Content.Server.Atmos.Components; +using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using JetBrains.Annotations; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; diff --git a/Content.Server/Chemistry/ReagentEntityReactions/WashCreamPieReaction.cs b/Content.Server/Chemistry/ReagentEntityReactions/WashCreamPieReaction.cs index 51794ebb7c..5676ce0bce 100644 --- a/Content.Server/Chemistry/ReagentEntityReactions/WashCreamPieReaction.cs +++ b/Content.Server/Chemistry/ReagentEntityReactions/WashCreamPieReaction.cs @@ -1,8 +1,7 @@ using System.Collections.Generic; -using Content.Server.Nutrition.Components; using Content.Server.Nutrition.EntitySystems; +using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using Content.Shared.Nutrition.Components; using JetBrains.Annotations; using Robust.Shared.GameObjects; diff --git a/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs b/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs index 577b6f7b48..a8674f9173 100644 --- a/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs @@ -1,8 +1,7 @@ using Content.Server.Fluids.Components; -using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using JetBrains.Annotations; using Robust.Shared.Map; using Robust.Shared.Serialization.Manager.Attributes; diff --git a/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs b/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs index 3fbb874f25..3c5116dc29 100644 --- a/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs @@ -1,8 +1,7 @@ using Content.Server.Fluids.Components; -using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using Content.Shared.Slippery; using JetBrains.Annotations; using Robust.Shared.Map; diff --git a/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs index 6840c36188..4d6eeca695 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs @@ -1,5 +1,5 @@ -using Content.Server.Chemistry.Components; using Content.Server.Fluids.Components; +using Content.Shared.Chemistry.EntitySystems; using JetBrains.Annotations; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; @@ -12,10 +12,11 @@ namespace Content.Server.Destructible.Thresholds.Behaviors { public void Execute(IEntity owner, DestructibleSystem system) { - if (!owner.TryGetComponent(out SolutionContainerComponent? solutionContainer)) + // TODO see if this is correct + if (!EntitySystem.Get().TryGetSolution(owner, SpillableComponent.SolutionName, out var solution)) return; - solutionContainer.Solution.SpillAt(owner.Transform.Coordinates, "PuddleSmear", false); + solution.SpillAt(owner.Transform.Coordinates, "PuddleSmear", false); } } } diff --git a/Content.Server/Extinguisher/FireExtinguisherComponent.cs b/Content.Server/Extinguisher/FireExtinguisherComponent.cs index 6492ff6803..f5a703b81e 100644 --- a/Content.Server/Extinguisher/FireExtinguisherComponent.cs +++ b/Content.Server/Extinguisher/FireExtinguisherComponent.cs @@ -1,9 +1,8 @@ using System.Threading.Tasks; using Content.Server.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution.Components; using Content.Shared.Interaction; -using Content.Shared.Notification; using Content.Shared.Notification.Managers; using Content.Shared.Sound; using Robust.Shared.Audio; @@ -12,13 +11,13 @@ using Robust.Shared.Localization; using Robust.Shared.Player; using Robust.Shared.Serialization.Manager.Attributes; - namespace Content.Server.Extinguisher { [RegisterComponent] public class FireExtinguisherComponent : Component, IAfterInteract { public override string Name => "FireExtinguisher"; + private const string SolutionName = "fireExtinguisher"; [DataField("refillSound")] SoundSpecifier _refillSound = new SoundPathSpecifier("/Audio/Effects/refill.ogg"); @@ -27,24 +26,26 @@ namespace Content.Server.Extinguisher async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { + var solutionContainerSystem = EntitySystem.Get(); if (eventArgs.Target == null || !eventArgs.CanReach) { return false; } - if (eventArgs.Target.TryGetComponent(out ReagentTankComponent? tank) - && eventArgs.Target.TryGetComponent(out ISolutionInteractionsComponent? targetSolution) - && targetSolution.CanDrain - && Owner.TryGetComponent(out SolutionContainerComponent? container)) + var targetEntity = eventArgs.Target; + if (eventArgs.Target.HasComponent() + && solutionContainerSystem.TryGetDrainableSolution(targetEntity.Uid, out var targetSolution) + && solutionContainerSystem.TryGetSolution(Owner, SolutionName, out var container)) { - var trans = ReagentUnit.Min(container.EmptyVolume, targetSolution.DrainAvailable); - if (trans > 0) + var transfer = ReagentUnit.Min(container.AvailableVolume, targetSolution.DrainAvailable); + if (transfer > 0) { - var drained = targetSolution.Drain(trans); - container.TryAddSolution(drained); + var drained = solutionContainerSystem.Drain(targetEntity.Uid, targetSolution, transfer); + solutionContainerSystem.TryAddSolution(Owner.Uid, container, drained); SoundSystem.Play(Filter.Pvs(Owner), _refillSound.GetSound(), Owner); - eventArgs.Target.PopupMessage(eventArgs.User, Loc.GetString("fire-extinguisher-component-after-interact-refilled-message",("owner", Owner))); + eventArgs.Target.PopupMessage(eventArgs.User, + Loc.GetString("fire-extinguisher-component-after-interact-refilled-message", ("owner", Owner))); } return true; diff --git a/Content.Server/Fluids/Components/BucketComponent.cs b/Content.Server/Fluids/Components/BucketComponent.cs index 2e7b3f771c..14600d1dd8 100644 --- a/Content.Server/Fluids/Components/BucketComponent.cs +++ b/Content.Server/Fluids/Components/BucketComponent.cs @@ -1,11 +1,10 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Content.Server.Chemistry.Components; using Content.Server.DoAfter; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; using Content.Shared.Interaction; using Content.Shared.Interaction.Helpers; -using Content.Shared.Notification; using Content.Shared.Notification.Managers; using Content.Shared.Sound; using Robust.Shared.Audio; @@ -23,38 +22,37 @@ namespace Content.Server.Fluids.Components public class BucketComponent : Component, IInteractUsing { public override string Name => "Bucket"; + public const string SolutionName = "bucket"; private List _currentlyUsing = new(); public ReagentUnit MaxVolume { - get => Owner.TryGetComponent(out SolutionContainerComponent? solution) ? solution.MaxVolume : ReagentUnit.Zero; + get => + EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution) + ? solution.MaxVolume + : ReagentUnit.Zero; set { - if (Owner.TryGetComponent(out SolutionContainerComponent? solution)) + if (EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution)) { solution.MaxVolume = value; } } } - public ReagentUnit CurrentVolume => Owner.TryGetComponent(out SolutionContainerComponent? solution) + public ReagentUnit CurrentVolume => EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution) ? solution.CurrentVolume : ReagentUnit.Zero; [DataField("sound")] private SoundSpecifier _sound = new SoundPathSpecifier("/Audio/Effects/Fluids/watersplash.ogg"); - /// - protected override void Initialize() - { - base.Initialize(); - Owner.EnsureComponentWarn(); - } async Task IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs) { - if (!Owner.TryGetComponent(out SolutionContainerComponent? contents) || + var solutionsSys = EntitySystem.Get(); + if (!solutionsSys.TryGetSolution(Owner, SolutionName, out var contents) || _currentlyUsing.Contains(eventArgs.Using.Uid) || !eventArgs.Using.TryGetComponent(out MopComponent? mopComponent) || mopComponent.Mopping) @@ -101,15 +99,15 @@ namespace Content.Server.Fluids.Components return false; } - var mopContents = mopComponent.Contents; + var mopContents = mopComponent.MopSolution; if (mopContents == null) { return false; } - var solution = contents.SplitSolution(transferAmount); - if (!mopContents.TryAddSolution(solution)) + var solution = solutionsSys.SplitSolution(Owner.Uid, contents, transferAmount); + if (!solutionsSys.TryAddSolution(mopComponent.Owner.Uid, mopContents, solution)) { return false; } diff --git a/Content.Server/Fluids/Components/MopComponent.cs b/Content.Server/Fluids/Components/MopComponent.cs index 379830be24..4bd7db7d0e 100644 --- a/Content.Server/Fluids/Components/MopComponent.cs +++ b/Content.Server/Fluids/Components/MopComponent.cs @@ -1,10 +1,10 @@ using System.Threading.Tasks; -using Content.Server.Chemistry.Components; using Content.Server.DoAfter; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; using Content.Shared.Interaction; using Content.Shared.Interaction.Helpers; -using Content.Shared.Notification; using Content.Shared.Notification.Managers; using Content.Shared.Sound; using Robust.Shared.Audio; @@ -23,28 +23,36 @@ namespace Content.Server.Fluids.Components public class MopComponent : Component, IAfterInteract { public override string Name => "Mop"; + public const string SolutionName = "mop"; /// /// Used to prevent do_after spam if we're currently mopping. /// public bool Mopping { get; private set; } - public SolutionContainerComponent? Contents => Owner.GetComponentOrNull(); + public Solution? MopSolution + { + get + { + EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution); + return solution; + } + } public ReagentUnit MaxVolume { - get => Owner.GetComponentOrNull()?.MaxVolume ?? ReagentUnit.Zero; + get => MopSolution?.MaxVolume ?? ReagentUnit.Zero; set { - if (Owner.TryGetComponent(out SolutionContainerComponent? solution)) + var solution = MopSolution; + if (solution != null) { solution.MaxVolume = value; } } } - public ReagentUnit CurrentVolume => - Owner.GetComponentOrNull()?.CurrentVolume ?? ReagentUnit.Zero; + public ReagentUnit CurrentVolume => MopSolution?.CurrentVolume ?? ReagentUnit.Zero; // Currently there's a separate amount for pickup and dropoff so // Picking up a puddle requires multiple clicks @@ -60,15 +68,7 @@ namespace Content.Server.Fluids.Components /// Multiplier for the do_after delay for how fast the mop works. /// [ViewVariables] - [DataField("speed")] - private float _mopSpeed = 1; - - protected override void Initialize() - { - base.Initialize(); - - Owner.EnsureComponentWarn(out SolutionContainerComponent _); - } + [DataField("speed")] private float _mopSpeed = 1; async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { @@ -80,7 +80,7 @@ namespace Content.Server.Fluids.Components * will spill some of the mop's solution onto the puddle which will evaporate eventually. */ - if (!Owner.TryGetComponent(out SolutionContainerComponent? contents) || + if (!EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var contents ) || Mopping || !eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) { @@ -94,7 +94,8 @@ namespace Content.Server.Fluids.Components if (currentVolume > 0) { // Drop the liquid on the mop on to the ground - contents.SplitSolution(CurrentVolume).SpillAt(eventArgs.ClickLocation, "PuddleSmear"); + EntitySystem.Get().SplitSolution(Owner.Uid, contents, CurrentVolume) + .SpillAt(eventArgs.ClickLocation, "PuddleSmear"); return true; } @@ -117,7 +118,8 @@ namespace Content.Server.Fluids.Components Mopping = true; // So if the puddle has 20 units we mop in 2 seconds. Don't just store CurrentVolume given it can change so need to re-calc it anyway. - var doAfterArgs = new DoAfterEventArgs(eventArgs.User, _mopSpeed * puddleVolume.Float() / 10.0f, target: eventArgs.Target) + var doAfterArgs = new DoAfterEventArgs(eventArgs.User, _mopSpeed * puddleVolume.Float() / 10.0f, + target: eventArgs.Target) { BreakOnUserMove = true, BreakOnStun = true, @@ -138,7 +140,9 @@ namespace Content.Server.Fluids.Components if (transferAmount == 0) { - if (puddleComponent.EmptyHolder) //The puddle doesn't actually *have* reagents, for example vomit because there's no "vomit" reagent. + if ( + puddleComponent + .EmptyHolder) //The puddle doesn't actually *have* reagents, for example vomit because there's no "vomit" reagent. { puddleComponent.Owner.Delete(); transferAmount = ReagentUnit.Min(ReagentUnit.New(5), CurrentVolume); @@ -154,13 +158,15 @@ namespace Content.Server.Fluids.Components puddleComponent.SplitSolution(transferAmount); } - if (puddleCleaned) //After cleaning the puddle, make a new puddle with solution from the mop as a "wet floor". Then evaporate it slowly. + if ( + puddleCleaned) //After cleaning the puddle, make a new puddle with solution from the mop as a "wet floor". Then evaporate it slowly. { - contents.SplitSolution(transferAmount).SpillAt(eventArgs.ClickLocation, "PuddleSmear"); + EntitySystem.Get().SplitSolution(Owner.Uid, contents, transferAmount) + .SpillAt(eventArgs.ClickLocation, "PuddleSmear"); } else { - contents.SplitSolution(transferAmount); + EntitySystem.Get().SplitSolution(Owner.Uid, contents, transferAmount); } SoundSystem.Play(Filter.Pvs(Owner), _pickupSound.GetSound(), Owner); diff --git a/Content.Server/Fluids/Components/PuddleComponent.cs b/Content.Server/Fluids/Components/PuddleComponent.cs index 129b308515..d9cf371e5e 100644 --- a/Content.Server/Fluids/Components/PuddleComponent.cs +++ b/Content.Server/Fluids/Components/PuddleComponent.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; -using Content.Server.Chemistry.Components; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using Content.Shared.Directions; using Content.Shared.Examine; using Content.Shared.Maps; @@ -50,15 +50,22 @@ namespace Content.Server.Fluids.Components [Dependency] private readonly IRobustRandom _random = default!; public override string Name => "Puddle"; + public const string DefaultSolutionName = "puddle"; + private CancellationTokenSource? _evaporationToken; - [DataField("evaporate_threshold")] - private ReagentUnit _evaporateThreshold = ReagentUnit.New(20); // How few we can hold prior to self-destructing + + [DataField("evaporate_threshold")] private ReagentUnit + _evaporateThreshold = + ReagentUnit.New(20); // How few we can hold prior to self-destructing + public ReagentUnit EvaporateThreshold { get => _evaporateThreshold; set => _evaporateThreshold = value; } + private ReagentUnit _slipThreshold = ReagentUnit.New(3); + public ReagentUnit SlipThreshold { get => _slipThreshold; @@ -83,40 +90,44 @@ namespace Content.Server.Fluids.Components public ReagentUnit MaxVolume { - get => _contents.MaxVolume; - set => _contents.MaxVolume = value; + get => PuddleSolution?.MaxVolume ?? ReagentUnit.Zero; + set + { + if (PuddleSolution != null) + { + PuddleSolution.MaxVolume = value; + } + } } - [ViewVariables] - public ReagentUnit CurrentVolume => _contents.CurrentVolume; + [ViewVariables] public ReagentUnit CurrentVolume => PuddleSolution?.CurrentVolume ?? ReagentUnit.Zero; // Volume at which the fluid will try to spill to adjacent components // Currently a random number, potentially change public ReagentUnit OverflowVolume => _overflowVolume; - [ViewVariables] - [DataField("overflow_volume")] + + [ViewVariables] [DataField("overflow_volume")] private ReagentUnit _overflowVolume = ReagentUnit.New(20); + private ReagentUnit OverflowLeft => CurrentVolume - OverflowVolume; - private SolutionContainerComponent _contents = default!; - public bool EmptyHolder => _contents.ReagentList.Count == 0; - [DataField("variants")] - private int _spriteVariants = 1; - // Whether the underlying solution color should be used - [DataField("recolor")] - private bool _recolor = default; + public bool EmptyHolder => PuddleSolution?.Contents.Count == 0; - [DataField("state")] - private string _spriteState = "puddle"; + [DataField("variants")] private int _spriteVariants = 1; + + // Whether the underlying solution color should be used + [DataField("recolor")] private bool _recolor = default; + + [DataField("state")] private string _spriteState = "puddle"; private bool Slippery => Owner.TryGetComponent(out SlipperyComponent? slippery) && slippery.Slippery; + private Solution? PuddleSolution => EntitySystem.Get().EnsureSolution(Owner, DefaultSolutionName); + protected override void Initialize() { base.Initialize(); - _contents = Owner.EnsureComponentWarn(); - // Smaller than 1m^3 for now but realistically this shouldn't be hit MaxVolume = ReagentUnit.New(1000); @@ -143,7 +154,7 @@ namespace Content.Server.Fluids.Components void IExamine.Examine(FormattedMessage message, bool inDetailsRange) { - if(Slippery) + if (Slippery) { message.AddText(Loc.GetString("puddle-component-examine-is-slipper-text")); } @@ -160,13 +171,15 @@ namespace Content.Server.Fluids.Components } // Flow rate should probably be controlled globally so this is it for now - internal bool TryAddSolution(Solution solution, bool sound = true, bool checkForEvaporate = true, bool checkForOverflow = true) + internal bool TryAddSolution(Solution solution, bool sound = true, bool checkForEvaporate = true, + bool checkForOverflow = true) { if (solution.TotalVolume == 0) { return false; } - var result = _contents.TryAddSolution(solution); + + var result = EntitySystem.Get().TryAddSolution(Owner.Uid, PuddleSolution, solution); if (!result) { return false; @@ -194,12 +207,15 @@ namespace Content.Server.Fluids.Components return true; } - internal Solution SplitSolution(ReagentUnit quantity) + internal void SplitSolution(ReagentUnit quantity) { - var split = _contents.SplitSolution(quantity); - CheckEvaporate(); - UpdateAppearance(); - return split; + if (PuddleSolution != null) + { + EntitySystem.Get().SplitSolution(Owner.Uid, PuddleSolution, quantity); + CheckEvaporate(); + UpdateAppearance(); + } + } public void CheckEvaporate() @@ -212,7 +228,12 @@ namespace Content.Server.Fluids.Components public void Evaporate() { - _contents.SplitSolution(ReagentUnit.Min(ReagentUnit.New(1), _contents.CurrentVolume)); + if (PuddleSolution != null) + { + EntitySystem.Get().SplitSolution(Owner.Uid, PuddleSolution, + ReagentUnit.Min(ReagentUnit.New(1), PuddleSolution.CurrentVolume)); + } + if (CurrentVolume == 0) { Owner.Delete(); @@ -226,7 +247,7 @@ namespace Content.Server.Fluids.Components public void UpdateStatus() { _evaporationToken?.Cancel(); - if(Owner.Deleted) return; + if (Owner.Deleted) return; UpdateAppearance(); UpdateSlip(); @@ -262,15 +283,16 @@ namespace Content.Server.Fluids.Components { return; } + // Opacity based on level of fullness to overflow // Hard-cap lower bound for visibility reasons var volumeScale = (CurrentVolume.Float() / OverflowVolume.Float()) * 0.75f + 0.25f; var cappedScale = Math.Min(1.0f, volumeScale); // Color based on the underlying solutioncomponent Color newColor; - if (_recolor) + if (_recolor && PuddleSolution != null) { - newColor = _contents.Color.WithAlpha(cappedScale); + newColor = PuddleSolution.Color.WithAlpha(cappedScale); } else { @@ -287,12 +309,10 @@ namespace Content.Server.Fluids.Components /// private void CheckOverflow() { - if (CurrentVolume <= _overflowVolume || _overflown) - { + if (PuddleSolution == null || CurrentVolume <= _overflowVolume || _overflown) return; - } - var nextPuddles = new List() {this}; + var nextPuddles = new List() { this }; var overflownPuddles = new List(); while (OverflowLeft > ReagentUnit.Zero && nextPuddles.Count > 0) @@ -321,7 +341,7 @@ namespace Content.Server.Fluids.Components { var adjacentPuddle = adjacent(); var quantity = ReagentUnit.Min(overflowSplit, adjacentPuddle.OverflowVolume); - var spillAmount = _contents.SplitSolution(quantity); + var spillAmount = EntitySystem.Get().SplitSolution(Owner.Uid, PuddleSolution, quantity); adjacentPuddle.TryAddSolution(spillAmount, false, false, false); nextPuddles.Add(adjacentPuddle); @@ -390,7 +410,9 @@ namespace Content.Server.Fluids.Components if (puddle == default) { - puddle = () => Owner.EntityManager.SpawnEntity(Owner.Prototype?.ID, mapGrid.DirectionToGrid(coords, direction)).GetComponent(); + puddle = () => + Owner.EntityManager.SpawnEntity(Owner.Prototype?.ID, mapGrid.DirectionToGrid(coords, direction)) + .GetComponent(); } return true; diff --git a/Content.Server/Fluids/Components/SpillExtensions.cs b/Content.Server/Fluids/Components/SpillExtensions.cs index 2a7a354362..9fa4f8c263 100644 --- a/Content.Server/Fluids/Components/SpillExtensions.cs +++ b/Content.Server/Fluids/Components/SpillExtensions.cs @@ -1,9 +1,9 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Server.Coordinates.Helpers; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; -using Content.Shared.Chemistry.Solution.Components; using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.IoC; @@ -23,7 +23,8 @@ namespace Content.Server.Fluids.Components /// The prototype to use. /// Play the spill sound. /// The puddle if one was created, null otherwise. - public static PuddleComponent? SpillAt(this Solution solution, IEntity entity, string prototype, bool sound = true) + public static PuddleComponent? SpillAt(this Solution solution, IEntity entity, string prototype, + bool sound = true) { return solution.SpillAt(entity.Transform.Coordinates, prototype, sound); } @@ -39,7 +40,8 @@ namespace Content.Server.Fluids.Components /// The puddle if one was created, null otherwise. /// Play the spill sound. /// True if a puddle was created, false otherwise. - public static bool TrySpillAt(this Solution solution, IEntity entity, string prototype, [NotNullWhen(true)] out PuddleComponent? puddle, bool sound = true) + public static bool TrySpillAt(this Solution solution, IEntity entity, string prototype, + [NotNullWhen(true)] out PuddleComponent? puddle, bool sound = true) { puddle = solution.SpillAt(entity, prototype, sound); return puddle != null; @@ -53,14 +55,16 @@ namespace Content.Server.Fluids.Components /// The prototype to use. /// Whether or not to play the spill sound. /// The puddle if one was created, null otherwise. - public static PuddleComponent? SpillAt(this Solution solution, EntityCoordinates coordinates, string prototype, bool overflow = true, bool sound = true) + public static PuddleComponent? SpillAt(this Solution solution, EntityCoordinates coordinates, string prototype, + bool overflow = true, bool sound = true) { if (solution.TotalVolume == 0) return null; var mapManager = IoCManager.Resolve(); var entityManager = IoCManager.Resolve(); - if (!mapManager.TryGetGrid(coordinates.GetGridId(entityManager), out var mapGrid)) return null; // Let's not spill to space. + if (!mapManager.TryGetGrid(coordinates.GetGridId(entityManager), out var mapGrid)) + return null; // Let's not spill to space. return SpillAt(mapGrid.GetTileRef(coordinates), solution, prototype, overflow, sound); } @@ -74,13 +78,15 @@ namespace Content.Server.Fluids.Components /// The puddle if one was created, null otherwise. /// Play the spill sound. /// True if a puddle was created, false otherwise. - public static bool TrySpillAt(this Solution solution, EntityCoordinates coordinates, string prototype, [NotNullWhen(true)] out PuddleComponent? puddle, bool sound = true) + public static bool TrySpillAt(this Solution solution, EntityCoordinates coordinates, string prototype, + [NotNullWhen(true)] out PuddleComponent? puddle, bool sound = true) { puddle = solution.SpillAt(coordinates, prototype, sound); return puddle != null; } - public static bool TryGetPuddle(this TileRef tileRef, GridTileLookupSystem? gridTileLookupSystem, [NotNullWhen(true)] out PuddleComponent? puddle) + public static bool TryGetPuddle(this TileRef tileRef, GridTileLookupSystem? gridTileLookupSystem, + [NotNullWhen(true)] out PuddleComponent? puddle) { foreach (var entity in tileRef.GetEntitiesInTileFast(gridTileLookupSystem)) { @@ -95,7 +101,8 @@ namespace Content.Server.Fluids.Components return false; } - public static PuddleComponent? SpillAt(this TileRef tileRef, Solution solution, string prototype, bool overflow = true, bool sound = true) + public static PuddleComponent? SpillAt(this TileRef tileRef, Solution solution, string prototype, + bool overflow = true, bool sound = true) { if (solution.TotalVolume <= 0) return null; @@ -116,15 +123,18 @@ namespace Content.Server.Fluids.Components PuddleComponent? puddle = null; var spilt = false; - var spillEntities = IoCManager.Resolve().GetEntitiesIntersecting(mapGrid.ParentMapId, spillGridCoords.Position).ToArray(); + var spillEntities = IoCManager.Resolve() + .GetEntitiesIntersecting(mapGrid.ParentMapId, spillGridCoords.Position).ToArray(); foreach (var spillEntity in spillEntities) { - if (spillEntity.TryGetComponent(out ISolutionInteractionsComponent? solutionContainerComponent) && - solutionContainerComponent.CanRefill) + if (EntitySystem.Get() + .TryGetRefillableSolution(spillEntity.Uid, out var solutionContainerComponent)) { - solutionContainerComponent.Refill( - solution.SplitSolution(ReagentUnit.Min(solutionContainerComponent.RefillSpaceAvailable, solutionContainerComponent.MaxSpillRefill)) - ); + EntitySystem.Get().Refill(spillEntity.Uid, solutionContainerComponent, + solution.SplitSolution(ReagentUnit.Min( + solutionContainerComponent.AvailableVolume, + solutionContainerComponent.MaxSpillRefill)) + ); } } diff --git a/Content.Server/Fluids/Components/SpillableComponent.cs b/Content.Server/Fluids/Components/SpillableComponent.cs index bd5dca6d3d..678fecaa4c 100644 --- a/Content.Server/Fluids/Components/SpillableComponent.cs +++ b/Content.Server/Fluids/Components/SpillableComponent.cs @@ -1,9 +1,8 @@ using Content.Shared.ActionBlocker; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution.Components; -using Content.Shared.DragDrop; using Content.Shared.Interaction; -using Content.Shared.Interaction.Events; using Content.Shared.Notification.Managers; using Content.Shared.Verbs; using Robust.Shared.GameObjects; @@ -15,6 +14,7 @@ namespace Content.Server.Fluids.Components public class SpillableComponent : Component, IDropped { public override string Name => "Spillable"; + public const string SolutionName = "puddle"; /// /// Transfers solution from the held container to the floor. @@ -25,8 +25,8 @@ namespace Content.Server.Fluids.Components protected override void GetData(IEntity user, SpillableComponent component, VerbData data) { if (!EntitySystem.Get().CanInteract(user) || - !component.Owner.TryGetComponent(out ISolutionInteractionsComponent? solutionComponent) || - !solutionComponent.CanDrain) + !EntitySystem.Get() + .TryGetDrainableSolution(component.Owner.Uid, out var solutionComponent)) { data.Visibility = VerbVisibility.Invisible; return; @@ -40,30 +40,40 @@ namespace Content.Server.Fluids.Components protected override void Activate(IEntity user, SpillableComponent component) { - if (component.Owner.TryGetComponent(out var solutionComponent)) + var solutionsSys = EntitySystem.Get(); + if (component.Owner.HasComponent()) { - if (!solutionComponent.CanDrain) + if (solutionsSys.TryGetDrainableSolution(component.Owner.Uid, out var solutionComponent)) + { + if (solutionComponent.DrainAvailable <= 0) + { + user.PopupMessage(user, + Loc.GetString("spill-target-verb-activate-is-empty-message", ("owner", component.Owner))); + } + + // Need this as when we split the component's owner may be deleted + EntitySystem.Get() + .Drain(component.Owner.Uid, solutionComponent, solutionComponent.DrainAvailable) + .SpillAt(component.Owner.Transform.Coordinates, "PuddleSmear"); + } + else { user.PopupMessage(user, - Loc.GetString("spill-target-verb-activate-cannot-drain-message",("owner", component.Owner))); + Loc.GetString("spill-target-verb-activate-cannot-drain-message", + ("owner", component.Owner))); } - - if (solutionComponent.DrainAvailable <= 0) - { - user.PopupMessage(user, Loc.GetString("spill-target-verb-activate-is-empty-message",("owner", component.Owner))); - } - - // Need this as when we split the component's owner may be deleted - solutionComponent.Drain(solutionComponent.DrainAvailable).SpillAt(component.Owner.Transform.Coordinates, "PuddleSmear"); } } } void IDropped.Dropped(DroppedEventArgs eventArgs) { - if (!eventArgs.Intentional && Owner.TryGetComponent(out ISolutionInteractionsComponent? solutionComponent)) + if (!eventArgs.Intentional + && EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solutionComponent)) { - solutionComponent.Drain(solutionComponent.DrainAvailable).SpillAt(Owner.Transform.Coordinates, "PuddleSmear"); + EntitySystem.Get() + .Drain(Owner.Uid, solutionComponent, solutionComponent.DrainAvailable) + .SpillAt(Owner.Transform.Coordinates, "PuddleSmear"); } } } diff --git a/Content.Server/Fluids/Components/SprayComponent.cs b/Content.Server/Fluids/Components/SprayComponent.cs index 3c97947ca3..4179491fe7 100644 --- a/Content.Server/Fluids/Components/SprayComponent.cs +++ b/Content.Server/Fluids/Components/SprayComponent.cs @@ -4,9 +4,9 @@ using Content.Server.Chemistry.Components; using Content.Server.Chemistry.EntitySystems; using Content.Shared.ActionBlocker; using Content.Shared.Audio; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; using Content.Shared.Cooldown; -using Content.Shared.DragDrop; using Content.Shared.Fluids; using Content.Shared.Interaction; using Content.Shared.Notification.Managers; @@ -30,6 +30,7 @@ namespace Content.Server.Fluids.Components internal sealed class SprayComponent : SharedSprayComponent, IAfterInteract, IUse, IActivate, IDropped { public const float SprayDistance = 3f; + public const string SolutionName = "spray"; [Dependency] private readonly IGameTiming _gameTiming = default!; @@ -82,14 +83,19 @@ namespace Content.Server.Fluids.Components [DataField("safetySound")] public SoundSpecifier SafetySound { get; } = new SoundPathSpecifier("/Audio/Machines/button.ogg"); - public ReagentUnit CurrentVolume => Owner.GetComponentOrNull()?.CurrentVolume ?? ReagentUnit.Zero; + + public ReagentUnit CurrentVolume { + get + { + EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution); + return solution?.CurrentVolume ?? ReagentUnit.Zero; + } + } protected override void Initialize() { base.Initialize(); - Owner.EnsureComponentWarn(out SolutionContainerComponent _); - if (_hasSafety) { SetSafety(Owner, _safety); @@ -124,7 +130,7 @@ namespace Content.Server.Fluids.Components if (eventArgs.ClickLocation.GetGridId(entManager) != playerPos.GetGridId(entManager)) return true; - if (!Owner.TryGetComponent(out SolutionContainerComponent? contents)) + if (!EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var contents)) return true; var direction = (eventArgs.ClickLocation.Position - playerPos.Position).Normalized; @@ -148,7 +154,7 @@ namespace Content.Server.Fluids.Components if (target.TryDistance(Owner.EntityManager, playerPos, out var distance) && distance > SprayDistance) target = eventArgs.User.Transform.Coordinates.Offset(diffNorm * SprayDistance); - var solution = contents.SplitSolution(_transferAmount); + var solution = EntitySystem.Get().SplitSolution(Owner.Uid, contents, _transferAmount); if (solution.TotalVolume <= ReagentUnit.Zero) break; diff --git a/Content.Server/Kitchen/Components/ExtractableComponent.cs b/Content.Server/Kitchen/Components/ExtractableComponent.cs new file mode 100644 index 0000000000..bd47e8898e --- /dev/null +++ b/Content.Server/Kitchen/Components/ExtractableComponent.cs @@ -0,0 +1,27 @@ +using Content.Server.Kitchen.EntitySystems; +using Content.Shared.Chemistry.Components; +using Robust.Shared.Analyzers; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.ViewVariables; + +namespace Content.Server.Kitchen.Components +{ + /// + /// Tag component that denotes an entity as Extractable + /// + [RegisterComponent] + [Friend(typeof(ReagentGrinderSystem))] + public class ExtractableComponent : Component + { + public override string Name => "Extractable"; + + [ViewVariables] + [DataField("result")] + public Solution ResultSolution = new(); + + [ViewVariables] + [DataField("extractableSolution")] + public string? GrindableSolution; + } +} diff --git a/Content.Server/Kitchen/Components/JuiceableComponent.cs b/Content.Server/Kitchen/Components/JuiceableComponent.cs deleted file mode 100644 index c91d6af2cc..0000000000 --- a/Content.Server/Kitchen/Components/JuiceableComponent.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Content.Shared.Chemistry.Solution; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.ViewVariables; - -namespace Content.Server.Kitchen.Components -{ - /// - /// Tag component that denotes an entity as Juiceable - /// - [RegisterComponent] - public class JuiceableComponent : Component - { - public override string Name => "Juiceable"; - [ViewVariables] [DataField("result")] public Solution JuiceResultSolution = new(); - } -} diff --git a/Content.Server/Kitchen/Components/MicrowaveComponent.cs b/Content.Server/Kitchen/Components/MicrowaveComponent.cs index 38ea0f9fe6..36f1f81f05 100644 --- a/Content.Server/Kitchen/Components/MicrowaveComponent.cs +++ b/Content.Server/Kitchen/Components/MicrowaveComponent.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -13,14 +12,12 @@ using Content.Server.UserInterface; using Content.Shared.Acts; using Content.Shared.Body.Components; using Content.Shared.Body.Part; -using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; -using Content.Shared.Chemistry.Solution.Components; using Content.Shared.Interaction; using Content.Shared.Kitchen; using Content.Shared.Kitchen.Components; -using Content.Shared.Notification; using Content.Shared.Notification.Managers; using Content.Shared.Power; using Content.Shared.Sound; @@ -38,27 +35,28 @@ namespace Content.Server.Kitchen.Components { [RegisterComponent] [ComponentReference(typeof(IActivate))] - public class MicrowaveComponent : SharedMicrowaveComponent, IActivate, IInteractUsing, ISolutionChange, ISuicideAct, IBreakAct + public class MicrowaveComponent : SharedMicrowaveComponent, IActivate, IInteractUsing, ISuicideAct, IBreakAct { [Dependency] private readonly RecipeManager _recipeManager = default!; #region YAMLSERIALIZE - [DataField("cookTime")] - private uint _cookTimeDefault = 5; - [DataField("cookTimeMultiplier")] - private int _cookTimeMultiplier = 1000; //For upgrades and stuff I guess? - [DataField("failureResult")] - private string _badRecipeName = "FoodBadRecipe"; - [DataField("beginCookingSound")] - private SoundSpecifier _startCookingSound = new SoundPathSpecifier("/Audio/Machines/microwave_start_beep.ogg"); - [DataField("foodDoneSound")] - private SoundSpecifier _cookingCompleteSound = new SoundPathSpecifier("/Audio/Machines/microwave_done_beep.ogg"); + + [DataField("cookTime")] private uint _cookTimeDefault = 5; + [DataField("cookTimeMultiplier")] private int _cookTimeMultiplier = 1000; //For upgrades and stuff I guess? + [DataField("failureResult")] private string _badRecipeName = "FoodBadRecipe"; + + [DataField("beginCookingSound")] private SoundSpecifier _startCookingSound = + new SoundPathSpecifier("/Audio/Machines/microwave_start_beep.ogg"); + + [DataField("foodDoneSound")] private SoundSpecifier _cookingCompleteSound = + new SoundPathSpecifier("/Audio/Machines/microwave_done_beep.ogg"); + [DataField("clickSound")] private SoundSpecifier _clickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg"); + #endregion YAMLSERIALIZE - [ViewVariables] - private bool _busy = false; + [ViewVariables] private bool _busy = false; private bool _broken; /// @@ -66,18 +64,25 @@ namespace Content.Server.Kitchen.Components /// The cook times for all recipes should be divisible by 5,with a minimum of 1 second. /// For right now, I don't think any recipe cook time should be greater than 60 seconds. /// - [ViewVariables] - private uint _currentCookTimerTime = 1; + [ViewVariables] private uint _currentCookTimerTime = 1; private bool Powered => !Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || receiver.Powered; - private bool _hasContents => Owner.TryGetComponent(out SolutionContainerComponent? solution) && (solution.ReagentList.Count > 0 || _storage.ContainedEntities.Count > 0); - private bool _uiDirty = true; - private bool _lostPower = false; - private int _currentCookTimeButtonIndex = 0; - void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) => _uiDirty = true; - private AudioSystem _audioSystem = default!; + private bool HasContents => EntitySystem.Get() + .TryGetSolution(Owner, SolutionName, out var solution) && + (solution.Contents.Count > 0 || _storage.ContainedEntities.Count > 0); + + private bool _uiDirty = true; + private bool _lostPower; + private int _currentCookTimeButtonIndex; + + public void DirtyUi() + { + _uiDirty = true; + } + private Container _storage = default!; + private const string SolutionName = "microwave"; [ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(MicrowaveUiKey.Key); @@ -87,10 +92,10 @@ namespace Content.Server.Kitchen.Components _currentCookTimerTime = _cookTimeDefault; - Owner.EnsureComponent(); + EntitySystem.Get().EnsureSolution(Owner, SolutionName); - _storage = ContainerHelpers.EnsureContainer(Owner, "microwave_entity_container", out var existed); - _audioSystem = EntitySystem.Get(); + _storage = ContainerHelpers.EnsureContainer(Owner, "microwave_entity_container", + out _); if (UserInterface != null) { @@ -107,33 +112,36 @@ namespace Content.Server.Kitchen.Components switch (message.Message) { - case MicrowaveStartCookMessage msg: + case MicrowaveStartCookMessage: Wzhzhzh(); break; - case MicrowaveEjectMessage msg: - if (_hasContents) + case MicrowaveEjectMessage: + if (HasContents) { VaporizeReagents(); EjectSolids(); ClickSound(); _uiDirty = true; } + break; case MicrowaveEjectSolidIndexedMessage msg: - if (_hasContents) + if (HasContents) { EjectSolid(msg.EntityID); ClickSound(); _uiDirty = true; } + break; case MicrowaveVaporizeReagentIndexedMessage msg: - if (_hasContents) + if (HasContents) { VaporizeReagentQuantity(msg.ReagentQuantity); ClickSound(); _uiDirty = true; } + break; case MicrowaveSelectCookTimeMessage msg: _currentCookTimeButtonIndex = msg.ButtonIndex; @@ -142,12 +150,10 @@ namespace Content.Server.Kitchen.Components _uiDirty = true; break; } - } public void OnUpdate() { - if (!Powered) { //TODO:If someone cuts power currently, microwave magically keeps going. FIX IT! @@ -175,11 +181,12 @@ namespace Content.Server.Kitchen.Components _uiDirty = true; } - if (_uiDirty && Owner.TryGetComponent(out SolutionContainerComponent? solution)) + if (_uiDirty && EntitySystem.Get() + .TryGetSolution(Owner, SolutionName, out var solution)) { UserInterface?.SetState(new MicrowaveUpdateUserInterfaceState ( - solution.Solution.Contents.ToArray(), + solution.Contents.ToArray(), _storage.ContainedEntities.Select(item => item.Uid).ToArray(), _busy, _currentCookTimeButtonIndex, @@ -244,40 +251,41 @@ namespace Content.Server.Kitchen.Components if (itemEntity.TryGetComponent(out var attackPourable)) { - if (!itemEntity.TryGetComponent(out var attackSolution) - || !attackSolution.CanDrain) + var solutionsSystem = EntitySystem.Get(); + if (!solutionsSystem.TryGetDrainableSolution(itemEntity.Uid, out var attackSolution)) { return false; } - if (!Owner.TryGetComponent(out SolutionContainerComponent? solution)) + if (!solutionsSystem.TryGetSolution(Owner, SolutionName, out var solution)) { return false; } //Get transfer amount. May be smaller than _transferAmount if not enough room - var realTransferAmount = ReagentUnit.Min(attackPourable.TransferAmount, solution.EmptyVolume); + var realTransferAmount = ReagentUnit.Min(attackPourable.TransferAmount, solution.AvailableVolume); if (realTransferAmount <= 0) //Special message if container is full { - Owner.PopupMessage(eventArgs.User, Loc.GetString("microwave-component-interact-using-container-full")); + Owner.PopupMessage(eventArgs.User, + Loc.GetString("microwave-component-interact-using-container-full")); return false; } //Move units from attackSolution to targetSolution - var removedSolution = attackSolution.Drain(realTransferAmount); - if (!solution.TryAddSolution(removedSolution)) + var removedSolution = EntitySystem.Get() + .Drain(itemEntity.Uid, attackSolution, realTransferAmount); + if (!EntitySystem.Get().TryAddSolution(Owner.Uid, solution, removedSolution)) { return false; } Owner.PopupMessage(eventArgs.User, Loc.GetString("microwave-component-interact-using-transfer-success", - ("amount", removedSolution.TotalVolume))); + ("amount", removedSolution.TotalVolume))); return true; } if (!itemEntity.TryGetComponent(typeof(ItemComponent), out var food)) { - Owner.PopupMessage(eventArgs.User, "microwave-component-interact-using-transfer-fail"); return false; } @@ -292,7 +300,7 @@ namespace Content.Server.Kitchen.Components // ReSharper disable once IdentifierTypo private void Wzhzhzh() { - if (!_hasContents) + if (!HasContents) { return; } @@ -331,14 +339,15 @@ namespace Content.Server.Kitchen.Components // Check recipes FoodRecipePrototype? recipeToCook = null; - foreach (var r in _recipeManager.Recipes.Where(r => CanSatisfyRecipe(r, solidsDict) == MicrowaveSuccessState.RecipePass)) + foreach (var r in _recipeManager.Recipes.Where(r => + CanSatisfyRecipe(r, solidsDict) == MicrowaveSuccessState.RecipePass)) { recipeToCook = r; } SetAppearance(MicrowaveVisualState.Cooking); SoundSystem.Play(Filter.Pvs(Owner), _startCookingSound.GetSound(), Owner, AudioParams.Default); - Owner.SpawnTimer((int) (_currentCookTimerTime * _cookTimeMultiplier), (Action) (() => + Owner.SpawnTimer((int) (_currentCookTimerTime * _cookTimeMultiplier), () => { if (_lostPower) { @@ -372,24 +381,25 @@ namespace Content.Server.Kitchen.Components _busy = false; _uiDirty = true; - })); + }); _lostPower = false; _uiDirty = true; } private void VaporizeReagents() { - if (Owner.TryGetComponent(out SolutionContainerComponent? solution)) + if (EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution)) { - solution.RemoveAllSolution(); + EntitySystem.Get().RemoveAllSolution(Owner.Uid, solution); } } private void VaporizeReagentQuantity(Solution.ReagentQuantity reagentQuantity) { - if (Owner.TryGetComponent(out SolutionContainerComponent? solution)) + if (EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution)) { - solution?.TryRemoveReagent(reagentQuantity.ReagentId, reagentQuantity.Quantity); + EntitySystem.Get() + .TryRemoveReagent(Owner.Uid, solution, reagentQuantity.ReagentId, reagentQuantity.Quantity); } } @@ -405,32 +415,32 @@ namespace Content.Server.Kitchen.Components private void EjectSolids() { - for (var i = _storage.ContainedEntities.Count - 1; i >= 0; i--) { _storage.Remove(_storage.ContainedEntities.ElementAt(i)); } } - private void EjectSolid(EntityUid entityID) + private void EjectSolid(EntityUid entityId) { - if (Owner.EntityManager.EntityExists(entityID)) + if (Owner.EntityManager.EntityExists(entityId)) { - _storage.Remove(Owner.EntityManager.GetEntity(entityID)); + _storage.Remove(Owner.EntityManager.GetEntity(entityId)); } } - private void SubtractContents(FoodRecipePrototype recipe) { - if (!Owner.TryGetComponent(out SolutionContainerComponent? solution)) + var solutionUid = Owner.Uid; + if (!EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution)) { return; } foreach (var recipeReagent in recipe.IngredientsReagents) { - solution?.TryRemoveReagent(recipeReagent.Key, ReagentUnit.New(recipeReagent.Value)); + EntitySystem.Get() + .TryRemoveReagent(solutionUid, solution, recipeReagent.Key, ReagentUnit.New(recipeReagent.Value)); } foreach (var recipeSolid in recipe.IngredientsSolids) @@ -453,7 +463,6 @@ namespace Content.Server.Kitchen.Components } } } - } private MicrowaveSuccessState CanSatisfyRecipe(FoodRecipePrototype recipe, Dictionary solids) @@ -463,14 +472,14 @@ namespace Content.Server.Kitchen.Components return MicrowaveSuccessState.RecipeFail; } - if (!Owner.TryGetComponent(out SolutionContainerComponent? solution)) + if (!EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution)) { return MicrowaveSuccessState.RecipeFail; } foreach (var reagent in recipe.IngredientsReagents) { - if (!solution.Solution.ContainsReagent(reagent.Key, out var amount)) + if (!solution.ContainsReagent(reagent.Key, out var amount)) { return MicrowaveSuccessState.RecipeFail; } diff --git a/Content.Server/Kitchen/Components/ReagentGrinderComponent.cs b/Content.Server/Kitchen/Components/ReagentGrinderComponent.cs index 51cc7b0b1d..2511ccf781 100644 --- a/Content.Server/Kitchen/Components/ReagentGrinderComponent.cs +++ b/Content.Server/Kitchen/Components/ReagentGrinderComponent.cs @@ -1,5 +1,4 @@ -using Content.Server.Chemistry.Components; -using Content.Shared.Interaction; +using Content.Shared.Chemistry.Components; using Content.Shared.Kitchen.Components; using Content.Shared.Sound; using Robust.Shared.Containers; @@ -9,7 +8,6 @@ using Robust.Shared.ViewVariables; namespace Content.Server.Kitchen.Components { - /// /// The combo reagent grinder/juicer. The reason why grinding and juicing are seperate is simple, /// think of grinding as a utility to break an object down into its reagents. Think of juicing as @@ -24,7 +22,7 @@ namespace Content.Server.Kitchen.Components /// /// Can be null since we won't always have a beaker in the grinder. /// - [ViewVariables] public SolutionContainerComponent? HeldBeaker = default!; + [ViewVariables] public Solution? HeldBeaker = default!; /// /// Contains the things that are going to be ground or juiced. diff --git a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs index 7a1791c786..140bbc482b 100644 --- a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs @@ -1,4 +1,5 @@ using Content.Server.Kitchen.Components; +using Content.Shared.Chemistry.EntitySystems; using JetBrains.Annotations; using Robust.Shared.GameObjects; @@ -7,6 +8,18 @@ namespace Content.Server.Kitchen.EntitySystems [UsedImplicitly] internal sealed class MicrowaveSystem : EntitySystem { + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSolutionChange); + } + + private void OnSolutionChange(EntityUid uid, MicrowaveComponent component, SolutionChangedEvent args) + { + component.DirtyUi(); + } + public override void Update(float frameTime) { base.Update(frameTime); diff --git a/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs b/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs index c13d5574fa..3cb133828c 100644 --- a/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs @@ -1,20 +1,18 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; -using Content.Server.Chemistry.Components; using Content.Server.Hands.Components; using Content.Server.Items; using Content.Server.Kitchen.Components; +using Content.Server.Kitchen.Events; using Content.Server.Power.Components; using Content.Server.Stack; using Content.Server.UserInterface; -using Content.Server.Kitchen.Events; -using Content.Shared.Chemistry.Solution; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Kitchen.Components; using Content.Shared.Notification.Managers; using Content.Shared.Random.Helpers; -using Content.Shared.Tag; using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Shared.Audio; @@ -30,7 +28,7 @@ namespace Content.Server.Kitchen.EntitySystems [UsedImplicitly] internal sealed class ReagentGrinderSystem : EntitySystem { - [Dependency] private readonly IEntityManager _entityManager = default!; + [Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!; private Queue _uiUpdateQueue = new(); @@ -39,13 +37,14 @@ namespace Content.Server.Kitchen.EntitySystems base.Initialize(); SubscribeLocalEvent(OnComponentInit); - SubscribeLocalEvent((_, component, _) => EnqueueUiUpdate(component)); + SubscribeLocalEvent((_, component, _) => + EnqueueUiUpdate(component)); SubscribeLocalEvent(OnInteractHand); SubscribeLocalEvent(OnInteractUsing); - SubscribeLocalEvent(JuiceableScaling); + SubscribeLocalEvent(ExtractableScaling); } - private void JuiceableScaling(EntityUid uid, StackComponent component, JuiceableScalingEvent args) + private void ExtractableScaling(EntityUid uid, StackComponent component, ExtractableScalingEvent args) { args.Scalar *= component.Count; // multiply scalar by amount of items in stack } @@ -54,19 +53,20 @@ namespace Content.Server.Kitchen.EntitySystems { if (args.Handled) return; - if (!args.User.TryGetComponent(out IHandsComponent? hands)) + if (!args.User.HasComponent()) { - component.Owner.PopupMessage(args.User, Loc.GetString("reagent-grinder-component-interact-using-no-hands")); + component.Owner.PopupMessage(args.User, + Loc.GetString("reagent-grinder-component-interact-using-no-hands")); args.Handled = true; return; } IEntity heldEnt = args.Used; - //First, check if user is trying to insert a beaker. - //No promise it will be a beaker right now, but whatever. - //Maybe this should whitelist "beaker" in the prototype id of heldEnt? - if (heldEnt.TryGetComponent(out SolutionContainerComponent? beaker) && beaker.Capabilities.HasFlag(SolutionContainerCaps.FitsInDispenser)) + // First, check if user is trying to insert a beaker. + // No promise it will be a beaker right now, but whatever. + // Maybe this should whitelist "beaker" in the prototype id of heldEnt? + if (_solutionsSystem.TryGetFitsInDispenser(heldEnt.Uid, out var beaker)) { component.BeakerContainer.Insert(heldEnt); component.HeldBeaker = beaker; @@ -74,15 +74,17 @@ namespace Content.Server.Kitchen.EntitySystems //We are done, return. Insert the beaker and exit! if (component.Owner.TryGetComponent(out AppearanceComponent? appearance)) { - appearance.SetData(SharedReagentGrinderComponent.ReagentGrinderVisualState.BeakerAttached, component.BeakerContainer.ContainedEntity != null); + appearance.SetData(SharedReagentGrinderComponent.ReagentGrinderVisualState.BeakerAttached, + component.BeakerContainer.ContainedEntity != null); } + ClickSound(component); args.Handled = true; return; } //Next, see if the user is trying to insert something they want to be ground/juiced. - if (!heldEnt.HasTag("Grindable") && !heldEnt.TryGetComponent(out JuiceableComponent? juice)) + if (!heldEnt.TryGetComponent(out ExtractableComponent? juice)) { //Entity did NOT pass the whitelist for grind/juice. //Wouldn't want the clown grinding up the Captain's ID card now would you? @@ -114,8 +116,10 @@ namespace Content.Server.Kitchen.EntitySystems { return; } + EnqueueUiUpdate(component); - component.Owner.GetUIOrNull(SharedReagentGrinderComponent.ReagentGrinderUiKey.Key)?.Toggle(actor.PlayerSession); + component.Owner.GetUIOrNull(SharedReagentGrinderComponent.ReagentGrinderUiKey.Key) + ?.Toggle(actor.PlayerSession); args.Handled = true; } @@ -130,11 +134,13 @@ namespace Content.Server.Kitchen.EntitySystems //A slot for the beaker where the grounds/juices will go. component.BeakerContainer = - ContainerHelpers.EnsureContainer(component.Owner, $"{component.Name}-reagentContainerContainer"); + ContainerHelpers.EnsureContainer(component.Owner, + $"{component.Name}-reagentContainerContainer"); //A container for the things that WILL be ground/juiced. Useful for ejecting them instead of deleting them from the hands of the user. component.Chamber = - ContainerHelpers.EnsureContainer(component.Owner, $"{component.Name}-entityContainerContainer"); + ContainerHelpers.EnsureContainer(component.Owner, + $"{component.Name}-entityContainerContainer"); var bui = component.Owner.GetUIOrNull(SharedReagentGrinderComponent.ReagentGrinderUiKey.Key); if (bui != null) @@ -154,15 +160,19 @@ namespace Content.Server.Kitchen.EntitySystems switch (message.Message) { case SharedReagentGrinderComponent.ReagentGrinderGrindStartMessage msg: - if (!component.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || !receiver.Powered) break; + if (!component.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || + !receiver.Powered) break; ClickSound(component); - DoWork(component, message.Session.AttachedEntity!, SharedReagentGrinderComponent.GrinderProgram.Grind); + DoWork(component, message.Session.AttachedEntity!, + SharedReagentGrinderComponent.GrinderProgram.Grind); break; case SharedReagentGrinderComponent.ReagentGrinderJuiceStartMessage msg: - if (!component.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver2) || !receiver2.Powered) break; + if (!component.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver2) || + !receiver2.Powered) break; ClickSound(component); - DoWork(component, message.Session.AttachedEntity!, SharedReagentGrinderComponent.GrinderProgram.Juice); + DoWork(component, message.Session.AttachedEntity!, + SharedReagentGrinderComponent.GrinderProgram.Juice); break; case SharedReagentGrinderComponent.ReagentGrinderEjectChamberAllMessage msg: @@ -175,8 +185,10 @@ namespace Content.Server.Kitchen.EntitySystems component.Chamber.Remove(entity); entity.RandomOffset(0.4f); } + EnqueueUiUpdate(component); } + break; case SharedReagentGrinderComponent.ReagentGrinderEjectChamberContentMessage msg: @@ -187,6 +199,7 @@ namespace Content.Server.Kitchen.EntitySystems EnqueueUiUpdate(component); ClickSound(component); } + break; case SharedReagentGrinderComponent.ReagentGrinderEjectBeakerMessage msg: @@ -203,7 +216,7 @@ namespace Content.Server.Kitchen.EntitySystems while (_uiUpdateQueue.TryDequeue(out var comp)) { - if(comp.Deleted) + if (comp.Deleted) continue; bool canJuice = false; @@ -212,23 +225,25 @@ namespace Content.Server.Kitchen.EntitySystems { foreach (var entity in comp.Chamber.ContainedEntities) { - if (!canJuice && entity.HasComponent()) canJuice = true; - if (!canGrind && entity.HasTag("Grindable")) canGrind = true; - if (canJuice && canGrind) break; + if (canJuice || !entity.TryGetComponent(out ExtractableComponent? component)) continue; + + canJuice = component.GrindableSolution == null; + canGrind = component.GrindableSolution != null; } } - comp.Owner.GetUIOrNull(SharedReagentGrinderComponent.ReagentGrinderUiKey.Key)?.SetState(new ReagentGrinderInterfaceState - ( - comp.Busy, - comp.BeakerContainer.ContainedEntity != null, - comp.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) && receiver.Powered, - canJuice, - canGrind, - comp.Chamber.ContainedEntities.Select(item => item.Uid).ToArray(), - //Remember the beaker can be null! - comp.HeldBeaker?.Solution.Contents.ToArray() - )); + comp.Owner.GetUIOrNull(SharedReagentGrinderComponent.ReagentGrinderUiKey.Key)?.SetState( + new ReagentGrinderInterfaceState + ( + comp.Busy, + comp.BeakerContainer.ContainedEntity != null, + comp.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) && receiver.Powered, + canJuice, + canGrind, + comp.Chamber.ContainedEntities.Select(item => item.Uid).ToArray(), + //Remember the beaker can be null! + comp.HeldBeaker?.Contents.ToArray() + )); } } @@ -247,7 +262,8 @@ namespace Content.Server.Kitchen.EntitySystems component.BeakerContainer.Remove(beaker); - if (user == null || !user.TryGetComponent(out var hands) || !component.HeldBeaker.Owner.TryGetComponent(out var item)) + if (user == null || !user.TryGetComponent(out var hands) || + !beaker.TryGetComponent(out var item)) return; hands.PutInHandOrDrop(item); @@ -255,7 +271,8 @@ namespace Content.Server.Kitchen.EntitySystems EnqueueUiUpdate(component); if (component.Owner.TryGetComponent(out AppearanceComponent? appearance)) { - appearance.SetData(SharedReagentGrinderComponent.ReagentGrinderVisualState.BeakerAttached, component.BeakerContainer.ContainedEntity != null); + appearance.SetData(SharedReagentGrinderComponent.ReagentGrinderVisualState.BeakerAttached, + component.BeakerContainer.ContainedEntity != null); } } @@ -263,10 +280,13 @@ namespace Content.Server.Kitchen.EntitySystems /// The wzhzhzh of the grinder. Processes the contents of the grinder and puts the output in the beaker. /// /// true for wanting to juice, false for wanting to grind. - private void DoWork(ReagentGrinderComponent component, IEntity user, SharedReagentGrinderComponent.GrinderProgram program) + private void DoWork(ReagentGrinderComponent component, IEntity user, + SharedReagentGrinderComponent.GrinderProgram program) { //Have power, are we busy, chamber has anything to grind, a beaker for the grounds to go? - if (!component.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || !receiver.Powered || component.Busy || component.Chamber.ContainedEntities.Count <= 0 || component.BeakerContainer.ContainedEntity == null || component.HeldBeaker == null) + if (!component.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || !receiver.Powered || + component.Busy || component.Chamber.ContainedEntities.Count <= 0 || + component.BeakerContainer.ContainedEntity == null || component.HeldBeaker == null) { return; } @@ -275,25 +295,31 @@ namespace Content.Server.Kitchen.EntitySystems var bui = component.Owner.GetUIOrNull(SharedReagentGrinderComponent.ReagentGrinderUiKey.Key); bui?.SendMessage(new SharedReagentGrinderComponent.ReagentGrinderWorkStartedMessage(program)); + var beakerEntity = component.BeakerContainer.ContainedEntity; switch (program) { case SharedReagentGrinderComponent.GrinderProgram.Grind: SoundSystem.Play(Filter.Pvs(component.Owner), component.GrindSound.GetSound(), component.Owner, AudioParams.Default); - //Get each item inside the chamber and get the reagents it contains. Transfer those reagents to the beaker, given we have one in. + // Get each item inside the chamber and get the reagents it contains. + // Transfer those reagents to the beaker, given we have one in. component.Owner.SpawnTimer(component.WorkTime, (Action) (() => { foreach (var item in component.Chamber.ContainedEntities.ToList()) { - if (!item.HasTag("Grindable")) continue; - if (!item.TryGetComponent(out var solution)) continue; - var juiceEvent = new JuiceableScalingEvent(); // default of scalar is always 1.0 - RaiseLocalEvent(item.Uid, juiceEvent, false); - if (component.HeldBeaker.CurrentVolume + solution.CurrentVolume * juiceEvent.Scalar > component.HeldBeaker.MaxVolume) continue; - solution.Solution.ScaleSolution(juiceEvent.Scalar); - component.HeldBeaker.TryAddSolution(solution.Solution); - solution.RemoveAllSolution(); + if (!item.TryGetComponent(out ExtractableComponent? extract) + || extract.GrindableSolution == null + || !_solutionsSystem.TryGetSolution(item, extract.GrindableSolution, out var solution)) continue; + + var juiceEvent = new ExtractableScalingEvent(); // default of scalar is always 1.0 + RaiseLocalEvent(item.Uid, juiceEvent, false); + if (component.HeldBeaker.CurrentVolume + solution.CurrentVolume * juiceEvent.Scalar > + component.HeldBeaker.MaxVolume) continue; + solution.ScaleSolution(juiceEvent.Scalar); + _solutionsSystem.TryAddSolution(beakerEntity.Uid, component.HeldBeaker, solution); + _solutionsSystem.RemoveAllSolution(beakerEntity.Uid, solution); item.Delete(); } + component.Busy = false; EnqueueUiUpdate(component); bui?.SendMessage(new SharedReagentGrinderComponent.ReagentGrinderWorkCompleteMessage()); @@ -306,17 +332,21 @@ namespace Content.Server.Kitchen.EntitySystems { foreach (var item in component.Chamber.ContainedEntities.ToList()) { - if (!item.TryGetComponent(out var juiceMe)) continue; - var juiceEvent = new JuiceableScalingEvent(); // default of scalar is always 1.0 + if (!item.TryGetComponent(out var juiceMe)) continue; + var juiceEvent = new ExtractableScalingEvent(); // default of scalar is always 1.0 if (item.HasComponent()) { - RaiseLocalEvent(item.Uid, juiceEvent); + RaiseLocalEvent(item.Uid, juiceEvent); } - if (component.HeldBeaker.CurrentVolume + juiceMe.JuiceResultSolution.TotalVolume * juiceEvent.Scalar > component.HeldBeaker.MaxVolume) continue; - juiceMe.JuiceResultSolution.ScaleSolution(juiceEvent.Scalar); - component.HeldBeaker.TryAddSolution(juiceMe.JuiceResultSolution); + + if (component.HeldBeaker.CurrentVolume + + juiceMe.ResultSolution.TotalVolume * juiceEvent.Scalar > + component.HeldBeaker.MaxVolume) continue; + juiceMe.ResultSolution.ScaleSolution(juiceEvent.Scalar); + _solutionsSystem.TryAddSolution(beakerEntity.Uid, component.HeldBeaker, juiceMe.ResultSolution); item.Delete(); } + bui?.SendMessage(new SharedReagentGrinderComponent.ReagentGrinderWorkCompleteMessage()); component.Busy = false; EnqueueUiUpdate(component); diff --git a/Content.Server/Kitchen/Events/JuiceableScalingEvent.cs b/Content.Server/Kitchen/Events/ExtractableScalingEvent.cs similarity index 76% rename from Content.Server/Kitchen/Events/JuiceableScalingEvent.cs rename to Content.Server/Kitchen/Events/ExtractableScalingEvent.cs index f904097e0a..fdef6c9c2a 100644 --- a/Content.Server/Kitchen/Events/JuiceableScalingEvent.cs +++ b/Content.Server/Kitchen/Events/ExtractableScalingEvent.cs @@ -5,10 +5,10 @@ namespace Content.Server.Kitchen.Events /// /// Used in scaling amount of solution to extract in juicing /// - public class JuiceableScalingEvent : EntityEventArgs + public class ExtractableScalingEvent : EntityEventArgs { - public JuiceableScalingEvent() + public ExtractableScalingEvent() { Scalar = 1f; } diff --git a/Content.Server/Nutrition/Components/DrinkComponent.cs b/Content.Server/Nutrition/Components/DrinkComponent.cs index 0a1a9fc39c..b1c565e963 100644 --- a/Content.Server/Nutrition/Components/DrinkComponent.cs +++ b/Content.Server/Nutrition/Components/DrinkComponent.cs @@ -2,16 +2,13 @@ using System.Linq; using System.Threading.Tasks; using Content.Server.Body.Behavior; using Content.Server.Fluids.Components; -using Content.Shared.Audio; using Content.Shared.Body.Components; -using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; -using Content.Shared.Chemistry.Solution.Components; using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Interaction.Helpers; -using Content.Shared.Notification; using Content.Shared.Notification.Managers; using Content.Shared.Nutrition.Components; using Content.Shared.Sound; @@ -23,7 +20,6 @@ using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Player; -using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Utility; @@ -32,11 +28,14 @@ using Robust.Shared.ViewVariables; namespace Content.Server.Nutrition.Components { [RegisterComponent] - public class DrinkComponent : Component, IUse, IAfterInteract, ISolutionChange, IExamine, ILand + public class DrinkComponent : Component, IUse, IAfterInteract, IExamine, ILand { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; + [DataField("solution")] + public string SolutionName { get; set; } = DefaultSolutionName; + public const string DefaultSolutionName = "drink"; + public override string Name => "Drink"; int IAfterInteract.Priority => 10; @@ -50,7 +49,7 @@ namespace Content.Server.Nutrition.Components [ViewVariables] [DataField("isOpen")] - private bool _defaultToOpened; + internal bool DefaultToOpened; [ViewVariables(VVAccess.ReadWrite)] public ReagentUnit TransferAmount { get; [UsedImplicitly] private set; } = ReagentUnit.New(5); @@ -71,8 +70,14 @@ namespace Content.Server.Nutrition.Components } } - [ViewVariables] - public bool Empty => Owner.GetComponentOrNull()?.DrainAvailable <= 0; + [ViewVariables] public bool Empty => IsEmpty(); + + private bool IsEmpty() + { + var drainAvailable = EntitySystem.Get() + .DrainAvailable(Owner); + return drainAvailable <= 0; + } [DataField("openSounds")] private SoundSpecifier _openSounds = new SoundCollectionSpecifier("canOpenSounds"); @@ -81,45 +86,39 @@ namespace Content.Server.Nutrition.Components [DataField("burstSound")] private SoundSpecifier _burstSound = new SoundPathSpecifier("/Audio/Effects/flash_bang.ogg"); - protected override void Initialize() - { - base.Initialize(); - - Opened = _defaultToOpened; - UpdateAppearance(); - } - private void OpenedChanged() { - if (!Owner.TryGetComponent(out SharedSolutionContainerComponent? contents)) + var solutionSys = EntitySystem.Get(); + if (!solutionSys.TryGetSolution(Owner, SolutionName, out _)) { return; } if (Opened) { - contents.Capabilities |= SolutionContainerCaps.Refillable | SolutionContainerCaps.Drainable; + var refillable = Owner.EnsureComponent(); + refillable.Solution = SolutionName; + var drainable = Owner.EnsureComponent(); + drainable.Solution = SolutionName; } else { - contents.Capabilities &= ~(SolutionContainerCaps.Refillable | SolutionContainerCaps.Drainable); + Owner.RemoveComponent(); + Owner.RemoveComponent(); } } - void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) - { - UpdateAppearance(); - } - - private void UpdateAppearance() + // TODO move to DrinkSystem + public void UpdateAppearance() { if (!Owner.TryGetComponent(out AppearanceComponent? appearance) || - !Owner.TryGetComponent(out ISolutionInteractionsComponent? contents)) + !Owner.HasComponent()) { return; } - appearance.SetData(SharedFoodComponent.FoodVisuals.Visual, contents.DrainAvailable.Float()); + var drainAvailable = EntitySystem.Get().DrainAvailable(Owner); + appearance.SetData(SharedFoodComponent.FoodVisuals.Visual, drainAvailable.Float()); } bool IUse.UseEntity(UseEntityEventArgs args) @@ -133,8 +132,8 @@ namespace Content.Server.Nutrition.Components return false; } - if (!Owner.TryGetComponent(out ISolutionInteractionsComponent? contents) || - contents.DrainAvailable <= 0) + if (!Owner.HasComponent() || + EntitySystem.Get().DrainAvailable(Owner) <= 0) { args.User.PopupMessage(Loc.GetString("drink-component-on-use-is-empty", ("owner", Owner))); return true; @@ -160,8 +159,10 @@ namespace Content.Server.Nutrition.Components { return; } + var color = Empty ? "gray" : "yellow"; - var openedText = Loc.GetString(Empty ? "drink-component-on-examine-is-empty" : "drink-component-on-examine-is-opened"); + var openedText = + Loc.GetString(Empty ? "drink-component-on-examine-is-empty" : "drink-component-on-examine-is-opened"); message.AddMarkup(Loc.GetString("drink-component-on-examine-details-text", ("colorName", color), ("text", openedText))); } @@ -173,8 +174,7 @@ namespace Content.Server.Nutrition.Components return false; } - if (!Owner.TryGetComponent(out ISolutionInteractionsComponent? interactions) || - !interactions.CanDrain || + if (!EntitySystem.Get().TryGetDrainableSolution(Owner.Uid, out var interactions) || interactions.DrainAvailable <= 0) { if (!forced) @@ -199,8 +199,9 @@ namespace Content.Server.Nutrition.Components return false; } + var solutionContainerSystem = EntitySystem.Get(); var transferAmount = ReagentUnit.Min(TransferAmount, interactions.DrainAvailable); - var drain = interactions.Drain(transferAmount); + var drain = solutionContainerSystem.Drain(Owner.Uid, interactions, transferAmount); var firstStomach = stomachs.FirstOrDefault(stomach => stomach.CanTransferSolution(drain)); // All stomach are full or can't handle whatever solution we have. @@ -208,13 +209,14 @@ namespace Content.Server.Nutrition.Components { target.PopupMessage(Loc.GetString("drink-component-try-use-drink-had-enough", ("owner", Owner))); - if (!interactions.CanRefill) + if (Owner.EntityManager.TryGetEntity(Owner.Uid, out var interactionEntity) + && !interactionEntity.HasComponent()) { drain.SpillAt(target, "PuddleSmear"); return false; } - interactions.Refill(drain); + solutionContainerSystem.Refill(Owner.Uid, interactions, drain); return false; } @@ -237,16 +239,12 @@ namespace Content.Server.Nutrition.Components if (_pressurized && !Opened && _random.Prob(0.25f) && - Owner.TryGetComponent(out ISolutionInteractionsComponent? interactions)) + EntitySystem.Get().TryGetDrainableSolution(Owner.Uid, out var interactions)) { Opened = true; - if (!interactions.CanDrain) - { - return; - } - - var solution = interactions.Drain(interactions.DrainAvailable); + var solution = EntitySystem.Get() + .Drain(Owner.Uid, interactions, interactions.DrainAvailable); solution.SpillAt(Owner, "PuddleSmear"); SoundSystem.Play(Filter.Pvs(Owner), _burstSound.GetSound(), Owner, AudioParams.Default.WithVolume(-4)); diff --git a/Content.Server/Nutrition/Components/FoodComponent.cs b/Content.Server/Nutrition/Components/FoodComponent.cs index fd88da5117..5d76c8985c 100644 --- a/Content.Server/Nutrition/Components/FoodComponent.cs +++ b/Content.Server/Nutrition/Components/FoodComponent.cs @@ -3,10 +3,10 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Content.Server.Body.Behavior; -using Content.Server.Chemistry.Components; using Content.Server.Hands.Components; using Content.Server.Items; using Content.Shared.Body.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; using Content.Shared.Interaction; using Content.Shared.Interaction.Helpers; @@ -28,6 +28,7 @@ namespace Content.Server.Nutrition.Components public class FoodComponent : Component, IUse, IAfterInteract { public override string Name => "Food"; + public static string SolutionName = "food"; [ViewVariables] [DataField("useSound")] @@ -52,7 +53,7 @@ namespace Content.Server.Nutrition.Components { get { - if (!Owner.TryGetComponent(out SolutionContainerComponent? solution)) + if (!EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution)) { return 0; } @@ -69,7 +70,7 @@ namespace Content.Server.Nutrition.Components protected override void Initialize() { base.Initialize(); - Owner.EnsureComponentWarn(); + // Owner.EnsureComponentWarn(); } bool IUse.UseEntity(UseEntityEventArgs eventArgs) @@ -97,7 +98,8 @@ namespace Content.Server.Nutrition.Components public bool TryUseFood(IEntity? user, IEntity? target, UtensilComponent? utensilUsed = null) { - if (!Owner.TryGetComponent(out SolutionContainerComponent? solution)) + var solutionContainerSys = EntitySystem.Get(); + if (!solutionContainerSys.TryGetSolution(Owner, SolutionName, out var solution)) { return false; } @@ -147,7 +149,8 @@ namespace Content.Server.Nutrition.Components if (!types.HasFlag(_utensilsNeeded)) { - trueTarget.PopupMessage(user, Loc.GetString("food-you-need-to-hold-utensil", ("utensil", _utensilsNeeded))); + trueTarget.PopupMessage(user, + Loc.GetString("food-you-need-to-hold-utensil", ("utensil", _utensilsNeeded))); return false; } } @@ -158,12 +161,12 @@ namespace Content.Server.Nutrition.Components } var transferAmount = TransferAmount != null ? ReagentUnit.Min((ReagentUnit)TransferAmount, solution.CurrentVolume) : solution.CurrentVolume; - var split = solution.SplitSolution(transferAmount); + var split = solutionContainerSys.SplitSolution(Owner.Uid, solution, transferAmount); var firstStomach = stomachs.FirstOrDefault(stomach => stomach.CanTransferSolution(split)); if (firstStomach == null) { - solution.TryAddSolution(split); + solutionContainerSys.TryAddSolution(Owner.Uid, solution, split); trueTarget.PopupMessage(user, Loc.GetString("food-you-cannot-eat-any-more")); return false; } @@ -203,6 +206,8 @@ namespace Content.Server.Nutrition.Components return true; } + + private void DeleteAndSpawnTrash(IEntity user) { //We're empty. Become trash. diff --git a/Content.Server/Nutrition/Components/SliceableFoodComponent.cs b/Content.Server/Nutrition/Components/SliceableFoodComponent.cs index 6f707b3daa..746b38cd7e 100644 --- a/Content.Server/Nutrition/Components/SliceableFoodComponent.cs +++ b/Content.Server/Nutrition/Components/SliceableFoodComponent.cs @@ -1,7 +1,8 @@ using System.Threading.Tasks; -using Content.Server.Chemistry.Components; using Content.Server.Hands.Components; using Content.Server.Items; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; using Content.Shared.Examine; using Content.Shared.Interaction; @@ -36,14 +37,16 @@ namespace Content.Server.Nutrition.Components [ViewVariables(VVAccess.ReadWrite)] private ushort _totalCount = 5; - [ViewVariables(VVAccess.ReadWrite)] public ushort Count; + [ViewVariables(VVAccess.ReadWrite)] + public ushort Count; protected override void Initialize() { base.Initialize(); Count = _totalCount; Owner.EnsureComponent(); - Owner.EnsureComponent(); + Owner.EnsureComponent(); + EntitySystem.Get().EnsureSolution(Owner, FoodComponent.SolutionName); } async Task IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs) @@ -52,10 +55,12 @@ namespace Content.Server.Nutrition.Components { return false; } - if (!Owner.TryGetComponent(out SolutionContainerComponent? solution)) + + if (!EntitySystem.Get().TryGetSolution(Owner, FoodComponent.SolutionName, out var solution)) { return false; } + if (!eventArgs.Using.TryGetComponent(out UtensilComponent? utensil) || !utensil.HasType(UtensilType.Knife)) { return false; @@ -79,7 +84,9 @@ namespace Content.Server.Nutrition.Components Owner.Delete(); return true; } - solution.TryRemoveReagent("Nutriment", solution.CurrentVolume / ReagentUnit.New(Count + 1)); + + EntitySystem.Get().TryRemoveReagent(Owner.Uid, solution, "Nutriment", + solution.CurrentVolume / ReagentUnit.New(Count + 1)); return true; } diff --git a/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs b/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs index 7d47fdb065..887e0cf337 100644 --- a/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs @@ -1,7 +1,8 @@ -using Content.Server.Chemistry.Components; using Content.Server.Fluids.Components; using Content.Server.Notification; +using Content.Server.Nutrition.Components; using Content.Shared.Audio; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Notification.Managers; using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.EntitySystems; @@ -9,6 +10,7 @@ using Content.Shared.Throwing; using JetBrains.Annotations; using Robust.Shared.Audio; using Robust.Shared.GameObjects; +using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Player; @@ -17,13 +19,15 @@ namespace Content.Server.Nutrition.EntitySystems [UsedImplicitly] public class CreamPieSystem : SharedCreamPieSystem { + [Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!; + protected override void SplattedCreamPie(EntityUid uid, CreamPieComponent creamPie) { SoundSystem.Play(Filter.Pvs(creamPie.Owner), creamPie.Sound.GetSound(), creamPie.Owner, AudioHelpers.WithVariation(0.125f)); - if (ComponentManager.TryGetComponent(uid, out SolutionContainerComponent? solution)) + if (_solutionsSystem.TryGetSolution(creamPie.Owner, FoodComponent.SolutionName, out var solution)) { - solution.Solution.SpillAt(creamPie.Owner, "PuddleSmear", false); + solution.SpillAt(creamPie.Owner, "PuddleSmear", false); } } diff --git a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs new file mode 100644 index 0000000000..3a0eaaf02d --- /dev/null +++ b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs @@ -0,0 +1,47 @@ +using Content.Server.Nutrition.Components; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; +using JetBrains.Annotations; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; + +namespace Content.Server.Nutrition.EntitySystems +{ + [UsedImplicitly] + public class DrinkSystem : EntitySystem + { + [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSolutionChange); + SubscribeLocalEvent(OnDrinkInit); + } + + private void OnDrinkInit(EntityUid uid, DrinkComponent component, ComponentInit args) + { + component.Opened = component.DefaultToOpened; + + var owner = EntityManager.GetEntity(uid); + if (owner.TryGetComponent(out DrainableSolutionComponent? existingDrainable)) + { + // Beakers have Drink component but they should use the existing Drainable + component.SolutionName = existingDrainable.Solution; + } + else + { + _solutionContainerSystem.EnsureSolution(owner, component.SolutionName); + } + + component.UpdateAppearance(); + } + + + private void OnSolutionChange(EntityUid uid, DrinkComponent component, SolutionChangedEvent args) + { + component.UpdateAppearance(); + } + } +} diff --git a/Content.Server/PowerCell/Components/PowerCellComponent.cs b/Content.Server/PowerCell/Components/PowerCellComponent.cs index 34c76aa4d4..596b9d1f51 100644 --- a/Content.Server/PowerCell/Components/PowerCellComponent.cs +++ b/Content.Server/PowerCell/Components/PowerCellComponent.cs @@ -21,15 +21,16 @@ namespace Content.Server.PowerCell.Components /// [RegisterComponent] [ComponentReference(typeof(BatteryComponent))] - public class PowerCellComponent : BatteryComponent, IExamine, ISolutionChange + public class PowerCellComponent : BatteryComponent, IExamine { public override string Name => "PowerCell"; + public const string SolutionName = "powerCell"; [ViewVariables] public PowerCellSize CellSize => _cellSize; [DataField("cellSize")] private PowerCellSize _cellSize = PowerCellSize.Small; - [ViewVariables] public bool IsRigged { get; private set; } + [ViewVariables] public bool IsRigged { get; set; } protected override void Initialize() { @@ -96,13 +97,6 @@ namespace Content.Server.PowerCell.Components message.AddMarkup(Loc.GetString("power-cell-component-examine-details", ("currentCharge", $"{CurrentCharge / MaxCharge * 100:F0}"))); } } - - void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) - { - IsRigged = Owner.TryGetComponent(out SolutionContainerComponent? solution) - && solution.Solution.ContainsReagent("Plasma", out var plasma) - && plasma >= 5; - } } public enum PowerCellSize diff --git a/Content.Server/PowerCell/PowerCellSystem.cs b/Content.Server/PowerCell/PowerCellSystem.cs new file mode 100644 index 0000000000..714a00fcbf --- /dev/null +++ b/Content.Server/PowerCell/PowerCellSystem.cs @@ -0,0 +1,28 @@ +using Content.Server.PowerCell.Components; +using Content.Shared.Chemistry.EntitySystems; +using JetBrains.Annotations; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; + +namespace Content.Server.PowerCell +{ + [UsedImplicitly] + public class PowerCellSystem : EntitySystem + { + [Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSolutionChange); + } + + private void OnSolutionChange(EntityUid uid, PowerCellComponent component, SolutionChangedEvent args) + { + component.IsRigged = _solutionsSystem.TryGetSolution(uid, PowerCellComponent.SolutionName, out var solution) + && solution.ContainsReagent("Plasma", out var plasma) + && plasma >= 5; + } + } +} diff --git a/Content.Server/Tools/Components/WelderComponent.cs b/Content.Server/Tools/Components/WelderComponent.cs index 440c090930..f07e629127 100644 --- a/Content.Server/Tools/Components/WelderComponent.cs +++ b/Content.Server/Tools/Components/WelderComponent.cs @@ -8,10 +8,10 @@ using Content.Server.Explosion; using Content.Server.Items; using Content.Server.Notification; using Content.Shared.Audio; -using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution.Components; -using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Notification.Managers; using Content.Shared.Sound; @@ -26,7 +26,6 @@ using Robust.Shared.Localization; using Robust.Shared.Player; using Robust.Shared.Players; using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.Utility; using Robust.Shared.ViewVariables; namespace Content.Server.Tools.Components @@ -36,12 +35,14 @@ namespace Content.Server.Tools.Components [ComponentReference(typeof(IToolComponent))] [ComponentReference(typeof(IHotItem))] [NetworkedComponent()] - public class WelderComponent : ToolComponent, IExamine, IUse, ISuicideAct, ISolutionChange, IHotItem, IAfterInteract + public class WelderComponent : ToolComponent, IUse, ISuicideAct, IHotItem, IAfterInteract { [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!; public override string Name => "Welder"; + public const string SolutionName = "welder"; + /// /// Default Cost of using the welder fuel for an action /// @@ -55,7 +56,6 @@ namespace Content.Server.Tools.Components private bool _welderLit; private WelderSystem _welderSystem = default!; private SpriteComponent? _spriteComponent; - private SolutionContainerComponent? _solutionComponent; private PointLightComponent? _pointLightComponent; [DataField("weldSounds")] @@ -70,11 +70,19 @@ namespace Content.Server.Tools.Components [DataField("welderRefill")] private SoundSpecifier WelderRefill { get; set; } = new SoundPathSpecifier("/Audio/Effects/refill.ogg"); - [ViewVariables] - public float Fuel => _solutionComponent?.Solution?.GetReagentQuantity("WeldingFuel").Float() ?? 0f; + [ViewVariables] public float Fuel => WelderSolution?.GetReagentQuantity("WeldingFuel").Float() ?? 0f; - [ViewVariables] - public float FuelCapacity => _solutionComponent?.MaxVolume.Float() ?? 0f; + [ViewVariables] public float FuelCapacity => WelderSolution?.MaxVolume.Float() ?? 0f; + + private Solution? WelderSolution + { + get + { + Owner.EntityManager.EntitySysManager.GetEntitySystem() + .TryGetSolution(Owner, SolutionName, out var solution); + return solution; + } + } /// /// Status of welder, whether it is ignited @@ -103,9 +111,10 @@ namespace Content.Server.Tools.Components _welderSystem = _entitySystemManager.GetEntitySystem(); - Owner.TryGetComponent(out _solutionComponent); + Owner.EnsureComponent(); Owner.TryGetComponent(out _spriteComponent); Owner.TryGetComponent(out _pointLightComponent); + EntitySystem.Get().EnsureSolution(Owner, "welder"); } public override ComponentState GetComponentState(ICommonSession player) @@ -113,7 +122,8 @@ namespace Content.Server.Tools.Components return new WelderComponentState(FuelCapacity, Fuel, WelderLit); } - public override async Task UseTool(IEntity user, IEntity? target, float doAfterDelay, ToolQuality toolQualityNeeded, Func? doAfterCheck = null) + public override async Task UseTool(IEntity user, IEntity? target, float doAfterDelay, + ToolQuality toolQualityNeeded, Func? doAfterCheck = null) { bool ExtraCheck() { @@ -134,7 +144,8 @@ namespace Content.Server.Tools.Components return toolQualityNeeded.HasFlag(ToolQuality.Welding) ? canUse && TryWeld(DefaultFuelCost, user) : canUse; } - public async Task UseTool(IEntity user, IEntity target, float doAfterDelay, ToolQuality toolQualityNeeded, float fuelConsumed, Func? doAfterCheck = null) + public async Task UseTool(IEntity user, IEntity target, float doAfterDelay, ToolQuality toolQualityNeeded, + float fuelConsumed, Func? doAfterCheck = null) { bool ExtraCheck() { @@ -143,7 +154,8 @@ namespace Content.Server.Tools.Components return extraCheck && CanWeld(fuelConsumed); } - return await base.UseTool(user, target, doAfterDelay, toolQualityNeeded, ExtraCheck) && TryWeld(fuelConsumed, user); + return await base.UseTool(user, target, doAfterDelay, toolQualityNeeded, ExtraCheck) && + TryWeld(fuelConsumed, user); } private bool TryWeld(float value, IEntity? user = null, bool silent = false) @@ -164,15 +176,17 @@ namespace Content.Server.Tools.Components return false; } - if (_solutionComponent == null) + if (WelderSolution == null) return false; - var succeeded = _solutionComponent.TryRemoveReagent("WeldingFuel", ReagentUnit.New(value)); + var succeeded = EntitySystem.Get() + .TryRemoveReagent(Owner.Uid, WelderSolution, "WeldingFuel", ReagentUnit.New(value)); if (succeeded && !silent) { PlaySound(WeldSounds); } + return succeeded; } @@ -232,26 +246,6 @@ namespace Content.Server.Tools.Components return ToggleWelderStatus(eventArgs.User); } - public void Examine(FormattedMessage message, bool inDetailsRange) - { - if (WelderLit) - { - message.AddMarkup(Loc.GetString("welder-component-on-examine-welder-lit-message") + "\n"); - } - else - { - message.AddText(Loc.GetString("welder-component-on-examine-welder-not-lit-message") + "\n"); - } - - if (inDetailsRange) - { - message.AddMarkup(Loc.GetString("welder-component-on-examine-detailed-message", - ("colorName", Fuel < FuelCapacity / 4f ? "darkorange" : "orange"), - ("fuelLeft", Math.Round(Fuel)), - ("fuelCapacity", FuelCapacity))); - } - } - protected override void Shutdown() { base.Shutdown(); @@ -263,13 +257,13 @@ namespace Content.Server.Tools.Components if (!HasQuality(ToolQuality.Welding) || !WelderLit || Owner.Deleted) return; - _solutionComponent?.TryRemoveReagent("WeldingFuel", ReagentUnit.New(FuelLossRate * frameTime)); + EntitySystem.Get().TryRemoveReagent(Owner.Uid, WelderSolution, "WeldingFuel", + ReagentUnit.New(FuelLossRate * frameTime)); EntitySystem.Get().HotspotExpose(Owner.Transform.Coordinates, 700, 50, true); if (Fuel == 0) ToggleWelderStatus(); - } SuicideKind ISuicideAct.Suicide(IEntity victim, IChatManager chat) @@ -283,7 +277,7 @@ namespace Content.Server.Tools.Components othersMessage = Loc.GetString("welder-component-suicide-lit-others-message", - ("victim",victim)); + ("victim", victim)); victim.PopupMessageOtherClients(othersMessage); selfMessage = Loc.GetString("welder-component-suicide-lit-message"); @@ -301,12 +295,6 @@ namespace Content.Server.Tools.Components return SuicideKind.Blunt; } - public void SolutionChanged(SolutionChangeEventArgs eventArgs) - { - Dirty(); - } - - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { if (eventArgs.Target == null || !eventArgs.CanReach) @@ -316,9 +304,9 @@ namespace Content.Server.Tools.Components if (eventArgs.Target.TryGetComponent(out ReagentTankComponent? tank) && tank.TankType == ReagentTankType.Fuel - && eventArgs.Target.TryGetComponent(out ISolutionInteractionsComponent? targetSolution) - && targetSolution.CanDrain - && _solutionComponent != null) + && EntitySystem.Get() + .TryGetDrainableSolution(eventArgs.Target.Uid, out var targetSolution) + && WelderSolution != null) { if (WelderLit) { @@ -327,13 +315,14 @@ namespace Content.Server.Tools.Components return true; } - var trans = ReagentUnit.Min(_solutionComponent.EmptyVolume, targetSolution.DrainAvailable); + var trans = ReagentUnit.Min(WelderSolution.AvailableVolume, targetSolution.DrainAvailable); if (trans > 0) { - var drained = targetSolution.Drain(trans); - _solutionComponent.TryAddSolution(drained); + var drained = EntitySystem.Get().Drain(eventArgs.Target.Uid, targetSolution, trans); + EntitySystem.Get().TryAddSolution(Owner.Uid, WelderSolution, drained); SoundSystem.Play(Filter.Pvs(Owner), WelderRefill.GetSound(), Owner); - eventArgs.Target.PopupMessage(eventArgs.User, Loc.GetString("welder-component-after-interact-refueled-message")); + eventArgs.Target.PopupMessage(eventArgs.User, + Loc.GetString("welder-component-after-interact-refueled-message")); } } diff --git a/Content.Server/Tools/WelderSystem.cs b/Content.Server/Tools/WelderSystem.cs index cce97b7883..976763b626 100644 --- a/Content.Server/Tools/WelderSystem.cs +++ b/Content.Server/Tools/WelderSystem.cs @@ -1,7 +1,11 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Content.Server.Tools.Components; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Examine; using Robust.Shared.GameObjects; +using Robust.Shared.Localization; namespace Content.Server.Tools { @@ -12,6 +16,39 @@ namespace Content.Server.Tools { private readonly HashSet _activeWelders = new(); + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSolutionChange); + SubscribeLocalEvent(OnExamine); + } + + private void OnExamine(EntityUid uid, WelderComponent component, ExaminedEvent args) + { + if (component.WelderLit) + { + args.Message.AddMarkup(Loc.GetString("welder-component-on-examine-welder-lit-message") + "\n"); + } + else + { + args.Message.AddText(Loc.GetString("welder-component-on-examine-welder-not-lit-message") + "\n"); + } + + if (args.IsInDetailsRange) + { + args.Message.AddMarkup(Loc.GetString("welder-component-on-examine-detailed-message", + ("colorName", component.Fuel < component.FuelCapacity / 4f ? "darkorange" : "orange"), + ("fuelLeft", Math.Round(component.Fuel)), + ("fuelCapacity", component.FuelCapacity))); + } + } + + private void OnSolutionChange(EntityUid uid, WelderComponent component, SolutionChangedEvent args) + { + component.Dirty(); + } + public bool Subscribe(WelderComponent welder) { return _activeWelders.Add(welder); diff --git a/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs index e0d3315252..e991a06199 100644 --- a/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs +++ b/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs @@ -5,6 +5,7 @@ using Content.Server.Body.Circulatory; using Content.Server.Chemistry.Components; using Content.Server.Cooldown; using Content.Server.Weapon.Melee.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Damage.Components; using Content.Shared.Hands; using Content.Shared.Interaction; @@ -16,7 +17,6 @@ using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Maths; using Robust.Shared.Physics; -using Robust.Shared.Physics.Broadphase; using Robust.Shared.Player; using Robust.Shared.Timing; @@ -25,7 +25,7 @@ namespace Content.Server.Weapon.Melee public sealed class MeleeWeaponSystem : EntitySystem { [Dependency] private IGameTiming _gameTiming = default!; - + [Dependency] private SolutionContainerSystem _solutionsSystem = default!; public override void Initialize() { @@ -219,7 +219,7 @@ namespace Content.Server.Weapon.Melee for (var i = 0; i < increments; i++) { var castAngle = new Angle(baseAngle + increment * i); - var res = EntitySystem.Get().IntersectRay(mapId, + var res = Get().IntersectRay(mapId, new CollisionRay(position, castAngle.ToWorldVec(), (int) (CollisionGroup.Impassable | CollisionGroup.MobImpassable)), range, ignore).ToList(); @@ -234,7 +234,8 @@ namespace Content.Server.Weapon.Melee private void OnChemicalInjectorHit(EntityUid uid, MeleeChemicalInjectorComponent comp, MeleeHitEvent args) { - if (!ComponentManager.TryGetComponent(uid, out var solutionContainer)) + IEntity owner = EntityManager.GetEntity(uid); + if (!_solutionsSystem.TryGetInjectableSolution(owner.Uid, out var solutionContainer)) return; var hitBloodstreams = new List(); @@ -250,7 +251,7 @@ namespace Content.Server.Weapon.Melee if (hitBloodstreams.Count < 1) return; - var removedSolution = solutionContainer.Solution.SplitSolution(comp.TransferAmount * hitBloodstreams.Count); + var removedSolution = solutionContainer.SplitSolution(comp.TransferAmount * hitBloodstreams.Count); var removedVol = removedSolution.TotalVolume; var solutionToInject = removedSolution.SplitSolution(removedVol * comp.TransferEfficiency); var volPerBloodstream = solutionToInject.TotalVolume * (1 / hitBloodstreams.Count); diff --git a/Content.Server/Weapon/Ranged/Ammunition/Components/ChemicalAmmoComponent.cs b/Content.Server/Weapon/Ranged/Ammunition/Components/ChemicalAmmoComponent.cs index 5ee383baf3..d1f33c1052 100644 --- a/Content.Server/Weapon/Ranged/Ammunition/Components/ChemicalAmmoComponent.cs +++ b/Content.Server/Weapon/Ranged/Ammunition/Components/ChemicalAmmoComponent.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using System.Linq; -using Content.Server.Chemistry.Components; using Content.Server.Weapon.Ranged.Barrels.Components; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; namespace Content.Server.Weapon.Ranged.Ammunition.Components { @@ -10,6 +12,10 @@ namespace Content.Server.Weapon.Ranged.Ammunition.Components public class ChemicalAmmoComponent : Component { public override string Name => "ChemicalAmmo"; + public const string DefaultSolutionName = "ammo"; + + [DataField("solution")] + public string SolutionName { get; set; } = DefaultSolutionName; public override void HandleMessage(ComponentMessage message, IComponent? component) { @@ -24,32 +30,34 @@ namespace Content.Server.Weapon.Ranged.Ammunition.Components private void TransferSolution(BarrelFiredMessage barrelFired) { - if (!Owner.TryGetComponent(out var ammoSolutionContainer)) + if (!EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var ammoSolution)) return; var projectiles = barrelFired.FiredProjectiles; + var solutionContainerSystem = EntitySystem.Get(); - var projectileSolutionContainers = new List(); + var projectileSolutionContainers = new List<(EntityUid, Solution)>(); foreach (var projectile in projectiles) { - if (projectile.TryGetComponent(out var projectileSolutionContainer)) + if (EntitySystem.Get() + .TryGetSolution(projectile, SolutionName, out var projectileSolutionContainer)) { - projectileSolutionContainers.Add(projectileSolutionContainer); + projectileSolutionContainers.Add((projectile.Uid, projectileSolutionContainer)); } } if (!projectileSolutionContainers.Any()) return; - var solutionPerProjectile = ammoSolutionContainer.CurrentVolume * (1 / projectileSolutionContainers.Count); + var solutionPerProjectile = ammoSolution.CurrentVolume * (1 / projectileSolutionContainers.Count); - foreach (var projectileSolutionContainer in projectileSolutionContainers) + foreach (var (projectileUid, projectileSolution) in projectileSolutionContainers) { - var solutionToTransfer = ammoSolutionContainer.SplitSolution(solutionPerProjectile); - projectileSolutionContainer.TryAddSolution(solutionToTransfer); + var solutionToTransfer = solutionContainerSystem.SplitSolution(Owner.Uid, ammoSolution, solutionPerProjectile); + solutionContainerSystem.TryAddSolution(projectileUid, projectileSolution, solutionToTransfer); } - ammoSolutionContainer.RemoveAllSolution(); + solutionContainerSystem.RemoveAllSolution(Owner.Uid, ammoSolution); } } } diff --git a/Content.Shared/Body/Networks/SharedBloodstreamComponent.cs b/Content.Shared/Body/Networks/SharedBloodstreamComponent.cs index 0ca35f5ff5..1ad502b386 100644 --- a/Content.Shared/Body/Networks/SharedBloodstreamComponent.cs +++ b/Content.Shared/Body/Networks/SharedBloodstreamComponent.cs @@ -1,4 +1,4 @@ -using Content.Shared.Chemistry.Solution; +using Content.Shared.Chemistry.Components; using Robust.Shared.GameObjects; namespace Content.Shared.Body.Networks @@ -12,5 +12,7 @@ namespace Content.Shared.Body.Networks /// The solution to be transferred. /// Whether or not transfer was successful. public abstract bool TryTransferSolution(Solution solution); + + public const string DefaultSolutionName = "bloodstream"; } } diff --git a/Content.Shared/Chemistry/ChemistrySystem.cs b/Content.Shared/Chemistry/ChemistrySystem.cs index 45881a2050..c85dcdd9c1 100644 --- a/Content.Shared/Chemistry/ChemistrySystem.cs +++ b/Content.Shared/Chemistry/ChemistrySystem.cs @@ -1,5 +1,3 @@ -using System; -using System.Linq; using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Reagent; using JetBrains.Annotations; @@ -9,51 +7,20 @@ using Robust.Shared.Prototypes; namespace Content.Shared.Chemistry { - /// - /// This interface gives components behavior on whether entities solution (implying SolutionComponent is in place) is changed - /// - public interface ISolutionChange - { - /// - /// Called when solution is mixed with some other solution, or when some part of the solution is removed - /// - void SolutionChanged(SolutionChangeEventArgs eventArgs); - } - - public class SolutionChangeEventArgs : EventArgs - { - public IEntity Owner { get; set; } = default!; - } - [UsedImplicitly] - public class ChemistrySystem : EntitySystem + public partial class ChemistrySystem : EntitySystem { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - public void HandleSolutionChange(IEntity owner) - { - var eventArgs = new SolutionChangeEventArgs - { - Owner = owner, - }; - var solutionChangeArgs = owner.GetAllComponents().ToList(); - - foreach (var solutionChangeArg in solutionChangeArgs) - { - solutionChangeArg.SolutionChanged(eventArgs); - - if (owner.Deleted) - return; - } - } - - public void ReactionEntity(IEntity? entity, ReactionMethod method, string reagentId, ReagentUnit reactVolume, Solution.Solution? source) + public void ReactionEntity(IEntity? entity, ReactionMethod method, string reagentId, ReagentUnit reactVolume, + Components.Solution? source) { // We throw if the reagent specified doesn't exist. ReactionEntity(entity, method, _prototypeManager.Index(reagentId), reactVolume, source); } - public void ReactionEntity(IEntity? entity, ReactionMethod method, ReagentPrototype reagent, ReagentUnit reactVolume, Solution.Solution? source) + public void ReactionEntity(IEntity? entity, ReactionMethod method, ReagentPrototype reagent, + ReagentUnit reactVolume, Components.Solution? source) { if (entity == null || entity.Deleted || !entity.TryGetComponent(out ReactiveComponent? reactive)) return; diff --git a/Content.Shared/Chemistry/Components/SharedChemMasterComponent.cs b/Content.Shared/Chemistry/Components/SharedChemMasterComponent.cs index d9817f97b7..7a0b30748a 100644 --- a/Content.Shared/Chemistry/Components/SharedChemMasterComponent.cs +++ b/Content.Shared/Chemistry/Components/SharedChemMasterComponent.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using Content.Shared.Chemistry.Reagent; using Content.Shared.Cloning; using Robust.Shared.GameObjects; @@ -14,6 +15,7 @@ namespace Content.Shared.Chemistry.Components public class SharedChemMasterComponent : Component { public override string Name => "ChemMaster"; + public const string SolutionName = "buffer"; [Serializable, NetSerializable] public class ChemMasterBoundUserInterfaceState : BoundUserInterfaceState @@ -27,11 +29,11 @@ namespace Content.Shared.Chemistry.Components /// /// A list of the reagents and their amounts within the beaker/reagent container, if applicable. /// - public readonly IReadOnlyList ContainerReagents; + public readonly IReadOnlyList ContainerReagents; /// /// A list of the reagents and their amounts within the buffer, if applicable. /// - public readonly IReadOnlyList BufferReagents; + public readonly IReadOnlyList BufferReagents; public readonly string DispenserName; public readonly bool BufferModeTransfer; @@ -39,7 +41,7 @@ namespace Content.Shared.Chemistry.Components public readonly ReagentUnit BufferCurrentVolume; public ChemMasterBoundUserInterfaceState(bool hasPower, bool hasBeaker, ReagentUnit beakerCurrentVolume, ReagentUnit beakerMaxVolume, string containerName, - string dispenserName, IReadOnlyList containerReagents, IReadOnlyList bufferReagents, bool bufferModeTransfer, ReagentUnit bufferCurrentVolume) + string dispenserName, IReadOnlyList containerReagents, IReadOnlyList bufferReagents, bool bufferModeTransfer, ReagentUnit bufferCurrentVolume) { HasPower = hasPower; HasBeaker = hasBeaker; diff --git a/Content.Shared/Chemistry/Components/SharedHyposprayComponent.cs b/Content.Shared/Chemistry/Components/SharedHyposprayComponent.cs index 975abbad91..8f5e9836fa 100644 --- a/Content.Shared/Chemistry/Components/SharedHyposprayComponent.cs +++ b/Content.Shared/Chemistry/Components/SharedHyposprayComponent.cs @@ -10,6 +10,7 @@ namespace Content.Shared.Chemistry.Components public abstract class SharedHyposprayComponent : Component { public sealed override string Name => "Hypospray"; + public const string SolutionName = "hypospray"; [Serializable, NetSerializable] protected sealed class HyposprayComponentState : ComponentState diff --git a/Content.Shared/Chemistry/Components/Solution.Managerial.cs b/Content.Shared/Chemistry/Components/Solution.Managerial.cs new file mode 100644 index 0000000000..4a22335fd6 --- /dev/null +++ b/Content.Shared/Chemistry/Components/Solution.Managerial.cs @@ -0,0 +1,50 @@ +using Content.Shared.Chemistry.Reagent; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.ViewVariables; + +namespace Content.Shared.Chemistry.Components +{ + public partial class Solution + { + + /// + /// If reactions will be checked for when adding reagents to the container. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("canReact")] + public bool CanReact { get; set; } = true; + + /// + /// Volume needed to fill this container. + /// + [ViewVariables] + public ReagentUnit AvailableVolume => MaxVolume - CurrentVolume; + + public ReagentUnit DrawAvailable => CurrentVolume; + public ReagentUnit DrainAvailable => CurrentVolume; + + /// + /// Checks if a solution can fit into the container. + /// + /// The solution that is trying to be added. + /// If the solution can be fully added. + public bool CanAddSolution(Solution solution) + { + return solution.TotalVolume <= AvailableVolume; + } + + [DataField("maxSpillRefill")] + public ReagentUnit MaxSpillRefill { get; set; } + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("maxVol")] + public ReagentUnit MaxVolume { get; set; } = ReagentUnit.Zero; + + [ViewVariables] + public ReagentUnit CurrentVolume => TotalVolume; + + // [ViewVariables] + // public EntityUid OwnerUid { get; set; } + } +} diff --git a/Content.Shared/Chemistry/Solution/Solution.cs b/Content.Shared/Chemistry/Components/Solution.cs similarity index 81% rename from Content.Shared/Chemistry/Solution/Solution.cs rename to Content.Shared/Chemistry/Components/Solution.cs index 8b89f0631a..5aeb4f2c8d 100644 --- a/Content.Shared/Chemistry/Solution/Solution.cs +++ b/Content.Shared/Chemistry/Components/Solution.cs @@ -14,27 +14,25 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy using Robust.Shared.Utility; using Robust.Shared.ViewVariables; -namespace Content.Shared.Chemistry.Solution +namespace Content.Shared.Chemistry.Components { /// /// A solution of reagents. /// [Serializable, NetSerializable] [DataDefinition] - public class Solution : IEnumerable, ISerializationHooks + public partial class Solution : IEnumerable, ISerializationHooks { // Most objects on the station hold only 1 or 2 reagents [ViewVariables] [DataField("reagents")] - private List _contents = new(2); - - public IReadOnlyList Contents => _contents; - + public List Contents = new(2); + /// /// The calculated total volume of all reagents in the solution (ex. Total volume of liquid in beaker). /// [ViewVariables] - public ReagentUnit TotalVolume { get; private set; } + public ReagentUnit TotalVolume { get; set; } public Color Color => GetColor(); @@ -56,7 +54,7 @@ namespace Content.Shared.Chemistry.Solution void ISerializationHooks.AfterDeserialization() { TotalVolume = ReagentUnit.Zero; - _contents.ForEach(reagent => TotalVolume += reagent.Quantity); + Contents.ForEach(reagent => TotalVolume += reagent.Quantity); } public bool ContainsReagent(string reagentId) @@ -100,18 +98,18 @@ namespace Content.Shared.Chemistry.Solution if (quantity <= 0) return; - for (var i = 0; i < _contents.Count; i++) + for (var i = 0; i < Contents.Count; i++) { - var reagent = _contents[i]; + var reagent = Contents[i]; if (reagent.ReagentId != reagentId) continue; - _contents[i] = new ReagentQuantity(reagentId, reagent.Quantity + quantity); + Contents[i] = new ReagentQuantity(reagentId, reagent.Quantity + quantity); TotalVolume += quantity; return; } - _contents.Add(new ReagentQuantity(reagentId, quantity)); + Contents.Add(new ReagentQuantity(reagentId, quantity)); TotalVolume += quantity; } @@ -122,7 +120,7 @@ namespace Content.Shared.Chemistry.Solution public void ScaleSolution(float scale) { if (scale == 1) return; - var tempContents = new List(_contents); + var tempContents = new List(Contents); foreach(ReagentQuantity current in tempContents) { if(scale > 1) @@ -143,10 +141,10 @@ namespace Content.Shared.Chemistry.Solution /// The quantity in milli-units. public ReagentUnit GetReagentQuantity(string reagentId) { - for (var i = 0; i < _contents.Count; i++) + for (var i = 0; i < Contents.Count; i++) { - if (_contents[i].ReagentId == reagentId) - return _contents[i].Quantity; + if (Contents[i].ReagentId == reagentId) + return Contents[i].Quantity; } return ReagentUnit.New(0); @@ -157,9 +155,9 @@ namespace Content.Shared.Chemistry.Solution if(quantity <= 0) return; - for (var i = 0; i < _contents.Count; i++) + for (var i = 0; i < Contents.Count; i++) { - var reagent = _contents[i]; + var reagent = Contents[i]; if(reagent.ReagentId != reagentId) continue; @@ -168,12 +166,12 @@ namespace Content.Shared.Chemistry.Solution var newQuantity = curQuantity - quantity; if (newQuantity <= 0) { - _contents.RemoveSwap(i); + Contents.RemoveSwap(i); TotalVolume -= curQuantity; } else { - _contents[i] = new ReagentQuantity(reagentId, newQuantity); + Contents[i] = new ReagentQuantity(reagentId, newQuantity); TotalVolume -= quantity; } @@ -198,16 +196,16 @@ namespace Content.Shared.Chemistry.Solution return; } - for (var i = 0; i < _contents.Count; i++) + for (var i = 0; i < Contents.Count; i++) { - var reagent = _contents[i]; + var reagent = Contents[i]; var oldQuantity = reagent.Quantity; // quantity taken is always a little greedy, so fractional quantities get rounded up to the nearest // whole unit. This should prevent little bits of chemical remaining because of float rounding errors. var newQuantity = oldQuantity * ratio; - _contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity); + Contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity); } TotalVolume = TotalVolume * ratio; @@ -215,7 +213,7 @@ namespace Content.Shared.Chemistry.Solution public void RemoveAllSolution() { - _contents.Clear(); + Contents.Clear(); TotalVolume = ReagentUnit.New(0); } @@ -237,17 +235,17 @@ namespace Content.Shared.Chemistry.Solution var newTotalVolume = ReagentUnit.New(0); var remainingVolume = TotalVolume; - for (var i = 0; i < _contents.Count; i++) + for (var i = 0; i < Contents.Count; i++) { - var reagent = _contents[i]; + var reagent = Contents[i]; var ratio = (remainingVolume - quantity).Double() / remainingVolume.Double(); remainingVolume -= reagent.Quantity; var newQuantity = reagent.Quantity * ratio; var splitQuantity = reagent.Quantity - newQuantity; - _contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity); - newSolution._contents.Add(new ReagentQuantity(reagent.ReagentId, splitQuantity)); + Contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity); + newSolution.Contents.Add(new ReagentQuantity(reagent.ReagentId, splitQuantity)); newTotalVolume += splitQuantity; quantity -= splitQuantity; } @@ -260,25 +258,25 @@ namespace Content.Shared.Chemistry.Solution public void AddSolution(Solution otherSolution) { - for (var i = 0; i < otherSolution._contents.Count; i++) + for (var i = 0; i < otherSolution.Contents.Count; i++) { - var otherReagent = otherSolution._contents[i]; + var otherReagent = otherSolution.Contents[i]; var found = false; - for (var j = 0; j < _contents.Count; j++) + for (var j = 0; j < Contents.Count; j++) { - var reagent = _contents[j]; + var reagent = Contents[j]; if (reagent.ReagentId == otherReagent.ReagentId) { found = true; - _contents[j] = new ReagentQuantity(reagent.ReagentId, reagent.Quantity + otherReagent.Quantity); + Contents[j] = new ReagentQuantity(reagent.ReagentId, reagent.Quantity + otherReagent.Quantity); break; } } if (!found) { - _contents.Add(new ReagentQuantity(otherReagent.ReagentId, otherReagent.Quantity)); + Contents.Add(new ReagentQuantity(otherReagent.ReagentId, otherReagent.Quantity)); } } @@ -322,10 +320,10 @@ namespace Content.Shared.Chemistry.Solution var volume = ReagentUnit.New(0); var newSolution = new Solution(); - for (var i = 0; i < _contents.Count; i++) + for (var i = 0; i < Contents.Count; i++) { - var reagent = _contents[i]; - newSolution._contents.Add(reagent); + var reagent = Contents[i]; + newSolution.Contents.Add(reagent); volume += reagent.Quantity; } @@ -337,7 +335,7 @@ namespace Content.Shared.Chemistry.Solution { var chemistry = EntitySystem.Get(); - foreach (var (reagentId, quantity) in _contents.ToArray()) + foreach (var (reagentId, quantity) in Contents.ToArray()) { chemistry.ReactionEntity(entity, method, reagentId, quantity, this); } @@ -377,7 +375,7 @@ namespace Content.Shared.Chemistry.Solution public IEnumerator GetEnumerator() { - return _contents.GetEnumerator(); + return Contents.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() diff --git a/Content.Shared/Chemistry/Components/SolutionManager/DrainableSolutionComponent.cs b/Content.Shared/Chemistry/Components/SolutionManager/DrainableSolutionComponent.cs new file mode 100644 index 0000000000..e2b6c525ee --- /dev/null +++ b/Content.Shared/Chemistry/Components/SolutionManager/DrainableSolutionComponent.cs @@ -0,0 +1,23 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.ViewVariables; + +namespace Content.Shared.Chemistry.Components.SolutionManager +{ + /// + /// Denotes the solution that can be easily removed through any reagent container. + /// Think pouring this or draining from a water tank. + /// + [RegisterComponent] + public class DrainableSolutionComponent : Component + { + public override string Name => "DrainableSolution"; + + /// + /// Solution name that can be drained. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("solution")] + public string Solution { get; set; } = "default"; + } +} diff --git a/Content.Shared/Chemistry/Components/SolutionManager/DrawableSolutionComponent.cs b/Content.Shared/Chemistry/Components/SolutionManager/DrawableSolutionComponent.cs new file mode 100644 index 0000000000..fcafa8724a --- /dev/null +++ b/Content.Shared/Chemistry/Components/SolutionManager/DrawableSolutionComponent.cs @@ -0,0 +1,22 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.ViewVariables; + +namespace Content.Shared.Chemistry.Components.SolutionManager +{ + /// + /// Denotes the solution that can removed be with syringes. + /// + [RegisterComponent] + public class DrawableSolutionComponent : Component + { + public override string Name => "DrawableSolution"; + + /// + /// Solution name that can be removed with syringes. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("solution")] + public string Solution { get; set; } = "default"; + } +} diff --git a/Content.Shared/Chemistry/Components/SolutionManager/ExaminableSolutionComponent.cs b/Content.Shared/Chemistry/Components/SolutionManager/ExaminableSolutionComponent.cs new file mode 100644 index 0000000000..87848932ad --- /dev/null +++ b/Content.Shared/Chemistry/Components/SolutionManager/ExaminableSolutionComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.ViewVariables; + +namespace Content.Shared.Chemistry.Components.SolutionManager +{ + [RegisterComponent] + public class ExaminableSolutionComponent: Component + { + public override string Name => "ExaminableSolution"; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("solution")] + public string Solution { get; set; } = "default"; + } +} diff --git a/Content.Shared/Chemistry/Components/SolutionManager/FitsInDispenserComponent.cs b/Content.Shared/Chemistry/Components/SolutionManager/FitsInDispenserComponent.cs new file mode 100644 index 0000000000..c3b3ce3528 --- /dev/null +++ b/Content.Shared/Chemistry/Components/SolutionManager/FitsInDispenserComponent.cs @@ -0,0 +1,24 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.ViewVariables; + +namespace Content.Shared.Chemistry.Components.SolutionManager +{ + /// Allows the entity with this component to be placed in a SharedReagentDispenserComponent. + /// Otherwise it's considered to be too large or the improper shape to fit. + /// Allows us to have obscenely large containers that are harder to abuse in chem dispensers + /// since they can't be placed directly in them. + /// + [RegisterComponent] + public class FitsInDispenserComponent : Component + { + public override string Name => "FitsInDispenser"; + + /// + /// Solution name that will interact with ReagentDispenserComponent. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("solution")] + public string Solution { get; set; } = "default"; + } +} diff --git a/Content.Shared/Chemistry/Components/SolutionManager/InjectableSolutionComponent.cs b/Content.Shared/Chemistry/Components/SolutionManager/InjectableSolutionComponent.cs new file mode 100644 index 0000000000..5e70702bf6 --- /dev/null +++ b/Content.Shared/Chemistry/Components/SolutionManager/InjectableSolutionComponent.cs @@ -0,0 +1,22 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.ViewVariables; + +namespace Content.Shared.Chemistry.Components.SolutionManager +{ + /// + /// Denotes a solution which can be added with syringes. + /// + [RegisterComponent] + public class InjectableSolutionComponent : Component + { + public override string Name => "InjectableSolution"; + + /// + /// Solution name which can be added with syringes. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("solution")] + public string Solution { get; set; } = "default"; + } +} diff --git a/Content.Shared/Chemistry/Components/SolutionManager/RefillableSolutionComponent.cs b/Content.Shared/Chemistry/Components/SolutionManager/RefillableSolutionComponent.cs new file mode 100644 index 0000000000..4e44214145 --- /dev/null +++ b/Content.Shared/Chemistry/Components/SolutionManager/RefillableSolutionComponent.cs @@ -0,0 +1,25 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.ViewVariables; + +namespace Content.Shared.Chemistry.Components.SolutionManager +{ + /// + /// Reagents that can be added easily. For example like + /// pouring something into another beaker, glass, or into the gas + /// tank of a car. + /// + [RegisterComponent] + public class RefillableSolutionComponent : Component + { + public override string Name => "RefillableSolution"; + + /// + /// Solution name that can added to easily. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("solution")] + public string Solution { get; set; } = "default"; + + } +} diff --git a/Content.Shared/Chemistry/Components/SolutionManager/SolutionContainerManagerComponent.cs b/Content.Shared/Chemistry/Components/SolutionManager/SolutionContainerManagerComponent.cs new file mode 100644 index 0000000000..2473ac428c --- /dev/null +++ b/Content.Shared/Chemistry/Components/SolutionManager/SolutionContainerManagerComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Content.Shared.Chemistry.EntitySystems; +using Robust.Shared.Analyzers; +using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.ViewVariables; + +namespace Content.Shared.Chemistry.Components.SolutionManager +{ + [NetworkedComponent()] + [RegisterComponent] + [DataDefinition] + [Friend(typeof(SolutionContainerSystem))] + public class SolutionContainerManagerComponent : Component + { + public override string Name => "SolutionContainerManager"; + + [ViewVariables] + [DataField("solutions")] + public readonly Dictionary Solutions = new(); + } +} diff --git a/Content.Shared/Chemistry/Dispenser/SharedReagentDispenserComponent.cs b/Content.Shared/Chemistry/Dispenser/SharedReagentDispenserComponent.cs index defe521834..56ef705e2a 100644 --- a/Content.Shared/Chemistry/Dispenser/SharedReagentDispenserComponent.cs +++ b/Content.Shared/Chemistry/Dispenser/SharedReagentDispenserComponent.cs @@ -36,12 +36,12 @@ namespace Content.Shared.Chemistry.Dispenser /// /// A list of the reagents and their amounts within the beaker/reagent container, if applicable. /// - public readonly List? ContainerReagents; + public readonly List? ContainerReagents; public readonly string DispenserName; public readonly ReagentUnit SelectedDispenseAmount; public ReagentDispenserBoundUserInterfaceState(bool hasPower, bool hasBeaker, ReagentUnit beakerCurrentVolume, ReagentUnit beakerMaxVolume, string containerName, - List inventory, string dispenserName, List? containerReagents, ReagentUnit selectedDispenseAmount) + List inventory, string dispenserName, List? containerReagents, ReagentUnit selectedDispenseAmount) { HasPower = hasPower; HasBeaker = hasBeaker; diff --git a/Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.Capabilities.cs b/Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.Capabilities.cs new file mode 100644 index 0000000000..8885f0db26 --- /dev/null +++ b/Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.Capabilities.cs @@ -0,0 +1,132 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.Reagent; +using Robust.Shared.GameObjects; + +namespace Content.Shared.Chemistry.EntitySystems +{ + public partial class SolutionContainerSystem + { + public void Refill(EntityUid targetUid, Solution targetSolution, Solution addedSolution) + { + if (!ComponentManager.HasComponent(targetUid)) + return; + + TryAddSolution(targetUid, targetSolution, addedSolution); + } + + public void Inject(EntityUid targetUid, Solution targetSolution, Solution addedSolution) + { + if (!ComponentManager.HasComponent(targetUid)) + return; + + TryAddSolution(targetUid, targetSolution, addedSolution); + } + + public Solution Draw(EntityUid targetUid, Solution solution, ReagentUnit amount) + { + if (!ComponentManager.HasComponent(targetUid)) + { + return new Solution(); + } + + return SplitSolution(targetUid, solution, amount); + } + + public Solution Drain(EntityUid targetUid, Solution targetSolution, ReagentUnit amount) + { + if (!ComponentManager.HasComponent(targetUid)) + { + return new Solution(); + } + + return SplitSolution(targetUid, targetSolution, amount); + } + + public bool TryGetInjectableSolution(EntityUid targetUid, + [NotNullWhen(true)] out Solution? solution) + { + if (ComponentManager.TryGetComponent(targetUid, out InjectableSolutionComponent? injectable) && + ComponentManager.TryGetComponent(targetUid, out SolutionContainerManagerComponent? manager) && + manager.Solutions.TryGetValue(injectable.Solution, out solution)) + { + return true; + } + + solution = null; + return false; + } + + public bool TryGetRefillableSolution(EntityUid targetUid, + [NotNullWhen(true)] out Solution? solution) + { + if (ComponentManager.TryGetComponent(targetUid, out RefillableSolutionComponent? refillable) && + ComponentManager.TryGetComponent(targetUid, out SolutionContainerManagerComponent? manager) && + manager.Solutions.TryGetValue(refillable.Solution, out var refillableSolution)) + { + solution = refillableSolution; + return true; + } + + solution = null; + return false; + } + + public bool TryGetDrainableSolution(EntityUid targetUid, + [NotNullWhen(true)] out Solution? solution) + { + if (ComponentManager.TryGetComponent(targetUid,out DrainableSolutionComponent? drainable) && + ComponentManager.TryGetComponent(targetUid,out SolutionContainerManagerComponent? manager) && + manager.Solutions.TryGetValue(drainable.Solution, out solution)) + { + return true; + } + + solution = null; + return false; + } + + public bool TryGetDrawableSolution(IEntity owner, + [NotNullWhen(true)] out Solution? solution) + { + if (owner.TryGetComponent(out DrawableSolutionComponent? drawable) && + owner.TryGetComponent(out SolutionContainerManagerComponent? manager) && + manager.Solutions.TryGetValue(drawable.Solution, out solution)) + { + return true; + } + + solution = null; + return false; + } + + public ReagentUnit DrainAvailable(IEntity? owner) + { + if (owner == null || !TryGetDrainableSolution(owner.Uid, out var solution)) + return ReagentUnit.Zero; + + return solution.CurrentVolume; + } + + public bool HasFitsInDispenser(IEntity owner) + { + return !owner.Deleted && owner.HasComponent(); + } + + public bool TryGetFitsInDispenser(EntityUid owner, + [NotNullWhen(true)] out Solution? solution) + { + if (EntityManager.TryGetEntity(owner, out var ownerEntity) && + ownerEntity.TryGetComponent(out FitsInDispenserComponent? dispenserFits) && + ownerEntity.TryGetComponent(out SolutionContainerManagerComponent? manager) && + manager.Solutions.TryGetValue(dispenserFits.Solution, out solution)) + { + return true; + } + + solution = null; + return false; + } + } +} diff --git a/Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.cs b/Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.cs new file mode 100644 index 0000000000..3ddc503be8 --- /dev/null +++ b/Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.cs @@ -0,0 +1,342 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.Reaction; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Examine; +using JetBrains.Annotations; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Log; +using Robust.Shared.Maths; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; + +namespace Content.Shared.Chemistry.EntitySystems +{ + /// + /// This event alerts system that the solution was changed + /// + public class SolutionChangedEvent : EntityEventArgs + { + } + + /// + /// Part of Chemistry system deal with SolutionContainers + /// + [UsedImplicitly] + public partial class SolutionContainerSystem : EntitySystem + { + [Dependency] + private readonly SharedChemicalReactionSystem _chemistrySystem = default!; + + [Dependency] + private readonly IPrototypeManager _prototypeManager = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(InitSolution); + SubscribeLocalEvent(OnExamineSolution); + } + + private void InitSolution(EntityUid uid, SolutionContainerManagerComponent component, ComponentInit args) + { + foreach (var keyValue in component.Solutions) + { + var solutionHolder = keyValue.Value; + // solutionHolder.OwnerUid = component.Owner.Uid; + if (solutionHolder.MaxVolume == ReagentUnit.Zero && solutionHolder.TotalVolume > solutionHolder.MaxVolume) + { + solutionHolder.MaxVolume = solutionHolder.TotalVolume; + } + + UpdateAppearance(uid, solutionHolder); + } + } + + private void OnExamineSolution(EntityUid uid, ExaminableSolutionComponent examinableComponent, + ExaminedEvent args) + { + if (!args.Examined.TryGetComponent(out SolutionContainerManagerComponent? solutionsManager) + || !solutionsManager.Solutions.TryGetValue(examinableComponent.Solution, out var solutionHolder)) + return; + + if (solutionHolder.Contents.Count == 0) + { + args.Message.AddText(Loc.GetString("shared-solution-container-component-on-examine-empty-container")); + return; + } + + var primaryReagent = solutionHolder.GetPrimaryReagentId(); + + if (!_prototypeManager.TryIndex(primaryReagent, out ReagentPrototype? proto)) + { + Logger.Error( + $"{nameof(Solution)} could not find the prototype associated with {primaryReagent}."); + return; + } + + var colorHex = solutionHolder.Color + .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.Message.AddMarkup(Loc.GetString(messageString, + ("color", colorHex), + ("wordedAmount", Loc.GetString(solutionHolder.Contents.Count == 1 + ? "shared-solution-container-component-on-examine-worded-amount-one-reagent" + : "shared-solution-container-component-on-examine-worded-amount-multiple-reagents")), + ("desc", Loc.GetString(proto.PhysicalDescription)))); + } + + private void UpdateAppearance(EntityUid uid, Solution solution) + { + if (!EntityManager.TryGetEntity(uid, out var solutionEntity) + || solutionEntity.Deleted + || !solutionEntity.TryGetComponent(out var appearance)) + return; + + var filledVolumeFraction = solution.CurrentVolume.Float() / solution.MaxVolume.Float(); + appearance.SetData(SolutionContainerVisuals.VisualState, new SolutionContainerVisualState(solution.Color, filledVolumeFraction)); + solutionEntity.Dirty(); + } + + /// + /// Removes part of the solution in the container. + /// + /// + /// + /// the volume of solution to remove. + /// The solution that was removed. + public Solution SplitSolution(EntityUid targetUid, Solution solutionHolder, ReagentUnit quantity) + { + var splitSol = solutionHolder.SplitSolution(quantity); + UpdateChemicals(targetUid, solutionHolder); + return splitSol; + } + + private void UpdateChemicals(EntityUid uid, Solution solutionHolder, bool needsReactionsProcessing = false) + { + // Process reactions + if (needsReactionsProcessing && solutionHolder.CanReact) + { + _chemistrySystem + .FullyReactSolution(solutionHolder, EntityManager.GetEntity(uid), solutionHolder.MaxVolume); + } + + UpdateAppearance(uid, solutionHolder); + RaiseLocalEvent(uid, new SolutionChangedEvent()); + } + + public void RemoveAllSolution(EntityUid uid, Solution solutionHolder) + { + if (solutionHolder.CurrentVolume == 0) + return; + + solutionHolder.RemoveAllSolution(); + UpdateChemicals(uid, solutionHolder); + } + + public void RemoveAllSolution(EntityUid uid) + { + if (!ComponentManager.TryGetComponent(uid, out SolutionContainerManagerComponent? solutionContainerManager)) + return; + + foreach (var solution in solutionContainerManager.Solutions.Values) + { + RemoveAllSolution(uid, solution); + } + } + + /// + /// Adds reagent of an Id to the container. + /// + /// + /// Container to which we are adding reagent + /// The Id of the reagent to add. + /// The amount of reagent to add. + /// The amount of reagent successfully added. + /// If all the reagent could be added. + public bool TryAddReagent(EntityUid targetUid, Solution targetSolution, string reagentId, ReagentUnit quantity, + out ReagentUnit acceptedQuantity) + { + acceptedQuantity = targetSolution.AvailableVolume > quantity ? quantity : targetSolution.AvailableVolume; + targetSolution.AddReagent(reagentId, acceptedQuantity); + + if (acceptedQuantity > 0) + UpdateChemicals(targetUid, targetSolution, true); + + return acceptedQuantity == quantity; + } + + /// + /// Removes reagent of an Id to the container. + /// + /// + /// Solution container from which we are removing reagent + /// The Id of the reagent to remove. + /// The amount of reagent to remove. + /// If the reagent to remove was found in the container. + public bool TryRemoveReagent(EntityUid targetUid, Solution? container, string reagentId, ReagentUnit quantity) + { + if (container == null || !container.ContainsReagent(reagentId)) + return false; + + container.RemoveReagent(reagentId, quantity); + UpdateChemicals(targetUid, container); + return true; + } + + /// + /// Adds a solution to the container, if it can fully fit. + /// + /// + /// The container to which we try to add. + /// The solution to try to add. + /// If the solution could be added. + public bool TryAddSolution(EntityUid targetUid, Solution? targetSolution, Solution solution) + { + if (targetSolution == null || !targetSolution.CanAddSolution(solution) || solution.TotalVolume == 0) + return false; + + targetSolution.AddSolution(solution); + UpdateChemicals(targetUid, targetSolution, true); + return true; + } + + public bool TryGetSolution(IEntity? target, string name, + [NotNullWhen(true)] out Solution? solution) + { + if (target == null || target.Deleted) + { + solution = null; + return false; + } + + return TryGetSolution(target.Uid, name, out solution); + } + + public bool TryGetSolution(EntityUid uid, string name, + [NotNullWhen(true)] out Solution? solution) + { + if (!ComponentManager.TryGetComponent(uid, out SolutionContainerManagerComponent? solutionsMgr)) + { + solution = null; + return false; + } + + return solutionsMgr.Solutions.TryGetValue(name, out solution); + } + + /// + /// Will ensure a solution is added to given entity even if it's missing solutionContainerManager + /// + /// Entity to which to add solution + /// name for the solution + /// solution + public Solution EnsureSolution(IEntity owner, string name) + { + var solutionsMgr = owner.EnsureComponent(); + if (!solutionsMgr.Solutions.ContainsKey(name)) + { + var newSolution = new Solution(); + solutionsMgr.Solutions.Add(name, newSolution); + } + + return solutionsMgr.Solutions[name]; + } + + public string[] RemoveEachReagent(Solution solution, ReagentUnit quantity) + { + var removedReagent = new string[solution.Contents.Count]; + if (quantity <= 0) + return Array.Empty(); + + var pos = 0; + for (var i = 0; i < solution.Contents.Count; i++) + { + var (reagentId, curQuantity) = solution.Contents[i]; + removedReagent[pos++] = reagentId; + + var newQuantity = curQuantity - quantity; + if (newQuantity <= 0) + { + solution.Contents.RemoveSwap(i); + solution.TotalVolume -= curQuantity; + } + else + { + solution.Contents[i] = new Solution.ReagentQuantity(reagentId, newQuantity); + solution.TotalVolume -= quantity; + } + } + + return removedReagent; + } + + public void TryRemoveAllReagents(Solution solution, List removeReagents) + { + foreach (var reagent in removeReagents) + { + solution.RemoveReagent(reagent.ReagentId, reagent.Quantity); + } + } + + public ReagentUnit GetReagentQuantity(EntityUid ownerUid, string reagentId) + { + var reagentQuantity = ReagentUnit.New(0); + if (EntityManager.TryGetEntity(ownerUid, out var owner) + && owner.TryGetComponent(out SolutionContainerManagerComponent? managerComponent)) + { + foreach (var solution in managerComponent.Solutions.Values) + { + reagentQuantity += solution.GetReagentQuantity(reagentId); + } + } + + return reagentQuantity; + } + } + + [Serializable, NetSerializable] + public enum SolutionContainerVisuals : byte + { + VisualState + } + + [Serializable, NetSerializable] + public class SolutionContainerVisualState + { + public readonly Color Color; + + /// + /// Represents how full the container is, as a fraction equivalent to /. + /// + public readonly byte FilledVolumeFraction; + + // do we really need this just to save three bytes? + public float FilledVolumePercent => (float) FilledVolumeFraction / byte.MaxValue; + + /// + /// Sets the solution state of a container. + /// + /// + /// The fraction of the container's volume that is filled. + public SolutionContainerVisualState(Color color, float filledVolumeFraction) + { + Color = color; + FilledVolumeFraction = (byte) (byte.MaxValue * filledVolumeFraction); + } + } + + public enum SolutionContainerLayers : byte + { + Fill, + Base + } +} diff --git a/Content.Shared/Chemistry/Reaction/IReactionEffect.cs b/Content.Shared/Chemistry/Reaction/IReactionEffect.cs index 9b8762dd28..995e1ccbb1 100644 --- a/Content.Shared/Chemistry/Reaction/IReactionEffect.cs +++ b/Content.Shared/Chemistry/Reaction/IReactionEffect.cs @@ -1,3 +1,4 @@ +using Content.Shared.Chemistry.Components; using Robust.Shared.GameObjects; namespace Content.Shared.Chemistry.Reaction @@ -7,6 +8,6 @@ namespace Content.Shared.Chemistry.Reaction /// public interface IReactionEffect { - void React(IEntity solutionEntity, double intensity); + void React(Solution solution, IEntity solutionEntity, double intensity); } } diff --git a/Content.Shared/Chemistry/Reaction/SharedChemicalReactionSystem.cs b/Content.Shared/Chemistry/Reaction/SharedChemicalReactionSystem.cs index bb39151e54..d5f9fb6e8f 100644 --- a/Content.Shared/Chemistry/Reaction/SharedChemicalReactionSystem.cs +++ b/Content.Shared/Chemistry/Reaction/SharedChemicalReactionSystem.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; using Robust.Shared.GameObjects; using Robust.Shared.IoC; @@ -28,7 +29,7 @@ namespace Content.Shared.Chemistry.Reaction /// The reaction to check. /// How many times this reaction can occur. /// - private static bool CanReact(Solution.Solution solution, ReactionPrototype reaction, out ReagentUnit lowestUnitReactions) + private static bool CanReact(Solution solution, ReactionPrototype reaction, out ReagentUnit lowestUnitReactions) { lowestUnitReactions = ReagentUnit.MaxValue; @@ -54,7 +55,7 @@ namespace Content.Shared.Chemistry.Reaction /// Perform a reaction on a solution. This assumes all reaction criteria are met. /// Removes the reactants from the solution, then returns a solution with all products. /// - private Solution.Solution PerformReaction(Solution.Solution solution, IEntity owner, ReactionPrototype reaction, ReagentUnit unitReactions) + private Solution PerformReaction(Solution solution, IEntity owner, ReactionPrototype reaction, ReagentUnit unitReactions) { //Remove reactants foreach (var reactant in reaction.Reactants) @@ -67,23 +68,23 @@ namespace Content.Shared.Chemistry.Reaction } //Create products - var products = new Solution.Solution(); + var products = new Solution(); foreach (var product in reaction.Products) { products.AddReagent(product.Key, product.Value * unitReactions); } // Trigger reaction effects - OnReaction(reaction, owner, unitReactions); + OnReaction(solution, reaction, owner, unitReactions); return products; } - protected virtual void OnReaction(ReactionPrototype reaction, IEntity owner, ReagentUnit unitReactions) + protected virtual void OnReaction(Solution solution, ReactionPrototype reaction, IEntity owner, ReagentUnit unitReactions) { foreach (var effect in reaction.Effects) { - effect.React(owner, unitReactions.Double()); + effect.React(solution, owner, unitReactions.Double()); } } @@ -92,10 +93,10 @@ namespace Content.Shared.Chemistry.Reaction /// Removes the reactants from the solution, then returns a solution with all products. /// WARNING: Does not trigger reactions between solution and new products. /// - private Solution.Solution ProcessReactions(Solution.Solution solution, IEntity owner) + private Solution ProcessReactions(Solution solution, IEntity owner) { //TODO: make a hashmap at startup and then look up reagents in the contents for a reaction - var overallProducts = new Solution.Solution(); + var overallProducts = new Solution(); foreach (var reaction in _reactions) { if (CanReact(solution, reaction, out var unitReactions)) @@ -111,7 +112,7 @@ namespace Content.Shared.Chemistry.Reaction /// /// Continually react a solution until no more reactions occur. /// - public void FullyReactSolution(Solution.Solution solution, IEntity owner) + public void FullyReactSolution(Solution solution, IEntity owner) { for (var i = 0; i < MaxReactionIterations; i++) { @@ -122,14 +123,14 @@ namespace Content.Shared.Chemistry.Reaction solution.AddSolution(products); } - Logger.Error($"{nameof(Solution.Solution)} on {owner} (Uid: {owner.Uid}) could not finish reacting in under {MaxReactionIterations} loops."); + Logger.Error($"{nameof(Solution)} on {owner} (Uid: {owner.Uid}) could not finish reacting in under {MaxReactionIterations} loops."); } /// /// Continually react a solution until no more reactions occur, with a volume constraint. /// If a reaction's products would exceed the max volume, some product is deleted. /// - public void FullyReactSolution(Solution.Solution solution, IEntity owner, ReagentUnit maxVolume) + public void FullyReactSolution(Solution solution, IEntity owner, ReagentUnit maxVolume) { for (var i = 0; i < MaxReactionIterations; i++) { @@ -148,7 +149,7 @@ namespace Content.Shared.Chemistry.Reaction solution.AddSolution(products); } - Logger.Error($"{nameof(Solution.Solution)} on {owner} (Uid: {owner.Uid}) could not finish reacting in under {MaxReactionIterations} loops."); + Logger.Error($"{nameof(Solution)} on {owner} (Uid: {owner.Uid}) could not finish reacting in under {MaxReactionIterations} loops."); } } } diff --git a/Content.Shared/Chemistry/Reagent/ReagentEffect.cs b/Content.Shared/Chemistry/Reagent/ReagentEffect.cs index 78ca370df0..d8aebb5ae9 100644 --- a/Content.Shared/Chemistry/Reagent/ReagentEffect.cs +++ b/Content.Shared/Chemistry/Reagent/ReagentEffect.cs @@ -18,6 +18,6 @@ namespace Content.Shared.Chemistry.Reagent [DataField("conditions")] public ReagentEffectCondition[]? Conditions; - public abstract void Metabolize(IEntity solutionEntity, Solution.Solution.ReagentQuantity amount); + public abstract void Metabolize(IEntity solutionEntity, Components.Solution.ReagentQuantity amount); } } diff --git a/Content.Shared/Chemistry/Reagent/ReagentEffectCondition.cs b/Content.Shared/Chemistry/Reagent/ReagentEffectCondition.cs index 54253d1c6d..aea367fce9 100644 --- a/Content.Shared/Chemistry/Reagent/ReagentEffectCondition.cs +++ b/Content.Shared/Chemistry/Reagent/ReagentEffectCondition.cs @@ -1,5 +1,4 @@ -using Content.Shared.Body.Components; -using Content.Shared.Chemistry.Solution; +using Content.Shared.Chemistry.Components; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; @@ -8,6 +7,6 @@ namespace Content.Shared.Chemistry.Reagent [ImplicitDataDefinitionForInheritors] public abstract class ReagentEffectCondition { - public abstract bool Condition(IEntity solutionEntity, Solution.Solution.ReagentQuantity reagent); + public abstract bool Condition(IEntity solutionEntity, Solution.ReagentQuantity reagent); } } diff --git a/Content.Shared/Chemistry/Reagent/ReagentEntityReaction.cs b/Content.Shared/Chemistry/Reagent/ReagentEntityReaction.cs index fe8e081e25..3d96dc3bc8 100644 --- a/Content.Shared/Chemistry/Reagent/ReagentEntityReaction.cs +++ b/Content.Shared/Chemistry/Reagent/ReagentEntityReaction.cs @@ -27,7 +27,7 @@ namespace Content.Shared.Chemistry.Reagent [DataField("ingestion")] public bool Ingestion { get; } = false; - public void React(ReactionMethod method, IEntity entity, ReagentPrototype reagent, ReagentUnit volume, Solution.Solution? source) + public void React(ReactionMethod method, IEntity entity, ReagentPrototype reagent, ReagentUnit volume, Components.Solution? source) { switch (method) { @@ -50,6 +50,6 @@ namespace Content.Shared.Chemistry.Reagent React(entity, reagent, volume, source); } - protected abstract void React(IEntity entity, ReagentPrototype reagent, ReagentUnit volume, Solution.Solution? source); + protected abstract void React(IEntity entity, ReagentPrototype reagent, ReagentUnit volume, Components.Solution? source); } } diff --git a/Content.Shared/Chemistry/Solution/Components/ISolutionInteractionsComponent.cs b/Content.Shared/Chemistry/Solution/Components/ISolutionInteractionsComponent.cs deleted file mode 100644 index d1965f970e..0000000000 --- a/Content.Shared/Chemistry/Solution/Components/ISolutionInteractionsComponent.cs +++ /dev/null @@ -1,89 +0,0 @@ -using Content.Shared.Chemistry.Reagent; -using Robust.Shared.GameObjects; - -namespace Content.Shared.Chemistry.Solution.Components -{ - /// - /// High-level solution transferring operations like "what happens when a syringe tries to inject this entity." - /// - /// - /// This interface is most often implemented by using - /// and setting the appropriate - /// - public interface ISolutionInteractionsComponent : IComponent - { - // - // INJECTING - // - - /// - /// Whether we CAN POTENTIALLY be injected with solutions by items like syringes. - /// - /// - /// - /// This should NOT change to communicate behavior like "the container is full". - /// Change to 0 for that. - /// - /// - /// If refilling is allowed () you should also always allow injecting. - /// - /// - bool CanInject => false; - - /// - /// The amount of solution space available for injecting. - /// - ReagentUnit InjectSpaceAvailable => ReagentUnit.Zero; - - /// - /// Actually inject reagents. - /// - void Inject(Solution solution) - { - - } - - // - // DRAWING - // - - bool CanDraw => false; - ReagentUnit DrawAvailable => ReagentUnit.Zero; - - Solution Draw(ReagentUnit amount) - { - return new(); - } - - - - // - // REFILLING - // - - bool CanRefill => false; - ReagentUnit RefillSpaceAvailable => ReagentUnit.Zero; - - /// - /// The amount that will transfer if something is spilled on the container. - /// - ReagentUnit MaxSpillRefill => ReagentUnit.Zero; - - void Refill(Solution solution) - { - - } - - // - // DRAINING - // - - bool CanDrain => false; - ReagentUnit DrainAvailable => ReagentUnit.Zero; - - Solution Drain(ReagentUnit amount) - { - return new(); - } - } -} diff --git a/Content.Shared/Chemistry/Solution/Components/SharedSolutionContainerComponent.cs b/Content.Shared/Chemistry/Solution/Components/SharedSolutionContainerComponent.cs deleted file mode 100644 index 0e6e5c97f9..0000000000 --- a/Content.Shared/Chemistry/Solution/Components/SharedSolutionContainerComponent.cs +++ /dev/null @@ -1,329 +0,0 @@ -using System; -using System.Collections.Generic; -using Content.Shared.Chemistry.Reaction; -using Content.Shared.Chemistry.Reagent; -using Content.Shared.Examine; -using Robust.Shared.GameObjects; -using Robust.Shared.GameStates; -using Robust.Shared.IoC; -using Robust.Shared.Localization; -using Robust.Shared.Log; -using Robust.Shared.Maths; -using Robust.Shared.Players; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.Utility; -using Robust.Shared.ViewVariables; - -namespace Content.Shared.Chemistry.Solution.Components -{ - /// - /// Holds a with a limited volume. - /// - [NetworkedComponent()] - public abstract class SharedSolutionContainerComponent : Component, IExamine, ISolutionInteractionsComponent - { - public override string Name => "SolutionContainer"; - - [ViewVariables] - [DataField("contents")] - public Solution Solution { get; private set; } = new(); - - public IReadOnlyList ReagentList => Solution.Contents; - - [ViewVariables(VVAccess.ReadWrite)] - [DataField("maxVol")] - public ReagentUnit MaxVolume { get; set; } = ReagentUnit.Zero; - - [ViewVariables] - public ReagentUnit CurrentVolume => Solution.TotalVolume; - - /// - /// Volume needed to fill this container. - /// - [ViewVariables] - public ReagentUnit EmptyVolume => MaxVolume - CurrentVolume; - - [ViewVariables] - public virtual Color Color => Solution.Color; - - /// - /// If reactions will be checked for when adding reagents to the container. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("canReact")] - public bool CanReact { get; set; } = true; - - [ViewVariables(VVAccess.ReadWrite)] - [DataField("caps")] - public SolutionContainerCaps Capabilities { get; set; } - - public bool CanExamineContents => Capabilities.HasCap(SolutionContainerCaps.CanExamine); - - public bool CanUseWithChemDispenser => Capabilities.HasCap(SolutionContainerCaps.FitsInDispenser); - - public bool CanInject => Capabilities.HasCap(SolutionContainerCaps.Injectable) || CanRefill; - public bool CanDraw => Capabilities.HasCap(SolutionContainerCaps.Drawable) || CanDrain; - - public bool CanRefill => Capabilities.HasCap(SolutionContainerCaps.Refillable); - public bool CanDrain => Capabilities.HasCap(SolutionContainerCaps.Drainable); - - protected override void Initialize() - { - base.Initialize(); - UpdateAppearance(); - } - - public void RemoveAllSolution() - { - if (CurrentVolume == 0) - return; - - Solution.RemoveAllSolution(); - ChemicalsRemoved(); - } - - /// - /// Adds reagent of an Id to the container. - /// - /// The Id of the reagent to add. - /// The amount of reagent to add. - /// The amount of reagent sucesfully added. - /// If all the reagent could be added. - public bool TryAddReagent(string reagentId, ReagentUnit quantity, out ReagentUnit acceptedQuantity) - { - acceptedQuantity = EmptyVolume > quantity ? quantity : EmptyVolume; - Solution.AddReagent(reagentId, acceptedQuantity); - - if (acceptedQuantity > 0) - ChemicalsAdded(); - - return acceptedQuantity == quantity; - } - - /// - /// Removes reagent of an Id to the container. - /// - /// The Id of the reagent to remove. - /// The amount of reagent to remove. - /// If the reagent to remove was found in the container. - public bool TryRemoveReagent(string reagentId, ReagentUnit quantity) - { - if (!Solution.ContainsReagent(reagentId)) - return false; - - Solution.RemoveReagent(reagentId, quantity); - ChemicalsRemoved(); - return true; - } - - /// - /// Removes part of the solution in the container. - /// - /// the volume of solution to remove. - /// The solution that was removed. - public Solution SplitSolution(ReagentUnit quantity) - { - var splitSol = Solution.SplitSolution(quantity); - ChemicalsRemoved(); - return splitSol; - } - - /// - /// Checks if a solution can fit into the container. - /// - /// The solution that is trying to be added. - /// If the solution can be fully added. - public bool CanAddSolution(Solution solution) - { - return solution.TotalVolume <= EmptyVolume; - } - - /// - /// Adds a solution to the container, if it can fully fit. - /// - /// The solution to try to add. - /// If the solution could be added. - public bool TryAddSolution(Solution solution) - { - if (!CanAddSolution(solution) || solution.TotalVolume == 0) - return false; - - Solution.AddSolution(solution); - ChemicalsAdded(); - return true; - } - - private void ChemicalsAdded() - { - ProcessReactions(); - SolutionChanged(); - UpdateAppearance(); - Dirty(); - } - - private void ChemicalsRemoved() - { - SolutionChanged(); - UpdateAppearance(); - Dirty(); - } - - private void SolutionChanged() - { - EntitySystem.Get().HandleSolutionChange(Owner); - } - - private void ProcessReactions() - { - if (!CanReact) - return; - - EntitySystem.Get() - .FullyReactSolution(Solution, Owner, MaxVolume); - } - - void IExamine.Examine(FormattedMessage message, bool inDetailsRange) - { - if (!CanExamineContents) - return; - - var prototypeManager = IoCManager.Resolve(); - - if (ReagentList.Count == 0) - { - message.AddText(Loc.GetString("shared-solution-container-component-on-examine-empty-container")); - return; - } - - var primaryReagent = Solution.GetPrimaryReagentId(); - if (!prototypeManager.TryIndex(primaryReagent, out ReagentPrototype? proto)) - { - Logger.Error($"{nameof(SharedSolutionContainerComponent)} could not find the prototype associated with {primaryReagent}."); - return; - } - - var colorHex = Color.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"; - - message.AddMarkup(Loc.GetString(messageString, - ("color", colorHex), - ("wordedAmount", Loc.GetString(ReagentList.Count == 1 ? "shared-solution-container-component-on-examine-worded-amount-one-reagent" : - "shared-solution-container-component-on-examine-worded-amount-multiple-reagents")), - ("desc", Loc.GetString(proto.PhysicalDescription)))); - } - - ReagentUnit ISolutionInteractionsComponent.RefillSpaceAvailable => EmptyVolume; - ReagentUnit ISolutionInteractionsComponent.InjectSpaceAvailable => EmptyVolume; - ReagentUnit ISolutionInteractionsComponent.DrawAvailable => CurrentVolume; - ReagentUnit ISolutionInteractionsComponent.DrainAvailable => CurrentVolume; - - [DataField("maxSpillRefill")] - public ReagentUnit MaxSpillRefill { get; set; } - - void ISolutionInteractionsComponent.Refill(Solution solution) - { - if (!CanRefill) - return; - - TryAddSolution(solution); - } - - void ISolutionInteractionsComponent.Inject(Solution solution) - { - if (!CanInject) - return; - - TryAddSolution(solution); - } - - Solution ISolutionInteractionsComponent.Draw(ReagentUnit amount) - { - if (!CanDraw) - return new Solution(); - - return SplitSolution(amount); - } - - Solution ISolutionInteractionsComponent.Drain(ReagentUnit amount) - { - if (!CanDrain) - return new Solution(); - - return SplitSolution(amount); - } - - private void UpdateAppearance() - { - if (Owner.Deleted || !Owner.TryGetComponent(out var appearance)) - return; - - appearance.SetData(SolutionContainerVisuals.VisualState, GetVisualState()); - } - - private SolutionContainerVisualState GetVisualState() - { - var filledVolumeFraction = CurrentVolume.Float() / MaxVolume.Float(); - - return new SolutionContainerVisualState(Color, filledVolumeFraction); - } - - public override ComponentState GetComponentState(ICommonSession player) - { - return new SolutionContainerComponentState(Solution); - } - - public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) - { - if (curState is not SolutionContainerComponentState containerState) - return; - - Solution = containerState.Solution; - } - } - - [Serializable, NetSerializable] - public enum SolutionContainerVisuals : byte - { - VisualState - } - - [Serializable, NetSerializable] - public class SolutionContainerVisualState - { - public readonly Color Color; - - /// - /// Represents how full the container is, as a fraction equivalent to /. - /// - public readonly byte FilledVolumeFraction; - - // do we really need this just to save three bytes? - public float FilledVolumePercent => (float) FilledVolumeFraction / byte.MaxValue; - - /// The fraction of the container's volume that is filled. - public SolutionContainerVisualState(Color color, float filledVolumeFraction) - { - Color = color; - FilledVolumeFraction = (byte) (byte.MaxValue * filledVolumeFraction); - } - } - - public enum SolutionContainerLayers : byte - { - Fill, - Base - } - - [Serializable, NetSerializable] - public class SolutionContainerComponentState : ComponentState - { - public readonly Solution Solution; - - public SolutionContainerComponentState(Solution solution) - { - Solution = solution; - } - } -} diff --git a/Content.Shared/Chemistry/Solution/SolutionContainerCaps.cs b/Content.Shared/Chemistry/Solution/SolutionContainerCaps.cs deleted file mode 100644 index 510018dff1..0000000000 --- a/Content.Shared/Chemistry/Solution/SolutionContainerCaps.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using Content.Shared.Chemistry.Solution.Components; -using Robust.Shared.Serialization; - -namespace Content.Shared.Chemistry.Solution -{ - /// - /// Define common interaction behaviors for - /// - /// - [Flags] - [Serializable, NetSerializable] - public enum SolutionContainerCaps : ushort - { - None = 0, - - /// - /// Reagents can be added with syringes. - /// - Injectable = 1 << 0, - - /// - /// Reagents can be removed with syringes. - /// - Drawable = 1 << 1, - - /// - /// Reagents can be easily added via all reagent containers. - /// Think pouring something into another beaker or into the gas tank of a car. - /// - Refillable = 1 << 2, - - /// - /// Reagents can be easily removed through any reagent container. - /// Think pouring this or draining from a water tank. - /// - Drainable = 1 << 3, - - /// - /// The contents of the solution can be examined directly. - /// - CanExamine = 1 << 4, - - /// - /// Allows the container to be placed in a ReagentDispenserComponent. - /// Otherwise it's considered to be too large or the improper shape to fit. - /// Allows us to have obscenely large containers that are harder to abuse in chem dispensers - /// since they can't be placed directly in them. - /// - FitsInDispenser = 1 << 5, - - OpenContainer = Refillable | Drainable | CanExamine - } - - public static class SolutionContainerCapsHelpers - { - public static bool HasCap(this SolutionContainerCaps cap, SolutionContainerCaps other) - { - return (cap & other) == other; - } - } -} diff --git a/Content.Shared/Kitchen/Components/SharedMicrowaveComponent.cs b/Content.Shared/Kitchen/Components/SharedMicrowaveComponent.cs index bd0b511317..2fb875f0bf 100644 --- a/Content.Shared/Kitchen/Components/SharedMicrowaveComponent.cs +++ b/Content.Shared/Kitchen/Components/SharedMicrowaveComponent.cs @@ -1,5 +1,5 @@ using System; -using Content.Shared.Chemistry.Solution; +using Content.Shared.Chemistry.Components; using Robust.Shared.GameObjects; using Robust.Shared.GameStates; using Robust.Shared.Serialization; diff --git a/Content.Shared/Kitchen/Components/SharedReagentGrinderComponent.cs b/Content.Shared/Kitchen/Components/SharedReagentGrinderComponent.cs index 0effbe15db..85bc5c474b 100644 --- a/Content.Shared/Kitchen/Components/SharedReagentGrinderComponent.cs +++ b/Content.Shared/Kitchen/Components/SharedReagentGrinderComponent.cs @@ -1,5 +1,5 @@ using System; -using Content.Shared.Chemistry.Solution; +using Content.Shared.Chemistry.Components; using Robust.Shared.GameObjects; using Robust.Shared.Serialization; diff --git a/Content.Shared/Vapor/SharedVaporComponent.cs b/Content.Shared/Vapor/SharedVaporComponent.cs index 0d9754dcf7..a2395aa91d 100644 --- a/Content.Shared/Vapor/SharedVaporComponent.cs +++ b/Content.Shared/Vapor/SharedVaporComponent.cs @@ -7,6 +7,7 @@ namespace Content.Shared.Vapor public class SharedVaporComponent : Component { public override string Name => "Vapor"; + public const string SolutionName = "vapor"; } [Serializable, NetSerializable] diff --git a/Content.Tests/Shared/Chemistry/Solution_Tests.cs b/Content.Tests/Shared/Chemistry/Solution_Tests.cs index b3dab60fda..5f2abb2e7d 100644 --- a/Content.Tests/Shared/Chemistry/Solution_Tests.cs +++ b/Content.Tests/Shared/Chemistry/Solution_Tests.cs @@ -1,6 +1,5 @@ -using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Chemistry.Solution; using NUnit.Framework; namespace Content.Tests.Shared.Chemistry diff --git a/Resources/Locale/en-US/chemistry/components/hypospray-component.ftl b/Resources/Locale/en-US/chemistry/components/hypospray-component.ftl index 6ea6911135..454f21099e 100644 --- a/Resources/Locale/en-US/chemistry/components/hypospray-component.ftl +++ b/Resources/Locale/en-US/chemistry/components/hypospray-component.ftl @@ -9,4 +9,5 @@ hypospray-component-inject-self-message = You inject yourself. hypospray-component-inject-self-clumsy-message = Oops! You injected yourself. hypospray-component-empty-message = It's empty! hypospray-component-feel-prick-message = You feel a tiny prick! -hypospray-component-transfer-already-full-message = {$owner} is already full! \ No newline at end of file +hypospray-component-transfer-already-full-message = {$owner} is already full! +hypospray-cant-inject = Can't inject into ${target}! diff --git a/Resources/Maps/saltern.yml b/Resources/Maps/saltern.yml index f489dc04a8..4079848fd6 100644 --- a/Resources/Maps/saltern.yml +++ b/Resources/Maps/saltern.yml @@ -534,8 +534,13 @@ entities: pos: -15.694785,24.608267 parent: 853 type: Transform - - caps: Refillable, Drainable - type: SolutionContainer + - type: SolutionContainerManager + solutions: + drink: + - type: RefillableSolution + solution: drink + - type: DrainableSolution + solution: drink - uid: 60 type: DisgustingSweptSoup components: @@ -650,8 +655,13 @@ entities: pos: -3.470539,16.956116 parent: 853 type: Transform - - caps: Refillable, Drainable - type: SolutionContainer + - type: SolutionContainerManager + solutions: + drink: + - type: RefillableSolution + solution: drink + - type: DrainableSolution + solution: drink - uid: 74 type: Carpet components: @@ -698,8 +708,13 @@ entities: - pos: 8.439846,26.712742 parent: 853 type: Transform - - caps: Refillable, Drainable - type: SolutionContainer + - type: SolutionContainerManager + solutions: + drink: + - type: RefillableSolution + solution: drink + - type: DrainableSolution + solution: drink - uid: 81 type: Table components: @@ -924,8 +939,13 @@ entities: - pos: 8.661116,25.513401 parent: 853 type: Transform - - caps: Refillable, Drainable - type: SolutionContainer + - type: SolutionContainerManager + solutions: + drink: + - type: RefillableSolution + solution: drink + - type: DrainableSolution + solution: drink - uid: 114 type: FoodBoxDonkpocket components: @@ -15150,7 +15170,7 @@ entities: parent: 853 type: Transform - caps: Refillable, Drainable - type: SolutionContainer + type: SolutionContainerManager - sleepTime: 1114.4688 type: Physics - uid: 1162 @@ -24934,8 +24954,13 @@ entities: pos: -8.476567,-17.420076 parent: 853 type: Transform - - caps: Refillable, Drainable - type: SolutionContainer + - type: SolutionContainerManager + solutions: + drink: + - type: RefillableSolution + solution: drink + - type: DrainableSolution + solution: drink - uid: 2443 type: TableWood components: @@ -47242,8 +47267,13 @@ entities: pos: -20.5,-1.5 parent: 853 type: Transform - - caps: Refillable, Drainable - type: SolutionContainer + - type: SolutionContainerManager + solutions: + bucket: + - type: RefillableSolution + solution: bucket + - type: DrainableSolution + solution: bucket - uid: 4719 type: SpawnPointLatejoin components: @@ -50248,7 +50278,7 @@ entities: parent: 853 type: Transform - caps: Refillable, Drainable - type: SolutionContainer + type: SolutionContainerManager - canCollide: False type: Physics - uid: 5079 @@ -50258,7 +50288,7 @@ entities: parent: 853 type: Transform - caps: Refillable, Drainable - type: SolutionContainer + type: SolutionContainerManager - canCollide: False type: Physics - uid: 5080 diff --git a/Resources/Prototypes/Body/Mechanisms/human.yml b/Resources/Prototypes/Body/Mechanisms/human.yml index 08340d6775..ce174ccc15 100644 --- a/Resources/Prototypes/Body/Mechanisms/human.yml +++ b/Resources/Prototypes/Body/Mechanisms/human.yml @@ -3,19 +3,19 @@ parent: BaseItem abstract: true components: - - type: Tag - tags: - - Grindable - type: Sprite netsync: false sprite: Mobs/Species/Human/organs.rsi - type: Mechanism - type: Food - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 + - type: Extractable + extractableSolution: extractableSolution + - type: SolutionContainerManager + solutions: + organ: + reagents: + - ReagentId: Nutriment + Quantity: 10 - type: entity id: OrganHumanBrain @@ -217,8 +217,10 @@ - !type:StomachBehavior max_volume: 250 digestionDelay: 20 - - type: SolutionContainer - maxVol: 250 + - type: SolutionContainerManager + solutions: + stomach: + maxVol: 250 # The stomach metabolizes stuff like foods and drinks. # TODO: Have it work off of the ent's solution container, and move this # to intestines instead. diff --git a/Resources/Prototypes/Entities/Effects/chemistry_effects.yml b/Resources/Prototypes/Entities/Effects/chemistry_effects.yml index 0776e99ed8..4b242be699 100644 --- a/Resources/Prototypes/Entities/Effects/chemistry_effects.yml +++ b/Resources/Prototypes/Entities/Effects/chemistry_effects.yml @@ -3,51 +3,55 @@ name: smoke abstract: true components: - - type: Sprite - drawdepth: Effects - sprite: Effects/chemsmoke.rsi - state: chemsmoke - - type: Appearance - visuals: - - type: SmokeVisualizer - - type: Occluder - - type: SnapGrid - - type: SmokeSolutionAreaEffect - - type: SolutionContainer - maxVol: 600 + - type: Sprite + drawdepth: Effects + sprite: Effects/chemsmoke.rsi + state: chemsmoke + - type: Appearance + visuals: + - type: SmokeVisualizer + - type: Occluder + - type: SnapGrid + - type: SmokeSolutionAreaEffect + - type: SolutionContainerManager + solutions: + smoke: + maxVol: 600 - type: entity id: Foam name: foam abstract: true components: - - type: Sprite - netsync: false - drawdepth: Effects - color: "#ffffffcc" #Add some transparency - sprite: Effects/foam.rsi - state: foam - layers: - - state: foam - map: ["enum.FoamVisualLayers.Base"] - - type: AnimationPlayer - - type: Appearance - visuals: - - type: FoamVisualizer - animationTime: 0.6 - animationState: foam-dissolve - - type: SnapGrid - - type: Physics - fixtures: - - shape: - !type:PhysShapeAabb - bounds: "-0.4,-0.4,0.4,0.4" - layer: - - MobImpassable - - type: FoamSolutionAreaEffect - - type: SolutionContainer - maxVol: 600 - - type: Slippery + - type: Sprite + netsync: false + drawdepth: Effects + color: "#ffffffcc" #Add some transparency + sprite: Effects/foam.rsi + state: foam + layers: + - state: foam + map: ["enum.FoamVisualLayers.Base"] + - type: AnimationPlayer + - type: Appearance + visuals: + - type: FoamVisualizer + animationTime: 0.6 + animationState: foam-dissolve + - type: SnapGrid + - type: Physics + fixtures: + - shape: + !type:PhysShapeAabb + bounds: "-0.4,-0.4,0.4,0.4" + layer: + - MobImpassable + - type: FoamSolutionAreaEffect + - type: SolutionContainerManager + solutions: + foam: + maxVol: 600 + - type: Slippery - type: entity id: IronMetalFoam @@ -55,18 +59,18 @@ abstract: true parent: Foam components: - - type: Sprite - state: mfoam - layers: - - state: mfoam - map: ["enum.FoamVisualLayers.Base"] - - type: Appearance - visuals: - - type: FoamVisualizer - animationTime: 0.6 - animationState: mfoam-dissolve - - type: FoamSolutionAreaEffect - foamedMetalPrototype: FoamedIronMetal + - type: Sprite + state: mfoam + layers: + - state: mfoam + map: ["enum.FoamVisualLayers.Base"] + - type: Appearance + visuals: + - type: FoamVisualizer + animationTime: 0.6 + animationState: mfoam-dissolve + - type: FoamSolutionAreaEffect + foamedMetalPrototype: FoamedIronMetal - type: entity id: AluminiumMetalFoam @@ -74,18 +78,18 @@ abstract: true parent: Foam components: - - type: Sprite - state: mfoam - layers: - - state: mfoam - map: ["enum.FoamVisualLayers.Base"] - - type: Appearance - visuals: - - type: FoamVisualizer - animationTime: 0.6 - animationState: mfoam-dissolve - - type: FoamSolutionAreaEffect - foamedMetalPrototype: FoamedAluminiumMetal + - type: Sprite + state: mfoam + layers: + - state: mfoam + map: ["enum.FoamVisualLayers.Base"] + - type: Appearance + visuals: + - type: FoamVisualizer + animationTime: 0.6 + animationState: mfoam-dissolve + - type: FoamSolutionAreaEffect + foamedMetalPrototype: FoamedAluminiumMetal - type: entity id: BaseFoamedMetal @@ -96,37 +100,37 @@ snap: - Wall components: - - type: RCDDeconstructWhitelist - - type: Clickable - - type: InteractionOutline - - type: Sprite - netsync: false - drawdepth: Walls - - type: Physics - fixtures: - - shape: - !type:PhysShapeAabb {} - layer: - - Opaque - - Impassable - - MobImpassable - - VaultImpassable - - SmallImpassable - - type: Occluder - sizeX: 32 - sizeY: 32 - - type: SnapGrid - - type: Airtight - - type: Damageable - resistances: metallicResistances - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 50 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] + - type: RCDDeconstructWhitelist + - type: Clickable + - type: InteractionOutline + - type: Sprite + netsync: false + drawdepth: Walls + - type: Physics + fixtures: + - shape: + !type:PhysShapeAabb {} + layer: + - Opaque + - Impassable + - MobImpassable + - VaultImpassable + - SmallImpassable + - type: Occluder + sizeX: 32 + sizeY: 32 + - type: SnapGrid + - type: Airtight + - type: Damageable + resistances: metallicResistances + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 50 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity id: FoamedIronMetal @@ -134,9 +138,9 @@ description: "For sealing hull breaches." parent: BaseFoamedMetal components: - - type: Sprite - sprite: Effects/foam.rsi - state: ironfoam + - type: Sprite + sprite: Effects/foam.rsi + state: ironfoam - type: entity id: FoamedAluminiumMetal @@ -144,6 +148,6 @@ description: "For sealing hull breaches." parent: BaseFoamedMetal components: - - type: Sprite - sprite: Effects/foam.rsi - state: metalfoam + - type: Sprite + sprite: Effects/foam.rsi + state: metalfoam diff --git a/Resources/Prototypes/Entities/Effects/puddle.yml b/Resources/Prototypes/Entities/Effects/puddle.yml index 1e03310877..8a1ad8c486 100644 --- a/Resources/Prototypes/Entities/Effects/puddle.yml +++ b/Resources/Prototypes/Entities/Effects/puddle.yml @@ -6,7 +6,7 @@ - type: SnapGrid - type: Sprite drawdepth: FloorObjects - - type: SolutionContainer + - type: SolutionContainerManager - type: Puddle spill_sound: path: /Audio/Effects/Fluids/splat.ogg diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 221bce7ff9..23238cb574 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -342,9 +342,6 @@ id: MouseMob_Content description: Squeak! components: - - type: Tag - tags: - - Grindable - type: GhostTakeoverAvailable makeSentient: true name: mouse @@ -387,11 +384,14 @@ # Eek! You can eat them alive for now until someone makes something that detects when # a mob is dead or something idk - type: Food - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: Extractable + extractableSolution: food + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: MouseAccent - type: entity diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml index b8eb93bea9..38c8e9040c 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml @@ -54,6 +54,7 @@ - SmallImpassable layer: - MobImpassable + - type: SolutionContainerManager - type: Bloodstream max_volume: 100 - type: Damageable diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index 289d99a7bd..19b6cc811f 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -36,14 +36,19 @@ - type: Barotrauma damageType: Blunt - type: DamageOnHighSpeedImpact - soundHit: + soundHit: path: /Audio/Effects/hit_kick.ogg - type: Hunger - type: Thirst # Organs - - type: SolutionContainer - maxVol: 250 - caps: Injectable, Drawable + - type: SolutionContainerManager + solutions: + bloodstream: + maxVol: 250 + - type: InjectableSolution + solution: bloodstream + - type: DrawableSolution + solution: bloodstream - type: Bloodstream max_volume: 100 # StatusEffects diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml index ddac24a2f3..28e2e07c48 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml @@ -9,24 +9,26 @@ - type: Sprite sprite: Objects/Consumable/Food/Baked/bread.rsi netsync: false - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 20 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 + reagents: + - ReagentId: Nutriment + Quantity: 20 - type: entity parent: FoodBreadBase id: FoodBreadSliceBase abstract: true components: - - type: SolutionContainer - maxVol: 7 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 7 + - type: SolutionContainerManager + solutions: + food: + maxVol: 7 + reagents: + - ReagentId: Nutriment + Quantity: 7 # Custom Bread Example diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml index 0ecb6b5fa6..d99802fb78 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml @@ -10,12 +10,13 @@ - type: Sprite sprite: Objects/Consumable/Food/Baked/cake.rsi netsync: false - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 20 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 + reagents: + - ReagentId: Nutriment + Quantity: 20 - type: entity parent: FoodCakeBase @@ -25,12 +26,13 @@ components: - type: Food trash: FoodPlateSmall - - type: SolutionContainer - maxVol: 7 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 7 + - type: SolutionContainerManager + solutions: + food: + maxVol: 7 + reagents: + - ReagentId: Nutriment + Quantity: 7 # Custom Cake Example diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/donkpocket.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/donkpocket.yml index c24a919ef9..1886e58d4d 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/donkpocket.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/donkpocket.yml @@ -9,12 +9,13 @@ - type: Sprite sprite: Objects/Consumable/Food/Baked/donkpocket.rsi netsync: false - - type: SolutionContainer - maxVol: 10 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 3 + - type: SolutionContainerManager + solutions: + food: + maxVol: 10 + reagents: + - ReagentId: Nutriment + Quantity: 3 - type: Item size: 1 @@ -38,11 +39,13 @@ id: FoodDonkpocketWarm description: The heated food of choice for the seasoned traitor. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 7 + - type: SolutionContainerManager + solutions: + food: + maxVol: 10 + reagents: + - ReagentId: Nutriment + Quantity: 7 - type: entity name: dank-pocket @@ -50,11 +53,12 @@ id: FoodDonkpocketDank description: The food of choice for the seasoned botanist. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: Sprite state: dank @@ -64,13 +68,14 @@ id: FoodDonkpocketDankWarm description: The heated food of choice for the seasoned botanist. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 - - ReagentId: Omnizine - Quantity: 4 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 10 + - ReagentId: Omnizine + Quantity: 4 - type: entity name: spicy-pocket @@ -87,13 +92,14 @@ id: FoodDonkpocketSpicyWarm description: The classic snack food, now maybe a bit too spicy. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 - - ReagentId: Omnizine - Quantity: 2 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 10 + - ReagentId: Omnizine + Quantity: 2 - type: Sprite state: spicy @@ -112,13 +118,14 @@ id: FoodDonkpocketTeriyakiWarm description: An East Asian take on the classic stationside snack, now steamy and warm. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 - - ReagentId: Omnizine - Quantity: 2 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 10 + - ReagentId: Omnizine + Quantity: 2 - type: entity name: pizza-pocket @@ -126,11 +133,12 @@ id: FoodDonkpocketPizza description: Delicious, cheesy and surprisingly filling. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 10 - type: Sprite state: pizza @@ -140,13 +148,14 @@ id: FoodDonkpocketPizzaWarm description: Cheese filling really hits the spot when warm. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 15 - - ReagentId: Omnizine - Quantity: 2 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 15 + - ReagentId: Omnizine + Quantity: 2 - type: entity name: honk-pocket @@ -154,12 +163,14 @@ id: FoodDonkpocketHonk description: The award-winning donk-pocket that won the hearts of clowns and humans alike. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 - - ReagentId: Omnizine + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 + - ReagentId: Omnizine + Quantity: 2 - type: Sprite state: banana @@ -169,13 +180,14 @@ id: FoodDonkpocketHonkWarm description: The award-winning donk-pocket, now warm and toasty. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 - - ReagentId: Omnizine - Quantity: 2 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 10 + - ReagentId: Omnizine + Quantity: 2 - type: entity name: berry-pocket @@ -183,11 +195,12 @@ id: FoodDonkpocketBerry description: A relentlessly sweet donk-pocket first created for use in Operation Iraqi Freedom. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: Sprite state: berry @@ -197,15 +210,16 @@ id: FoodDonkpocketBerryWarm description: A relentlessly sweet donk-pocket, now warm and delicious. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 - - ReagentId: Omnizine - Quantity: 2 - - ReagentId: Glucose - Quantity: 2 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 10 + - ReagentId: Omnizine + Quantity: 2 + - ReagentId: Glucose + Quantity: 2 - type: entity name: gondola-pocket @@ -213,11 +227,12 @@ id: FoodDonkpocketGondola description: The choice to use real gondola meat in the recipe is controversial, to say the least. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: Sprite state: gondola @@ -226,24 +241,26 @@ parent: FoodDonkpocketGondola id: FoodDonkpocketGondolaWarm components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 - - ReagentId: Omnizine - Quantity: 2 - + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 10 + - ReagentId: Omnizine + Quantity: 2 + - type: entity name: dink-pocket parent: FoodDonkpocketBase id: FoodDonkpocketDink description: An off-brand lizard donk-pocket, filled with pickled carrot and wrapped with seaweed. Best eaten cold, or even better, not eaten at all. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 2 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 2 - type: Sprite state: dink diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/donut.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/donut.yml index 5f71cd9431..b3c94091d1 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/donut.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/donut.yml @@ -9,16 +9,17 @@ - type: Food - type: Tag tags: - - Donut + - Donut - type: Sprite sprite: Objects/Consumable/Food/Baked/donut.rsi netsync: false - - type: SolutionContainer - maxVol: 5 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 3 + - type: SolutionContainerManager + solutions: + food: + maxVol: 5 + reagents: + - ReagentId: Nutriment + Quantity: 3 - type: Item sprite: Objects/Consumable/Food/Baked/donut.rsi size: 1 diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/misc.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/misc.yml index 416fcd82ff..00220f274e 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/misc.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/misc.yml @@ -9,12 +9,13 @@ - type: Sprite sprite: Objects/Consumable/Food/Baked/misc.rsi netsync: false - - type: SolutionContainer - maxVol: 5 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + maxVol: 5 + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: Item size: 1 @@ -258,12 +259,13 @@ layers: - state: tray - state: waffles - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 15 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 + reagents: + - ReagentId: Nutriment + Quantity: 15 - type: entity name: soy waffles @@ -317,9 +319,10 @@ components: - type: Sprite state: cannoli - - type: SolutionContainer - maxVol: 15 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 15 + - type: SolutionContainerManager + solutions: + food: + maxVol: 15 + reagents: + - ReagentId: Nutriment + Quantity: 15 diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml index b636863570..925649a8e7 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml @@ -10,12 +10,13 @@ - type: Sprite sprite: Objects/Consumable/Food/Baked/pie.rsi netsync: false - - type: SolutionContainer - maxVol: 15 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 15 + - type: SolutionContainerManager + solutions: + food: + maxVol: 15 + reagents: + - ReagentId: Nutriment + Quantity: 15 - type: SliceableFood count: 4 - type: Tag @@ -30,12 +31,13 @@ components: - type: Food trash: FoodPlateSmall - - type: SolutionContainer - maxVol: 5 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + maxVol: 5 + reagents: + - ReagentId: Nutriment + Quantity: 5 # Pie diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml index 2a5c2fa711..8e3175e013 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml @@ -10,12 +10,13 @@ - type: Sprite sprite: Objects/Consumable/Food/Baked/pizza.rsi netsync: false - - type: SolutionContainer - maxVol: 15 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 15 + - type: SolutionContainerManager + solutions: + food: + maxVol: 15 + reagents: + - ReagentId: Nutriment + Quantity: 15 - type: SliceableFood count: 8 - type: Item @@ -31,12 +32,13 @@ components: - type: Food trash: FoodPlateSmall - - type: SolutionContainer - maxVol: 5 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + maxVol: 5 + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: Item size: 1 - type: Tag diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/bowl.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/bowl.yml index ac2ef427ed..c2fde4d386 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/bowl.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/bowl.yml @@ -6,8 +6,10 @@ id: FoodBowlBig description: A simple bowl, used for soups and salads. components: - - type: SolutionContainer - maxVol: 20 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 - type: Sprite sprite: Objects/Consumable/Food/bowl.rsi state: bowl diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/condiments.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/condiments.yml index 2768df6b6b..6c8a067b1d 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/condiments.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/condiments.yml @@ -12,8 +12,10 @@ - type: Drink openSounds: collection: packetOpenSounds - - type: SolutionContainer - maxVol: 10 + - type: SolutionContainerManager + solutions: + food: + maxVol: 10 - type: SolutionTransfer maxTransferAmount: 10 - type: Sprite @@ -30,11 +32,13 @@ name: Astrotame description: The sweetness of a thousand sugars but none of the calories. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Astrotame - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + maxVol: 10 + reagents: + - ReagentId: Astrotame + Quantity: 10 - type: Sprite layers: - state: packet-trans-2 @@ -55,11 +59,13 @@ name: BBQ sauce description: Hand wipes not included. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: BbqSauce - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + maxVol: 10 + reagents: + - ReagentId: BbqSauce + Quantity: 10 - type: Sprite layers: - state: packet-trans-2 @@ -80,11 +86,13 @@ name: corn oil description: Corn oil. A delicious oil used in cooking. Made from corn. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Cornoil - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + maxVol: 10 + reagents: + - ReagentId: Cornoil + Quantity: 10 - type: Sprite layers: - state: packet-trans-2 @@ -105,11 +113,13 @@ name: coldsauce description: Coldsauce. Leaves the tongue numb in its passage. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Frostoil - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + maxVol: 10 + reagents: + - ReagentId: Frostoil + Quantity: 10 - type: Sprite layers: - state: packet-trans-2 @@ -130,11 +140,13 @@ name: horseradish sauce description: A packet of smelly horseradish sauce. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: HorseradishSauce - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + maxVol: 10 + reagents: + - ReagentId: HorseradishSauce + Quantity: 10 - type: Sprite layers: - state: packet-solid-2 @@ -155,11 +167,13 @@ name: hotsauce description: You can almost TASTE the stomach ulcers now! components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Hotsauce - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + maxVol: 10 + reagents: + - ReagentId: Hotsauce + Quantity: 10 - type: Sprite layers: - state: packet-trans-2 @@ -180,11 +194,12 @@ name: ketchup description: You feel more American already. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Ketchup - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Ketchup + Quantity: 10 - type: Sprite layers: - state: packet-solid-2 @@ -205,11 +220,12 @@ name: black pepper description: Often used to flavor food or make people sneeze. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Blackpepper - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Blackpepper + Quantity: 10 - type: Sprite layers: - state: packet-solid-2 @@ -230,11 +246,12 @@ name: salt description: Salt. From space oceans, presumably. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Salt - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Salt + Quantity: 10 - type: Sprite layers: - state: packet-solid-2 @@ -257,11 +274,12 @@ name: soy sauce description: A salty soy-based flavoring. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Soysauce - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Soysauce + Quantity: 10 - type: Sprite layers: - state: packet-solid-2 @@ -282,11 +300,12 @@ name: sugar description: Tasty spacey sugar! components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Sugar - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Sugar + Quantity: 10 - type: Sprite layers: - state: packet-solid-2 @@ -312,8 +331,10 @@ - type: Drink openSounds: collection: pop - - type: SolutionContainer - maxVol: 30 + - type: SolutionContainerManager + solutions: + food: + maxVol: 30 - type: SolutionTransfer maxTransferAmount: 30 - type: Sprite @@ -332,11 +353,12 @@ name: coldsauce bottle description: Leaves the tongue numb in its passage. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Frostoil - Quantity: 30 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Frostoil + Quantity: 30 - type: Sprite layers: - state: bottle-empty @@ -354,11 +376,12 @@ name: universal enzyme description: Used in cooking various dishes. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Enzyme - Quantity: 30 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Enzyme + Quantity: 30 - type: Sprite layers: - state: bottle-empty @@ -376,11 +399,12 @@ name: hotsauce description: You can almost TASTE the stomach ulcers now! components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Hotsauce - Quantity: 30 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Hotsauce + Quantity: 30 - type: Sprite layers: - state: bottle-empty @@ -398,11 +422,12 @@ name: ketchup description: You feel more American already. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Ketchup - Quantity: 30 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Ketchup + Quantity: 30 - type: Sprite layers: - state: bottle-empty @@ -420,11 +445,12 @@ name: BBQ sauce description: Hand wipes not included. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: BbqSauce - Quantity: 30 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: BbqSauce + Quantity: 30 - type: Sprite layers: - state: bottle-empty @@ -448,8 +474,10 @@ - type: Drink openSounds: collection: pop - - type: SolutionContainer - maxVol: 15 + - type: SolutionContainerManager + solutions: + food: + maxVol: 15 - type: SolutionTransfer maxTransferAmount: 15 - type: Sprite @@ -468,11 +496,12 @@ name: coldsauce bottle description: Leaves the tongue numb in its passage. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Frostoil - Quantity: 15 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Frostoil + Quantity: 15 - type: Sprite layers: - state: bottle-s-empty @@ -490,11 +519,12 @@ name: hotsauce description: You can almost TASTE the stomach ulcers now! components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Hotsauce - Quantity: 15 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Hotsauce + Quantity: 15 - type: Sprite layers: - state: bottle-s-empty @@ -512,11 +542,12 @@ name: ketchup description: You feel more American already. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Ketchup - Quantity: 15 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Ketchup + Quantity: 15 - type: Sprite layers: - state: bottle-s-empty @@ -534,11 +565,12 @@ name: vinegar description: Used in cooking to enhance flavor. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Vinegar - Quantity: 15 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Vinegar + Quantity: 15 - type: Sprite layers: - state: bottle-s-empty diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/tin.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/tin.yml index 7433f49d8c..c8b1508128 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/tin.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/tin.yml @@ -7,8 +7,10 @@ name: tin description: A tin of something, sealed tight. components: - - type: SolutionContainer - maxVol: 20 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 - type: Sprite sprite: Objects/Consumable/Food/snacks.rsi netsync: false @@ -74,11 +76,13 @@ components: - type: Sprite state: peachtin-open - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 15 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 + reagents: + - ReagentId: Nutriment + Quantity: 15 - type: Food trash: FoodTinPeachesTrash @@ -123,11 +127,12 @@ components: - type: Sprite state: peachtinmaint-open - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 15 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 15 - type: Food trash: FoodTinPeachesMaintTrash @@ -172,11 +177,12 @@ components: - type: Sprite state: beans-open - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 15 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 15 - type: Food trash: FoodTinBeansTrash @@ -223,11 +229,12 @@ - type: Sprite sprite: Objects/Consumable/Food/mre.rsi state: tin-open - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 15 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 15 - type: Food trash: FoodTinMRETrash diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/burger.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/burger.yml index 7f4dfafec8..e31ef53fef 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/burger.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/burger.yml @@ -10,12 +10,13 @@ - type: Sprite sprite: Objects/Consumable/Food/burger.rsi netsync: false - - type: SolutionContainer - maxVol: 15 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 15 + - type: SolutionContainerManager + solutions: + food: + maxVol: 15 + reagents: + - ReagentId: Nutriment + Quantity: 15 - type: Item sprite: Objects/Consumable/Food/burger.rsi @@ -153,12 +154,13 @@ id: FoodBurgerBig description: Forget the Big Mac. THIS is the future! components: - - type: SolutionContainer - maxVol: 25 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 25 + - type: SolutionContainerManager + solutions: + food: + maxVol: 25 + reagents: + - ReagentId: Nutriment + Quantity: 25 - type: Sprite state: bigbite # Tastes like bun, silver, magic. @@ -169,12 +171,13 @@ id: FoodBurgerSuper description: This is a mountain of a burger. FOOD! components: - - type: SolutionContainer - maxVol: 40 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 40 + - type: SolutionContainerManager + solutions: + food: + maxVol: 40 + reagents: + - ReagentId: Nutriment + Quantity: 40 - type: Sprite state: superbite # Tastes like bun, diabetes. diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/egg.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/egg.yml index d782afa24f..301215bd73 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/egg.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/egg.yml @@ -17,12 +17,13 @@ sprite: Objects/Consumable/Food/egg.rsi netsync: false size: 1 - - type: SolutionContainer - maxVol: 5 - contents: - reagents: - - ReagentId: Egg - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + maxVol: 5 + reagents: + - ReagentId: Egg + Quantity: 5 - type: DamageOnLand amount: 1 - type: DamageOtherOnHit @@ -77,12 +78,13 @@ netsync: false - type: Item size: 1 - - type: SolutionContainer - maxVol: 1 - contents: - reagents: - - ReagentId: Egg - Quantity: 1 + - type: SolutionContainerManager + solutions: + food: + maxVol: 1 + reagents: + - ReagentId: Egg + Quantity: 1 - type: Tag tags: - Egg diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/frozen.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/frozen.yml index 745d0ec100..3c2b3deeca 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/frozen.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/frozen.yml @@ -9,12 +9,13 @@ - type: Sprite sprite: Objects/Consumable/Food/frozen.rsi netsync: false - - type: SolutionContainer - maxVol: 20 # For sprinkles or something? Idk. - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 # For sprinkles or something? Idk. + reagents: + - ReagentId: Nutriment + Quantity: 10 # Ice-cream diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml index ff6a976368..d3bc2fcc22 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml @@ -25,8 +25,10 @@ - type: Sprite netsync: false sprite: Objects/Consumable/Food/ingredients.rsi - - type: SolutionContainer - maxVol: 50 + - type: SolutionContainerManager + solutions: + food: + maxVol: 50 - type: SolutionTransfer - type: Drink openSounds: @@ -43,11 +45,13 @@ components: - type: Sprite state: flour-big - - type: SolutionContainer - contents: - reagents: - - ReagentId: Flour - Quantity: 50 + - type: SolutionContainerManager + solutions: + food: + maxVol: 50 + reagents: + - ReagentId: Flour + Quantity: 50 - type: DamageOnLand amount: 1 - type: DamageOtherOnHit @@ -78,12 +82,13 @@ components: - type: Sprite state: flour-small - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Flour - Quantity: 20 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 + reagents: + - ReagentId: Flour + Quantity: 20 - type: DamageOnLand amount: 1 - type: DamageOtherOnHit @@ -114,11 +119,12 @@ components: - type: Sprite state: rice-big - - type: SolutionContainer - contents: - reagents: - - ReagentId: Rice - Quantity: 50 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Rice + Quantity: 50 - type: entity parent: ReagentContainerBase @@ -128,12 +134,13 @@ components: - type: Sprite state: rice-small - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Rice - Quantity: 20 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 + reagents: + - ReagentId: Rice + Quantity: 20 - type: entity parent: ReagentContainerBase @@ -143,11 +150,12 @@ components: - type: Sprite state: sugar-big - - type: SolutionContainer - contents: - reagents: - - ReagentId: Sugar - Quantity: 50 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Sugar + Quantity: 50 - type: entity parent: ReagentContainerBase @@ -157,12 +165,13 @@ components: - type: Sprite state: sugar-small - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Sugar - Quantity: 20 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 + reagents: + - ReagentId: Sugar + Quantity: 20 # Milk @@ -174,12 +183,13 @@ components: - type: Sprite state: milk - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Milk - Quantity: 20 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 + reagents: + - ReagentId: Milk + Quantity: 20 - type: entity parent: ReagentContainerBase @@ -189,12 +199,13 @@ components: - type: Sprite state: milk-soy - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: MilkSoy - Quantity: 20 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 + reagents: + - ReagentId: MilkSoy + Quantity: 20 - type: entity parent: ReagentContainerBase @@ -204,12 +215,13 @@ components: - type: Sprite state: milk-oat - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: MilkOat - Quantity: 20 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 + reagents: + - ReagentId: MilkOat + Quantity: 20 # Misc @@ -221,12 +233,13 @@ components: - type: Sprite state: oliveoil - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: OilOlive - Quantity: 20 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 + reagents: + - ReagentId: OilOlive + Quantity: 20 # - type: entity # parent: ReagentContainerBase @@ -236,7 +249,7 @@ # components: # - type: Sprite # state: mayo -# - type: SolutionContainer +# - type: SolutionContainerManager # maxVol: 10 # contents: # reagents: @@ -251,7 +264,7 @@ # components: # - type: Sprite # state: spice -# - type: SolutionContainer +# - type: SolutionContainerManager # maxVol: 10 # contents: # reagents: @@ -270,12 +283,13 @@ sprite: Objects/Consumable/Food/ingredients.rsi netsync: false - type: Food - - type: SolutionContainer - maxVol: 15 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 15 + - type: SolutionContainerManager + solutions: + food: + maxVol: 15 + reagents: + - ReagentId: Nutriment + Quantity: 15 - type: entity name: dough @@ -400,11 +414,12 @@ components: - type: Sprite state: badrecipe - - type: SolutionContainer - maxVol: 5 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 2 - - ReagentId: Toxin - Quantity: 3 + - type: SolutionContainerManager + solutions: + food: + maxVol: 5 + reagents: + - ReagentId: Nutriment + Quantity: 2 + - ReagentId: Toxin + Quantity: 3 diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/meals.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/meals.yml index e6b5d75aa8..5a47264d11 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/meals.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/meals.yml @@ -13,12 +13,13 @@ - type: Sprite sprite: Objects/Consumable/Food/meals.rsi netsync: false - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 20 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 + reagents: + - ReagentId: Nutriment + Quantity: 20 # Meals @@ -100,12 +101,13 @@ components: - type: Sprite state: mint - - type: SolutionContainer - maxVol: 1 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 1 + - type: SolutionContainerManager + solutions: + food: + maxVol: 1 + reagents: + - ReagentId: Nutriment + Quantity: 1 # Tastes like parsnips, salt. - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml index fef5800303..3a9474f268 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml @@ -9,14 +9,15 @@ - type: Sprite sprite: Objects/Consumable/Food/meat.rsi netsync: false - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 - - ReagentId: Toxin - Quantity: 1 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 + reagents: + - ReagentId: Nutriment + Quantity: 5 + - ReagentId: Toxin + Quantity: 1 - type: Item size: 5 @@ -39,14 +40,15 @@ components: - type: Tag tags: - - Raw + - Raw - type: Sprite state: plain - - type: SolutionContainer - contents: - reagents: - - ReagentId: Toxin - Quantity: 1 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Toxin + Quantity: 1 - type: SliceableFood count: 3 slice: FoodMeatCutlet @@ -59,19 +61,20 @@ components: - type: Tag tags: - - Raw + - Raw - type: Sprite netsync: true state: bacon - type: RandomSpriteState spriteStates: - - bacon - - bacon2 - - type: SolutionContainer - contents: - reagents: - - ReagentId: Toxin - Quantity: 1 + - bacon + - bacon2 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Toxin + Quantity: 1 - type: entity name: raw bear meat @@ -81,14 +84,15 @@ components: - type: Tag tags: - - Raw + - Raw - type: Sprite state: bear - - type: SolutionContainer - contents: - reagents: - - ReagentId: Toxin - Quantity: 1 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Toxin + Quantity: 1 - type: SliceableFood count: 3 slice: FoodMeatBearCutlet @@ -101,14 +105,15 @@ components: - type: Tag tags: - - Raw + - Raw - type: Sprite state: bird - - type: SolutionContainer - contents: - reagents: - - ReagentId: Toxin - Quantity: 1 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Toxin + Quantity: 1 - type: SliceableFood count: 3 slice: FoodMeatPenguinCutletSlice @@ -121,14 +126,15 @@ components: - type: Tag tags: - - Raw + - Raw - type: Sprite state: bird - - type: SolutionContainer - contents: - reagents: - - ReagentId: Toxin - Quantity: 1 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Toxin + Quantity: 1 - type: SliceableFood count: 3 slice: FoodMeatChickenCutlet @@ -141,14 +147,15 @@ components: - type: Tag tags: - - Raw + - Raw - type: Sprite state: crab - - type: SolutionContainer - contents: - reagents: - - ReagentId: Toxin - Quantity: 1 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Toxin + Quantity: 1 - type: entity name: raw goliath meat @@ -158,14 +165,15 @@ components: - type: Tag tags: - - Raw + - Raw - type: Sprite state: goliath - - type: SolutionContainer - contents: - reagents: - - ReagentId: Toxin - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Toxin + Quantity: 5 - type: entity name: raw lizard meat @@ -175,14 +183,15 @@ components: - type: Tag tags: - - Raw + - Raw - type: Sprite state: lizard - - type: SolutionContainer - contents: - reagents: - - ReagentId: Toxin - Quantity: 1 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Toxin + Quantity: 1 - type: SliceableFood count: 3 slice: FoodMeatLizardCutlet @@ -204,14 +213,15 @@ components: - type: Tag tags: - - Raw + - Raw - type: Sprite state: rotten - - type: SolutionContainer - contents: - reagents: - - ReagentId: Toxin - Quantity: 20 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Toxin + Quantity: 20 - type: entity name: raw spider meat @@ -221,14 +231,15 @@ components: - type: Tag tags: - - Raw + - Raw - type: Sprite state: spider - - type: SolutionContainer - contents: - reagents: - - ReagentId: Toxin - Quantity: 20 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Toxin + Quantity: 20 - type: SliceableFood count: 3 slice: FoodMeatSpiderCutlet @@ -241,14 +252,15 @@ components: - type: Tag tags: - - Raw + - Raw - type: Sprite state: spiderleg - - type: SolutionContainer - contents: - reagents: - - ReagentId: Toxin - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Toxin + Quantity: 10 - type: entity name: meatwheat clump @@ -258,14 +270,15 @@ components: - type: Tag tags: - - Raw + - Raw - type: Sprite state: clump - - type: SolutionContainer - contents: - reagents: - - ReagentId: Toxin - Quantity: 1 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Toxin + Quantity: 1 - type: entity name: raw xeno meat @@ -275,14 +288,15 @@ components: - type: Tag tags: - - Raw + - Raw - type: Sprite state: xeno - - type: SolutionContainer - contents: - reagents: - - ReagentId: SulfuricAcid - Quantity: 20 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: SulfuricAcid + Quantity: 20 - type: SliceableFood count: 3 slice: FoodMeatXenoCutlet @@ -307,7 +321,7 @@ components: - type: Tag tags: - - Raw + - Raw - type: Sprite state: salami - type: SliceableFood @@ -321,7 +335,7 @@ components: - type: Tag tags: - - Raw + - Raw - type: Sprite state: clown - type: SliceableFood @@ -335,7 +349,7 @@ components: - type: Tag tags: - - Raw + - Raw - type: Sprite state: meatball @@ -349,18 +363,19 @@ components: - type: Tag tags: - - Cooked + - Cooked - type: Food trash: FoodPlateSmall - type: Sprite layers: - - state: plate-meat - - state: plain-cooked - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 20 + - state: plate-meat + - state: plain-cooked + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 20 - type: SliceableFood count: 3 slice: FoodMeatCutletCooked @@ -373,24 +388,25 @@ components: - type: Tag tags: - - Cooked + - Cooked - type: Food trash: FoodPlateSmall - type: Sprite netsync: true layers: - - state: plate-meat - - state: bacon-cooked + - state: plate-meat + - state: bacon-cooked - type: RandomSpriteState spriteLayer: 1 spriteStates: - - bacon-cooked - - bacon2-cooked - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 15 + - bacon-cooked + - bacon2-cooked + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 15 - type: entity name: cooked bear @@ -400,18 +416,19 @@ components: - type: Tag tags: - - Cooked + - Cooked - type: Food trash: FoodPlateSmall - type: Sprite layers: - - state: plate-meat - - state: product-cooked - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 15 + - state: plate-meat + - state: product-cooked + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 15 - type: SliceableFood count: 3 slice: FoodMeatBearCutletCooked @@ -424,18 +441,19 @@ components: - type: Tag tags: - - Cooked + - Cooked - type: Food trash: FoodPlateSmall - type: Sprite layers: - - state: plate-meat - - state: bird-cooked - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 15 + - state: plate-meat + - state: bird-cooked + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 15 - type: SliceableFood count: 3 slice: FoodMeatPenguinCutletCooked @@ -448,18 +466,19 @@ components: - type: Tag tags: - - Cooked + - Cooked - type: Food trash: FoodPlateSmall - type: Sprite layers: - - state: plate-meat - - state: bird-cooked - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 + - state: plate-meat + - state: bird-cooked + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 10 - type: SliceableFood count: 3 slice: FoodMeatChickenCutletCooked @@ -472,7 +491,7 @@ components: - type: Tag tags: - - Cooked + - Cooked - type: Food trash: FoodPlateSmall - type: Sprite @@ -480,13 +499,14 @@ state: chicken-fried - type: RandomSpriteState spriteStates: - - chicken-fried - - chicken2-fried - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 15 + - chicken-fried + - chicken2-fried + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 15 - type: entity name: cooked crab @@ -496,18 +516,19 @@ components: - type: Tag tags: - - Cooked + - Cooked - type: Food trash: FoodPlateSmall - type: Sprite layers: - - state: plate-meat - - state: crab-cooked - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 15 + - state: plate-meat + - state: crab-cooked + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 15 - type: entity name: goliath steak @@ -517,18 +538,19 @@ components: - type: Tag tags: - - Cooked + - Cooked - type: Food trash: FoodPlateSmall - type: Sprite layers: - - state: plate-meat - - state: goliath-cooked - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 15 + - state: plate-meat + - state: goliath-cooked + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 15 - type: entity name: lizard steak @@ -538,18 +560,19 @@ components: - type: Tag tags: - - Cooked + - Cooked - type: Food trash: FoodPlateSmall - type: Sprite layers: - - state: plate-meat - - state: lizard-cooked - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 + - state: plate-meat + - state: lizard-cooked + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 10 - type: SliceableFood count: 3 slice: FoodMeatLizardCutletCooked @@ -562,18 +585,19 @@ components: - type: Tag tags: - - Cooked + - Cooked - type: Food trash: FoodPlateSmall - type: Sprite layers: - - state: plate-meat - - state: spiderleg-cooked - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 15 + - state: plate-meat + - state: spiderleg-cooked + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 15 - type: entity name: meatball @@ -583,14 +607,15 @@ components: - type: Tag tags: - - Cooked + - Cooked - type: Sprite state: meatball-cooked - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 10 # Cutlets @@ -604,15 +629,16 @@ components: - type: Tag tags: - - Raw - - Cutlet + - Raw + - Cutlet - type: Sprite state: cutlet - - type: SolutionContainer - contents: - reagents: - - ReagentId: Toxin - Quantity: 1 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Toxin + Quantity: 1 - type: entity name: raw bear cutlet @@ -622,18 +648,19 @@ components: - type: Tag tags: - - Raw - - Cutlet + - Raw + - Cutlet - type: Sprite layers: - - state: cutlet - - state: cutlet-alpha - color: brown - - type: SolutionContainer - contents: - reagents: - - ReagentId: Toxin - Quantity: 1 + - state: cutlet + - state: cutlet-alpha + color: brown + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Toxin + Quantity: 1 - type: entity name: raw penguin cutlet @@ -643,16 +670,17 @@ components: - type: Tag tags: - - Raw - - Cutlet + - Raw + - Cutlet - type: Sprite state: cutlet color: white - - type: SolutionContainer - contents: - reagents: - - ReagentId: Toxin - Quantity: 1 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Toxin + Quantity: 1 - type: entity name: raw chicken cutlet @@ -662,16 +690,17 @@ components: - type: Tag tags: - - Raw - - Cutlet + - Raw + - Cutlet - type: Sprite state: cutlet color: white - - type: SolutionContainer - contents: - reagents: - - ReagentId: Toxin - Quantity: 1 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Toxin + Quantity: 1 - type: entity name: raw lizard cutlet @@ -681,19 +710,20 @@ components: - type: Tag tags: - - Raw - - Cutlet + - Raw + - Cutlet - type: Sprite layers: - - state: cutlet - color: green - - state: cutlet-alpha - color: pink - - type: SolutionContainer - contents: - reagents: - - ReagentId: Toxin - Quantity: 1 + - state: cutlet + color: green + - state: cutlet-alpha + color: pink + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Toxin + Quantity: 1 - type: entity name: raw spider cutlet @@ -703,19 +733,20 @@ components: - type: Tag tags: - - Raw - - Cutlet + - Raw + - Cutlet - type: Sprite layers: - - state: cutlet - color: black - - state: cutlet-alpha - color: red - - type: SolutionContainer - contents: - reagents: - - ReagentId: Toxin - Quantity: 20 + - state: cutlet + color: black + - state: cutlet-alpha + color: red + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Toxin + Quantity: 20 - type: entity name: raw xeno cutlet @@ -725,19 +756,20 @@ components: - type: Tag tags: - - Raw - - Cutlet + - Raw + - Cutlet - type: Sprite layers: - - state: cutlet - color: black - - state: cutlet-alpha - color: green - - type: SolutionContainer - contents: - reagents: - - ReagentId: SulfuricAcid - Quantity: 20 + - state: cutlet + color: black + - state: cutlet-alpha + color: green + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: SulfuricAcid + Quantity: 20 - type: entity name: raw killer tomato cutlet @@ -747,7 +779,7 @@ components: - type: Tag tags: - - Cutlet + - Cutlet - type: Sprite state: salami-slice color: red @@ -760,15 +792,16 @@ components: - type: Tag tags: - - Raw - - Cutlet + - Raw + - Cutlet - type: Sprite state: salami-slice - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 2 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 2 # Cooked @@ -780,15 +813,16 @@ components: - type: Tag tags: - - Cooked - - Cutlet + - Cooked + - Cutlet - type: Sprite state: cutlet-cooked - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 10 - type: entity name: bear cutlet @@ -798,18 +832,19 @@ components: - type: Tag tags: - - Cooked - - Cutlet + - Cooked + - Cutlet - type: Sprite layers: - - state: cutlet-cooked - - state: cutlet-alpha - color: brown - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 + - state: cutlet-cooked + - state: cutlet-alpha + color: brown + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 10 - type: entity name: penguin cutlet @@ -819,16 +854,17 @@ components: - type: Tag tags: - - Cooked - - Cutlet + - Cooked + - Cutlet - type: Sprite state: cutlet-cooked color: white - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 10 - type: entity name: chicken cutlet @@ -838,16 +874,18 @@ components: - type: Tag tags: - - Cooked - - Cutlet + - Cooked + - Cutlet - type: Sprite state: cutlet-cooked color: white - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 10 + - type: entity name: lizard cutlet @@ -857,17 +895,18 @@ components: - type: Tag tags: - - Cooked - - Cutlet + - Cooked + - Cutlet - type: Sprite layers: - - state: cutlet-cooked - color: green - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 + - state: cutlet-cooked + color: green + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 10 - type: entity name: spider cutlet @@ -877,19 +916,20 @@ components: - type: Tag tags: - - Cooked - - Cutlet + - Cooked + - Cutlet - type: Sprite layers: - - state: cutlet-cooked - color: black - - state: cutlet-alpha - color: red - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - state: cutlet-cooked + color: black + - state: cutlet-alpha + color: red + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: entity name: xeno cutlet @@ -899,16 +939,17 @@ components: - type: Tag tags: - - Cooked - - Cutlet + - Cooked + - Cutlet - type: Sprite layers: - - state: cutlet-cooked - color: black - - state: cutlet-alpha - color: green - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - state: cutlet-cooked + color: black + - state: cutlet-alpha + color: green + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/noodles.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/noodles.yml index 02ee16cf0c..d785cca0b0 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/noodles.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/noodles.yml @@ -11,12 +11,13 @@ - type: Sprite sprite: Objects/Consumable/Food/noodles.rsi netsync: false - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 20 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 + reagents: + - ReagentId: Nutriment + Quantity: 20 # Noodles diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml index a1f5c80362..c8390b81bd 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml @@ -3,15 +3,13 @@ id: ProduceBase abstract: true components: - - type: SolutionContainer - caps: None + - type: SolutionContainerManager - type: Sprite netsync: false state: produce - type: Produce - - type: Tag - tags: - - Grindable + - type: Extractable + extractableSolution: food - type: entity name: wheat bushel @@ -21,12 +19,12 @@ components: - type: Sprite sprite: Objects/Specific/Hydroponics/wheat.rsi - - type: SolutionContainer - caps: None - contents: - reagents: - - ReagentId: Flour - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Flour + Quantity: 10 - type: Produce seed: wheat @@ -38,15 +36,15 @@ components: - type: Sprite sprite: Objects/Specific/Hydroponics/oat.rsi - - type: SolutionContainer - caps: None - contents: - reagents: - - ReagentId: Oats - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Oats + Quantity: 10 - type: Produce seed: oat - - type: Juiceable + - type: Extractable result: reagents: - ReagentId: MilkOat @@ -60,12 +58,12 @@ components: - type: Sprite sprite: Objects/Specific/Hydroponics/sugarcane.rsi - - type: SolutionContainer - caps: None - contents: - reagents: - - ReagentId: Glucose - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Glucose + Quantity: 10 - type: Produce seed: sugarcane @@ -77,8 +75,7 @@ components: - type: Sprite sprite: Objects/Specific/Hydroponics/towercap.rsi - - type: SolutionContainer - caps: None + - type: SolutionContainerManager - type: MeleeWeapon - type: Produce seed: towercap @@ -92,16 +89,17 @@ components: - type: Food trash: TrashBananaPeel - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: Sprite sprite: Objects/Specific/Hydroponics/banana.rsi - type: Produce seed: banana - - type: Juiceable + - type: Extractable result: reagents: - ReagentId: JuiceBanana @@ -149,16 +147,17 @@ tags: - Carrot - type: Food - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: Sprite sprite: Objects/Specific/Hydroponics/carrot.rsi - type: Produce seed: carrots - - type: Juiceable + - type: Extractable result: reagents: - ReagentId: JuiceCarrot @@ -171,16 +170,17 @@ description: When life gives you lemons, be grateful they aren't limes. components: - type: Food - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: Sprite sprite: Objects/Specific/Hydroponics/lemon.rsi - type: Produce seed: lemon - - type: Juiceable + - type: Extractable result: reagents: - ReagentId: JuiceLime @@ -193,16 +193,17 @@ description: Mmm, tropical. components: - type: Food - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: Sprite sprite: Objects/Specific/Hydroponics/pineapple.rsi - type: Produce seed: pineapple - - type: Juiceable + - type: Extractable result: reagents: - ReagentId: JuicePineapple @@ -218,11 +219,12 @@ description: The space Irish starved to death after their potato crops died. Sadly they were unable to fish for space carp due to it being the queen's space. Bringing this up to any space IRA member will drive them insane with anger. components: - type: Food - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: Sprite sprite: Objects/Specific/Hydroponics/potato.rsi - type: Produce @@ -235,16 +237,17 @@ description: I say to-mah-to, you say tom-mae-to. components: - type: Food - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: Sprite sprite: Objects/Specific/Hydroponics/tomato.rsi - type: Produce seed: tomato - - type: Juiceable + - type: Extractable result: reagents: - ReagentId: JuiceTomato @@ -290,11 +293,12 @@ description: Maybe there's a chicken inside? components: - type: Food - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: Sprite sprite: Objects/Specific/Hydroponics/eggplant.rsi - type: Produce @@ -307,16 +311,17 @@ description: It's a little piece of Eden. components: - type: Food - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: Sprite sprite: Objects/Specific/Hydroponics/apple.rsi - type: Produce seed: apple - - type: Juiceable + - type: Extractable result: reagents: - ReagentId: JuiceApple @@ -330,11 +335,12 @@ components: - type: Food trash: FoodCornTrash - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: Sprite sprite: Objects/Specific/Hydroponics/corn.rsi - type: Produce @@ -360,11 +366,12 @@ description: Nothing to cry over. components: - type: Food - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: Sprite sprite: Objects/Specific/Hydroponics/onion.rsi - type: Produce @@ -380,11 +387,12 @@ description: Purple despite the name. components: - type: Food - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: Sprite sprite: Objects/Specific/Hydroponics/onion_red.rsi - type: Produce @@ -400,11 +408,12 @@ description: "Cantharellus Cibarius: These jolly yellow little shrooms sure look tasty!" components: - type: Food - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: Sprite sprite: Objects/Specific/Hydroponics/chanterelle.rsi - type: Produce @@ -418,17 +427,17 @@ abstract: true components: - type: Food - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 2 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 2 - type: Sprite netsync: false state: slice - - type: Tag - tags: - - Grindable + - type: Extractable + extractableSolution: food - type: entity name: pineapple slice @@ -438,7 +447,7 @@ components: - type: Sprite sprite: Objects/Specific/Hydroponics/pineapple.rsi - - type: Juiceable + - type: Extractable result: reagents: - ReagentId: JuicePineapple diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/skewer.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/skewer.yml index 3de439686d..3f45d64e48 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/skewer.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/skewer.yml @@ -10,12 +10,13 @@ - type: Sprite sprite: Objects/Consumable/Food/skewer.rsi netsync: false - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 20 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 + reagents: + - ReagentId: Nutriment + Quantity: 20 - type: Item size: 5 @@ -103,11 +104,12 @@ layers: - state: skewer - state: skewer-rat1 - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: entity name: double rat kebab @@ -120,11 +122,12 @@ - state: skewer - state: skewer-rat1 - state: skewer-rat2 - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 10 - type: entity name: fiesta kebab diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/snacks.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/snacks.yml index 47c990e5ce..c9002d1c52 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/snacks.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/snacks.yml @@ -9,12 +9,13 @@ - type: Sprite sprite: Objects/Consumable/Food/snacks.rsi netsync: false - - type: SolutionContainer - maxVol: 30 # Room for extra condiments - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 + - type: SolutionContainerManager + solutions: + food: + maxVol: 30 # Room for extra condiments + reagents: + - ReagentId: Nutriment + Quantity: 10 - type: Item sprite: Objects/Consumable/Food/snacks.rsi HeldPrefix: packet diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml index 14a0e8e8ae..0ab9718224 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml @@ -5,12 +5,13 @@ components: - type: Food trash: FoodBowlBig - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Nutriment - Quantity: 20 + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 + reagents: + - ReagentId: Nutriment + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Food/bowl.rsi netsync: false @@ -403,12 +404,13 @@ id: FoodSoupMiso description: Salty, fishy soup, best had with ramen. components: + - type: Food + trash: FoodBowlFancy - type: Sprite layers: - state: bowl2 - state: milo - - type: SolutionContainer - trash: FoodBowlFancy + - type: SolutionContainerManager # Tastes like miso. - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Consumable/drinks.yml b/Resources/Prototypes/Entities/Objects/Consumable/drinks.yml index 61c090a64f..c504b18983 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/drinks.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/drinks.yml @@ -6,8 +6,10 @@ id: DrinkBase abstract: true components: - - type: SolutionContainer - maxVol: 50 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 50 - type: SolutionTransfer canChangeTransferAmount: true - type: Drink @@ -58,8 +60,12 @@ components: - type: Sprite sprite: Objects/Consumable/Drinks/glass_clear.rsi - - type: SolutionContainer - maxVol: 50 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 50 + - type: FitsInDispenser + solution: drink - type: SolutionTransfer canChangeTransferAmount: true - type: TransformableContainer @@ -74,12 +80,13 @@ name: ale description: A dark alchoholic beverage made with malted barley and yeast. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Ale - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: Ale + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/aleglass.rsi @@ -89,12 +96,13 @@ name: anti-freeze description: Ultimate refreshment. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Antifreeze - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: Antifreeze + Quantity: 20 - type: Drink - type: Sprite sprite: Objects/Consumable/Drinks/antifreeze.rsi @@ -105,12 +113,13 @@ name: atomic bomb glass description: Nuclear proliferation never tasted so good. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: AtomicBomb - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: AtomicBomb + Quantity: 20 - type: Drink - type: Sprite sprite: Objects/Consumable/Drinks/atomicbombglass.rsi @@ -121,12 +130,13 @@ name: b-52 glass description: Coffee, Irish Cream, and cognac. You will get bombed. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: B52 - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: B52 + Quantity: 20 - type: Drink - type: Sprite sprite: Objects/Consumable/Drinks/b52glass.rsi @@ -157,12 +167,13 @@ name: beer # 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. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Beer - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: Beer + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/beer.rsi @@ -172,12 +183,13 @@ name: beer glass description: An alcoholic beverage made from malted grains, hops, yeast, and water. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Beer - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: Beer + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/beerglass.rsi @@ -188,10 +200,11 @@ description: A delicious blend of several different kinds of berries. components: - type: Drink - - type: SolutionContainer - maxVol: 20 - contents: - reagents: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: - ReagentId: JuiceBerry Quantity: 20 - type: Sprite @@ -265,10 +278,11 @@ description: Has a uniquely sweet flavour of concentrated carrots. components: - type: Drink - - type: SolutionContainer - maxVol: 20 - contents: - reagents: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: - ReagentId: JuiceCarrot Quantity: 20 - type: Sprite @@ -303,12 +317,13 @@ name: coffee description: Coffee is a brewed drink prepared from roasted seeds, commonly called coffee beans, of the coffee plant. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Coffee - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: Coffee + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/coffee.rsi @@ -319,12 +334,13 @@ name: cognac glass description: A sweet and strongly alchoholic drink, made after numerous distillations and years of maturing. Classy as fornication. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Cognac - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: Cognac + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/cognacglass.rsi @@ -335,12 +351,13 @@ name: space cola bottle description: Cola. in space components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Cola - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: Cola + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/colabottle.rsi @@ -351,12 +368,13 @@ name: cream description: Dairy product composed of the higher-fat layer skimmed from the top of milk before homogenization. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Cream - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: Cream + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/cream.rsi @@ -367,12 +385,13 @@ name: cuba libre glass description: Rum, mixed with cola. Viva la revolucion. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: CubaLibre - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: CubaLibre + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/cubalibreglass.rsi @@ -582,10 +601,11 @@ description: The sweet-sour juice of limes. components: - type: Drink - - type: SolutionContainer - maxVol: 20 - contents: - reagents: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: - ReagentId: JuiceLime Quantity: 20 - type: Sprite @@ -599,10 +619,11 @@ description: Liquid extract of the orange tree fruit, produced by squeezing or reaming oranges. components: - type: Drink - - type: SolutionContainer - maxVol: 20 - contents: - reagents: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: - ReagentId: JuiceOrange Quantity: 20 - type: Sprite @@ -615,10 +636,11 @@ description: Used to make lemonade, soft drinks, and cocktails. components: - type: Drink - - type: SolutionContainer - maxVol: 20 - contents: - reagents: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: - ReagentId: JuiceLemon Quantity: 20 - type: Sprite @@ -632,10 +654,11 @@ description: Juice made from tomatoes, usually used as a beverage, either plain or in cocktails components: - type: Drink - - type: SolutionContainer - maxVol: 20 - contents: - reagents: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: - ReagentId: JuiceTomato Quantity: 20 - type: Sprite @@ -648,12 +671,13 @@ name: milk description: An opaque white liquid produced by the mammary glands of mammals. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Milk - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: Milk + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/glass_white.rsi @@ -675,10 +699,11 @@ description: The juice is often sold in stores or fermented and made into wine, brandy, or vinegar. components: - type: Drink - - type: SolutionContainer - maxVol: 20 - contents: - reagents: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: - ReagentId: JuiceGrape Quantity: 20 - type: Sprite @@ -791,12 +816,13 @@ name: ice glass description: Water frozen into a solid state. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Ice - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: Ice + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/iceglass.rsi @@ -807,12 +833,13 @@ name: irish car bomb description: Mmm, tastes like chocolate cake... components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: IrishCarBomb - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: IrishCarBomb + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/irishcarbomb.rsi @@ -823,12 +850,13 @@ name: irish coffee glass description: Coffee, and alcohol. More fun than a Mimosa to drink in the morning. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: IrishCoffee - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: IrishCoffee + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/irishcoffeeglass.rsi @@ -839,12 +867,13 @@ name: irish cream glass description: Whiskey-imbued cream, what else would you expect from the Irish. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: IrishCream - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: IrishCream + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/irishcreamglass.rsi @@ -888,12 +917,13 @@ name: kahlua glass description: A widely known, Mexican coffee-flavoured liqueur. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Kahlua - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: Kahlua + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/kahluaglass.rsi @@ -949,10 +979,11 @@ description: Used to make lemonade, soft drinks, and cocktails. components: - type: Drink - - type: SolutionContainer - maxVol: 20 - contents: - reagents: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: - ReagentId: JuiceLemon Quantity: 20 - type: Sprite @@ -966,10 +997,11 @@ description: A tangy substance made of lime and lemon. components: - type: Drink - - type: SolutionContainer - maxVol: 20 - contents: - reagents: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: - ReagentId: JuiceLemon Quantity: 10 - ReagentId: JuiceLime @@ -985,10 +1017,11 @@ description: The sweet-sour juice of limes. components: - type: Drink - - type: SolutionContainer - maxVol: 20 - contents: - reagents: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: - ReagentId: JuiceLime Quantity: 20 - type: Sprite @@ -1034,12 +1067,13 @@ name: the manly dorf glass description: Beer and Ale, brought together in a delicious mix. Intended for true men only. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: ManlyDorf - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: ManlyDorf + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/manlydorfglass.rsi @@ -1083,12 +1117,13 @@ name: milk jug description: An opaque white liquid produced by the mammary glands of mammals. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Milk - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: Milk + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/milk.rsi @@ -1166,10 +1201,11 @@ description: Liquid extract of the orange tree fruit, produced by squeezing or reaming oranges. components: - type: Drink - - type: SolutionContainer - maxVol: 20 - contents: - reagents: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: - ReagentId: JuiceOrange Quantity: 20 - type: Sprite @@ -1194,10 +1230,11 @@ description: A tasty juice blended from various kinds of very deadly and toxic berries. components: - type: Drink - - type: SolutionContainer - maxVol: 20 - contents: - reagents: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: - ReagentId: JuiceBerryPoison Quantity: 20 - type: Sprite @@ -1509,12 +1546,13 @@ name: syndicate bomb description: Tastes like terrorism! components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: SyndicateBomb - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: SyndicateBomb + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/syndicatebomb.rsi @@ -1525,12 +1563,13 @@ name: tea glass description: Tasty black tea. Contains caffeine. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Tea - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: Tea + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/teaglass.rsi @@ -1541,12 +1580,13 @@ name: teapot # short and stout description: An elegant teapot. It simply oozes class. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Tea - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: Tea + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/teapot.rsi @@ -1602,10 +1642,11 @@ description: Juice made from tomatoes, usually used as a beverage, either plain or in cocktails. components: - type: Drink - - type: SolutionContainer - maxVol: 20 - contents: - reagents: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: - ReagentId: JuiceTomato Quantity: 20 - type: Sprite @@ -1696,10 +1737,11 @@ description: Delicious juice made from watermelon. components: - type: Drink - - type: SolutionContainer - maxVol: 20 - contents: - reagents: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: - ReagentId: JuiceWatermelon Quantity: 20 - type: Sprite @@ -1723,12 +1765,13 @@ name: whiskey cola glass description: Whiskey, mixed with cola. Surprisingly refreshing. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: WhiskeyCola - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: WhiskeyCola + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/whiskeycolaglass.rsi @@ -1739,12 +1782,13 @@ name: special blend whiskey glass description: Just when you thought regular station whiskey was good... This silky, amber goodness has to come along and ruin everything. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Whiskey - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: Whiskey + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/whiskeyglass.rsi @@ -1788,11 +1832,12 @@ name: wine glass description: An premium alchoholic beverage made from distilled grape juice. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Wine - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: Wine + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/wineglass.rsi diff --git a/Resources/Prototypes/Entities/Objects/Consumable/drinks_bottles.yml b/Resources/Prototypes/Entities/Objects/Consumable/drinks_bottles.yml index 9769567b1f..dd11d2b0cf 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/drinks_bottles.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/drinks_bottles.yml @@ -6,8 +6,10 @@ - type: Drink openSounds: collection: bottleOpenSounds - - type: SolutionContainer - maxVol: 100 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 100 - type: SolutionTransfer canChangeTransferAmount: true - type: UserInterface @@ -49,11 +51,13 @@ - type: Drink - type: Sprite sprite: Objects/Consumable/Drinks/absinthebottle.rsi - - type: SolutionContainer - contents: - reagents: - - ReagentId: Absinthe - Quantity: 100 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 100 + reagents: + - ReagentId: Absinthe + Quantity: 100 - type: entity parent: DrinkBottleBaseFull @@ -72,12 +76,13 @@ name: Magm-Ale description: A true dorf's drink of choice. components: - - type: SolutionContainer - maxVol: 100 - contents: - reagents: - - ReagentId: Ale - Quantity: 100 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 100 + reagents: + - ReagentId: Ale + Quantity: 100 - type: Sprite sprite: Objects/Consumable/Drinks/alebottle.rsi @@ -99,11 +104,13 @@ name: cognac bottle description: A sweet and strongly alchoholic drink, made after numerous distillations and years of maturing. You might as well not scream 'SHITCURITY' this time. components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Cognac - Quantity: 100 + - type: SolutionContainerManager + solutions: + drink: + reagents: + - ReagentId: Cognac + Quantity: 100 + maxVol: 100 - type: Sprite sprite: Objects/Consumable/Drinks/cognacbottle.rsi @@ -117,11 +124,13 @@ - type: Drink - type: Sprite sprite: Objects/Consumable/Drinks/ginbottle.rsi - - type: SolutionContainer - contents: - reagents: + - type: SolutionContainerManager + solutions: + drink: + reagents: - ReagentId: Gin Quantity: 100 + maxVol: 100 - type: entity parent: DrinkBottleBaseFull @@ -140,12 +149,13 @@ name: kahlua bottle description: A widely known, Mexican coffee-flavoured liqueur. In production since 1936, HONK. components: - - type: SolutionContainer - maxVol: 80 - contents: - reagents: - - ReagentId: Water - Quantity: 80 + - type: SolutionContainerManager + solutions: + drink: + reagents: + - ReagentId: Water + Quantity: 80 + maxVol: 80 - type: Sprite sprite: Objects/Consumable/Drinks/kahluabottle.rsi @@ -211,12 +221,13 @@ name: vodka bottle description: Aah, vodka. Prime choice of drink AND fuel by Russians worldwide. components: - - type: SolutionContainer - maxVol: 80 - contents: - reagents: - - ReagentId: Vodka - Quantity: 80 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 80 + reagents: + - ReagentId: Vodka + Quantity: 80 - type: Sprite sprite: Objects/Consumable/Drinks/vodkabottle.rsi @@ -227,12 +238,13 @@ name: uncle git's special reserve description: A premium single-malt whiskey, gently matured inside the tunnels of a nuclear shelter. TUNNEL WHISKEY RULES. components: - - type: SolutionContainer - maxVol: 80 - contents: - reagents: - - ReagentId: Whiskey - Quantity: 80 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 80 + reagents: + - ReagentId: Whiskey + Quantity: 80 - type: Sprite sprite: Objects/Consumable/Drinks/whiskeybottle.rsi @@ -243,11 +255,12 @@ name: doublebearded bearded special wine bottle description: A faint aura of unease and asspainery surrounds the bottle. components: - - type: SolutionContainer - maxVol: 80 - contents: - reagents: - - ReagentId: Wine - Quantity: 80 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 80 + reagents: + - ReagentId: Wine + Quantity: 80 - type: Sprite sprite: Objects/Consumable/Drinks/winebottle.rsi diff --git a/Resources/Prototypes/Entities/Objects/Consumable/drinks_cans.yml b/Resources/Prototypes/Entities/Objects/Consumable/drinks_cans.yml index 72ee575de3..670e74b7f2 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/drinks_cans.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/drinks_cans.yml @@ -7,13 +7,13 @@ openSounds: collection: canOpenSounds pressurized: true - - type: SolutionContainer - maxVol: 20 - caps: None - contents: - reagents: - - ReagentId: Cola - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + reagents: + - ReagentId: Cola + Quantity: 20 + maxVol: 20 - type: SolutionTransfer canChangeTransferAmount: true maxTransferAmount: 10 @@ -35,7 +35,6 @@ - type: Tag tags: - Cola - - type: SolutionContainer - type: Sprite sprite: Objects/Consumable/Drinks/cola.rsi @@ -120,13 +119,13 @@ name: thirteen loko can description: The MBO has advised crew members that consumption of Thirteen Loko may result in seizures, blindness, drunkeness, or even death. Please Drink Responsibly. components: - - type: SolutionContainer - maxVol: 20 - caps: None - contents: - reagents: - - ReagentId: ThirteenLoko - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 20 + reagents: + - ReagentId: ThirteenLoko + Quantity: 20 - type: Sprite sprite: Objects/Consumable/Drinks/thirteen_loko.rsi diff --git a/Resources/Prototypes/Entities/Objects/Consumable/drinks_cups.yml b/Resources/Prototypes/Entities/Objects/Consumable/drinks_cups.yml index 1803334384..34f246e8c3 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/drinks_cups.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/drinks_cups.yml @@ -5,8 +5,10 @@ name: base cup abstract: true components: - - type: SolutionContainer - maxVol: 20 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 10 - type: SolutionTransfer canChangeTransferAmount: true maxTransferAmount: 10 @@ -26,8 +28,6 @@ name: golden cup description: A golden cup. components: - - type: SolutionContainer - maxVol: 10 - type: Sprite sprite: Objects/Consumable/Drinks/golden_cup.rsi @@ -37,8 +37,6 @@ name: mug description: A plain white mug. components: - - type: SolutionContainer - maxVol: 10 - type: Sprite sprite: Objects/Consumable/Drinks/mug.rsi state: icon-3 @@ -49,8 +47,6 @@ name: mug black description: A sleek black mug. components: - - type: SolutionContainer - maxVol: 10 - type: Sprite sprite: Objects/Consumable/Drinks/mug_black.rsi state: icon-3 @@ -61,8 +57,6 @@ name: mug blue description: A blue and black mug. components: - - type: SolutionContainer - maxVol: 10 - type: Sprite sprite: Objects/Consumable/Drinks/mug_blue.rsi state: icon-3 @@ -73,8 +67,6 @@ name: mug green description: A pale green and pink mug. components: - - type: SolutionContainer - maxVol: 10 - type: Sprite sprite: Objects/Consumable/Drinks/mug_green.rsi state: icon-3 @@ -85,8 +77,6 @@ name: mug heart description: A white mug, it prominently features a red heart. components: - - type: SolutionContainer - maxVol: 10 - type: Sprite sprite: Objects/Consumable/Drinks/mug_heart.rsi state: icon-3 @@ -97,8 +87,6 @@ name: mug metal description: A metal mug. You're not sure which metal. components: - - type: SolutionContainer - maxVol: 10 - type: Sprite sprite: Objects/Consumable/Drinks/mug_metal.rsi state: icon-3 @@ -109,8 +97,6 @@ name: mug moebius description: A mug with a Moebius Laboratories logo on it. Not even your morning coffee is safe from corporate advertising. components: - - type: SolutionContainer - maxVol: 10 - type: Sprite sprite: Objects/Consumable/Drinks/mug_moebius.rsi state: icon-3 @@ -121,8 +107,6 @@ name: "#1 mug" description: "A white mug, it prominently features a #1." components: - - type: SolutionContainer - maxVol: 10 - type: Sprite sprite: Objects/Consumable/Drinks/mug_one.rsi state: icon-3 @@ -133,8 +117,6 @@ name: mug rainbow description: A rainbow mug. The colors are almost as blinding as a welder. components: - - type: SolutionContainer - maxVol: 10 - type: Sprite sprite: Objects/Consumable/Drinks/mug_rainbow.rsi state: icon-3 @@ -145,8 +127,6 @@ name: mug red description: A red and black mug. components: - - type: SolutionContainer - maxVol: 10 - type: Sprite sprite: Objects/Consumable/Drinks/mug_red.rsi state: icon-3 @@ -167,12 +147,13 @@ name: Coffee description: Coffee is a brewed drink prepared from roasted seeds, commonly called coffee beans, of the coffee plant. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Coffee - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + reagents: + - ReagentId: Coffee + Quantity: 20 + maxVol: 20 - type: Sprite sprite: Objects/Consumable/Drinks/hot_coffee.rsi @@ -182,12 +163,13 @@ name: Teacup description: A plain white porcelain teacup. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Tea - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + reagents: + - ReagentId: Tea + Quantity: 20 + maxVol: 20 - type: Sprite sprite: Objects/Consumable/Drinks/teacup.rsi state: icon-1 @@ -198,12 +180,13 @@ name: Lean description: Bro Drake has such a cool fade bro Drake is the best bro c'mon bro. components: - - type: SolutionContainer - maxVol: 20 - contents: - reagents: - - ReagentId: Lean - Quantity: 20 + - type: SolutionContainerManager + solutions: + drink: + reagents: + - ReagentId: Lean + Quantity: 20 + maxVol: 20 - type: Sprite sprite: Objects/Consumable/Drinks/lean.rsi state: icon diff --git a/Resources/Prototypes/Entities/Objects/Consumable/drinks_solutioncontainerexample.yml b/Resources/Prototypes/Entities/Objects/Consumable/drinks_solutioncontainerexample.yml index 65d7f74cc8..edc46d5860 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/drinks_solutioncontainerexample.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/drinks_solutioncontainerexample.yml @@ -8,10 +8,11 @@ name: solution container vis cut-out description: A stainless steel insulated pitcher. Everyone's best friend in the morning. components: - - type: SolutionContainer - maxVol: 30 - contents: - reagents: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: - ReagentId: JuiceWatermelon Quantity: 30 - type: Sprite @@ -37,10 +38,11 @@ name: solution container vis cut-not description: A stainless steel insulated pitcher. Everyone's best friend in the morning. components: - - type: SolutionContainer - maxVol: 30 - contents: - reagents: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: - ReagentId: JuiceWatermelon Quantity: 30 - type: Sprite diff --git a/Resources/Prototypes/Entities/Objects/Consumable/trash_drinks.yml b/Resources/Prototypes/Entities/Objects/Consumable/trash_drinks.yml index eb10d855f8..5b9941c1cb 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/trash_drinks.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/trash_drinks.yml @@ -1,15 +1,17 @@ # These can still be used as containers - type: entity name: base empty bottle + id: DrinkBottleBaseEmpty parent: BaseItem abstract: true - id: DrinkBottleBaseEmpty description: That's an empty bottle. components: - type: Sprite state: icon - - type: SolutionContainer - maxVol: 10 + - type: SolutionContainerManager + solutions: + drink: + maxVol: 10 - type: SolutionTransfer canChangeTransferAmount: true maxTransferAmount: 5 diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml index 6a4d77d8aa..cb4ef340d9 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml @@ -92,15 +92,16 @@ - plasma - plasma_2 - plasma_3 - - type: SolutionContainer - caps: 1 - contents: - reagents: - - ReagentId: Plasma - Quantity: 10 + - type: Extractable + extractableSolution: plasma + - type: SolutionContainerManager + solutions: + plasma: + reagents: + - ReagentId: Plasma + Quantity: 10 - type: Tag tags: - - Grindable - Sheet - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml index 7eb9dd8b68..dfcb771a5a 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml @@ -8,18 +8,22 @@ netsync: false sprite: Objects/Misc/fire_extinguisher.rsi layers: - - state: fire_extinguisher_closed - map: [ "enum.SprayVisualLayers.Base" ] + - state: fire_extinguisher_closed + map: [ "enum.SprayVisualLayers.Base" ] - type: Item sprite: Objects/Misc/fire_extinguisher.rsi size: 10 - - type: SolutionContainer - maxVol: 100 - caps: Refillable, Drainable - contents: - reagents: + - type: SolutionContainerManager + solutions: + fireExtinguisher: + maxVol: 100 + reagents: - ReagentId: Water Quantity: 100 + - type: RefillableSolution + solution: fireExtinguisher + - type: DrainableSolution + solution: fireExtinguisher - type: ItemCooldown - type: Spray spraySound: diff --git a/Resources/Prototypes/Entities/Objects/Power/powercells.yml b/Resources/Prototypes/Entities/Objects/Power/powercells.yml index 31aba331f7..3fd41d32c9 100644 --- a/Resources/Prototypes/Entities/Objects/Power/powercells.yml +++ b/Resources/Prototypes/Entities/Objects/Power/powercells.yml @@ -15,10 +15,14 @@ - type: PowerCell - type: Sprite netsync: false - - type: SolutionContainer - maxVol: 5 - caps: Injectable, Drawable - + - type: SolutionContainerManager + solutions: + powerCell: + maxVol: 5 + - type: InjectableSolution + solution: powerCell + - type: DrawableSolution + solution: powerCell - type: entity id: PowerCellSmallBase diff --git a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/leaves.yml b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/leaves.yml index d177ef2de8..91c31a82b4 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/leaves.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/leaves.yml @@ -11,11 +11,12 @@ - type: Produce seed: cannabis - type: Food - - type: SolutionContainer - contents: - reagents: - - ReagentId: THCOil - Quantity: 5 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: THCOil + Quantity: 5 - type: entity name: dried cannabis leaves @@ -27,11 +28,12 @@ stackType: LeavesCannabisDried max: 5 count: 1 - - type: SolutionContainer - contents: - reagents: - - ReagentId: THC - Quantity: 2 + - type: SolutionContainerManager + solutions: + produce: + reagents: + - ReagentId: THC + Quantity: 2 - type: Sprite sprite: Objects/Specific/Hydroponics/tobacco.rsi state: dried @@ -46,11 +48,12 @@ stackType: GroundCannabis max: 5 count: 1 - - type: SolutionContainer - contents: - reagents: - - ReagentId: THC - Quantity: 10 + - type: SolutionContainerManager + solutions: + produce: + reagents: + - ReagentId: THC + Quantity: 10 - type: Sprite sprite: Objects/Misc/reageant_fillings.rsi state: powderpile @@ -65,10 +68,10 @@ id: LeavesTobacco description: "Dry them out to make some smokes." components: - - type: Sprite - sprite: Objects/Specific/Hydroponics/tobacco.rsi - - type: Produce - seed: tobacco + - type: Sprite + sprite: Objects/Specific/Hydroponics/tobacco.rsi + - type: Produce + seed: tobacco - type: entity name: dried tobacco leaves @@ -76,18 +79,19 @@ id: LeavesTobaccoDried description: "Dried tobacco leaves, ready to be ground." components: - - type: Stack - stackType: LeavesTobaccoDried - max: 5 - count: 1 - - type: SolutionContainer - contents: + - type: Stack + stackType: LeavesTobaccoDried + max: 5 + count: 1 + - type: SolutionContainerManager + solutions: + produce: reagents: - - ReagentId: Nicotine - Quantity: 2 - - type: Sprite - sprite: Objects/Specific/Hydroponics/tobacco.rsi - state: dried + - ReagentId: Nicotine + Quantity: 2 + - type: Sprite + sprite: Objects/Specific/Hydroponics/tobacco.rsi + state: dried - type: entity name: ground tobacco @@ -95,19 +99,20 @@ id: GroundTobacco description: "Ground tobacco, perfect for hand-rolled cigarettes." components: - - type: Stack - stackType: GroundTobacco - max: 5 - count: 1 - - type: SolutionContainer - contents: + - type: Stack + stackType: GroundTobacco + max: 5 + count: 1 + - type: SolutionContainerManager + solutions: + produce: reagents: - - ReagentId: Nicotine - Quantity: 10 - - type: Sprite - sprite: Objects/Misc/reageant_fillings.rsi - state: powderpile - color: brown - - type: Construction - graph: smokeableGroundTobacco - node: ground + - ReagentId: Nicotine + Quantity: 10 + - type: Sprite + sprite: Objects/Misc/reageant_fillings.rsi + state: powderpile + color: brown + - type: Construction + graph: smokeableGroundTobacco + node: ground diff --git a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/seeds.yml b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/seeds.yml index fb1b48220a..2efe6a83b4 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/seeds.yml @@ -4,8 +4,7 @@ abstract: true components: - type: Seed - - type: SolutionContainer - caps: None + - type: SolutionContainerManager - type: Sprite sprite: Objects/Specific/Hydroponics/seeds.rsi state: seed diff --git a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/sprays.yml b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/sprays.yml index abc6ffceea..84c8d1f4dd 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/sprays.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/sprays.yml @@ -11,13 +11,15 @@ - type: Sprite sprite: Objects/Tools/Hydroponics/sprays.rsi state: plantbgone - - type: SolutionContainer - maxVol: 100 - caps: Drainable - contents: - reagents: + - type: SolutionContainerManager + solutions: + spray: + maxVol: 100 + reagents: - ReagentId: PlantBGone Quantity: 100 + - type: DrainableSolution + solution: spray - type: Item sprite: Objects/Tools/Hydroponics/sprays.rsi HeldPrefix: plantbgone @@ -32,13 +34,15 @@ - type: Sprite sprite: Objects/Tools/Hydroponics/sprays.rsi state: weedspray - - type: SolutionContainer - maxVol: 50 - caps: Drainable - contents: - reagents: + - type: SolutionContainerManager + solutions: + spray: + maxVol: 50 + reagents: - ReagentId: WeedKiller Quantity: 50 + - type: DrainableSolution + solution: spray - type: Spillable - type: ItemCooldown - type: Spray @@ -55,12 +59,14 @@ components: - type: Sprite state: pestspray - - type: SolutionContainer - maxVol: 50 - caps: Drainable - contents: - reagents: + - type: SolutionContainerManager + solutions: + spray: + maxVol: 50 + reagents: - ReagentId: PestKiller Quantity: 50 + - type: DrainableSolution + solution: spray - type: Item sprite: Objects/Tools/Hydroponics/sprays.rsi diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml index bef94c97fd..b094a5b4b8 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml @@ -12,8 +12,10 @@ size: 10 sprite: Objects/Specific/Janitorial/mop.rsi - type: Mop - - type: SolutionContainer - maxVol: 10 + - type: SolutionContainerManager + solutions: + mop: + maxVol: 10 - type: entity name: mop bucket @@ -30,10 +32,11 @@ drawdepth: Objects - type: InteractionOutline - type: Bucket - - type: SolutionContainer - maxVol: 500 - contents: - reagents: + - type: SolutionContainerManager + solutions: + bucket: + maxVol: 500 + reagents: - ReagentId: Water Quantity: 500 - type: Physics diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml index 618932fe88..53fa57e758 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml @@ -14,9 +14,14 @@ state: cleaner - type: Item sprite: Objects/Specific/Janitorial/janitorial.rsi - - type: SolutionContainer - maxVol: 100 - caps: Refillable, Drainable + - type: SolutionContainerManager + solutions: + spray: + maxVol: 100 + - type: RefillableSolution + solution: spray + - type: DrainableSolution + solution: spray - type: SolutionTransfer - type: Spillable - type: ItemCooldown @@ -32,10 +37,11 @@ suffix: Filled parent: SprayBottle components: - - type: SolutionContainer - maxVol: 100 - contents: - reagents: + - type: SolutionContainerManager + solutions: + spray: + maxVol: 100 + reagents: - ReagentId: Water Quantity: 100 @@ -46,10 +52,11 @@ parent: SprayBottle suffix: "" components: - - type: SolutionContainer - maxVol: 100 - contents: - reagents: + - type: SolutionContainerManager + solutions: + spray: + maxVol: 100 + reagents: - ReagentId: SpaceCleaner Quantity: 100 @@ -60,27 +67,29 @@ name: "vapor" abstract: true components: - - type: SolutionContainer - maxVol: 50 + - type: SolutionContainerManager + solutions: + vapor: + maxVol: 50 - type: Vapor - type: AnimationPlayer - type: Sprite netsync: false sprite: Effects/chempuff.rsi layers: - - state: chempuff - map: [ "enum.VaporVisualLayers.Base" ] + - state: chempuff + map: ["enum.VaporVisualLayers.Base"] - type: Physics bodyType: Dynamic fixtures: - shape: - !type:PhysShapeAabb - bounds: "-0.25,-0.25,0.25,0.25" + !type:PhysShapeAabb + bounds: "-0.25,-0.25,0.25,0.25" hard: false mask: - - Impassable - - MobImpassable - - SmallImpassable + - Impassable + - MobImpassable + - SmallImpassable - type: Appearance visuals: - - type: VaporVisualizer + - type: VaporVisualizer diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml index bca4d61823..743eda8c8b 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml @@ -9,7 +9,12 @@ state: hypo - type: Item sprite: Objects/Specific/Medical/hypospray.rsi - - type: SolutionContainer - maxVol: 30 - caps: Refillable, CanExamine + - type: SolutionContainerManager + solutions: + hypospray: + maxVol: 30 + - type: RefillableSolution + solution: hypospray + - type: ExaminableSolution + solution: hypospray - type: Hypospray diff --git a/Resources/Prototypes/Entities/Objects/Specific/chemistry-bottles.yml b/Resources/Prototypes/Entities/Objects/Specific/chemistry-bottles.yml index c6ef1a8215..11314a3754 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/chemistry-bottles.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/chemistry-bottles.yml @@ -14,9 +14,16 @@ state: bottle-1 - type: Drink isOpen: true - - type: SolutionContainer - maxVol: 30 - caps: OpenContainer + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + - type: RefillableSolution + solution: drink + - type: DrainableSolution + solution: drink + - type: ExaminableSolution + solution: drink - type: SolutionTransfer maxTransferAmount: 30 canChangeTransferAmount: true diff --git a/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml b/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml index 5152af496d..c1f60b379b 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml @@ -17,9 +17,18 @@ visible: false - type: Item sprite: Objects/Specific/Chemistry/beaker.rsi - - type: SolutionContainer - maxVol: 50 - caps: OpenContainer, FitsInDispenser # can add and remove solutions and fits in the chemmaster. + - type: SolutionContainerManager + solutions: + beaker: + maxVol: 50 + - type: FitsInDispenser + solution: beaker + - type: RefillableSolution + solution: beaker + - type: DrainableSolution + solution: beaker + - type: ExaminableSolution + solution: beaker - type: SolutionTransfer canChangeTransferAmount: true - type: UserInterface @@ -72,8 +81,10 @@ visible: false - type: Item sprite: Objects/Specific/Chemistry/beaker_large.rsi - - type: SolutionContainer - maxVol: 100 + - type: SolutionContainerManager + solutions: + beaker: + maxVol: 100 - type: Appearance visuals: - type: SolutionContainerVisualizer @@ -90,9 +101,11 @@ sprite: Objects/Specific/Chemistry/beaker_cryostasis.rsi layers: - state: beakernoreact - - type: SolutionContainer - maxVol: 60 - canReact: false + - type: SolutionContainerManager + solutions: + beaker: + maxVol: 60 + canReact: false - type: entity name: bluespace beaker @@ -104,8 +117,10 @@ sprite: Objects/Specific/Chemistry/beaker_bluespace.rsi layers: - state: beakerbluespace - - type: SolutionContainer - maxVol: 300 + - type: SolutionContainerManager + solutions: + beaker: + maxVol: 300 - type: entity name: dropper @@ -121,9 +136,16 @@ - state: dropper1 map: ["enum.SolutionContainerLayers.Fill"] visible: false - - type: SolutionContainer - caps: OpenContainer - maxVol: 5 + - type: SolutionContainerManager + solutions: + dropper: + maxVol: 5 + - type: RefillableSolution + solution: dropper + - type: DrainableSolution + solution: dropper + - type: ExaminableSolution + solution: dropper - type: SolutionTransfer minTransferAmount: 1 transferAmount: 1 @@ -160,8 +182,10 @@ - type: Item sprite: Objects/Specific/Chemistry/syringe.rsi HeldPrefix: 0 - - type: SolutionContainer - maxVol: 15 + - type: SolutionContainerManager + solutions: + injector: + maxVol: 15 - type: Injector injectOnly: false - type: Spillable @@ -196,6 +220,7 @@ - type: Food transferAmount: null eatMessage: food-swallow - - type: SolutionContainer - maxVol: 50 - caps: None + - type: SolutionContainerManager + solutions: + pill: + maxVol: 50 diff --git a/Resources/Prototypes/Entities/Objects/Specific/rehydrateable.yml b/Resources/Prototypes/Entities/Objects/Specific/rehydrateable.yml index ba7fe8872a..0278e434b9 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/rehydrateable.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/rehydrateable.yml @@ -4,13 +4,15 @@ name: monkey cube description: Just add water! components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 - maxVol: 11 # needs room for water - caps: Refillable + - type: SolutionContainerManager + solutions: + cube: + maxVol: 11 # needs room for water + reagents: + - ReagentId: Nutriment + Quantity: 10 + - type: RefillableSolution + solution: cube - type: Sprite sprite: Objects/Misc/monkeycube.rsi state: cube @@ -35,10 +37,10 @@ mass: 2.5 hard: false layer: - - SmallImpassable + - SmallImpassable mask: - - Impassable - - MobImpassable + - Impassable + - MobImpassable - type: entity parent: PlushieCarp @@ -46,13 +48,15 @@ name: dehydrated space carp description: Looks like a plush toy carp, but just add water and it becomes a real-life space carp! components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 10 - maxVol: 11 # needs room for water - caps: Refillable + - type: SolutionContainerManager + solutions: + plushie: + maxVol: 11 # needs room for water + reagents: + - ReagentId: Nutriment + Quantity: 10 + - type: RefillableSolution + solution: cube - type: Reactive reactions: - !type:AddToSolutionReaction @@ -60,7 +64,7 @@ ingestion: true injection: true reagents: - - Water + - Water - type: Rehydratable target: CarpMob_Content - type: CollisionWake @@ -74,7 +78,7 @@ mass: 2.5 hard: false layer: - - SmallImpassable + - SmallImpassable mask: - - Impassable - - MobImpassable + - Impassable + - MobImpassable diff --git a/Resources/Prototypes/Entities/Objects/Tools/bucket.yml b/Resources/Prototypes/Entities/Objects/Tools/bucket.yml index be03c43997..ba2aeea44d 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/bucket.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/bucket.yml @@ -17,8 +17,10 @@ Slots: - Helmet - type: Bucket - - type: SolutionContainer - maxVol: 500 + - type: SolutionContainerManager + solutions: + bucket: + maxVol: 500 - type: SolutionTransfer transferAmount: 50 maxTransferAmount: 100 diff --git a/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml b/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml index cbe6c707b3..ec45872bbb 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml @@ -4,22 +4,22 @@ id: Haycutters description: This kills the wire. Moo! components: - - type: Tag - tags: - - PlantSampleTaker - - type: Sprite - sprite: Objects/Tools/Cowtools/haycutters.rsi - state: haycutters - - type: ItemCooldown - - type: MeleeWeapon - - type: Tool - qualities: - - Cutting - useSound: - path: /Audio/Items/wirecutter.ogg - speed: 0.05 - - type: Item - sprite: Objects/Tools/Cowtools/haycutters.rsi + - type: Tag + tags: + - PlantSampleTaker + - type: Sprite + sprite: Objects/Tools/Cowtools/haycutters.rsi + state: haycutters + - type: ItemCooldown + - type: MeleeWeapon + - type: Tool + qualities: + - Cutting + useSound: + path: /Audio/Items/wirecutter.ogg + speed: 0.05 + - type: Item + sprite: Objects/Tools/Cowtools/haycutters.rsi - type: entity name: moodriver @@ -27,18 +27,18 @@ id: Moodriver description: Turn to use. Moo! components: - - type: Sprite - sprite: Objects/Tools/Cowtools/moodriver.rsi - state: moodriver - - type: Item - sprite: Objects/Tools/Cowtools/moodriver.rsi - - type: ItemCooldown - - type: MeleeWeapon - - type: Tool - qualities: - - Screwing - useSoundCollection: Screwdriver - speed: 0.05 + - type: Sprite + sprite: Objects/Tools/Cowtools/moodriver.rsi + state: moodriver + - type: Item + sprite: Objects/Tools/Cowtools/moodriver.rsi + - type: ItemCooldown + - type: MeleeWeapon + - type: Tool + qualities: + - Screwing + useSoundCollection: Screwdriver + speed: 0.05 - type: entity name: wronch @@ -46,19 +46,19 @@ id: Wronch description: Wronch thing. Moo! components: - - type: Sprite - sprite: Objects/Tools/Cowtools/wronch.rsi - state: wronch - - type: Item - sprite: Objects/Tools/Cowtools/wronch.rsi - - type: ItemCooldown - - type: MeleeWeapon - - type: Tool - qualities: - - Anchoring - useSound: - path: /Audio/Items/ratchet.ogg - speed: 0.05 + - type: Sprite + sprite: Objects/Tools/Cowtools/wronch.rsi + state: wronch + - type: Item + sprite: Objects/Tools/Cowtools/wronch.rsi + - type: ItemCooldown + - type: MeleeWeapon + - type: Tool + qualities: + - Anchoring + useSound: + path: /Audio/Items/ratchet.ogg + speed: 0.05 - type: entity name: cowbar @@ -66,20 +66,20 @@ id: Cowbar description: Cow your problems away. Moo! components: - - type: Sprite - sprite: Objects/Tools/Cowtools/cowbar.rsi - state: cowbar - - type: Item - sprite: Objects/Tools/Cowtools/cowbar.rsi - - type: ItemCooldown - - type: MeleeWeapon - - type: Tool - qualities: - - Prying - useSound: - path: /Audio/Items/crowbar.ogg - speed: 0.05 - - type: TilePrying + - type: Sprite + sprite: Objects/Tools/Cowtools/cowbar.rsi + state: cowbar + - type: Item + sprite: Objects/Tools/Cowtools/cowbar.rsi + - type: ItemCooldown + - type: MeleeWeapon + - type: Tool + qualities: + - Prying + useSound: + path: /Audio/Items/crowbar.ogg + speed: 0.05 + - type: TilePrying - type: entity name: mooltitool @@ -87,15 +87,15 @@ id: Mooltitool description: An crude tool to copy, store, and send electrical pulses and signals through wires and machines. Moo! components: - - type: Sprite - sprite: Objects/Tools/Cowtools/mooltitool.rsi - state: mooltitool - - type: Item - sprite: Objects/Tools/Cowtools/mooltitool.rsi - - type: Tool - qualities: - - Multitool - - type: SignalLinker + - type: Sprite + sprite: Objects/Tools/Cowtools/mooltitool.rsi + state: mooltitool + - type: Item + sprite: Objects/Tools/Cowtools/mooltitool.rsi + - type: Tool + qualities: + - Multitool + - type: SignalLinker - type: entity name: cowelding tool @@ -103,34 +103,36 @@ id: Cowelder description: "Melts anything as long as it's fueled, don't forget your eye protection! Moo!" components: - - type: Sprite - sprite: Objects/Tools/Cowtools/cowelder.rsi - layers: - - state: icon - - state: welder_flame - shader: unshaded - visible: false - - type: Item - size: 10 - sprite: Objects/Tools/Cowtools/cowelder.rsi - HeldPrefix: off - - type: ItemCooldown - - type: MeleeWeapon - - type: ItemStatus - - type: SolutionContainer - maxVol: 50 - caps: Refillable - contents: + - type: Sprite + sprite: Objects/Tools/Cowtools/cowelder.rsi + layers: + - state: icon + - state: welder_flame + shader: unshaded + visible: false + - type: Item + size: 10 + sprite: Objects/Tools/Cowtools/cowelder.rsi + HeldPrefix: off + - type: ItemCooldown + - type: MeleeWeapon + - type: ItemStatus + - type: SolutionContainerManager + solutions: + welder: + maxVol: 50 reagents: - - ReagentId: WeldingFuel - Quantity: 100 - - type: Welder - weldSoundCollection: Welder - speed: 0.05 - - type: PointLight - enabled: false - radius: 1.5 - color: orange + - ReagentId: WeldingFuel + Quantity: 100 + - type: RefillableSolution + solution: welder + - type: Welder + weldSoundCollection: Welder + speed: 0.05 + - type: PointLight + enabled: false + radius: 1.5 + color: orange - type: entity name: milkalyzer @@ -138,19 +140,19 @@ id: Milkalyzer description: A hand-held environmental scanner which reports current gas levels. Moo! components: - - type: Sprite - sprite: Objects/Tools/Cowtools/milkalyzer.rsi - state: milkalyzer - - type: GasAnalyzer - - type: UserInterface - interfaces: - - key: enum.GasAnalyzerUiKey.Key - type: GasAnalyzerBoundUserInterface - - type: Appearance - visuals: - - type: GasAnalyzerVisualizer - state_off: milkalyzer - state_working: milkalyzer + - type: Sprite + sprite: Objects/Tools/Cowtools/milkalyzer.rsi + state: milkalyzer + - type: GasAnalyzer + - type: UserInterface + interfaces: + - key: enum.GasAnalyzerUiKey.Key + type: GasAnalyzerBoundUserInterface + - type: Appearance + visuals: + - type: GasAnalyzerVisualizer + state_off: milkalyzer + state_working: milkalyzer - type: entity name: cow toolbox @@ -158,11 +160,11 @@ id: CowToolbox description: A weirdly shaped box, stocked with... tools? components: - - type: Sprite - sprite: Objects/Tools/Cowtools/cow_toolbox.rsi - state: cow_toolbox - - type: Item - sprite: Objects/Tools/Cowtools/cow_toolbox.rsi + - type: Sprite + sprite: Objects/Tools/Cowtools/cow_toolbox.rsi + state: cow_toolbox + - type: Item + sprite: Objects/Tools/Cowtools/cow_toolbox.rsi - type: entity id: CowToolboxFilled @@ -170,14 +172,14 @@ suffix: Filled parent: CowToolbox components: - - type: StorageFill - contents: - - id: Haycutters - - id: Moodriver - - id: Wronch - - id: Cowbar - - id: Mooltitool - - id: Cowelder - - id: Milkalyzer + - type: StorageFill + contents: + - id: Haycutters + - id: Moodriver + - id: Wronch + - id: Cowbar + - id: Mooltitool + - id: Cowelder + - id: Milkalyzer # I hate these fucking cowtools I hope the burn in hell -Swept diff --git a/Resources/Prototypes/Entities/Objects/Tools/welders.yml b/Resources/Prototypes/Entities/Objects/Tools/welders.yml index b350084edb..506f0da0b2 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/welders.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/welders.yml @@ -21,13 +21,15 @@ - type: ItemCooldown - type: MeleeWeapon - type: ItemStatus - - type: SolutionContainer - maxVol: 100 - caps: Refillable - contents: - reagents: + - type: RefillableSolution + solution: welder + - type: SolutionContainerManager + solutions: + welder: + reagents: - ReagentId: WeldingFuel Quantity: 100 + maxVol: 100 - type: Welder - type: PointLight enabled: false @@ -44,13 +46,15 @@ sprite: Objects/Tools/welder_experimental.rsi - type: Item sprite: Objects/Tools/welder_experimental.rsi - - type: SolutionContainer - maxVol: 1000 - caps: Refillable - contents: - reagents: + - type: RefillableSolution + solution: welder + - type: SolutionContainerManager + solutions: + welder: + reagents: - ReagentId: WeldingFuel Quantity: 1000 + maxVol: 1000 - type: PointLight enabled: false radius: 1.5 @@ -66,13 +70,15 @@ sprite: Objects/Tools/welder_mini.rsi - type: Item sprite: Objects/Tools/welder_mini.rsi - - type: SolutionContainer - maxVol: 25 - caps: Refillable - contents: - reagents: + - type: RefillableSolution + solution: welder + - type: SolutionContainerManager + solutions: + welder: + reagents: - ReagentId: WeldingFuel Quantity: 25 + maxVol: 25 - type: PointLight enabled: false radius: 1.0 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/shotgun.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/shotgun.yml index 5d7e0cb237..62384b9d8b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/shotgun.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/shotgun.yml @@ -101,8 +101,13 @@ projectilesFired: 1 ammoSpread: 0 - type: ChemicalAmmo - - type: SolutionContainer - maxVol: 15 - caps: Refillable, Drainable + - type: SolutionContainerManager + solutions: + ammo: + maxVol: 15 + - type: RefillableSolution + solution: ammo + - type: DrainableSolution + solution: ammo - type: SolutionTransfer maxTransferAmount: 15 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/shotgun.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/shotgun.yml index 2bfe712a92..078cbaf4a8 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/shotgun.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/shotgun.yml @@ -89,8 +89,13 @@ - type: Projectile damages: Blunt: 1 - - type: SolutionContainer - maxVol: 15 - caps: Refillable, Drainable + - type: SolutionContainerManager + solutions: + ammo: + maxVol: 15 + - type: RefillableSolution + solution: ammo + - type: DrainableSolution + solution: ammo - type: SolutionInjectOnCollide transferAmount: 15 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml index 816843660e..ee15816217 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml @@ -309,8 +309,9 @@ layers: - state: tin - state: plain - - type: SolutionContainer - contents: - reagents: - - ReagentId: Nutriment - Quantity: 8 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Nutriment + Quantity: 8 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml index 42d092684a..d2c0ce7f83 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml @@ -27,9 +27,12 @@ node: spear - type: ItemCooldown - type: MeleeChemicalInjector - - type: SolutionContainer - caps: Refillable - maxVol: 5 + - type: SolutionContainerManager + solutions: + melee: + maxVol: 5 + - type: RefillableSolution + solution: melee - type: SolutionTransfer maxTransferAmount: 5 diff --git a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml index c5a9cc88d0..67e877d5d8 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml @@ -13,8 +13,10 @@ - type: Toilet - type: SecretStash secretPartName: the toilet cistern - - type: SolutionContainer - maxVol: 250 + - type: SolutionContainerManager + solutions: + toilet: + maxVol: 250 - type: Physics fixtures: - shape: @@ -32,10 +34,11 @@ parent: ToiletEmpty suffix: Dirty Water components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Water - Quantity: 180 - - ReagentId: Toxin - Quantity: 20 + - type: SolutionContainerManager + solutions: + toilet: + reagents: + - ReagentId: Water + Quantity: 180 + - ReagentId: Toxin + Quantity: 20 diff --git a/Resources/Prototypes/Entities/Structures/Machines/microwave.yml b/Resources/Prototypes/Entities/Structures/Machines/microwave.yml index dd841859f8..6ce9ae49a4 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/microwave.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/microwave.yml @@ -5,9 +5,12 @@ description: It's magic. components: - type: Microwave - - type: SolutionContainer - maxVol: 100 - caps: Refillable + - type: SolutionContainerManager + solutions: + microwave: + maxVol: 100 + - type: RefillableSolution + solution: microwave - type: Appearance visuals: - type: MicrowaveVisualizer diff --git a/Resources/Prototypes/Entities/Structures/Storage/Tanks/base.yml b/Resources/Prototypes/Entities/Structures/Storage/Tanks/base.yml index 536a020c93..e438f47eee 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Tanks/base.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Tanks/base.yml @@ -33,7 +33,10 @@ behaviors: - !type:DoActsBehavior acts: ["Destruction"] - - type: SolutionContainer - maxVol: 1500 - caps: Drainable + - type: SolutionContainerManager + solutions: + tank: + maxVol: 1500 + - type: DrainableSolution + solution: tank - type: ReagentTank diff --git a/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml b/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml index ef0c53bfd0..85caca8ce8 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml @@ -24,11 +24,12 @@ heavyImpactRange: 2 lightImpactRange: 6 flashRange: 5 - - type: SolutionContainer - contents: - reagents: - - ReagentId: WeldingFuel - Quantity: 1500 + - type: SolutionContainerManager + solutions: + tank: + reagents: + - ReagentId: WeldingFuel + Quantity: 1500 # Water @@ -48,8 +49,9 @@ id: WaterTankFull suffix: Full components: - - type: SolutionContainer - contents: - reagents: - - ReagentId: Water - Quantity: 1500 + - type: SolutionContainerManager + solutions: + tank: + reagents: + - ReagentId: Water + Quantity: 1500 diff --git a/Resources/Prototypes/Entities/Structures/soil.yml b/Resources/Prototypes/Entities/Structures/soil.yml index e87dfcfdfc..dd05e691b0 100644 --- a/Resources/Prototypes/Entities/Structures/soil.yml +++ b/Resources/Prototypes/Entities/Structures/soil.yml @@ -40,9 +40,12 @@ drawdepth: FloorObjects - type: PlantHolder drawWarnings: false - - type: SolutionContainer - maxVol: 200 - caps: Refillable + - type: SolutionContainerManager + solutions: + soil: + maxVol: 200 + - type: RefillableSolution + solution: soil - type: SnapGrid - type: Reactive reactions: diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 902b96f68f..8e6b33c1fd 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -112,9 +112,6 @@ - type: Tag id: GlassBeaker -- type: Tag - id: Grindable - - type: Tag id: Hoe