item cabinet rework + stuff (#26779)
This commit is contained in:
@@ -1,27 +0,0 @@
|
||||
using Content.Shared.Cabinet;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Cabinet;
|
||||
|
||||
public sealed class ItemCabinetSystem : SharedItemCabinetSystem
|
||||
{
|
||||
protected override void UpdateAppearance(EntityUid uid, ItemCabinetComponent? cabinet = null)
|
||||
{
|
||||
if (!Resolve(uid, ref cabinet))
|
||||
return;
|
||||
|
||||
if (!TryComp<SpriteComponent>(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);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ItemCabinetVisualLayers
|
||||
{
|
||||
Door,
|
||||
ContainsItem
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using Content.Shared.Cabinet;
|
||||
|
||||
namespace Content.Server.Cabinet;
|
||||
|
||||
public sealed class ItemCabinetSystem : SharedItemCabinetSystem
|
||||
{
|
||||
// shitposting on main???
|
||||
}
|
||||
|
||||
@@ -1,43 +1,25 @@
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Cabinet;
|
||||
|
||||
/// <summary>
|
||||
/// Used for entities that can be opened, closed, and can hold one item. E.g., fire extinguisher cabinets.
|
||||
/// Requires <c>OpenableComponent</c>.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(ItemCabinetSystem))]
|
||||
public sealed partial class ItemCabinetComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Sound to be played when the cabinet door is opened.
|
||||
/// Name of the <see cref="ItemSlot"/> that stores the actual item.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public SoundSpecifier? DoorSound;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ItemSlot"/> that stores the actual item. The entity whitelist, sounds, and other
|
||||
/// behaviours are specified by this <see cref="ItemSlot"/> definition.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables]
|
||||
public ItemSlot CabinetSlot = new();
|
||||
|
||||
/// <summary>
|
||||
/// Whether the cabinet is currently open or not.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool Opened;
|
||||
|
||||
/// <summary>
|
||||
/// The state for when the cabinet is open
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public string? OpenState;
|
||||
|
||||
/// <summary>
|
||||
/// The state for when the cabinet is closed
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public string? ClosedState;
|
||||
[DataField]
|
||||
public string Slot = "ItemCabinet";
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum ItemCabinetVisuals : byte
|
||||
{
|
||||
ContainsItem,
|
||||
Layer
|
||||
}
|
||||
|
||||
95
Content.Shared/Cabinet/ItemCabinetSystem.cs
Normal file
95
Content.Shared/Cabinet/ItemCabinetSystem.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
using Robust.Shared.Containers;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Content.Shared.Cabinet;
|
||||
|
||||
/// <summary>
|
||||
/// Controls ItemCabinet slot locking and visuals.
|
||||
/// </summary>
|
||||
public sealed class ItemCabinetSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ItemSlotsSystem _slots = default!;
|
||||
[Dependency] private readonly OpenableSystem _openable = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ItemCabinetComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<ItemCabinetComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<ItemCabinetComponent, EntInsertedIntoContainerMessage>(OnContainerModified);
|
||||
SubscribeLocalEvent<ItemCabinetComponent, EntRemovedFromContainerMessage>(OnContainerModified);
|
||||
SubscribeLocalEvent<ItemCabinetComponent, OpenableOpenedEvent>(OnOpened);
|
||||
SubscribeLocalEvent<ItemCabinetComponent, OpenableClosedEvent>(OnClosed);
|
||||
}
|
||||
|
||||
private void OnStartup(Entity<ItemCabinetComponent> ent, ref ComponentStartup args)
|
||||
{
|
||||
UpdateAppearance(ent);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<ItemCabinetComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
// update at mapinit to avoid copy pasting locked: true and locked: false for each closed/open prototype
|
||||
SetSlotLock(ent, !_openable.IsOpen(ent));
|
||||
}
|
||||
|
||||
private void UpdateAppearance(Entity<ItemCabinetComponent> ent)
|
||||
{
|
||||
_appearance.SetData(ent, ItemCabinetVisuals.ContainsItem, HasItem(ent));
|
||||
}
|
||||
|
||||
private void OnContainerModified(EntityUid uid, ItemCabinetComponent component, ContainerModifiedMessage args)
|
||||
{
|
||||
if (args.Container.ID == component.Slot)
|
||||
UpdateAppearance((uid, component));
|
||||
}
|
||||
|
||||
private void OnOpened(Entity<ItemCabinetComponent> ent, ref OpenableOpenedEvent args)
|
||||
{
|
||||
SetSlotLock(ent, false);
|
||||
}
|
||||
|
||||
private void OnClosed(Entity<ItemCabinetComponent> ent, ref OpenableClosedEvent args)
|
||||
{
|
||||
SetSlotLock(ent, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the cabinet's item slot.
|
||||
/// </summary>
|
||||
public bool TryGetSlot(Entity<ItemCabinetComponent> ent, [NotNullWhen(true)] out ItemSlot? slot)
|
||||
{
|
||||
slot = null;
|
||||
if (!TryComp<ItemSlotsComponent>(ent, out var slots))
|
||||
return false;
|
||||
|
||||
return _slots.TryGetSlot(ent, ent.Comp.Slot, out slot, slots);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the cabinet contains an item.
|
||||
/// </summary>
|
||||
public bool HasItem(Entity<ItemCabinetComponent> ent)
|
||||
{
|
||||
return TryGetSlot(ent, out var slot) && slot.HasItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lock or unlock the underlying item slot.
|
||||
/// </summary>
|
||||
public void SetSlotLock(Entity<ItemCabinetComponent> ent, bool closed)
|
||||
{
|
||||
if (!TryComp<ItemSlotsComponent>(ent, out var slots))
|
||||
return;
|
||||
|
||||
if (_slots.TryGetSlot(ent, ent.Comp.Slot, out var slot, slots))
|
||||
_slots.SetLock(ent, slot, closed, slots);
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Lock;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
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!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<ItemCabinetComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<ItemCabinetComponent, ComponentRemove>(OnComponentRemove);
|
||||
SubscribeLocalEvent<ItemCabinetComponent, ComponentStartup>(OnComponentStartup);
|
||||
SubscribeLocalEvent<ItemCabinetComponent, AfterAutoHandleStateEvent>(OnComponentHandleState);
|
||||
|
||||
SubscribeLocalEvent<ItemCabinetComponent, ActivateInWorldEvent>(OnActivateInWorld);
|
||||
SubscribeLocalEvent<ItemCabinetComponent, GetVerbsEvent<AlternativeVerb>>(AddToggleOpenVerb);
|
||||
|
||||
SubscribeLocalEvent<ItemCabinetComponent, EntInsertedIntoContainerMessage>(OnContainerModified);
|
||||
SubscribeLocalEvent<ItemCabinetComponent, EntRemovedFromContainerMessage>(OnContainerModified);
|
||||
|
||||
SubscribeLocalEvent<ItemCabinetComponent, LockToggleAttemptEvent>(OnLockToggleAttempt);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private void OnComponentHandleState(Entity<ItemCabinetComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
UpdateAppearance(ent, ent);
|
||||
}
|
||||
|
||||
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<AlternativeVerb> args)
|
||||
{
|
||||
if (args.Hands == null || !args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
if (TryComp<LockComponent>(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.Icon =
|
||||
new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/close.svg.192dpi.png"));
|
||||
}
|
||||
else
|
||||
{
|
||||
toggleVerb.Text = Loc.GetString("verb-common-open");
|
||||
toggleVerb.Icon =
|
||||
new SpriteSpecifier.Texture(new ResPath("/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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the ItemCabinet's state.
|
||||
/// </summary>
|
||||
public void ToggleItemCabinet(EntityUid uid, EntityUid? user = null, ItemCabinetComponent? cabinet = null)
|
||||
{
|
||||
if (!Resolve(uid, ref cabinet))
|
||||
return;
|
||||
|
||||
if (TryComp<LockComponent>(uid, out var lockComponent) && lockComponent.Locked)
|
||||
return;
|
||||
|
||||
cabinet.Opened = !cabinet.Opened;
|
||||
Dirty(uid, 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -235,6 +235,7 @@ public sealed class LockSystem : EntitySystem
|
||||
return !ev.Cancelled;
|
||||
}
|
||||
|
||||
// TODO: this should be a helper on AccessReaderSystem since so many systems copy paste it
|
||||
private bool HasUserAccess(EntityUid uid, EntityUid user, AccessReaderComponent? reader = null, bool quiet = true)
|
||||
{
|
||||
// Not having an AccessComponent means you get free access. woo!
|
||||
|
||||
@@ -26,6 +26,12 @@ public sealed partial class OpenableComponent : Component
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool OpenableByHand = true;
|
||||
|
||||
/// <summary>
|
||||
/// If true, tries to open when activated in world.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool OpenOnActivate;
|
||||
|
||||
/// <summary>
|
||||
/// Text shown when examining and its open.
|
||||
/// </summary>
|
||||
@@ -58,7 +64,7 @@ public sealed partial class OpenableComponent : Component
|
||||
/// Sound played when opening.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SoundSpecifier Sound = new SoundCollectionSpecifier("canOpenSounds");
|
||||
public SoundSpecifier? Sound = new SoundCollectionSpecifier("canOpenSounds");
|
||||
|
||||
/// <summary>
|
||||
/// Can this item be closed again after opening?
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Lock;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
@@ -16,6 +17,7 @@ namespace Content.Shared.Nutrition.EntitySystems;
|
||||
/// </summary>
|
||||
public sealed partial class OpenableSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly LockSystem _lock = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
@@ -26,26 +28,49 @@ public sealed partial class OpenableSystem : EntitySystem
|
||||
|
||||
SubscribeLocalEvent<OpenableComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<OpenableComponent, UseInHandEvent>(OnUse);
|
||||
// always try to unlock first before opening
|
||||
SubscribeLocalEvent<OpenableComponent, ActivateInWorldEvent>(OnActivated, after: new[] { typeof(LockSystem) });
|
||||
SubscribeLocalEvent<OpenableComponent, ExaminedEvent>(OnExamined);
|
||||
SubscribeLocalEvent<OpenableComponent, MeleeHitEvent>(HandleIfClosed);
|
||||
SubscribeLocalEvent<OpenableComponent, AfterInteractEvent>(HandleIfClosed);
|
||||
SubscribeLocalEvent<OpenableComponent, GetVerbsEvent<Verb>>(AddOpenCloseVerbs);
|
||||
SubscribeLocalEvent<OpenableComponent, GetVerbsEvent<AlternativeVerb>>(OnGetVerbs);
|
||||
SubscribeLocalEvent<OpenableComponent, SolutionTransferAttemptEvent>(OnTransferAttempt);
|
||||
SubscribeLocalEvent<OpenableComponent, AttemptShakeEvent>(OnAttemptShake);
|
||||
SubscribeLocalEvent<OpenableComponent, AttemptAddFizzinessEvent>(OnAttemptAddFizziness);
|
||||
SubscribeLocalEvent<OpenableComponent, LockToggleAttemptEvent>(OnLockToggleAttempt);
|
||||
|
||||
#if DEBUG
|
||||
SubscribeLocalEvent<OpenableComponent, MapInitEvent>(OnMapInit);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, OpenableComponent comp, ComponentInit args)
|
||||
private void OnMapInit(Entity<OpenableComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
UpdateAppearance(uid, comp);
|
||||
if (ent.Comp.Opened && _lock.IsLocked(ent.Owner))
|
||||
Log.Error($"Entity {ent} spawned locked open, this is a prototype mistake.");
|
||||
}
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
|
||||
private void OnInit(Entity<OpenableComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
UpdateAppearance(ent, ent.Comp);
|
||||
}
|
||||
|
||||
private void OnUse(EntityUid uid, OpenableComponent comp, UseInHandEvent args)
|
||||
private void OnUse(Entity<OpenableComponent> ent, ref UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled || !comp.OpenableByHand)
|
||||
if (args.Handled || !ent.Comp.OpenableByHand)
|
||||
return;
|
||||
|
||||
args.Handled = TryOpen(uid, comp, args.User);
|
||||
args.Handled = TryToggle(ent, args.User);
|
||||
}
|
||||
|
||||
private void OnActivated(Entity<OpenableComponent> ent, ref ActivateInWorldEvent args)
|
||||
{
|
||||
if (args.Handled || !ent.Comp.OpenOnActivate)
|
||||
return;
|
||||
|
||||
args.Handled = TryToggle(ent, args.User);
|
||||
}
|
||||
|
||||
private void OnExamined(EntityUid uid, OpenableComponent comp, ExaminedEvent args)
|
||||
@@ -63,12 +88,12 @@ public sealed partial class OpenableSystem : EntitySystem
|
||||
args.Handled = !comp.Opened;
|
||||
}
|
||||
|
||||
private void AddOpenCloseVerbs(EntityUid uid, OpenableComponent comp, GetVerbsEvent<Verb> args)
|
||||
private void OnGetVerbs(EntityUid uid, OpenableComponent comp, GetVerbsEvent<AlternativeVerb> args)
|
||||
{
|
||||
if (args.Hands == null || !args.CanAccess || !args.CanInteract)
|
||||
if (args.Hands == null || !args.CanAccess || !args.CanInteract || _lock.IsLocked(uid))
|
||||
return;
|
||||
|
||||
Verb verb;
|
||||
AlternativeVerb verb;
|
||||
if (comp.Opened)
|
||||
{
|
||||
if (!comp.Closeable)
|
||||
@@ -78,7 +103,8 @@ public sealed partial class OpenableSystem : EntitySystem
|
||||
{
|
||||
Text = Loc.GetString(comp.CloseVerbText),
|
||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/close.svg.192dpi.png")),
|
||||
Act = () => TryClose(args.Target, comp, args.User)
|
||||
Act = () => TryClose(args.Target, comp, args.User),
|
||||
// this verb is lower priority than drink verb (2) so it doesn't conflict
|
||||
};
|
||||
}
|
||||
else
|
||||
@@ -116,6 +142,13 @@ public sealed partial class OpenableSystem : EntitySystem
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void OnLockToggleAttempt(Entity<OpenableComponent> ent, ref LockToggleAttemptEvent args)
|
||||
{
|
||||
// can't lock something while it's open
|
||||
if (ent.Comp.Opened)
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the entity either does not have OpenableComponent or it is opened.
|
||||
/// Drinks that don't have OpenableComponent are automatically open, so it returns true.
|
||||
@@ -189,7 +222,12 @@ public sealed partial class OpenableSystem : EntitySystem
|
||||
/// <returns>Whether it got opened</returns>
|
||||
public bool TryOpen(EntityUid uid, OpenableComponent? comp = null, EntityUid? user = null)
|
||||
{
|
||||
if (!Resolve(uid, ref comp, false) || comp.Opened)
|
||||
if (!Resolve(uid, ref comp, false) || comp.Opened || _lock.IsLocked(uid))
|
||||
return false;
|
||||
|
||||
var ev = new OpenableOpenAttemptEvent(user);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
if (ev.Cancelled)
|
||||
return false;
|
||||
|
||||
SetOpen(uid, true, comp, user);
|
||||
@@ -211,6 +249,18 @@ public sealed partial class OpenableSystem : EntitySystem
|
||||
_audio.PlayPredicted(comp.CloseSound, uid, user);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If opened, tries closing it if it's closeable.
|
||||
/// If closed, tries opening it.
|
||||
/// </summary>
|
||||
public bool TryToggle(Entity<OpenableComponent> ent, EntityUid? user)
|
||||
{
|
||||
if (ent.Comp.Opened && ent.Comp.Closeable)
|
||||
return TryClose(ent, ent.Comp, user);
|
||||
|
||||
return TryOpen(ent, ent.Comp, user);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -224,3 +274,9 @@ public record struct OpenableOpenedEvent(EntityUid? User = null);
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct OpenableClosedEvent(EntityUid? User = null);
|
||||
|
||||
/// <summary>
|
||||
/// Raised before trying to open an Openable.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct OpenableOpenAttemptEvent(EntityUid? User, bool Cancelled = false);
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
- type: entity
|
||||
abstract: true
|
||||
id: BaseItemCabinet
|
||||
components:
|
||||
- type: Openable
|
||||
openOnActivate: true
|
||||
closeable: true
|
||||
sound:
|
||||
path: /Audio/Machines/machine_switch.ogg
|
||||
closeSound:
|
||||
path: /Audio/Machines/machine_switch.ogg
|
||||
- type: ItemCabinet
|
||||
- type: ItemSlots
|
||||
- type: ContainerContainer
|
||||
containers:
|
||||
ItemCabinet: !type:ContainerSlot
|
||||
- type: Appearance
|
||||
# perfect for most things but you can always replace it
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.ItemCabinetVisuals.ContainsItem:
|
||||
enum.ItemCabinetVisuals.Layer:
|
||||
True: { visible: true }
|
||||
False: { visible: false }
|
||||
enum.OpenableVisuals.Opened:
|
||||
enum.OpenableVisuals.Layer:
|
||||
True: { state: open }
|
||||
False: { state: closed }
|
||||
|
||||
- type: entity
|
||||
abstract: true
|
||||
parent: BaseItemCabinet
|
||||
id: BaseItemCabinetGlass
|
||||
components:
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.ItemCabinetVisuals.ContainsItem:
|
||||
enum.ItemCabinetVisuals.Layer:
|
||||
True: { visible: true }
|
||||
False: { visible: false }
|
||||
enum.OpenableVisuals.Opened:
|
||||
enum.OpenableVisuals.Layer:
|
||||
True: { state: glass-up }
|
||||
False: { state: glass }
|
||||
@@ -1,15 +1,12 @@
|
||||
- type: entity
|
||||
parent: [BaseStructureDynamic, BaseItemCabinetGlass]
|
||||
id: BaseGlassBox
|
||||
parent: BaseStructureDynamic
|
||||
abstract: true
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
components:
|
||||
- type: Transform
|
||||
anchored: true
|
||||
- type: Physics
|
||||
bodyType: Static
|
||||
- type: Clickable
|
||||
- type: InteractionOutline
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
@@ -23,13 +20,8 @@
|
||||
layer:
|
||||
- MidImpassable
|
||||
- LowImpassable
|
||||
- type: ItemSlots
|
||||
- type: ContainerContainer
|
||||
containers:
|
||||
ItemCabinet: !type:ContainerSlot
|
||||
- type: Anchorable
|
||||
delay: 4
|
||||
- type: Appearance
|
||||
|
||||
- type: entity
|
||||
id: GlassBox
|
||||
@@ -44,10 +36,10 @@
|
||||
layers:
|
||||
- state: base
|
||||
- state: caplaser # TODO: Remove it after item scaling in cabinets is implemented.
|
||||
map: ["enum.ItemCabinetVisualLayers.ContainsItem"]
|
||||
map: ["enum.ItemCabinetVisuals.Layer"]
|
||||
visible: true
|
||||
- state: glass
|
||||
map: ["enum.ItemCabinetVisualLayers.Door"]
|
||||
map: ["enum.OpenableVisuals.Layer"]
|
||||
- state: locked
|
||||
shader: unshaded
|
||||
map: ["enum.LockVisualLayers.Lock"]
|
||||
@@ -118,49 +110,39 @@
|
||||
- type: Construction
|
||||
graph: GlassBox
|
||||
node: glassBox
|
||||
- type: ItemCabinet
|
||||
cabinetSlot:
|
||||
- type: ItemSlots
|
||||
slots:
|
||||
ItemCabinet:
|
||||
ejectOnInteract: true
|
||||
whitelist:
|
||||
tags:
|
||||
- WeaponAntiqueLaser
|
||||
doorSound:
|
||||
path: /Audio/Machines/machine_switch.ogg
|
||||
openState: glass-up
|
||||
closedState: glass
|
||||
ejectSound: /Audio/Machines/machine_switch.ogg
|
||||
|
||||
- type: entity
|
||||
id: GlassBoxLaserOpen
|
||||
parent: GlassBoxLaser
|
||||
suffix: AntiqueLaser, Open
|
||||
components:
|
||||
- type: Openable
|
||||
opened: true
|
||||
- type: Lock
|
||||
locked: false
|
||||
- type: ItemCabinet
|
||||
opened: true
|
||||
|
||||
- type: entity
|
||||
id: GlassBoxLaserFilled
|
||||
parent: GlassBoxLaser
|
||||
suffix: AntiqueLaser, Filled
|
||||
components:
|
||||
- type: ItemCabinet
|
||||
cabinetSlot:
|
||||
startingItem: WeaponAntiqueLaser
|
||||
ejectOnInteract: true
|
||||
whitelist:
|
||||
tags:
|
||||
- type: ContainerFill
|
||||
containers:
|
||||
ItemCabinet:
|
||||
- WeaponAntiqueLaser
|
||||
|
||||
- type: entity
|
||||
parent: [GlassBoxLaserFilled, GlassBoxLaserOpen]
|
||||
id: GlassBoxLaserFilledOpen
|
||||
parent: GlassBoxLaserFilled
|
||||
suffix: AntiqueLaser, Filled, Open
|
||||
components:
|
||||
- type: Lock
|
||||
locked: false
|
||||
- type: ItemCabinet
|
||||
opened: true
|
||||
|
||||
- type: entity
|
||||
id: GlassBoxFrame
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
- type: entity
|
||||
# TODO: same as other wallmount cabinets they should use a base structure prototype
|
||||
- type: entity
|
||||
parent: BaseItemCabinet
|
||||
id: DefibrillatorCabinet
|
||||
name: defibrillator cabinet
|
||||
description: A small wall mounted cabinet designed to hold a defibrillator.
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
components:
|
||||
- type: WallMount
|
||||
arc: 175
|
||||
@@ -15,38 +19,28 @@
|
||||
layers:
|
||||
- state: frame
|
||||
- state: fill
|
||||
map: ["enum.ItemCabinetVisualLayers.ContainsItem"]
|
||||
map: ["enum.ItemCabinetVisuals.Layer"]
|
||||
visible: true
|
||||
- state: closed
|
||||
map: ["enum.ItemCabinetVisualLayers.Door"]
|
||||
- type: ItemCabinet
|
||||
cabinetSlot:
|
||||
map: ["enum.OpenableVisuals.Layer"]
|
||||
- type: ItemSlots
|
||||
slots:
|
||||
ItemCabinet:
|
||||
ejectOnInteract: true
|
||||
whitelist:
|
||||
components:
|
||||
- Defibrillator
|
||||
doorSound:
|
||||
path: /Audio/Machines/machine_switch.ogg
|
||||
openState: open
|
||||
closedState: closed
|
||||
- type: Appearance
|
||||
- type: ItemSlots
|
||||
- type: ContainerContainer
|
||||
containers:
|
||||
ItemCabinet: !type:ContainerSlot
|
||||
- type: Damageable
|
||||
damageContainer: Inorganic
|
||||
damageModifierSet: Metallic
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
- trigger:
|
||||
!type:DamageTrigger
|
||||
- trigger: !type:DamageTrigger
|
||||
damage: 80
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- trigger:
|
||||
!type:DamageTrigger
|
||||
- trigger: !type:DamageTrigger
|
||||
damage: 40
|
||||
behaviors:
|
||||
- !type:EmptyAllContainersBehaviour
|
||||
@@ -55,46 +49,26 @@
|
||||
- !type:PlaySoundBehavior
|
||||
sound:
|
||||
collection: MetalGlassBreak
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
|
||||
- type: entity
|
||||
id: DefibrillatorCabinetOpen
|
||||
parent: DefibrillatorCabinet
|
||||
id: DefibrillatorCabinetOpen
|
||||
suffix: Open
|
||||
components:
|
||||
- type: ItemCabinet
|
||||
- type: Openable
|
||||
opened: true
|
||||
doorSound:
|
||||
path: /Audio/Machines/machine_switch.ogg
|
||||
openState: open
|
||||
closedState: closed
|
||||
|
||||
- type: entity
|
||||
id: DefibrillatorCabinetFilled
|
||||
parent: DefibrillatorCabinet
|
||||
id: DefibrillatorCabinetFilled
|
||||
suffix: Filled
|
||||
components:
|
||||
- type: ItemCabinet
|
||||
cabinetSlot:
|
||||
ejectOnInteract: true
|
||||
startingItem: Defibrillator
|
||||
whitelist:
|
||||
components:
|
||||
- type: ContainerFill
|
||||
containers:
|
||||
ItemCabinet:
|
||||
- Defibrillator
|
||||
doorSound:
|
||||
path: /Audio/Machines/machine_switch.ogg
|
||||
openState: open
|
||||
closedState: closed
|
||||
|
||||
- type: entity
|
||||
parent: [DefibrillatorCabinetFilled, DefibrillatorCabinetOpen]
|
||||
id: DefibrillatorCabinetFilledOpen
|
||||
parent: DefibrillatorCabinetFilled
|
||||
suffix: Filled, Open
|
||||
components:
|
||||
- type: ItemCabinet
|
||||
opened: true
|
||||
doorSound:
|
||||
path: /Audio/Machines/machine_switch.ogg
|
||||
openState: open
|
||||
closedState: closed
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
- type: entity
|
||||
# TODO: this could probably use some kind of base structure prototype
|
||||
# every wallmount cabinet copypastes placement and like 8 components
|
||||
- type: entity
|
||||
parent: BaseItemCabinet
|
||||
id: ExtinguisherCabinet
|
||||
name: extinguisher cabinet
|
||||
description: A small wall mounted cabinet designed to hold a fire extinguisher.
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
components:
|
||||
- type: WallMount
|
||||
arc: 360
|
||||
@@ -15,25 +20,17 @@
|
||||
layers:
|
||||
- state: frame
|
||||
- state: extinguisher
|
||||
map: ["enum.ItemCabinetVisualLayers.ContainsItem"]
|
||||
map: ["enum.ItemCabinetVisuals.Layer"]
|
||||
visible: true
|
||||
- state: closed
|
||||
map: ["enum.ItemCabinetVisualLayers.Door"]
|
||||
- type: ItemCabinet
|
||||
cabinetSlot:
|
||||
map: ["enum.OpenableVisuals.Layer"]
|
||||
- type: ItemSlots
|
||||
slots:
|
||||
ItemCabinet:
|
||||
ejectOnInteract: true
|
||||
whitelist:
|
||||
components:
|
||||
- FireExtinguisher
|
||||
doorSound:
|
||||
path: /Audio/Machines/machine_switch.ogg
|
||||
openState: open
|
||||
closedState: closed
|
||||
- type: Appearance
|
||||
- type: ItemSlots
|
||||
- type: ContainerContainer
|
||||
containers:
|
||||
ItemCabinet: !type:ContainerSlot
|
||||
- type: Damageable
|
||||
damageContainer: Inorganic
|
||||
damageModifierSet: Metallic
|
||||
@@ -57,46 +54,26 @@
|
||||
collection: MetalGlassBreak
|
||||
params:
|
||||
volume: -4
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
|
||||
- type: entity
|
||||
id: ExtinguisherCabinetOpen
|
||||
parent: ExtinguisherCabinet
|
||||
id: ExtinguisherCabinetOpen
|
||||
suffix: Open
|
||||
components:
|
||||
- type: ItemCabinet
|
||||
- type: Openable
|
||||
opened: true
|
||||
doorSound:
|
||||
path: /Audio/Machines/machine_switch.ogg
|
||||
openState: open
|
||||
closedState: closed
|
||||
|
||||
- type: entity
|
||||
id: ExtinguisherCabinetFilled
|
||||
parent: ExtinguisherCabinet
|
||||
id: ExtinguisherCabinetFilled
|
||||
suffix: Filled
|
||||
components:
|
||||
- type: ItemCabinet
|
||||
cabinetSlot:
|
||||
ejectOnInteract: true
|
||||
startingItem: FireExtinguisher
|
||||
whitelist:
|
||||
components:
|
||||
- type: ContainerFill
|
||||
containers:
|
||||
ItemCabinet:
|
||||
- FireExtinguisher
|
||||
doorSound:
|
||||
path: /Audio/Machines/machine_switch.ogg
|
||||
openState: open
|
||||
closedState: closed
|
||||
|
||||
- type: entity
|
||||
parent: [ExtinguisherCabinetFilled, ExtinguisherCabinetOpen]
|
||||
id: ExtinguisherCabinetFilledOpen
|
||||
parent: ExtinguisherCabinetFilled
|
||||
suffix: Filled, Open
|
||||
components:
|
||||
- type: ItemCabinet
|
||||
opened: true
|
||||
doorSound:
|
||||
path: /Audio/Machines/machine_switch.ogg
|
||||
openState: open
|
||||
closedState: closed
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
# TODO: same as fire extinguisher make it use a base structure theres lots of copy paste
|
||||
- type: entity
|
||||
parent: BaseItemCabinetGlass
|
||||
id: FireAxeCabinet
|
||||
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.
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
components:
|
||||
- type: Damageable
|
||||
damageContainer: Inorganic
|
||||
@@ -36,68 +40,42 @@
|
||||
layers:
|
||||
- state: cabinet
|
||||
- state: fireaxe
|
||||
map: ["enum.ItemCabinetVisualLayers.ContainsItem"]
|
||||
map: ["enum.ItemCabinetVisuals.Layer"]
|
||||
visible: true
|
||||
- state: glass
|
||||
map: ["enum.ItemCabinetVisualLayers.Door"]
|
||||
- type: ItemCabinet
|
||||
cabinetSlot:
|
||||
map: ["enum.OpenableVisuals.Layer"]
|
||||
- type: ItemSlots
|
||||
slots:
|
||||
ItemCabinet:
|
||||
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"], ["Command"]]
|
||||
- type: ItemSlots
|
||||
- type: ContainerContainer
|
||||
containers:
|
||||
ItemCabinet: !type:ContainerSlot
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
|
||||
- type: entity
|
||||
id: FireAxeCabinetOpen
|
||||
parent: FireAxeCabinet
|
||||
id: FireAxeCabinetOpen
|
||||
suffix: Open
|
||||
components:
|
||||
- type: ItemCabinet
|
||||
- type: Openable
|
||||
opened: true
|
||||
doorSound:
|
||||
path: /Audio/Machines/machine_switch.ogg
|
||||
openState: glass-up
|
||||
closedState: glass
|
||||
- type: Lock
|
||||
locked: false
|
||||
|
||||
- type: entity
|
||||
id: FireAxeCabinetFilled
|
||||
parent: FireAxeCabinet
|
||||
id: FireAxeCabinetFilled
|
||||
suffix: Filled
|
||||
components:
|
||||
- type: ItemCabinet
|
||||
cabinetSlot:
|
||||
startingItem: FireAxe
|
||||
ejectOnInteract: true
|
||||
whitelist:
|
||||
tags:
|
||||
- type: ContainerFill
|
||||
containers:
|
||||
ItemCabinet:
|
||||
- FireAxe
|
||||
doorSound:
|
||||
path: /Audio/Machines/machine_switch.ogg
|
||||
openState: glass-up
|
||||
closedState: glass
|
||||
|
||||
- type: entity
|
||||
parent: [FireAxeCabinetFilled, FireAxeCabinetOpen]
|
||||
id: FireAxeCabinetFilledOpen
|
||||
parent: FireAxeCabinetFilled
|
||||
suffix: Filled, Open
|
||||
components:
|
||||
- type: ItemCabinet
|
||||
opened: true
|
||||
doorSound:
|
||||
path: /Audio/Machines/machine_switch.ogg
|
||||
openState: glass-up
|
||||
closedState: glass
|
||||
|
||||
@@ -9,12 +9,13 @@
|
||||
layers:
|
||||
- state: cabinet
|
||||
- state: shotgun
|
||||
map: ["enum.ItemCabinetVisualLayers.ContainsItem"]
|
||||
map: ["enum.ItemCabinetVisuals.Layer"]
|
||||
visible: true
|
||||
- state: glass
|
||||
map: ["enum.ItemCabinetVisualLayers.Door"]
|
||||
- type: ItemCabinet
|
||||
cabinetSlot:
|
||||
map: ["enum.OpenableVisuals.Layer"]
|
||||
- type: ItemSlots
|
||||
slots:
|
||||
ItemCabinet:
|
||||
ejectOnInteract: true
|
||||
whitelist:
|
||||
tags:
|
||||
@@ -23,25 +24,26 @@
|
||||
access: [["Security"], ["Command"]]
|
||||
|
||||
- type: entity
|
||||
parent: ShotGunCabinet
|
||||
id: ShotGunCabinetOpen
|
||||
parent: [ShotGunCabinet, FireAxeCabinetOpen]
|
||||
suffix: Open
|
||||
components:
|
||||
- type: Openable
|
||||
opened: true
|
||||
- type: Lock
|
||||
locked: false
|
||||
|
||||
- type: entity
|
||||
parent: ShotGunCabinet
|
||||
id: ShotGunCabinetFilled
|
||||
parent: [ShotGunCabinet,FireAxeCabinetFilled]
|
||||
suffix: Filled
|
||||
components:
|
||||
- type: ItemCabinet
|
||||
cabinetSlot:
|
||||
startingItem: WeaponShotgunKammerer
|
||||
ejectOnInteract: true
|
||||
whitelist:
|
||||
tags:
|
||||
- type: ContainerFill
|
||||
containers:
|
||||
ItemCabinet:
|
||||
- WeaponShotgunKammerer
|
||||
|
||||
- type: entity
|
||||
parent: [ShotGunCabinetFilled, ShotGunCabinetOpen]
|
||||
id: ShotGunCabinetFilledOpen
|
||||
parent: [ShotGunCabinetFilled,FireAxeCabinetFilledOpen]
|
||||
suffix: Filled, Open
|
||||
|
||||
|
||||
Reference in New Issue
Block a user