/// <summary> /// Adds a solution to the container, overflowing the rest. /// It will /// Unlike <see cref="TryAddSolution"/> it will ignore size limits. /// </summary> /// <param name="targetUid">entity holding targetSolution</param> /// <param name="targetSolution">The container to which we try to add.</param> /// <param name="addedSolution">solution being added</param> /// <param name="overflowThreshold">After addition this much will be left in targetSolution. Should be less /// than targetSolution.TotalVolume</param> /// <param name="overflowingSolution">Solution that exceeded overflowThreshold</param> /// <returns></returns> public bool TryMixAndOverflow(EntityUid targetUid, Solution targetSolution, Solution addedSolution, FixedPoint2 overflowThreshold, [NotNullWhen(true)] out Solution?overflowingSolution) { if (addedSolution.TotalVolume == 0 || overflowThreshold > targetSolution.MaxVolume) { overflowingSolution = null; return(false); } targetSolution.AddSolution(addedSolution); UpdateChemicals(targetUid, targetSolution, true); overflowingSolution = targetSolution.SplitSolution(FixedPoint2.Max(FixedPoint2.Zero, targetSolution.CurrentVolume - overflowThreshold)); return(true); }
// Handles logic for our different types of valid target. // Checks for conditions that would prevent a doAfter from starting. private void HandleDoAfter(EntityUid user, EntityUid used, EntityUid target, AbsorbentComponent component, FixedPoint2 currentVolume, FixedPoint2 availableVolume) { // Below variables will be set within this function depending on what kind of target was clicked. // They will be passed to the OnTransferComplete if the doAfter succeeds. EntityUid donor; EntityUid acceptor; string donorSolutionName; string acceptorSolutionName; FixedPoint2 transferAmount; var delay = 1.0f; //default do_after delay in seconds. string msg; SoundSpecifier sfx; // For our purposes, if our target has a PuddleComponent, treat it as a puddle above all else. if (TryComp <PuddleComponent>(target, out var puddle)) { // These return conditions will abort BEFORE the do_after is called: if (!_solutionSystem.TryGetSolution(target, puddle.SolutionName, out var puddleSolution) || // puddle Solution is null (puddleSolution.TotalVolume <= 0)) // puddle is completely empty { return; } else if (availableVolume < 0) // mop is completely full { msg = "mopping-system-tool-full"; user.PopupMessage(user, Loc.GetString(msg, ("used", used))); // play message now because we are aborting. return; } // adding to puddles else if (puddleSolution.TotalVolume < component.MopLowerLimit && // if the puddle is too small for the tool to effectively absorb any more solution from it currentVolume > 0) // tool needs a solution to dilute the puddle with. { // Dilutes the puddle with some solution from the tool transferAmount = FixedPoint2.Max(component.ResidueAmount, currentVolume); TryTransfer(used, target, "absorbed", puddle.SolutionName, transferAmount); // Complete the transfer right away, with no doAfter. sfx = component.TransferSound; SoundSystem.Play(sfx.GetSound(), Filter.Pvs(user), used); // Give instant feedback for diluting puddle, so that it's clear that the player is adding to the puddle (as opposed to other behaviours, which have a doAfter). msg = "mopping-system-puddle-diluted"; user.PopupMessage(user, Loc.GetString(msg)); // play message now because we are aborting. return; // Do not begin a doAfter. } else { // Taking from puddles: // Determine transferAmount: transferAmount = FixedPoint2.Min(component.PickupAmount, puddleSolution.TotalVolume, availableVolume); // TODO: consider onelining this with the above, using additional args on Min()? if ((puddleSolution.TotalVolume - transferAmount) < component.MopLowerLimit) // If the transferAmount would bring the puddle below the MopLowerLimit { transferAmount = puddleSolution.TotalVolume - component.MopLowerLimit; // Then the transferAmount should bring the puddle down to the MopLowerLimit exactly } donor = target; // the puddle Uid donorSolutionName = puddle.SolutionName; acceptor = used; // the mop/tool Uid acceptorSolutionName = "absorbed"; // by definition on AbsorbentComponent // Set delay/popup/sound if nondefault. Popup and sound will only play on a successful doAfter. delay = (component.PickupAmount.Float() / 10.0f) * component.MopSpeed; // Delay should scale with PickupAmount, which represents the maximum we can pick up per click. msg = "mopping-system-puddle-success"; sfx = component.PickupSound; DoMopInteraction(user, used, target, donor, acceptor, component, donorSolutionName, acceptorSolutionName, transferAmount, delay, msg, sfx); } } else if ((TryComp <RefillableSolutionComponent>(target, out var refillable)) && // We can put solution from the tool into the target (currentVolume > 0)) // And the tool is wet { // These return conditions will abort BEFORE the do_after is called: if (!_solutionSystem.TryGetRefillableSolution(target, out var refillableSolution)) // refillable Solution is null { return; } else if (refillableSolution.AvailableVolume <= 0) // target container is full (liquid destination) { msg = "mopping-system-target-container-full"; user.PopupMessage(user, Loc.GetString(msg, ("target", target))); // play message now because we are aborting. return; } else if (refillableSolution.MaxVolume <= FixedPoint2.New(20)) // target container is too small (e.g. syringe) { msg = "mopping-system-target-container-too-small"; user.PopupMessage(user, Loc.GetString(msg, ("target", target))); // play message now because we are aborting. return; } else { // Determine transferAmount if (_tagSystem.HasTag(used, "Mop") && // if the tool used is a literal mop (and not a sponge, rag, etc.) !_tagSystem.HasTag(target, "Wringer")) // and if the target does not have a wringer for properly drying the mop { delay = 5.0f; // Should take much longer if you don't have a wringer if ((currentVolume / (currentVolume + availableVolume)) > 0.25) // mop is more than one-quarter full { transferAmount = FixedPoint2.Min(refillableSolution.AvailableVolume, currentVolume * 0.6); // squeeze up to 60% of the solution from the mop. msg = "mopping-system-hand-squeeze-little-wet"; if ((currentVolume / (currentVolume + availableVolume)) > 0.5) // if the mop is more than half full { msg = "mopping-system-hand-squeeze-still-wet"; // overwrites the above } } else // mop is less than one-quarter full { transferAmount = FixedPoint2.Min(refillableSolution.AvailableVolume, currentVolume); // squeeze remainder of solution from the mop. msg = "mopping-system-hand-squeeze-dry"; } } else { transferAmount = FixedPoint2.Min(refillableSolution.AvailableVolume, currentVolume); //Transfer all liquid from the tool to the container, but only if it will fit. msg = "mopping-system-refillable-success"; delay = 1.0f; } donor = used; // the mop/tool Uid donorSolutionName = "absorbed"; // by definition on AbsorbentComponent acceptor = target; // the refillable container's Uid acceptorSolutionName = refillable.Solution; // Set delay/popup/sound if nondefault. Popup and sound will only play on a successful doAfter. sfx = component.TransferSound; DoMopInteraction(user, used, target, donor, acceptor, component, donorSolutionName, acceptorSolutionName, transferAmount, delay, msg, sfx); } }