From 83a7dfebef99b57b400836a8ac1218ff81676e64 Mon Sep 17 00:00:00 2001 From: ike709 Date: Thu, 13 Aug 2020 12:39:23 -0500 Subject: [PATCH] Improves the RCD (#1609) * Improves the RCD * oops * Unnecessary * Merge 2 checks * RCD whitelist and reorganization * Makes the RCD great again * Ignored components * loicense * Fix missing using --- Content.Client/IgnoredComponents.cs | 2 + .../Components/Items/RCD/RCDAmmoComponent.cs | 61 +++++ .../Components/Items/RCD/RCDComponent.cs | 241 ++++++++++++++++++ .../RCD/RCDDeconstructWhitelistComponent.cs | 10 + .../Components/Items/RCDComponent.cs | 150 ----------- Content.Shared/Maps/TurfHelpers.cs | 6 +- .../Constructible/Doors/airlock_base.yml | 1 + .../Entities/Constructible/Walls/low_wall.yml | 1 + .../Entities/Constructible/Walls/walls.yml | 1 + .../Entities/Constructible/Walls/windows.yml | 1 + .../Entities/Objects/Tools/tools.yml | 16 +- .../Textures/Objects/Tools/rcd.rsi/meta.json | 65 ++++- .../Textures/Objects/Tools/rcd.rsi/rcd.png | Bin 317 -> 638 bytes .../Objects/Tools/rcd.rsi/rcd_ammo.png | Bin 0 -> 331 bytes 14 files changed, 400 insertions(+), 155 deletions(-) create mode 100644 Content.Server/GameObjects/Components/Items/RCD/RCDAmmoComponent.cs create mode 100644 Content.Server/GameObjects/Components/Items/RCD/RCDComponent.cs create mode 100644 Content.Server/GameObjects/Components/Items/RCD/RCDDeconstructWhitelistComponent.cs delete mode 100644 Content.Server/GameObjects/Components/Items/RCDComponent.cs create mode 100644 Resources/Textures/Objects/Tools/rcd.rsi/rcd_ammo.png diff --git a/Content.Client/IgnoredComponents.cs b/Content.Client/IgnoredComponents.cs index c0915caf7b..5169e4a189 100644 --- a/Content.Client/IgnoredComponents.cs +++ b/Content.Client/IgnoredComponents.cs @@ -136,6 +136,8 @@ "TrashSpawner", "Pill", "RCD", + "RCDDeconstructWhitelist", + "RCDAmmo", "Pullable", "CursedEntityStorage", "Listening", diff --git a/Content.Server/GameObjects/Components/Items/RCD/RCDAmmoComponent.cs b/Content.Server/GameObjects/Components/Items/RCD/RCDAmmoComponent.cs new file mode 100644 index 0000000000..16d2e5557e --- /dev/null +++ b/Content.Server/GameObjects/Components/Items/RCD/RCDAmmoComponent.cs @@ -0,0 +1,61 @@ +using System; +using Content.Server.Interfaces; +using Content.Server.Interfaces.GameObjects.Components.Items; +using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Interfaces.GameObjects.Components; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; +using Robust.Shared.ViewVariables; + +namespace Content.Server.GameObjects.Components.Items.RCD +{ + [RegisterComponent] + public class RCDAmmoComponent : Component, IAfterInteract, IExamine + { + +#pragma warning disable 649 + [Dependency] private IServerNotifyManager _serverNotifyManager; +#pragma warning restore 649 + public override string Name => "RCDAmmo"; + + //How much ammo we refill + [ViewVariables(VVAccess.ReadWrite)] private int refillAmmo = 5; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref refillAmmo, "refillAmmo", 5); + } + + public void Examine(FormattedMessage message, bool inDetailsRange) + { + message.AddMarkup(Loc.GetString("It holds {0} charges.", refillAmmo)); + } + + void IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) + { + if (eventArgs.Target == null || !eventArgs.Target.TryGetComponent(out RCDComponent rcdComponent) || !eventArgs.User.TryGetComponent(out IHandsComponent hands)) + { + return; + } + + if (rcdComponent.maxAmmo - rcdComponent._ammo < refillAmmo) + { + _serverNotifyManager.PopupMessage(rcdComponent.Owner, eventArgs.User, "The RCD is full!"); + return; + } + + rcdComponent._ammo = Math.Min(rcdComponent.maxAmmo, rcdComponent._ammo + refillAmmo); + _serverNotifyManager.PopupMessage(rcdComponent.Owner, eventArgs.User, "You refill the RCD."); + + //Deleting a held item causes a lot of errors + hands.Drop(Owner, false); + Owner.Delete(); + + } + } +} diff --git a/Content.Server/GameObjects/Components/Items/RCD/RCDComponent.cs b/Content.Server/GameObjects/Components/Items/RCD/RCDComponent.cs new file mode 100644 index 0000000000..d479c31d02 --- /dev/null +++ b/Content.Server/GameObjects/Components/Items/RCD/RCDComponent.cs @@ -0,0 +1,241 @@ +using System; +using System.Threading; +using Content.Server.GameObjects.EntitySystems.DoAfter; +using Content.Server.Interfaces; +using Content.Server.Utility; +using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Maps; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Server.Interfaces.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components.Transform; +using Robust.Shared.GameObjects.Systems; +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.Serialization; +using Robust.Shared.Utility; +using Robust.Shared.ViewVariables; + +namespace Content.Server.GameObjects.Components.Items.RCD +{ + [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 RcdMode _mode = 0; //What mode are we on? Can be floors, walls, deconstruct. + private readonly RcdMode[] _modes = (RcdMode[]) Enum.GetValues(typeof(RcdMode)); + [ViewVariables(VVAccess.ReadWrite)] public int maxAmmo; + public int _ammo; //How much "ammo" we have left. You can refill this with RCD ammo. + [ViewVariables(VVAccess.ReadWrite)] private float _delay; + private DoAfterSystem doAfterSystem; + + + ///Enum to store the different mode states for clarity. + private enum RcdMode + { + Floors, + Walls, + Airlock, + Deconstruct + } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref maxAmmo, "maxAmmo", 5); + serializer.DataField(ref _delay, "delay", 2f); + } + + public override void Initialize() + { + base.Initialize(); + _ammo = maxAmmo; + doAfterSystem = EntitySystem.Get(); + } + + /// + /// Method called when the RCD is clicked in-hand, this will cycle the RCD mode. + /// + + 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) _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 + _mode = (RcdMode) mode; //Finally, cast the newly acquired int mode to an RCDmode so we can use it. + _serverNotifyManager.PopupMessage(Owner, eventArgs.User, $"The RCD is now set to {this._mode} mode."); //Prints an overhead message above the RCD + } + + 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)); + } + + public async void AfterInteract(AfterInteractEventArgs eventArgs) + { + //No changing mode mid-RCD + var startingMode = _mode; + + var mapGrid = _mapManager.GetGrid(eventArgs.ClickLocation.GridID); + var tile = mapGrid.GetTileRef(eventArgs.ClickLocation); + var snapPos = mapGrid.SnapGridCellFor(eventArgs.ClickLocation, SnapGridOffset.Center); + + //Using an RCD isn't instantaneous + var cancelToken = new CancellationTokenSource(); + var doAfterEventArgs = new DoAfterEventArgs(eventArgs.User, _delay, cancelToken.Token, eventArgs.Target) + { + BreakOnDamage = true, + BreakOnStun = true, + NeedHand = true, + ExtraCheck = () => IsRCDStillValid(eventArgs, mapGrid, tile, snapPos, startingMode) //All of the sanity checks are here + }; + + var result = await doAfterSystem.DoAfter(doAfterEventArgs); + if (result == DoAfterStatus.Cancelled) + { + return; + } + + switch (_mode) + { + //Floor mode just needs the tile to be a space tile (subFloor) + case RcdMode.Floors: + mapGrid.SetTile(eventArgs.ClickLocation, new Tile(_tileDefinitionManager["floor_steel"].TileId)); + 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: + if (!tile.IsBlockedTurf(true)) //Delete the turf + { + mapGrid.SetTile(snapPos, Tile.Empty); + } + else //Delete what the user targeted + { + eventArgs.Target.Delete(); + } + 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 ent = _serverEntityManager.SpawnEntity("solid_wall", mapGrid.GridTileToLocal(snapPos)); + ent.Transform.LocalRotation = Owner.Transform.LocalRotation; //Now apply icon smoothing. + break; + case RcdMode.Airlock: + var airlock = _serverEntityManager.SpawnEntity("Airlock", mapGrid.GridTileToLocal(snapPos)); + airlock.Transform.LocalRotation = Owner.Transform.LocalRotation; //Now apply icon smoothing. + break; + default: + return; //I don't know why this would happen, but sure I guess. Get out of here invalid state! + } + + _entitySystemManager.GetEntitySystem().PlayFromEntity("/Audio/Items/deconstruct.ogg", Owner); + _ammo--; + + } + + private bool IsRCDStillValid(AfterInteractEventArgs eventArgs, IMapGrid mapGrid, TileRef tile, MapIndices snapPos, RcdMode startingMode) + { + //Less expensive checks first. Failing those ones, we need to check that the tile isn't obstructed. + if (_ammo <= 0) + { + _serverNotifyManager.PopupMessage(Owner, eventArgs.User, $"The RCD is out of ammo!"); + return false; + } + + if (_mode != startingMode) + { + return false; + } + + var coordinates = mapGrid.GridTileToLocal(tile.GridIndices); + if (coordinates == GridCoordinates.InvalidGrid || !InteractionChecks.InRangeUnobstructed(eventArgs)) + { + return false; + } + + switch (_mode) + { + //Floor mode just needs the tile to be a space tile (subFloor) + case RcdMode.Floors: + if (!tile.Tile.IsEmpty) + { + _serverNotifyManager.PopupMessage(Owner, eventArgs.User, $"You can only build a floor on space!"); + return false; + } + + return true; + //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: + if (tile.Tile.IsEmpty) + { + return false; + } + + //They tried to decon a turf but the turf is blocked + if (eventArgs.Target == null && tile.IsBlockedTurf(true)) + { + _serverNotifyManager.PopupMessage(Owner, eventArgs.User, $"That tile is obstructed!"); + return false; + } + //They tried to decon a non-turf but it's not in the whitelist + if (eventArgs.Target != null && !eventArgs.Target.TryGetComponent(out RCDDeconstructWhitelist rcd_decon)) + { + _serverNotifyManager.PopupMessage(Owner, eventArgs.User, $"You can't deconstruct that!"); + return false; + } + + return true; + //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: + if (tile.Tile.IsEmpty) + { + _serverNotifyManager.PopupMessage(Owner, eventArgs.User, $"Cannot build a wall on space!"); + return false; + } + + if (tile.IsBlockedTurf(true)) + { + _serverNotifyManager.PopupMessage(Owner, eventArgs.User, $"That tile is obstructed!"); + return false; + } + return true; + case RcdMode.Airlock: + if (tile.Tile.IsEmpty) + { + _serverNotifyManager.PopupMessage(Owner, eventArgs.User, $"Cannot build an airlock on space!"); + return false; + } + if (tile.IsBlockedTurf(true)) + { + _serverNotifyManager.PopupMessage(Owner, eventArgs.User, $"That tile is obstructed!"); + return false; + } + return true; + default: + return false; //I don't know why this would happen, but sure I guess. Get out of here invalid state! + } + } + } +} diff --git a/Content.Server/GameObjects/Components/Items/RCD/RCDDeconstructWhitelistComponent.cs b/Content.Server/GameObjects/Components/Items/RCD/RCDDeconstructWhitelistComponent.cs new file mode 100644 index 0000000000..7fb18a45cd --- /dev/null +++ b/Content.Server/GameObjects/Components/Items/RCD/RCDDeconstructWhitelistComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.GameObjects; + +namespace Content.Server.GameObjects.Components.Items.RCD +{ + [RegisterComponent] + public class RCDDeconstructWhitelist : Component + { + public override string Name => "RCDDeconstructWhitelist"; + } +} diff --git a/Content.Server/GameObjects/Components/Items/RCDComponent.cs b/Content.Server/GameObjects/Components/Items/RCDComponent.cs deleted file mode 100644 index 382360ccd4..0000000000 --- a/Content.Server/GameObjects/Components/Items/RCDComponent.cs +++ /dev/null @@ -1,150 +0,0 @@ -using System; -using Content.Server.Interfaces; -using Content.Server.Utility; -using Content.Shared.GameObjects.EntitySystems; -using Content.Shared.Interfaces.GameObjects.Components; -using Content.Shared.Maps; -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.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/Content.Shared/Maps/TurfHelpers.cs b/Content.Shared/Maps/TurfHelpers.cs index aa60e7168c..b02472cfa5 100644 --- a/Content.Shared/Maps/TurfHelpers.cs +++ b/Content.Shared/Maps/TurfHelpers.cs @@ -83,13 +83,13 @@ namespace Content.Shared.Maps foreach (var body in query) { if (body.CanCollide && body.Hard && (body.CollisionLayer & (int) CollisionGroup.Impassable) != 0) - return false; + return true; if (filterMobs && (body.CollisionLayer & (int) CollisionGroup.MobMask) != 0) - return false; + return true; } - return true; + return false; } /// diff --git a/Resources/Prototypes/Entities/Constructible/Doors/airlock_base.yml b/Resources/Prototypes/Entities/Constructible/Doors/airlock_base.yml index 6e8200cf27..ad88ad239f 100644 --- a/Resources/Prototypes/Entities/Constructible/Doors/airlock_base.yml +++ b/Resources/Prototypes/Entities/Constructible/Doors/airlock_base.yml @@ -4,6 +4,7 @@ description: It opens, it closes, and maybe crushes you. components: - type: Clickable + - type: RCDDeconstructWhitelist - type: InteractionOutline - type: Sprite netsync: false diff --git a/Resources/Prototypes/Entities/Constructible/Walls/low_wall.yml b/Resources/Prototypes/Entities/Constructible/Walls/low_wall.yml index 36d945b8de..c381fae699 100644 --- a/Resources/Prototypes/Entities/Constructible/Walls/low_wall.yml +++ b/Resources/Prototypes/Entities/Constructible/Walls/low_wall.yml @@ -8,6 +8,7 @@ - Wall components: - type: Clickable + - type: RCDDeconstructWhitelist - type: InteractionOutline - type: Sprite netsync: false diff --git a/Resources/Prototypes/Entities/Constructible/Walls/walls.yml b/Resources/Prototypes/Entities/Constructible/Walls/walls.yml index 2ba3947118..7f82a01ce2 100644 --- a/Resources/Prototypes/Entities/Constructible/Walls/walls.yml +++ b/Resources/Prototypes/Entities/Constructible/Walls/walls.yml @@ -8,6 +8,7 @@ snap: - Wall components: + - type: RCDDeconstructWhitelist - type: Clickable - type: InteractionOutline - type: Sprite diff --git a/Resources/Prototypes/Entities/Constructible/Walls/windows.yml b/Resources/Prototypes/Entities/Constructible/Walls/windows.yml index dbb604d2ed..280139d3e6 100644 --- a/Resources/Prototypes/Entities/Constructible/Walls/windows.yml +++ b/Resources/Prototypes/Entities/Constructible/Walls/windows.yml @@ -8,6 +8,7 @@ - Window components: - type: Clickable + - type: RCDDeconstructWhitelist - type: InteractionOutline - type: Sprite color: "#DDDDDD" diff --git a/Resources/Prototypes/Entities/Objects/Tools/tools.yml b/Resources/Prototypes/Entities/Objects/Tools/tools.yml index ef5191d36c..51df8c6cac 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/tools.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/tools.yml @@ -249,7 +249,7 @@ name: RCD parent: BaseItem id: RCD - description: An advanced construction device which can place floors / walls down quickly. + description: An advanced construction device which can place/remove walls, floors, and airlocks quickly. components: - type: RCD - type: UseDelay @@ -262,3 +262,17 @@ state: rcd - type: Item sprite: Objects/Tools/rcd.rsi + +- type: entity + name: RCD Ammo + parent: BaseItem + id: RCDAmmo + description: Ammo cartridge for an RCD. + components: + - type: RCDAmmo + - type: Sprite + sprite: Objects/Tools/rcd.rsi + state: rcd_ammo + - type: Icon + sprite: Objects/Tools/rcd.rsi + state: rcd_ammo diff --git a/Resources/Textures/Objects/Tools/rcd.rsi/meta.json b/Resources/Textures/Objects/Tools/rcd.rsi/meta.json index 1cfaa7b48f..ace9e5c739 100644 --- a/Resources/Textures/Objects/Tools/rcd.rsi/meta.json +++ b/Resources/Textures/Objects/Tools/rcd.rsi/meta.json @@ -1 +1,64 @@ -{"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 +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC BY-SA 3.0", + "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 + ] + ] + }, + { + "name": "rcd_ammo", + "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 index fe1b33a4cf3f221db7329025a82e60bb7eb24e95..cb62979010f4ddc12341edfdbc08a6cd69426857 100644 GIT binary patch delta 624 zcmV-$0+0Q@0{#S$8Gi-<0047(dh`GQ0!2wgK~#90?Upfb(@+$JKO+_t)hLFcX%Lv9 zEgcMMyOLt5f{9ElB~uo*`VX)~{06fiB$P1=EX`VQ8&(&AhsZ$!4^&yeksL*pSj_Nn z64!AOP#{Z=6kX>&C-2L-$Bre-vY4kX%yst#zye?a09;I42!BrWFgiX{ssLQ_%g9@n z^j`o@KEdN%SlfVZ?mhrZZ8$jA!&yS40syqWiP}5+0zk%t;*NljrbJlU91oiSFbpFE zc(gtKi`T9ScP6Rc(8F;MBOTB*4f}@*?g0RKRrGsmgI2Tf+l7HckyfvXdi+t;ojdTb zFdiat6_}dyuP18_S6-7}{6eYHv@d~l`^?!PicDsn@dFW+Jx?GKqrS-GK z(^uM`Ip8=>ym3>Me(2uj0q}hv-}iyR*lqw7TO*szBFi$eoVqsv(zH)RA_qzB?LzGk z^m5!{!!W`DgpA`jiEP1y8_}@NZxE?g)x*j@JbSK(Y3Gd7O#Y1AX0m6Rrqt6I5rAH{q*_%EH&c1Z!SV{+dXNn1OacBzbAKdT04xCJ4)6=+o!KM-?O&Y$0000< KMNUMnLSTZ!Ngq-G delta 301 zcmV+|0n+~d1ib=~8Gi!+002a!ipBr{07y_wR7JqRz_N}2si2^MU;x>p0RPMY^5x~T zhXBsQ!?TY7EiEltT3Ss_P1vIVnsfj^KR=n70A^+Yj{pF!002HdK1@tZn0f%Qg#dNB zGb{iA00DGTPE!Ct=GbNc005&&L_t(IjqT3e4uUWY1>lypY=4#&f5G>EQ@bI?5kRkv z=W0p6o)zGqw;*EE;0_>`wv`=0fn#LB!z>O$rG%X zr6^~_*y7QXh1EcHI>W)~oV+6hs^(qFd&~|}Qn>g(oh}MAuQB=q;HtNw0?#8#jLOQY zn3&K9@O{OwT_!PQ9M=KR-AZH&z}&Dk|HV82g+d3y+h<9L00000NkvXXu0mjfUowHD diff --git a/Resources/Textures/Objects/Tools/rcd.rsi/rcd_ammo.png b/Resources/Textures/Objects/Tools/rcd.rsi/rcd_ammo.png new file mode 100644 index 0000000000000000000000000000000000000000..5422d5b97644fca92247567d8658a5b0bddd26c9 GIT binary patch literal 331 zcmV-R0kr;!P)pcY)L?EG#Z8TO3&1%A`n6 z5ww_#i3IkCg=LDFkN;n0BZRB!=DOnpKmZ5;0pJG!c9_elyH>YdhbLJ|JTd~%%Pb5L z_qvGm-9&fnAQzq&Vh6dfT?yNj9MzQlBJF3?+U<&x$B%aa-tHc-vk3rN7t|5N#?b3q zm`!L}C@PXHWwUnLcKSzH&&T^gt|)Ou3BYFUsOMuffI5Qv(Ua`FKREF{ysgzQ)8CB% zAjwjiWkuutQ7uI9ClTztKN~@_thDXAt>SYLbdfp&I4q;s7~snl&@Z9R0GHJN{f+}b d00;n=(kJE;p|w>1)~x^l002ovPDHLkV1oarkK_OV literal 0 HcmV?d00001