From e8b95dd88ae1077ace0f713222d9613d9940a6e7 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 30 Jan 2022 14:48:18 +1100 Subject: [PATCH] Tile prying do_after (#6341) --- Content.Server/DoAfter/DoAfter.cs | 7 +- .../Tools/Components/TilePryingComponent.cs | 61 ++++--------- Content.Server/Tools/ToolSystem.TilePrying.cs | 86 +++++++++++++++++++ Content.Server/Tools/ToolSystem.cs | 25 ++++-- 4 files changed, 125 insertions(+), 54 deletions(-) create mode 100644 Content.Server/Tools/ToolSystem.TilePrying.cs diff --git a/Content.Server/DoAfter/DoAfter.cs b/Content.Server/DoAfter/DoAfter.cs index a2d6c0ad24..1f073b50e5 100644 --- a/Content.Server/DoAfter/DoAfter.cs +++ b/Content.Server/DoAfter/DoAfter.cs @@ -42,7 +42,7 @@ namespace Content.Server.DoAfter UserGrid = entityManager.GetComponent(eventArgs.User).Coordinates; } - if (eventArgs.BreakOnTargetMove) + if (eventArgs.Target != null && eventArgs.BreakOnTargetMove) { // Target should never be null if the bool is set. TargetGrid = entityManager.GetComponent(eventArgs.Target!.Value).Coordinates; @@ -122,8 +122,9 @@ namespace Content.Server.DoAfter return true; } - if (EventArgs.BreakOnTargetMove && !entityManager.GetComponent(EventArgs.Target!.Value).Coordinates.InRange( - entityManager, TargetGrid, EventArgs.MovementThreshold)) + if (EventArgs.Target != null && + EventArgs.BreakOnTargetMove && + !entityManager.GetComponent(EventArgs.Target!.Value).Coordinates.InRange(entityManager, TargetGrid, EventArgs.MovementThreshold)) { return true; } diff --git a/Content.Server/Tools/Components/TilePryingComponent.cs b/Content.Server/Tools/Components/TilePryingComponent.cs index 37123df84b..353ee9d9a2 100644 --- a/Content.Server/Tools/Components/TilePryingComponent.cs +++ b/Content.Server/Tools/Components/TilePryingComponent.cs @@ -1,61 +1,30 @@ -using System.Threading.Tasks; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Helpers; -using Content.Shared.Maps; +using System.Threading; using Content.Shared.Tools; using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Map; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.ViewVariables; namespace Content.Server.Tools.Components { - [RegisterComponent] - public class TilePryingComponent : Component, IAfterInteract + [RegisterComponent, ComponentProtoName("TilePrying")] + public sealed class TilePryingComponent : Component { - [Dependency] private readonly IEntityManager _entMan = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; - - public override string Name => "TilePrying"; - + [ViewVariables] [DataField("toolComponentNeeded")] - private bool _toolComponentNeeded = true; + public bool ToolComponentNeeded = true; + [ViewVariables] [DataField("qualityNeeded", customTypeSerializer:typeof(PrototypeIdSerializer))] - private string _qualityNeeded = "Prying"; + public string QualityNeeded = "Prying"; - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) - { - TryPryTile(eventArgs.User, eventArgs.ClickLocation); - return true; - } + [ViewVariables] + [DataField("delay")] + public float Delay = 1f; - public async void TryPryTile(EntityUid user, EntityCoordinates clickLocation) - { - if (!_entMan.TryGetComponent(Owner, out var tool) && _toolComponentNeeded) - return; - - if (!_mapManager.TryGetGrid(clickLocation.GetGridId(_entMan), out var mapGrid)) - return; - - var tile = mapGrid.GetTileRef(clickLocation); - - var coordinates = mapGrid.GridTileToLocal(tile.GridIndices); - - if (!user.InRangeUnobstructed(coordinates, popup: false)) - return; - - var tileDef = (ContentTileDefinition)_tileDefinitionManager[tile.Tile.TypeId]; - - if (!tileDef.CanCrowbar) - return; - - if (_toolComponentNeeded && !await EntitySystem.Get().UseTool(Owner, user, null, 0f, 0f, _qualityNeeded, toolComponent:tool)) - return; - - coordinates.PryTile(_entMan, _mapManager); - } + /// + /// Used for do_afters. + /// + public CancellationTokenSource? CancelToken = null; } } diff --git a/Content.Server/Tools/ToolSystem.TilePrying.cs b/Content.Server/Tools/ToolSystem.TilePrying.cs new file mode 100644 index 0000000000..076cbab53f --- /dev/null +++ b/Content.Server/Tools/ToolSystem.TilePrying.cs @@ -0,0 +1,86 @@ +using System; +using System.Threading; +using Content.Server.Tools.Components; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Helpers; +using Content.Shared.Maps; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; + +namespace Content.Server.Tools; + +public sealed partial class ToolSystem +{ + private void InitializeTilePrying() + { + SubscribeLocalEvent(OnTilePryingAfterInteract); + SubscribeLocalEvent(OnTilePryComplete); + } + + private void OnTilePryComplete(EntityUid uid, TilePryingComponent component, TilePryingCompleteEvent args) + { + component.CancelToken = null; + args.Coordinates.PryTile(EntityManager, _mapManager); + } + + private void OnTilePryingAfterInteract(EntityUid uid, TilePryingComponent component, AfterInteractEvent args) + { + if (args.Handled) return; + + if (TryPryTile(args.User, component, args.ClickLocation)) + args.Handled = true; + } + + private bool TryPryTile(EntityUid user, TilePryingComponent component, EntityCoordinates clickLocation) + { + if (component.CancelToken != null) + { + component.CancelToken.Cancel(); + component.CancelToken = null; + return false; + } + + if (!TryComp(component.Owner, out var tool) && component.ToolComponentNeeded) + return false; + + if (!_mapManager.TryGetGrid(clickLocation.GetGridId(EntityManager), out var mapGrid)) + return false; + + var tile = mapGrid.GetTileRef(clickLocation); + + var coordinates = mapGrid.GridTileToLocal(tile.GridIndices); + + if (!user.InRangeUnobstructed(coordinates, popup: false)) + return false; + + var tileDef = (ContentTileDefinition)_tileDefinitionManager[tile.Tile.TypeId]; + + if (!tileDef.CanCrowbar) + return false; + + var token = new CancellationTokenSource(); + component.CancelToken = token; + + UseTool( + component.Owner, + user, + null, + 0f, + component.Delay, + new [] {component.QualityNeeded}, + new TilePryingCompleteEvent + { + Coordinates = clickLocation, + }, + toolComponent: tool, + doAfterEventTarget: component.Owner, + cancelToken: token.Token); + + return true; + } + + private sealed class TilePryingCompleteEvent : EntityEventArgs + { + public EntityCoordinates Coordinates { get; init; } + } +} diff --git a/Content.Server/Tools/ToolSystem.cs b/Content.Server/Tools/ToolSystem.cs index aceb354dff..d7a166e715 100644 --- a/Content.Server/Tools/ToolSystem.cs +++ b/Content.Server/Tools/ToolSystem.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Content.Server.Atmos.EntitySystems; using Content.Server.Chemistry.EntitySystems; @@ -11,6 +12,7 @@ using Content.Shared.Audio; using Robust.Shared.Audio; using Robust.Shared.GameObjects; using Robust.Shared.IoC; +using Robust.Shared.Map; using Robust.Shared.Player; using Robust.Shared.Prototypes; @@ -18,6 +20,8 @@ namespace Content.Server.Tools { public partial class ToolSystem : EntitySystem { + [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; + [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; @@ -30,6 +34,7 @@ namespace Content.Server.Tools { base.Initialize(); + InitializeTilePrying(); InitializeWelders(); InitializeMultipleTools(); @@ -94,14 +99,24 @@ namespace Content.Server.Tools /// Where to direct the do-after events. If null, events are broadcast /// An optional check to perform for the doAfter. /// The tool component. + /// Token to provide to do_after for cancelling /// Whether initially, using the tool succeeded. If there's a doAfter delay, you'll need to listen to /// the and being broadcast /// to see whether using the tool succeeded or not. If the is zero, /// this simply returns whether using the tool succeeded or not. - public bool UseTool(EntityUid tool, EntityUid user, EntityUid? target, float fuel, - float doAfterDelay, IEnumerable toolQualitiesNeeded, - object? doAfterCompleteEvent = null, object? doAfterCancelledEvent = null, EntityUid? doAfterEventTarget = null, - Func? doAfterCheck = null, ToolComponent? toolComponent = null) + public bool UseTool( + EntityUid tool, + EntityUid user, + EntityUid? target, + float fuel, + float doAfterDelay, + IEnumerable toolQualitiesNeeded, + object? doAfterCompleteEvent = null, + object? doAfterCancelledEvent = null, + EntityUid? doAfterEventTarget = null, + Func? doAfterCheck = null, + ToolComponent? toolComponent = null, + CancellationToken? cancelToken = null) { // No logging here, after all that'd mean the caller would need to check if the component is there or not. if (!Resolve(tool, ref toolComponent, false)) @@ -112,7 +127,7 @@ namespace Content.Server.Tools if (doAfterDelay > 0f) { - var doAfterArgs = new DoAfterEventArgs(user, doAfterDelay / toolComponent.SpeedModifier, default, target) + var doAfterArgs = new DoAfterEventArgs(user, doAfterDelay / toolComponent.SpeedModifier, cancelToken ?? default, target) { ExtraCheck = doAfterCheck, BreakOnDamage = true,