private void HandleChemicalPayloadTrigger(EntityUid uid, ChemicalPayloadComponent component, TriggerEvent args) { if (component.BeakerSlotA.Item is not EntityUid beakerA || component.BeakerSlotB.Item is not EntityUid beakerB || !TryComp(beakerA, out FitsInDispenserComponent? compA) || !TryComp(beakerB, out FitsInDispenserComponent? compB) || !_solutionSystem.TryGetSolution(beakerA, compA.Solution, out var solutionA) || !_solutionSystem.TryGetSolution(beakerB, compB.Solution, out var solutionB) || solutionA.TotalVolume == 0 || solutionB.TotalVolume == 0) { return; } var solStringA = SolutionContainerSystem.ToPrettyString(solutionA); var solStringB = SolutionContainerSystem.ToPrettyString(solutionB); _logSystem.Add(LogType.ChemicalReaction, $"Chemical bomb payload {ToPrettyString(uid):payload} at {Transform(uid).MapPosition:location} is combining two solutions: {solStringA:solutionA} and {solStringB:solutionB}"); solutionA.MaxVolume += solutionB.MaxVolume; _solutionSystem.TryAddSolution(beakerA, solutionA, solutionB); solutionB.RemoveAllSolution(); // The grenade might be a dud. Redistribute solution: var tmpSol = _solutionSystem.SplitSolution(beakerA, solutionA, solutionA.CurrentVolume * solutionB.MaxVolume / solutionA.MaxVolume); _solutionSystem.TryAddSolution(beakerB, solutionB, tmpSol); solutionA.MaxVolume -= solutionB.MaxVolume; _solutionSystem.UpdateChemicals(beakerA, solutionA, false); }
private void SpillOnLand(EntityUid uid, SpillableComponent component, LandEvent args) { if (!_solutionContainerSystem.TryGetSolution(uid, component.SolutionName, out var solution)) { return; } if (args.User != null) { _logSystem.Add(LogType.Landed, $"{ToPrettyString(uid):entity} spilled a solution {SolutionContainerSystem.ToPrettyString(solution):solution} on landing"); } var drainedSolution = _solutionContainerSystem.Drain(uid, solution, solution.DrainAvailable); SpillAt(drainedSolution, EntityManager.GetComponent <TransformComponent>(uid).Coordinates, "PuddleSmear"); }
private bool TryDrink(EntityUid user, EntityUid target, DrinkComponent drink) { // cannot stack do-afters if (drink.CancelToken != null) { drink.CancelToken.Cancel(); drink.CancelToken = null; return(true); } if (!EntityManager.HasComponent <SharedBodyComponent>(target)) { return(false); } if (!drink.Opened) { _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-not-open", ("owner", EntityManager.GetComponent <MetaDataComponent>(drink.Owner).EntityName)), drink.Owner, Filter.Entities(user)); return(true); } if (!_solutionContainerSystem.TryGetDrainableSolution(drink.Owner, out var drinkSolution) || drinkSolution.DrainAvailable <= 0) { _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-is-empty", ("entity", EntityManager.GetComponent <MetaDataComponent>(drink.Owner).EntityName)), drink.Owner, Filter.Entities(user)); return(true); } if (_foodSystem.IsMouthBlocked(target, user)) { return(true); } if (!_interactionSystem.InRangeUnobstructed(user, drink.Owner, popup: true)) { return(true); } var forceDrink = user != target; if (forceDrink) { EntityManager.TryGetComponent(user, out MetaDataComponent? meta); var userName = meta?.EntityName ?? string.Empty; _popupSystem.PopupEntity(Loc.GetString("drink-component-force-feed", ("user", userName)), user, Filter.Entities(target)); // logging _logSystem.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(user):user} is forcing {ToPrettyString(target):target} to drink {ToPrettyString(drink.Owner):drink} {SolutionContainerSystem.ToPrettyString(drinkSolution)}"); } drink.CancelToken = new CancellationTokenSource(); _doAfterSystem.DoAfter(new DoAfterEventArgs(user, forceDrink ? drink.ForceFeedDelay : drink.Delay, drink.CancelToken.Token, target) { BreakOnUserMove = true, BreakOnDamage = true, BreakOnStun = true, BreakOnTargetMove = true, MovementThreshold = 0.01f, TargetFinishedEvent = new DrinkEvent(user, drink, drinkSolution), BroadcastCancelledEvent = new DrinkCancelledEvent(drink), NeedHand = true, }); return(true); }
public bool TryFeed(EntityUid user, EntityUid target, FoodComponent food) { // if currently being used to feed, cancel that action. if (food.CancelToken != null) { food.CancelToken.Cancel(); food.CancelToken = null; return(true); } if (food.Owner == user || //Suppresses self-eating EntityManager.TryGetComponent <MobStateComponent>(food.Owner, out var mobState) && mobState.IsAlive()) // Suppresses eating alive mobs { return(false); } // Target can't be fed if (!EntityManager.HasComponent <SharedBodyComponent>(target)) { return(false); } if (!_solutionContainerSystem.TryGetSolution(food.Owner, food.SolutionName, out var foodSolution)) { return(false); } if (food.UsesRemaining <= 0) { _popupSystem.PopupEntity(Loc.GetString("food-system-try-use-food-is-empty", ("entity", food.Owner)), user, Filter.Entities(user)); DeleteAndSpawnTrash(food, user); return(false); } if (IsMouthBlocked(target, user)) { return(false); } if (!TryGetRequiredUtensils(user, food, out var utensils)) { return(false); } if (!_interactionSystem.InRangeUnobstructed(user, food.Owner, popup: true)) { return(true); } var forceFeed = user != target; food.CancelToken = new CancellationTokenSource(); if (forceFeed) { 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)); // logging _logSystem.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(user):user} is forcing {ToPrettyString(target):target} to eat {ToPrettyString(food.Owner):food} {SolutionContainerSystem.ToPrettyString(foodSolution)}"); } var moveBreak = user != target; _doAfterSystem.DoAfter(new DoAfterEventArgs(user, forceFeed ? food.ForceFeedDelay : food.Delay, food.CancelToken.Token, target) { BreakOnUserMove = moveBreak, BreakOnDamage = true, BreakOnStun = true, BreakOnTargetMove = moveBreak, MovementThreshold = 0.01f, TargetFinishedEvent = new FeedEvent(user, food, foodSolution, utensils), BroadcastCancelledEvent = new ForceFeedCancelledEvent(food), NeedHand = true, }); return(true); }
/// <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> /// Send informative pop-up messages and wait for a do-after to complete. /// </summary> private void InjectDoAfter(InjectorComponent component, EntityUid user, EntityUid target) { // Create a pop-up for the user _popup.PopupEntity(Loc.GetString("injector-component-injecting-user"), target, Filter.Entities(user)); if (!_solutions.TryGetSolution(component.Owner, InjectorComponent.SolutionName, out var solution)) { return; } var actualDelay = MathF.Max(component.Delay, 1f); if (user != target) { // Create a pop-up for the target var userName = MetaData(user).EntityName; _popup.PopupEntity(Loc.GetString("injector-component-injecting-target", ("user", userName)), user, Filter.Entities(target)); // Check if the target is incapacitated or in combat mode and modify time accordingly. if (TryComp <MobStateComponent>(target, out var mobState) && mobState.IsIncapacitated()) { actualDelay /= 2; } else if (TryComp <CombatModeComponent>(target, out var combat) && combat.IsInCombatMode) { // Slightly increase the delay when the target is in combat mode. Helps prevents cheese injections in // combat with fast syringes & lag. actualDelay += 1; } // Add an admin log, using the "force feed" log type. It's not quite feeding, but the effect is the same. if (component.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject) { _logs.Add(LogType.ForceFeed, $"{EntityManager.ToPrettyString(user):user} is attempting to inject {EntityManager.ToPrettyString(target):target} with a solution {SolutionContainerSystem.ToPrettyString(solution):solution}"); } } else { // Self-injections take half as long. actualDelay /= 2; if (component.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject) { _logs.Add(LogType.Ingestion, $"{EntityManager.ToPrettyString(user):user} is attempting to inject themselves with a solution {SolutionContainerSystem.ToPrettyString(solution):solution}."); } } component.CancelToken = new CancellationTokenSource(); _doAfter.DoAfter(new DoAfterEventArgs(user, actualDelay, component.CancelToken.Token, target) { BreakOnUserMove = true, BreakOnDamage = true, BreakOnStun = true, BreakOnTargetMove = true, MovementThreshold = 0.1f, BroadcastFinishedEvent = new InjectionCompleteEvent() { Component = component, User = user, Target = target, }, BroadcastCancelledEvent = new InjectionCancelledEvent() { Component = component, } }); }
/// <summary> /// Attempt to force someone else to drink some of a drink. Returns true if any interaction took place, /// including generation of pop-up messages. /// </summary> private bool TryForceDrink(EntityUid uid, EntityUid userUid, EntityUid targetUid, DrinkComponent?drink = null) { if (!Resolve(uid, ref drink)) { return(false); } // cannot stack do-afters if (drink.CancelToken != null) { drink.CancelToken.Cancel(); drink.CancelToken = null; return(true); } if (!EntityManager.HasComponent <SharedBodyComponent>(targetUid)) { return(false); } 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 (!_solutionContainerSystem.TryGetDrainableSolution(uid, 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 (_foodSystem.IsMouthBlocked(targetUid, userUid)) { return(true); } EntityManager.TryGetComponent(userUid, out MetaDataComponent? meta); var userName = meta?.EntityName ?? string.Empty; _popupSystem.PopupEntity(Loc.GetString("drink-component-force-feed", ("user", userName)), userUid, Filter.Entities(targetUid)); drink.CancelToken = new(); _doAfterSystem.DoAfter(new DoAfterEventArgs(userUid, drink.ForceFeedDelay, drink.CancelToken.Token, targetUid) { BreakOnUserMove = true, BreakOnDamage = true, BreakOnStun = true, BreakOnTargetMove = true, MovementThreshold = 1.0f, TargetFinishedEvent = new ForceDrinkEvent(userUid, drink, drinkSolution), BroadcastCancelledEvent = new ForceDrinkCancelledEvent(drink), }); // logging _logSystem.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(userUid):user} is forcing {ToPrettyString(targetUid):target} to drink {ToPrettyString(uid):drink} {SolutionContainerSystem.ToPrettyString(drinkSolution):solution}"); return(true); }
/// <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, $"{ToPrettyString(user):user} is forcing {ToPrettyString(target):target} to eat {ToPrettyString(uid):food} {SolutionContainerSystem.ToPrettyString(foodSolution):solution}"); return(true); }