Add IngestionBlocker component (#5692)

This commit is contained in:
Leon Friedrich
2021-12-05 07:56:27 +13:00
committed by GitHub
parent e9e9a6871d
commit bfff21ea73
11 changed files with 169 additions and 152 deletions

View File

@@ -8,6 +8,7 @@ namespace Content.Client.Entry
"Anchorable", "Anchorable",
"AmmoBox", "AmmoBox",
"Pickaxe", "Pickaxe",
"IngestionBlocker",
"Interactable", "Interactable",
"CloningPod", "CloningPod",
"Destructible", "Destructible",

View File

@@ -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;
}

View File

@@ -263,13 +263,8 @@ namespace Content.Server.Nutrition.EntitySystems
return true; return true;
} }
if (_foodSystem.IsMouthBlocked(userUid, out var blocker)) if (_foodSystem.IsMouthBlocked(userUid, userUid))
{
var name = EntityManager.GetComponent<MetaDataComponent>(blocker.Value).EntityName;
_popupSystem.PopupEntity(Loc.GetString("food-system-remove-mask", ("entity", name)),
userUid, Filter.Entities(userUid));
return true; return true;
}
var transferAmount = FixedPoint2.Min(drink.TransferAmount, drinkSolution.DrainAvailable); var transferAmount = FixedPoint2.Min(drink.TransferAmount, drinkSolution.DrainAvailable);
var drain = _solutionContainerSystem.Drain(uid, drinkSolution, transferAmount); var drain = _solutionContainerSystem.Drain(uid, drinkSolution, transferAmount);
@@ -336,13 +331,8 @@ namespace Content.Server.Nutrition.EntitySystems
return true; return true;
} }
if (_foodSystem.IsMouthBlocked(targetUid, out var blocker)) if (_foodSystem.IsMouthBlocked(targetUid, userUid))
{
var name = EntityManager.GetComponent<MetaDataComponent>(blocker.Value).EntityName;
_popupSystem.PopupEntity(Loc.GetString("food-system-remove-mask", ("entity", name)),
userUid, Filter.Entities(userUid));
return true; return true;
}
EntityManager.TryGetComponent(userUid, out MetaDataComponent? meta); EntityManager.TryGetComponent(userUid, out MetaDataComponent? meta);
var userName = meta?.EntityName ?? string.Empty; var userName = meta?.EntityName ?? string.Empty;
@@ -425,28 +415,4 @@ namespace Content.Server.Nutrition.EntitySystems
args.Drink.InUse = false; 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;
}
}
} }

View File

@@ -27,8 +27,6 @@ using System.Linq;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using Content.Server.Inventory.Components; using Content.Server.Inventory.Components;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using System.Diagnostics.CodeAnalysis;
using Content.Shared.Tag;
namespace Content.Server.Nutrition.EntitySystems namespace Content.Server.Nutrition.EntitySystems
{ {
@@ -55,6 +53,7 @@ namespace Content.Server.Nutrition.EntitySystems
SubscribeLocalEvent<FoodComponent, GetInteractionVerbsEvent>(AddEatVerb); SubscribeLocalEvent<FoodComponent, GetInteractionVerbsEvent>(AddEatVerb);
SubscribeLocalEvent<SharedBodyComponent, ForceFeedEvent>(OnForceFeed); SubscribeLocalEvent<SharedBodyComponent, ForceFeedEvent>(OnForceFeed);
SubscribeLocalEvent<ForceFeedCancelledEvent>(OnForceFeedCancelled); SubscribeLocalEvent<ForceFeedCancelledEvent>(OnForceFeedCancelled);
SubscribeLocalEvent<InventoryComponent, IngestionAttemptEvent>(OnInventoryIngestAttempt);
} }
/// <summary> /// <summary>
@@ -139,13 +138,8 @@ namespace Content.Server.Nutrition.EntitySystems
!_bodySystem.TryGetComponentsOnMechanisms<StomachComponent>(userUid, out var stomachs, body)) !_bodySystem.TryGetComponentsOnMechanisms<StomachComponent>(userUid, out var stomachs, body))
return false; return false;
if (IsMouthBlocked(userUid, out var blocker)) if (IsMouthBlocked(userUid, userUid))
{
var name = EntityManager.GetComponent<MetaDataComponent>(blocker.Value).EntityName;
_popupSystem.PopupEntity(Loc.GetString("food-system-remove-mask", ("entity", name)),
userUid, Filter.Entities(userUid));
return true; return true;
}
var usedUtensils = new List<UtensilComponent>(); var usedUtensils = new List<UtensilComponent>();
@@ -264,13 +258,8 @@ namespace Content.Server.Nutrition.EntitySystems
return true; return true;
} }
if (IsMouthBlocked(targetUid, out var blocker)) if (IsMouthBlocked(targetUid, userUid))
{
var name = EntityManager.GetComponent<MetaDataComponent>(blocker.Value).EntityName;
_popupSystem.PopupEntity(Loc.GetString("food-system-remove-mask", ("entity", name)),
userUid, Filter.Entities(userUid));
return true; return true;
}
if (!TryGetRequiredUtensils(userUid, food, out var utensils)) if (!TryGetRequiredUtensils(userUid, food, out var utensils))
return true; return true;
@@ -364,7 +353,7 @@ namespace Content.Server.Nutrition.EntitySystems
if (!Resolve(uid, ref food) || !Resolve(target, ref body, false)) if (!Resolve(uid, ref food) || !Resolve(target, ref body, false))
return; return;
if (IsMouthBlocked(target, out _)) if (IsMouthBlocked(target))
return; return;
if (!_solutionContainerSystem.TryGetSolution(uid, food.SolutionName, out var foodSolution)) if (!_solutionContainerSystem.TryGetSolution(uid, food.SolutionName, out var foodSolution))
@@ -448,62 +437,53 @@ namespace Content.Server.Nutrition.EntitySystems
} }
/// <summary> /// <summary>
/// Is an entity's mouth accessible, or is it blocked by something like a mask? Does not actually check if /// Block ingestion attempts based on the equipped mask or head-wear
/// the user has a mouth. Body system when?
/// </summary> /// </summary>
public bool IsMouthBlocked(EntityUid uid, [NotNullWhen(true)] out EntityUid? blockingEntity, private void OnInventoryIngestAttempt(EntityUid uid, InventoryComponent component, IngestionAttemptEvent args)
InventoryComponent? inventory = null)
{ {
blockingEntity = null; if (args.Cancelled)
return;
if (!Resolve(uid, ref inventory, false)) IngestionBlockerComponent blocker;
return false;
// check masks if (component.TryGetSlotItem(EquipmentSlotDefines.Slots.MASK, out ItemComponent? mask) &&
if (inventory.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 args.Blocker = mask.OwnerUid;
// TODO MASKS if the ability is added to raise/lower masks, this needs to be updated. args.Cancel();
blockingEntity = mask.OwnerUid; return;
return true;
} }
// check helmets. Note that not all helmets cover the face. if (component.TryGetSlotItem(EquipmentSlotDefines.Slots.HEAD, out ItemComponent? head) &&
if (inventory.TryGetSlotItem(EquipmentSlotDefines.Slots.HEAD, out ItemComponent? head) && EntityManager.TryGetComponent(head.OwnerUid, out blocker) &&
EntityManager.TryGetComponent(head.OwnerUid, out TagComponent tag) && blocker.Enabled)
tag.HasTag("ConcealsFace"))
{ {
blockingEntity = head.OwnerUid; args.Blocker = head.OwnerUid;
return true; args.Cancel();
}
}
/// <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)
{
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));
} }
return false; return attempt.Cancelled;
}
}
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;
}
}
public sealed class ForceFeedCancelledEvent : EntityEventArgs
{
public readonly FoodComponent Food;
public ForceFeedCancelledEvent(FoodComponent food)
{
Food = food;
} }
} }
} }

View 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;
}
}

View File

@@ -41,9 +41,7 @@
Piercing: 0.95 Piercing: 0.95
Heat: 0.90 Heat: 0.90
Radiation: 0.25 Radiation: 0.25
- type: Tag - type: IngestionBlocker
tags:
- ConcealsFace
- type: entity - type: entity
abstract: true abstract: true

View File

@@ -8,9 +8,7 @@
sprite: Clothing/Head/Helmets/bombsuit.rsi sprite: Clothing/Head/Helmets/bombsuit.rsi
- type: Clothing - type: Clothing
sprite: Clothing/Head/Helmets/bombsuit.rsi sprite: Clothing/Head/Helmets/bombsuit.rsi
- type: Tag - type: IngestionBlocker
tags:
- ConcealsFace
- type: entity - type: entity
parent: ClothingHeadBase parent: ClothingHeadBase
@@ -22,9 +20,7 @@
sprite: Clothing/Head/Helmets/cosmonaut.rsi sprite: Clothing/Head/Helmets/cosmonaut.rsi
- type: Clothing - type: Clothing
sprite: Clothing/Head/Helmets/cosmonaut.rsi sprite: Clothing/Head/Helmets/cosmonaut.rsi
- type: Tag - type: IngestionBlocker
tags:
- ConcealsFace
- type: entity - type: entity
parent: ClothingHeadBase parent: ClothingHeadBase
@@ -36,9 +32,7 @@
sprite: Clothing/Head/Helmets/cult.rsi sprite: Clothing/Head/Helmets/cult.rsi
- type: Clothing - type: Clothing
sprite: Clothing/Head/Helmets/cult.rsi sprite: Clothing/Head/Helmets/cult.rsi
- type: Tag - type: IngestionBlocker
tags:
- ConcealsFace
- type: entity - type: entity
parent: ClothingHeadBase parent: ClothingHeadBase
@@ -79,9 +73,7 @@
sprite: Clothing/Head/Helmets/light_riot.rsi sprite: Clothing/Head/Helmets/light_riot.rsi
- type: Clothing - type: Clothing
sprite: Clothing/Head/Helmets/light_riot.rsi sprite: Clothing/Head/Helmets/light_riot.rsi
- type: Tag - type: IngestionBlocker
tags:
- ConcealsFace
- type: entity - type: entity
parent: ClothingHeadBase parent: ClothingHeadBase
@@ -93,9 +85,7 @@
sprite: Clothing/Head/Helmets/scaf.rsi sprite: Clothing/Head/Helmets/scaf.rsi
- type: Clothing - type: Clothing
sprite: Clothing/Head/Helmets/scaf.rsi sprite: Clothing/Head/Helmets/scaf.rsi
- type: Tag - type: IngestionBlocker
tags:
- ConcealsFace
- type: entity - type: entity
parent: ClothingHeadBase parent: ClothingHeadBase
@@ -107,9 +97,7 @@
sprite: Clothing/Head/Helmets/spaceninja.rsi sprite: Clothing/Head/Helmets/spaceninja.rsi
- type: Clothing - type: Clothing
sprite: Clothing/Head/Helmets/spaceninja.rsi sprite: Clothing/Head/Helmets/spaceninja.rsi
- type: Tag - type: IngestionBlocker
tags:
- ConcealsFace # well only partially?
- type: entity - type: entity
parent: ClothingHeadBase parent: ClothingHeadBase
@@ -121,9 +109,7 @@
sprite: Clothing/Head/Helmets/syndicate.rsi sprite: Clothing/Head/Helmets/syndicate.rsi
- type: Clothing - type: Clothing
sprite: Clothing/Head/Helmets/syndicate.rsi sprite: Clothing/Head/Helmets/syndicate.rsi
- type: Tag - type: IngestionBlocker
tags:
- ConcealsFace
- type: entity - type: entity
parent: ClothingHeadBase parent: ClothingHeadBase
@@ -135,9 +121,7 @@
sprite: Clothing/Head/Helmets/templar.rsi sprite: Clothing/Head/Helmets/templar.rsi
- type: Clothing - type: Clothing
sprite: Clothing/Head/Helmets/templar.rsi sprite: Clothing/Head/Helmets/templar.rsi
- type: Tag - type: IngestionBlocker
tags:
- ConcealsFace
- type: entity - type: entity
parent: ClothingHeadBase parent: ClothingHeadBase
@@ -160,6 +144,4 @@
sprite: Clothing/Head/Helmets/wizardhelm.rsi sprite: Clothing/Head/Helmets/wizardhelm.rsi
- type: Clothing - type: Clothing
sprite: Clothing/Head/Helmets/wizardhelm.rsi sprite: Clothing/Head/Helmets/wizardhelm.rsi
- type: Tag - type: IngestionBlocker
tags:
- ConcealsFace

View File

@@ -30,9 +30,7 @@
sprite: Clothing/Head/Misc/chickenhead.rsi sprite: Clothing/Head/Misc/chickenhead.rsi
- type: Clothing - type: Clothing
sprite: Clothing/Head/Misc/chickenhead.rsi sprite: Clothing/Head/Misc/chickenhead.rsi
- type: Tag - type: IngestionBlocker
tags:
- ConcealsFace
- type: entity - type: entity
parent: ClothingHeadBase parent: ClothingHeadBase
@@ -55,9 +53,7 @@
sprite: Clothing/Head/Misc/pumpkin.rsi sprite: Clothing/Head/Misc/pumpkin.rsi
- type: Clothing - type: Clothing
sprite: Clothing/Head/Misc/pumpkin.rsi sprite: Clothing/Head/Misc/pumpkin.rsi
- type: Tag - type: IngestionBlocker
tags:
- ConcealsFace
- type: entity - type: entity
parent: ClothingHeadBase parent: ClothingHeadBase
@@ -80,9 +76,7 @@
sprite: Clothing/Head/Misc/richard.rsi sprite: Clothing/Head/Misc/richard.rsi
- type: Clothing - type: Clothing
sprite: Clothing/Head/Misc/richard.rsi sprite: Clothing/Head/Misc/richard.rsi
- type: Tag - type: IngestionBlocker
tags:
- ConcealsFace
- type: entity - type: entity
parent: ClothingHeadBase parent: ClothingHeadBase
@@ -94,9 +88,7 @@
sprite: Clothing/Head/Misc/skubhead.rsi sprite: Clothing/Head/Misc/skubhead.rsi
- type: Clothing - type: Clothing
sprite: Clothing/Head/Misc/skubhead.rsi sprite: Clothing/Head/Misc/skubhead.rsi
- type: Tag - type: IngestionBlocker
tags:
- ConcealsFace
- type: entity - type: entity
parent: ClothingHeadBase parent: ClothingHeadBase
@@ -108,9 +100,7 @@
sprite: Clothing/Head/Misc/xenom.rsi sprite: Clothing/Head/Misc/xenom.rsi
- type: Clothing - type: Clothing
sprite: Clothing/Head/Misc/xenom.rsi sprite: Clothing/Head/Misc/xenom.rsi
- type: Tag - type: IngestionBlocker
tags:
- ConcealsFace
- type: entity - type: entity
parent: ClothingHeadBase parent: ClothingHeadBase
@@ -122,9 +112,7 @@
sprite: Clothing/Head/Misc/xenos.rsi sprite: Clothing/Head/Misc/xenos.rsi
- type: Clothing - type: Clothing
sprite: Clothing/Head/Misc/xenos.rsi sprite: Clothing/Head/Misc/xenos.rsi
- type: Tag - type: IngestionBlocker
tags:
- ConcealsFace
- type: entity - type: entity
parent: ClothingHeadBase parent: ClothingHeadBase

View File

@@ -4,9 +4,7 @@
name: welding mask name: welding mask
abstract: true abstract: true
components: components:
- type: Tag - type: IngestionBlocker
tags:
- ConcealsFace
- type: entity - type: entity
parent: WeldingMaskBase parent: WeldingMaskBase

View File

@@ -9,6 +9,7 @@
- type: Clothing - type: Clothing
sprite: Clothing/Mask/gas.rsi sprite: Clothing/Mask/gas.rsi
- type: BreathMask - type: BreathMask
- type: IngestionBlocker
- type: entity - type: entity
parent: ClothingMaskBase parent: ClothingMaskBase
@@ -21,6 +22,7 @@
- type: Clothing - type: Clothing
sprite: Clothing/Mask/breath.rsi sprite: Clothing/Mask/breath.rsi
- type: BreathMask - type: BreathMask
- type: IngestionBlocker
- type: entity - type: entity
parent: ClothingMaskBase parent: ClothingMaskBase
@@ -69,3 +71,4 @@
- type: Clothing - type: Clothing
sprite: Clothing/Mask/sterile.rsi sprite: Clothing/Mask/sterile.rsi
- type: BreathMask - type: BreathMask
- type: IngestionBlocker

View File

@@ -242,7 +242,3 @@
- type: Tag - type: Tag
id: Write id: Write
# for head wear & masks that cover the face.
- type: Tag
id: ConcealsFace