using System.Threading.Tasks; using Content.Server.Interfaces; using Content.Shared.Chemistry; using Content.Shared.Interfaces.GameObjects.Components; using Robust.Shared.GameObjects; using Robust.Shared.IoC; 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 { [Dependency] private readonly IServerNotifyManager _notifyManager = default!; 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] 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.CanPourIn && attackSolution.CanPourOut) { // default logic (beakers and glasses) // transfer solution from object in hand to attacked return TryTransfer(eventArgs, attackSolution, targetSolution); } else if (targetSolution.CanPourOut && attackSolution.CanPourIn) { // 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, SolutionComponent fromSolution, SolutionComponent 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 { _notifyManager.PopupMessage(Owner.Transform.GridPosition, eventArgs.User, Loc.GetString("Container is full")); return false; } //Move units from attackSolution to targetSolution var removedSolution = fromSolution.SplitSolution(realTransferAmount); if (!toSolution.TryAddSolution(removedSolution)) return false; _notifyManager.PopupMessage(Owner.Transform.GridPosition, eventArgs.User, Loc.GetString("Transferred {0}u", removedSolution.TotalVolume)); return true; } } }