Add IngestionBlocker component (#5692)
This commit is contained in:
@@ -8,6 +8,7 @@ namespace Content.Client.Entry
|
||||
"Anchorable",
|
||||
"AmmoBox",
|
||||
"Pickaxe",
|
||||
"IngestionBlocker",
|
||||
"Interactable",
|
||||
"CloningPod",
|
||||
"Destructible",
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Nutrition.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// Component that denotes a piece of clothing that blocks the mouth or otherwise prevents eating & drinking.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In the event that more head-wear & mask functionality is added (like identity systems, or raising/lowering of
|
||||
/// masks), then this component might become redundant.
|
||||
/// </remarks>
|
||||
[RegisterComponent, Friend(typeof(FoodSystem), typeof(DrinkSystem))]
|
||||
public class IngestionBlockerComponent : Component
|
||||
{
|
||||
public override string Name => "IngestionBlocker";
|
||||
|
||||
/// <summary>
|
||||
/// Is this component currently blocking consumption.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("enabled")]
|
||||
public bool Enabled { get; set; } = true;
|
||||
}
|
||||
@@ -263,13 +263,8 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_foodSystem.IsMouthBlocked(userUid, out var blocker))
|
||||
{
|
||||
var name = EntityManager.GetComponent<MetaDataComponent>(blocker.Value).EntityName;
|
||||
_popupSystem.PopupEntity(Loc.GetString("food-system-remove-mask", ("entity", name)),
|
||||
userUid, Filter.Entities(userUid));
|
||||
if (_foodSystem.IsMouthBlocked(userUid, userUid))
|
||||
return true;
|
||||
}
|
||||
|
||||
var transferAmount = FixedPoint2.Min(drink.TransferAmount, drinkSolution.DrainAvailable);
|
||||
var drain = _solutionContainerSystem.Drain(uid, drinkSolution, transferAmount);
|
||||
@@ -336,13 +331,8 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_foodSystem.IsMouthBlocked(targetUid, out var blocker))
|
||||
{
|
||||
var name = EntityManager.GetComponent<MetaDataComponent>(blocker.Value).EntityName;
|
||||
_popupSystem.PopupEntity(Loc.GetString("food-system-remove-mask", ("entity", name)),
|
||||
userUid, Filter.Entities(userUid));
|
||||
if (_foodSystem.IsMouthBlocked(targetUid, userUid))
|
||||
return true;
|
||||
}
|
||||
|
||||
EntityManager.TryGetComponent(userUid, out MetaDataComponent? meta);
|
||||
var userName = meta?.EntityName ?? string.Empty;
|
||||
@@ -425,28 +415,4 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
args.Drink.InUse = false;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ForceDrinkEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid User;
|
||||
public readonly DrinkComponent Drink;
|
||||
public readonly Solution DrinkSolution;
|
||||
|
||||
public ForceDrinkEvent(EntityUid user, DrinkComponent drink, Solution drinkSolution)
|
||||
{
|
||||
User = user;
|
||||
Drink = drink;
|
||||
DrinkSolution = drinkSolution;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ForceDrinkCancelledEvent : EntityEventArgs
|
||||
{
|
||||
public readonly DrinkComponent Drink;
|
||||
|
||||
public ForceDrinkCancelledEvent( DrinkComponent drink)
|
||||
{
|
||||
Drink = drink;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,6 @@ using System.Linq;
|
||||
using Robust.Shared.Utility;
|
||||
using Content.Server.Inventory.Components;
|
||||
using Content.Shared.Inventory;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Tag;
|
||||
|
||||
namespace Content.Server.Nutrition.EntitySystems
|
||||
{
|
||||
@@ -55,6 +53,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
SubscribeLocalEvent<FoodComponent, GetInteractionVerbsEvent>(AddEatVerb);
|
||||
SubscribeLocalEvent<SharedBodyComponent, ForceFeedEvent>(OnForceFeed);
|
||||
SubscribeLocalEvent<ForceFeedCancelledEvent>(OnForceFeedCancelled);
|
||||
SubscribeLocalEvent<InventoryComponent, IngestionAttemptEvent>(OnInventoryIngestAttempt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -139,13 +138,8 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
!_bodySystem.TryGetComponentsOnMechanisms<StomachComponent>(userUid, out var stomachs, body))
|
||||
return false;
|
||||
|
||||
if (IsMouthBlocked(userUid, out var blocker))
|
||||
{
|
||||
var name = EntityManager.GetComponent<MetaDataComponent>(blocker.Value).EntityName;
|
||||
_popupSystem.PopupEntity(Loc.GetString("food-system-remove-mask", ("entity", name)),
|
||||
userUid, Filter.Entities(userUid));
|
||||
if (IsMouthBlocked(userUid, userUid))
|
||||
return true;
|
||||
}
|
||||
|
||||
var usedUtensils = new List<UtensilComponent>();
|
||||
|
||||
@@ -264,13 +258,8 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsMouthBlocked(targetUid, out var blocker))
|
||||
{
|
||||
var name = EntityManager.GetComponent<MetaDataComponent>(blocker.Value).EntityName;
|
||||
_popupSystem.PopupEntity(Loc.GetString("food-system-remove-mask", ("entity", name)),
|
||||
userUid, Filter.Entities(userUid));
|
||||
if (IsMouthBlocked(targetUid, userUid))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!TryGetRequiredUtensils(userUid, food, out var utensils))
|
||||
return true;
|
||||
@@ -364,7 +353,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
if (!Resolve(uid, ref food) || !Resolve(target, ref body, false))
|
||||
return;
|
||||
|
||||
if (IsMouthBlocked(target, out _))
|
||||
if (IsMouthBlocked(target))
|
||||
return;
|
||||
|
||||
if (!_solutionContainerSystem.TryGetSolution(uid, food.SolutionName, out var foodSolution))
|
||||
@@ -448,62 +437,53 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is an entity's mouth accessible, or is it blocked by something like a mask? Does not actually check if
|
||||
/// the user has a mouth. Body system when?
|
||||
/// Block ingestion attempts based on the equipped mask or head-wear
|
||||
/// </summary>
|
||||
public bool IsMouthBlocked(EntityUid uid, [NotNullWhen(true)] out EntityUid? blockingEntity,
|
||||
InventoryComponent? inventory = null)
|
||||
private void OnInventoryIngestAttempt(EntityUid uid, InventoryComponent component, IngestionAttemptEvent args)
|
||||
{
|
||||
blockingEntity = null;
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
if (!Resolve(uid, ref inventory, false))
|
||||
return false;
|
||||
IngestionBlockerComponent blocker;
|
||||
|
||||
// check masks
|
||||
if (inventory.TryGetSlotItem(EquipmentSlotDefines.Slots.MASK, out ItemComponent? mask))
|
||||
if (component.TryGetSlotItem(EquipmentSlotDefines.Slots.MASK, out ItemComponent? mask) &&
|
||||
EntityManager.TryGetComponent(mask.OwnerUid, out blocker) &&
|
||||
blocker.Enabled)
|
||||
{
|
||||
// For now, lets just assume that any masks always covers the mouth
|
||||
// TODO MASKS if the ability is added to raise/lower masks, this needs to be updated.
|
||||
blockingEntity = mask.OwnerUid;
|
||||
return true;
|
||||
args.Blocker = mask.OwnerUid;
|
||||
args.Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// check helmets. Note that not all helmets cover the face.
|
||||
if (inventory.TryGetSlotItem(EquipmentSlotDefines.Slots.HEAD, out ItemComponent? head) &&
|
||||
EntityManager.TryGetComponent(head.OwnerUid, out TagComponent tag) &&
|
||||
tag.HasTag("ConcealsFace"))
|
||||
if (component.TryGetSlotItem(EquipmentSlotDefines.Slots.HEAD, out ItemComponent? head) &&
|
||||
EntityManager.TryGetComponent(head.OwnerUid, out blocker) &&
|
||||
blocker.Enabled)
|
||||
{
|
||||
blockingEntity = head.OwnerUid;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
args.Blocker = head.OwnerUid;
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ForceFeedEvent : EntityEventArgs
|
||||
|
||||
/// <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)
|
||||
{
|
||||
public readonly EntityUid User;
|
||||
public readonly FoodComponent Food;
|
||||
public readonly Solution FoodSolution;
|
||||
public readonly List<UtensilComponent> Utensils;
|
||||
|
||||
public ForceFeedEvent(EntityUid user, FoodComponent food, Solution foodSolution, List<UtensilComponent> utensils)
|
||||
var attempt = new IngestionAttemptEvent();
|
||||
RaiseLocalEvent(uid, attempt, false);
|
||||
if (attempt.Cancelled && attempt.Blocker != null && popupUid != null)
|
||||
{
|
||||
User = user;
|
||||
Food = food;
|
||||
FoodSolution = foodSolution;
|
||||
Utensils = utensils;
|
||||
}
|
||||
var name = EntityManager.GetComponent<MetaDataComponent>(attempt.Blocker.Value).EntityName;
|
||||
_popupSystem.PopupEntity(Loc.GetString("food-system-remove-mask", ("entity", name)),
|
||||
uid, Filter.Entities(popupUid.Value));
|
||||
}
|
||||
|
||||
public sealed class ForceFeedCancelledEvent : EntityEventArgs
|
||||
{
|
||||
public readonly FoodComponent Food;
|
||||
|
||||
public ForceFeedCancelledEvent(FoodComponent food)
|
||||
{
|
||||
Food = food;
|
||||
return attempt.Cancelled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
79
Content.Server/Nutrition/IngestionEvents.cs
Normal file
79
Content.Server/Nutrition/IngestionEvents.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using Content.Server.Nutrition.Components;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Content.Server.Nutrition;
|
||||
|
||||
/// <summary>
|
||||
/// Raised directed at the consumer when attempting to ingest something.
|
||||
/// </summary>
|
||||
public sealed class IngestionAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The equipment that is blocking consumption. Should only be non-null if the event was canceled.
|
||||
/// </summary>
|
||||
public EntityUid? Blocker = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised directed at the food after a successful force-feed do-after.
|
||||
/// </summary>
|
||||
public sealed class ForceFeedEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid User;
|
||||
public readonly FoodComponent Food;
|
||||
public readonly Solution FoodSolution;
|
||||
public readonly List<UtensilComponent> Utensils;
|
||||
|
||||
public ForceFeedEvent(EntityUid user, FoodComponent food, Solution foodSolution, List<UtensilComponent> utensils)
|
||||
{
|
||||
User = user;
|
||||
Food = food;
|
||||
FoodSolution = foodSolution;
|
||||
Utensils = utensils;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised directed at the food after a failed force-feed do-after.
|
||||
/// </summary>
|
||||
public sealed class ForceFeedCancelledEvent : EntityEventArgs
|
||||
{
|
||||
public readonly FoodComponent Food;
|
||||
|
||||
public ForceFeedCancelledEvent(FoodComponent food)
|
||||
{
|
||||
Food = food;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised directed at the drink after a successful force-drink do-after.
|
||||
/// </summary>
|
||||
public sealed class ForceDrinkEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid User;
|
||||
public readonly DrinkComponent Drink;
|
||||
public readonly Solution DrinkSolution;
|
||||
|
||||
public ForceDrinkEvent(EntityUid user, DrinkComponent drink, Solution drinkSolution)
|
||||
{
|
||||
User = user;
|
||||
Drink = drink;
|
||||
DrinkSolution = drinkSolution;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised directed at the food after a failed force-dink do-after.
|
||||
/// </summary>
|
||||
public sealed class ForceDrinkCancelledEvent : EntityEventArgs
|
||||
{
|
||||
public readonly DrinkComponent Drink;
|
||||
|
||||
public ForceDrinkCancelledEvent(DrinkComponent drink)
|
||||
{
|
||||
Drink = drink;
|
||||
}
|
||||
}
|
||||
@@ -41,9 +41,7 @@
|
||||
Piercing: 0.95
|
||||
Heat: 0.90
|
||||
Radiation: 0.25
|
||||
- type: Tag
|
||||
tags:
|
||||
- ConcealsFace
|
||||
- type: IngestionBlocker
|
||||
|
||||
- type: entity
|
||||
abstract: true
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
sprite: Clothing/Head/Helmets/bombsuit.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Helmets/bombsuit.rsi
|
||||
- type: Tag
|
||||
tags:
|
||||
- ConcealsFace
|
||||
- type: IngestionBlocker
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
@@ -22,9 +20,7 @@
|
||||
sprite: Clothing/Head/Helmets/cosmonaut.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Helmets/cosmonaut.rsi
|
||||
- type: Tag
|
||||
tags:
|
||||
- ConcealsFace
|
||||
- type: IngestionBlocker
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
@@ -36,9 +32,7 @@
|
||||
sprite: Clothing/Head/Helmets/cult.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Helmets/cult.rsi
|
||||
- type: Tag
|
||||
tags:
|
||||
- ConcealsFace
|
||||
- type: IngestionBlocker
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
@@ -79,9 +73,7 @@
|
||||
sprite: Clothing/Head/Helmets/light_riot.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Helmets/light_riot.rsi
|
||||
- type: Tag
|
||||
tags:
|
||||
- ConcealsFace
|
||||
- type: IngestionBlocker
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
@@ -93,9 +85,7 @@
|
||||
sprite: Clothing/Head/Helmets/scaf.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Helmets/scaf.rsi
|
||||
- type: Tag
|
||||
tags:
|
||||
- ConcealsFace
|
||||
- type: IngestionBlocker
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
@@ -107,9 +97,7 @@
|
||||
sprite: Clothing/Head/Helmets/spaceninja.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Helmets/spaceninja.rsi
|
||||
- type: Tag
|
||||
tags:
|
||||
- ConcealsFace # well only partially?
|
||||
- type: IngestionBlocker
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
@@ -121,9 +109,7 @@
|
||||
sprite: Clothing/Head/Helmets/syndicate.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Helmets/syndicate.rsi
|
||||
- type: Tag
|
||||
tags:
|
||||
- ConcealsFace
|
||||
- type: IngestionBlocker
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
@@ -135,9 +121,7 @@
|
||||
sprite: Clothing/Head/Helmets/templar.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Helmets/templar.rsi
|
||||
- type: Tag
|
||||
tags:
|
||||
- ConcealsFace
|
||||
- type: IngestionBlocker
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
@@ -160,6 +144,4 @@
|
||||
sprite: Clothing/Head/Helmets/wizardhelm.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Helmets/wizardhelm.rsi
|
||||
- type: Tag
|
||||
tags:
|
||||
- ConcealsFace
|
||||
- type: IngestionBlocker
|
||||
@@ -30,9 +30,7 @@
|
||||
sprite: Clothing/Head/Misc/chickenhead.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Misc/chickenhead.rsi
|
||||
- type: Tag
|
||||
tags:
|
||||
- ConcealsFace
|
||||
- type: IngestionBlocker
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
@@ -55,9 +53,7 @@
|
||||
sprite: Clothing/Head/Misc/pumpkin.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Misc/pumpkin.rsi
|
||||
- type: Tag
|
||||
tags:
|
||||
- ConcealsFace
|
||||
- type: IngestionBlocker
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
@@ -80,9 +76,7 @@
|
||||
sprite: Clothing/Head/Misc/richard.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Misc/richard.rsi
|
||||
- type: Tag
|
||||
tags:
|
||||
- ConcealsFace
|
||||
- type: IngestionBlocker
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
@@ -94,9 +88,7 @@
|
||||
sprite: Clothing/Head/Misc/skubhead.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Misc/skubhead.rsi
|
||||
- type: Tag
|
||||
tags:
|
||||
- ConcealsFace
|
||||
- type: IngestionBlocker
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
@@ -108,9 +100,7 @@
|
||||
sprite: Clothing/Head/Misc/xenom.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Misc/xenom.rsi
|
||||
- type: Tag
|
||||
tags:
|
||||
- ConcealsFace
|
||||
- type: IngestionBlocker
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
@@ -122,9 +112,7 @@
|
||||
sprite: Clothing/Head/Misc/xenos.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Misc/xenos.rsi
|
||||
- type: Tag
|
||||
tags:
|
||||
- ConcealsFace
|
||||
- type: IngestionBlocker
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
name: welding mask
|
||||
abstract: true
|
||||
components:
|
||||
- type: Tag
|
||||
tags:
|
||||
- ConcealsFace
|
||||
- type: IngestionBlocker
|
||||
|
||||
- type: entity
|
||||
parent: WeldingMaskBase
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
- type: Clothing
|
||||
sprite: Clothing/Mask/gas.rsi
|
||||
- type: BreathMask
|
||||
- type: IngestionBlocker
|
||||
|
||||
- type: entity
|
||||
parent: ClothingMaskBase
|
||||
@@ -21,6 +22,7 @@
|
||||
- type: Clothing
|
||||
sprite: Clothing/Mask/breath.rsi
|
||||
- type: BreathMask
|
||||
- type: IngestionBlocker
|
||||
|
||||
- type: entity
|
||||
parent: ClothingMaskBase
|
||||
@@ -69,3 +71,4 @@
|
||||
- type: Clothing
|
||||
sprite: Clothing/Mask/sterile.rsi
|
||||
- type: BreathMask
|
||||
- type: IngestionBlocker
|
||||
|
||||
@@ -242,7 +242,3 @@
|
||||
|
||||
- type: Tag
|
||||
id: Write
|
||||
|
||||
# for head wear & masks that cover the face.
|
||||
- type: Tag
|
||||
id: ConcealsFace
|
||||
|
||||
Reference in New Issue
Block a user