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