diff --git a/Content.Server/GameObjects/Components/ActionBlocking/HandcuffComponent.cs b/Content.Server/GameObjects/Components/ActionBlocking/HandcuffComponent.cs index 18f4892d11..9e05ab7cd1 100644 --- a/Content.Server/GameObjects/Components/ActionBlocking/HandcuffComponent.cs +++ b/Content.Server/GameObjects/Components/ActionBlocking/HandcuffComponent.cs @@ -148,41 +148,41 @@ namespace Content.Server.GameObjects.Components.ActionBlocking return new HandcuffedComponentState(Broken ? BrokenState : string.Empty); } - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) + async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { if (eventArgs.Target == null || !ActionBlockerSystem.CanUse(eventArgs.User) || !eventArgs.Target.TryGetComponent(out var cuffed)) { - return; + return false; } if (eventArgs.Target == eventArgs.User) { eventArgs.User.PopupMessage(Loc.GetString("You can't cuff yourself!")); - return; + return true; } if (Broken) { eventArgs.User.PopupMessage(Loc.GetString("The cuffs are broken!")); - return; + return true; } if (!eventArgs.Target.TryGetComponent(out var hands)) { eventArgs.User.PopupMessage(Loc.GetString("{0:theName} has no hands!", eventArgs.Target)); - return; + return true; } if (cuffed.CuffedHandCount == hands.Count) { eventArgs.User.PopupMessage(Loc.GetString("{0:theName} has no free hands to handcuff!", eventArgs.Target)); - return; + return true; } if (!eventArgs.InRangeUnobstructed(_interactRange, ignoreInsideBlocker: true)) { eventArgs.User.PopupMessage(Loc.GetString("You are too far away to use the cuffs!")); - return; + return true; } eventArgs.User.PopupMessage(Loc.GetString("You start cuffing {0:theName}.", eventArgs.Target)); @@ -190,6 +190,7 @@ namespace Content.Server.GameObjects.Components.ActionBlocking _audioSystem.PlayFromEntity(StartCuffSound, Owner); TryUpdateCuff(eventArgs.User, eventArgs.Target, cuffed); + return true; } /// diff --git a/Content.Server/GameObjects/Components/Atmos/GasAnalyzerComponent.cs b/Content.Server/GameObjects/Components/Atmos/GasAnalyzerComponent.cs index a63a27d274..586a332bdb 100644 --- a/Content.Server/GameObjects/Components/Atmos/GasAnalyzerComponent.cs +++ b/Content.Server/GameObjects/Components/Atmos/GasAnalyzerComponent.cs @@ -253,18 +253,20 @@ namespace Content.Server.GameObjects.Components.Atmos } } - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) + async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { if (!eventArgs.CanReach) { eventArgs.User.PopupMessage(Loc.GetString("You can't reach there!")); - return; + return true; } if (eventArgs.User.TryGetComponent(out IActorComponent? actor)) { OpenInterface(actor.playerSession, eventArgs.ClickLocation); } + + return true; } diff --git a/Content.Server/GameObjects/Components/Body/MechanismComponent.cs b/Content.Server/GameObjects/Components/Body/MechanismComponent.cs index 1ff3f77f52..8dd4658573 100644 --- a/Content.Server/GameObjects/Components/Body/MechanismComponent.cs +++ b/Content.Server/GameObjects/Components/Body/MechanismComponent.cs @@ -36,11 +36,11 @@ namespace Content.Server.GameObjects.Components.Body } } - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) + async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { if (eventArgs.Target == null) { - return; + return false; } CloseAllSurgeryUIs(); @@ -61,6 +61,8 @@ namespace Content.Server.GameObjects.Components.Body eventArgs.Target.PopupMessage(eventArgs.User, Loc.GetString("You can't fit it in!")); } } + + return true; } private void SendBodyPartListToUser(AfterInteractEventArgs eventArgs, IBody body) diff --git a/Content.Server/GameObjects/Components/Body/Part/BodyPartComponent.cs b/Content.Server/GameObjects/Components/Body/Part/BodyPartComponent.cs index 44c7cc5a7e..6a1c2e5220 100644 --- a/Content.Server/GameObjects/Components/Body/Part/BodyPartComponent.cs +++ b/Content.Server/GameObjects/Components/Body/Part/BodyPartComponent.cs @@ -99,12 +99,12 @@ namespace Content.Server.GameObjects.Components.Body.Part } } - public async Task AfterInteract(AfterInteractEventArgs eventArgs) + public async Task AfterInteract(AfterInteractEventArgs eventArgs) { // TODO BODY if (eventArgs.Target == null) { - return; + return false; } CloseAllSurgeryUIs(); @@ -116,6 +116,8 @@ namespace Content.Server.GameObjects.Components.Body.Part { SendSlots(eventArgs, body); } + + return true; } private void SendSlots(AfterInteractEventArgs eventArgs, IBody body) diff --git a/Content.Server/GameObjects/Components/Body/Surgery/SurgeryToolComponent.cs b/Content.Server/GameObjects/Components/Body/Surgery/SurgeryToolComponent.cs index ac1aaa3ad7..717bbccd99 100644 --- a/Content.Server/GameObjects/Components/Body/Surgery/SurgeryToolComponent.cs +++ b/Content.Server/GameObjects/Components/Body/Surgery/SurgeryToolComponent.cs @@ -50,16 +50,16 @@ namespace Content.Server.GameObjects.Components.Body.Surgery public IEntity? PerformerCache { get; private set; } - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) + async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { if (eventArgs.Target == null) { - return; + return false; } if (!eventArgs.User.TryGetComponent(out IActorComponent? actor)) { - return; + return false; } CloseAllSurgeryUIs(); @@ -101,20 +101,22 @@ namespace Content.Server.GameObjects.Components.Body.Surgery if (!part.SurgeryCheck(_surgeryType)) { NotUsefulPopup(); - return; + return true; } // ...do the surgery. if (part.AttemptSurgery(_surgeryType, part, this, eventArgs.User)) { - return; + return true; } // Log error if the surgery fails somehow. Logger.Debug($"Error when trying to perform surgery on ${nameof(IBodyPart)} {eventArgs.User.Name}"); throw new InvalidOperationException(); } + + return true; } public float BaseOperationTime { get => _baseOperateTime; set => _baseOperateTime = value; } diff --git a/Content.Server/GameObjects/Components/Botany/PlantHolderComponent.cs b/Content.Server/GameObjects/Components/Botany/PlantHolderComponent.cs index ec9fa8b951..64712083ab 100644 --- a/Content.Server/GameObjects/Components/Botany/PlantHolderComponent.cs +++ b/Content.Server/GameObjects/Components/Botany/PlantHolderComponent.cs @@ -12,6 +12,7 @@ using Content.Server.Utility; using Content.Shared.Audio; using Content.Shared.Chemistry; using Content.Shared.GameObjects.Components.Botany; +using Content.Shared.GameObjects.Components.Chemistry; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems.ActionBlocker; using Content.Shared.Interfaces; @@ -718,23 +719,28 @@ namespace Content.Server.GameObjects.Components.Botany return true; } - if (usingItem.TryGetComponent(out SolutionContainerComponent? solution) && solution.CanRemoveSolutions) + if (usingItem.TryGetComponent(out ISolutionInteractionsComponent? solution) && solution.CanDrain) { - var amount = 5f; + var amount = ReagentUnit.New(5); var sprayed = false; if (usingItem.TryGetComponent(out SprayComponent? spray)) { sprayed = true; - amount = 1f; + amount = ReagentUnit.New(1); EntitySystem.Get().PlayFromEntity(spray.SpraySound, usingItem, AudioHelpers.WithVariation(0.125f)); } - var chemAmount = ReagentUnit.New(amount); + var split = solution.Drain(amount); + if (split.TotalVolume == 0) + { + user.PopupMessageCursor(Loc.GetString("{0:TheName} is empty!", usingItem)); + return true; + } - var split = solution.Solution.SplitSolution(chemAmount <= solution.Solution.TotalVolume ? chemAmount : solution.Solution.TotalVolume); - - user.PopupMessageCursor(Loc.GetString(sprayed ? $"You spray {Owner.Name} with {usingItem.Name}." : $"You transfer {split.TotalVolume.ToString()}u to {Owner.Name}")); + user.PopupMessageCursor(Loc.GetString( + sprayed ? "You spray {0:TheName}" : "You transfer {1}u to {0:TheName}", + Owner, split.TotalVolume)); _solutionContainer?.TryAddSolution(split); diff --git a/Content.Server/GameObjects/Components/Chemistry/HyposprayComponent.cs b/Content.Server/GameObjects/Components/Chemistry/HyposprayComponent.cs index 8b88d21d4b..7783b38f72 100644 --- a/Content.Server/GameObjects/Components/Chemistry/HyposprayComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/HyposprayComponent.cs @@ -52,10 +52,12 @@ namespace Content.Server.GameObjects.Components.Chemistry return TryDoInject(target, user); } - Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) + async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { - TryDoInject(eventArgs.Target, eventArgs.User); - return Task.CompletedTask; + if (!eventArgs.CanReach) + return false; + + return TryDoInject(eventArgs.Target, eventArgs.User); } private bool TryDoInject(IEntity? target, IEntity user) diff --git a/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs b/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs index 275fdc9342..fa23f8c0de 100644 --- a/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs @@ -28,21 +28,18 @@ namespace Content.Server.GameObjects.Components.Chemistry /// Whether or not the injector is able to draw from containers or if it's a single use /// device that can only inject. /// - [ViewVariables] - private bool _injectOnly; + [ViewVariables] private bool _injectOnly; /// /// Amount to inject or draw on each usage. If the injector is inject only, it will /// attempt to inject it's entire contents upon use. /// - [ViewVariables] - private ReagentUnit _transferAmount; + [ViewVariables] private ReagentUnit _transferAmount; /// /// Initial storage volume of the injector /// - [ViewVariables] - private ReagentUnit _initialMaxVolume; + [ViewVariables] private ReagentUnit _initialMaxVolume; private InjectorToggleMode _toggleState; @@ -68,19 +65,14 @@ namespace Content.Server.GameObjects.Components.Chemistry serializer.DataField(ref _injectOnly, "injectOnly", false); serializer.DataField(ref _initialMaxVolume, "initialMaxVolume", ReagentUnit.New(15)); serializer.DataField(ref _transferAmount, "transferAmount", ReagentUnit.New(5)); - serializer.DataField(ref _toggleState, "toggleState", _injectOnly ? InjectorToggleMode.Inject : InjectorToggleMode.Draw); + serializer.DataField(ref _toggleState, "toggleState", + _injectOnly ? InjectorToggleMode.Inject : InjectorToggleMode.Draw); } protected override void Startup() { base.Startup(); - var solution = Owner.EnsureComponent(); - solution.Capabilities = SolutionContainerCaps.AddTo | SolutionContainerCaps.RemoveFrom; - - // Set _toggleState based on prototype - _toggleState = _injectOnly ? InjectorToggleMode.Inject : InjectorToggleMode.Draw; - Dirty(); } @@ -116,58 +108,55 @@ namespace Content.Server.GameObjects.Components.Chemistry /// Called when clicking on entities while holding in active hand /// /// - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) + async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { - if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return; + if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) + return false; //Make sure we have the attacking entity - if (eventArgs.Target == null || !Owner.TryGetComponent(out SolutionContainerComponent? solution)) + if (eventArgs.Target == null || !Owner.HasComponent()) { - return; + return false; } var targetEntity = eventArgs.Target; // Handle injecting/drawing for solutions - if (targetEntity.TryGetComponent(out var targetSolution)) + if (targetEntity.TryGetComponent(out var targetSolution)) { if (ToggleState == InjectorToggleMode.Inject) { - if (solution.CanRemoveSolutions && targetSolution.CanAddSolutions) + if (targetSolution.CanInject) { TryInject(targetSolution, eventArgs.User); } else { - eventArgs.User.PopupMessage(eventArgs.User, Loc.GetString("You aren't able to transfer to {0:theName}!", targetSolution.Owner)); + eventArgs.User.PopupMessage(eventArgs.User, + Loc.GetString("You aren't able to transfer to {0:theName}!", targetSolution.Owner)); } } else if (ToggleState == InjectorToggleMode.Draw) { - if (targetSolution.CanRemoveSolutions && solution.CanAddSolutions) + if (targetSolution.CanDraw) { TryDraw(targetSolution, eventArgs.User); } else { - eventArgs.User.PopupMessage(eventArgs.User, Loc.GetString("You aren't able to draw from {0:theName}!", targetSolution.Owner)); + eventArgs.User.PopupMessage(eventArgs.User, + Loc.GetString("You aren't able to draw from {0:theName}!", targetSolution.Owner)); } } } - else // Handle injecting into bloodstream + // Handle injecting into bloodstream + else if (targetEntity.TryGetComponent(out BloodstreamComponent? bloodstream) && + ToggleState == InjectorToggleMode.Inject) { - if (targetEntity.TryGetComponent(out BloodstreamComponent? bloodstream) && ToggleState == InjectorToggleMode.Inject) - { - if (solution.CanRemoveSolutions) - { - TryInjectIntoBloodstream(bloodstream, eventArgs.User); - } - else - { - eventArgs.User.PopupMessage(eventArgs.User, Loc.GetString("You aren't able to inject {0:theName}!", targetEntity)); - } - } + TryInjectIntoBloodstream(bloodstream, eventArgs.User); } + + return true; } /// @@ -193,7 +182,8 @@ namespace Content.Server.GameObjects.Components.Chemistry if (realTransferAmount <= 0) { - Owner.PopupMessage(user, Loc.GetString("You aren't able to inject {0:theName}!", targetBloodstream.Owner)); + Owner.PopupMessage(user, + Loc.GetString("You aren't able to inject {0:theName}!", targetBloodstream.Owner)); return; } @@ -213,12 +203,14 @@ namespace Content.Server.GameObjects.Components.Chemistry removedSolution.DoEntityReaction(targetBloodstream.Owner, ReactionMethod.Injection); - Owner.PopupMessage(user, Loc.GetString("You inject {0}u into {1:theName}!", removedSolution.TotalVolume, targetBloodstream.Owner)); + Owner.PopupMessage(user, + Loc.GetString("You inject {0}u into {1:theName}!", removedSolution.TotalVolume, + targetBloodstream.Owner)); Dirty(); AfterInject(); } - private void TryInject(SolutionContainerComponent targetSolution, IEntity user) + private void TryInject(ISolutionInteractionsComponent targetSolution, IEntity user) { if (!Owner.TryGetComponent(out SolutionContainerComponent? solution) || solution.CurrentVolume == 0) { @@ -226,7 +218,7 @@ namespace Content.Server.GameObjects.Components.Chemistry } // 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.InjectSpaceAvailable); if (realTransferAmount <= 0) { @@ -237,16 +229,12 @@ namespace Content.Server.GameObjects.Components.Chemistry // Move units from attackSolution to targetSolution var removedSolution = solution.SplitSolution(realTransferAmount); - if (!targetSolution.CanAddSolution(removedSolution)) - { - return; - } - removedSolution.DoEntityReaction(targetSolution.Owner, ReactionMethod.Injection); - targetSolution.TryAddSolution(removedSolution); + targetSolution.Inject(removedSolution); - Owner.PopupMessage(user, Loc.GetString("You transfer {0}u to {1:theName}", removedSolution.TotalVolume, targetSolution.Owner)); + Owner.PopupMessage(user, + Loc.GetString("You transfer {0}u to {1:theName}", removedSolution.TotalVolume, targetSolution.Owner)); Dirty(); AfterInject(); } @@ -260,7 +248,7 @@ namespace Content.Server.GameObjects.Components.Chemistry } } - private void TryDraw(SolutionContainerComponent targetSolution, IEntity user) + private void TryDraw(ISolutionInteractionsComponent targetSolution, IEntity user) { if (!Owner.TryGetComponent(out SolutionContainerComponent? solution) || solution.EmptyVolume == 0) { @@ -268,7 +256,7 @@ namespace Content.Server.GameObjects.Components.Chemistry } // Get transfer amount. May be smaller than _transferAmount if not enough room - var realTransferAmount = ReagentUnit.Min(_transferAmount, targetSolution.CurrentVolume); + var realTransferAmount = ReagentUnit.Min(_transferAmount, targetSolution.DrawAvailable); if (realTransferAmount <= 0) { @@ -277,14 +265,15 @@ namespace Content.Server.GameObjects.Components.Chemistry } // Move units from attackSolution to targetSolution - var removedSolution = targetSolution.SplitSolution(realTransferAmount); + var removedSolution = targetSolution.Draw(realTransferAmount); if (!solution.TryAddSolution(removedSolution)) { return; } - Owner.PopupMessage(user, Loc.GetString("Drew {0}u from {1:theName}", removedSolution.TotalVolume, targetSolution.Owner)); + Owner.PopupMessage(user, + Loc.GetString("Drew {0}u from {1:theName}", removedSolution.TotalVolume, targetSolution.Owner)); Dirty(); AfterDraw(); } diff --git a/Content.Server/GameObjects/Components/Chemistry/PillComponent.cs b/Content.Server/GameObjects/Components/Chemistry/PillComponent.cs index d96e27cc60..f333605dd1 100644 --- a/Content.Server/GameObjects/Components/Chemistry/PillComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/PillComponent.cs @@ -59,14 +59,15 @@ namespace Content.Server.GameObjects.Components.Chemistry } // Feeding someone else - public async Task AfterInteract(AfterInteractEventArgs eventArgs) + public async Task AfterInteract(AfterInteractEventArgs eventArgs) { if (eventArgs.Target == null) { - return; + return false; } TryUseFood(eventArgs.User, eventArgs.Target); + return true; } public override bool TryUseFood(IEntity user, IEntity target, UtensilComponent utensilUsed = null) diff --git a/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs b/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs deleted file mode 100644 index ac747c962a..0000000000 --- a/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System.Threading.Tasks; -using Content.Shared.Chemistry; -using Content.Shared.Interfaces; -using Content.Shared.Interfaces.GameObjects.Components; -using Robust.Shared.GameObjects; -using Robust.Shared.Localization; -using Robust.Shared.Serialization; -using Robust.Shared.ViewVariables; - -namespace Content.Server.GameObjects.Components.Chemistry -{ - /// - /// Gives an entity click behavior for pouring reagents into - /// other entities and being poured into. The entity must have - /// a SolutionComponent or DrinkComponent for this to work. - /// (DrinkComponent adds a SolutionComponent if one isn't present). - /// - [RegisterComponent] - class PourableComponent : Component, IInteractUsing - { - public override string Name => "Pourable"; - - private ReagentUnit _transferAmount; - - /// - /// The amount of solution to be transferred from this solution when clicking on other solutions with it. - /// - [ViewVariables(VVAccess.ReadWrite)] - public ReagentUnit TransferAmount - { - get => _transferAmount; - set => _transferAmount = value; - } - - public override void ExposeData(ObjectSerializer serializer) - { - base.ExposeData(serializer); - serializer.DataField(ref _transferAmount, "transferAmount", ReagentUnit.New(5.0)); - } - - /// - /// Called when the owner of this component is clicked on with another entity. - /// The owner of this component is the target. - /// The entity used to click on this one is the attacker. - /// - /// Attack event args - /// - async Task IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs) - { - //Get target solution component - if (!Owner.TryGetComponent(out var targetSolution)) - return false; - - //Get attack solution component - var attackEntity = eventArgs.Using; - if (!attackEntity.TryGetComponent(out var attackSolution)) - return false; - - // Calculate possibe solution transfer - if (targetSolution.CanAddSolutions && attackSolution.CanRemoveSolutions) - { - // default logic (beakers and glasses) - // transfer solution from object in hand to attacked - return TryTransfer(eventArgs, attackSolution, targetSolution); - } - else if (targetSolution.CanRemoveSolutions && attackSolution.CanAddSolutions) - { - // storage tanks and sinks logic - // drain solution from attacked object to object in hand - return TryTransfer(eventArgs, targetSolution, attackSolution); - } - - // No transfer possible - return false; - } - - bool TryTransfer(InteractUsingEventArgs eventArgs, SolutionContainerComponent fromSolution, SolutionContainerComponent toSolution) - { - var fromEntity = fromSolution.Owner; - - if (!fromEntity.TryGetComponent(out var fromPourable)) - { - return false; - } - - //Get transfer amount. May be smaller than _transferAmount if not enough room - var realTransferAmount = ReagentUnit.Min(fromPourable.TransferAmount, toSolution.EmptyVolume); - - if (realTransferAmount <= 0) // Special message if container is full - { - Owner.PopupMessage(eventArgs.User, Loc.GetString("{0:theName} is full!", toSolution.Owner)); - return false; - } - - //Move units from attackSolution to targetSolution - var removedSolution = fromSolution.SplitSolution(realTransferAmount); - - if (removedSolution.TotalVolume <= ReagentUnit.Zero) - { - return false; - } - - if (!toSolution.TryAddSolution(removedSolution)) - { - return false; - } - - Owner.PopupMessage(eventArgs.User, Loc.GetString("You transfer {0}u to {1:theName}.", removedSolution.TotalVolume, toSolution.Owner)); - - return true; - } - } -} diff --git a/Content.Server/GameObjects/Components/Chemistry/ReagentTankComponent.cs b/Content.Server/GameObjects/Components/Chemistry/ReagentTankComponent.cs new file mode 100644 index 0000000000..0f654380fb --- /dev/null +++ b/Content.Server/GameObjects/Components/Chemistry/ReagentTankComponent.cs @@ -0,0 +1,35 @@ +using Content.Shared.Chemistry; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; + +#nullable enable + +namespace Content.Server.GameObjects.Components.Chemistry +{ + [RegisterComponent] + public class ReagentTankComponent : Component + { + public override string Name => "ReagentTank"; + + [ViewVariables(VVAccess.ReadWrite)] + public ReagentUnit TransferAmount { get; set; } + + [ViewVariables(VVAccess.ReadWrite)] + public ReagentTankType TankType { get; set; } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(this, c => c.TransferAmount, "transferAmount", ReagentUnit.New(10)); + serializer.DataField(this, c => c.TankType, "tankType", ReagentTankType.Unspecified); + } + } + + public enum ReagentTankType : byte + { + Unspecified, + Fuel + } +} diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionContainerComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionContainerComponent.cs index d60b1dc0fb..36ff877874 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionContainerComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionContainerComponent.cs @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable using Content.Server.Administration; using Content.Server.Eui; using Content.Server.GameObjects.Components.GUI; @@ -6,6 +6,7 @@ using Content.Shared.Administration; using Content.Shared.Chemistry; using Content.Shared.Eui; using Content.Shared.GameObjects.Components.Chemistry; +using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems.ActionBlocker; using Content.Shared.GameObjects.Verbs; using Robust.Server.Interfaces.GameObjects; @@ -19,213 +20,8 @@ namespace Content.Server.GameObjects.Components.Chemistry { [RegisterComponent] [ComponentReference(typeof(SharedSolutionContainerComponent))] + [ComponentReference(typeof(ISolutionInteractionsComponent))] public class SolutionContainerComponent : SharedSolutionContainerComponent { - /// - /// Transfers solution from the held container to the target container. - /// - [Verb] - private sealed class FillTargetVerb : Verb - { - protected override void GetData(IEntity user, SolutionContainerComponent component, VerbData data) - { - if (!ActionBlockerSystem.CanInteract(user) || - !user.TryGetComponent(out var hands) || - hands.GetActiveHand == null || - hands.GetActiveHand.Owner == component.Owner || - !hands.GetActiveHand.Owner.TryGetComponent(out var solution) || - !solution.CanRemoveSolutions || - !component.CanAddSolutions) - { - data.Visibility = VerbVisibility.Invisible; - return; - } - - var heldEntityName = hands.GetActiveHand.Owner?.Prototype?.Name ?? ""; - var myName = component.Owner.Prototype?.Name ?? ""; - - var locHeldEntityName = Loc.GetString(heldEntityName); - var locMyName = Loc.GetString(myName); - - data.Visibility = VerbVisibility.Visible; - data.Text = Loc.GetString("Transfer liquid from [{0}] to [{1}].", locHeldEntityName, locMyName); - } - - protected override void Activate(IEntity user, SolutionContainerComponent component) - { - if (!user.TryGetComponent(out var hands) || hands.GetActiveHand == null) - { - 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)); - - if (transferQuantity <= 0) - { - return; - } - - var transferSolution = handSolutionComp.SplitSolution(transferQuantity); - component.TryAddSolution(transferSolution); - } - } - - /// - /// Transfers solution from a target container to the held container. - /// - [Verb] - private sealed class EmptyTargetVerb : Verb - { - protected override void GetData(IEntity user, SolutionContainerComponent component, VerbData data) - { - if (!ActionBlockerSystem.CanInteract(user) || - !user.TryGetComponent(out var hands) || - hands.GetActiveHand == null || - hands.GetActiveHand.Owner == component.Owner || - !hands.GetActiveHand.Owner.TryGetComponent(out var solution) || - !solution.CanAddSolutions || - !component.CanRemoveSolutions) - { - data.Visibility = VerbVisibility.Invisible; - return; - } - - var heldEntityName = hands.GetActiveHand.Owner?.Prototype?.Name ?? ""; - var myName = component.Owner.Prototype?.Name ?? ""; - - var locHeldEntityName = Loc.GetString(heldEntityName); - var locMyName = Loc.GetString(myName); - - data.Visibility = VerbVisibility.Visible; - data.Text = Loc.GetString("Transfer liquid from [{0}] to [{1}].", locMyName, locHeldEntityName); - return; - } - - protected override void Activate(IEntity user, SolutionContainerComponent component) - { - if (!user.TryGetComponent(out var hands) || hands.GetActiveHand == null) - { - return; - } - - if (!hands.GetActiveHand.Owner.TryGetComponent(out var handSolutionComp) || - !handSolutionComp.CanAddSolutions || - !component.CanRemoveSolutions) - { - return; - } - - var transferQuantity = ReagentUnit.Min(handSolutionComp.MaxVolume - handSolutionComp.CurrentVolume, - component.CurrentVolume, ReagentUnit.New(10)); - - if (transferQuantity <= 0) - { - return; - } - - var transferSolution = component.SplitSolution(transferQuantity); - handSolutionComp.TryAddSolution(transferSolution); - } - } - - [Verb] - private sealed class AdminAddReagentVerb : Verb - { - private const AdminFlags ReqFlags = AdminFlags.Fun; - - protected override void GetData(IEntity user, SolutionContainerComponent component, VerbData data) - { - data.Text = Loc.GetString("Add Reagent..."); - data.CategoryData = VerbCategories.Debug; - data.Visibility = VerbVisibility.Invisible; - - var adminManager = IoCManager.Resolve(); - - if (user.TryGetComponent(out var player)) - { - if (adminManager.HasAdminFlag(player.playerSession, ReqFlags)) - { - data.Visibility = VerbVisibility.Visible; - } - } - } - - protected override void Activate(IEntity user, SolutionContainerComponent component) - { - var groupController = IoCManager.Resolve(); - if (user.TryGetComponent(out var player)) - { - if (groupController.HasAdminFlag(player.playerSession, ReqFlags)) - OpenAddReagentMenu(player.playerSession, component); - } - } - - private static void OpenAddReagentMenu(IPlayerSession player, SolutionContainerComponent comp) - { - var euiMgr = IoCManager.Resolve(); - euiMgr.OpenEui(new AdminAddReagentEui(comp), player); - } - - private sealed class AdminAddReagentEui : BaseEui - { - private readonly SolutionContainerComponent _target; - [Dependency] private readonly IAdminManager _adminManager = default!; - - public AdminAddReagentEui(SolutionContainerComponent target) - { - _target = target; - - IoCManager.InjectDependencies(this); - } - - public override void Opened() - { - StateDirty(); - } - - public override EuiStateBase GetNewState() - { - return new AdminAddReagentEuiState - { - CurVolume = _target.CurrentVolume, - MaxVolume = _target.MaxVolume - }; - } - - public override void HandleMessage(EuiMessageBase msg) - { - switch (msg) - { - case AdminAddReagentEuiMsg.Close: - Close(); - break; - case AdminAddReagentEuiMsg.DoAdd doAdd: - // Double check that user wasn't de-adminned in the mean time... - // Or the target was deleted. - if (!_adminManager.HasAdminFlag(Player, ReqFlags) || _target.Deleted) - { - Close(); - return; - } - - _target.TryAddReagent(doAdd.ReagentId, doAdd.Amount, out _); - StateDirty(); - - if (doAdd.CloseAfter) - Close(); - - break; - } - } - } - } } } diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionTransferComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionTransferComponent.cs new file mode 100644 index 0000000000..b0f43572eb --- /dev/null +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionTransferComponent.cs @@ -0,0 +1,144 @@ +#nullable enable +using System.Threading.Tasks; +using Content.Shared.Chemistry; +using Content.Shared.GameObjects.Components.Chemistry; +using Content.Shared.Interfaces; +using Content.Shared.Interfaces.GameObjects.Components; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Localization; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; + +namespace Content.Server.GameObjects.Components.Chemistry +{ + /// + /// Gives click behavior for transferring to/from other reagent containers. + /// + [RegisterComponent] + public sealed class SolutionTransferComponent : Component, IAfterInteract + { + // Behavior is as such: + // If it's a reagent tank, TAKE reagent. + // If it's anything else, GIVE reagent. + // Of course, only if possible. + + public override string Name => "SolutionTransfer"; + + private ReagentUnit _transferAmount; + private bool _canReceive; + private bool _canSend; + + /// + /// The amount of solution to be transferred from this solution when clicking on other solutions with it. + /// + [ViewVariables(VVAccess.ReadWrite)] + public ReagentUnit TransferAmount + { + get => _transferAmount; + set => _transferAmount = value; + } + + /// + /// Can this entity take reagent from reagent tanks? + /// + [ViewVariables(VVAccess.ReadWrite)] + public bool CanReceive + { + get => _canReceive; + set => _canReceive = value; + } + + /// + /// Can this entity give reagent to other reagent containers? + /// + [ViewVariables(VVAccess.ReadWrite)] + public bool CanSend + { + get => _canSend; + set => _canSend = value; + } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + serializer.DataField(ref _transferAmount, "transferAmount", ReagentUnit.New(5)); + serializer.DataField(ref _canReceive, "canReceive", true); + serializer.DataField(ref _canSend, "canSend", true); + } + + async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) + { + if (!eventArgs.CanReach || eventArgs.Target == null) + return false; + + if (!Owner.TryGetComponent(out ISolutionInteractionsComponent? ownerSolution)) + return false; + + var target = eventArgs.Target; + if (!target.TryGetComponent(out ISolutionInteractionsComponent? targetSolution)) + { + return false; + } + + if (CanReceive && target.TryGetComponent(out ReagentTankComponent? tank) + && ownerSolution.CanRefill && targetSolution.CanDrain) + { + var transferred = DoTransfer(targetSolution, ownerSolution, tank.TransferAmount, eventArgs.User); + if (transferred > 0) + { + var toTheBrim = ownerSolution.RefillSpaceAvailable == 0; + var msg = toTheBrim + ? "You fill {0:TheName} to the brim with {1}u from {2:theName}" + : "You fill {0:TheName} with {1}u from {2:theName}"; + + target.PopupMessage(eventArgs.User, Loc.GetString(msg, Owner, transferred, target)); + return true; + } + } + + if (CanSend && targetSolution.CanRefill && ownerSolution.CanDrain) + { + var transferred = DoTransfer(ownerSolution, targetSolution, TransferAmount, eventArgs.User); + + if (transferred > 0) + { + Owner.PopupMessage(eventArgs.User, Loc.GetString("You transfer {0}u to {1:theName}.", + transferred, target)); + + return true; + } + } + + return true; + } + + /// The actual amount transferred. + private static ReagentUnit DoTransfer( + ISolutionInteractionsComponent source, + ISolutionInteractionsComponent target, + ReagentUnit amount, + IEntity user) + { + if (source.DrainAvailable == 0) + { + source.Owner.PopupMessage(user, Loc.GetString("{0:TheName} is empty!", source.Owner)); + return ReagentUnit.Zero; + } + + if (target.RefillSpaceAvailable == 0) + { + target.Owner.PopupMessage(user, Loc.GetString("{0:TheName} is full!", target.Owner)); + return ReagentUnit.Zero; + } + + var actualAmount = + ReagentUnit.Min(amount, ReagentUnit.Min(source.DrainAvailable, target.RefillSpaceAvailable)); + + var solution = source.Drain(actualAmount); + target.Refill(solution); + + return actualAmount; + } + } +} diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionTransferVerbs.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionTransferVerbs.cs new file mode 100644 index 0000000000..8715ed027a --- /dev/null +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionTransferVerbs.cs @@ -0,0 +1,282 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Server.Administration; +using Content.Server.Eui; +using Content.Server.GameObjects.Components.GUI; +using Content.Shared.Administration; +using Content.Shared.Chemistry; +using Content.Shared.Eui; +using Content.Shared.GameObjects.Components.Chemistry; +using Content.Shared.GameObjects.EntitySystems.ActionBlocker; +using Content.Shared.GameObjects.Verbs; +using Robust.Server.Interfaces.GameObjects; +using Robust.Server.Interfaces.Player; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; + +#nullable enable + +namespace Content.Server.GameObjects.Components.Chemistry +{ + internal abstract class SolutionTransferVerbBase : GlobalVerb + { + protected static bool GetHeldSolution( + IEntity holder, + [NotNullWhen(true)] + out IEntity? held, + [NotNullWhen(true)] + out ISolutionInteractionsComponent? heldSolution) + { + if (!holder.TryGetComponent(out HandsComponent? hands) + || hands.GetActiveHand == null + || !hands.GetActiveHand.Owner.TryGetComponent(out heldSolution)) + { + held = null; + heldSolution = null; + return false; + } + + held = heldSolution.Owner; + return true; + } + } + + /// + /// Transfers solution from the held container to the target container. + /// + [GlobalVerb] + internal sealed class SolutionFillTargetVerb : SolutionTransferVerbBase + { + public override void GetData(IEntity user, IEntity target, VerbData data) + { + if (!target.TryGetComponent(out ISolutionInteractionsComponent? targetSolution) || + !ActionBlockerSystem.CanInteract(user) || + !GetHeldSolution(user, out var source, out var sourceSolution) || + source != target || + !sourceSolution.CanDrain || + !targetSolution.CanRefill) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + + data.Visibility = VerbVisibility.Visible; + data.Text = Loc.GetString("Transfer liquid from [{0}] to [{1}].", source.Name, target.Name); + } + + public override void Activate(IEntity user, IEntity target) + { + if (!GetHeldSolution(user, out _, out var handSolutionComp)) + { + return; + } + + if (!handSolutionComp.CanDrain || + !target.TryGetComponent(out ISolutionInteractionsComponent? targetComp) || + !targetComp.CanRefill) + { + return; + } + + var transferQuantity = ReagentUnit.Min( + targetComp.RefillSpaceAvailable, + handSolutionComp.DrainAvailable, + ReagentUnit.New(10)); + + if (transferQuantity <= 0) + { + return; + } + + var transferSolution = handSolutionComp.Drain(transferQuantity); + targetComp.Refill(transferSolution); + } + } + + /// + /// Transfers solution from a target container to the held container. + /// + [GlobalVerb] + internal sealed class SolutionDrainTargetVerb : SolutionTransferVerbBase + { + public override void GetData(IEntity user, IEntity target, VerbData data) + { + if (!target.TryGetComponent(out ISolutionInteractionsComponent? sourceSolution) || + !ActionBlockerSystem.CanInteract(user) || + !GetHeldSolution(user, out var held, out var targetSolution) || + !sourceSolution.CanDrain || + !targetSolution.CanRefill) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + + data.Visibility = VerbVisibility.Visible; + data.Text = Loc.GetString("Transfer liquid from [{0}] to [{1}].", held.Name, target.Name); + } + + public override void Activate(IEntity user, IEntity target) + { + if (!GetHeldSolution(user, out _, out var targetComp)) + { + return; + } + + if (!targetComp.CanRefill || + !target.TryGetComponent(out ISolutionInteractionsComponent? sourceComp) || + !sourceComp.CanDrain) + { + return; + } + + var transferQuantity = ReagentUnit.Min( + targetComp.RefillSpaceAvailable, + sourceComp.DrainAvailable, + ReagentUnit.New(10)); + + if (transferQuantity <= 0) + { + return; + } + + var transferSolution = sourceComp.Drain(transferQuantity); + targetComp.Refill(transferSolution); + } + } + + [GlobalVerb] + internal sealed class AdminAddReagentVerb : GlobalVerb + { + public override bool RequireInteractionRange => false; + public override bool BlockedByContainers => false; + + private const AdminFlags ReqFlags = AdminFlags.Fun; + + private static void OpenAddReagentMenu(IPlayerSession player, IEntity target) + { + var euiMgr = IoCManager.Resolve(); + euiMgr.OpenEui(new AdminAddReagentEui(target), player); + } + + public override void GetData(IEntity user, IEntity target, VerbData data) + { + // 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)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + + data.Text = Loc.GetString("Add Reagent..."); + data.CategoryData = VerbCategories.Debug; + data.Visibility = VerbVisibility.Invisible; + + var adminManager = IoCManager.Resolve(); + + if (user.TryGetComponent(out var player)) + { + if (adminManager.HasAdminFlag(player.playerSession, ReqFlags)) + { + data.Visibility = VerbVisibility.Visible; + } + } + } + + public override void Activate(IEntity user, IEntity target) + { + var groupController = IoCManager.Resolve(); + if (user.TryGetComponent(out var player)) + { + if (groupController.HasAdminFlag(player.playerSession, ReqFlags)) + OpenAddReagentMenu(player.playerSession, target); + } + } + + private sealed class AdminAddReagentEui : BaseEui + { + private readonly IEntity _target; + [Dependency] private readonly IAdminManager _adminManager = default!; + + public AdminAddReagentEui(IEntity target) + { + _target = target; + + IoCManager.InjectDependencies(this); + } + + public override void Opened() + { + StateDirty(); + } + + public override EuiStateBase GetNewState() + { + if (_target.TryGetComponent(out SolutionContainerComponent? container)) + { + return new AdminAddReagentEuiState + { + CurVolume = container.CurrentVolume, + MaxVolume = container.MaxVolume + }; + } + + 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, + MaxVolume = ReagentUnit.Zero + }; + } + + public override void HandleMessage(EuiMessageBase msg) + { + switch (msg) + { + case AdminAddReagentEuiMsg.Close: + Close(); + break; + case AdminAddReagentEuiMsg.DoAdd doAdd: + // Double check that user wasn't de-adminned in the mean time... + // Or the target was deleted. + if (!_adminManager.HasAdminFlag(Player, ReqFlags) || _target.Deleted) + { + Close(); + return; + } + + var id = doAdd.ReagentId; + var amount = doAdd.Amount; + + if (_target.TryGetComponent(out SolutionContainerComponent? container)) + { + container.TryAddReagent(id, amount, out _); + } + else if (_target.TryGetComponent(out ISolutionInteractionsComponent? interactions)) + { + var solution = new Solution(id, amount); + interactions.Inject(solution); + } + + StateDirty(); + + if (doAdd.CloseAfter) + Close(); + + break; + } + } + } + } +} diff --git a/Content.Server/GameObjects/Components/CrayonComponent.cs b/Content.Server/GameObjects/Components/CrayonComponent.cs index 5c0067a4f7..d44644999a 100644 --- a/Content.Server/GameObjects/Components/CrayonComponent.cs +++ b/Content.Server/GameObjects/Components/CrayonComponent.cs @@ -109,19 +109,22 @@ namespace Content.Server.GameObjects.Components return false; } - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) + async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: false, popup: true, - collisionMask: Shared.Physics.CollisionGroup.MobImpassable)) return; + collisionMask: Shared.Physics.CollisionGroup.MobImpassable)) + { + return true; + } if (Charges <= 0) { eventArgs.User.PopupMessage(Loc.GetString("Not enough left.")); - return; + return true; } var entityManager = IoCManager.Resolve(); - + var entity = entityManager.SpawnEntity("CrayonDecal", eventArgs.ClickLocation); if (entity.TryGetComponent(out AppearanceComponent? appearance)) { @@ -138,6 +141,7 @@ namespace Content.Server.GameObjects.Components // Decrease "Ammo" Charges--; Dirty(); + return true; } void IDropped.Dropped(DroppedEventArgs eventArgs) diff --git a/Content.Server/GameObjects/Components/Culinary/UtensilComponent.cs b/Content.Server/GameObjects/Components/Culinary/UtensilComponent.cs index d6c8629c6b..01eaeb5068 100644 --- a/Content.Server/GameObjects/Components/Culinary/UtensilComponent.cs +++ b/Content.Server/GameObjects/Components/Culinary/UtensilComponent.cs @@ -107,9 +107,10 @@ namespace Content.Server.GameObjects.Components.Culinary serializer.DataField(ref _breakSound, "breakSound", "/Audio/Items/snap.ogg"); } - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) + async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { TryUseUtensil(eventArgs.User, eventArgs.Target); + return true; } private void TryUseUtensil(IEntity user, IEntity? target) diff --git a/Content.Server/GameObjects/Components/Fluids/MopComponent.cs b/Content.Server/GameObjects/Components/Fluids/MopComponent.cs index ad6e85b951..11fda8a54a 100644 --- a/Content.Server/GameObjects/Components/Fluids/MopComponent.cs +++ b/Content.Server/GameObjects/Components/Fluids/MopComponent.cs @@ -62,14 +62,16 @@ namespace Content.Server.GameObjects.Components.Fluids Owner.EnsureComponentWarn(out SolutionContainerComponent _); } - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) + async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { - if (!Owner.TryGetComponent(out SolutionContainerComponent? contents)) return; - if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return; + if (!Owner.TryGetComponent(out SolutionContainerComponent? contents)) + return false; + if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) + return false; if (CurrentVolume <= 0) { - return; + return true; } if (eventArgs.Target == null) @@ -77,12 +79,12 @@ namespace Content.Server.GameObjects.Components.Fluids // Drop the liquid on the mop on to the ground contents.SplitSolution(CurrentVolume).SpillAt(eventArgs.ClickLocation, "PuddleSmear"); - return; + return true; } if (!eventArgs.Target.TryGetComponent(out PuddleComponent? puddleComponent)) { - return; + return true; } // Essentially pickup either: // - _pickupAmount, @@ -101,7 +103,7 @@ namespace Content.Server.GameObjects.Components.Fluids } else { - return; + return true; } } else @@ -121,12 +123,12 @@ namespace Content.Server.GameObjects.Components.Fluids // Give some visual feedback shit's happening (for anyone who can't hear sound) Owner.PopupMessage(eventArgs.User, Loc.GetString("Swish")); - if (string.IsNullOrWhiteSpace(_pickupSound)) + if (!string.IsNullOrWhiteSpace(_pickupSound)) { - return; + EntitySystem.Get().PlayFromEntity(_pickupSound, Owner); } - EntitySystem.Get().PlayFromEntity(_pickupSound, Owner); + return true; } } } diff --git a/Content.Server/GameObjects/Components/Fluids/SpillableComponent.cs b/Content.Server/GameObjects/Components/Fluids/SpillableComponent.cs index 62b2a5ec6c..5026ede0e4 100644 --- a/Content.Server/GameObjects/Components/Fluids/SpillableComponent.cs +++ b/Content.Server/GameObjects/Components/Fluids/SpillableComponent.cs @@ -1,6 +1,5 @@ -using Content.Server.GameObjects.Components.Chemistry; -using Content.Shared.Chemistry; -using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Chemistry; +using Content.Shared.GameObjects.Components.Chemistry; using Content.Shared.GameObjects.EntitySystems.ActionBlocker; using Content.Shared.GameObjects.Verbs; using Content.Shared.Interfaces; @@ -24,34 +23,37 @@ namespace Content.Server.GameObjects.Components.Fluids protected override void GetData(IEntity user, SpillableComponent component, VerbData data) { if (!ActionBlockerSystem.CanInteract(user) || - !component.Owner.TryGetComponent(out SolutionContainerComponent solutionComponent) || - !solutionComponent.CanRemoveSolutions) + !component.Owner.TryGetComponent(out ISolutionInteractionsComponent solutionComponent) || + !solutionComponent.CanDrain) { data.Visibility = VerbVisibility.Invisible; return; } data.Text = Loc.GetString("Spill liquid"); - data.Visibility = solutionComponent.CurrentVolume > ReagentUnit.Zero ? VerbVisibility.Visible : VerbVisibility.Disabled; + data.Visibility = solutionComponent.DrainAvailable > ReagentUnit.Zero + ? VerbVisibility.Visible + : VerbVisibility.Disabled; } protected override void Activate(IEntity user, SpillableComponent component) { - if (component.Owner.TryGetComponent(out var solutionComponent)) + if (component.Owner.TryGetComponent(out var solutionComponent)) { - if (!solutionComponent.CanRemoveSolutions) + if (!solutionComponent.CanDrain) { - user.PopupMessage(user, Loc.GetString("You can't pour anything from {0:theName}!", component.Owner)); + user.PopupMessage(user, + Loc.GetString("You can't pour anything from {0:theName}!", component.Owner)); } - if (solutionComponent.CurrentVolume.Float() <= 0) + if (solutionComponent.DrainAvailable <= 0) { user.PopupMessage(user, Loc.GetString("{0:theName} is empty!", component.Owner)); } // Need this as when we split the component's owner may be deleted var entityLocation = component.Owner.Transform.Coordinates; - var solution = solutionComponent.SplitSolution(solutionComponent.CurrentVolume); + var solution = solutionComponent.Drain(solutionComponent.DrainAvailable); solution.SpillAt(entityLocation, "PuddleSmear"); } } diff --git a/Content.Server/GameObjects/Components/Fluids/SprayComponent.cs b/Content.Server/GameObjects/Components/Fluids/SprayComponent.cs index 8a45830637..1d020093b4 100644 --- a/Content.Server/GameObjects/Components/Fluids/SprayComponent.cs +++ b/Content.Server/GameObjects/Components/Fluids/SprayComponent.cs @@ -100,34 +100,34 @@ namespace Content.Server.GameObjects.Components.Fluids serializer.DataField(ref _safety, "safety", true); } - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) + async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { if (!ActionBlockerSystem.CanInteract(eventArgs.User)) - return; + return false; if (_hasSafety && _safety) { Owner.PopupMessage(eventArgs.User, Loc.GetString("Its safety is on!")); - return; + return true; } if (CurrentVolume <= 0) { Owner.PopupMessage(eventArgs.User, Loc.GetString("It's empty!")); - return; + return true; } var curTime = _gameTiming.CurTime; if(curTime < _cooldownEnd) - return; + return true; var playerPos = eventArgs.User.Transform.Coordinates; if (eventArgs.ClickLocation.GetGridId(_serverEntityManager) != playerPos.GetGridId(_serverEntityManager)) - return; + return true; if (!Owner.TryGetComponent(out SolutionContainerComponent contents)) - return; + return true; var direction = (eventArgs.ClickLocation.Position - playerPos.Position).Normalized; var threeQuarters = direction * 0.75f; @@ -183,6 +183,8 @@ namespace Content.Server.GameObjects.Components.Fluids cooldown.CooldownStart = _lastUseTime; cooldown.CooldownEnd = _cooldownEnd; } + + return true; } public bool UseEntity(UseEntityEventArgs eventArgs) diff --git a/Content.Server/GameObjects/Components/Interactable/TilePryingComponent.cs b/Content.Server/GameObjects/Components/Interactable/TilePryingComponent.cs index 3099b9779c..26edc3c903 100644 --- a/Content.Server/GameObjects/Components/Interactable/TilePryingComponent.cs +++ b/Content.Server/GameObjects/Components/Interactable/TilePryingComponent.cs @@ -21,9 +21,10 @@ namespace Content.Server.GameObjects.Components.Interactable public override string Name => "TilePrying"; private bool _toolComponentNeeded = true; - public async Task AfterInteract(AfterInteractEventArgs eventArgs) + public async Task AfterInteract(AfterInteractEventArgs eventArgs) { TryPryTile(eventArgs.User, eventArgs.ClickLocation); + return true; } public override void ExposeData(ObjectSerializer serializer) diff --git a/Content.Server/GameObjects/Components/Interactable/WelderComponent.cs b/Content.Server/GameObjects/Components/Interactable/WelderComponent.cs index 21776ab4e8..76348dbeff 100644 --- a/Content.Server/GameObjects/Components/Interactable/WelderComponent.cs +++ b/Content.Server/GameObjects/Components/Interactable/WelderComponent.cs @@ -2,6 +2,7 @@ using System; using System.Threading.Tasks; using Content.Server.Atmos; +using Content.Server.Explosions; using Content.Server.GameObjects.Components.Chemistry; using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.EntitySystems; @@ -10,12 +11,15 @@ using Content.Server.Interfaces.GameObjects; using Content.Server.Utility; using Content.Shared.Chemistry; using Content.Shared.GameObjects; +using Content.Shared.GameObjects.Components.Chemistry; using Content.Shared.GameObjects.Components.Interactable; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Interfaces; using Content.Shared.Interfaces.GameObjects.Components; using Robust.Server.GameObjects; +using Robust.Server.GameObjects.EntitySystems; using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Localization; @@ -29,7 +33,7 @@ namespace Content.Server.GameObjects.Components.Interactable [ComponentReference(typeof(ToolComponent))] [ComponentReference(typeof(IToolComponent))] [ComponentReference(typeof(IHotItem))] - public class WelderComponent : ToolComponent, IExamine, IUse, ISuicideAct, ISolutionChange, IHotItem + public class WelderComponent : ToolComponent, IExamine, IUse, ISuicideAct, ISolutionChange, IHotItem, IAfterInteract { [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!; @@ -293,5 +297,38 @@ namespace Content.Server.GameObjects.Components.Interactable } + async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) + { + if (eventArgs.Target == null || !eventArgs.CanReach) + { + return false; + } + + if (eventArgs.Target.TryGetComponent(out ReagentTankComponent? tank) + && tank.TankType == ReagentTankType.Fuel + && eventArgs.Target.TryGetComponent(out ISolutionInteractionsComponent? targetSolution) + && targetSolution.CanDrain + && _solutionComponent != null) + { + if (WelderLit) + { + // Oh no no + eventArgs.Target.SpawnExplosion(); + return true; + } + + var trans = ReagentUnit.Min(_solutionComponent.EmptyVolume, targetSolution.DrainAvailable); + if (trans > 0) + { + var drained = targetSolution.Drain(trans); + _solutionComponent.TryAddSolution(drained); + + EntitySystem.Get().PlayFromEntity("/Audio/Effects/refill.ogg", Owner); + eventArgs.Target.PopupMessage(eventArgs.User, Loc.GetString("Welder refueled")); + } + } + + return true; + } } } diff --git a/Content.Server/GameObjects/Components/Items/FireExtinguisherComponent.cs b/Content.Server/GameObjects/Components/Items/FireExtinguisherComponent.cs index 6159aa7f52..3edcd5b392 100644 --- a/Content.Server/GameObjects/Components/Items/FireExtinguisherComponent.cs +++ b/Content.Server/GameObjects/Components/Items/FireExtinguisherComponent.cs @@ -1,10 +1,52 @@ -using Robust.Shared.GameObjects; +using System.Threading.Tasks; +using Content.Server.GameObjects.Components.Chemistry; +using Content.Shared.Chemistry; +using Content.Shared.GameObjects.Components.Chemistry; +using Content.Shared.Interfaces; +using Content.Shared.Interfaces.GameObjects.Components; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Localization; + +#nullable enable namespace Content.Server.GameObjects.Components.Items { [RegisterComponent] - public class FireExtinguisherComponent : Component + public class FireExtinguisherComponent : Component, IAfterInteract { public override string Name => "FireExtinguisher"; + + // Higher priority than sprays. + int IAfterInteract.Priority => 1; + + async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) + { + 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 trans = ReagentUnit.Min(container.EmptyVolume, targetSolution.DrainAvailable); + if (trans > 0) + { + var drained = targetSolution.Drain(trans); + container.TryAddSolution(drained); + + EntitySystem.Get().PlayFromEntity("/Audio/Effects/refill.ogg", Owner); + eventArgs.Target.PopupMessage(eventArgs.User, Loc.GetString("{0:TheName} is now refilled", Owner)); + } + + return true; + } + + return false; + } } } diff --git a/Content.Server/GameObjects/Components/Items/FloorTileItemComponent.cs b/Content.Server/GameObjects/Components/Items/FloorTileItemComponent.cs index 57969eda49..341cdca33b 100644 --- a/Content.Server/GameObjects/Components/Items/FloorTileItemComponent.cs +++ b/Content.Server/GameObjects/Components/Items/FloorTileItemComponent.cs @@ -56,10 +56,14 @@ namespace Content.Server.GameObjects.Components.Items EntitySystem.Get().PlayAtCoords("/Audio/Items/genhit.ogg", location, AudioHelpers.WithVariation(0.125f)); } - public async Task AfterInteract(AfterInteractEventArgs eventArgs) + public async Task AfterInteract(AfterInteractEventArgs eventArgs) { - if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return; - if (!Owner.TryGetComponent(out StackComponent stack)) return; + if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) + return true; + + if (!Owner.TryGetComponent(out StackComponent stack)) + return true; + var mapManager = IoCManager.Resolve(); var location = eventArgs.ClickLocation.AlignWithClosestGridTile(); @@ -88,10 +92,9 @@ namespace Content.Server.GameObjects.Components.Items PlaceAt(mapGrid, location, _tileDefinitionManager[_outputTiles[0]].TileId, mapGrid.TileSize / 2f); break; } - - } + return true; } } } diff --git a/Content.Server/GameObjects/Components/Items/RCD/RCDAmmoComponent.cs b/Content.Server/GameObjects/Components/Items/RCD/RCDAmmoComponent.cs index b79f422205..a06aa830b6 100644 --- a/Content.Server/GameObjects/Components/Items/RCD/RCDAmmoComponent.cs +++ b/Content.Server/GameObjects/Components/Items/RCD/RCDAmmoComponent.cs @@ -32,17 +32,17 @@ namespace Content.Server.GameObjects.Components.Items.RCD message.AddMarkup(Loc.GetString("It holds {0} charges.", refillAmmo)); } - public async Task AfterInteract(AfterInteractEventArgs eventArgs) + public async Task AfterInteract(AfterInteractEventArgs eventArgs) { if (eventArgs.Target == null || !eventArgs.Target.TryGetComponent(out RCDComponent rcdComponent) || !eventArgs.User.TryGetComponent(out IHandsComponent hands)) { - return; + return false; } if (rcdComponent.maxAmmo - rcdComponent._ammo < refillAmmo) { rcdComponent.Owner.PopupMessage(eventArgs.User, Loc.GetString("The RCD is full!")); - return; + return true; } rcdComponent._ammo = Math.Min(rcdComponent.maxAmmo, rcdComponent._ammo + refillAmmo); @@ -51,6 +51,7 @@ namespace Content.Server.GameObjects.Components.Items.RCD //Deleting a held item causes a lot of errors hands.Drop(Owner, false); Owner.Delete(); + return true; } } } diff --git a/Content.Server/GameObjects/Components/Items/RCD/RCDComponent.cs b/Content.Server/GameObjects/Components/Items/RCD/RCDComponent.cs index bef07051db..393a3780d8 100644 --- a/Content.Server/GameObjects/Components/Items/RCD/RCDComponent.cs +++ b/Content.Server/GameObjects/Components/Items/RCD/RCDComponent.cs @@ -94,7 +94,7 @@ namespace Content.Server.GameObjects.Components.Items.RCD message.AddMarkup(Loc.GetString("It's currently on {0} mode, and holds {1} charges.",_mode.ToString(), _ammo)); } - public async Task AfterInteract(AfterInteractEventArgs eventArgs) + public async Task AfterInteract(AfterInteractEventArgs eventArgs) { //No changing mode mid-RCD var startingMode = _mode; @@ -116,7 +116,7 @@ namespace Content.Server.GameObjects.Components.Items.RCD var result = await doAfterSystem.DoAfter(doAfterEventArgs); if (result == DoAfterStatus.Cancelled) { - return; + return true; } switch (_mode) @@ -146,12 +146,12 @@ namespace Content.Server.GameObjects.Components.Items.RCD airlock.Transform.LocalRotation = Owner.Transform.LocalRotation; //Now apply icon smoothing. break; default: - return; //I don't know why this would happen, but sure I guess. Get out of here invalid state! + return true; //I don't know why this would happen, but sure I guess. Get out of here invalid state! } _entitySystemManager.GetEntitySystem().PlayFromEntity("/Audio/Items/deconstruct.ogg", Owner); _ammo--; - + return true; } private bool IsRCDStillValid(AfterInteractEventArgs eventArgs, IMapGrid mapGrid, TileRef tile, Vector2i snapPos, RcdMode startingMode) diff --git a/Content.Server/GameObjects/Components/Kitchen/MicrowaveComponent.cs b/Content.Server/GameObjects/Components/Kitchen/MicrowaveComponent.cs index 60aca373e1..4009844a5b 100644 --- a/Content.Server/GameObjects/Components/Kitchen/MicrowaveComponent.cs +++ b/Content.Server/GameObjects/Components/Kitchen/MicrowaveComponent.cs @@ -13,6 +13,7 @@ using Content.Server.Utility; using Content.Shared.Chemistry; using Content.Shared.GameObjects.Components.Body; using Content.Shared.GameObjects.Components.Body.Part; +using Content.Shared.GameObjects.Components.Chemistry; using Content.Shared.GameObjects.Components.Power; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Interfaces; @@ -213,10 +214,10 @@ namespace Content.Server.GameObjects.Components.Kitchen return false; } - if (itemEntity.TryGetComponent(out var attackPourable)) + if (itemEntity.TryGetComponent(out var attackPourable)) { - if (!itemEntity.TryGetComponent(out var attackSolution) - || !attackSolution.CanRemoveSolutions) + if (!itemEntity.TryGetComponent(out var attackSolution) + || !attackSolution.CanDrain) { return false; } @@ -235,7 +236,7 @@ namespace Content.Server.GameObjects.Components.Kitchen } //Move units from attackSolution to targetSolution - var removedSolution = attackSolution.SplitSolution(realTransferAmount); + var removedSolution = attackSolution.Drain(realTransferAmount); if (!solution.TryAddSolution(removedSolution)) { return false; diff --git a/Content.Server/GameObjects/Components/Medical/HealingComponent.cs b/Content.Server/GameObjects/Components/Medical/HealingComponent.cs index cb4684238a..abe0c0a83a 100644 --- a/Content.Server/GameObjects/Components/Medical/HealingComponent.cs +++ b/Content.Server/GameObjects/Components/Medical/HealingComponent.cs @@ -26,39 +26,41 @@ namespace Content.Server.GameObjects.Components.Medical serializer.DataField(this, h => h.Heal, "heal", new Dictionary()); } - public async Task AfterInteract(AfterInteractEventArgs eventArgs) + public async Task AfterInteract(AfterInteractEventArgs eventArgs) { if (eventArgs.Target == null) { - return; + return false; } if (!eventArgs.Target.TryGetComponent(out IDamageableComponent damageable)) { - return; + return true; } if (!ActionBlockerSystem.CanInteract(eventArgs.User)) { - return; + return true; } if (eventArgs.User != eventArgs.Target && !eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) { - return; + return true; } if (Owner.TryGetComponent(out StackComponent stack) && !stack.Use(1)) { - return; + return true; } foreach (var (type, amount) in Heal) { damageable.ChangeDamage(type, -amount, true); } + + return true; } } } diff --git a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs index 4990ac6075..cd41d3f2c5 100644 --- a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs @@ -3,11 +3,9 @@ using System.Threading.Tasks; using Content.Server.GameObjects.Components.Body.Behavior; using Content.Server.GameObjects.Components.Chemistry; using Content.Server.GameObjects.Components.Fluids; -using Content.Server.GameObjects.EntitySystems; using Content.Shared.Audio; using Content.Shared.Chemistry; using Content.Shared.GameObjects.Components.Body; -using Content.Shared.GameObjects.Components.Body.Mechanism; using Content.Shared.GameObjects.Components.Nutrition; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Interfaces; @@ -75,7 +73,7 @@ namespace Content.Server.GameObjects.Components.Nutrition _contents = Owner.AddComponent(); } - _contents.Capabilities = SolutionContainerCaps.AddTo | SolutionContainerCaps.RemoveFrom; + _contents.Capabilities = SolutionContainerCaps.Refillable | SolutionContainerCaps.Drainable; Opened = _defaultToOpened; UpdateAppearance(); } @@ -113,9 +111,10 @@ namespace Content.Server.GameObjects.Components.Nutrition } //Force feeding a drink to someone. - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) + async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { TryUseDrink(eventArgs.Target, forced: true); + return true; } public void Examine(FormattedMessage message, bool inDetailsRange) @@ -131,7 +130,7 @@ namespace Content.Server.GameObjects.Components.Nutrition private bool TryUseDrink(IEntity target, bool forced = false) { - if (target == null || !_contents.CanRemoveSolutions) + if (target == null || !_contents.CanDrain) { return false; } diff --git a/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs b/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs index b7318fdce1..0cb8436e11 100644 --- a/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs @@ -100,14 +100,15 @@ namespace Content.Server.GameObjects.Components.Nutrition } // Feeding someone else - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) + async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { if (eventArgs.Target == null) { - return; + return false; } TryUseFood(eventArgs.User, eventArgs.Target); + return true; } public virtual bool TryUseFood(IEntity? user, IEntity? target, UtensilComponent? utensilUsed = null) diff --git a/Content.Server/GameObjects/Components/Portal/TeleporterComponent.cs b/Content.Server/GameObjects/Components/Portal/TeleporterComponent.cs index f53d4b8f1d..b92632aa3b 100644 --- a/Content.Server/GameObjects/Components/Portal/TeleporterComponent.cs +++ b/Content.Server/GameObjects/Components/Portal/TeleporterComponent.cs @@ -23,7 +23,7 @@ using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Portal { [RegisterComponent] - public class TeleporterComponent : Component, IAfterInteract + public class TeleporterComponent : Component, IAfterInteract { [Dependency] private readonly IServerEntityManager _serverEntityManager = default!; [Dependency] private readonly IRobustRandom _spreadRandom = default!; @@ -78,7 +78,7 @@ namespace Content.Server.GameObjects.Components.Portal _state = newState; } - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) + async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { if (_teleporterType == TeleporterType.Directed) { @@ -89,6 +89,8 @@ namespace Content.Server.GameObjects.Components.Portal { TryRandomTeleport(eventArgs.User); } + + return true; } public void TryDirectedTeleport(IEntity user, MapCoordinates mapCoords) diff --git a/Content.Server/GameObjects/Components/Power/WirePlacerComponent.cs b/Content.Server/GameObjects/Components/Power/WirePlacerComponent.cs index 81d8654360..e3a33418ee 100644 --- a/Content.Server/GameObjects/Components/Power/WirePlacerComponent.cs +++ b/Content.Server/GameObjects/Components/Power/WirePlacerComponent.cs @@ -34,28 +34,29 @@ namespace Content.Server.GameObjects.Components.Power } /// - public async Task AfterInteract(AfterInteractEventArgs eventArgs) + public async Task AfterInteract(AfterInteractEventArgs eventArgs) { if (_wirePrototypeID == null) - return; - - if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return; + return true; + if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) + return true; if(!_mapManager.TryGetGrid(eventArgs.ClickLocation.GetGridId(Owner.EntityManager), out var grid)) - return; + return true; var snapPos = grid.SnapGridCellFor(eventArgs.ClickLocation, SnapGridOffset.Center); var snapCell = grid.GetSnapGridCell(snapPos, SnapGridOffset.Center); if(grid.GetTileRef(snapPos).Tile.IsEmpty) - return; + return true; foreach (var snapComp in snapCell) { if (snapComp.Owner.TryGetComponent(out var wire) && wire.WireType == _blockingWireType) { - return; + return true; } } if (Owner.TryGetComponent(out var stack) && !stack.Use(1)) - return; + return true; Owner.EntityManager.SpawnEntity(_wirePrototypeID, grid.GridTileToLocal(snapPos)); + return true; } } } diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Ammunition/SpeedLoaderComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Ammunition/SpeedLoaderComponent.cs index f674459c2a..5ddf430552 100644 --- a/Content.Server/GameObjects/Components/Weapon/Ranged/Ammunition/SpeedLoaderComponent.cs +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Ammunition/SpeedLoaderComponent.cs @@ -146,11 +146,11 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition return entity; } - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) + async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { if (eventArgs.Target == null) { - return; + return false; } // This area is dirty but not sure of an easier way to do it besides add an interface or somethin @@ -203,6 +203,8 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition { UpdateAppearance(); } + + return true; } async Task IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs) diff --git a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs index 552d3c43c5..40ee427b78 100644 --- a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs @@ -416,13 +416,8 @@ namespace Content.Server.GameObjects.EntitySystems.Click return; } - var afterInteracts = weapon.GetAllComponents().ToList(); var afterInteractEventArgs = new AfterInteractEventArgs(user, clickLocation, null, canReach); - - foreach (var afterInteract in afterInteracts) - { - await afterInteract.AfterInteract(afterInteractEventArgs); - } + await DoAfterInteract(weapon, afterInteractEventArgs); } /// @@ -465,13 +460,9 @@ namespace Content.Server.GameObjects.EntitySystems.Click } // If we aren't directly attacking the nearby object, lets see if our item has an after attack we can do - var afterAttacks = weapon.GetAllComponents().ToList(); var afterAttackEventArgs = new AfterInteractEventArgs(user, clickLocation, attacked, canReach: true); - foreach (var afterAttack in afterAttacks) - { - await afterAttack.AfterInteract(afterAttackEventArgs); - } + await DoAfterInteract(weapon, afterAttackEventArgs); } /// @@ -835,13 +826,21 @@ namespace Content.Server.GameObjects.EntitySystems.Click if (afterAtkMsg.Handled) return; - var afterAttacks = weapon.GetAllComponents().ToList(); + // See if we have a ranged attack interaction var afterAttackEventArgs = new AfterInteractEventArgs(user, clickLocation, attacked, canReach: false); + await DoAfterInteract(weapon, afterAttackEventArgs); + } + + private static async Task DoAfterInteract(IEntity weapon, AfterInteractEventArgs afterAttackEventArgs) + { + var afterAttacks = weapon.GetAllComponents().OrderByDescending(x => x.Priority).ToList(); - //See if we have a ranged attack interaction foreach (var afterAttack in afterAttacks) { - await afterAttack.AfterInteract(afterAttackEventArgs); + if (await afterAttack.AfterInteract(afterAttackEventArgs)) + { + return; + } } } diff --git a/Content.Shared/Chemistry/Solution.cs b/Content.Shared/Chemistry/Solution.cs index 7fb2315c1e..cd6d801210 100644 --- a/Content.Shared/Chemistry/Solution.cs +++ b/Content.Shared/Chemistry/Solution.cs @@ -92,7 +92,7 @@ namespace Content.Shared.Chemistry return ""; } - var majorReagent = Contents.OrderByDescending(reagent => reagent.Quantity).First(); ; + var majorReagent = Contents.OrderByDescending(reagent => reagent.Quantity).First(); return majorReagent.ReagentId; } diff --git a/Content.Shared/Chemistry/SolutionCaps.cs b/Content.Shared/Chemistry/SolutionCaps.cs deleted file mode 100644 index 3dada4d98e..0000000000 --- a/Content.Shared/Chemistry/SolutionCaps.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using Robust.Shared.Serialization; - -namespace Content.Shared.Chemistry -{ - /// - /// These are the defined capabilities of a container of a solution. - /// - [Flags] - [Serializable, NetSerializable] - public enum SolutionContainerCaps - { - None = 0, - - /// - /// Can solutions be added into the container? - /// - AddTo = 1, - - /// - /// Can solutions be removed from the container? - /// - RemoveFrom = 2, - - /// - /// 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 = 4, - - /// - /// Can people examine the solution in the container or is it impossible to see? - /// - CanExamine = 8, - } - - public static class SolutionContainerCapsHelpers - { - public static bool HasCap(this SolutionContainerCaps cap, SolutionContainerCaps other) - { - return (cap & other) == other; - } - } -} diff --git a/Content.Shared/Chemistry/SolutionContainerCaps.cs b/Content.Shared/Chemistry/SolutionContainerCaps.cs new file mode 100644 index 0000000000..47d8a76371 --- /dev/null +++ b/Content.Shared/Chemistry/SolutionContainerCaps.cs @@ -0,0 +1,62 @@ +using System; +using Content.Shared.GameObjects.Components.Chemistry; +using Robust.Shared.Serialization; + +namespace Content.Shared.Chemistry +{ + /// + /// 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/GameObjects/Components/Chemistry/ISolutionInteractionsComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/ISolutionInteractionsComponent.cs new file mode 100644 index 0000000000..c88c46234d --- /dev/null +++ b/Content.Shared/GameObjects/Components/Chemistry/ISolutionInteractionsComponent.cs @@ -0,0 +1,84 @@ +using Content.Shared.Chemistry; +using Robust.Shared.Interfaces.GameObjects; + +namespace Content.Shared.GameObjects.Components.Chemistry +{ + /// + /// 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; + + void Refill(Solution solution) + { + + } + + // + // DRAINING + // + + bool CanDrain => false; + ReagentUnit DrainAvailable => ReagentUnit.Zero; + + Solution Drain(ReagentUnit amount) + { + return new(); + } + } +} diff --git a/Content.Shared/GameObjects/Components/Chemistry/SharedSolutionContainerComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SharedSolutionContainerComponent.cs index e453f4aeda..0a7d1fe7d2 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SharedSolutionContainerComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SharedSolutionContainerComponent.cs @@ -20,7 +20,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// /// Holds a with a limited volume. /// - public abstract class SharedSolutionContainerComponent : Component, IExamine + public abstract class SharedSolutionContainerComponent : Component, IExamine, ISolutionInteractionsComponent { public override string Name => "SolutionContainer"; @@ -60,9 +60,11 @@ namespace Content.Shared.GameObjects.Components.Chemistry public bool CanUseWithChemDispenser => Capabilities.HasCap(SolutionContainerCaps.FitsInDispenser); - public bool CanAddSolutions => Capabilities.HasCap(SolutionContainerCaps.AddTo); + public bool CanInject => Capabilities.HasCap(SolutionContainerCaps.Injectable) || CanRefill; + public bool CanDraw => Capabilities.HasCap(SolutionContainerCaps.Drawable) || CanDrain; - public bool CanRemoveSolutions => Capabilities.HasCap(SolutionContainerCaps.RemoveFrom); + public bool CanRefill => Capabilities.HasCap(SolutionContainerCaps.Refillable); + public bool CanDrain => Capabilities.HasCap(SolutionContainerCaps.Drainable); public override void ExposeData(ObjectSerializer serializer) { @@ -71,7 +73,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry serializer.DataField(this, x => x.CanReact, "canReact", true); serializer.DataField(this, x => x.MaxVolume, "maxVol", ReagentUnit.New(0)); serializer.DataField(this, x => x.Solution, "contents", new Solution()); - serializer.DataField(this, x => x.Capabilities, "caps", SolutionContainerCaps.AddTo | SolutionContainerCaps.RemoveFrom | SolutionContainerCaps.CanExamine); + serializer.DataField(this, x => x.Capabilities, "caps", SolutionContainerCaps.None); } public void RemoveAllSolution() @@ -209,6 +211,43 @@ namespace Content.Shared.GameObjects.Components.Chemistry message.AddMarkup(Loc.GetString(messageString, colorHex, Loc.GetString(proto.PhysicalDescription))); } + ReagentUnit ISolutionInteractionsComponent.RefillSpaceAvailable => EmptyVolume; + ReagentUnit ISolutionInteractionsComponent.InjectSpaceAvailable => EmptyVolume; + ReagentUnit ISolutionInteractionsComponent.DrawAvailable => CurrentVolume; + ReagentUnit ISolutionInteractionsComponent.DrainAvailable => CurrentVolume; + + 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)) diff --git a/Content.Shared/Interfaces/GameObjects/Components/Interaction/IAfterInteract.cs b/Content.Shared/Interfaces/GameObjects/Components/Interaction/IAfterInteract.cs index 16299a23c9..716d0b3fd7 100644 --- a/Content.Shared/Interfaces/GameObjects/Components/Interaction/IAfterInteract.cs +++ b/Content.Shared/Interfaces/GameObjects/Components/Interaction/IAfterInteract.cs @@ -16,10 +16,16 @@ namespace Content.Shared.Interfaces.GameObjects.Components /// public interface IAfterInteract { + /// + /// The interaction priority. Higher numbers get called first. + /// + /// Priority defaults to 0 + int Priority => 0; + /// /// Called when we interact with nothing, or when we interact with an entity out of range that has no behavior /// - Task AfterInteract(AfterInteractEventArgs eventArgs); + Task AfterInteract(AfterInteractEventArgs eventArgs); } public class AfterInteractEventArgs : EventArgs diff --git a/Resources/Audio/Effects/refill.ogg b/Resources/Audio/Effects/refill.ogg new file mode 100644 index 0000000000..ec5c4dc344 Binary files /dev/null and b/Resources/Audio/Effects/refill.ogg differ diff --git a/Resources/Maps/saltern.yml b/Resources/Maps/saltern.yml index a97f8ac388..6c744ecaf1 100644 --- a/Resources/Maps/saltern.yml +++ b/Resources/Maps/saltern.yml @@ -534,8 +534,6 @@ entities: pos: -15.694785,24.608267 rot: -1.5707963267948966 rad type: Transform - - caps: AddTo, RemoveFrom - type: SolutionContainer - uid: 60 type: DisgustingSweptSoup components: @@ -652,8 +650,6 @@ entities: pos: -3.470539,16.956116 rot: -1.5707963267948966 rad type: Transform - - caps: AddTo, RemoveFrom - type: SolutionContainer - uid: 74 type: Carpet components: @@ -703,8 +699,6 @@ entities: pos: 8.439846,26.712742 rot: 1.5707963267948966 rad type: Transform - - caps: AddTo, RemoveFrom - type: SolutionContainer - uid: 81 type: Table components: @@ -929,8 +923,6 @@ entities: - parent: 853 pos: 8.661116,25.513401 type: Transform - - caps: AddTo, RemoveFrom - type: SolutionContainer - uid: 114 type: BoxDonkpocket components: @@ -32236,8 +32228,6 @@ entities: pos: -8.476567,-17.420076 rot: -1.5707963267948966 rad type: Transform - - caps: AddTo, RemoveFrom - type: SolutionContainer - uid: 2443 type: TableWood components: diff --git a/Resources/Prototypes/Entities/Constructible/Specific/Cooking/microwave.yml b/Resources/Prototypes/Entities/Constructible/Specific/Cooking/microwave.yml index 629bcfd5b3..cd7565c4db 100644 --- a/Resources/Prototypes/Entities/Constructible/Specific/Cooking/microwave.yml +++ b/Resources/Prototypes/Entities/Constructible/Specific/Cooking/microwave.yml @@ -12,7 +12,7 @@ - type: InteractionOutline - type: SolutionContainer maxVol: 100 - caps: AddTo + caps: Refillable - type: Appearance visuals: - type: MicrowaveVisualizer diff --git a/Resources/Prototypes/Entities/Constructible/Specific/hydroponics.yml b/Resources/Prototypes/Entities/Constructible/Specific/hydroponics.yml index 416b1324de..ab9faa97dd 100644 --- a/Resources/Prototypes/Entities/Constructible/Specific/hydroponics.yml +++ b/Resources/Prototypes/Entities/Constructible/Specific/hydroponics.yml @@ -38,8 +38,7 @@ drawWarnings: false - type: SolutionContainer maxVol: 200 - caps: AddTo - - type: Pourable + caps: Refillable - type: SnapGrid offset: Center - type: Appearance diff --git a/Resources/Prototypes/Entities/Constructible/Storage/StorageTanks/base_tank.yml b/Resources/Prototypes/Entities/Constructible/Storage/StorageTanks/base_tank.yml index 98d0b7efda..54110ab38d 100644 --- a/Resources/Prototypes/Entities/Constructible/Storage/StorageTanks/base_tank.yml +++ b/Resources/Prototypes/Entities/Constructible/Storage/StorageTanks/base_tank.yml @@ -33,9 +33,9 @@ acts: ["Destruction"] - type: SolutionContainer maxVol: 1500 - caps: RemoveFrom - - type: Pourable - transferAmount: 100.0 + caps: Drainable + - type: ReagentTank + placement: snap: - Wall diff --git a/Resources/Prototypes/Entities/Constructible/Storage/StorageTanks/fuel_tank.yml b/Resources/Prototypes/Entities/Constructible/Storage/StorageTanks/fuel_tank.yml index 5249cb04ea..f66808039f 100644 --- a/Resources/Prototypes/Entities/Constructible/Storage/StorageTanks/fuel_tank.yml +++ b/Resources/Prototypes/Entities/Constructible/Storage/StorageTanks/fuel_tank.yml @@ -17,9 +17,7 @@ reagents: - ReagentId: chem.WeldingFuel Quantity: 1500 - - type: DamageOnToolInteract - damage: 200 - tools: - - Welding - type: Anchorable - type: Pullable + - type: ReagentTank + tankType: Fuel diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index 22dc2ab80c..3f9254499c 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -20,7 +20,7 @@ # Organs - type: SolutionContainer maxVol: 250 - caps: AddTo, RemoveFrom + caps: Injectable, Drawable - type: Bloodstream max_volume: 100 # StatusEffects diff --git a/Resources/Prototypes/Entities/Objects/Consumable/drinks.yml b/Resources/Prototypes/Entities/Objects/Consumable/drinks.yml index c01489c8d5..545c343390 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/drinks.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/drinks.yml @@ -1,4 +1,4 @@ -# TODO: Find remaining cans and move to drinks_cans +# TODO: Find remaining cans and move to drinks_cans # TODO: Find empty containers (e.g. mug, pitcher) and move to their own yml # TODO: Move bottles to their own yml - type: entity @@ -8,7 +8,7 @@ components: - type: SolutionContainer maxVol: 50 - - type: Pourable + - type: SolutionTransfer transferAmount: 5 - type: Drink - type: Sprite @@ -55,7 +55,7 @@ - type: SolutionContainer fillingState: glass maxVol: 50 - - type: Pourable + - type: SolutionTransfer transferAmount: 5 - type: TransformableContainer diff --git a/Resources/Prototypes/Entities/Objects/Consumable/drinks_bottles.yml b/Resources/Prototypes/Entities/Objects/Consumable/drinks_bottles.yml index be7543e45c..1dce8418b5 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/drinks_bottles.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/drinks_bottles.yml @@ -7,7 +7,7 @@ openSounds: bottleOpenSounds - type: SolutionContainer maxVol: 100 - - type: Pourable + - type: SolutionTransfer transferAmount: 5 - type: Sprite state: icon diff --git a/Resources/Prototypes/Entities/Objects/Consumable/drinks_cans.yml b/Resources/Prototypes/Entities/Objects/Consumable/drinks_cans.yml index d1e0e53cd8..7c44c0888e 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/drinks_cans.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/drinks_cans.yml @@ -8,12 +8,12 @@ pressurized: true - type: SolutionContainer maxVol: 20 - caps: AddTo, RemoveFrom + caps: None contents: reagents: - ReagentId: chem.Cola Quantity: 20 - - type: Pourable + - type: SolutionTransfer transferAmount: 5 - type: Sprite state: icon diff --git a/Resources/Prototypes/Entities/Objects/Consumable/drinks_cups.yml b/Resources/Prototypes/Entities/Objects/Consumable/drinks_cups.yml index 5094bb3266..a263bf2d13 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/drinks_cups.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/drinks_cups.yml @@ -7,7 +7,7 @@ components: - type: SolutionContainer maxVol: 20 - - type: Pourable + - type: SolutionTransfer transferAmount: 5 - type: Drink isOpen: true diff --git a/Resources/Prototypes/Entities/Objects/Consumable/kitchen_reagent_containers.yml b/Resources/Prototypes/Entities/Objects/Consumable/kitchen_reagent_containers.yml index 3a0fc6f0d4..ac3e0b1beb 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/kitchen_reagent_containers.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/kitchen_reagent_containers.yml @@ -10,7 +10,7 @@ reagents: - ReagentId: chem.Flour Quantity: 50 - - type: Pourable + - type: SolutionTransfer transferAmount: 5 - type: Drink - type: Sprite diff --git a/Resources/Prototypes/Entities/Objects/Consumable/trash_drinks.yml b/Resources/Prototypes/Entities/Objects/Consumable/trash_drinks.yml index 5364681a1f..ab854aa184 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/trash_drinks.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/trash_drinks.yml @@ -12,7 +12,7 @@ - type: SolutionContainer maxVol: 10 - - type: Pourable + - type: SolutionTransfer transferAmount: 5 - type: Drink isOpen: true diff --git a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml index 7070e43629..77c85606d3 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml @@ -14,7 +14,7 @@ size: 10 - type: SolutionContainer maxVol: 100 - caps: AddTo, RemoveFrom + caps: Refillable, Drainable contents: reagents: - ReagentId: chem.Water diff --git a/Resources/Prototypes/Entities/Objects/Power/powercells.yml b/Resources/Prototypes/Entities/Objects/Power/powercells.yml index 02af2ec187..e9d51da5ce 100644 --- a/Resources/Prototypes/Entities/Objects/Power/powercells.yml +++ b/Resources/Prototypes/Entities/Objects/Power/powercells.yml @@ -17,7 +17,7 @@ netsync: false - type: SolutionContainer maxVol: 5 - caps: AddTo, RemoveFrom + caps: Injectable, Drawable - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml b/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml index 071f68b2ec..ecb95dc641 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml @@ -13,8 +13,8 @@ - type: SolutionContainer fillingState: beaker maxVol: 50 - caps: CanExamine, AddTo, RemoveFrom, FitsInDispenser # can add and remove solutions and fits in the chemmaster. - - type: Pourable + caps: OpenContainer, FitsInDispenser # can add and remove solutions and fits in the chemmaster. + - type: SolutionTransfer transferAmount: 5.0 - type: Spillable - type: GlassBeaker @@ -34,8 +34,8 @@ - type: SolutionContainer fillingState: beakerlarge maxVol: 100 - caps: CanExamine, AddTo, RemoveFrom, FitsInDispenser - - type: Pourable + caps: OpenContainer, FitsInDispenser + - type: SolutionTransfer transferAmount: 5.0 - type: Spillable - type: GlassBeaker @@ -53,7 +53,7 @@ fillingState: dropper fillingSteps: 2 maxVol: 5 - - type: Pourable + - type: SolutionTransfer transferAmount: 5.0 - type: Spillable @@ -85,7 +85,7 @@ - type: Drink - type: SolutionContainer maxVol: 30 - - type: Pourable + - type: SolutionTransfer transferAmount: 5 - type: Spillable diff --git a/Resources/Prototypes/Entities/Objects/Specific/janitor.yml b/Resources/Prototypes/Entities/Objects/Specific/janitor.yml index 4b4ba32251..a21de46789 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/janitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/janitor.yml @@ -198,8 +198,8 @@ state: cleaner - type: SolutionContainer maxVol: 100 - caps: AddTo, RemoveFrom - - type: Pourable + caps: Refillable, Drainable + - type: SolutionTransfer transferAmount: 5.0 - type: Spillable - type: ItemCooldown diff --git a/Resources/Prototypes/Entities/Objects/Specific/rehydrateable.yml b/Resources/Prototypes/Entities/Objects/Specific/rehydrateable.yml index 7bea165c02..7c9c5c04bd 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/rehydrateable.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/rehydrateable.yml @@ -10,7 +10,7 @@ - ReagentId: chem.Nutriment Quantity: 10 maxVol: 11 # needs room for water - caps: AddTo + caps: Refillable - type: Sprite sprite: Objects/Consumable/Food/monkeycube.rsi - type: Rehydratable @@ -28,6 +28,6 @@ - ReagentId: chem.Nutriment Quantity: 10 maxVol: 11 # needs room for water - caps: AddTo + caps: Refillable - type: Rehydratable target: CarpMob_Content diff --git a/Resources/Prototypes/Entities/Objects/Tools/botany_tools.yml b/Resources/Prototypes/Entities/Objects/Tools/botany_tools.yml index 1d1025be6e..9f65f81ba3 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/botany_tools.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/botany_tools.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity name: mini hoe parent: BaseItem id: MiniHoe @@ -24,7 +24,7 @@ state: plantbgone - type: SolutionContainer maxVol: 100 - caps: RemoveFrom + caps: Drainable contents: reagents: - ReagentId: chem.PlantBGone @@ -42,12 +42,12 @@ state: weedspray - type: SolutionContainer maxVol: 50 - caps: RemoveFrom + caps: Drainable contents: reagents: - ReagentId: chem.WeedKiller Quantity: 50 - - type: Pourable + - type: SolutionTransfer transferAmount: 1.0 - type: Spillable - type: ItemCooldown @@ -65,7 +65,7 @@ state: pestspray - type: SolutionContainer maxVol: 50 - caps: RemoveFrom + caps: Drainable contents: reagents: - ReagentId: chem.PestKiller diff --git a/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml b/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml index 0545aad5fd..df9d23c359 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity name: haycutters parent: BaseItem id: Haycutters @@ -114,7 +114,7 @@ - type: ItemStatus - type: SolutionContainer maxVol: 50 - caps: AddTo + caps: Refillable contents: reagents: - ReagentId: chem.WeldingFuel diff --git a/Resources/Prototypes/Entities/Objects/Tools/welders.yml b/Resources/Prototypes/Entities/Objects/Tools/welders.yml index 7906328243..3827ccd87a 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/welders.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/welders.yml @@ -20,7 +20,7 @@ - type: ItemStatus - type: SolutionContainer maxVol: 100 - caps: AddTo + caps: Refillable contents: reagents: - ReagentId: chem.WeldingFuel @@ -44,7 +44,7 @@ sprite: Objects/Tools/welder_experimental.rsi - type: SolutionContainer maxVol: 1000 - caps: AddTo + caps: Refillable contents: reagents: - ReagentId: chem.WeldingFuel @@ -66,7 +66,7 @@ sprite: Objects/Tools/welder_mini.rsi - type: SolutionContainer maxVol: 25 - caps: AddTo + caps: Refillable contents: reagents: - ReagentId: chem.WeldingFuel diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Shotgun/cartridges.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Shotgun/cartridges.yml index c6a37c4017..dbe23a0384 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Shotgun/cartridges.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Shotgun/cartridges.yml @@ -102,5 +102,5 @@ - type: ChemicalAmmo - type: SolutionContainer maxVol: 15 - caps: AddTo, RemoveFrom - - type: Pourable \ No newline at end of file + caps: Refillable, Drainable + - type: SolutionTransfer diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Shotgun/projectiles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Shotgun/projectiles.yml index a60c07a38b..d3ed06e0ee 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Shotgun/projectiles.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Shotgun/projectiles.yml @@ -91,6 +91,6 @@ Blunt: 1 - type: SolutionContainer maxVol: 15 - caps: AddTo, RemoveFrom + caps: Refillable, Drainable - type: ChemicalInjectionProjectile transferAmount: 15 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml index 6df83f9c61..d73b704788 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity name: spear parent: BaseItem id: Spear @@ -22,7 +22,7 @@ - type: MeleeChemicalInjector - type: SolutionContainer maxVol: 5 - - type: Pourable + - type: SolutionTransfer - type: MeleeWeaponAnimation id: spear diff --git a/Resources/Prototypes/Entities/Objects/hypospray.yml b/Resources/Prototypes/Entities/Objects/hypospray.yml index a2d11fbc7e..77b16231c6 100644 --- a/Resources/Prototypes/Entities/Objects/hypospray.yml +++ b/Resources/Prototypes/Entities/Objects/hypospray.yml @@ -11,5 +11,5 @@ sprite: Objects/Specific/Medical/hypospray.rsi - type: SolutionContainer maxVol: 30 - caps: AddTo, CanExamine + caps: Refillable, CanExamine - type: Hypospray diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings index c8f876874b..6e80925829 100644 --- a/SpaceStation14.sln.DotSettings +++ b/SpaceStation14.sln.DotSettings @@ -89,6 +89,7 @@ True True True + True True True True