Move MaskComponent to shared and add toggle events (#22395)
* Move MaskComponent to shared and add toggle events * Datafield and network IsToggled * Add missing dirty
This commit is contained in:
@@ -3,12 +3,14 @@ using Content.Server.Atmos.EntitySystems;
|
||||
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!;
|
||||
@@ -21,6 +23,7 @@ public sealed class LungSystem : EntitySystem
|
||||
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)
|
||||
@@ -48,6 +51,28 @@ public sealed class LungSystem : EntitySystem
|
||||
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>())
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
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;
|
||||
@@ -33,6 +34,7 @@ public class IdentitySystem : SharedIdentitySystem
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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.
|
||||
@@ -9,7 +9,7 @@ namespace Content.Server.Nutrition.EntitySystems;
|
||||
/// 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>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
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;
|
||||
@@ -21,6 +22,7 @@ public sealed partial class VoiceMaskSystem : EntitySystem
|
||||
{
|
||||
SubscribeLocalEvent<VoiceMaskComponent, TransformSpeakerNameEvent>(OnSpeakerNameTransform);
|
||||
SubscribeLocalEvent<VoiceMaskComponent, VoiceMaskChangeNameMessage>(OnChangeName);
|
||||
SubscribeLocalEvent<VoiceMaskComponent, ItemMaskToggledEvent>(OnMaskToggled);
|
||||
SubscribeLocalEvent<VoiceMaskerComponent, GotEquippedEvent>(OnEquip);
|
||||
SubscribeLocalEvent<VoiceMaskerComponent, GotUnequippedEvent>(OnUnequip);
|
||||
SubscribeLocalEvent<VoiceMaskSetNameEvent>(OnSetName);
|
||||
@@ -67,6 +69,11 @@ public sealed partial class VoiceMaskSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
@@ -59,3 +59,15 @@ public sealed class EquipmentVisualsUpdatedEvent : EntityEventArgs
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
22
Content.Shared/Clothing/Components/MaskComponent.cs
Normal file
22
Content.Shared/Clothing/Components/MaskComponent.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
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;
|
||||
}
|
||||
@@ -25,6 +25,7 @@ public abstract class ClothingSystem : EntitySystem
|
||||
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)
|
||||
@@ -52,6 +53,12 @@ public abstract class ClothingSystem : EntitySystem
|
||||
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)
|
||||
|
||||
71
Content.Shared/Clothing/EntitySystems/MaskSystem.cs
Normal file
71
Content.Shared/Clothing/EntitySystems/MaskSystem.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.IdentityManagement.Components;
|
||||
using Content.Shared.Inventory;
|
||||
using Robust.Shared.Containers;
|
||||
@@ -16,6 +17,7 @@ public abstract class SharedIdentitySystem : EntitySystem
|
||||
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)
|
||||
@@ -28,4 +30,9 @@ public abstract class SharedIdentitySystem : EntitySystem
|
||||
{
|
||||
component.IdentityEntitySlot = _container.EnsureContainer<ContainerSlot>(uid, SlotName);
|
||||
}
|
||||
|
||||
private void OnMaskToggled(Entity<IdentityBlockerComponent> ent, ref ItemMaskToggledEvent args)
|
||||
{
|
||||
ent.Comp.Enabled = !args.IsToggled;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user