+++ /dev/null
-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
-}
+++ /dev/null
-using Content.Shared.Cabinet;
-
-namespace Content.Server.Cabinet;
-
-public sealed class ItemCabinetSystem : SharedItemCabinetSystem
-{
- // shitposting on main???
-}
-
-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.
+/// 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;
+ [DataField]
+ public string Slot = "ItemCabinet";
+}
- /// <summary>
- /// The state for when the cabinet is closed
- /// </summary>
- [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
- public string? ClosedState;
+[Serializable, NetSerializable]
+public enum ItemCabinetVisuals : byte
+{
+ ContainsItem,
+ Layer
}
--- /dev/null
+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);
+ }
+}
+++ /dev/null
-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));
- }
- }
-}
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!
[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>
/// 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?
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;
/// </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!;
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(Entity<OpenableComponent> ent, ref UseInHandEvent args)
+ {
+ if (args.Handled || !ent.Comp.OpenableByHand)
+ return;
+
+ args.Handled = TryToggle(ent, args.User);
}
- private void OnUse(EntityUid uid, OpenableComponent comp, UseInHandEvent args)
+ private void OnActivated(Entity<OpenableComponent> ent, ref ActivateInWorldEvent args)
{
- if (args.Handled || !comp.OpenableByHand)
+ if (args.Handled || !ent.Comp.OpenOnActivate)
return;
- args.Handled = TryOpen(uid, comp, args.User);
+ args.Handled = TryToggle(ent, args.User);
}
private void OnExamined(EntityUid uid, OpenableComponent comp, ExaminedEvent args)
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)
{
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
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.
/// <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);
_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>
/// </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);
--- /dev/null
+- 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 }
- 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:
layer:
- MidImpassable
- LowImpassable
- - type: ItemSlots
- - type: ContainerContainer
- containers:
- ItemCabinet: !type:ContainerSlot
- type: Anchorable
delay: 4
- - type: Appearance
- type: entity
id: GlassBox
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"]
- type: Construction
graph: GlassBox
node: glassBox
- - type: ItemCabinet
- cabinetSlot:
- ejectOnInteract: true
- whitelist:
- tags:
- - WeaponAntiqueLaser
- doorSound:
- path: /Audio/Machines/machine_switch.ogg
- openState: glass-up
- closedState: glass
+ - type: ItemSlots
+ slots:
+ ItemCabinet:
+ ejectOnInteract: true
+ whitelist:
+ tags:
+ - WeaponAntiqueLaser
+ 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:
- - WeaponAntiqueLaser
+ - 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
-- 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
- - type: Transform
- anchored: true
- - type: Clickable
- - type: InteractionOutline
- - type: Sprite
- sprite: Structures/Wallmounts/defib_cabinet.rsi
- noRot: false
- layers:
- - state: frame
- - state: fill
- map: ["enum.ItemCabinetVisualLayers.ContainsItem"]
- visible: true
- - state: closed
- map: ["enum.ItemCabinetVisualLayers.Door"]
- - type: ItemCabinet
- cabinetSlot:
+ - type: WallMount
+ arc: 175
+ - type: Transform
+ anchored: true
+ - type: Clickable
+ - type: InteractionOutline
+ - type: Sprite
+ sprite: Structures/Wallmounts/defib_cabinet.rsi
+ noRot: false
+ layers:
+ - state: frame
+ - state: fill
+ map: ["enum.ItemCabinetVisuals.Layer"]
+ visible: true
+ - state: closed
+ 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
- damage: 80
- behaviors:
- - !type:DoActsBehavior
- acts: [ "Destruction" ]
- - trigger:
- !type:DamageTrigger
- damage: 40
- behaviors:
- - !type:EmptyAllContainersBehaviour
- - !type:DoActsBehavior
- acts: [ "Destruction" ]
- - !type:PlaySoundBehavior
- sound:
- collection: MetalGlassBreak
- placement:
- mode: SnapgridCenter
+ - type: Damageable
+ damageContainer: Inorganic
+ damageModifierSet: Metallic
+ - type: Destructible
+ thresholds:
+ - trigger: !type:DamageTrigger
+ damage: 80
+ behaviors:
+ - !type:DoActsBehavior
+ acts: [ "Destruction" ]
+ - trigger: !type:DamageTrigger
+ damage: 40
+ behaviors:
+ - !type:EmptyAllContainersBehaviour
+ - !type:DoActsBehavior
+ acts: [ "Destruction" ]
+ - !type:PlaySoundBehavior
+ sound:
+ collection: MetalGlassBreak
- 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:
- - Defibrillator
- doorSound:
- path: /Audio/Machines/machine_switch.ogg
- openState: open
- closedState: closed
+ - type: ContainerFill
+ containers:
+ ItemCabinet:
+ - Defibrillator
- 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
-- 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
layers:
- state: frame
- state: extinguisher
- map: ["enum.ItemCabinetVisualLayers.ContainsItem"]
+ map: ["enum.ItemCabinetVisuals.Layer"]
visible: true
- state: closed
- map: ["enum.ItemCabinetVisualLayers.Door"]
- - type: ItemCabinet
- cabinetSlot:
- ejectOnInteract: true
- whitelist:
- components:
- - FireExtinguisher
- doorSound:
- path: /Audio/Machines/machine_switch.ogg
- openState: open
- closedState: closed
- - type: Appearance
+ map: ["enum.OpenableVisuals.Layer"]
- type: ItemSlots
- - type: ContainerContainer
- containers:
- ItemCabinet: !type:ContainerSlot
+ slots:
+ ItemCabinet:
+ ejectOnInteract: true
+ whitelist:
+ components:
+ - FireExtinguisher
- type: Damageable
damageContainer: Inorganic
damageModifierSet: Metallic
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:
- - FireExtinguisher
- doorSound:
- path: /Audio/Machines/machine_switch.ogg
- openState: open
- closedState: closed
+ - type: ContainerFill
+ containers:
+ ItemCabinet:
+ - FireExtinguisher
- 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
+# 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
damageModifierSet: Glass
- type: Destructible
thresholds:
- - trigger:
- !type:DamageTrigger
- damage: 300
- behaviors:
- - !type:DoActsBehavior
- acts: [ "Destruction" ]
- - trigger:
- !type:DamageTrigger
- damage: 200 #20ish crowbar hits
- behaviors:
- - !type:EmptyAllContainersBehaviour
- - !type:DoActsBehavior
- acts: [ "Destruction" ]
- - !type:PlaySoundBehavior
- sound:
- collection: MetalGlassBreak
+ - trigger:
+ !type:DamageTrigger
+ damage: 300
+ behaviors:
+ - !type:DoActsBehavior
+ acts: [ "Destruction" ]
+ - trigger:
+ !type:DamageTrigger
+ damage: 200 #20ish crowbar hits
+ behaviors:
+ - !type:EmptyAllContainersBehaviour
+ - !type:DoActsBehavior
+ acts: [ "Destruction" ]
+ - !type:PlaySoundBehavior
+ sound:
+ collection: MetalGlassBreak
- type: MeleeSound
soundGroups:
Brute:
layers:
- state: cabinet
- state: fireaxe
- map: ["enum.ItemCabinetVisualLayers.ContainsItem"]
+ map: ["enum.ItemCabinetVisuals.Layer"]
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
+ map: ["enum.OpenableVisuals.Layer"]
+ - type: ItemSlots
+ slots:
+ ItemCabinet:
+ ejectOnInteract: true
+ whitelist:
+ tags:
+ - FireAxe
- 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:
- - FireAxe
- doorSound:
- path: /Audio/Machines/machine_switch.ogg
- openState: glass-up
- closedState: glass
+ - type: ContainerFill
+ containers:
+ ItemCabinet:
+ - FireAxe
- 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
layers:
- state: cabinet
- state: shotgun
- map: ["enum.ItemCabinetVisualLayers.ContainsItem"]
+ map: ["enum.ItemCabinetVisuals.Layer"]
visible: true
- state: glass
- map: ["enum.ItemCabinetVisualLayers.Door"]
- - type: ItemCabinet
- cabinetSlot:
- ejectOnInteract: true
- whitelist:
- tags:
- - WeaponShotgunKammerer
+ map: ["enum.OpenableVisuals.Layer"]
+ - type: ItemSlots
+ slots:
+ ItemCabinet:
+ ejectOnInteract: true
+ whitelist:
+ tags:
+ - WeaponShotgunKammerer
- type: AccessReader
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:
- - WeaponShotgunKammerer
+ - type: ContainerFill
+ containers:
+ ItemCabinet:
+ - WeaponShotgunKammerer
- type: entity
+ parent: [ShotGunCabinetFilled, ShotGunCabinetOpen]
id: ShotGunCabinetFilledOpen
- parent: [ShotGunCabinetFilled,FireAxeCabinetFilledOpen]
suffix: Filled, Open
-