diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs
index 5741ee1b8f..452f5be09f 100644
--- a/Content.Client/Entry/IgnoredComponents.cs
+++ b/Content.Client/Entry/IgnoredComponents.cs
@@ -299,6 +299,7 @@ namespace Content.Client.Entry
"IncreaseDamageOnWield",
"TabletopGame",
"LitOnPowered",
+ "Foldable",
"TriggerOnSignalReceived",
"ToggleDoorOnTrigger",
"DeviceNetworkComponent",
diff --git a/Content.Client/Morgue/Visualizers/BodyBagVisualizer.cs b/Content.Client/Morgue/Visualizers/BodyBagVisualizer.cs
index 1aeff3e8c8..b64ce7d8d8 100644
--- a/Content.Client/Morgue/Visualizers/BodyBagVisualizer.cs
+++ b/Content.Client/Morgue/Visualizers/BodyBagVisualizer.cs
@@ -23,6 +23,10 @@ namespace Content.Client.Morgue.Visualizers
{
sprite.LayerSetVisible(BodyBagVisualLayers.Label, labelVal);
}
+ else
+ {
+ sprite.LayerSetVisible(BodyBagVisualLayers.Label, false);
+ }
}
}
diff --git a/Content.Client/Storage/Visualizers/StorageVisualizer.cs b/Content.Client/Storage/Visualizers/StorageVisualizer.cs
index cd1a73927e..a48e0535db 100644
--- a/Content.Client/Storage/Visualizers/StorageVisualizer.cs
+++ b/Content.Client/Storage/Visualizers/StorageVisualizer.cs
@@ -10,6 +10,9 @@ namespace Content.Client.Storage.Visualizers
[UsedImplicitly]
public sealed class StorageVisualizer : AppearanceVisualizer
{
+ ///
+ /// Sets the base sprite to this layer. Exists to make the inheritance tree less boilerplate-y.
+ ///
[DataField("state")]
private string? _stateBase;
[DataField("state_open")]
@@ -41,9 +44,24 @@ namespace Content.Client.Storage.Visualizers
}
component.TryGetData(StorageVisuals.Open, out bool open);
- var state = open ? _stateOpen ?? $"{_stateBase}_open" : _stateClosed ?? $"{_stateBase}_door";
- sprite.LayerSetState(StorageVisualLayers.Door, state);
+ if (sprite.LayerMapTryGet(StorageVisualLayers.Door, out _))
+ {
+ sprite.LayerSetVisible(StorageVisualLayers.Door, true);
+
+ if (open && _stateOpen != null)
+ {
+ sprite.LayerSetState(StorageVisualLayers.Door, _stateOpen);
+ }
+ else if (!open && _stateClosed != null)
+ {
+ sprite.LayerSetState(StorageVisualLayers.Door, _stateClosed);
+ }
+ else
+ {
+ sprite.LayerSetVisible(StorageVisualLayers.Door, false);
+ }
+ }
if (component.TryGetData(StorageVisuals.CanLock, out bool canLock) && canLock)
{
diff --git a/Content.Client/Visualizer/FoldableVisualizer.cs b/Content.Client/Visualizer/FoldableVisualizer.cs
new file mode 100644
index 0000000000..6b2861dc7b
--- /dev/null
+++ b/Content.Client/Visualizer/FoldableVisualizer.cs
@@ -0,0 +1,36 @@
+using Robust.Client.GameObjects;
+using Robust.Shared.GameObjects;
+using Robust.Shared.IoC;
+using Robust.Shared.Serialization.Manager.Attributes;
+
+namespace Content.Client.Visualizer;
+
+
+public sealed class FoldableVisualizer : AppearanceVisualizer
+{
+ [DataField("key")]
+ private string _key = default!;
+
+ public override void OnChangeData(AppearanceComponent appearance)
+ {
+ base.OnChangeData(appearance);
+
+ var entManager = IoCManager.Resolve();
+
+ if (!entManager.TryGetComponent(appearance.Owner, out SpriteComponent? sprite)) return;
+
+ if (appearance.TryGetData("FoldedState", out bool folded) && folded)
+ {
+ sprite.LayerSetState(FoldableVisualLayers.Base, $"{_key}_folded");
+ }
+ else
+ {
+ sprite.LayerSetState(FoldableVisualLayers.Base, $"{_key}");
+ }
+ }
+
+ public enum FoldableVisualLayers : byte
+ {
+ Base,
+ }
+}
diff --git a/Content.Client/Visualizer/RollerbedVisualizer.cs b/Content.Client/Visualizer/RollerbedVisualizer.cs
new file mode 100644
index 0000000000..053d521d57
--- /dev/null
+++ b/Content.Client/Visualizer/RollerbedVisualizer.cs
@@ -0,0 +1,29 @@
+using JetBrains.Annotations;
+using Robust.Client.GameObjects;
+using Robust.Shared.GameObjects;
+using Robust.Shared.IoC;
+using Robust.Shared.Serialization.Manager.Attributes;
+
+namespace Content.Client.Visualizer
+{
+ [UsedImplicitly]
+ public sealed class RollerbedVisualizer : AppearanceVisualizer
+ {
+ [DataField("key")]
+ private string _key = default!;
+
+ public override void OnChangeData(AppearanceComponent appearance)
+ {
+ base.OnChangeData(appearance);
+
+ var entManager = IoCManager.Resolve();
+
+ if (!entManager.TryGetComponent(appearance.Owner, out SpriteComponent? sprite)) return;
+
+ if (appearance.TryGetData("StrapState", out bool strapped) && strapped)
+ {
+ sprite.LayerSetState(0, $"{_key}_buckled");
+ }
+ }
+ }
+}
diff --git a/Content.Server/Buckle/Components/BuckleComponent.cs b/Content.Server/Buckle/Components/BuckleComponent.cs
index 0c40c6c768..d140e264fb 100644
--- a/Content.Server/Buckle/Components/BuckleComponent.cs
+++ b/Content.Server/Buckle/Components/BuckleComponent.cs
@@ -133,7 +133,7 @@ namespace Content.Server.Buckle.Components
break;
}
- ownTransform.LocalPosition = Vector2.Zero + BuckleOffset;
+ ownTransform.LocalPosition = Vector2.Zero + strap.BuckleOffset;
}
public bool CanBuckle(EntityUid user, EntityUid to, [NotNullWhen(true)] out StrapComponent? strap)
@@ -317,10 +317,17 @@ namespace Content.Server.Buckle.Components
BuckledTo = null;
- if (_entMan.GetComponent(Owner).Parent == _entMan.GetComponent(oldBuckledTo.Owner))
+ var entManager = IoCManager.Resolve();
+ var xform = entManager.GetComponent(Owner);
+ var oldBuckledXform = entManager.GetComponent(oldBuckledTo.Owner);
+
+ if (xform.ParentUid == oldBuckledXform.Owner)
{
- _entMan.GetComponent(Owner).AttachParentToContainerOrGrid();
- _entMan.GetComponent(Owner).WorldRotation = _entMan.GetComponent(oldBuckledTo.Owner).WorldRotation;
+ xform.AttachParentToContainerOrGrid();
+ xform.WorldRotation = oldBuckledXform.WorldRotation;
+
+ if (oldBuckledTo.UnbuckleOffset != Vector2.Zero)
+ xform.Coordinates = oldBuckledXform.Coordinates.Offset(oldBuckledTo.UnbuckleOffset);
}
Appearance?.SetData(BuckleVisuals.Buckled, false);
diff --git a/Content.Server/Buckle/Components/StrapComponent.cs b/Content.Server/Buckle/Components/StrapComponent.cs
index 3d06c638e0..21d1588225 100644
--- a/Content.Server/Buckle/Components/StrapComponent.cs
+++ b/Content.Server/Buckle/Components/StrapComponent.cs
@@ -1,7 +1,9 @@
using System.Collections.Generic;
using System.Linq;
+using Content.Shared.ActionBlocker;
using Content.Shared.Acts;
using Content.Shared.Alert;
+using Content.Shared.Buckle;
using Content.Shared.Buckle.Components;
using Content.Shared.DragDrop;
using Content.Shared.Interaction;
@@ -9,6 +11,8 @@ using Content.Shared.Sound;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
+using Robust.Shared.Maths;
+using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
@@ -21,8 +25,6 @@ namespace Content.Server.Buckle.Components
{
[ComponentDependency] public readonly SpriteComponent? SpriteComponent = null;
- [Dependency] private readonly IEntityManager _entMan = default!;
-
private readonly HashSet _buckledEntities = new();
///
@@ -37,6 +39,50 @@ namespace Content.Server.Buckle.Components
[ViewVariables] [DataField("size")] private int _size = 100;
private int _occupiedSize;
+ ///
+ /// The buckled entity will be offset by this amount from the center of the strap object.
+ /// If this offset it too big, it will be clamped to
+ ///
+ [DataField("buckleOffset", required: false)]
+ private Vector2 _buckleOffset = Vector2.Zero;
+
+ private bool _enabled = true;
+
+ ///
+ /// If disabled, nothing can be buckled on this object, and it will unbuckle anything that's already buckled
+ ///
+ public bool Enabled
+ {
+ get => _enabled;
+ set
+ {
+ _enabled = value;
+ if (_enabled == value) return;
+ RemoveAll();
+ }
+ }
+
+ ///
+ /// The distance above which a buckled entity will be automatically unbuckled.
+ /// Don't change it unless you really have to
+ ///
+ [DataField("maxBuckleDistance", required: false)]
+ public float MaxBuckleDistance = 1f;
+
+ ///
+ /// You can specify the offset the entity will have after unbuckling.
+ ///
+ [DataField("unbuckleOffset", required: false)]
+ public Vector2 UnbuckleOffset = Vector2.Zero;
+
+ ///
+ /// Gets and clamps the buckle offset to MaxBuckleDistance
+ ///
+ public Vector2 BuckleOffset => Vector2.Clamp(
+ _buckleOffset,
+ Vector2.One * -MaxBuckleDistance,
+ Vector2.One * MaxBuckleDistance);
+
///
/// The entity that is currently buckled here, synced from
///
@@ -96,6 +142,8 @@ namespace Content.Server.Buckle.Components
/// True if added, false otherwise
public bool TryAdd(BuckleComponent buckle, bool force = false)
{
+ if (!Enabled) return false;
+
if (!force && !HasSpace(buckle))
{
return false;
@@ -110,6 +158,12 @@ namespace Content.Server.Buckle.Components
buckle.Appearance?.SetData(StrapVisuals.RotationAngle, _rotation);
+ // Update the visuals of the strap object
+ if (IoCManager.Resolve().TryGetComponent(Owner, out var appearance))
+ {
+ appearance.SetData("StrapState", true);
+ }
+
#pragma warning disable 618
SendMessage(new StrapMessage(buckle.Owner, Owner));
#pragma warning restore 618
@@ -126,6 +180,11 @@ namespace Content.Server.Buckle.Components
{
if (_buckledEntities.Remove(buckle.Owner))
{
+ if (IoCManager.Resolve().TryGetComponent(Owner, out var appearance))
+ {
+ appearance.SetData("StrapState", false);
+ }
+
_occupiedSize -= buckle.Size;
#pragma warning disable 618
SendMessage(new UnStrapMessage(buckle.Owner, Owner));
@@ -147,9 +206,11 @@ namespace Content.Server.Buckle.Components
private void RemoveAll()
{
+ var entManager = IoCManager.Resolve();
+
foreach (var entity in _buckledEntities.ToArray())
{
- if (_entMan.TryGetComponent(entity, out var buckle))
+ if (entManager.TryGetComponent(entity, out var buckle))
{
buckle.TryUnbuckle(entity, true);
}
@@ -166,7 +227,9 @@ namespace Content.Server.Buckle.Components
bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs)
{
- if (!_entMan.TryGetComponent(eventArgs.User, out var buckle))
+ var entManager = IoCManager.Resolve();
+
+ if (!entManager.TryGetComponent(eventArgs.User, out var buckle))
{
return false;
}
@@ -176,7 +239,9 @@ namespace Content.Server.Buckle.Components
public override bool DragDropOn(DragDropEvent eventArgs)
{
- if (!_entMan.TryGetComponent(eventArgs.Dragged, out BuckleComponent? buckleComponent)) return false;
+ var entManager = IoCManager.Resolve();
+
+ if (!entManager.TryGetComponent(eventArgs.Dragged, out BuckleComponent? buckleComponent)) return false;
return buckleComponent.TryBuckle(eventArgs.User, Owner);
}
}
diff --git a/Content.Server/Buckle/Systems/BuckleSystem.cs b/Content.Server/Buckle/Systems/BuckleSystem.cs
index 0135e3aea0..8ec67713a6 100644
--- a/Content.Server/Buckle/Systems/BuckleSystem.cs
+++ b/Content.Server/Buckle/Systems/BuckleSystem.cs
@@ -42,10 +42,12 @@ namespace Content.Server.Buckle.Systems
if (!args.CanAccess || !args.CanInteract || !component.Buckled)
return;
- Verb verb = new();
- verb.Act = () => component.TryUnbuckle(args.User);
- verb.Text = Loc.GetString("verb-categories-unbuckle");
- verb.IconTexture = "/Textures/Interface/VerbIcons/unbuckle.svg.192dpi.png";
+ Verb verb = new()
+ {
+ Act = () => component.TryUnbuckle(args.User),
+ Text = Loc.GetString("verb-categories-unbuckle"),
+ IconTexture = "/Textures/Interface/VerbIcons/unbuckle.svg.192dpi.png"
+ };
if (args.Target == args.User && args.Using == null)
{
@@ -81,7 +83,7 @@ namespace Content.Server.Buckle.Systems
var strapPosition = EntityManager.GetComponent(strap.Owner).Coordinates.Offset(buckle.BuckleOffset);
- if (ev.NewPosition.InRange(EntityManager, strapPosition, 0.2f))
+ if (ev.NewPosition.InRange(EntityManager, strapPosition, strap.MaxBuckleDistance))
{
return;
}
diff --git a/Content.Server/Buckle/Systems/StrapSystem.cs b/Content.Server/Buckle/Systems/StrapSystem.cs
index 9acd58d2db..c1d8ebe784 100644
--- a/Content.Server/Buckle/Systems/StrapSystem.cs
+++ b/Content.Server/Buckle/Systems/StrapSystem.cs
@@ -27,7 +27,7 @@ namespace Content.Server.Buckle.Systems
private void AddStrapVerbs(EntityUid uid, StrapComponent component, GetInteractionVerbsEvent args)
{
- if (args.Hands == null || !args.CanAccess || !args.CanInteract)
+ if (args.Hands == null || !args.CanAccess || !args.CanInteract || !component.Enabled)
return;
// Note that for whatever bloody reason, buckle component has its own interaction range. Additionally, this
@@ -41,9 +41,12 @@ namespace Content.Server.Buckle.Systems
if (!_interactionSystem.InRangeUnobstructed(args.User, args.Target, range: buckledComp.Range))
continue;
- Verb verb = new();
- verb.Act = () => buckledComp.TryUnbuckle(args.User);
- verb.Category = VerbCategory.Unbuckle;
+ Verb verb = new()
+ {
+ Act = () => buckledComp.TryUnbuckle(args.User),
+ Category = VerbCategory.Unbuckle
+ };
+
if (entity == args.User)
verb.Text = Loc.GetString("verb-self-target-pronoun");
else
@@ -64,10 +67,12 @@ namespace Content.Server.Buckle.Systems
component.HasSpace(buckle) &&
_interactionSystem.InRangeUnobstructed(args.User, args.Target, range: buckle.Range))
{
- Verb verb = new();
- verb.Act = () => buckle.TryBuckle(args.User, args.Target);
- verb.Category = VerbCategory.Buckle;
- verb.Text = Loc.GetString("verb-self-target-pronoun");
+ Verb verb = new()
+ {
+ Act = () => buckle.TryBuckle(args.User, args.Target),
+ Category = VerbCategory.Buckle,
+ Text = Loc.GetString("verb-self-target-pronoun")
+ };
args.Verbs.Add(verb);
}
@@ -82,14 +87,15 @@ namespace Content.Server.Buckle.Systems
if (!_interactionSystem.InRangeUnobstructed(@using, args.Target, usingBuckle.Range, predicate: Ignored))
return;
- Verb verb = new();
- verb.Act = () => usingBuckle.TryBuckle(args.User, args.Target);
- verb.Category = VerbCategory.Buckle;
- verb.Text = EntityManager.GetComponent(@using).EntityName;
-
- // If the used entity is a person being pulled, prioritize this verb. Conversely, if it is
- // just a held object, the user is probably just trying to sit down.
- verb.Priority = EntityManager.HasComponent(@using) ? 1 : -1;
+ Verb verb = new()
+ {
+ Act = () => usingBuckle.TryBuckle(args.User, args.Target),
+ Category = VerbCategory.Buckle,
+ Text = EntityManager.GetComponent(@using).EntityName,
+ // just a held object, the user is probably just trying to sit down.
+ // If the used entity is a person being pulled, prioritize this verb. Conversely, if it is
+ Priority = EntityManager.HasComponent(@using) ? 1 : -1
+ };
args.Verbs.Add(verb);
}
diff --git a/Content.Server/Foldable/FoldableComponent.cs b/Content.Server/Foldable/FoldableComponent.cs
new file mode 100644
index 0000000000..514d4eddb5
--- /dev/null
+++ b/Content.Server/Foldable/FoldableComponent.cs
@@ -0,0 +1,25 @@
+#nullable enable
+
+using Robust.Shared.Containers;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Serialization.Manager.Attributes;
+using Robust.Shared.ViewVariables;
+
+namespace Content.Server.Foldable
+{
+
+ ///
+ /// Used to create "foldable structures" that you can pickup like an item when folded. Used for rollerbeds and wheelchairs
+ ///
+ [RegisterComponent]
+ public class FoldableComponent : Component
+ {
+ public override string Name => "Foldable";
+
+ [DataField("folded")]
+ [ViewVariables]
+ public bool IsFolded = false;
+
+ public bool CanBeFolded = true;
+ }
+}
diff --git a/Content.Server/Foldable/FoldableSystem.cs b/Content.Server/Foldable/FoldableSystem.cs
new file mode 100644
index 0000000000..380b9f29b5
--- /dev/null
+++ b/Content.Server/Foldable/FoldableSystem.cs
@@ -0,0 +1,131 @@
+using System.Linq;
+using Content.Server.Buckle.Components;
+using Content.Server.Storage.Components;
+using Content.Shared.Interaction;
+using Content.Shared.Item;
+using Content.Shared.Verbs;
+using JetBrains.Annotations;
+using Robust.Shared.Containers;
+using Robust.Shared.GameObjects;
+using Robust.Shared.IoC;
+using Robust.Shared.Localization;
+
+namespace Content.Server.Foldable
+{
+ [UsedImplicitly]
+ public sealed class FoldableSystem : EntitySystem
+ {
+ [Dependency] private SharedContainerSystem _container = default!;
+
+ private const string FoldKey = "FoldedState";
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnFoldableInit);
+ SubscribeLocalEvent(OnFoldableOpenAttempt);
+ SubscribeLocalEvent(OnPickedUpAttempt);
+ SubscribeLocalEvent(AddFoldVerb);
+ }
+
+ private void OnFoldableOpenAttempt(EntityUid uid, FoldableComponent component, StorageOpenAttemptEvent args)
+ {
+ if (component.IsFolded)
+ args.Cancel();
+ }
+
+ private void OnFoldableInit(EntityUid uid, FoldableComponent component, ComponentInit args)
+ {
+ SetFolded(component, component.IsFolded);
+ }
+
+ private bool TryToggleFold(FoldableComponent comp)
+ {
+ return TrySetFolded(comp, !comp.IsFolded);
+ }
+
+ ///
+ /// Try to fold/unfold
+ ///
+ ///
+ /// Folded state we want
+ /// True if successful
+ private bool TrySetFolded(FoldableComponent comp, bool state)
+ {
+ if (state == comp.IsFolded)
+ return false;
+
+ if (_container.IsEntityInContainer(comp.Owner))
+ return false;
+
+ // First we check if the foldable object has a strap component
+ if (EntityManager.TryGetComponent(comp.Owner, out StrapComponent? strap))
+ {
+ // If an entity is buckled to the object we can't pick it up or fold it
+ if (strap.BuckledEntities.Any())
+ return false;
+ }
+
+ SetFolded(comp, state);
+ return true;
+ }
+
+ ///
+ /// Set the folded state of the given
+ ///
+ ///
+ /// If true, the component will become folded, else unfolded
+ private void SetFolded(FoldableComponent component, bool folded)
+ {
+ component.IsFolded = folded;
+ component.CanBeFolded = !_container.IsEntityInContainer(component.Owner);
+
+ // You can't buckle an entity to a folded object
+ if (EntityManager.TryGetComponent(component.Owner, out StrapComponent? strap))
+ strap.Enabled = !component.IsFolded;
+
+ // Update visuals only if the value has changed
+ if (EntityManager.TryGetComponent(component.Owner, out AppearanceComponent? appearance))
+ appearance.SetData(FoldKey, folded);
+ }
+
+ #region Event handlers
+
+ ///
+ /// Prevents foldable objects to be picked up when unfolded
+ ///
+ ///
+ ///
+ ///
+ private void OnPickedUpAttempt(EntityUid uid, FoldableComponent component, AttemptItemPickupEvent args)
+ {
+ if (!component.IsFolded)
+ args.Cancel();
+ }
+
+ #endregion
+
+ #region Verb
+
+ private void AddFoldVerb(EntityUid uid, FoldableComponent component, GetAlternativeVerbsEvent args)
+ {
+ if (!args.CanAccess || !args.CanInteract)
+ return;
+
+ Verb verb = new()
+ {
+ Act = () => TryToggleFold(component),
+ Text = component.IsFolded ? Loc.GetString("unfold-verb") : Loc.GetString("fold-verb"),
+ IconTexture = "/Textures/Interface/VerbIcons/fold.svg.192dpi.png",
+
+ // If the object is unfolded and they click it, they want to fold it, if it's folded, they want to pick it up
+ Priority = component.IsFolded ? 0 : 2,
+ };
+
+ args.Verbs.Add(verb);
+ }
+
+ #endregion
+ }
+}
diff --git a/Content.Server/Storage/Components/EntityStorageComponent.cs b/Content.Server/Storage/Components/EntityStorageComponent.cs
index 7ff590084a..bada7c58ca 100644
--- a/Content.Server/Storage/Components/EntityStorageComponent.cs
+++ b/Content.Server/Storage/Components/EntityStorageComponent.cs
@@ -193,12 +193,18 @@ namespace Content.Server.Storage.Components
return false;
}
- return true;
+ var @event = new StorageOpenAttemptEvent();
+ IoCManager.Resolve().EventBus.RaiseLocalEvent(Owner, @event);
+
+ return !@event.Cancelled;
}
public virtual bool CanClose(EntityUid user, bool silent = false)
{
- return true;
+ var @event = new StorageCloseAttemptEvent();
+ IoCManager.Resolve().EventBus.RaiseLocalEvent(Owner, @event);
+
+ return !@event.Cancelled;
}
public void ToggleOpen(EntityUid user)
@@ -479,4 +485,14 @@ namespace Content.Server.Storage.Components
}
}
}
+
+ public sealed class StorageOpenAttemptEvent : CancellableEntityEventArgs
+ {
+
+ }
+
+ public sealed class StorageCloseAttemptEvent : CancellableEntityEventArgs
+ {
+
+ }
}
diff --git a/Content.Shared/Buckle/Components/SharedStrapComponent.cs b/Content.Shared/Buckle/Components/SharedStrapComponent.cs
index d396eacf32..38aeb1f341 100644
--- a/Content.Shared/Buckle/Components/SharedStrapComponent.cs
+++ b/Content.Shared/Buckle/Components/SharedStrapComponent.cs
@@ -57,11 +57,13 @@ namespace Content.Shared.Buckle.Components
}
[Serializable, NetSerializable]
- public enum StrapVisuals
+ public enum StrapVisuals : byte
{
- RotationAngle
+ RotationAngle,
+ BuckledState
}
+ // TODO : Convert this to an Entity Message. Careful, it will Break ShuttleControllerComponent (only place where it's used)
[Serializable, NetSerializable]
#pragma warning disable 618
public abstract class StrapChangeMessage : ComponentMessage
diff --git a/Content.Shared/Hands/Components/SharedHandsComponent.cs b/Content.Shared/Hands/Components/SharedHandsComponent.cs
index 67028d1353..398aec3203 100644
--- a/Content.Shared/Hands/Components/SharedHandsComponent.cs
+++ b/Content.Shared/Hands/Components/SharedHandsComponent.cs
@@ -615,11 +615,14 @@ namespace Content.Shared.Hands.Components
protected bool CanInsertEntityIntoHand(Hand hand, EntityUid entity)
{
var handContainer = hand.Container;
- if (handContainer == null)
- return false;
+ if (handContainer == null) return false;
- if (!handContainer.CanInsert(entity))
- return false;
+ if (!handContainer.CanInsert(entity)) return false;
+
+ var @event = new AttemptItemPickupEvent();
+ _entMan.EventBus.RaiseLocalEvent(entity, @event);
+
+ if (@event.Cancelled) return false;
return true;
}
diff --git a/Content.Shared/Item/PickupAttemptEvent.cs b/Content.Shared/Item/PickupAttemptEvent.cs
index 1d922a6d04..e52c0722a9 100644
--- a/Content.Shared/Item/PickupAttemptEvent.cs
+++ b/Content.Shared/Item/PickupAttemptEvent.cs
@@ -2,6 +2,9 @@
namespace Content.Shared.Item
{
+ ///
+ /// Raised on a *mob* when it tries to pickup something
+ ///
public class PickupAttemptEvent : CancellableEntityEventArgs
{
public PickupAttemptEvent(EntityUid uid)
@@ -11,4 +14,14 @@ namespace Content.Shared.Item
public EntityUid Uid { get; }
}
+
+ ///
+ /// Raised on the *item* when tried to be picked up
+ ///
+ ///
+ /// Doesn't just handle "items" but calling it "PickedUpAttempt" is too close to "Pickup" for the sleep deprived brain.
+ ///
+ public sealed class AttemptItemPickupEvent : CancellableEntityEventArgs
+ {
+ }
}
diff --git a/Content.Shared/Item/SharedItemComponent.cs b/Content.Shared/Item/SharedItemComponent.cs
index bd86f8e983..5c83bb95c7 100644
--- a/Content.Shared/Item/SharedItemComponent.cs
+++ b/Content.Shared/Item/SharedItemComponent.cs
@@ -9,7 +9,6 @@ using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
-using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
diff --git a/Resources/Locale/en-US/foldable/components/foldable-component.ftl b/Resources/Locale/en-US/foldable/components/foldable-component.ftl
new file mode 100644
index 0000000000..d3e4ecefb5
--- /dev/null
+++ b/Resources/Locale/en-US/foldable/components/foldable-component.ftl
@@ -0,0 +1,5 @@
+# Foldable
+
+foldable-deploy-fail = You can't deploy the {$object} here.
+fold-verb = Fold
+unfold-verb = Unfold
diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml
index f9d3b4f2b3..201928f6d2 100644
--- a/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml
+++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml
@@ -9,6 +9,7 @@
sprite: Objects/Specific/Medical/Morgue/bodybags.rsi
layers:
- state: bag
+ map: ["enum.FoldableVisualLayers.Base"]
- state: open_overlay
map: ["enum.StorageVisualLayers.Door"]
- state: label_overlay
@@ -27,13 +28,18 @@
!type:PhysShapeAabb
bounds: "-0.5,-0.45,0.5,0.1"
mass: 5
+ mask:
+ - Impassable
+ - SmallImpassable
- type: BodyBagEntityStorage
CanWeldShut: false
Capacity: 1
+ IsCollidableWhenOpen: true
closeSound:
path: /Audio/Misc/zip.ogg
openSound:
path: /Audio/Misc/zip.ogg
+ - type: Foldable
- type: PaperLabel
labelSlot:
insertVerbText: Attach Label
@@ -45,20 +51,20 @@
visuals:
- type: StorageVisualizer
state_open: open_overlay
- state_closed: bag
+ - type: FoldableVisualizer
+ key: bag
- type: BodyBagVisualizer
- type: Pullable
- type: entity
- id: BodyBag_Item
+ id: BodyBag_Folded
name: body bag
description: A plastic bag designed for the storage and transportation of cadavers.
- parent: BaseItem
+ parent: BodyBag_Container
+ suffix: Folded
components:
- - type: Sprite
- netsync: false
- sprite: Objects/Specific/Medical/Morgue/bodybags.rsi
- state: item
+ - type: Foldable
+ folded: true
# - type: BodyBagItem #TODO: we need some kind of generic placable, like thus:
# - type: Placeable
# prototype: someId
diff --git a/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml b/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml
index 1ea3b3c08e..10081edb36 100644
--- a/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml
+++ b/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml
@@ -24,6 +24,7 @@
noRot: true
- type: Strap
position: Stand
+ buckleOffset: "0,0.15"
- type: Pullable
- type: Damageable
damageContainer: Inorganic
diff --git a/Resources/Prototypes/Entities/Structures/Furniture/rollerbeds.yml b/Resources/Prototypes/Entities/Structures/Furniture/rollerbeds.yml
new file mode 100644
index 0000000000..524c812043
--- /dev/null
+++ b/Resources/Prototypes/Entities/Structures/Furniture/rollerbeds.yml
@@ -0,0 +1,94 @@
+- type: entity
+ id: RollerBed
+ parent: BaseItem
+ name: rollerbed
+ description: Used to carry patients around without damaging them.
+ components:
+ - type: Transform
+ noRot: true
+ - type: Item
+ size: 5
+ - type: Sprite
+ sprite: Structures/Furniture/rollerbeds.rsi
+ netsync: false
+ noRot: true
+ layers:
+ - state: rollerbed
+ map: ["enum.FoldableVisualLayers.Base"]
+ - type: MovedByPressure
+ - type: DamageOnHighSpeedImpact
+ soundHit: /Audio/Effects/bang.ogg
+ - type: InteractionOutline
+ - type: Physics
+ - type: Fixtures
+ fixtures:
+ - shape:
+ !type:PhysShapeCircle
+ radius: 0.35
+ mass: 30
+ mask:
+ - Impassable
+ - SmallImpassable
+ layer:
+ - Opaque
+ - type: Damageable
+ - type: Destructible
+ thresholds:
+ - trigger:
+ !type:DamageTrigger
+ damage: 50
+ behaviors:
+ - !type:DoActsBehavior
+ acts: ["Destruction"]
+ - !type:SpawnEntitiesBehavior
+ spawn:
+ SheetSteel1:
+ min: 1
+ max: 1
+ - type: Pullable
+ - type: Strap
+ position: Down
+ rotation: -90
+ buckleOffset: "0,0.15"
+ unbuckleOffset: "0,0.15"
+ - type: Foldable
+ - type: Appearance
+ visuals:
+ - type: FoldableVisualizer
+ key: rollerbed
+ - type: RollerbedVisualizer
+ key: rollerbed
+
+- type: entity
+ id: CheapRollerBed
+ name: rollerbed
+ parent: RollerBed
+ description: A run-down rollerbed. Used to carry patients around.
+ components:
+ - type: Sprite
+ layers:
+ - state: cheap_rollerbed
+ map: [ "enum.FoldableVisualLayers.Base" ]
+ - type: Appearance
+ visuals:
+ - type: FoldableVisualizer
+ key: cheap_rollerbed
+ - type: RollerbedVisualizer
+ key: cheap_rollerbed
+
+- type: entity
+ id: EmergencyRollerBed
+ name: rollerbed
+ parent: RollerBed
+ description: A robust looking rollerbed used for emergencies.
+ components:
+ - type: Sprite
+ layers:
+ - state: emergency_rollerbed
+ map: [ "enum.FoldableVisualLayers.Base" ]
+ - type: Appearance
+ visuals:
+ - type: FoldableVisualizer
+ key: emergency_rollerbed
+ - type: RollerbedVisualizer
+ key: emergency_rollerbed
diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml
index d8073e3104..e2b05bd992 100644
--- a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml
+++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml
@@ -8,6 +8,9 @@
visuals:
- type: StorageVisualizer
state: cabinet
+ state_open: cabinet_open
+ state_closed: cabinet_door
+
- type: AccessReader
access: [["Bar"]]
- type: EntityStorage
@@ -26,6 +29,8 @@
visuals:
- type: StorageVisualizer
state: qm
+ state_open: qm_open
+ state_closed: qm_door
- type: AccessReader
access: [["Cargo"]] # TODO access [["Quartermaster"]]
@@ -39,6 +44,8 @@
visuals:
- type: StorageVisualizer
state: cap
+ state_open: cap_open
+ state_closed: cap_door
- type: AccessReader
access: [["Captain"]]
@@ -51,6 +58,8 @@
visuals:
- type: StorageVisualizer
state: hop
+ state_open: hop_open
+ state_closed: hop_door
- type: AccessReader
access: [["HeadOfPersonnel"]]
@@ -64,6 +73,8 @@
visuals:
- type: StorageVisualizer
state: ce
+ state_open: ce_open
+ state_closed: ce_door
- type: AccessReader
access: [ [ "Engineering", "Command" ] ]
@@ -77,6 +88,7 @@
visuals:
- type: StorageVisualizer
state: eng
+ state_open: eng_open
state_closed: eng_elec_door
- type: AccessReader
access: [ [ "Engineering" ] ]
@@ -91,6 +103,7 @@
visuals:
- type: StorageVisualizer
state: eng
+ state_open: eng_open
state_closed: eng_weld_door
- type: AccessReader
access: [ [ "Engineering" ] ]
@@ -105,6 +118,8 @@
visuals:
- type: StorageVisualizer
state: atmos
+ state_open: atmos_open
+ state_closed: atmos_door
- type: AccessReader
access: [ [ "Engineering" ] ]
@@ -118,6 +133,8 @@
visuals:
- type: StorageVisualizer
state: eng_secure
+ state_open: eng_secure_open
+ state_closed: eng_secure_door
- type: AccessReader
access: [ [ "Engineering" ] ]
@@ -131,6 +148,8 @@
visuals:
- type: StorageVisualizer
state: freezer
+ state_open: freezer_open
+ state_closed: freezer_door
- type: AccessReader
access: [ [ "Service" ] ]
@@ -144,6 +163,8 @@
visuals:
- type: StorageVisualizer
state: hydro
+ state_open: hydro_open
+ state_closed: hydro_door
- type: AccessReader
access: [ [ "Service" ] ]
@@ -158,6 +179,8 @@
visuals:
- type: StorageVisualizer
state: med
+ state_open: med_open
+ state_closed: med_door
- type: AccessReader
access: [ [ "Medical" ] ]
@@ -171,6 +194,8 @@
visuals:
- type: StorageVisualizer
state: med_secure
+ state_open: med_secure_open
+ state_closed: med_secure_door
- type: AccessReader
access: [ [ "Medical" ] ]
@@ -184,6 +209,7 @@
visuals:
- type: StorageVisualizer
state: med
+ state_open: med_open
state_closed: chemical_door
- type: AccessReader
access: [ [ "Chemistry" ] ]
@@ -198,6 +224,8 @@
visuals:
- type: StorageVisualizer
state: cmo
+ state_open: cmo_open
+ state_closed: cmo_door
- type: AccessReader
access: [ [ "Medical", "Command" ] ]
@@ -211,6 +239,8 @@
visuals:
- type: StorageVisualizer
state: rd
+ state_open: rd_open
+ state_closed: rd_door
- type: AccessReader
access: [ [ "Research", "Command" ] ]
@@ -223,6 +253,8 @@
visuals:
- type: StorageVisualizer
state: science
+ state_open: science_open
+ state_closed: science_door
- type: AccessReader
access: [ [ "Research" ] ]
@@ -236,6 +268,8 @@
visuals:
- type: StorageVisualizer
state: hos
+ state_open: hos_open
+ state_closed: hos_door
- type: AccessReader
access: [["Security", "Command"]]
@@ -249,6 +283,8 @@
visuals:
- type: StorageVisualizer
state: warden
+ state_open: warden_open
+ state_closed: warden_door
- type: AccessReader
access: [["Security", "Armory"]] # TODO access [["Brig"]]
@@ -262,6 +298,8 @@
visuals:
- type: StorageVisualizer
state: sec
+ state_open: sec_open
+ state_closed: sec_door
- type: AccessReader
access: [["Security"]]
@@ -286,3 +324,5 @@
visuals:
- type: StorageVisualizer
state: syndicate
+ state_open: syndicate_open
+ state_closed: syndicate_door
diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml
index 4df4cb70cb..a68b891b68 100644
--- a/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml
+++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml
@@ -65,5 +65,6 @@
- type: Appearance
visuals:
- type: StorageVisualizer
+ state: generic
state_open: generic_open
state_closed: generic_door
diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/closets.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/closets.yml
index 41c491a005..0deada2727 100644
--- a/Resources/Prototypes/Entities/Structures/Storage/Closets/closets.yml
+++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/closets.yml
@@ -9,6 +9,7 @@
visuals:
- type: StorageVisualizer
state: eng
+ state_open: eng_open
state_closed: eng_tool_door
# Radiation suit closet
@@ -22,6 +23,7 @@
visuals:
- type: StorageVisualizer
state: eng
+ state_open: eng_open
state_closed: eng_rad_door
# Emergency closet
@@ -35,6 +37,8 @@
visuals:
- type: StorageVisualizer
state: emergency
+ state_open: emergency_open
+ state_closed: emergency_door
# Fire safety closet
- type: entity
@@ -47,6 +51,8 @@
visuals:
- type: StorageVisualizer
state: fire
+ state_open: fire_open
+ state_closed: fire_door
# EOD closet
- type: entity
@@ -59,6 +65,8 @@
visuals:
- type: StorageVisualizer
state: bomb
+ state_open: bomb_open
+ state_closed: bomb_door
# Biohazard
@@ -73,6 +81,8 @@
visuals:
- type: StorageVisualizer
state: bio
+ state_open: bio_sec_open
+ false: bio_sec_door
# Virology variant
- type: entity
@@ -83,6 +93,8 @@
visuals:
- type: StorageVisualizer
state: bio_viro
+ state_open: bio_viro_open
+ state_closed: bio_viro_door
# Security variant
- type: entity
@@ -93,6 +105,8 @@
visuals:
- type: StorageVisualizer
state: bio_sec
+ state_open: bio_sec_open
+ state_closed: bio_sec_door
# Janitor variant
- type: entity
@@ -103,11 +117,13 @@
visuals:
- type: StorageVisualizer
state: bio_jan
+ state_open: bio_jan_open
+ state_closed: bio_jan_door
# Maintenance closet
- type: entity
id: ClosetMaintenance
- name: maintenance closet
+ name: maintenance closet
parent: ClosetBase
description: It's a storage unit.
components:
diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/wardrobe.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/wardrobe.yml
index c288d5641a..4404185472 100644
--- a/Resources/Prototypes/Entities/Structures/Storage/Closets/wardrobe.yml
+++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/wardrobe.yml
@@ -17,6 +17,7 @@
visuals:
- type: StorageVisualizer
state: generic
+ state_open: generic_open
state_closed: blue_door
# Pink wardrobe
@@ -29,6 +30,7 @@
visuals:
- type: StorageVisualizer
state: generic
+ state_open: generic_open
state_closed: pink_door
# Black wardrobe
@@ -41,6 +43,7 @@
visuals:
- type: StorageVisualizer
state: generic
+ state_open: generic_open
state_closed: black_door
# Green wardrobe
@@ -53,6 +56,7 @@
visuals:
- type: StorageVisualizer
state: generic
+ state_open: generic_open
state_closed: green_door
# Prison wardrobe
@@ -65,6 +69,7 @@
visuals:
- type: StorageVisualizer
state: generic
+ state_open: generic_open
state_closed: orange_door
# Yellow wardrobe
@@ -77,6 +82,7 @@
visuals:
- type: StorageVisualizer
state: generic
+ state_open: generic_open
state_closed: yellow_door
# White wardrobe
@@ -89,6 +95,7 @@
visuals:
- type: StorageVisualizer
state: generic
+ state_open: generic_open
state_closed: white_door
# Grey wardrobe
@@ -101,6 +108,7 @@
visuals:
- type: StorageVisualizer
state: generic
+ state_open: generic_open
state_closed: grey_door
# Mixed wardrobe
@@ -113,6 +121,7 @@
visuals:
- type: StorageVisualizer
state: generic
+ state_open: generic_open
state_closed: mixed_door
# Jobs
@@ -126,6 +135,7 @@
visuals:
- type: StorageVisualizer
state: generic
+ state_open: generic_open
state_closed: red_door
- type: entity
@@ -137,6 +147,7 @@
visuals:
- type: StorageVisualizer
state: generic
+ state_open: generic_open
state_closed: atmos_wardrobe_door
- type: entity
diff --git a/Resources/Prototypes/Recipes/Lathes/medical.yml b/Resources/Prototypes/Recipes/Lathes/medical.yml
index 05d6931950..24d6431f47 100644
--- a/Resources/Prototypes/Recipes/Lathes/medical.yml
+++ b/Resources/Prototypes/Recipes/Lathes/medical.yml
@@ -73,9 +73,9 @@
Steel: 200
- type: latheRecipe
- id: BodyBag_Item
- icon: Objects/Specific/Medical/Morgue/bodybags.rsi/item.png
- result: BodyBag_Item
+ id: BodyBag_Folded
+ icon: Objects/Specific/Medical/Morgue/bodybags.rsi/bag_folded.png
+ result: BodyBag_Folded
completetime: 300
materials:
Plastic: 200
diff --git a/Resources/Textures/Interface/VerbIcons/fold.svg b/Resources/Textures/Interface/VerbIcons/fold.svg
new file mode 100644
index 0000000000..b931cd45ae
--- /dev/null
+++ b/Resources/Textures/Interface/VerbIcons/fold.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Resources/Textures/Interface/VerbIcons/fold.svg.192dpi.png b/Resources/Textures/Interface/VerbIcons/fold.svg.192dpi.png
new file mode 100644
index 0000000000..c0afa2c525
Binary files /dev/null and b/Resources/Textures/Interface/VerbIcons/fold.svg.192dpi.png differ
diff --git a/Resources/Textures/Interface/VerbIcons/fold.svg.192dpi.png.yml b/Resources/Textures/Interface/VerbIcons/fold.svg.192dpi.png.yml
new file mode 100644
index 0000000000..5c43e23305
--- /dev/null
+++ b/Resources/Textures/Interface/VerbIcons/fold.svg.192dpi.png.yml
@@ -0,0 +1,2 @@
+sample:
+ filter: true
diff --git a/Resources/Textures/Objects/Specific/Medical/Morgue/bodybags.rsi/item.png b/Resources/Textures/Objects/Specific/Medical/Morgue/bodybags.rsi/bag_folded.png
similarity index 100%
rename from Resources/Textures/Objects/Specific/Medical/Morgue/bodybags.rsi/item.png
rename to Resources/Textures/Objects/Specific/Medical/Morgue/bodybags.rsi/bag_folded.png
diff --git a/Resources/Textures/Objects/Specific/Medical/Morgue/bodybags.rsi/meta.json b/Resources/Textures/Objects/Specific/Medical/Morgue/bodybags.rsi/meta.json
index fab67ad463..b6feb49959 100644
--- a/Resources/Textures/Objects/Specific/Medical/Morgue/bodybags.rsi/meta.json
+++ b/Resources/Textures/Objects/Specific/Medical/Morgue/bodybags.rsi/meta.json
@@ -11,7 +11,7 @@
"name": "bag"
},
{
- "name": "item"
+ "name": "bag_folded"
},
{
"name": "label_overlay"
diff --git a/Resources/Textures/Structures/Furniture/rollerbeds.rsi/cheap_rollerbed.png b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/cheap_rollerbed.png
new file mode 100644
index 0000000000..c5dbd975b9
Binary files /dev/null and b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/cheap_rollerbed.png differ
diff --git a/Resources/Textures/Structures/Furniture/rollerbeds.rsi/cheap_rollerbed_buckled.png b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/cheap_rollerbed_buckled.png
new file mode 100644
index 0000000000..61330875a5
Binary files /dev/null and b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/cheap_rollerbed_buckled.png differ
diff --git a/Resources/Textures/Structures/Furniture/rollerbeds.rsi/cheap_rollerbed_folded.png b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/cheap_rollerbed_folded.png
new file mode 100644
index 0000000000..c0a7bec080
Binary files /dev/null and b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/cheap_rollerbed_folded.png differ
diff --git a/Resources/Textures/Structures/Furniture/rollerbeds.rsi/emergency_rollerbed.png b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/emergency_rollerbed.png
new file mode 100644
index 0000000000..809a1b0499
Binary files /dev/null and b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/emergency_rollerbed.png differ
diff --git a/Resources/Textures/Structures/Furniture/rollerbeds.rsi/emergency_rollerbed_buckled.png b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/emergency_rollerbed_buckled.png
new file mode 100644
index 0000000000..afad4c3fe0
Binary files /dev/null and b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/emergency_rollerbed_buckled.png differ
diff --git a/Resources/Textures/Structures/Furniture/rollerbeds.rsi/emergency_rollerbed_folded.png b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/emergency_rollerbed_folded.png
new file mode 100644
index 0000000000..f1e3a2b820
Binary files /dev/null and b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/emergency_rollerbed_folded.png differ
diff --git a/Resources/Textures/Structures/Furniture/rollerbeds.rsi/meta.json b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/meta.json
new file mode 100644
index 0000000000..669e74f8e2
--- /dev/null
+++ b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/meta.json
@@ -0,0 +1,38 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from cev-eris at https://github.com/discordia-space/CEV-Eris/commit/59fe5dd2841f47a8abce60eecb9fafad34282bd0, Baystation 12 and AuroraStation",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "rollerbed"
+ },
+ {
+ "name": "rollerbed_buckled"
+ },
+ {
+ "name": "rollerbed_folded"
+ },
+ {
+ "name": "cheap_rollerbed"
+ },
+ {
+ "name": "cheap_rollerbed_buckled"
+ },
+ {
+ "name": "cheap_rollerbed_folded"
+ },
+ {
+ "name": "emergency_rollerbed"
+ },
+ {
+ "name": "emergency_rollerbed_buckled"
+ },
+ {
+ "name": "emergency_rollerbed_folded"
+ }
+ ]
+}
diff --git a/Resources/Textures/Structures/Furniture/rollerbeds.rsi/rollerbed.png b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/rollerbed.png
new file mode 100644
index 0000000000..846341ca49
Binary files /dev/null and b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/rollerbed.png differ
diff --git a/Resources/Textures/Structures/Furniture/rollerbeds.rsi/rollerbed_buckled.png b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/rollerbed_buckled.png
new file mode 100644
index 0000000000..eaf05429e0
Binary files /dev/null and b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/rollerbed_buckled.png differ
diff --git a/Resources/Textures/Structures/Furniture/rollerbeds.rsi/rollerbed_folded.png b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/rollerbed_folded.png
new file mode 100644
index 0000000000..5783eaef98
Binary files /dev/null and b/Resources/Textures/Structures/Furniture/rollerbeds.rsi/rollerbed_folded.png differ