diff --git a/Content.Client/Cabinet/ItemCabinetSystem.cs b/Content.Client/Cabinet/ItemCabinetSystem.cs index ec14c4049e..aba4fdae00 100644 --- a/Content.Client/Cabinet/ItemCabinetSystem.cs +++ b/Content.Client/Cabinet/ItemCabinetSystem.cs @@ -3,20 +3,20 @@ using Robust.Client.GameObjects; namespace Content.Client.Cabinet; -public sealed class ItemCabinetSystem : VisualizerSystem +public sealed class ItemCabinetSystem : SharedItemCabinetSystem { - protected override void OnAppearanceChange(EntityUid uid, ItemCabinetVisualsComponent component, ref AppearanceChangeEvent args) + protected override void UpdateAppearance(EntityUid uid, ItemCabinetComponent? cabinet = null) { - if (args.Sprite == null) + if (!Resolve(uid, ref cabinet)) return; - if (AppearanceSystem.TryGetData(uid, ItemCabinetVisuals.IsOpen, out var isOpen, args.Component) - && AppearanceSystem.TryGetData(uid, ItemCabinetVisuals.ContainsItem, out var contains, args.Component)) - { - var state = isOpen ? component.OpenState : component.ClosedState; - args.Sprite.LayerSetState(ItemCabinetVisualLayers.Door, state); - args.Sprite.LayerSetVisible(ItemCabinetVisualLayers.ContainsItem, contains); - } + if (!TryComp(uid, out var sprite)) + return; + + var state = cabinet.Opened ? cabinet.OpenState : cabinet.ClosedState; + if (state != null) + sprite.LayerSetState(ItemCabinetVisualLayers.Door, state); + sprite.LayerSetVisible(ItemCabinetVisualLayers.ContainsItem, cabinet.CabinetSlot.HasItem); } } diff --git a/Content.Client/Cabinet/ItemCabinetVisualsComponent.cs b/Content.Client/Cabinet/ItemCabinetVisualsComponent.cs deleted file mode 100644 index 480445e1e5..0000000000 --- a/Content.Client/Cabinet/ItemCabinetVisualsComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Content.Client.Cabinet; - -[RegisterComponent] -public sealed class ItemCabinetVisualsComponent : Component -{ - [DataField("openState", required: true)] - public string OpenState = default!; - - [DataField("closedState", required: true)] - public string ClosedState = default!; -} diff --git a/Content.Server/Cabinet/ItemCabinetComponent.cs b/Content.Server/Cabinet/ItemCabinetComponent.cs deleted file mode 100644 index 92cdb87e26..0000000000 --- a/Content.Server/Cabinet/ItemCabinetComponent.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Content.Shared.Containers.ItemSlots; -using Robust.Shared.Audio; - -namespace Content.Server.Cabinet -{ - /// - /// Used for entities that can be opened, closed, and can hold one item. E.g., fire extinguisher cabinets. - /// - [RegisterComponent] - public sealed class ItemCabinetComponent : Component - { - /// - /// Sound to be played when the cabinet door is opened. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("doorSound", required: true)] - public SoundSpecifier DoorSound { get; set; } = default!; - - /// - /// The that stores the actual item. The entity whitelist, sounds, and other - /// behaviours are specified by this definition. - /// - [DataField("cabinetSlot")] - public ItemSlot CabinetSlot = new(); - - /// - /// Whether the cabinet is currently open or not. - /// - [DataField("opened")] - public bool Opened { get; set; } = false; - } -} diff --git a/Content.Server/Cabinet/ItemCabinetSystem.cs b/Content.Server/Cabinet/ItemCabinetSystem.cs index 439c6e7da5..a21532ad0c 100644 --- a/Content.Server/Cabinet/ItemCabinetSystem.cs +++ b/Content.Server/Cabinet/ItemCabinetSystem.cs @@ -1,130 +1,9 @@ -using Content.Server.Storage.Components; -using Content.Shared.Audio; using Content.Shared.Cabinet; -using Content.Shared.Containers.ItemSlots; -using Content.Shared.Interaction; -using Content.Shared.Lock; -using Content.Shared.Verbs; -using Robust.Server.GameObjects; -using Robust.Shared.Audio; -using Robust.Shared.Containers; -using Robust.Shared.Player; -namespace Content.Server.Cabinet +namespace Content.Server.Cabinet; + +public sealed class ItemCabinetSystem : SharedItemCabinetSystem { - public sealed class ItemCabinetSystem : EntitySystem - { - [Dependency] private readonly IComponentFactory _factory = default!; - [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnComponentInit); - SubscribeLocalEvent(OnComponentRemove); - SubscribeLocalEvent(OnComponentStartup); - - SubscribeLocalEvent(OnActivateInWorld); - SubscribeLocalEvent>(AddToggleOpenVerb); - - SubscribeLocalEvent(OnContainerModified); - SubscribeLocalEvent(OnContainerModified); - - SubscribeLocalEvent(OnLockToggleAttempt); - } - - private void OnComponentInit(EntityUid uid, ItemCabinetComponent cabinet, ComponentInit args) - { - _itemSlotsSystem.AddItemSlot(uid, _factory.GetComponentName(cabinet.GetType()), cabinet.CabinetSlot); - } - private void OnComponentRemove(EntityUid uid, ItemCabinetComponent cabinet, ComponentRemove args) - { - _itemSlotsSystem.RemoveItemSlot(uid, cabinet.CabinetSlot); - } - - private void OnComponentStartup(EntityUid uid, ItemCabinetComponent cabinet, ComponentStartup args) - { - UpdateAppearance(uid, cabinet); - _itemSlotsSystem.SetLock(uid, cabinet.CabinetSlot, !cabinet.Opened); - } - - private void UpdateAppearance(EntityUid uid, - ItemCabinetComponent? cabinet = null, - AppearanceComponent? appearance = null) - { - if (!Resolve(uid, ref cabinet, ref appearance, false)) - return; - - _appearance.SetData(uid, ItemCabinetVisuals.IsOpen, cabinet.Opened, appearance); - _appearance.SetData(uid, ItemCabinetVisuals.ContainsItem, cabinet.CabinetSlot.HasItem, appearance); - } - - private void OnContainerModified(EntityUid uid, ItemCabinetComponent cabinet, ContainerModifiedMessage args) - { - if (!cabinet.Initialized) return; - - if (args.Container.ID == cabinet.CabinetSlot.ID) - UpdateAppearance(uid, cabinet); - } - - private void OnLockToggleAttempt(EntityUid uid, ItemCabinetComponent cabinet, ref LockToggleAttemptEvent args) - { - // Cannot lock or unlock while open. - if (cabinet.Opened) - args.Cancelled = true; - } - - private void AddToggleOpenVerb(EntityUid uid, ItemCabinetComponent cabinet, GetVerbsEvent args) - { - if (args.Hands == null || !args.CanAccess || !args.CanInteract) - return; - - if (TryComp(uid, out var lockComponent) && lockComponent.Locked) - return; - - // Toggle open verb - ActivationVerb toggleVerb = new(); - toggleVerb.Act = () => ToggleItemCabinet(uid, cabinet); - if (cabinet.Opened) - { - toggleVerb.Text = Loc.GetString("verb-common-close"); - toggleVerb.IconTexture = "/Textures/Interface/VerbIcons/close.svg.192dpi.png"; - } - else - { - toggleVerb.Text = Loc.GetString("verb-common-open"); - toggleVerb.IconTexture = "/Textures/Interface/VerbIcons/open.svg.192dpi.png"; - } - args.Verbs.Add(toggleVerb); - } - - private void OnActivateInWorld(EntityUid uid, ItemCabinetComponent comp, ActivateInWorldEvent args) - { - if (args.Handled) - return; - - args.Handled = true; - ToggleItemCabinet(uid, comp); - } - - /// - /// Toggles the ItemCabinet's state. - /// - private void ToggleItemCabinet(EntityUid uid, ItemCabinetComponent? cabinet = null) - { - if (!Resolve(uid, ref cabinet)) - return; - - if (TryComp(uid, out var lockComponent) && lockComponent.Locked) - return; - - cabinet.Opened = !cabinet.Opened; - SoundSystem.Play(cabinet.DoorSound.GetSound(), Filter.Pvs(uid), uid, AudioHelpers.WithVariation(0.15f)); - _itemSlotsSystem.SetLock(uid, cabinet.CabinetSlot, !cabinet.Opened); - - UpdateAppearance(uid, cabinet); - } - } + // shitposting on main??? } + diff --git a/Content.Shared/Cabinet/ItemCabinetComponent.cs b/Content.Shared/Cabinet/ItemCabinetComponent.cs new file mode 100644 index 0000000000..adb5f6d26d --- /dev/null +++ b/Content.Shared/Cabinet/ItemCabinetComponent.cs @@ -0,0 +1,65 @@ +using Content.Shared.Containers.ItemSlots; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared.Cabinet; + +/// +/// Used for entities that can be opened, closed, and can hold one item. E.g., fire extinguisher cabinets. +/// +[RegisterComponent, NetworkedComponent] +public sealed class ItemCabinetComponent : Component +{ + /// + /// Sound to be played when the cabinet door is opened. + /// + [DataField("doorSound"), ViewVariables(VVAccess.ReadWrite)] + public SoundSpecifier? DoorSound; + + /// + /// The that stores the actual item. The entity whitelist, sounds, and other + /// behaviours are specified by this definition. + /// + [DataField("cabinetSlot"), ViewVariables] + public ItemSlot CabinetSlot = new(); + + /// + /// Whether the cabinet is currently open or not. + /// + [DataField("opened")] + public bool Opened; + + /// + /// The state for when the cabinet is open + /// + [DataField("openState"), ViewVariables(VVAccess.ReadWrite)] + public string? OpenState; + + /// + /// The state for when the cabinet is closed + /// + [DataField("closedState"), ViewVariables(VVAccess.ReadWrite)] + public string? ClosedState; +} + +[Serializable, NetSerializable] +public sealed class ItemCabinetComponentState : ComponentState +{ + public SoundSpecifier? DoorSound; + + public bool Opened; + + public string? OpenState; + + public string? ClosedState; + + public ItemCabinetComponentState(SoundSpecifier? doorSound, bool opened, string? openState, string? closedState) + { + DoorSound = doorSound; + Opened = opened; + OpenState = openState; + ClosedState = closedState; + } +} + diff --git a/Content.Shared/Cabinet/ItemCabinetVisuals.cs b/Content.Shared/Cabinet/ItemCabinetVisuals.cs deleted file mode 100644 index d09e1ba9cf..0000000000 --- a/Content.Shared/Cabinet/ItemCabinetVisuals.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Robust.Shared.Serialization; - -namespace Content.Shared.Cabinet -{ - [Serializable, NetSerializable] - public enum ItemCabinetVisuals : byte - { - IsOpen, - ContainsItem - } -} diff --git a/Content.Shared/Cabinet/SharedItemCabinetSystem.cs b/Content.Shared/Cabinet/SharedItemCabinetSystem.cs new file mode 100644 index 0000000000..766373c129 --- /dev/null +++ b/Content.Shared/Cabinet/SharedItemCabinetSystem.cs @@ -0,0 +1,148 @@ +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Interaction; +using Content.Shared.Lock; +using Content.Shared.Verbs; +using Robust.Shared.Audio; +using Robust.Shared.Containers; +using Robust.Shared.GameStates; +using Robust.Shared.Timing; + +namespace Content.Shared.Cabinet; + +public abstract class SharedItemCabinetSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(OnHandleState); + + SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent(OnComponentRemove); + SubscribeLocalEvent(OnComponentStartup); + + SubscribeLocalEvent(OnActivateInWorld); + SubscribeLocalEvent>(AddToggleOpenVerb); + + SubscribeLocalEvent(OnContainerModified); + SubscribeLocalEvent(OnContainerModified); + + SubscribeLocalEvent(OnLockToggleAttempt); + } + + private void OnGetState(EntityUid uid, ItemCabinetComponent component, ref ComponentGetState args) + { + args.State = new ItemCabinetComponentState(component.DoorSound, + component.Opened, + component.OpenState, + component.ClosedState); + } + + private void OnHandleState(EntityUid uid, ItemCabinetComponent component, ref ComponentHandleState args) + { + if (args.Current is not ItemCabinetComponentState state) + return; + component.DoorSound = state.DoorSound; + component.Opened = state.Opened; + component.OpenState = state.OpenState; + component.ClosedState = state.ClosedState; + } + + private void OnComponentInit(EntityUid uid, ItemCabinetComponent cabinet, ComponentInit args) + { + _itemSlots.AddItemSlot(uid, "ItemCabinet", cabinet.CabinetSlot); + } + + private void OnComponentRemove(EntityUid uid, ItemCabinetComponent cabinet, ComponentRemove args) + { + _itemSlots.RemoveItemSlot(uid, cabinet.CabinetSlot); + } + + private void OnComponentStartup(EntityUid uid, ItemCabinetComponent cabinet, ComponentStartup args) + { + UpdateAppearance(uid, cabinet); + _itemSlots.SetLock(uid, cabinet.CabinetSlot, !cabinet.Opened); + } + + protected virtual void UpdateAppearance(EntityUid uid, ItemCabinetComponent? cabinet = null) + { + // we don't fuck with appearance data, and instead just manually update the sprite on the client + } + + private void OnContainerModified(EntityUid uid, ItemCabinetComponent cabinet, ContainerModifiedMessage args) + { + if (!cabinet.Initialized) + return; + + if (args.Container.ID == cabinet.CabinetSlot.ID) + UpdateAppearance(uid, cabinet); + } + + private void OnLockToggleAttempt(EntityUid uid, ItemCabinetComponent cabinet, ref LockToggleAttemptEvent args) + { + // Cannot lock or unlock while open. + if (cabinet.Opened) + args.Cancelled = true; + } + + private void AddToggleOpenVerb(EntityUid uid, ItemCabinetComponent cabinet, GetVerbsEvent args) + { + if (args.Hands == null || !args.CanAccess || !args.CanInteract) + return; + + if (TryComp(uid, out var lockComponent) && lockComponent.Locked) + return; + + // Toggle open verb + AlternativeVerb toggleVerb = new() + { + Act = () => ToggleItemCabinet(uid, args.User, cabinet) + }; + if (cabinet.Opened) + { + toggleVerb.Text = Loc.GetString("verb-common-close"); + toggleVerb.IconTexture = "/Textures/Interface/VerbIcons/close.svg.192dpi.png"; + } + else + { + toggleVerb.Text = Loc.GetString("verb-common-open"); + toggleVerb.IconTexture = "/Textures/Interface/VerbIcons/open.svg.192dpi.png"; + } + args.Verbs.Add(toggleVerb); + } + + private void OnActivateInWorld(EntityUid uid, ItemCabinetComponent comp, ActivateInWorldEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + ToggleItemCabinet(uid, args.User, comp); + } + + /// + /// Toggles the ItemCabinet's state. + /// + public void ToggleItemCabinet(EntityUid uid, EntityUid? user = null, ItemCabinetComponent? cabinet = null) + { + if (!Resolve(uid, ref cabinet)) + return; + + if (TryComp(uid, out var lockComponent) && lockComponent.Locked) + return; + + cabinet.Opened = !cabinet.Opened; + Dirty(cabinet); + _itemSlots.SetLock(uid, cabinet.CabinetSlot, !cabinet.Opened); + + if (_timing.IsFirstTimePredicted) + { + UpdateAppearance(uid, cabinet); + _audio.PlayPredicted(cabinet.DoorSound, uid, user, AudioParams.Default.WithVariation(0.15f)); + } + } +} diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/extinguisher_cabinet.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/extinguisher_cabinet.yml index 644e89d976..91c744f33a 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/extinguisher_cabinet.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/extinguisher_cabinet.yml @@ -28,10 +28,9 @@ - FireExtinguisher doorSound: path: /Audio/Machines/machine_switch.ogg - - type: Appearance - - type: ItemCabinetVisuals openState: open closedState: closed + - type: Appearance - type: ItemSlots - type: ContainerContainer containers: @@ -61,6 +60,10 @@ components: - type: ItemCabinet opened: true + doorSound: + path: /Audio/Machines/machine_switch.ogg + openState: open + closedState: closed - type: entity id: ExtinguisherCabinetFilled @@ -74,6 +77,10 @@ whitelist: components: - FireExtinguisher + doorSound: + path: /Audio/Machines/machine_switch.ogg + openState: open + closedState: closed - type: entity id: ExtinguisherCabinetFilledOpen @@ -82,3 +89,7 @@ components: - type: ItemCabinet opened: true + doorSound: + path: /Audio/Machines/machine_switch.ogg + openState: open + closedState: closed diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/fireaxe_cabinet.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/fireaxe_cabinet.yml index 19111ec26c..1d021c50a4 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/fireaxe_cabinet.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/fireaxe_cabinet.yml @@ -3,43 +3,42 @@ name: fire axe cabinet description: There is a small label that reads "For Emergency use only" along with details for safe use of the axe. As if. components: - - type: MeleeSound - soundGroups: - Brute: - path: - "/Audio/Effects/glass_hit.ogg" - - type: WallMount - - type: Clickable - - type: InteractionOutline - - type: Sprite - sprite: Structures/Wallmounts/fireaxe_cabinet.rsi - netsync: false - layers: - - state: cabinet - - state: fireaxe - map: ["enum.ItemCabinetVisualLayers.ContainsItem"] - visible: true - - state: glass - map: ["enum.ItemCabinetVisualLayers.Door"] - - type: ItemCabinet - cabinetSlot: - ejectOnInteract: true - whitelist: - tags: - - FireAxe - doorSound: - path: /Audio/Machines/machine_switch.ogg - - type: Appearance - - type: ItemCabinetVisuals - openState: glass-up - closedState: glass - - type: Lock - - type: AccessReader - access: [["Atmospherics"]] - - type: ItemSlots - - type: ContainerContainer - containers: - ItemCabinet: !type:ContainerSlot + - type: MeleeSound + soundGroups: + Brute: + path: + "/Audio/Effects/glass_hit.ogg" + - type: WallMount + - type: Clickable + - type: InteractionOutline + - type: Sprite + sprite: Structures/Wallmounts/fireaxe_cabinet.rsi + netsync: false + layers: + - state: cabinet + - state: fireaxe + map: ["enum.ItemCabinetVisualLayers.ContainsItem"] + visible: true + - state: glass + map: ["enum.ItemCabinetVisualLayers.Door"] + - type: ItemCabinet + cabinetSlot: + ejectOnInteract: true + whitelist: + tags: + - FireAxe + doorSound: + path: /Audio/Machines/machine_switch.ogg + openState: glass-up + closedState: glass + - type: Appearance + - type: Lock + - type: AccessReader + access: [["Atmospherics"]] + - type: ItemSlots + - type: ContainerContainer + containers: + ItemCabinet: !type:ContainerSlot placement: mode: SnapgridCenter @@ -50,6 +49,10 @@ components: - type: ItemCabinet opened: true + doorSound: + path: /Audio/Machines/machine_switch.ogg + openState: glass-up + closedState: glass - type: entity id: FireAxeCabinetFilled @@ -63,6 +66,10 @@ whitelist: tags: - FireAxe + doorSound: + path: /Audio/Machines/machine_switch.ogg + openState: glass-up + closedState: glass - type: entity id: FireAxeCabinetFilledOpen @@ -71,3 +78,7 @@ components: - type: ItemCabinet opened: true + doorSound: + path: /Audio/Machines/machine_switch.ogg + openState: glass-up + closedState: glass