private bool TrySliceFood(EntityUid uid, EntityUid user, EntityUid usedItem, SliceableFoodComponent?component = null, FoodComponent?food = null, TransformComponent?transform = null) { if (!Resolve(uid, ref component, ref food, ref transform) || string.IsNullOrEmpty(component.Slice)) { return(false); } if (!_solutionContainerSystem.TryGetSolution(uid, food.SolutionName, out var solution)) { return(false); } if (!EntityManager.TryGetComponent(usedItem, out UtensilComponent ? utensil) || (utensil.Types & UtensilType.Knife) == 0) { return(false); } var sliceUid = EntityManager.SpawnEntity(component.Slice, transform.Coordinates); var lostSolution = _solutionContainerSystem.SplitSolution(uid, solution, solution.CurrentVolume / FixedPoint2.New(component.Count)); // Fill new slice FillSlice(sliceUid, lostSolution); if (EntityManager.TryGetComponent(user, out HandsComponent? handsComponent)) { if (ContainerHelpers.IsInContainer(component.Owner)) { handsComponent.PutInHandOrDrop(EntityManager.GetComponent <ItemComponent>(sliceUid)); } } SoundSystem.Play(Filter.Pvs(uid), component.Sound.GetSound(), transform.Coordinates, AudioParams.Default.WithVolume(-2)); component.Count--; // If someone makes food proto with 1 slice... if (component.Count < 1) { EntityManager.DeleteEntity(uid); return(true); } // Split last slice if (component.Count == 1) { var lastSlice = EntityManager.SpawnEntity(component.Slice, transform.Coordinates); // Fill last slice with the rest of the solution FillSlice(lastSlice, solution); EntityManager.DeleteEntity(uid); } return(true); }
public override Outcome Execute(float frameTime) { if (_interactionCooldown >= 0) { _interactionCooldown -= frameTime; return(Outcome.Continuing); } var entities = IoCManager.Resolve <IEntityManager>(); // TODO: Also have this check storage a la backpack etc. if (entities.Deleted(_target) || !entities.TryGetComponent(_owner, out HandsComponent? handsComponent) || !entities.TryGetComponent(_target, out SharedItemComponent? itemComponent)) { return(Outcome.Failed); } FoodComponent?foodComponent = null; foreach (var slot in handsComponent.ActivePriorityEnumerable()) { if (handsComponent.GetItem(slot) != itemComponent) { continue; } handsComponent.ActiveHand = slot; if (!entities.TryGetComponent(_target, out foodComponent)) { return(Outcome.Failed); } // This should also implicitly open it. handsComponent.ActivateItem(); _interactionCooldown = IoCManager.Resolve <IRobustRandom>().NextFloat() + 0.5f; } if (foodComponent == null) { return(Outcome.Failed); } if ((!entities.EntityExists(_target) ? EntityLifeStage.Deleted : entities.GetComponent <MetaDataComponent>(_target).EntityLifeStage) >= EntityLifeStage.Deleted || foodComponent.UsesRemaining == 0 || entities.TryGetComponent(_owner, out HungerComponent? hungerComponent) && hungerComponent.CurrentHunger >= hungerComponent.HungerThresholds[HungerThreshold.Okay]) { return(Outcome.Success); } return(Outcome.Continuing); }
/// <summary> /// Force feeds someone remotely. Does not require utensils (well, not the normal type anyways). /// </summary> public void ProjectileForceFeed(EntityUid uid, EntityUid target, EntityUid?user, FoodComponent?food = null, BodyComponent?body = null) { // TODO: Combine with regular feeding because holy code duplication batman. if (!Resolve(uid, ref food, false) || !Resolve(target, ref body, false)) { return; } if (IsMouthBlocked(target)) { return; } if (!_solutionContainerSystem.TryGetSolution(uid, food.SolutionName, out var foodSolution)) { return; } if (!_bodySystem.TryGetComponentsOnMechanisms <StomachComponent>(target, out var stomachs, body)) { return; } if (food.UsesRemaining <= 0) { DeleteAndSpawnTrash(food); } var firstStomach = stomachs.FirstOrNull( stomach => _stomachSystem.CanTransferSolution(((IComponent)stomach.Comp).Owner, foodSolution)); if (firstStomach == null) { return; } // logging if (user == null) { _logSystem.Add(LogType.ForceFeed, $"{ToPrettyString(uid):food} {SolutionContainerSystem.ToPrettyString(foodSolution):solution} was thrown into the mouth of {ToPrettyString(target):target}"); } else { _logSystem.Add(LogType.ForceFeed, $"{ToPrettyString(user.Value):user} threw {ToPrettyString(uid):food} {SolutionContainerSystem.ToPrettyString(foodSolution):solution} into the mouth of {ToPrettyString(target):target}"); } var filter = user == null?Filter.Entities(target) : Filter.Entities(target, user.Value); _popupSystem.PopupEntity(Loc.GetString(food.EatMessage), target, filter); foodSolution.DoEntityReaction(uid, ReactionMethod.Ingestion); _stomachSystem.TryTransferSolution(((IComponent)firstStomach.Value.Comp).Owner, foodSolution, firstStomach.Value.Comp); SoundSystem.Play(Filter.Pvs(target), food.UseSound.GetSound(), target, AudioParams.Default.WithVolume(-1f)); if (string.IsNullOrEmpty(food.TrashPrototype)) { EntityManager.QueueDeleteEntity(food.Owner); } else { DeleteAndSpawnTrash(food); } }
/// <summary> /// Attempts to force feed a target. Returns true if any interaction occurred, including pop-up generation /// </summary> public bool TryForceFeed(EntityUid uid, EntityUid user, EntityUid target, 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 (!EntityManager.HasComponent <SharedBodyComponent>(target)) { return(false); } if (!_solutionContainerSystem.TryGetSolution(uid, food.SolutionName, out var foodSolution)) { 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 (IsMouthBlocked(target, user)) { return(true); } if (!TryGetRequiredUtensils(user, food, out var utensils)) { return(true); } EntityManager.TryGetComponent(user, out MetaDataComponent? meta); var userName = meta?.EntityName ?? string.Empty; _popupSystem.PopupEntity(Loc.GetString("food-system-force-feed", ("user", userName)), user, Filter.Entities(target)); food.CancelToken = new(); _doAfterSystem.DoAfter(new DoAfterEventArgs(user, food.ForceFeedDelay, food.CancelToken.Token, target) { BreakOnUserMove = true, BreakOnDamage = true, BreakOnStun = true, BreakOnTargetMove = true, MovementThreshold = 1.0f, TargetFinishedEvent = new ForceFeedEvent(user, food, foodSolution, utensils), BroadcastCancelledEvent = new ForceFeedCancelledEvent(food) }); // logging _logSystem.Add(LogType.ForceFeed, LogImpact.Medium, $"{user} is forcing {target} to eat {uid}"); return(true); }
/// <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); }