diff --git a/Content.Server/GameObjects/Components/Body/Digestive/StomachComponent.cs b/Content.Server/GameObjects/Components/Body/Digestive/StomachComponent.cs index 08e2049129..1a1e3cfc75 100644 --- a/Content.Server/GameObjects/Components/Body/Digestive/StomachComponent.cs +++ b/Content.Server/GameObjects/Components/Body/Digestive/StomachComponent.cs @@ -72,7 +72,7 @@ namespace Content.Server.GameObjects.Components.Body.Digestive solution.MaxVolume = _initialMaxVolume; } - public bool TryTransferSolution(Solution solution) + public bool CanTransferSolution(Solution solution) { if (!Owner.TryGetComponent(out SolutionContainerComponent? solutionComponent)) { @@ -80,11 +80,21 @@ namespace Content.Server.GameObjects.Components.Body.Digestive } // TODO: For now no partial transfers. Potentially change by design - if (solution.TotalVolume + solutionComponent.CurrentVolume > solutionComponent.MaxVolume) + if (!solutionComponent.CanAddSolution(solution)) { return false; } + return true; + } + + public bool TryTransferSolution(Solution solution) + { + if (!CanTransferSolution(solution)) + return false; + + var solutionComponent = Owner.GetComponent(); + // Add solution to _stomachContents solutionComponent.TryAddSolution(solution, false, true); // Add each reagent to _reagentDeltas. Used to track how long each reagent has been in the stomach diff --git a/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs b/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs index a7e4e8ec38..f14836feeb 100644 --- a/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs @@ -8,7 +8,9 @@ using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Utility; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; using Robust.Shared.Localization; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; @@ -22,6 +24,8 @@ namespace Content.Server.GameObjects.Components.Chemistry [RegisterComponent] public class InjectorComponent : SharedInjectorComponent, IAfterInteract, IUse { + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + /// /// Whether or not the injector is able to draw from containers or if it's a single use /// device that can only inject. @@ -126,7 +130,7 @@ namespace Content.Server.GameObjects.Components.Chemistry else { eventArgs.User.PopupMessage(eventArgs.User, Loc.GetString("You aren't able to transfer to {0:theName}!", targetSolution.Owner)); - } + } } else if (_toggleState == InjectorToggleMode.Draw) { @@ -186,11 +190,27 @@ namespace Content.Server.GameObjects.Components.Chemistry // Move units from attackSolution to targetSolution var removedSolution = solution.SplitSolution(realTransferAmount); - if (!targetBloodstream.TryTransferSolution(removedSolution)) + if (!solution.CanAddSolution(removedSolution)) { return; } + // TODO: Account for partial transfer. + + foreach (var (reagentId, quantity) in removedSolution.Contents) + { + if(!_prototypeManager.TryIndex(reagentId, out ReagentPrototype reagent)) continue; + removedSolution.RemoveReagent(reagentId, reagent.ReactionEntity(solution.Owner, ReactionMethod.Injection, quantity)); + } + + solution.TryAddSolution(removedSolution); + + foreach (var (reagentId, quantity) in removedSolution.Contents) + { + if(!_prototypeManager.TryIndex(reagentId, out ReagentPrototype reagent)) continue; + reagent.ReactionEntity(targetBloodstream.Owner, ReactionMethod.Injection, quantity); + } + Owner.PopupMessage(user, Loc.GetString("You inject {0}u into {1:theName}!", removedSolution.TotalVolume, targetBloodstream.Owner)); Dirty(); } @@ -214,11 +234,19 @@ namespace Content.Server.GameObjects.Components.Chemistry // Move units from attackSolution to targetSolution var removedSolution = solution.SplitSolution(realTransferAmount); - if (!targetSolution.TryAddSolution(removedSolution)) + if (!targetSolution.CanAddSolution(removedSolution)) { return; } + foreach (var (reagentId, quantity) in removedSolution.Contents) + { + if(!_prototypeManager.TryIndex(reagentId, out ReagentPrototype reagent)) continue; + removedSolution.RemoveReagent(reagentId, reagent.ReactionEntity(targetSolution.Owner, ReactionMethod.Injection, quantity)); + } + + targetSolution.TryAddSolution(removedSolution); + Owner.PopupMessage(user, Loc.GetString("You transfter {0}u to {1:theName}", removedSolution.TotalVolume, targetSolution.Owner)); Dirty(); } diff --git a/Content.Server/GameObjects/Components/Chemistry/PillComponent.cs b/Content.Server/GameObjects/Components/Chemistry/PillComponent.cs index ad6d8238e0..fda8e43720 100644 --- a/Content.Server/GameObjects/Components/Chemistry/PillComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/PillComponent.cs @@ -11,6 +11,8 @@ using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Localization; +using Robust.Shared.Log; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; @@ -21,6 +23,7 @@ namespace Content.Server.GameObjects.Components.Chemistry public class PillComponent : FoodComponent, IUse, IAfterInteract { [Dependency] private readonly IEntitySystemManager _entitySystem = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; public override string Name => "Pill"; @@ -45,8 +48,11 @@ namespace Content.Server.GameObjects.Components.Chemistry public override void Initialize() { base.Initialize(); - - _contents = Owner.GetComponent(); + + if (!Owner.EnsureComponent(out _contents)) + { + Logger.Error($"Prototype {Owner.Prototype?.ID} had a {nameof(PillComponent)} without a {nameof(SolutionContainerComponent)}!"); + } } bool IUse.UseEntity(UseEntityEventArgs eventArgs) @@ -86,13 +92,24 @@ namespace Content.Server.GameObjects.Components.Chemistry var transferAmount = ReagentUnit.Min(_transferAmount, _contents.CurrentVolume); var split = _contents.SplitSolution(transferAmount); - if (!stomach.TryTransferSolution(split)) + + if (!stomach.CanTransferSolution(split)) { _contents.TryAddSolution(split); trueTarget.PopupMessage(user, Loc.GetString("You can't eat any more!")); return false; } + // TODO: Account for partial transfer. + + foreach (var (reagentId, quantity) in split.Contents) + { + if (!_prototypeManager.TryIndex(reagentId, out ReagentPrototype reagent)) continue; + split.RemoveReagent(reagentId, reagent.ReactionEntity(trueTarget, ReactionMethod.Ingestion, quantity)); + } + + stomach.TryTransferSolution(split); + if (_useSound != null) { _entitySystem.GetEntitySystem() diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionContainerComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionContainerComponent.cs index 16a1917edc..db41dc70a3 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionContainerComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionContainerComponent.cs @@ -91,7 +91,7 @@ namespace Content.Server.GameObjects.Components.Chemistry serializer.DataField(this, x => MaxVolume, "maxVol", ReagentUnit.New(0)); serializer.DataField(this, x => Solution, "contents", new Solution()); - serializer.DataField(this, x => Capabilities, "caps", SolutionContainerCaps.AddTo | SolutionContainerCaps.RemoveFrom); + serializer.DataField(this, x => Capabilities, "caps", SolutionContainerCaps.AddTo | SolutionContainerCaps.RemoveFrom); serializer.DataField(ref _fillInitState, "fillingState", string.Empty); serializer.DataField(ref _fillInitSteps, "fillingSteps", 7); } @@ -147,7 +147,7 @@ namespace Content.Server.GameObjects.Components.Chemistry { return false; } - + Solution.RemoveSolution(quantity); OnSolutionChanged(false); return true; @@ -179,7 +179,7 @@ namespace Content.Server.GameObjects.Components.Chemistry { continue; } - + if (mixColor == default) { mixColor = proto.SubstanceColor; @@ -229,13 +229,13 @@ namespace Content.Server.GameObjects.Components.Chemistry { return; } - + if (!hands.GetActiveHand.Owner.TryGetComponent(out var handSolutionComp) || !handSolutionComp.CanRemoveSolutions || !component.CanAddSolutions) { return; - } + } var transferQuantity = ReagentUnit.Min(component.MaxVolume - component.CurrentVolume, handSolutionComp.CurrentVolume, ReagentUnit.New(10)); @@ -243,7 +243,7 @@ namespace Content.Server.GameObjects.Components.Chemistry { return; } - + var transferSolution = handSolutionComp.SplitSolution(transferQuantity); component.TryAddSolution(transferSolution); } @@ -393,9 +393,14 @@ namespace Content.Server.GameObjects.Components.Chemistry return true; } + public bool CanAddSolution(Solution solution) + { + return solution.TotalVolume <= (MaxVolume - Solution.TotalVolume); + } + public bool TryAddSolution(Solution solution, bool skipReactionCheck = false, bool skipColor = false) { - if (solution.TotalVolume > (MaxVolume - Solution.TotalVolume)) + if (!CanAddSolution(solution)) return false; Solution.AddSolution(solution); diff --git a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs index ac511c3d24..4aac528138 100644 --- a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs @@ -145,7 +145,7 @@ namespace Content.Server.GameObjects.Components.Nutrition { target.PopupMessage(Loc.GetString("{0:theName} is empty!", Owner)); } - + return false; } @@ -157,7 +157,7 @@ namespace Content.Server.GameObjects.Components.Nutrition var transferAmount = ReagentUnit.Min(TransferAmount, _contents.CurrentVolume); var split = _contents.SplitSolution(transferAmount); - if (stomachComponent.TryTransferSolution(split)) + if (stomachComponent.CanTransferSolution(split)) { if (_useSound == null) { @@ -167,6 +167,17 @@ namespace Content.Server.GameObjects.Components.Nutrition EntitySystem.Get().PlayFromEntity(_useSound, target, AudioParams.Default.WithVolume(-2f)); target.PopupMessage(Loc.GetString("Slurp")); UpdateAppearance(); + + // TODO: Account for partial transfer. + + foreach (var (reagentId, quantity) in split.Contents) + { + if (!_prototypeManager.TryIndex(reagentId, out ReagentPrototype reagent)) continue; + split.RemoveReagent(reagentId, reagent.ReactionEntity(target, ReactionMethod.Ingestion, quantity)); + } + + stomachComponent.TryTransferSolution(split); + return true; } diff --git a/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs b/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs index 6dd5a9f80c..ab4a048975 100644 --- a/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs @@ -17,6 +17,7 @@ using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Localization; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; @@ -27,6 +28,7 @@ namespace Content.Server.GameObjects.Components.Nutrition public class FoodComponent : Component, IUse, IAfterInteract { [Dependency] private readonly IEntitySystemManager _entitySystem = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; public override string Name => "Food"; @@ -169,13 +171,22 @@ namespace Content.Server.GameObjects.Components.Nutrition var transferAmount = ReagentUnit.Min(_transferAmount, solution.CurrentVolume); var split = solution.SplitSolution(transferAmount); - if (!stomach.TryTransferSolution(split)) + if (!stomach.CanTransferSolution(split)) { - solution.TryAddSolution(split); trueTarget.PopupMessage(user, Loc.GetString("You can't eat any more!")); return false; } + // TODO: Account for partial transfer. + + foreach (var (reagentId, quantity) in split.Contents) + { + if (!_prototypeManager.TryIndex(reagentId, out ReagentPrototype reagent)) continue; + split.RemoveReagent(reagentId, reagent.ReactionEntity(target, ReactionMethod.Ingestion, quantity)); + } + + stomach.TryTransferSolution(split); + _entitySystem.GetEntitySystem() .PlayFromEntity(_useSound, trueTarget, AudioParams.Default.WithVolume(-1f)); trueTarget.PopupMessage(user, Loc.GetString("Nom")); diff --git a/Content.Shared/Chemistry/Solution.cs b/Content.Shared/Chemistry/Solution.cs index 988bf74402..50eed0c8c6 100644 --- a/Content.Shared/Chemistry/Solution.cs +++ b/Content.Shared/Chemistry/Solution.cs @@ -264,6 +264,12 @@ namespace Content.Shared.Chemistry } public int CompareTo(ReagentQuantity other) { return Quantity.Float().CompareTo(other.Quantity.Float()); } + + public void Deconstruct(out string reagentId, out ReagentUnit quantity) + { + reagentId = ReagentId; + quantity = Quantity; + } } #region Enumeration