/// <returns>The actual amount transferred.</returns> private static FixedPoint2 DoTransfer(EntityUid user, EntityUid sourceEntity, Solution source, EntityUid targetEntity, Solution target, FixedPoint2 amount) { if (source.DrainAvailable == 0) { sourceEntity.PopupMessage(user, Loc.GetString("comp-solution-transfer-is-empty", ("target", sourceEntity))); return(FixedPoint2.Zero); } if (target.AvailableVolume == 0) { targetEntity.PopupMessage(user, Loc.GetString("comp-solution-transfer-is-full", ("target", targetEntity))); return(FixedPoint2.Zero); } var actualAmount = FixedPoint2.Min(amount, FixedPoint2.Min(source.DrainAvailable, target.AvailableVolume)); var solution = EntitySystem.Get <SolutionContainerSystem>().Drain(sourceEntity, source, actualAmount); EntitySystem.Get <SolutionContainerSystem>().Refill(targetEntity, target, solution); return(actualAmount); }
private void TryDraw(InjectorComponent component, EntityUid targetEntity, Solution targetSolution, EntityUid user) { if (!_solutions.TryGetSolution(component.Owner, InjectorComponent.SolutionName, out var solution) || solution.AvailableVolume == 0) { return; } // Get transfer amount. May be smaller than _transferAmount if not enough room var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetSolution.DrawAvailable); if (realTransferAmount <= 0) { _popup.PopupEntity(Loc.GetString("injector-component-target-is-empty-message", ("target", targetEntity)), component.Owner, Filter.Entities(user)); return; } // Move units from attackSolution to targetSolution var removedSolution = _solutions.Draw(targetEntity, targetSolution, realTransferAmount); if (!_solutions.TryAddSolution(component.Owner, solution, removedSolution)) { return; } _popup.PopupEntity(Loc.GetString("injector-component-draw-success-message", ("amount", removedSolution.TotalVolume), ("target", targetEntity)), component.Owner, Filter.Entities(user)); Dirty(component); AfterDraw(component); }
private void TryInjectIntoBloodstream(InjectorComponent component, BloodstreamComponent targetBloodstream, EntityUid user) { // Get transfer amount. May be smaller than _transferAmount if not enough room var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetBloodstream.ChemicalSolution.AvailableVolume); if (realTransferAmount <= 0) { _popup.PopupEntity(Loc.GetString("injector-component-cannot-inject-message", ("target", targetBloodstream.Owner)), component.Owner, Filter.Entities(user)); return; } // Move units from attackSolution to targetSolution var removedSolution = _solutions.SplitSolution(user, targetBloodstream.ChemicalSolution, realTransferAmount); _blood.TryAddToChemicals((targetBloodstream).Owner, removedSolution, targetBloodstream); removedSolution.DoEntityReaction(targetBloodstream.Owner, ReactionMethod.Injection); _popup.PopupEntity(Loc.GetString("injector-component-inject-success-message", ("amount", removedSolution.TotalVolume), ("target", targetBloodstream.Owner)), component.Owner, Filter.Entities(user)); Dirty(component); AfterInject(component); }
/// <summary> /// Will overflow this entity to neighboring entities if required /// </summary> private void CheckOverflow(PuddleComponent puddleComponent) { if (puddleComponent.CurrentVolume <= puddleComponent.OverflowVolume || puddleComponent.Overflown) { return; } var nextPuddles = new List <PuddleComponent>() { puddleComponent }; var overflownPuddles = new List <PuddleComponent>(); while (puddleComponent.OverflowLeft > FixedPoint2.Zero && nextPuddles.Count > 0) { foreach (var next in nextPuddles.ToArray()) { nextPuddles.Remove(next); next.Overflown = true; overflownPuddles.Add(next); var adjacentPuddles = GetAllAdjacentOverflow(next).ToArray(); if (puddleComponent.OverflowLeft <= FixedPoint2.Epsilon * adjacentPuddles.Length) { break; } if (adjacentPuddles.Length == 0) { continue; } var numberOfAdjacent = FixedPoint2.New(adjacentPuddles.Length); var overflowSplit = puddleComponent.OverflowLeft / numberOfAdjacent; foreach (var adjacent in adjacentPuddles) { var adjacentPuddle = adjacent(); var quantity = FixedPoint2.Min(overflowSplit, adjacentPuddle.OverflowVolume); var puddleSolution = _solutionContainerSystem.EnsureSolution(puddleComponent.Owner, puddleComponent.SolutionName); var spillAmount = _solutionContainerSystem.SplitSolution(puddleComponent.Owner, puddleSolution, quantity); TryAddSolution(adjacentPuddle.Owner, spillAmount, false, false); nextPuddles.Add(adjacentPuddle); } } } foreach (var puddle in overflownPuddles) { puddle.Overflown = false; } }
private void ReleaseToFloor(EntityCoordinates clickLocation, AbsorbentComponent absorbent, Solution?absorbedSolution) { if ((_mapManager.TryGetGrid(clickLocation.GetGridUid(EntityManager), out var mapGrid)) && // needs valid grid absorbedSolution is not null) // needs a solution to place on the tile { TileRef tile = mapGrid.GetTileRef(clickLocation); // Drop some of the absorbed liquid onto the ground var releaseAmount = FixedPoint2.Min(absorbent.ResidueAmount, absorbedSolution.CurrentVolume); // The release amount specified on the absorbent component, or the amount currently absorbed (whichever is less). var releasedSolution = _solutionSystem.SplitSolution(absorbent.Owner, absorbedSolution, releaseAmount); // Remove releaseAmount of solution from the absorbent component _spillableSystem.SpillAt(tile, releasedSolution, puddlePrototypeId); // And spill it onto the tile. } }
public void FixedPoint2Min() { var unorderedList = new[] { FixedPoint2.New(5), FixedPoint2.New(3), FixedPoint2.New(1), FixedPoint2.New(2), FixedPoint2.New(4), }; var min = FixedPoint2.Min(unorderedList); Assert.That(min, Is.EqualTo(FixedPoint2.New(1))); }
/// <summary> /// Raised directed at a victim when someone has force fed them a drink. /// </summary> private void OnForceDrink(EntityUid uid, SharedBodyComponent body, ForceDrinkEvent args) { if (args.Drink.Deleted) { return; } args.Drink.CancelToken = null; var transferAmount = FixedPoint2.Min(args.Drink.TransferAmount, args.DrinkSolution.DrainAvailable); var drained = _solutionContainerSystem.Drain((args.Drink).Owner, args.DrinkSolution, transferAmount); if (!_bodySystem.TryGetComponentsOnMechanisms <StomachComponent>(uid, out var stomachs, body)) { _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-cannot-drink-other"), uid, Filter.Entities(args.User)); _spillableSystem.SpillAt(uid, drained, "PuddleSmear"); return; } var firstStomach = stomachs.FirstOrNull( stomach => _stomachSystem.CanTransferSolution((stomach.Comp).Owner, drained)); // All stomach are full or can't handle whatever solution we have. if (firstStomach == null) { _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough-other"), uid, Filter.Entities(args.User)); _spillableSystem.SpillAt(uid, drained, "PuddleSmear"); return; } EntityManager.TryGetComponent(uid, out MetaDataComponent? targetMeta); var targetName = targetMeta?.EntityName ?? string.Empty; EntityManager.TryGetComponent(args.User, out MetaDataComponent? userMeta); var userName = userMeta?.EntityName ?? string.Empty; _popupSystem.PopupEntity(Loc.GetString("drink-component-force-feed-success", ("user", userName)), uid, Filter.Entities(uid)); _popupSystem.PopupEntity(Loc.GetString("drink-component-force-feed-success-user", ("target", targetName)), args.User, Filter.Entities(args.User)); SoundSystem.Play(Filter.Pvs(uid), args.Drink.UseSound.GetSound(), uid, AudioParams.Default.WithVolume(-2f)); drained.DoEntityReaction(uid, ReactionMethod.Ingestion); _stomachSystem.TryTransferSolution((firstStomach.Value.Comp).Owner, drained, firstStomach.Value.Comp); }
public override void Update(float frameTime) { base.Update(frameTime); var queueDelete = new RemQueue <EvaporationComponent>(); foreach (var evaporationComponent in EntityManager.EntityQuery <EvaporationComponent>()) { var uid = evaporationComponent.Owner; evaporationComponent.Accumulator += frameTime; if (!_solutionContainerSystem.TryGetSolution(uid, evaporationComponent.SolutionName, out var solution)) { // If no solution, delete the entity EntityManager.QueueDeleteEntity(uid); continue; } if (evaporationComponent.Accumulator < evaporationComponent.EvaporateTime) { continue; } evaporationComponent.Accumulator -= evaporationComponent.EvaporateTime; if (evaporationComponent.EvaporationToggle == true) { _solutionContainerSystem.SplitSolution(uid, solution, FixedPoint2.Min(FixedPoint2.New(1), solution.CurrentVolume)); // removes 1 unit, or solution current volume, whichever is lower. } if (solution.CurrentVolume <= 0) { EntityManager.QueueDeleteEntity(uid); } else if (solution.CurrentVolume <= evaporationComponent.LowerLimit || // if puddle is too big or too small to evaporate. solution.CurrentVolume >= evaporationComponent.UpperLimit) { evaporationComponent.EvaporationToggle = false; // pause evaporation } else { evaporationComponent.EvaporationToggle = true; // unpause evaporation, e.g. if a puddle previously above evaporation UpperLimit was brought down below evaporation UpperLimit via mopping. } } foreach (var evaporationComponent in queueDelete) { EntityManager.RemoveComponent(evaporationComponent.Owner, evaporationComponent); } }
private void OnAfterInteract(EntityUid uid, FireExtinguisherComponent component, AfterInteractEvent args) { if (args.Target == null || !args.CanReach) { return; } if (args.Handled) { return; } args.Handled = true; if (component.HasSafety && component.Safety) { _popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-safety-on-message"), uid, Filter.Entities(args.User)); return; } if (args.Target is not { Valid : true } target || !_solutionContainerSystem.TryGetDrainableSolution(target, out var targetSolution) || !_solutionContainerSystem.TryGetRefillableSolution(uid, out var container)) { return; } var transfer = container.AvailableVolume; if (TryComp <SolutionTransferComponent>(uid, out var solTrans)) { transfer = solTrans.TransferAmount; } transfer = FixedPoint2.Min(transfer, targetSolution.DrainAvailable); if (transfer > 0) { var drained = _solutionContainerSystem.Drain(target, targetSolution, transfer); _solutionContainerSystem.TryAddSolution(uid, container, drained); SoundSystem.Play(Filter.Pvs(uid), component.RefillSound.GetSound(), uid); _popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-after-interact-refilled-message", ("owner", uid)), uid, Filter.Entities(args.Target.Value)); } }
protected override void ReactWithEntity(EntityUid entity, double solutionFraction) { if (!EntitySystem.Get <SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution)) { return; } if (!_entMan.TryGetComponent(entity, out BloodstreamComponent? bloodstream)) { return; } var invSystem = EntitySystem.Get <InventorySystem>(); // TODO: Add a permeability property to clothing // For now it just adds to protection for each clothing equipped var protection = 0f; if (invSystem.TryGetSlots(entity, out var slotDefinitions)) { foreach (var slot in slotDefinitions) { if (slot.Name == "back" || slot.Name == "pocket1" || slot.Name == "pocket2" || slot.Name == "id") { continue; } if (invSystem.TryGetSlotEntity(entity, slot.Name, out _)) { protection += 0.025f; } } } var bloodstreamSys = EntitySystem.Get <BloodstreamSystem>(); var cloneSolution = solution.Clone(); var transferAmount = FixedPoint2.Min(cloneSolution.TotalVolume * solutionFraction * (1 - protection), bloodstream.Solution.AvailableVolume); var transferSolution = cloneSolution.SplitSolution(transferAmount); bloodstreamSys.TryAddToBloodstream(entity, transferSolution, bloodstream); }
public override void Update(float frameTime) { base.Update(frameTime); var queueDelete = new RemQueue <EvaporationComponent>(); foreach (var evaporationComponent in EntityManager.EntityQuery <EvaporationComponent>()) { var uid = evaporationComponent.Owner; evaporationComponent.Accumulator += frameTime; if (!_solutionContainerSystem.TryGetSolution(uid, evaporationComponent.SolutionName, out var solution)) { // If no solution, delete the entity queueDelete.Add(evaporationComponent); continue; } if (evaporationComponent.Accumulator < evaporationComponent.EvaporateTime) { continue; } evaporationComponent.Accumulator -= evaporationComponent.EvaporateTime; _solutionContainerSystem.SplitSolution(uid, solution, FixedPoint2.Min(FixedPoint2.New(1), solution.CurrentVolume)); if (solution.CurrentVolume == 0) { EntityManager.QueueDeleteEntity(uid); } else if (solution.CurrentVolume <= evaporationComponent.LowerLimit || solution.CurrentVolume >= evaporationComponent.UpperLimit) { queueDelete.Add(evaporationComponent); } } foreach (var evaporationComponent in queueDelete) { EntityManager.RemoveComponent(evaporationComponent.Owner, evaporationComponent); } }
protected override void ReactWithEntity(EntityUid entity, double solutionFraction) { if (!EntitySystem.Get <SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution)) { return; } if (!_entMan.TryGetComponent(entity, out BloodstreamComponent? bloodstream)) { return; } // TODO: Add a permeability property to clothing // For now it just adds to protection for each clothing equipped var protection = 0f; if (_entMan.TryGetComponent(entity, out InventoryComponent? inventory)) { foreach (var slot in inventory.Slots) { if (slot == EquipmentSlotDefines.Slots.BACKPACK || slot == EquipmentSlotDefines.Slots.POCKET1 || slot == EquipmentSlotDefines.Slots.POCKET2 || slot == EquipmentSlotDefines.Slots.IDCARD) { continue; } if (inventory.TryGetSlotItem(slot, out ItemComponent? _)) { protection += 0.025f; } } } var bloodstreamSys = EntitySystem.Get <BloodstreamSystem>(); var cloneSolution = solution.Clone(); var transferAmount = FixedPoint2.Min(cloneSolution.TotalVolume * solutionFraction * (1 - protection), bloodstream.Solution.AvailableVolume); var transferSolution = cloneSolution.SplitSolution(transferAmount); bloodstreamSys.TryAddToBloodstream(entity, transferSolution, bloodstream); }
public void TryAddSolution(Solution solution) { if (solution.TotalVolume == 0) { return; } if (!EntitySystem.Get <SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solutionArea)) { return; } var addSolution = solution.SplitSolution(FixedPoint2.Min(solution.TotalVolume, solutionArea.AvailableVolume)); EntitySystem.Get <SolutionContainerSystem>().TryAddSolution(Owner, solutionArea, addSolution); UpdateVisuals(); }
private void TryInject(InjectorComponent component, EntityUid targetEntity, Solution targetSolution, EntityUid user, bool asRefill) { if (!_solutions.TryGetSolution(component.Owner, InjectorComponent.SolutionName, out var solution) || solution.CurrentVolume == 0) { return; } // Get transfer amount. May be smaller than _transferAmount if not enough room var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetSolution.AvailableVolume); if (realTransferAmount <= 0) { _popup.PopupEntity(Loc.GetString("injector-component-target-already-full-message", ("target", targetEntity)), component.Owner, Filter.Entities(user)); return; } // Move units from attackSolution to targetSolution var removedSolution = _solutions.SplitSolution(component.Owner, solution, realTransferAmount); removedSolution.DoEntityReaction(targetEntity, ReactionMethod.Injection); if (!asRefill) { _solutions.Inject(targetEntity, targetSolution, removedSolution); } else { _solutions.Refill(targetEntity, targetSolution, removedSolution); } _popup.PopupEntity(Loc.GetString("injector-component-transfer-success-message", ("amount", removedSolution.TotalVolume), ("target", targetEntity)), component.Owner, Filter.Entities(user)); Dirty(component); AfterInject(component); }
async Task <bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { var solutionContainerSystem = EntitySystem.Get <SolutionContainerSystem>(); if (eventArgs.Target == null || !eventArgs.CanReach) { if (_hasSafety && _safety) { Owner.PopupMessage(eventArgs.User, Loc.GetString("fire-extinguisher-component-safety-on-message")); return(true); } return(false); } if (eventArgs.Target is not { Valid : true } target || !_entMan.HasComponent <ReagentTankComponent>(target) || !solutionContainerSystem.TryGetDrainableSolution(target, out var targetSolution) || !solutionContainerSystem.TryGetDrainableSolution(Owner, out var container)) { return(false); } var transfer = FixedPoint2.Min(container.AvailableVolume, targetSolution.DrainAvailable); if (transfer > 0) { var drained = solutionContainerSystem.Drain(target, targetSolution, transfer); solutionContainerSystem.TryAddSolution(Owner, container, drained); SoundSystem.Play(Filter.Pvs(Owner), _refillSound.GetSound(), Owner); eventArgs.Target.Value.PopupMessage(eventArgs.User, Loc.GetString("fire-extingusiher-component-after-interact-refilled-message", ("owner", Owner))); } return(true); }
protected override void ReactWithEntity(EntityUid entity, double solutionFraction) { if (!EntitySystem.Get <SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution)) { return; } if (!_entMan.TryGetComponent(entity, out BloodstreamComponent? bloodstream)) { return; } if (_entMan.TryGetComponent(entity, out InternalsComponent? internals) && internals.AreInternalsWorking()) { return; } var chemistry = EntitySystem.Get <ReactiveSystem>(); var cloneSolution = solution.Clone(); var transferAmount = FixedPoint2.Min(cloneSolution.TotalVolume * solutionFraction, bloodstream.Solution.AvailableVolume); var transferSolution = cloneSolution.SplitSolution(transferAmount); foreach (var reagentQuantity in transferSolution.Contents.ToArray()) { if (reagentQuantity.Quantity == FixedPoint2.Zero) { continue; } chemistry.ReactionEntity(entity, ReactionMethod.Ingestion, reagentQuantity.ReagentId, reagentQuantity.Quantity, transferSolution); } var bloodstreamSys = EntitySystem.Get <BloodstreamSystem>(); bloodstreamSys.TryAddToBloodstream(entity, transferSolution, bloodstream); }
private void OnFeed(EntityUid uid, SharedBodyComponent body, FeedEvent args) { if (args.Food.Deleted) { return; } args.Food.CancelToken = null; if (!_bodySystem.TryGetComponentsOnMechanisms <StomachComponent>(uid, out var stomachs, body)) { return; } var transferAmount = args.Food.TransferAmount != null ? FixedPoint2.Min((FixedPoint2)args.Food.TransferAmount, args.FoodSolution.CurrentVolume) : args.FoodSolution.CurrentVolume; var split = _solutionContainerSystem.SplitSolution((args.Food).Owner, args.FoodSolution, transferAmount); var firstStomach = stomachs.FirstOrNull( stomach => _stomachSystem.CanTransferSolution((stomach.Comp).Owner, split)); var forceFeed = uid != args.User; // No stomach so just popup a message that they can't eat. if (firstStomach == null) { _solutionContainerSystem.TryAddSolution(uid, args.FoodSolution, split); _popupSystem.PopupEntity( forceFeed ? Loc.GetString("food-system-you-cannot-eat-any-more-other") : Loc.GetString("food-system-you-cannot-eat-any-more") , uid, Filter.Entities(args.User)); return; } split.DoEntityReaction(uid, ReactionMethod.Ingestion); _stomachSystem.TryTransferSolution(firstStomach.Value.Comp.Owner, split, firstStomach.Value.Comp); if (forceFeed) { EntityManager.TryGetComponent(uid, out MetaDataComponent? targetMeta); var targetName = targetMeta?.EntityName ?? string.Empty; EntityManager.TryGetComponent(args.User, out MetaDataComponent? userMeta); var userName = userMeta?.EntityName ?? string.Empty; _popupSystem.PopupEntity(Loc.GetString("food-system-force-feed-success", ("user", userName)), uid, Filter.Entities(uid)); _popupSystem.PopupEntity(Loc.GetString("food-system-force-feed-success-user", ("target", targetName)), args.User, Filter.Entities(args.User)); } else { _popupSystem.PopupEntity(Loc.GetString(args.Food.EatMessage, ("food", args.Food.Owner)), args.User, Filter.Entities(args.User)); } SoundSystem.Play(Filter.Pvs(uid), args.Food.UseSound.GetSound(), uid, AudioParams.Default.WithVolume(-1f)); // Try to break all used utensils foreach (var utensil in args.Utensils) { _utensilSystem.TryBreak((utensil).Owner, args.User); } if (args.Food.UsesRemaining > 0) { return; } if (string.IsNullOrEmpty(args.Food.TrashPrototype)) { EntityManager.QueueDeleteEntity(args.Food.Owner); } else { DeleteAndSpawnTrash(args.Food, args.User); } }
public override void Update(float frameTime) { base.Update(frameTime); foreach (var drain in EntityQuery <DrainComponent>()) { drain.Accumulator += frameTime; if (drain.Accumulator < drain.DrainFrequency) { continue; } drain.Accumulator -= drain.DrainFrequency; /// Best to do this one every second rather than once every tick... _solutionSystem.TryGetSolution(drain.Owner, DrainComponent.SolutionName, out var drainSolution); if (drainSolution is null) { return; } /// Remove a bit from the buffer _solutionSystem.SplitSolution(drain.Owner, drainSolution, (drain.UnitsDestroyedPerSecond * drain.DrainFrequency)); /// This will ensure that UnitsPerSecond is per second... var amount = drain.UnitsPerSecond * drain.DrainFrequency; var xform = Transform(drain.Owner); List <PuddleComponent> puddles = new(); foreach (var entity in _lookup.GetEntitiesInRange(xform.MapPosition, drain.Range)) { /// No InRangeUnobstructed because there's no collision group that fits right now /// and these are placed by mappers and not buildable/movable so shouldnt really be a problem... if (TryComp <PuddleComponent>(entity, out var puddleComp)) { puddles.Add(puddleComp); } } if (puddles.Count == 0) { _ambientSoundSystem.SetAmbience(drain.Owner, false); continue; } _ambientSoundSystem.SetAmbience(drain.Owner, true); amount /= puddles.Count; foreach (var puddle in puddles) { /// Queue the solution deletion if it's empty. EvaporationSystem might also do this /// but queuedelete should be pretty safe. if (!_solutionSystem.TryGetSolution(puddle.Owner, puddle.SolutionName, out var puddleSolution)) { EntityManager.QueueDeleteEntity(puddle.Owner); continue; } /// Removes the lowest of: /// the drain component's units per second adjusted for # of puddles /// the puddle's remaining volume (making it cleanly zero) /// the drain's remaining volume in its buffer. var transferSolution = _solutionSystem.SplitSolution(puddle.Owner, puddleSolution, FixedPoint2.Min(FixedPoint2.New(amount), puddleSolution.CurrentVolume, drainSolution.AvailableVolume)); _solutionSystem.TryAddSolution(drain.Owner, drainSolution, transferSolution); if (puddleSolution.CurrentVolume <= 0) { EntityManager.QueueDeleteEntity(puddle.Owner); } } } }
async Task <bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs) { if (!Powered) { Owner.PopupMessage(eventArgs.User, Loc.GetString("microwave-component-interact-using-no-power")); return(false); } if (_broken) { Owner.PopupMessage(eventArgs.User, Loc.GetString("microwave-component-interact-using-broken")); return(false); } if (_entities.GetComponent <HandsComponent>(eventArgs.User).GetActiveHand?.Owner is not { Valid: true } itemEntity) { eventArgs.User.PopupMessage(Loc.GetString("microwave-component-interact-using-no-active-hand")); return(false); } if (_entities.TryGetComponent <SolutionTransferComponent?>(itemEntity, out var attackPourable)) { var solutionsSystem = EntitySystem.Get <SolutionContainerSystem>(); if (!solutionsSystem.TryGetDrainableSolution(itemEntity, out var attackSolution)) { return(false); } if (!solutionsSystem.TryGetSolution(Owner, SolutionName, out var solution)) { return(false); } //Get transfer amount. May be smaller than _transferAmount if not enough room var realTransferAmount = FixedPoint2.Min(attackPourable.TransferAmount, solution.AvailableVolume); if (realTransferAmount <= 0) //Special message if container is full { Owner.PopupMessage(eventArgs.User, Loc.GetString("microwave-component-interact-using-container-full")); return(false); } //Move units from attackSolution to targetSolution var removedSolution = EntitySystem.Get <SolutionContainerSystem>() .Drain(itemEntity, attackSolution, realTransferAmount); if (!EntitySystem.Get <SolutionContainerSystem>().TryAddSolution(Owner, solution, removedSolution)) { return(false); } Owner.PopupMessage(eventArgs.User, Loc.GetString("microwave-component-interact-using-transfer-success", ("amount", removedSolution.TotalVolume))); return(true); } if (!_entities.TryGetComponent(itemEntity, typeof(ItemComponent), out var food)) { Owner.PopupMessage(eventArgs.User, "microwave-component-interact-using-transfer-fail"); return(false); } var ent = food.Owner; //Get the entity of the ItemComponent. _storage.Insert(ent); _uiDirty = true; return(true); }
public PuddleComponent?SpillAt(TileRef tileRef, Solution solution, string prototype, bool overflow = true, bool sound = true, bool noTileReact = false, bool combine = true) { if (solution.TotalVolume <= 0) { return(null); } // If space return early, let that spill go out into the void if (tileRef.Tile.IsEmpty) { return(null); } var gridId = tileRef.GridIndex; if (!_mapManager.TryGetGrid(gridId, out var mapGrid)) { return(null); // Let's not spill to invalid grids. } if (!noTileReact) { // First, do all tile reactions foreach (var(reagentId, quantity) in solution.Contents) { var proto = _prototypeManager.Index <ReagentPrototype>(reagentId); proto.ReactionTile(tileRef, quantity); } } // Tile reactions used up everything. if (solution.CurrentVolume == FixedPoint2.Zero) { return(null); } // Get normalized co-ordinate for spill location and spill it in the centre // TODO: Does SnapGrid or something else already do this? var spillGridCoords = mapGrid.GridTileToWorld(tileRef.GridIndices); var spillEntities = _entityLookup.GetEntitiesIntersecting(mapGrid.ParentMapId, spillGridCoords.Position).ToArray(); foreach (var spillEntity in spillEntities) { if (_solutionContainerSystem.TryGetRefillableSolution(spillEntity, out var solutionContainerComponent)) { _solutionContainerSystem.Refill(spillEntity, solutionContainerComponent, solution.SplitSolution(FixedPoint2.Min( solutionContainerComponent.AvailableVolume, solutionContainerComponent.MaxSpillRefill)) ); } } if (combine) { foreach (var spillEntity in spillEntities) { if (!EntityManager.TryGetComponent(spillEntity, out PuddleComponent? puddleComponent)) { continue; } if (!overflow && _puddleSystem.WouldOverflow(puddleComponent.Owner, solution, puddleComponent)) { return(null); } if (!_puddleSystem.TryAddSolution(puddleComponent.Owner, solution, sound)) { continue; } return(puddleComponent); } } var puddleEnt = EntityManager.SpawnEntity(prototype, spillGridCoords); var newPuddleComponent = EntityManager.GetComponent <PuddleComponent>(puddleEnt); _puddleSystem.TryAddSolution(newPuddleComponent.Owner, solution, sound); return(newPuddleComponent); }
/// <summary> /// Tries to eat some food /// </summary> /// <param name="uid">Food entity.</param> /// <param name="user">Feeding initiator.</param> /// <returns>True if an interaction occurred (i.e., food was consumed, or a pop-up message was created)</returns> public bool TryUseFood(EntityUid uid, EntityUid user, FoodComponent?food = null) { if (!Resolve(uid, ref food)) { return(false); } // if currently being used to force-feed, cancel that action. if (food.CancelToken != null) { food.CancelToken.Cancel(); food.CancelToken = null; return(true); } if (uid == user || //Suppresses self-eating EntityManager.TryGetComponent <MobStateComponent>(uid, out var mobState) && mobState.IsAlive()) // Suppresses eating alive mobs { return(false); } if (!_solutionContainerSystem.TryGetSolution(uid, food.SolutionName, out var solution)) { return(false); } if (food.UsesRemaining <= 0) { _popupSystem.PopupEntity(Loc.GetString("food-system-try-use-food-is-empty", ("entity", uid)), user, Filter.Entities(user)); DeleteAndSpawnTrash(food, user); return(true); } if (!EntityManager.TryGetComponent(user, out SharedBodyComponent ? body) || !_bodySystem.TryGetComponentsOnMechanisms <StomachComponent>(user, out var stomachs, body)) { return(false); } if (IsMouthBlocked(user, user)) { return(true); } var usedUtensils = new List <UtensilComponent>(); if (!TryGetRequiredUtensils(user, food, out var utensils)) { return(true); } if (!user.InRangeUnobstructed(uid, popup: true)) { return(true); } var transferAmount = food.TransferAmount != null?FixedPoint2.Min((FixedPoint2)food.TransferAmount, solution.CurrentVolume) : solution.CurrentVolume; var split = _solutionContainerSystem.SplitSolution(uid, solution, transferAmount); var firstStomach = stomachs.FirstOrNull( stomach => _stomachSystem.CanTransferSolution((stomach.Comp).Owner, split)); if (firstStomach == null) { _solutionContainerSystem.TryAddSolution(uid, solution, split); _popupSystem.PopupEntity(Loc.GetString("food-system-you-cannot-eat-any-more"), user, Filter.Entities(user)); return(true); } // TODO: Account for partial transfer. split.DoEntityReaction(user, ReactionMethod.Ingestion); _stomachSystem.TryTransferSolution((firstStomach.Value.Comp).Owner, split, firstStomach.Value.Comp); SoundSystem.Play(Filter.Pvs(user), food.UseSound.GetSound(), user, AudioParams.Default.WithVolume(-1f)); _popupSystem.PopupEntity(Loc.GetString(food.EatMessage, ("food", food.Owner)), user, Filter.Entities(user)); // Try to break all used utensils foreach (var utensil in usedUtensils) { _utensilSystem.TryBreak((utensil).Owner, user); } if (food.UsesRemaining > 0) { return(true); } if (string.IsNullOrEmpty(food.TrashPrototype)) { EntityManager.QueueDeleteEntity((food).Owner); } else { DeleteAndSpawnTrash(food, user); } return(true); }
async Task <bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { var solutionsSys = EntitySystem.Get <SolutionContainerSystem>(); if (!eventArgs.InRangeUnobstructed() || eventArgs.Target == null) { return(false); } if (!_entities.HasComponent <SolutionContainerManagerComponent>(Owner)) { return(false); } var target = eventArgs.Target !.Value; if (!_entities.HasComponent <SolutionContainerManagerComponent>(target)) { return(false); } if (CanReceive && _entities.TryGetComponent(target, out ReagentTankComponent? tank) && solutionsSys.TryGetRefillableSolution(Owner, out var ownerRefill) && solutionsSys.TryGetDrainableSolution(target, out var targetDrain)) { var tankTransferAmount = tank.TransferAmount; if (_entities.TryGetComponent(Owner, out RefillableSolutionComponent? refill) && refill.MaxRefill != null) { tankTransferAmount = FixedPoint2.Min(tankTransferAmount, (FixedPoint2)refill.MaxRefill); } var transferred = DoTransfer(eventArgs.User, target, targetDrain, Owner, ownerRefill, tankTransferAmount); if (transferred > 0) { var toTheBrim = ownerRefill.AvailableVolume == 0; var msg = toTheBrim ? "comp-solution-transfer-fill-fully" : "comp-solution-transfer-fill-normal"; target.PopupMessage(eventArgs.User, Loc.GetString(msg, ("owner", eventArgs.Target), ("amount", transferred), ("target", Owner))); return(true); } } if (CanSend && solutionsSys.TryGetRefillableSolution(target, out var targetRefill) && solutionsSys.TryGetDrainableSolution(Owner, out var ownerDrain)) { var transferAmount = TransferAmount; if (_entities.TryGetComponent(target, out RefillableSolutionComponent? refill) && refill.MaxRefill != null) { transferAmount = FixedPoint2.Min(transferAmount, (FixedPoint2)refill.MaxRefill); } var transferred = DoTransfer(eventArgs.User, Owner, ownerDrain, target, targetRefill, transferAmount); if (transferred > 0) { Owner.PopupMessage(eventArgs.User, Loc.GetString("comp-solution-transfer-transfer-solution", ("amount", transferred), ("target", target))); return(true); } } 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); } }
/// <summary> /// Attempt to drink some of a drink. Returns true if any interaction took place, including generation of /// pop-up messages. /// </summary> private bool TryUseDrink(EntityUid uid, EntityUid userUid, DrinkComponent?drink = null) { if (!Resolve(uid, ref drink)) { return(false); } // if currently being used to force-feed, cancel that action. if (drink.CancelToken != null) { drink.CancelToken.Cancel(); drink.CancelToken = null; return(true); } if (!drink.Opened) { _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-not-open", ("owner", Name: EntityManager.GetComponent <MetaDataComponent>(drink.Owner).EntityName)), uid, Filter.Entities(userUid)); return(true); } if (!EntityManager.TryGetComponent(userUid, out SharedBodyComponent? body)) { return(false); } if (!_solutionContainerSystem.TryGetDrainableSolution((drink).Owner, out var drinkSolution) || drinkSolution.DrainAvailable <= 0) { _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-is-empty", ("entity", Name: EntityManager.GetComponent <MetaDataComponent>(drink.Owner).EntityName)), uid, Filter.Entities(userUid)); return(true); } if (!_bodySystem.TryGetComponentsOnMechanisms <StomachComponent>(userUid, out var stomachs, body)) { _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-cannot-drink"), userUid, Filter.Entities(userUid)); return(true); } if (_foodSystem.IsMouthBlocked(userUid, userUid)) { return(true); } var transferAmount = FixedPoint2.Min(drink.TransferAmount, drinkSolution.DrainAvailable); var drain = _solutionContainerSystem.Drain(uid, drinkSolution, transferAmount); var firstStomach = stomachs.FirstOrNull( stomach => _stomachSystem.CanTransferSolution((stomach.Comp).Owner, drain)); // All stomach are full or can't handle whatever solution we have. if (firstStomach == null) { _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough"), userUid, Filter.Entities(userUid)); if (EntityManager.HasComponent <RefillableSolutionComponent>(uid)) { _spillableSystem.SpillAt(userUid, drain, "PuddleSmear"); return(true); } _solutionContainerSystem.Refill(uid, drinkSolution, drain); return(true); } SoundSystem.Play(Filter.Pvs(userUid), drink.UseSound.GetSound(), userUid, AudioParams.Default.WithVolume(-2f)); _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-success-slurp"), userUid, Filter.Pvs(userUid)); drain.DoEntityReaction(userUid, ReactionMethod.Ingestion); _stomachSystem.TryTransferSolution((firstStomach.Value.Comp).Owner, drain, firstStomach.Value.Comp); return(true); }
private void TransferReagent(string id, FixedPoint2 amount, bool isBuffer) { if (!BeakerSlot.HasItem && _bufferModeTransfer) { return; } if (BeakerSlot.Item is not { Valid: true } beaker || !_entities.TryGetComponent(beaker, out FitsInDispenserComponent? fits) || !EntitySystem.Get <SolutionContainerSystem>().TryGetSolution(beaker, fits.Solution, out var beakerSolution)) { return; } if (isBuffer) { foreach (var reagent in BufferSolution.Contents) { if (reagent.ReagentId == id) { FixedPoint2 actualAmount; if ( amount == FixedPoint2 .New(-1)) //amount is FixedPoint2.New(-1) when the client sends a message requesting to remove all solution from the container { actualAmount = FixedPoint2.Min(reagent.Quantity, beakerSolution.AvailableVolume); } else { actualAmount = FixedPoint2.Min(reagent.Quantity, amount, beakerSolution.AvailableVolume); } BufferSolution.RemoveReagent(id, actualAmount); if (_bufferModeTransfer) { EntitySystem.Get <SolutionContainerSystem>() .TryAddReagent(beaker, beakerSolution, id, actualAmount, out var _); // beakerSolution.Solution.AddReagent(id, actualAmount); } break; } } } else { foreach (var reagent in beakerSolution.Contents) { if (reagent.ReagentId == id) { FixedPoint2 actualAmount; if (amount == FixedPoint2.New(-1)) { actualAmount = reagent.Quantity; } else { actualAmount = FixedPoint2.Min(reagent.Quantity, amount); } EntitySystem.Get <SolutionContainerSystem>().TryRemoveReagent(beaker, beakerSolution, id, actualAmount); BufferSolution.AddReagent(id, actualAmount); break; } } } _label = GenerateLabel(); UpdateUserInterface(); }
public bool TryDoInject(EntityUid?target, EntityUid user) { if (!EligibleEntity(target, _entMan)) { return(false); } string?msgFormat = null; if (target == user) { msgFormat = "hypospray-component-inject-self-message"; } else if (EligibleEntity(user, _entMan) && ClumsyComponent.TryRollClumsy(user, ClumsyFailChance)) { msgFormat = "hypospray-component-inject-self-clumsy-message"; target = user; } var solutionsSys = EntitySystem.Get <SolutionContainerSystem>(); solutionsSys.TryGetSolution(Owner, SolutionName, out var hypoSpraySolution); if (hypoSpraySolution == null || hypoSpraySolution.CurrentVolume == 0) { user.PopupMessageCursor(Loc.GetString("hypospray-component-empty-message")); return(true); } if (!solutionsSys.TryGetInjectableSolution(target.Value, out var targetSolution)) { user.PopupMessage(user, Loc.GetString("hypospray-cant-inject", ("target", Identity.Entity(target.Value, _entMan)))); return(false); } user.PopupMessage(Loc.GetString(msgFormat ?? "hypospray-component-inject-other-message", ("other", target))); if (target != user) { target.Value.PopupMessage(Loc.GetString("hypospray-component-feel-prick-message")); var meleeSys = EntitySystem.Get <MeleeWeaponSystem>(); var angle = Angle.FromWorldVec(_entMan.GetComponent <TransformComponent>(target.Value).WorldPosition - _entMan.GetComponent <TransformComponent>(user).WorldPosition); meleeSys.SendLunge(angle, user); } SoundSystem.Play(_injectSound.GetSound(), Filter.Pvs(user), user); // Get transfer amount. May be smaller than _transferAmount if not enough room var realTransferAmount = FixedPoint2.Min(TransferAmount, targetSolution.AvailableVolume); if (realTransferAmount <= 0) { user.PopupMessage(user, Loc.GetString("hypospray-component-transfer-already-full-message", ("owner", target))); return(true); } // Move units from attackSolution to targetSolution var removedSolution = EntitySystem.Get <SolutionContainerSystem>() .SplitSolution(Owner, hypoSpraySolution, realTransferAmount); if (!targetSolution.CanAddSolution(removedSolution)) { return(true); } removedSolution.DoEntityReaction(target.Value, ReactionMethod.Injection); EntitySystem.Get <SolutionContainerSystem>().TryAddSolution(target.Value, targetSolution, removedSolution);
private void TryCreatePackage(EntityUid user, UiAction action, string label, int pillAmount, int bottleAmount) { if (BufferSolution.TotalVolume == 0) { user.PopupMessageCursor(Loc.GetString("chem-master-window-buffer-empty-text")); return; } if (action == UiAction.CreateBottles) { var individualVolume = BufferSolution.TotalVolume / FixedPoint2.New(bottleAmount); if (individualVolume < FixedPoint2.New(1)) { user.PopupMessageCursor(Loc.GetString("chem-master-window-buffer-low-text")); return; } var actualVolume = FixedPoint2.Min(individualVolume, FixedPoint2.New(30)); for (int i = 0; i < bottleAmount; i++) { var bottle = _entities.SpawnEntity("ChemistryEmptyBottle01", _entities.GetComponent <TransformComponent>(Owner).Coordinates); //Adding label LabelComponent labelComponent = bottle.EnsureComponent <LabelComponent>(); labelComponent.OriginalName = _entities.GetComponent <MetaDataComponent>(bottle).EntityName; string val = _entities.GetComponent <MetaDataComponent>(bottle).EntityName + $" ({label})"; _entities.GetComponent <MetaDataComponent>(bottle).EntityName = val; labelComponent.CurrentLabel = label; var bufferSolution = BufferSolution.SplitSolution(actualVolume); var bottleSolution = EntitySystem.Get <SolutionContainerSystem>().EnsureSolution(bottle, "drink"); EntitySystem.Get <SolutionContainerSystem>().TryAddSolution(bottle, bottleSolution, bufferSolution); //Try to give them the bottle if (_entities.TryGetComponent <HandsComponent?>(user, out var hands) && _entities.TryGetComponent <SharedItemComponent?>(bottle, out var item)) { if (hands.CanPutInHand(item)) { hands.PutInHand(item); continue; } } //Put it on the floor _entities.GetComponent <TransformComponent>(bottle).Coordinates = _entities.GetComponent <TransformComponent>(user).Coordinates; //Give it an offset bottle.RandomOffset(0.2f); } } else //Pills { var individualVolume = BufferSolution.TotalVolume / FixedPoint2.New(pillAmount); if (individualVolume < FixedPoint2.New(1)) { user.PopupMessageCursor(Loc.GetString("chem-master-window-buffer-low-text")); return; } var actualVolume = FixedPoint2.Min(individualVolume, FixedPoint2.New(50)); for (int i = 0; i < pillAmount; i++) { var pill = _entities.SpawnEntity("pill", _entities.GetComponent <TransformComponent>(Owner).Coordinates); //Adding label LabelComponent labelComponent = pill.EnsureComponent <LabelComponent>(); labelComponent.OriginalName = _entities.GetComponent <MetaDataComponent>(pill).EntityName; string val = _entities.GetComponent <MetaDataComponent>(pill).EntityName + $" ({label})"; _entities.GetComponent <MetaDataComponent>(pill).EntityName = val; labelComponent.CurrentLabel = label; var bufferSolution = BufferSolution.SplitSolution(actualVolume); var pillSolution = EntitySystem.Get <SolutionContainerSystem>().EnsureSolution(pill, "food"); EntitySystem.Get <SolutionContainerSystem>().TryAddSolution(pill, pillSolution, bufferSolution); //Change pill Sprite component state if (!_entities.TryGetComponent(pill, out SpriteComponent? sprite)) { return; } sprite?.LayerSetState(0, "pill" + _pillType); //Try to give them the bottle if (_entities.TryGetComponent <HandsComponent?>(user, out var hands) && _entities.TryGetComponent <SharedItemComponent?>(pill, out var item)) { if (hands.CanPutInHand(item)) { hands.PutInHand(item); continue; } } //Put it on the floor _entities.GetComponent <TransformComponent>(pill).Coordinates = _entities.GetComponent <TransformComponent>(user).Coordinates; //Give it an offset pill.RandomOffset(0.2f); } } if (_bufferSolution?.Contents.Count == 0) { _label = ""; } UpdateUserInterface(); }
async Task <bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { if (!eventArgs.CanReach || eventArgs.Target == null) { return(false); } var target = eventArgs.Target !.Value; var solutionsSys = EntitySystem.Get <SolutionContainerSystem>(); var transferSystem = EntitySystem.Get <SolutionTransferSystem>(); //Special case for reagent tanks, because normally clicking another container will give solution, not take it. if (CanReceive && !_entities.HasComponent <RefillableSolutionComponent>(target) && // target must not be refillable (e.g. Reagent Tanks) solutionsSys.TryGetDrainableSolution(target, out var targetDrain) && // target must be drainable _entities.TryGetComponent(Owner, out RefillableSolutionComponent? refillComp) && solutionsSys.TryGetRefillableSolution(Owner, out var ownerRefill, refillable: refillComp)) { var transferAmount = TransferAmount; // This is the player-configurable transfer amount of "Owner," not the target reagent tank. if (_entities.TryGetComponent(Owner, out RefillableSolutionComponent? refill) && refill.MaxRefill != null) // Owner is the entity receiving solution from target. { transferAmount = FixedPoint2.Min(transferAmount, (FixedPoint2)refill.MaxRefill); // if the receiver has a smaller transfer limit, use that instead } var transferred = transferSystem.Transfer(eventArgs.User, target, targetDrain, Owner, ownerRefill, transferAmount); if (transferred > 0) { var toTheBrim = ownerRefill.AvailableVolume == 0; var msg = toTheBrim ? "comp-solution-transfer-fill-fully" : "comp-solution-transfer-fill-normal"; target.PopupMessage(eventArgs.User, Loc.GetString(msg, ("owner", eventArgs.Target), ("amount", transferred), ("target", Owner))); return(true); } } // if target is refillable, and owner is drainable if (CanSend && solutionsSys.TryGetRefillableSolution(target, out var targetRefill) && solutionsSys.TryGetDrainableSolution(Owner, out var ownerDrain)) { var transferAmount = TransferAmount; if (_entities.TryGetComponent(target, out RefillableSolutionComponent? refill) && refill.MaxRefill != null) { transferAmount = FixedPoint2.Min(transferAmount, (FixedPoint2)refill.MaxRefill); } var transferred = transferSystem.Transfer(eventArgs.User, Owner, ownerDrain, target, targetRefill, transferAmount); if (transferred > 0) { Owner.PopupMessage(eventArgs.User, Loc.GetString("comp-solution-transfer-transfer-solution", ("amount", transferred), ("target", target))); return(true); } } return(false); }