using Content.Server.Body.Components;
using Content.Shared.Atmos;
using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.Clothing;
using Content.Shared.Inventory.Events;
namespace Content.Server.Body.Systems;
public sealed class LungSystem : EntitySystem
{
+ [Dependency] private readonly AtmosphereSystem _atmos = default!;
[Dependency] private readonly InternalsSystem _internals = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
SubscribeLocalEvent<LungComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<BreathToolComponent, GotEquippedEvent>(OnGotEquipped);
SubscribeLocalEvent<BreathToolComponent, GotUnequippedEvent>(OnGotUnequipped);
+ SubscribeLocalEvent<BreathToolComponent, ItemMaskToggledEvent>(OnMaskToggled);
}
private void OnGotUnequipped(EntityUid uid, BreathToolComponent component, GotUnequippedEvent args)
component.LungSolution.CanReact = false; // No dexalin lungs
}
+ private void OnMaskToggled(Entity<BreathToolComponent> ent, ref ItemMaskToggledEvent args)
+ {
+ // toggle breath tool connection (skip during equip since that is handled in LungSystem)
+ if (args.IsEquip)
+ {
+ if (args.IsToggled)
+ {
+ _atmos.DisconnectInternals(ent.Comp);
+ }
+ else
+ {
+ ent.Comp.IsFunctional = true;
+
+ if (TryComp(args.Wearer, out InternalsComponent? internals))
+ {
+ ent.Comp.ConnectedInternalsEntity = args.Wearer;
+ _internals.ConnectBreathTool((args.Wearer, internals), ent);
+ }
+ }
+ }
+ }
+
public void GasToReagent(EntityUid uid, LungComponent lung)
{
foreach (var gas in Enum.GetValues<Gas>())
+++ /dev/null
-using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-
-namespace Content.Server.Clothing.Components
-{
- [Access(typeof(MaskSystem))]
- [RegisterComponent]
- public sealed partial class MaskComponent : Component
- {
- [DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
- public string ToggleAction = "ActionToggleMask";
-
- /// <summary>
- /// This mask can be toggled (pulled up/down)
- /// </summary>
- [DataField("toggleActionEntity")]
- public EntityUid? ToggleActionEntity;
-
- public bool IsToggled = false;
- }
-}
+++ /dev/null
-using Content.Server.Actions;
-using Content.Server.Atmos.Components;
-using Content.Server.Atmos.EntitySystems;
-using Content.Server.Body.Components;
-using Content.Server.Body.Systems;
-using Content.Server.Clothing.Components;
-using Content.Server.IdentityManagement;
-using Content.Server.Nutrition.EntitySystems;
-using Content.Server.Popups;
-using Content.Server.VoiceMask;
-using Content.Shared.Actions;
-using Content.Shared.Clothing;
-using Content.Shared.Clothing.Components;
-using Content.Shared.Clothing.EntitySystems;
-using Content.Shared.IdentityManagement.Components;
-using Content.Shared.Inventory;
-using Content.Shared.Inventory.Events;
-
-namespace Content.Server.Clothing
-{
- public sealed class MaskSystem : EntitySystem
- {
- [Dependency] private readonly ActionsSystem _actionSystem = default!;
- [Dependency] private readonly AtmosphereSystem _atmos = default!;
- [Dependency] private readonly InternalsSystem _internals = default!;
- [Dependency] private readonly InventorySystem _inventorySystem = default!;
- [Dependency] private readonly PopupSystem _popupSystem = default!;
- [Dependency] private readonly IdentitySystem _identity = default!;
- [Dependency] private readonly ClothingSystem _clothing = default!;
-
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent<MaskComponent, ToggleMaskEvent>(OnToggleMask);
- SubscribeLocalEvent<MaskComponent, GetItemActionsEvent>(OnGetActions);
- SubscribeLocalEvent<MaskComponent, GotUnequippedEvent>(OnGotUnequipped);
- }
-
- private void OnGetActions(EntityUid uid, MaskComponent component, GetItemActionsEvent args)
- {
- if (!args.InHands)
- args.AddAction(ref component.ToggleActionEntity, component.ToggleAction);
- }
-
- private void OnToggleMask(Entity<MaskComponent> ent, ref ToggleMaskEvent args)
- {
- var (uid, mask) = ent;
- if (mask.ToggleActionEntity == null)
- return;
-
- if (!_inventorySystem.TryGetSlotEntity(args.Performer, "mask", out var existing) || !uid.Equals(existing))
- return;
-
- mask.IsToggled ^= true;
- _actionSystem.SetToggled(mask.ToggleActionEntity, mask.IsToggled);
-
- // Pulling mask down can change identity, so we want to update that
- _identity.QueueIdentityUpdate(args.Performer);
-
- if (mask.IsToggled)
- _popupSystem.PopupEntity(Loc.GetString("action-mask-pull-down-popup-message", ("mask", uid)), args.Performer, args.Performer);
- else
- _popupSystem.PopupEntity(Loc.GetString("action-mask-pull-up-popup-message", ("mask", uid)), args.Performer, args.Performer);
-
- ToggleMaskComponents(uid, mask, args.Performer);
- }
-
- // set to untoggled when unequipped, so it isn't left in a 'pulled down' state
- private void OnGotUnequipped(EntityUid uid, MaskComponent mask, GotUnequippedEvent args)
- {
- if (mask.ToggleActionEntity == null)
- return;
-
- mask.IsToggled = false;
- _actionSystem.SetToggled(mask.ToggleActionEntity, mask.IsToggled);
-
- ToggleMaskComponents(uid, mask, args.Equipee, true);
- }
-
- private void ToggleMaskComponents(EntityUid uid, MaskComponent mask, EntityUid wearer, bool isEquip = false)
- {
- // toggle visuals
- if (TryComp<ClothingComponent>(uid, out var clothing))
- {
- //TODO: sprites for 'pulled down' state. defaults to invisible due to no sprite with this prefix
- _clothing.SetEquippedPrefix(uid, mask.IsToggled ? "toggled" : null, clothing);
- }
-
- // shouldn't this be an event?
-
- // toggle ingestion blocking
- if (TryComp<IngestionBlockerComponent>(uid, out var blocker))
- blocker.Enabled = !mask.IsToggled;
-
- // toggle identity
- if (TryComp<IdentityBlockerComponent>(uid, out var identity))
- identity.Enabled = !mask.IsToggled;
-
- // toggle voice masking
- if (TryComp<VoiceMaskComponent>(wearer, out var voiceMask))
- voiceMask.Enabled = !mask.IsToggled;
-
- // toggle breath tool connection (skip during equip since that is handled in LungSystem)
- if (isEquip || !TryComp<BreathToolComponent>(uid, out var breathTool))
- return;
-
- if (mask.IsToggled)
- {
- _atmos.DisconnectInternals(breathTool);
- }
- else
- {
- breathTool.IsFunctional = true;
-
- if (TryComp(wearer, out InternalsComponent? internals))
- {
- breathTool.ConnectedInternalsEntity = wearer;
- _internals.ConnectBreathTool((wearer, internals), uid);
- }
- }
- }
- }
-}
using Content.Server.Access.Systems;
using Content.Server.Administration.Logs;
using Content.Server.Humanoid;
+using Content.Shared.Clothing;
using Content.Shared.Database;
using Content.Shared.Hands;
using Content.Shared.Humanoid;
SubscribeLocalEvent<IdentityComponent, DidEquipHandEvent>((uid, _, _) => QueueIdentityUpdate(uid));
SubscribeLocalEvent<IdentityComponent, DidUnequipEvent>((uid, _, _) => QueueIdentityUpdate(uid));
SubscribeLocalEvent<IdentityComponent, DidUnequipHandEvent>((uid, _, _) => QueueIdentityUpdate(uid));
+ SubscribeLocalEvent<IdentityComponent, WearerMaskToggledEvent>((uid, _, _) => QueueIdentityUpdate(uid));
SubscribeLocalEvent<IdentityComponent, MapInitEvent>(OnMapInit);
}
-using Content.Server.Clothing;
+using Content.Server.Nutrition.EntitySystems;
-namespace Content.Server.Nutrition.EntitySystems;
+namespace Content.Server.Nutrition.Components;
/// <summary>
/// Component that denotes a piece of clothing that blocks the mouth or otherwise prevents eating & drinking.
/// 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, Access(typeof(FoodSystem), typeof(DrinkSystem), typeof(MaskSystem))]
+[RegisterComponent, Access(typeof(FoodSystem), typeof(DrinkSystem), typeof(IngestionBlockerSystem))]
public sealed partial class IngestionBlockerComponent : Component
{
/// <summary>
--- /dev/null
+using Content.Shared.Clothing;
+
+namespace Content.Server.Nutrition.EntitySystems;
+
+public sealed class IngestionBlockerSystem : EntitySystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<Components.IngestionBlockerComponent, ItemMaskToggledEvent>(OnBlockerMaskToggled);
+ }
+
+ private void OnBlockerMaskToggled(Entity<Components.IngestionBlockerComponent> ent, ref ItemMaskToggledEvent args)
+ {
+ ent.Comp.Enabled = !args.IsToggled;
+ }
+}
using Content.Server.Administration.Logs;
using Content.Server.Chat.Systems;
using Content.Server.Popups;
+using Content.Shared.Clothing;
using Content.Shared.Database;
using Content.Shared.Inventory.Events;
using Content.Shared.Popups;
{
SubscribeLocalEvent<VoiceMaskComponent, TransformSpeakerNameEvent>(OnSpeakerNameTransform);
SubscribeLocalEvent<VoiceMaskComponent, VoiceMaskChangeNameMessage>(OnChangeName);
+ SubscribeLocalEvent<VoiceMaskComponent, ItemMaskToggledEvent>(OnMaskToggled);
SubscribeLocalEvent<VoiceMaskerComponent, GotEquippedEvent>(OnEquip);
SubscribeLocalEvent<VoiceMaskerComponent, GotUnequippedEvent>(OnUnequip);
SubscribeLocalEvent<VoiceMaskSetNameEvent>(OnSetName);
}
}
+ private void OnMaskToggled(Entity<VoiceMaskComponent> ent, ref ItemMaskToggledEvent args)
+ {
+ ent.Comp.Enabled = !args.IsToggled;
+ }
+
private void OpenUI(EntityUid player, ActorComponent? actor = null)
{
if (!Resolve(player, ref actor))
}
public sealed partial class ToggleMaskEvent : InstantActionEvent { }
+
+/// <summary>
+/// Event raised on the mask entity when it is toggled.
+/// </summary>
+[ByRefEvent]
+public readonly record struct ItemMaskToggledEvent(EntityUid Wearer, bool IsToggled, bool IsEquip);
+
+/// <summary>
+/// Event raised on the entity wearing the mask when it is toggled.
+/// </summary>
+[ByRefEvent]
+public readonly record struct WearerMaskToggledEvent(bool Enabled);
--- /dev/null
+using Content.Shared.Clothing.EntitySystems;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Clothing.Components;
+
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(MaskSystem))]
+public sealed partial class MaskComponent : Component
+{
+ [DataField, AutoNetworkedField]
+ public EntProtoId ToggleAction = "ActionToggleMask";
+
+ /// <summary>
+ /// This mask can be toggled (pulled up/down)
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public EntityUid? ToggleActionEntity;
+
+ [DataField, AutoNetworkedField]
+ public bool IsToggled;
+}
SubscribeLocalEvent<ClothingComponent, ComponentHandleState>(OnHandleState);
SubscribeLocalEvent<ClothingComponent, GotEquippedEvent>(OnGotEquipped);
SubscribeLocalEvent<ClothingComponent, GotUnequippedEvent>(OnGotUnequipped);
+ SubscribeLocalEvent<ClothingComponent, ItemMaskToggledEvent>(OnMaskToggled);
}
protected virtual void OnGotEquipped(EntityUid uid, ClothingComponent component, GotEquippedEvent args)
SetEquippedPrefix(uid, state.EquippedPrefix, component);
}
+ private void OnMaskToggled(Entity<ClothingComponent> ent, ref ItemMaskToggledEvent args)
+ {
+ //TODO: sprites for 'pulled down' state. defaults to invisible due to no sprite with this prefix
+ SetEquippedPrefix(ent, args.IsToggled ? "toggled" : null, ent);
+ }
+
#region Public API
public void SetEquippedPrefix(EntityUid uid, string? prefix, ClothingComponent? clothing = null)
--- /dev/null
+using Content.Shared.Actions;
+using Content.Shared.Clothing.Components;
+using Content.Shared.Inventory;
+using Content.Shared.Inventory.Events;
+using Content.Shared.Popups;
+
+namespace Content.Shared.Clothing.EntitySystems;
+
+public sealed class MaskSystem : EntitySystem
+{
+ [Dependency] private readonly SharedActionsSystem _actionSystem = default!;
+ [Dependency] private readonly InventorySystem _inventorySystem = default!;
+ [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<MaskComponent, ToggleMaskEvent>(OnToggleMask);
+ SubscribeLocalEvent<MaskComponent, GetItemActionsEvent>(OnGetActions);
+ SubscribeLocalEvent<MaskComponent, GotUnequippedEvent>(OnGotUnequipped);
+ }
+
+ private void OnGetActions(EntityUid uid, MaskComponent component, GetItemActionsEvent args)
+ {
+ if (!args.InHands)
+ args.AddAction(ref component.ToggleActionEntity, component.ToggleAction);
+ }
+
+ private void OnToggleMask(Entity<MaskComponent> ent, ref ToggleMaskEvent args)
+ {
+ var (uid, mask) = ent;
+ if (mask.ToggleActionEntity == null)
+ return;
+
+ if (!_inventorySystem.TryGetSlotEntity(args.Performer, "mask", out var existing) || !uid.Equals(existing))
+ return;
+
+ mask.IsToggled ^= true;
+ _actionSystem.SetToggled(mask.ToggleActionEntity, mask.IsToggled);
+
+ if (mask.IsToggled)
+ _popupSystem.PopupEntity(Loc.GetString("action-mask-pull-down-popup-message", ("mask", uid)), args.Performer, args.Performer);
+ else
+ _popupSystem.PopupEntity(Loc.GetString("action-mask-pull-up-popup-message", ("mask", uid)), args.Performer, args.Performer);
+
+ ToggleMaskComponents(uid, mask, args.Performer);
+ }
+
+ // set to untoggled when unequipped, so it isn't left in a 'pulled down' state
+ private void OnGotUnequipped(EntityUid uid, MaskComponent mask, GotUnequippedEvent args)
+ {
+ if (mask.ToggleActionEntity == null)
+ return;
+
+ mask.IsToggled = false;
+ Dirty(uid, mask);
+ _actionSystem.SetToggled(mask.ToggleActionEntity, mask.IsToggled);
+
+ ToggleMaskComponents(uid, mask, args.Equipee, true);
+ }
+
+ private void ToggleMaskComponents(EntityUid uid, MaskComponent mask, EntityUid wearer, bool isEquip = false)
+ {
+ var maskEv = new ItemMaskToggledEvent(wearer, mask.IsToggled, isEquip);
+ RaiseLocalEvent(uid, ref maskEv);
+
+ var wearerEv = new WearerMaskToggledEvent(mask.IsToggled);
+ RaiseLocalEvent(uid, ref wearerEv);
+ }
+}
+using Content.Shared.Clothing;
using Content.Shared.IdentityManagement.Components;
using Content.Shared.Inventory;
using Robust.Shared.Containers;
SubscribeLocalEvent<IdentityComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<IdentityBlockerComponent, SeeIdentityAttemptEvent>(OnSeeIdentity);
SubscribeLocalEvent<IdentityBlockerComponent, InventoryRelayedEvent<SeeIdentityAttemptEvent>>((e, c, ev) => OnSeeIdentity(e, c, ev.Args));
+ SubscribeLocalEvent<IdentityBlockerComponent, ItemMaskToggledEvent>(OnMaskToggled);
}
private void OnSeeIdentity(EntityUid uid, IdentityBlockerComponent component, SeeIdentityAttemptEvent args)
{
component.IdentityEntitySlot = _container.EnsureContainer<ContainerSlot>(uid, SlotName);
}
+
+ private void OnMaskToggled(Entity<IdentityBlockerComponent> ent, ref ItemMaskToggledEvent args)
+ {
+ ent.Comp.Enabled = !args.IsToggled;
+ }
}