item cabinet rework + stuff (#26779)

This commit is contained in:
deltanedas
2024-05-29 20:08:33 +00:00
committed by GitHub
parent a315f90abc
commit 404fe818e8
14 changed files with 379 additions and 454 deletions

View File

@@ -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
}

View File

@@ -1,9 +0,0 @@
using Content.Shared.Cabinet;
namespace Content.Server.Cabinet;
public sealed class ItemCabinetSystem : SharedItemCabinetSystem
{
// shitposting on main???
}

View File

@@ -1,43 +1,25 @@
using Content.Shared.Containers.ItemSlots;
using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared.Cabinet; namespace Content.Shared.Cabinet;
/// <summary> /// <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> /// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] [RegisterComponent, NetworkedComponent, Access(typeof(ItemCabinetSystem))]
public sealed partial class ItemCabinetComponent : Component public sealed partial class ItemCabinetComponent : Component
{ {
/// <summary> /// <summary>
/// Sound to be played when the cabinet door is opened. /// Name of the <see cref="ItemSlot"/> that stores the actual item.
/// </summary> /// </summary>
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] [DataField]
public SoundSpecifier? DoorSound; public string Slot = "ItemCabinet";
}
/// <summary>
/// The <see cref="ItemSlot"/> that stores the actual item. The entity whitelist, sounds, and other [Serializable, NetSerializable]
/// behaviours are specified by this <see cref="ItemSlot"/> definition. public enum ItemCabinetVisuals : byte
/// </summary> {
[DataField, ViewVariables] ContainsItem,
public ItemSlot CabinetSlot = new(); Layer
/// <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;
} }

View 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);
}
}

View File

@@ -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));
}
}
}

View File

@@ -235,6 +235,7 @@ public sealed class LockSystem : EntitySystem
return !ev.Cancelled; 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) private bool HasUserAccess(EntityUid uid, EntityUid user, AccessReaderComponent? reader = null, bool quiet = true)
{ {
// Not having an AccessComponent means you get free access. woo! // Not having an AccessComponent means you get free access. woo!

View File

@@ -26,6 +26,12 @@ public sealed partial class OpenableComponent : Component
[DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public bool OpenableByHand = true; public bool OpenableByHand = true;
/// <summary>
/// If true, tries to open when activated in world.
/// </summary>
[DataField, AutoNetworkedField]
public bool OpenOnActivate;
/// <summary> /// <summary>
/// Text shown when examining and its open. /// Text shown when examining and its open.
/// </summary> /// </summary>
@@ -58,7 +64,7 @@ public sealed partial class OpenableComponent : Component
/// Sound played when opening. /// Sound played when opening.
/// </summary> /// </summary>
[DataField] [DataField]
public SoundSpecifier Sound = new SoundCollectionSpecifier("canOpenSounds"); public SoundSpecifier? Sound = new SoundCollectionSpecifier("canOpenSounds");
/// <summary> /// <summary>
/// Can this item be closed again after opening? /// Can this item be closed again after opening?

View File

@@ -1,5 +1,6 @@
using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Lock;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Interaction.Events; using Content.Shared.Interaction.Events;
using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.Components;
@@ -16,6 +17,7 @@ namespace Content.Shared.Nutrition.EntitySystems;
/// </summary> /// </summary>
public sealed partial class OpenableSystem : EntitySystem public sealed partial class OpenableSystem : EntitySystem
{ {
[Dependency] private readonly LockSystem _lock = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedPopupSystem _popup = default!;
@@ -26,26 +28,49 @@ public sealed partial class OpenableSystem : EntitySystem
SubscribeLocalEvent<OpenableComponent, ComponentInit>(OnInit); SubscribeLocalEvent<OpenableComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<OpenableComponent, UseInHandEvent>(OnUse); 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, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<OpenableComponent, MeleeHitEvent>(HandleIfClosed); SubscribeLocalEvent<OpenableComponent, MeleeHitEvent>(HandleIfClosed);
SubscribeLocalEvent<OpenableComponent, AfterInteractEvent>(HandleIfClosed); SubscribeLocalEvent<OpenableComponent, AfterInteractEvent>(HandleIfClosed);
SubscribeLocalEvent<OpenableComponent, GetVerbsEvent<Verb>>(AddOpenCloseVerbs); SubscribeLocalEvent<OpenableComponent, GetVerbsEvent<AlternativeVerb>>(OnGetVerbs);
SubscribeLocalEvent<OpenableComponent, SolutionTransferAttemptEvent>(OnTransferAttempt); SubscribeLocalEvent<OpenableComponent, SolutionTransferAttemptEvent>(OnTransferAttempt);
SubscribeLocalEvent<OpenableComponent, AttemptShakeEvent>(OnAttemptShake); SubscribeLocalEvent<OpenableComponent, AttemptShakeEvent>(OnAttemptShake);
SubscribeLocalEvent<OpenableComponent, AttemptAddFizzinessEvent>(OnAttemptAddFizziness); 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; 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) private void OnExamined(EntityUid uid, OpenableComponent comp, ExaminedEvent args)
@@ -63,12 +88,12 @@ public sealed partial class OpenableSystem : EntitySystem
args.Handled = !comp.Opened; 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; return;
Verb verb; AlternativeVerb verb;
if (comp.Opened) if (comp.Opened)
{ {
if (!comp.Closeable) if (!comp.Closeable)
@@ -78,7 +103,8 @@ public sealed partial class OpenableSystem : EntitySystem
{ {
Text = Loc.GetString(comp.CloseVerbText), Text = Loc.GetString(comp.CloseVerbText),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/close.svg.192dpi.png")), 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 else
@@ -116,6 +142,13 @@ public sealed partial class OpenableSystem : EntitySystem
args.Cancelled = true; 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> /// <summary>
/// Returns true if the entity either does not have OpenableComponent or it is opened. /// 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. /// 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> /// <returns>Whether it got opened</returns>
public bool TryOpen(EntityUid uid, OpenableComponent? comp = null, EntityUid? user = null) 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; return false;
SetOpen(uid, true, comp, user); SetOpen(uid, true, comp, user);
@@ -211,6 +249,18 @@ public sealed partial class OpenableSystem : EntitySystem
_audio.PlayPredicted(comp.CloseSound, uid, user); _audio.PlayPredicted(comp.CloseSound, uid, user);
return true; 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>
@@ -224,3 +274,9 @@ public record struct OpenableOpenedEvent(EntityUid? User = null);
/// </summary> /// </summary>
[ByRefEvent] [ByRefEvent]
public record struct OpenableClosedEvent(EntityUid? User = null); 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);

View File

@@ -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 }

View File

@@ -1,15 +1,12 @@
- type: entity - type: entity
parent: [BaseStructureDynamic, BaseItemCabinetGlass]
id: BaseGlassBox id: BaseGlassBox
parent: BaseStructureDynamic
abstract: true abstract: true
placement:
mode: SnapgridCenter
components: components:
- type: Transform - type: Transform
anchored: true anchored: true
- type: Physics - type: Physics
bodyType: Static bodyType: Static
- type: Clickable
- type: InteractionOutline - type: InteractionOutline
- type: Fixtures - type: Fixtures
fixtures: fixtures:
@@ -23,13 +20,8 @@
layer: layer:
- MidImpassable - MidImpassable
- LowImpassable - LowImpassable
- type: ItemSlots
- type: ContainerContainer
containers:
ItemCabinet: !type:ContainerSlot
- type: Anchorable - type: Anchorable
delay: 4 delay: 4
- type: Appearance
- type: entity - type: entity
id: GlassBox id: GlassBox
@@ -44,10 +36,10 @@
layers: layers:
- state: base - state: base
- state: caplaser # TODO: Remove it after item scaling in cabinets is implemented. - state: caplaser # TODO: Remove it after item scaling in cabinets is implemented.
map: ["enum.ItemCabinetVisualLayers.ContainsItem"] map: ["enum.ItemCabinetVisuals.Layer"]
visible: true visible: true
- state: glass - state: glass
map: ["enum.ItemCabinetVisualLayers.Door"] map: ["enum.OpenableVisuals.Layer"]
- state: locked - state: locked
shader: unshaded shader: unshaded
map: ["enum.LockVisualLayers.Lock"] map: ["enum.LockVisualLayers.Lock"]
@@ -118,49 +110,39 @@
- type: Construction - type: Construction
graph: GlassBox graph: GlassBox
node: glassBox node: glassBox
- type: ItemCabinet - type: ItemSlots
cabinetSlot: slots:
ejectOnInteract: true ItemCabinet:
whitelist: ejectOnInteract: true
tags: whitelist:
- WeaponAntiqueLaser tags:
doorSound: - WeaponAntiqueLaser
path: /Audio/Machines/machine_switch.ogg ejectSound: /Audio/Machines/machine_switch.ogg
openState: glass-up
closedState: glass
- type: entity - type: entity
id: GlassBoxLaserOpen id: GlassBoxLaserOpen
parent: GlassBoxLaser parent: GlassBoxLaser
suffix: AntiqueLaser, Open suffix: AntiqueLaser, Open
components: components:
- type: Openable
opened: true
- type: Lock - type: Lock
locked: false locked: false
- type: ItemCabinet
opened: true
- type: entity - type: entity
id: GlassBoxLaserFilled id: GlassBoxLaserFilled
parent: GlassBoxLaser parent: GlassBoxLaser
suffix: AntiqueLaser, Filled suffix: AntiqueLaser, Filled
components: components:
- type: ItemCabinet - type: ContainerFill
cabinetSlot: containers:
startingItem: WeaponAntiqueLaser ItemCabinet:
ejectOnInteract: true - WeaponAntiqueLaser
whitelist:
tags:
- WeaponAntiqueLaser
- type: entity - type: entity
parent: [GlassBoxLaserFilled, GlassBoxLaserOpen]
id: GlassBoxLaserFilledOpen id: GlassBoxLaserFilledOpen
parent: GlassBoxLaserFilled
suffix: AntiqueLaser, Filled, Open suffix: AntiqueLaser, Filled, Open
components:
- type: Lock
locked: false
- type: ItemCabinet
opened: true
- type: entity - type: entity
id: GlassBoxFrame id: GlassBoxFrame

View File

@@ -1,100 +1,74 @@
- type: entity # TODO: same as other wallmount cabinets they should use a base structure prototype
- type: entity
parent: BaseItemCabinet
id: DefibrillatorCabinet id: DefibrillatorCabinet
name: defibrillator cabinet name: defibrillator cabinet
description: A small wall mounted cabinet designed to hold a defibrillator. description: A small wall mounted cabinet designed to hold a defibrillator.
placement:
mode: SnapgridCenter
components: components:
- type: WallMount - type: WallMount
arc: 175 arc: 175
- type: Transform - type: Transform
anchored: true anchored: true
- type: Clickable - type: Clickable
- type: InteractionOutline - type: InteractionOutline
- type: Sprite - type: Sprite
sprite: Structures/Wallmounts/defib_cabinet.rsi sprite: Structures/Wallmounts/defib_cabinet.rsi
noRot: false noRot: false
layers: layers:
- state: frame - state: frame
- state: fill - state: fill
map: ["enum.ItemCabinetVisualLayers.ContainsItem"] map: ["enum.ItemCabinetVisuals.Layer"]
visible: true visible: true
- state: closed - state: closed
map: ["enum.ItemCabinetVisualLayers.Door"] map: ["enum.OpenableVisuals.Layer"]
- type: ItemCabinet - type: ItemSlots
cabinetSlot: slots:
ItemCabinet:
ejectOnInteract: true ejectOnInteract: true
whitelist: whitelist:
components: components:
- Defibrillator - Defibrillator
doorSound: - type: Damageable
path: /Audio/Machines/machine_switch.ogg damageContainer: Inorganic
openState: open damageModifierSet: Metallic
closedState: closed - type: Destructible
- type: Appearance thresholds:
- type: ItemSlots - trigger: !type:DamageTrigger
- type: ContainerContainer damage: 80
containers: behaviors:
ItemCabinet: !type:ContainerSlot - !type:DoActsBehavior
- type: Damageable acts: [ "Destruction" ]
damageContainer: Inorganic - trigger: !type:DamageTrigger
damageModifierSet: Metallic damage: 40
- type: Destructible behaviors:
thresholds: - !type:EmptyAllContainersBehaviour
- trigger: - !type:DoActsBehavior
!type:DamageTrigger acts: [ "Destruction" ]
damage: 80 - !type:PlaySoundBehavior
behaviors: sound:
- !type:DoActsBehavior collection: MetalGlassBreak
acts: [ "Destruction" ]
- trigger:
!type:DamageTrigger
damage: 40
behaviors:
- !type:EmptyAllContainersBehaviour
- !type:DoActsBehavior
acts: [ "Destruction" ]
- !type:PlaySoundBehavior
sound:
collection: MetalGlassBreak
placement:
mode: SnapgridCenter
- type: entity - type: entity
id: DefibrillatorCabinetOpen
parent: DefibrillatorCabinet parent: DefibrillatorCabinet
id: DefibrillatorCabinetOpen
suffix: Open suffix: Open
components: components:
- type: ItemCabinet - type: Openable
opened: true opened: true
doorSound:
path: /Audio/Machines/machine_switch.ogg
openState: open
closedState: closed
- type: entity - type: entity
id: DefibrillatorCabinetFilled
parent: DefibrillatorCabinet parent: DefibrillatorCabinet
id: DefibrillatorCabinetFilled
suffix: Filled suffix: Filled
components: components:
- type: ItemCabinet - type: ContainerFill
cabinetSlot: containers:
ejectOnInteract: true ItemCabinet:
startingItem: Defibrillator - Defibrillator
whitelist:
components:
- Defibrillator
doorSound:
path: /Audio/Machines/machine_switch.ogg
openState: open
closedState: closed
- type: entity - type: entity
parent: [DefibrillatorCabinetFilled, DefibrillatorCabinetOpen]
id: DefibrillatorCabinetFilledOpen id: DefibrillatorCabinetFilledOpen
parent: DefibrillatorCabinetFilled
suffix: Filled, Open suffix: Filled, Open
components:
- type: ItemCabinet
opened: true
doorSound:
path: /Audio/Machines/machine_switch.ogg
openState: open
closedState: closed

View File

@@ -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 id: ExtinguisherCabinet
name: extinguisher cabinet name: extinguisher cabinet
description: A small wall mounted cabinet designed to hold a fire extinguisher. description: A small wall mounted cabinet designed to hold a fire extinguisher.
placement:
mode: SnapgridCenter
components: components:
- type: WallMount - type: WallMount
arc: 360 arc: 360
@@ -15,25 +20,17 @@
layers: layers:
- state: frame - state: frame
- state: extinguisher - state: extinguisher
map: ["enum.ItemCabinetVisualLayers.ContainsItem"] map: ["enum.ItemCabinetVisuals.Layer"]
visible: true visible: true
- state: closed - state: closed
map: ["enum.ItemCabinetVisualLayers.Door"] map: ["enum.OpenableVisuals.Layer"]
- type: ItemCabinet
cabinetSlot:
ejectOnInteract: true
whitelist:
components:
- FireExtinguisher
doorSound:
path: /Audio/Machines/machine_switch.ogg
openState: open
closedState: closed
- type: Appearance
- type: ItemSlots - type: ItemSlots
- type: ContainerContainer slots:
containers: ItemCabinet:
ItemCabinet: !type:ContainerSlot ejectOnInteract: true
whitelist:
components:
- FireExtinguisher
- type: Damageable - type: Damageable
damageContainer: Inorganic damageContainer: Inorganic
damageModifierSet: Metallic damageModifierSet: Metallic
@@ -57,46 +54,26 @@
collection: MetalGlassBreak collection: MetalGlassBreak
params: params:
volume: -4 volume: -4
placement:
mode: SnapgridCenter
- type: entity - type: entity
id: ExtinguisherCabinetOpen
parent: ExtinguisherCabinet parent: ExtinguisherCabinet
id: ExtinguisherCabinetOpen
suffix: Open suffix: Open
components: components:
- type: ItemCabinet - type: Openable
opened: true opened: true
doorSound:
path: /Audio/Machines/machine_switch.ogg
openState: open
closedState: closed
- type: entity - type: entity
id: ExtinguisherCabinetFilled
parent: ExtinguisherCabinet parent: ExtinguisherCabinet
id: ExtinguisherCabinetFilled
suffix: Filled suffix: Filled
components: components:
- type: ItemCabinet - type: ContainerFill
cabinetSlot: containers:
ejectOnInteract: true ItemCabinet:
startingItem: FireExtinguisher - FireExtinguisher
whitelist:
components:
- FireExtinguisher
doorSound:
path: /Audio/Machines/machine_switch.ogg
openState: open
closedState: closed
- type: entity - type: entity
parent: [ExtinguisherCabinetFilled, ExtinguisherCabinetOpen]
id: ExtinguisherCabinetFilledOpen id: ExtinguisherCabinetFilledOpen
parent: ExtinguisherCabinetFilled
suffix: Filled, Open suffix: Filled, Open
components:
- type: ItemCabinet
opened: true
doorSound:
path: /Audio/Machines/machine_switch.ogg
openState: open
closedState: closed

View File

@@ -1,29 +1,33 @@
# TODO: same as fire extinguisher make it use a base structure theres lots of copy paste
- type: entity - type: entity
parent: BaseItemCabinetGlass
id: FireAxeCabinet id: FireAxeCabinet
name: fire axe cabinet 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. 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: components:
- type: Damageable - type: Damageable
damageContainer: Inorganic damageContainer: Inorganic
damageModifierSet: Glass damageModifierSet: Glass
- type: Destructible - type: Destructible
thresholds: thresholds:
- trigger: - trigger:
!type:DamageTrigger !type:DamageTrigger
damage: 300 damage: 300
behaviors: behaviors:
- !type:DoActsBehavior - !type:DoActsBehavior
acts: [ "Destruction" ] acts: [ "Destruction" ]
- trigger: - trigger:
!type:DamageTrigger !type:DamageTrigger
damage: 200 #20ish crowbar hits damage: 200 #20ish crowbar hits
behaviors: behaviors:
- !type:EmptyAllContainersBehaviour - !type:EmptyAllContainersBehaviour
- !type:DoActsBehavior - !type:DoActsBehavior
acts: [ "Destruction" ] acts: [ "Destruction" ]
- !type:PlaySoundBehavior - !type:PlaySoundBehavior
sound: sound:
collection: MetalGlassBreak collection: MetalGlassBreak
- type: MeleeSound - type: MeleeSound
soundGroups: soundGroups:
Brute: Brute:
@@ -36,68 +40,42 @@
layers: layers:
- state: cabinet - state: cabinet
- state: fireaxe - state: fireaxe
map: ["enum.ItemCabinetVisualLayers.ContainsItem"] map: ["enum.ItemCabinetVisuals.Layer"]
visible: true visible: true
- state: glass - state: glass
map: ["enum.ItemCabinetVisualLayers.Door"] map: ["enum.OpenableVisuals.Layer"]
- type: ItemCabinet - type: ItemSlots
cabinetSlot: slots:
ejectOnInteract: true ItemCabinet:
whitelist: ejectOnInteract: true
tags: whitelist:
- FireAxe tags:
doorSound: - FireAxe
path: /Audio/Machines/machine_switch.ogg
openState: glass-up
closedState: glass
- type: Appearance
- type: Lock - type: Lock
- type: AccessReader - type: AccessReader
access: [["Atmospherics"], ["Command"]] access: [["Atmospherics"], ["Command"]]
- type: ItemSlots
- type: ContainerContainer
containers:
ItemCabinet: !type:ContainerSlot
placement:
mode: SnapgridCenter
- type: entity - type: entity
id: FireAxeCabinetOpen
parent: FireAxeCabinet parent: FireAxeCabinet
id: FireAxeCabinetOpen
suffix: Open suffix: Open
components: components:
- type: ItemCabinet - type: Openable
opened: true opened: true
doorSound: - type: Lock
path: /Audio/Machines/machine_switch.ogg locked: false
openState: glass-up
closedState: glass
- type: entity - type: entity
id: FireAxeCabinetFilled
parent: FireAxeCabinet parent: FireAxeCabinet
id: FireAxeCabinetFilled
suffix: Filled suffix: Filled
components: components:
- type: ItemCabinet - type: ContainerFill
cabinetSlot: containers:
startingItem: FireAxe ItemCabinet:
ejectOnInteract: true - FireAxe
whitelist:
tags:
- FireAxe
doorSound:
path: /Audio/Machines/machine_switch.ogg
openState: glass-up
closedState: glass
- type: entity - type: entity
parent: [FireAxeCabinetFilled, FireAxeCabinetOpen]
id: FireAxeCabinetFilledOpen id: FireAxeCabinetFilledOpen
parent: FireAxeCabinetFilled
suffix: Filled, Open suffix: Filled, Open
components:
- type: ItemCabinet
opened: true
doorSound:
path: /Audio/Machines/machine_switch.ogg
openState: glass-up
closedState: glass

View File

@@ -9,39 +9,41 @@
layers: layers:
- state: cabinet - state: cabinet
- state: shotgun - state: shotgun
map: ["enum.ItemCabinetVisualLayers.ContainsItem"] map: ["enum.ItemCabinetVisuals.Layer"]
visible: true visible: true
- state: glass - state: glass
map: ["enum.ItemCabinetVisualLayers.Door"] map: ["enum.OpenableVisuals.Layer"]
- type: ItemCabinet - type: ItemSlots
cabinetSlot: slots:
ejectOnInteract: true ItemCabinet:
whitelist: ejectOnInteract: true
tags: whitelist:
- WeaponShotgunKammerer tags:
- WeaponShotgunKammerer
- type: AccessReader - type: AccessReader
access: [["Security"], ["Command"]] access: [["Security"], ["Command"]]
- type: entity - type: entity
parent: ShotGunCabinet
id: ShotGunCabinetOpen id: ShotGunCabinetOpen
parent: [ShotGunCabinet, FireAxeCabinetOpen]
suffix: Open suffix: Open
components:
- type: Openable
opened: true
- type: Lock
locked: false
- type: entity - type: entity
parent: ShotGunCabinet
id: ShotGunCabinetFilled id: ShotGunCabinetFilled
parent: [ShotGunCabinet,FireAxeCabinetFilled]
suffix: Filled suffix: Filled
components: components:
- type: ItemCabinet - type: ContainerFill
cabinetSlot: containers:
startingItem: WeaponShotgunKammerer ItemCabinet:
ejectOnInteract: true - WeaponShotgunKammerer
whitelist:
tags:
- WeaponShotgunKammerer
- type: entity - type: entity
parent: [ShotGunCabinetFilled, ShotGunCabinetOpen]
id: ShotGunCabinetFilledOpen id: ShotGunCabinetFilledOpen
parent: [ShotGunCabinetFilled,FireAxeCabinetFilledOpen]
suffix: Filled, Open suffix: Filled, Open