From bba096f1a4a31575316dbe1aaefbc3acb9e27e07 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Wed, 23 Mar 2022 13:02:49 +1100 Subject: [PATCH] RCD logs (#7208) Co-authored-by: metalgearsloth --- Content.Server/RCD/Components/RCDComponent.cs | 14 ++-- Content.Server/RCD/Systems/RCDSystem.cs | 81 +++++++++++-------- Content.Shared.Database/LogType.cs | 2 + 3 files changed, 59 insertions(+), 38 deletions(-) diff --git a/Content.Server/RCD/Components/RCDComponent.cs b/Content.Server/RCD/Components/RCDComponent.cs index 5e99b33766..382cc8ad51 100644 --- a/Content.Server/RCD/Components/RCDComponent.cs +++ b/Content.Server/RCD/Components/RCDComponent.cs @@ -1,3 +1,4 @@ +using System.Threading; using Content.Shared.Sound; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; @@ -5,7 +6,7 @@ using Robust.Shared.ViewVariables; namespace Content.Server.RCD.Components { - public enum RcdMode + public enum RcdMode : byte { Floors, Walls, @@ -16,11 +17,10 @@ namespace Content.Server.RCD.Components [RegisterComponent] public sealed class RCDComponent : Component { - [ViewVariables(VVAccess.ReadOnly)] - [DataField("maxAmmo")] public int MaxAmmo = 5; + private const int DefaultAmmoCount = 5; [ViewVariables(VVAccess.ReadOnly)] - [DataField("startingAmmo")] public int StartingAmmo = 5; + [DataField("maxAmmo")] public int MaxAmmo = DefaultAmmoCount; [ViewVariables(VVAccess.ReadWrite)] [DataField("delay")] public float Delay = 2f; @@ -34,12 +34,16 @@ namespace Content.Server.RCD.Components /// /// What mode are we on? Can be floors, walls, deconstruct. /// + [ViewVariables] + [DataField("mode")] public RcdMode Mode = RcdMode.Floors; /// /// How much "ammo" we have left. You can refill this with RCD ammo. /// - public int CurrentAmmo; + [ViewVariables(VVAccess.ReadWrite)] [DataField("ammo")] + public int CurrentAmmo = DefaultAmmoCount; + public CancellationTokenSource? CancelToken = null; } } diff --git a/Content.Server/RCD/Systems/RCDSystem.cs b/Content.Server/RCD/Systems/RCDSystem.cs index d4e6dad4c0..4dc40b8114 100644 --- a/Content.Server/RCD/Systems/RCDSystem.cs +++ b/Content.Server/RCD/Systems/RCDSystem.cs @@ -1,8 +1,11 @@ using System; using System.Threading; +using Content.Server.Administration.Logs; using Content.Server.DoAfter; +using Content.Server.Popups; using Content.Server.RCD.Components; using Content.Shared.Coordinates; +using Content.Shared.Database; using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; @@ -25,26 +28,22 @@ namespace Content.Server.RCD.Systems [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly AdminLogSystem _logs = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; + [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly TagSystem _tagSystem = default!; - private readonly RcdMode[] _modes = (RcdMode[]) Enum.GetValues(typeof(RcdMode)); + private readonly int RCDModeCount = Enum.GetValues(typeof(RcdMode)).Length; 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", @@ -66,7 +65,16 @@ namespace Content.Server.RCD.Systems if (args.Handled || !args.CanReach) return; - // FIXME: Make this work properly. Right now it relies on the click location being on a grid, which is bad. + if (rcd.CancelToken != null) + { + rcd.CancelToken?.Cancel(); + rcd.CancelToken = null; + args.Handled = true; + return; + } + + if (!args.ClickLocation.IsValid(EntityManager)) return; + var clickLocationMod = args.ClickLocation; // Initial validity check if (!clickLocationMod.IsValid(EntityManager)) @@ -89,41 +97,46 @@ namespace Content.Server.RCD.Systems //No changing mode mid-RCD var startingMode = rcd.Mode; + args.Handled = true; + var user = args.User; //Using an RCD isn't instantaneous - var cancelToken = new CancellationTokenSource(); - var doAfterEventArgs = new DoAfterEventArgs(args.User, rcd.Delay, cancelToken.Token, args.Target) + rcd.CancelToken = new CancellationTokenSource(); + var doAfterEventArgs = new DoAfterEventArgs(user, rcd.Delay, rcd.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 + ExtraCheck = () => IsRCDStillValid(rcd, args, mapGrid, tile, startingMode) //All of the sanity checks are here }; var result = await _doAfterSystem.WaitDoAfter(doAfterEventArgs); + + rcd.CancelToken = null; + 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(clickLocationMod, new Tile(_tileDefinitionManager["floor_steel"].TileId)); + mapGrid.SetTile(snapPos, new Tile(_tileDefinitionManager["floor_steel"].TileId)); + _logs.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to set grid: {tile.GridIndex} {snapPos} to floor_steel"); 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); + _logs.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to set grid: {tile.GridIndex} tile: {snapPos} to space"); } else //Delete what the user targeted { if (args.Target is {Valid: true} target) { - EntityManager.DeleteEntity(target); + _logs.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to delete {ToPrettyString(target):target}"); + QueueDel(target); } } break; @@ -131,28 +144,30 @@ namespace Content.Server.RCD.Systems // thus we early return to avoid the tile set code. case RcdMode.Walls: var ent = EntityManager.SpawnEntity("WallSolid", mapGrid.GridTileToLocal(snapPos)); - EntityManager.GetComponent(ent).LocalRotation = Angle.Zero; // Walls always need to point south. + Transform(ent).LocalRotation = Angle.Zero; // Walls always need to point south. + _logs.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to spawn {ToPrettyString(ent)} at {snapPos} on grid {mapGrid.Index}"); break; case RcdMode.Airlock: var airlock = EntityManager.SpawnEntity("Airlock", mapGrid.GridTileToLocal(snapPos)); - EntityManager.GetComponent(airlock).LocalRotation = EntityManager.GetComponent(rcd.Owner).LocalRotation; //Now apply icon smoothing. + Transform(airlock).LocalRotation = Transform(rcd.Owner).LocalRotation; //Now apply icon smoothing. + _logs.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to spawn {ToPrettyString(airlock)} at {snapPos} on grid {mapGrid.Index}"); 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); + SoundSystem.Play(Filter.Pvs(uid, entityManager: EntityManager), 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) + private bool IsRCDStillValid(RCDComponent rcd, AfterInteractEvent eventArgs, IMapGrid mapGrid, TileRef tile, 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")); + _popup.PopupEntity(Loc.GetString("rcd-component-no-ammo-message"), rcd.Owner, Filter.Entities(eventArgs.User)); return false; } @@ -174,7 +189,7 @@ namespace Content.Server.RCD.Systems case RcdMode.Floors: if (!tile.Tile.IsEmpty) { - rcd.Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-cannot-build-floor-tile-not-empty-message")); + _popup.PopupEntity(Loc.GetString("rcd-component-cannot-build-floor-tile-not-empty-message"), rcd.Owner, Filter.Entities(eventArgs.User)); return false; } @@ -189,13 +204,13 @@ namespace Content.Server.RCD.Systems //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")); + _popup.PopupEntity(Loc.GetString("rcd-component-tile-obstructed-message"), rcd.Owner, Filter.Entities(eventArgs.User)); return false; } //They tried to decon a non-turf but it's not in the whitelist if (eventArgs.Target != null && !_tagSystem.HasTag(eventArgs.Target.Value, "RCDDeconstructWhitelist")) { - rcd.Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-deconstruct-target-not-on-whitelist-message")); + _popup.PopupEntity(Loc.GetString("rcd-component-deconstruct-target-not-on-whitelist-message"), rcd.Owner, Filter.Entities(eventArgs.User)); return false; } @@ -204,25 +219,25 @@ namespace Content.Server.RCD.Systems case RcdMode.Walls: if (tile.Tile.IsEmpty) { - rcd.Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-cannot-build-wall-tile-not-empty-message")); + _popup.PopupEntity(Loc.GetString("rcd-component-cannot-build-wall-tile-not-empty-message"), rcd.Owner, Filter.Entities(eventArgs.User)); return false; } if (tile.IsBlockedTurf(true)) { - rcd.Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-tile-obstructed-message")); + _popup.PopupEntity(Loc.GetString("rcd-component-tile-obstructed-message"), rcd.Owner, Filter.Entities(eventArgs.User)); 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")); + _popup.PopupEntity(Loc.GetString("rcd-component-cannot-build-airlock-tile-not-empty-message"), rcd.Owner, Filter.Entities(eventArgs.User)); return false; } if (tile.IsBlockedTurf(true)) { - rcd.Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-tile-obstructed-message")); + _popup.PopupEntity(Loc.GetString("rcd-component-tile-obstructed-message"), rcd.Owner, Filter.Entities(eventArgs.User)); return false; } return true; @@ -233,14 +248,14 @@ namespace Content.Server.RCD.Systems private void NextMode(EntityUid uid, RCDComponent rcd, EntityUid user) { - SoundSystem.Play(Filter.Pvs(uid), rcd.SwapModeSound.GetSound(), uid); + SoundSystem.Play(Filter.Pvs(uid, entityManager: EntityManager), 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. + var mode = (int) rcd.Mode; + mode = ++mode % RCDModeCount; + rcd.Mode = (RcdMode) mode; var msg = Loc.GetString("rcd-component-change-mode", ("mode", rcd.Mode.ToString())); - rcd.Owner.PopupMessage(user, msg); //Prints an overhead message above the RCD + _popup.PopupEntity(msg, rcd.Owner, Filter.Entities(user)); } } } diff --git a/Content.Shared.Database/LogType.cs b/Content.Shared.Database/LogType.cs index 24990cbea8..5419d28e2a 100644 --- a/Content.Shared.Database/LogType.cs +++ b/Content.Shared.Database/LogType.cs @@ -64,5 +64,7 @@ public enum LogType GhostRoleTaken = 60, Chat = 61, Action = 62, + RCD = 63, + // haha so funny Emag = 69, }