From 7de1ebbd773077b8cbb1c69fd638cfbea122bf15 Mon Sep 17 00:00:00 2001 From: Alex Evgrashin Date: Sun, 24 Oct 2021 06:34:00 +0300 Subject: [PATCH] Move RCD to ECS (#4742) * Moved ammo to ecs * Moved RCD to ecs * Replaced RCDDeconstructWhitelist with Tag * Updated new structures RCDDeconstructWhitelist * Queue delete --- Content.Client/Entry/IgnoredComponents.cs | 1 - .../RCD/Components/RCDAmmoComponent.cs | 41 +-- Content.Server/RCD/Components/RCDComponent.cs | 261 ++---------------- .../RCDDeconstructWhitelistComponent.cs | 10 - Content.Server/RCD/Systems/RCDAmmoSystem.cs | 49 ++++ Content.Server/RCD/Systems/RCDSystem.cs | 233 ++++++++++++++++ .../Entities/Effects/chemistry_effects.yml | 6 +- .../Entities/Structures/Walls/grille.yml | 8 +- .../Entities/Structures/Walls/low.yml | 4 +- .../Entities/Structures/Walls/walls.yml | 62 +++-- .../Entities/Structures/Windows/window.yml | 6 +- Resources/Prototypes/tags.yml | 3 + 12 files changed, 378 insertions(+), 306 deletions(-) delete mode 100644 Content.Server/RCD/Components/RCDDeconstructWhitelistComponent.cs create mode 100644 Content.Server/RCD/Systems/RCDAmmoSystem.cs create mode 100644 Content.Server/RCD/Systems/RCDSystem.cs diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index 0876a1f078..e01dba3311 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -119,7 +119,6 @@ namespace Content.Client.Entry "Telecrystal", "TrashSpawner", "RCD", - "RCDDeconstructWhitelist", "RCDAmmo", "CursedEntityStorage", "Listening", diff --git a/Content.Server/RCD/Components/RCDAmmoComponent.cs b/Content.Server/RCD/Components/RCDAmmoComponent.cs index 64a97f49a1..2c6aff8985 100644 --- a/Content.Server/RCD/Components/RCDAmmoComponent.cs +++ b/Content.Server/RCD/Components/RCDAmmoComponent.cs @@ -1,52 +1,15 @@ -using System; -using System.Threading.Tasks; -using Content.Server.Hands.Components; -using Content.Shared.Examine; -using Content.Shared.Interaction; -using Content.Shared.Popups; using Robust.Shared.GameObjects; -using Robust.Shared.Localization; using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.Utility; using Robust.Shared.ViewVariables; namespace Content.Server.RCD.Components { [RegisterComponent] - public class RCDAmmoComponent : Component, IAfterInteract, IExamine + public class RCDAmmoComponent : Component { public override string Name => "RCDAmmo"; //How much ammo we refill - [ViewVariables(VVAccess.ReadWrite)] [DataField("refillAmmo")] private int refillAmmo = 5; - - public void Examine(FormattedMessage message, bool inDetailsRange) - { - message.AddMarkup(Loc.GetString("rcd-ammo-component-on-examine-text",("ammo", refillAmmo))); - } - - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) - { - if (eventArgs.Target == null || - !eventArgs.Target.TryGetComponent(out RCDComponent? rcdComponent) || - !eventArgs.User.TryGetComponent(out IHandsComponent? hands)) - { - return false; - } - - if (rcdComponent.MaxAmmo - rcdComponent._ammo < refillAmmo) - { - rcdComponent.Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-ammo-component-after-interact-full-text")); - return true; - } - - rcdComponent._ammo = Math.Min(rcdComponent.MaxAmmo, rcdComponent._ammo + refillAmmo); - rcdComponent.Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-ammo-component-after-interact-refilled-text")); - - //Deleting a held item causes a lot of errors - hands.Drop(Owner, false); - Owner.Delete(); - return true; - } + [ViewVariables(VVAccess.ReadWrite)] [DataField("refillAmmo")] public int RefillAmmo = 5; } } diff --git a/Content.Server/RCD/Components/RCDComponent.cs b/Content.Server/RCD/Components/RCDComponent.cs index 63467e154a..61d4893181 100644 --- a/Content.Server/RCD/Components/RCDComponent.cs +++ b/Content.Server/RCD/Components/RCDComponent.cs @@ -1,252 +1,47 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Content.Server.DoAfter; -using Content.Shared.Coordinates; -using Content.Shared.Examine; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Helpers; -using Content.Shared.Maps; -using Content.Shared.Popups; using Content.Shared.Sound; -using Robust.Server.GameObjects; -using Robust.Shared.Audio; using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Localization; -using Robust.Shared.Map; -using Robust.Shared.Maths; -using Robust.Shared.Player; using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.Utility; using Robust.Shared.ViewVariables; namespace Content.Server.RCD.Components { - [RegisterComponent] - public class RCDComponent : Component, IAfterInteract, IUse, IExamine + public enum RcdMode { - [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly IServerEntityManager _serverEntityManager = default!; + Floors, + Walls, + Airlock, + Deconstruct + } + [RegisterComponent] + public class RCDComponent : Component + { 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)] [DataField("maxAmmo")] public int MaxAmmo = 5; - public int _ammo; //How much "ammo" we have left. You can refill this with RCD ammo. - [ViewVariables(VVAccess.ReadWrite)] [DataField("delay")] private float _delay = 2f; - private DoAfterSystem _doAfterSystem = default!; + + [ViewVariables(VVAccess.ReadOnly)] + [DataField("maxAmmo")] public int MaxAmmo = 5; + + [ViewVariables(VVAccess.ReadOnly)] + [DataField("startingAmmo")] public int StartingAmmo = 5; + + [ViewVariables(VVAccess.ReadWrite)] [DataField("delay")] + public float Delay = 2f; [DataField("swapModeSound")] - private SoundSpecifier _swapModeSound = new SoundPathSpecifier("/Audio/Items/genhit.ogg"); + public SoundSpecifier SwapModeSound = new SoundPathSpecifier("/Audio/Items/genhit.ogg"); [DataField("successSound")] - private SoundSpecifier _successSound = new SoundPathSpecifier("/Audio/Items/deconstruct.ogg"); + public SoundSpecifier SuccessSound = new SoundPathSpecifier("/Audio/Items/deconstruct.ogg"); - ///Enum to store the different mode states for clarity. - private enum RcdMode - { - Floors, - Walls, - Airlock, - Deconstruct - } + /// + /// What mode are we on? Can be floors, walls, deconstruct. + /// + public RcdMode Mode = RcdMode.Floors; - protected override void Initialize() - { - base.Initialize(); - _ammo = MaxAmmo; - _doAfterSystem = EntitySystem.Get(); - } + /// + /// How much "ammo" we have left. You can refill this with RCD ammo. + /// + public int CurrentAmmo; - /// - /// Method called when the RCD is clicked in-hand, this will cycle the RCD mode. - /// - bool IUse.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) - { - SoundSystem.Play(Filter.Pvs(Owner), _swapModeSound.GetSound(), Owner); - var 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. - Owner.PopupMessage(eventArgs.User, - Loc.GetString( - "rcd-component-change-mode", - ("mode", _mode.ToString()) - ) - ); //Prints an overhead message above the RCD - } - - public void Examine(FormattedMessage message, bool inDetailsRange) - { - if (inDetailsRange) - { - message.AddMarkup( - Loc.GetString( - "rcd-component-examine-detail-count", - ("mode", _mode), - ("ammoCount", _ammo) - ) - ); - } - } - - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) - { - // FIXME: Make this work properly. Right now it relies on the click location being on a grid, which is bad. - if (!eventArgs.ClickLocation.IsValid(Owner.EntityManager) || !eventArgs.ClickLocation.GetGridId(Owner.EntityManager).IsValid()) - return false; - - //No changing mode mid-RCD - var startingMode = _mode; - - var mapGrid = _mapManager.GetGrid(eventArgs.ClickLocation.GetGridId(Owner.EntityManager)); - var tile = mapGrid.GetTileRef(eventArgs.ClickLocation); - var snapPos = mapGrid.TileIndicesFor(eventArgs.ClickLocation); - - //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.WaitDoAfter(doAfterEventArgs); - if (result == DoAfterStatus.Cancelled) - { - return true; - } - - switch (_mode) - { - //Floor mode just needs the tile to be a space tile (subFloor) - case RcdMode.Floors: - mapGrid.SetTile(eventArgs.ClickLocation, new Robust.Shared.Map.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, Robust.Shared.Map.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("WallSolid", mapGrid.GridTileToLocal(snapPos)); - ent.Transform.LocalRotation = Angle.Zero; // Walls always need to point south. - 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 true; //I don't know why this would happen, but sure I guess. Get out of here invalid state! - } - - SoundSystem.Play(Filter.Pvs(Owner), _successSound.GetSound(), Owner); - _ammo--; - return true; - } - - private bool IsRCDStillValid(AfterInteractEventArgs eventArgs, IMapGrid mapGrid, TileRef tile, Vector2i snapPos, RcdMode startingMode) - { - //Less expensive checks first. Failing those ones, we need to check that the tile isn't obstructed. - if (_ammo <= 0) - { - Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-no-ammo-message")); - return false; - } - - if (_mode != startingMode) - { - return false; - } - - var coordinates = mapGrid.ToCoordinates(tile.GridIndices); - if (coordinates == EntityCoordinates.Invalid || !eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) - { - return false; - } - - switch (_mode) - { - //Floor mode just needs the tile to be a space tile (subFloor) - case RcdMode.Floors: - if (!tile.Tile.IsEmpty) - { - Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-cannot-build-floor-tile-not-empty-message")); - 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)) - { - Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-tile-obstructed-message")); - 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? deCon)) - { - Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-deconstruct-target-not-on-whitelist-message")); - 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) - { - Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-cannot-build-wall-tile-not-empty-message")); - return false; - } - - if (tile.IsBlockedTurf(true)) - { - Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-tile-obstructed-message")); - return false; - } - return true; - case RcdMode.Airlock: - if (tile.Tile.IsEmpty) - { - Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-cannot-build-airlock-tile-not-empty-message")); - return false; - } - if (tile.IsBlockedTurf(true)) - { - Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-tile-obstructed-message")); - 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/RCD/Components/RCDDeconstructWhitelistComponent.cs b/Content.Server/RCD/Components/RCDDeconstructWhitelistComponent.cs deleted file mode 100644 index 34e517f210..0000000000 --- a/Content.Server/RCD/Components/RCDDeconstructWhitelistComponent.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Robust.Shared.GameObjects; - -namespace Content.Server.RCD.Components -{ - [RegisterComponent] - public class RCDDeconstructWhitelist : Component - { - public override string Name => "RCDDeconstructWhitelist"; - } -} diff --git a/Content.Server/RCD/Systems/RCDAmmoSystem.cs b/Content.Server/RCD/Systems/RCDAmmoSystem.cs new file mode 100644 index 0000000000..897bde1805 --- /dev/null +++ b/Content.Server/RCD/Systems/RCDAmmoSystem.cs @@ -0,0 +1,49 @@ +using Content.Server.RCD.Components; +using Content.Shared.Examine; +using Content.Shared.Hands.Components; +using Content.Shared.Interaction; +using Content.Shared.Popups; +using Robust.Shared.GameObjects; +using Robust.Shared.Localization; +using System; + +namespace Content.Server.RCD.Systems +{ + public class RCDAmmoSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent(OnAfterInteract); + } + + private void OnExamine(EntityUid uid, RCDAmmoComponent component, ExaminedEvent args) + { + var examineMessage = Loc.GetString("rcd-ammo-component-on-examine-text", ("ammo", component.RefillAmmo)); + args.PushText(examineMessage); + } + + private void OnAfterInteract(EntityUid uid, RCDAmmoComponent component, AfterInteractEvent args) + { + if (args.Handled || !args.CanReach) + return; + + if (args.Target == null || !EntityManager.TryGetComponent(args.Target.Uid, out RCDComponent? rcdComponent)) + return; + + if (rcdComponent.MaxAmmo - rcdComponent.CurrentAmmo < component.RefillAmmo) + { + rcdComponent.Owner.PopupMessage(args.User, Loc.GetString("rcd-ammo-component-after-interact-full-text")); + args.Handled = true; + return; + } + + rcdComponent.CurrentAmmo = Math.Min(rcdComponent.MaxAmmo, rcdComponent.CurrentAmmo + component.RefillAmmo); + rcdComponent.Owner.PopupMessage(args.User, Loc.GetString("rcd-ammo-component-after-interact-refilled-text")); + EntityManager.QueueDeleteEntity(uid); + + args.Handled = true; + } + } +} diff --git a/Content.Server/RCD/Systems/RCDSystem.cs b/Content.Server/RCD/Systems/RCDSystem.cs new file mode 100644 index 0000000000..64c8ccde65 --- /dev/null +++ b/Content.Server/RCD/Systems/RCDSystem.cs @@ -0,0 +1,233 @@ +using Content.Server.DoAfter; +using Content.Server.RCD.Components; +using Content.Shared.Coordinates; +using Content.Shared.Examine; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Helpers; +using Content.Shared.Maps; +using Content.Shared.Popups; +using Content.Shared.Tag; +using Robust.Shared.Audio; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Player; +using System; +using System.Threading; + +namespace Content.Server.RCD.Systems +{ + public class RCDSystem : EntitySystem + { + [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + + [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; + + private readonly RcdMode[] _modes = (RcdMode[]) Enum.GetValues(typeof(RcdMode)); + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent(OnUseInHand); + SubscribeLocalEvent(OnAfterInteract); + } + + private void OnMapInit(EntityUid uid, RCDComponent component, MapInitEvent args) + { + component.CurrentAmmo = component.StartingAmmo; + } + + private void OnExamine(EntityUid uid, RCDComponent component, ExaminedEvent args) + { + var msg = Loc.GetString("rcd-component-examine-detail-count", + ("mode", component.Mode), ("ammoCount", component.CurrentAmmo)); + args.PushMarkup(msg); + } + + private void OnUseInHand(EntityUid uid, RCDComponent component, UseInHandEvent args) + { + if (args.Handled) + return; + + NextMode(uid, component, args.User); + args.Handled = true; + } + + private async void OnAfterInteract(EntityUid uid, RCDComponent rcd, AfterInteractEvent args) + { + if (args.Handled) + return; + + // FIXME: Make this work properly. Right now it relies on the click location being on a grid, which is bad. + if (!args.ClickLocation.IsValid(EntityManager)) + return; + + var gridID = args.ClickLocation.GetGridId(EntityManager); + if (!gridID.IsValid()) + return; + + var mapGrid = _mapManager.GetGrid(gridID); + var tile = mapGrid.GetTileRef(args.ClickLocation); + var snapPos = mapGrid.TileIndicesFor(args.ClickLocation); + + //No changing mode mid-RCD + var startingMode = rcd.Mode; + + //Using an RCD isn't instantaneous + var cancelToken = new CancellationTokenSource(); + var doAfterEventArgs = new DoAfterEventArgs(args.User, rcd.Delay, cancelToken.Token, args.Target) + { + BreakOnDamage = true, + BreakOnStun = true, + NeedHand = true, + ExtraCheck = () => IsRCDStillValid(rcd, args, mapGrid, tile, snapPos, startingMode) //All of the sanity checks are here + }; + + var result = await _doAfterSystem.WaitDoAfter(doAfterEventArgs); + if (result == DoAfterStatus.Cancelled) + { + args.Handled = true; + return; + } + + switch (rcd.Mode) + { + //Floor mode just needs the tile to be a space tile (subFloor) + case RcdMode.Floors: + mapGrid.SetTile(args.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 + { + args.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 = EntityManager.SpawnEntity("WallSolid", mapGrid.GridTileToLocal(snapPos)); + ent.Transform.LocalRotation = Angle.Zero; // Walls always need to point south. + break; + case RcdMode.Airlock: + var airlock = EntityManager.SpawnEntity("Airlock", mapGrid.GridTileToLocal(snapPos)); + airlock.Transform.LocalRotation = rcd.Owner.Transform.LocalRotation; //Now apply icon smoothing. + break; + default: + args.Handled = true; + return; //I don't know why this would happen, but sure I guess. Get out of here invalid state! + } + + SoundSystem.Play(Filter.Pvs(uid), rcd.SuccessSound.GetSound(), rcd.Owner); + rcd.CurrentAmmo--; + args.Handled = true; + } + + private bool IsRCDStillValid(RCDComponent rcd, AfterInteractEvent eventArgs, IMapGrid mapGrid, TileRef tile, Vector2i snapPos, RcdMode startingMode) + { + //Less expensive checks first. Failing those ones, we need to check that the tile isn't obstructed. + if (rcd.CurrentAmmo <= 0) + { + rcd.Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-no-ammo-message")); + return false; + } + + if (rcd.Mode != startingMode) + { + return false; + } + + var coordinates = mapGrid.ToCoordinates(tile.GridIndices); + if (coordinates == EntityCoordinates.Invalid || !eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) + { + return false; + } + + switch (rcd.Mode) + { + //Floor mode just needs the tile to be a space tile (subFloor) + case RcdMode.Floors: + if (!tile.Tile.IsEmpty) + { + rcd.Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-cannot-build-floor-tile-not-empty-message")); + 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)) + { + rcd.Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-tile-obstructed-message")); + return false; + } + //They tried to decon a non-turf but it's not in the whitelist + if (eventArgs.Target != null && !eventArgs.Target.HasTag("RCDDeconstructWhitelist")) + { + rcd.Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-deconstruct-target-not-on-whitelist-message")); + 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) + { + rcd.Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-cannot-build-wall-tile-not-empty-message")); + return false; + } + + if (tile.IsBlockedTurf(true)) + { + rcd.Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-tile-obstructed-message")); + return false; + } + return true; + case RcdMode.Airlock: + if (tile.Tile.IsEmpty) + { + rcd.Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-cannot-build-airlock-tile-not-empty-message")); + return false; + } + if (tile.IsBlockedTurf(true)) + { + rcd.Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-tile-obstructed-message")); + 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! + } + } + + private void NextMode(EntityUid uid, RCDComponent rcd, IEntity? user) + { + SoundSystem.Play(Filter.Pvs(uid), rcd.SwapModeSound.GetSound(), uid); + + var mode = (int) rcd.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 + rcd.Mode = (RcdMode) mode; //Finally, cast the newly acquired int mode to an RCDmode so we can use it. + + if (user != null) + { + var msg = Loc.GetString("rcd-component-change-mode", ("mode", rcd.Mode.ToString())); + rcd.Owner.PopupMessage(user, msg); //Prints an overhead message above the RCD + } + } + } +} diff --git a/Resources/Prototypes/Entities/Effects/chemistry_effects.yml b/Resources/Prototypes/Entities/Effects/chemistry_effects.yml index d213d07282..74399bb97b 100644 --- a/Resources/Prototypes/Entities/Effects/chemistry_effects.yml +++ b/Resources/Prototypes/Entities/Effects/chemistry_effects.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity id: Smoke name: smoke abstract: true @@ -102,7 +102,9 @@ snap: - Wall components: - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: Clickable - type: InteractionOutline - type: Sprite diff --git a/Resources/Prototypes/Entities/Structures/Walls/grille.yml b/Resources/Prototypes/Entities/Structures/Walls/grille.yml index fef2998108..db80c0114a 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/grille.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/grille.yml @@ -4,6 +4,9 @@ name: grille description: A flimsy framework of iron rods. components: + - type: Tag + tags: + - RCDDeconstructWhitelist - type: CanBuildWindowOnTop - type: Sprite netsync: false @@ -13,7 +16,6 @@ - type: Icon sprite: Structures/Walls/grille.rsi state: grille - - type: RCDDeconstructWhitelist - type: Construction graph: grille node: grille @@ -46,7 +48,9 @@ - type: Icon sprite: Structures/Walls/grille.rsi state: grille_broken - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: Construction graph: grille node: grilleBroken diff --git a/Resources/Prototypes/Entities/Structures/Walls/low.yml b/Resources/Prototypes/Entities/Structures/Walls/low.yml index 42c948af39..715a956b95 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/low.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/low.yml @@ -4,7 +4,9 @@ name: low wall description: Goes up to about your waist. components: - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: CanBuildWindowOnTop - type: Sprite netsync: false diff --git a/Resources/Prototypes/Entities/Structures/Walls/walls.yml b/Resources/Prototypes/Entities/Structures/Walls/walls.yml index 867f2b7f76..b0477f2fc2 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/walls.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/walls.yml @@ -3,7 +3,9 @@ id: WallBrick name: brick wall components: - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/brick.rsi - type: Icon @@ -30,7 +32,9 @@ id: WallClock name: clock wall components: - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/clock.rsi - type: Icon @@ -57,7 +61,9 @@ id: WallClown name: clown wall components: - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/clown.rsi - type: Icon @@ -85,7 +91,9 @@ id: WallCult name: cult wall components: - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/cult.rsi - type: Icon @@ -116,7 +124,7 @@ - type: Tag tags: - Debug - - type: RCDDeconstructWhitelist + - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/debug.rsi - type: Icon @@ -143,7 +151,9 @@ id: WallDiamond name: diamond wall components: - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/diamond.rsi - type: Icon @@ -170,7 +180,9 @@ id: WallGold name: gold wall components: - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/gold.rsi - type: Icon @@ -197,7 +209,9 @@ id: WallIce name: ice wall components: - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/ice.rsi - type: Icon @@ -224,7 +238,9 @@ id: WallMetal name: metal wall components: - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/metal.rsi - type: Icon @@ -251,7 +267,9 @@ id: WallPlasma name: plasma wall components: - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/plasma.rsi - type: Icon @@ -278,7 +296,9 @@ id: WallPlastic name: plastic wall components: - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/plastic.rsi - type: Icon @@ -337,7 +357,9 @@ id: WallRiveted name: riveted wall components: - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/riveted.rsi - type: Icon @@ -364,7 +386,9 @@ id: WallSandstone name: sandstone wall components: - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/sandstone.rsi - type: Icon @@ -391,7 +415,9 @@ id: WallSilver name: silver wall components: - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/silver.rsi - type: Icon @@ -418,7 +444,9 @@ id: WallSolid name: solid wall components: - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/solid.rsi - type: Construction @@ -476,7 +504,9 @@ id: WallWood name: wood wall components: - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: Sprite sprite: Structures/Walls/wood.rsi - type: Icon diff --git a/Resources/Prototypes/Entities/Structures/Windows/window.yml b/Resources/Prototypes/Entities/Structures/Windows/window.yml index c3076e3023..f88d99a1e0 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/window.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/window.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity id: Window parent: BaseStructure name: window @@ -8,7 +8,9 @@ snap: - Window components: - - type: RCDDeconstructWhitelist + - type: Tag + tags: + - RCDDeconstructWhitelist - type: Sprite netsync: false drawdepth: WallTops diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index c4f82499ac..fff9c9dff3 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -205,6 +205,9 @@ - type: Tag id: Write +- type: Tag + id: RCDDeconstructWhitelist + - type: Tag id: Wooden # just like our atmos