diff --git a/Content.Server/GameObjects/Components/Items/RCDComponent.cs b/Content.Server/GameObjects/Components/Items/RCDComponent.cs
new file mode 100644
index 0000000000..657addaba8
--- /dev/null
+++ b/Content.Server/GameObjects/Components/Items/RCDComponent.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Content.Server.GameObjects.EntitySystems;
+using Content.Server.GameObjects.EntitySystems.Click;
+using Content.Server.Interfaces;
+using Content.Server.Interfaces.GameObjects.Components.Interaction;
+using Content.Server.Utility;
+using Content.Shared.Construction;
+using Content.Shared.Maps;
+using Microsoft.EntityFrameworkCore.Internal;
+using Robust.Server.GameObjects.EntitySystems;
+using Robust.Server.Interfaces.GameObjects;
+using Robust.Shared.GameObjects;
+using Robust.Shared.GameObjects.Components.Transform;
+using Robust.Shared.Interfaces.GameObjects;
+using Robust.Shared.Interfaces.Map;
+using Robust.Shared.IoC;
+using Robust.Shared.Localization;
+using Robust.Shared.Map;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+using Robust.Shared.Utility;
+
+namespace Content.Server.GameObjects.Components.Items
+{
+ [RegisterComponent]
+ public class RCDComponent : Component, IAfterInteract, IUse, IExamine
+ {
+
+#pragma warning disable 649
+ [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager;
+ [Dependency] private readonly IEntitySystemManager _entitySystemManager;
+ [Dependency] private readonly IMapManager _mapManager;
+ [Dependency] private readonly IServerEntityManager _serverEntityManager;
+ [Dependency] private IServerNotifyManager _serverNotifyManager;
+#pragma warning restore 649
+ public override string Name => "RCD";
+ private string _outputTile = "floor_steel";
+ private RcdMode _mode = 0; //What mode are we on? Can be floors, walls, deconstruct.
+ private readonly RcdMode[] _modes = (RcdMode[]) Enum.GetValues(typeof(RcdMode));
+ private int _ammo = 5; //How much "ammo" we have left. You can refille this with RCD ammo.
+
+ ///Enum to store the different mode states for clarity.
+ private enum RcdMode
+ {
+ Floors,
+ Walls,
+ Deconstruct
+ }
+
+ public override void ExposeData(ObjectSerializer serializer)
+ {
+ base.ExposeData(serializer);
+ serializer.DataField(ref _outputTile, "output", "floor_steel");
+ }
+
+
+ ///
+ /// Method called when the RCD is clicked in-hand, this will swap the RCD's mode from "floors" to "walls".
+ ///
+
+ public bool UseEntity(UseEntityEventArgs eventArgs)
+ {
+ SwapMode(eventArgs);
+ return true;
+ }
+
+ ///
+ ///Method to allow the user to swap the mode of the RCD by clicking it in hand, the actual in hand clicking bit is done over on UseEntity()
+ ///@param UseEntityEventArgs = The entity which triggered this method call, used to know where to play the "click" sound.
+ ///
+
+ public void SwapMode(UseEntityEventArgs eventArgs)
+ {
+ _entitySystemManager.GetEntitySystem().PlayFromEntity("/Audio/items/genhit.ogg", Owner);
+ int mode = (int) this._mode; //Firstly, cast our RCDmode mode to an int (enums are backed by ints anyway by default)
+ mode = (++mode) % _modes.Length; //Then, do a rollover on the value so it doesnt hit an invalid state
+ this._mode = (RcdMode) mode; //Finally, cast the newly acquired int mode to an RCDmode so we can use it.
+ switch (this._mode)
+ {
+ case RcdMode.Floors:
+ _outputTile = "floor_steel";
+ break;
+ case RcdMode.Walls:
+ _outputTile = "base_wall";
+ break;
+ case RcdMode.Deconstruct:
+ _outputTile = "space";
+ break;
+ }
+ _serverNotifyManager.PopupMessage(Owner, eventArgs.User, $"The RCD is now set to {this._mode} mode."); //Prints an overhead message above the RCD
+ }
+
+ ///
+ ///Method called when the user examines this object, it'll simply add the mode that it's in to the object's description
+ ///@params message = The original message from examining, like ..() in BYOND's examine
+ ///
+
+ public void Examine(FormattedMessage message, bool inDetailsRange)
+ {
+ message.AddMarkup(Loc.GetString("It's currently on {0} mode, and holds {1} charges.",_mode.ToString(), this._ammo));
+ }
+
+ ///
+ /// Method to handle clicking on a tile to then appropriately RCD it. This can have several behaviours depending on mode.
+ /// @param eventAargs = An action event telling us what tile was clicked on. We use this to exrapolate where to place the new tile / remove the old one etc.
+ ///
+
+ public void AfterInteract(AfterInteractEventArgs eventArgs)
+ {
+ var mapGrid = _mapManager.GetGrid(eventArgs.ClickLocation.GridID);
+ var tile = mapGrid.GetTileRef(eventArgs.ClickLocation);
+ var coordinates = mapGrid.GridTileToLocal(tile.GridIndices);
+ //Less expensive checks first. Failing those ones, we need to check that the tile isn't obstructed.
+ if (_ammo <= 0 || coordinates == GridCoordinates.InvalidGrid || !InteractionChecks.InRangeUnobstructed(eventArgs))
+ {
+ return;
+ }
+
+ var targetTile = (ContentTileDefinition) _tileDefinitionManager[tile.Tile.TypeId];
+
+ var canPlaceTile = targetTile.IsSubFloor; //Boolean to check if we're able to build the desired tile. This defaults to checking for subfloors, but is overridden by "deconstruct" which sets it to the inverse.
+
+ switch (this._mode)
+ {
+ //Floor mode just needs the tile to be a space tile (subFloor)
+ case RcdMode.Floors:
+ break;
+ //We don't want to place a space tile on something that's already a space tile. Let's do the inverse of the last check.
+ case RcdMode.Deconstruct:
+ canPlaceTile = !targetTile.IsSubFloor;
+ break;
+ //Walls are a special behaviour, and require us to build a new object with a transform rather than setting a grid tile, thus we early return to avoid the tile set code.
+ case RcdMode.Walls:
+ var snapPos = mapGrid.SnapGridCellFor(eventArgs.ClickLocation, SnapGridOffset.Center);
+ var ent = _serverEntityManager.SpawnEntity("solid_wall", mapGrid.GridTileToLocal(snapPos));
+ ent.Transform.LocalRotation = Owner.Transform.LocalRotation; //Now apply icon smoothing.
+ _entitySystemManager.GetEntitySystem().PlayFromEntity("/Audio/items/deconstruct.ogg", Owner);
+ _ammo--;
+ return; //Alright we're done here
+ default:
+ return; //I don't know why this would happen, but sure I guess. Get out of here invalid state!
+ }
+
+ ITileDefinition desiredTile = null;
+ desiredTile = _tileDefinitionManager[_outputTile];
+ if (canPlaceTile) //If desiredTile is null by this point, something has gone horribly wrong and you need to fix it.
+ {
+ mapGrid.SetTile(eventArgs.ClickLocation, new Tile(desiredTile.TileId));
+ _entitySystemManager.GetEntitySystem().PlayFromEntity("/Audio/items/deconstruct.ogg", Owner);
+ _ammo--;
+ }
+ }
+ }
+}
diff --git a/Resources/Prototypes/Entities/Items/tools.yml b/Resources/Prototypes/Entities/Items/tools.yml
index b7be1f5173..01cad99583 100644
--- a/Resources/Prototypes/Entities/Items/tools.yml
+++ b/Resources/Prototypes/Entities/Items/tools.yml
@@ -244,3 +244,20 @@
state: drill_bolt
useSound: /Audio/Items/drill_use.ogg
changeSound: /Audio/Items/change_drill.ogg
+- type: entity
+ name: RCD
+ parent: BaseItem
+ id: RCD
+ description: An advanced construction device which can place floors / walls down quickly.
+ components:
+ - type: RCD
+ - type: UseDelay
+ delay: 1.0
+ - type: Sprite
+ sprite: Objects/Tools/rcd.rsi
+ state: rcd
+ - type: Icon
+ sprite: Objects/Tools/rcd.rsi
+ state: rcd
+ - type: Item
+ sprite: Objects/Tools/rcd.rsi
diff --git a/Resources/Textures/Objects/Tools/rcd.rsi/inhand-left.png b/Resources/Textures/Objects/Tools/rcd.rsi/inhand-left.png
new file mode 100644
index 0000000000..1873ca3ccb
Binary files /dev/null and b/Resources/Textures/Objects/Tools/rcd.rsi/inhand-left.png differ
diff --git a/Resources/Textures/Objects/Tools/rcd.rsi/inhand-right.png b/Resources/Textures/Objects/Tools/rcd.rsi/inhand-right.png
new file mode 100644
index 0000000000..c0e0a8eb9e
Binary files /dev/null and b/Resources/Textures/Objects/Tools/rcd.rsi/inhand-right.png differ
diff --git a/Resources/Textures/Objects/Tools/rcd.rsi/meta.json b/Resources/Textures/Objects/Tools/rcd.rsi/meta.json
new file mode 100644
index 0000000000..1cfaa7b48f
--- /dev/null
+++ b/Resources/Textures/Objects/Tools/rcd.rsi/meta.json
@@ -0,0 +1 @@
+{"version": 1, "size": {"x": 32, "y": 32}, "states": [{"name": "inhand-left", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "inhand-right", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "rcd", "directions": 1, "delays": [[1.0]]}]}
\ No newline at end of file
diff --git a/Resources/Textures/Objects/Tools/rcd.rsi/rcd.png b/Resources/Textures/Objects/Tools/rcd.rsi/rcd.png
new file mode 100644
index 0000000000..fe1b33a4cf
Binary files /dev/null and b/Resources/Textures/Objects/Tools/rcd.rsi/rcd.png differ
diff --git a/RobustToolbox b/RobustToolbox
index 9e7b50a12e..eed8a679ad 160000
--- a/RobustToolbox
+++ b/RobustToolbox
@@ -1 +1 @@
-Subproject commit 9e7b50a12ed1fdf443a01072f303b15e703e8413
+Subproject commit eed8a679ad0c6eadda61d67843812b63f3023b23