2021-12-05 18:09:01 +01:00
using System.Collections.Generic ;
2021-11-21 10:35:09 +03:00
using Content.Server.Body.Components ;
using Content.Server.Body.Systems ;
using Content.Server.Chemistry.EntitySystems ;
2021-11-29 16:27:15 +13:00
using Content.Server.DoAfter ;
2021-11-21 10:35:09 +03:00
using Content.Server.Hands.Components ;
using Content.Server.Nutrition.Components ;
using Content.Server.Popups ;
2021-11-29 16:27:15 +13:00
using Content.Shared.ActionBlocker ;
using Content.Shared.Administration.Logs ;
2021-11-21 10:35:09 +03:00
using Content.Shared.Body.Components ;
using Content.Shared.Chemistry.Reagent ;
2021-11-29 16:27:15 +13:00
using Content.Shared.Database ;
2021-11-21 10:35:09 +03:00
using Content.Shared.FixedPoint ;
using Content.Shared.Interaction ;
using Content.Shared.Interaction.Helpers ;
2021-11-30 13:45:33 +03:00
using Content.Shared.MobState.Components ;
2021-11-21 10:35:09 +03:00
using Content.Shared.Verbs ;
using Robust.Shared.Audio ;
using Robust.Shared.GameObjects ;
using Robust.Shared.IoC ;
using Robust.Shared.Localization ;
using Robust.Shared.Player ;
2021-11-30 18:25:02 -07:00
using Robust.Shared.Utility ;
2021-12-07 19:19:26 +13:00
using Content.Shared.Hands ;
2021-12-30 22:56:10 +01:00
using Content.Shared.Inventory ;
using Content.Shared.Item ;
2021-11-21 10:35:09 +03:00
namespace Content.Server.Nutrition.EntitySystems
{
/// <summary>
/// Handles feeding attempts both on yourself and on the target.
/// </summary>
internal class FoodSystem : EntitySystem
{
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default ! ;
[Dependency] private readonly BodySystem _bodySystem = default ! ;
[Dependency] private readonly StomachSystem _stomachSystem = default ! ;
[Dependency] private readonly PopupSystem _popupSystem = default ! ;
[Dependency] private readonly UtensilSystem _utensilSystem = default ! ;
2021-11-29 16:27:15 +13:00
[Dependency] private readonly DoAfterSystem _doAfterSystem = default ! ;
[Dependency] private readonly SharedAdminLogSystem _logSystem = default ! ;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default ! ;
2021-12-30 22:56:10 +01:00
[Dependency] private readonly InventorySystem _inventorySystem = default ! ;
2021-11-21 10:35:09 +03:00
public override void Initialize ( )
{
base . Initialize ( ) ;
SubscribeLocalEvent < FoodComponent , UseInHandEvent > ( OnUseFoodInHand ) ;
SubscribeLocalEvent < FoodComponent , AfterInteractEvent > ( OnFeedFood ) ;
2021-12-07 19:19:26 +13:00
SubscribeLocalEvent < FoodComponent , HandDeselectedEvent > ( OnFoodDeselected ) ;
2021-11-21 10:35:09 +03:00
SubscribeLocalEvent < FoodComponent , GetInteractionVerbsEvent > ( AddEatVerb ) ;
2021-11-29 16:27:15 +13:00
SubscribeLocalEvent < SharedBodyComponent , ForceFeedEvent > ( OnForceFeed ) ;
SubscribeLocalEvent < ForceFeedCancelledEvent > ( OnForceFeedCancelled ) ;
2021-12-05 07:56:27 +13:00
SubscribeLocalEvent < InventoryComponent , IngestionAttemptEvent > ( OnInventoryIngestAttempt ) ;
2021-11-21 10:35:09 +03:00
}
2021-12-07 19:19:26 +13:00
/// <summary>
/// If the user is currently force feeding someone, this cancels the attempt if they swap hands or otherwise
/// loose the item. Prevents force-feeding dual-wielding.
/// </summary>
private void OnFoodDeselected ( EntityUid uid , FoodComponent component , HandDeselectedEvent args )
{
if ( component . CancelToken ! = null )
{
component . CancelToken . Cancel ( ) ;
component . CancelToken = null ;
}
2021-11-21 10:35:09 +03:00
}
/// <summary>
/// Eat item
/// </summary>
private void OnUseFoodInHand ( EntityUid uid , FoodComponent foodComponent , UseInHandEvent ev )
{
if ( ev . Handled )
return ;
2021-12-05 18:09:01 +01:00
if ( ! _actionBlockerSystem . CanInteract ( ev . User ) | | ! _actionBlockerSystem . CanUse ( ev . User ) )
2021-11-29 16:27:15 +13:00
return ;
2021-12-05 18:09:01 +01:00
if ( ! ev . User . InRangeUnobstructed ( uid , popup : true ) )
2021-11-29 16:27:15 +13:00
{
2021-11-21 10:35:09 +03:00
ev . Handled = true ;
2021-11-29 16:27:15 +13:00
return ;
}
2021-12-05 18:09:01 +01:00
ev . Handled = TryUseFood ( uid , ev . User ) ;
2021-11-21 10:35:09 +03:00
}
/// <summary>
/// Feed someone else
/// </summary>
2021-11-29 16:27:15 +13:00
private void OnFeedFood ( EntityUid uid , FoodComponent foodComponent , AfterInteractEvent args )
2021-11-21 10:35:09 +03:00
{
2021-12-06 14:00:39 +01:00
if ( args . Handled | | args . Target = = null )
2021-11-21 10:35:09 +03:00
return ;
2021-12-05 18:09:01 +01:00
if ( ! _actionBlockerSystem . CanInteract ( args . User ) | | ! _actionBlockerSystem . CanUse ( args . User ) )
2021-11-29 16:27:15 +13:00
return ;
2021-12-05 18:09:01 +01:00
if ( ! args . User . InRangeUnobstructed ( uid , popup : true ) )
2021-11-29 16:27:15 +13:00
{
args . Handled = true ;
return ;
}
2021-12-05 18:09:01 +01:00
if ( args . User = = args . Target )
2021-11-29 16:27:15 +13:00
{
2021-12-05 18:09:01 +01:00
args . Handled = TryUseFood ( uid , args . User ) ;
2021-11-29 16:27:15 +13:00
return ;
}
2021-12-05 18:09:01 +01:00
if ( ! args . User . InRangeUnobstructed ( args . Target . Value , popup : true ) )
2021-11-29 16:27:15 +13:00
{
args . Handled = true ;
return ;
}
2021-11-30 18:25:02 -07:00
2021-12-05 18:09:01 +01:00
args . Handled = TryForceFeed ( uid , args . User , args . Target . Value ) ;
2021-11-21 10:35:09 +03:00
}
/// <summary>
2021-11-29 16:27:15 +13:00
/// Tries to eat some food
2021-11-21 10:35:09 +03:00
/// </summary>
/// <param name="uid">Food entity.</param>
2021-12-05 18:09:01 +01:00
/// <param name="user">Feeding initiator.</param>
2021-11-29 16:27:15 +13:00
/// <returns>True if an interaction occurred (i.e., food was consumed, or a pop-up message was created)</returns>
2021-12-05 18:09:01 +01:00
public bool TryUseFood ( EntityUid uid , EntityUid user , FoodComponent ? food = null )
2021-11-21 10:35:09 +03:00
{
2021-12-05 18:09:01 +01:00
if ( ! Resolve ( uid , ref food ) )
2021-11-21 10:35:09 +03:00
return false ;
2021-12-07 19:19:26 +13:00
// if currently being used to force-feed, cancel that action.
2021-12-07 17:48:49 +01:00
if ( food . CancelToken ! = null )
2021-12-07 19:19:26 +13:00
{
2021-12-07 17:48:49 +01:00
food . CancelToken . Cancel ( ) ;
food . CancelToken = null ;
2021-12-07 19:19:26 +13:00
return true ;
}
2021-12-05 18:09:01 +01:00
if ( uid = = user | | //Suppresses self-eating
2021-11-30 13:45:33 +03:00
EntityManager . TryGetComponent < MobStateComponent > ( uid , out var mobState ) & & mobState . IsAlive ( ) ) // Suppresses eating alive mobs
return false ;
2021-12-05 18:09:01 +01:00
if ( ! _solutionContainerSystem . TryGetSolution ( uid , food . SolutionName , out var solution ) )
2021-11-21 10:35:09 +03:00
return false ;
2021-12-05 18:09:01 +01:00
if ( food . UsesRemaining < = 0 )
2021-11-21 10:35:09 +03:00
{
2021-12-05 18:09:01 +01:00
_popupSystem . PopupEntity ( Loc . GetString ( "food-system-try-use-food-is-empty" , ( "entity" , uid ) ) , user , Filter . Entities ( user ) ) ;
DeleteAndSpawnTrash ( food , user ) ;
2021-11-29 16:27:15 +13:00
return true ;
2021-11-21 10:35:09 +03:00
}
2021-12-05 18:09:01 +01:00
if ( ! EntityManager . TryGetComponent ( user , out SharedBodyComponent ? body ) | |
! _bodySystem . TryGetComponentsOnMechanisms < StomachComponent > ( user , out var stomachs , body ) )
2021-11-21 10:35:09 +03:00
return false ;
2021-12-07 17:48:49 +01:00
if ( IsMouthBlocked ( user , user ) )
2021-12-03 03:51:05 +13:00
{
return true ;
}
2021-11-21 10:35:09 +03:00
var usedUtensils = new List < UtensilComponent > ( ) ;
2021-12-05 18:09:01 +01:00
if ( ! TryGetRequiredUtensils ( user , food , out var utensils ) )
2021-11-29 16:27:15 +13:00
return true ;
2021-11-21 10:35:09 +03:00
2021-12-05 18:09:01 +01:00
if ( ! user . InRangeUnobstructed ( uid , popup : true ) )
2021-11-29 16:27:15 +13:00
return true ;
2021-11-21 10:35:09 +03:00
2021-12-05 18:09:01 +01:00
var transferAmount = food . TransferAmount ! = null ? FixedPoint2 . Min ( ( FixedPoint2 ) food . TransferAmount , solution . CurrentVolume ) : solution . CurrentVolume ;
2021-11-21 10:35:09 +03:00
var split = _solutionContainerSystem . SplitSolution ( uid , solution , transferAmount ) ;
2021-11-30 18:25:02 -07:00
var firstStomach = stomachs . FirstOrNull (
2021-12-07 22:22:34 +11:00
stomach = > _stomachSystem . CanTransferSolution ( ( stomach . Comp ) . Owner , split ) ) ;
2021-11-21 10:35:09 +03:00
if ( firstStomach = = null )
{
_solutionContainerSystem . TryAddSolution ( uid , solution , split ) ;
2021-12-05 18:09:01 +01:00
_popupSystem . PopupEntity ( Loc . GetString ( "food-system-you-cannot-eat-any-more" ) , user , Filter . Entities ( user ) ) ;
2021-11-29 16:27:15 +13:00
return true ;
2021-11-21 10:35:09 +03:00
}
// TODO: Account for partial transfer.
2021-12-05 18:09:01 +01:00
split . DoEntityReaction ( user , ReactionMethod . Ingestion ) ;
2021-12-07 22:22:34 +11:00
_stomachSystem . TryTransferSolution ( ( firstStomach . Value . Comp ) . Owner , split , firstStomach . Value . Comp ) ;
2021-11-21 10:35:09 +03:00
2021-12-05 18:09:01 +01:00
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 ) ) ;
2021-11-21 10:35:09 +03:00
// Try to break all used utensils
foreach ( var utensil in usedUtensils )
{
2021-12-07 22:22:34 +11:00
_utensilSystem . TryBreak ( ( utensil ) . Owner , user ) ;
2021-11-21 10:35:09 +03:00
}
2021-12-05 18:09:01 +01:00
if ( food . UsesRemaining > 0 )
2021-11-21 10:35:09 +03:00
{
return true ;
}
2021-12-05 18:09:01 +01:00
if ( string . IsNullOrEmpty ( food . TrashPrototype ) )
2021-12-07 22:22:34 +11:00
EntityManager . QueueDeleteEntity ( ( food ) . Owner ) ;
2021-11-29 16:27:15 +13:00
else
2021-12-05 18:09:01 +01:00
DeleteAndSpawnTrash ( food , user ) ;
2021-11-21 10:35:09 +03:00
return true ;
}
2021-12-05 18:09:01 +01:00
private void DeleteAndSpawnTrash ( FoodComponent component , EntityUid ? user = null )
2021-11-21 10:35:09 +03:00
{
//We're empty. Become trash.
2021-12-05 18:09:01 +01:00
var position = EntityManager . GetComponent < TransformComponent > ( component . Owner ) . Coordinates ;
var finisher = EntityManager . SpawnEntity ( component . TrashPrototype , position ) ;
2021-11-21 10:35:09 +03:00
// If the user is holding the item
2021-12-05 18:09:01 +01:00
if ( user ! = null & &
EntityManager . TryGetComponent ( user . Value , out HandsComponent ? handsComponent ) & &
2021-11-21 10:35:09 +03:00
handsComponent . IsHolding ( component . Owner ) )
{
2021-12-07 22:22:34 +11:00
EntityManager . DeleteEntity ( ( component ) . Owner ) ;
2021-11-21 10:35:09 +03:00
// Put the trash in the user's hand
2021-12-30 22:56:10 +01:00
if ( EntityManager . TryGetComponent ( finisher , out SharedItemComponent ? item ) & &
2021-11-21 10:35:09 +03:00
handsComponent . CanPutInHand ( item ) )
{
handsComponent . PutInHand ( item ) ;
}
2021-11-29 16:27:15 +13:00
return ;
2021-11-21 10:35:09 +03:00
}
2021-11-29 16:27:15 +13:00
2021-12-07 22:22:34 +11:00
EntityManager . QueueDeleteEntity ( ( component ) . Owner ) ;
2021-11-21 10:35:09 +03:00
}
private void AddEatVerb ( EntityUid uid , FoodComponent component , GetInteractionVerbsEvent ev )
{
2021-12-07 19:19:26 +13:00
if ( component . CancelToken ! = null )
return ;
2021-12-05 18:09:01 +01:00
if ( uid = = ev . User | |
2021-11-30 13:45:33 +03:00
! ev . CanInteract | |
2021-11-25 05:16:56 +03:00
! ev . CanAccess | |
2021-12-05 18:09:01 +01:00
! EntityManager . TryGetComponent ( ev . User , out SharedBodyComponent ? body ) | |
! _bodySystem . TryGetComponentsOnMechanisms < StomachComponent > ( ev . User , out var stomachs , body ) )
2021-11-30 13:45:33 +03:00
return ;
if ( EntityManager . TryGetComponent < MobStateComponent > ( uid , out var mobState ) & & mobState . IsAlive ( ) )
2021-11-21 10:35:09 +03:00
return ;
2021-12-05 18:09:01 +01:00
Verb verb = new ( )
2021-11-21 10:35:09 +03:00
{
2021-12-05 18:09:01 +01:00
Act = ( ) = >
{
TryUseFood ( uid , ev . User , component ) ;
} ,
Text = Loc . GetString ( "food-system-verb-eat" ) ,
Priority = - 1
2021-11-21 10:35:09 +03:00
} ;
2021-11-30 18:25:02 -07:00
2021-11-21 10:35:09 +03:00
ev . Verbs . Add ( verb ) ;
}
2021-11-29 16:27:15 +13:00
/// <summary>
/// Attempts to force feed a target. Returns true if any interaction occurred, including pop-up generation
/// </summary>
2021-12-05 18:09:01 +01:00
public bool TryForceFeed ( EntityUid uid , EntityUid user , EntityUid target , FoodComponent ? food = null )
2021-11-29 16:27:15 +13:00
{
if ( ! Resolve ( uid , ref food ) )
return false ;
2021-12-07 19:19:26 +13:00
// if currently being used to force-feed, cancel that action.
if ( food . CancelToken ! = null )
{
food . CancelToken . Cancel ( ) ;
food . CancelToken = null ;
return true ;
}
2021-12-05 18:09:01 +01:00
if ( ! EntityManager . HasComponent < SharedBodyComponent > ( target ) )
2021-11-29 16:27:15 +13:00
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" ,
2021-12-05 18:09:01 +01:00
( "entity" , uid ) ) , user , Filter . Entities ( user ) ) ;
DeleteAndSpawnTrash ( food , user ) ;
2021-11-29 16:27:15 +13:00
return true ;
}
2021-12-07 17:48:49 +01:00
if ( IsMouthBlocked ( target , user ) )
2021-12-03 03:51:05 +13:00
{
return true ;
}
2021-12-05 18:09:01 +01:00
if ( ! TryGetRequiredUtensils ( user , food , out var utensils ) )
2021-11-29 16:27:15 +13:00
return true ;
2021-12-05 18:09:01 +01:00
EntityManager . TryGetComponent ( user , out MetaDataComponent ? meta ) ;
2021-11-29 16:27:15 +13:00
var userName = meta ? . EntityName ? ? string . Empty ;
_popupSystem . PopupEntity ( Loc . GetString ( "food-system-force-feed" , ( "user" , userName ) ) ,
2021-12-05 18:09:01 +01:00
user , Filter . Entities ( target ) ) ;
2021-11-29 16:27:15 +13:00
2021-12-07 19:19:26 +13:00
food . CancelToken = new ( ) ;
2021-12-07 17:48:49 +01:00
_doAfterSystem . DoAfter ( new DoAfterEventArgs ( user , food . ForceFeedDelay , food . CancelToken . Token , target )
2021-11-29 16:27:15 +13:00
{
BreakOnUserMove = true ,
BreakOnDamage = true ,
BreakOnStun = true ,
BreakOnTargetMove = true ,
MovementThreshold = 1.0f ,
2021-12-05 18:09:01 +01:00
TargetFinishedEvent = new ForceFeedEvent ( user , food , foodSolution , utensils ) ,
2021-11-29 16:27:15 +13:00
BroadcastCancelledEvent = new ForceFeedCancelledEvent ( food )
} ) ;
// logging
2021-12-14 00:22:58 +13:00
_logSystem . Add ( LogType . ForceFeed , LogImpact . Medium , $"{ToPrettyString(user):user} is forcing {ToPrettyString(target):target} to eat {ToPrettyString(uid):food} {SolutionContainerSystem.ToPrettyString(foodSolution):solution}" ) ;
2021-11-30 18:25:02 -07:00
2021-11-29 16:27:15 +13:00
return true ;
}
private void OnForceFeed ( EntityUid uid , SharedBodyComponent body , ForceFeedEvent args )
{
2021-12-07 19:19:26 +13:00
if ( args . Food . Deleted )
return ;
args . Food . CancelToken = null ;
2021-11-29 16:27:15 +13:00
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 ;
2021-12-07 22:22:34 +11:00
var split = _solutionContainerSystem . SplitSolution ( ( args . Food ) . Owner , args . FoodSolution , transferAmount ) ;
2021-11-30 18:25:02 -07:00
var firstStomach = stomachs . FirstOrNull (
2021-12-07 22:22:34 +11:00
stomach = > _stomachSystem . CanTransferSolution ( ( stomach . Comp ) . Owner , split ) ) ;
2021-11-29 16:27:15 +13:00
if ( firstStomach = = null )
{
_solutionContainerSystem . TryAddSolution ( uid , args . FoodSolution , split ) ;
_popupSystem . PopupEntity ( Loc . GetString ( "food-system-you-cannot-eat-any-more-other" ) , uid , Filter . Entities ( args . User ) ) ;
return ;
}
split . DoEntityReaction ( uid , ReactionMethod . Ingestion ) ;
2021-12-07 22:22:34 +11:00
_stomachSystem . TryTransferSolution ( ( firstStomach . Value . Comp ) . Owner , split , firstStomach . Value . Comp ) ;
2021-11-29 16:27:15 +13:00
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 ) ) ;
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 )
{
2021-12-07 22:22:34 +11:00
_utensilSystem . TryBreak ( ( utensil ) . Owner , args . User ) ;
2021-11-29 16:27:15 +13:00
}
if ( args . Food . UsesRemaining > 0 )
return ;
if ( string . IsNullOrEmpty ( args . Food . TrashPrototype ) )
2021-12-07 22:22:34 +11:00
EntityManager . QueueDeleteEntity ( ( args . Food ) . Owner ) ;
2021-11-29 16:27:15 +13:00
else
DeleteAndSpawnTrash ( args . Food , args . User ) ;
}
/// <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 )
{
2022-01-24 13:06:51 -07:00
if ( ! Resolve ( uid , ref food , false ) | | ! Resolve ( target , ref body , false ) )
2021-11-29 16:27:15 +13:00
return ;
2021-12-05 07:56:27 +13:00
if ( IsMouthBlocked ( target ) )
2021-12-03 03:51:05 +13:00
return ;
2021-11-29 16:27:15 +13:00
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 ) ;
2021-11-30 18:25:02 -07:00
var firstStomach = stomachs . FirstOrNull (
2021-12-03 16:30:34 +01:00
stomach = > _stomachSystem . CanTransferSolution ( ( ( IComponent ) stomach . Comp ) . Owner , foodSolution ) ) ;
2021-11-30 18:25:02 -07:00
2021-11-29 16:27:15 +13:00
if ( firstStomach = = null )
return ;
// logging
2021-12-05 18:09:01 +01:00
if ( user = = null )
2021-12-14 00:22:58 +13:00
_logSystem . Add ( LogType . ForceFeed , $"{ToPrettyString(uid):food} {SolutionContainerSystem.ToPrettyString(foodSolution):solution} was thrown into the mouth of {ToPrettyString(target):target}" ) ;
2021-11-29 16:27:15 +13:00
else
2021-12-14 00:22:58 +13:00
_logSystem . Add ( LogType . ForceFeed , $"{ToPrettyString(user.Value):user} threw {ToPrettyString(uid):food} {SolutionContainerSystem.ToPrettyString(foodSolution):solution} into the mouth of {ToPrettyString(target):target}" ) ;
2021-12-03 03:51:05 +13:00
2021-12-05 18:09:01 +01:00
var filter = user = = null ? Filter . Entities ( target ) : Filter . Entities ( target , user . Value ) ;
2021-12-03 03:51:05 +13:00
_popupSystem . PopupEntity ( Loc . GetString ( food . EatMessage ) , target , filter ) ;
2021-11-29 16:27:15 +13:00
foodSolution . DoEntityReaction ( uid , ReactionMethod . Ingestion ) ;
2021-12-03 16:30:34 +01:00
_stomachSystem . TryTransferSolution ( ( ( IComponent ) firstStomach . Value . Comp ) . Owner , foodSolution , firstStomach . Value . Comp ) ;
2021-11-29 16:27:15 +13:00
SoundSystem . Play ( Filter . Pvs ( target ) , food . UseSound . GetSound ( ) , target , AudioParams . Default . WithVolume ( - 1f ) ) ;
if ( string . IsNullOrEmpty ( food . TrashPrototype ) )
2021-12-03 16:30:34 +01:00
EntityManager . QueueDeleteEntity ( ( ( IComponent ) food ) . Owner ) ;
2021-11-29 16:27:15 +13:00
else
DeleteAndSpawnTrash ( food ) ;
}
2021-12-05 18:09:01 +01:00
private bool TryGetRequiredUtensils ( EntityUid user , FoodComponent component ,
2021-11-29 16:27:15 +13:00
out List < UtensilComponent > utensils , HandsComponent ? hands = null )
{
utensils = new ( ) ;
if ( component . Utensil ! = UtensilType . None )
return true ;
2021-12-05 18:09:01 +01:00
if ( ! Resolve ( user , ref hands , false ) )
2021-11-29 16:27:15 +13:00
return false ;
var usedTypes = UtensilType . None ;
foreach ( var item in hands . GetAllHeldItems ( ) )
{
// Is utensil?
2021-12-05 18:09:01 +01:00
if ( ! EntityManager . TryGetComponent ( item . Owner , out UtensilComponent ? utensil ) )
2021-11-29 16:27:15 +13:00
continue ;
if ( ( utensil . Types & component . Utensil ) ! = 0 & & // Acceptable type?
( usedTypes & utensil . Types ) ! = utensil . Types ) // Type is not used already? (removes usage of identical utensils)
{
// Add to used list
usedTypes | = utensil . Types ;
utensils . Add ( utensil ) ;
}
}
// If "required" field is set, try to block eating without proper utensils used
if ( component . UtensilRequired & & ( usedTypes & component . Utensil ) ! = component . Utensil )
{
2021-12-05 18:09:01 +01:00
_popupSystem . PopupEntity ( Loc . GetString ( "food-you-need-to-hold-utensil" , ( "utensil" , component . Utensil ^ usedTypes ) ) , user , Filter . Entities ( user ) ) ;
2021-11-29 16:27:15 +13:00
return false ;
}
return true ;
}
private void OnForceFeedCancelled ( ForceFeedCancelledEvent args )
{
2021-12-07 19:19:26 +13:00
args . Food . CancelToken = null ;
2021-11-29 16:27:15 +13:00
}
2021-12-03 03:51:05 +13:00
/// <summary>
2021-12-05 07:56:27 +13:00
/// Block ingestion attempts based on the equipped mask or head-wear
2021-12-03 03:51:05 +13:00
/// </summary>
2021-12-05 07:56:27 +13:00
private void OnInventoryIngestAttempt ( EntityUid uid , InventoryComponent component , IngestionAttemptEvent args )
2021-12-03 03:51:05 +13:00
{
2021-12-05 07:56:27 +13:00
if ( args . Cancelled )
return ;
2021-12-03 03:51:05 +13:00
2021-12-05 07:56:27 +13:00
IngestionBlockerComponent blocker ;
2021-12-03 03:51:05 +13:00
2021-12-30 22:56:10 +01:00
if ( _inventorySystem . TryGetSlotEntity ( uid , "mask" , out var maskUid ) & &
EntityManager . TryGetComponent ( maskUid , out blocker ) & &
2021-12-05 07:56:27 +13:00
blocker . Enabled )
2021-12-03 03:51:05 +13:00
{
2021-12-30 22:56:10 +01:00
args . Blocker = maskUid ;
2021-12-05 07:56:27 +13:00
args . Cancel ( ) ;
return ;
2021-12-03 03:51:05 +13:00
}
2021-12-30 22:56:10 +01:00
if ( _inventorySystem . TryGetSlotEntity ( uid , "head" , out var headUid ) & &
EntityManager . TryGetComponent ( headUid , out blocker ) & &
2021-12-05 07:56:27 +13:00
blocker . Enabled )
2021-12-03 03:51:05 +13:00
{
2021-12-30 22:56:10 +01:00
args . Blocker = headUid ;
2021-12-05 07:56:27 +13:00
args . Cancel ( ) ;
2021-12-03 03:51:05 +13:00
}
}
2021-11-29 16:27:15 +13:00
2021-12-05 07:56:27 +13:00
/// <summary>
/// Check whether the target's mouth is blocked by equipment (masks or head-wear).
/// </summary>
/// <param name="uid">The target whose equipment is checked</param>
/// <param name="popupUid">Optional entity that will receive an informative pop-up identifying the blocking
/// piece of equipment.</param>
/// <returns></returns>
public bool IsMouthBlocked ( EntityUid uid , EntityUid ? popupUid = null )
2021-11-29 16:27:15 +13:00
{
2021-12-05 07:56:27 +13:00
var attempt = new IngestionAttemptEvent ( ) ;
RaiseLocalEvent ( uid , attempt , false ) ;
if ( attempt . Cancelled & & attempt . Blocker ! = null & & popupUid ! = null )
{
var name = EntityManager . GetComponent < MetaDataComponent > ( attempt . Blocker . Value ) . EntityName ;
_popupSystem . PopupEntity ( Loc . GetString ( "food-system-remove-mask" , ( "entity" , name ) ) ,
uid , Filter . Entities ( popupUid . Value ) ) ;
}
2021-11-29 16:27:15 +13:00
2021-12-05 07:56:27 +13:00
return attempt . Cancelled ;
2021-11-29 16:27:15 +13:00
}
2021-11-21 10:35:09 +03:00
}
}