More DoAfter Changes (#14609)

* DoAfters

* Compact Clone()

* Fix mice and cuffables

* Try generalize attempt events

* moves climbabledoafter event to shared, fixes issue with climbable target

* Fix merge (cuffing)

* Make all events netserializable

* handful of doafter events moved

* moves the rest of the events to their respective shared folders

* Changes all mentions of server doafter to shared

* stop stripping cancellation

* fix merge errors

* draw paused doafters

* handle unpausing

* missing netserializable ref

* removes break on stun reference

* removes cuffing state reference

* Fix tools

* Fix door prying.

* Fix construction

* Fix dumping

* Fix wielding assert

* fix rev

* Fix test

* more test fixes

---------

Co-authored-by: keronshb <keronshb@live.com>
This commit is contained in:
Leon Friedrich
2023-04-03 13:13:48 +12:00
committed by GitHub
parent 9e66fac805
commit 19277a2276
170 changed files with 3042 additions and 2954 deletions

View File

@@ -25,7 +25,6 @@ public sealed class CuffableSystem : SharedCuffableSystem
if (args.Current is not HandcuffComponentState state) if (args.Current is not HandcuffComponentState state)
return; return;
component.Cuffing = state.Cuffing;
component.OverlayIconState = state.IconState; component.OverlayIconState = state.IconState;
} }
@@ -41,7 +40,6 @@ public sealed class CuffableSystem : SharedCuffableSystem
return; return;
component.CanStillInteract = cuffState.CanStillInteract; component.CanStillInteract = cuffState.CanStillInteract;
component.Uncuffing = cuffState.Uncuffing;
_actionBlocker.UpdateCanMove(uid); _actionBlocker.UpdateCanMove(uid);
var ev = new CuffedStateChangeEvent(); var ev = new CuffedStateChangeEvent();

View File

@@ -3,6 +3,7 @@ using Robust.Client.GameObjects;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Client.DoAfter; namespace Content.Client.DoAfter;
@@ -10,17 +11,30 @@ namespace Content.Client.DoAfter;
public sealed class DoAfterOverlay : Overlay public sealed class DoAfterOverlay : Overlay
{ {
private readonly IEntityManager _entManager; private readonly IEntityManager _entManager;
private readonly IGameTiming _timing;
private readonly SharedTransformSystem _transform; private readonly SharedTransformSystem _transform;
private readonly MetaDataSystem _meta;
private readonly Texture _barTexture; private readonly Texture _barTexture;
private readonly ShaderInstance _shader; private readonly ShaderInstance _shader;
/// <summary>
/// Flash time for cancelled DoAfters
/// </summary>
private const float FlashTime = 0.125f;
// Hardcoded width of the progress bar because it doesn't match the texture.
private const float StartX = 2;
private const float EndX = 22f;
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV; public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
public DoAfterOverlay(IEntityManager entManager, IPrototypeManager protoManager) public DoAfterOverlay(IEntityManager entManager, IPrototypeManager protoManager, IGameTiming timing)
{ {
_entManager = entManager; _entManager = entManager;
_timing = timing;
_transform = _entManager.EntitySysManager.GetEntitySystem<SharedTransformSystem>(); _transform = _entManager.EntitySysManager.GetEntitySystem<SharedTransformSystem>();
_meta = _entManager.EntitySysManager.GetEntitySystem<MetaDataSystem>();
var sprite = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Interface/Misc/progress_bar.rsi"), "icon"); var sprite = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Interface/Misc/progress_bar.rsi"), "icon");
_barTexture = _entManager.EntitySysManager.GetEntitySystem<SpriteSystem>().Frame0(sprite); _barTexture = _entManager.EntitySysManager.GetEntitySystem<SpriteSystem>().Frame0(sprite);
@@ -31,7 +45,6 @@ public sealed class DoAfterOverlay : Overlay
{ {
var handle = args.WorldHandle; var handle = args.WorldHandle;
var rotation = args.Viewport.Eye?.Rotation ?? Angle.Zero; var rotation = args.Viewport.Eye?.Rotation ?? Angle.Zero;
var spriteQuery = _entManager.GetEntityQuery<SpriteComponent>();
var xformQuery = _entManager.GetEntityQuery<TransformComponent>(); var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
// If you use the display UI scale then need to set max(1f, displayscale) because 0 is valid. // If you use the display UI scale then need to set max(1f, displayscale) because 0 is valid.
@@ -40,43 +53,42 @@ public sealed class DoAfterOverlay : Overlay
var rotationMatrix = Matrix3.CreateRotation(-rotation); var rotationMatrix = Matrix3.CreateRotation(-rotation);
handle.UseShader(_shader); handle.UseShader(_shader);
// TODO: Need active DoAfter component (or alternatively just make DoAfter itself active) var curTime = _timing.CurTime;
foreach (var comp in _entManager.EntityQuery<DoAfterComponent>(true))
{ var bounds = args.WorldAABB.Enlarged(5f);
if (comp.DoAfters.Count == 0 ||
!xformQuery.TryGetComponent(comp.Owner, out var xform) || var metaQuery = _entManager.GetEntityQuery<MetaDataComponent>();
xform.MapID != args.MapId) var enumerator = _entManager.AllEntityQueryEnumerator<ActiveDoAfterComponent, DoAfterComponent, SpriteComponent, TransformComponent>();
{ while (enumerator.MoveNext(out var uid, out _, out var comp, out var sprite, out var xform))
continue; {
} if (xform.MapID != args.MapId)
continue;
if (comp.DoAfters.Count == 0)
continue;
var worldPosition = _transform.GetWorldPosition(xform, xformQuery);
if (!bounds.Contains(worldPosition))
continue;
// If the entity is paused, we will draw the do-after as it was when the entity got paused.
var meta = metaQuery.GetComponent(uid);
var time = meta.EntityPaused
? curTime - _meta.GetPauseTime(uid, meta)
: curTime;
var worldPosition = _transform.GetWorldPosition(xform);
var index = 0;
var worldMatrix = Matrix3.CreateTranslation(worldPosition); var worldMatrix = Matrix3.CreateTranslation(worldPosition);
Matrix3.Multiply(scaleMatrix, worldMatrix, out var scaledWorld);
Matrix3.Multiply(rotationMatrix, scaledWorld, out var matty);
handle.SetTransform(matty);
var offset = 0f;
foreach (var doAfter in comp.DoAfters.Values) foreach (var doAfter in comp.DoAfters.Values)
{ {
var elapsed = doAfter.Elapsed;
var displayRatio = MathF.Min(1.0f,
(float)elapsed.TotalSeconds / doAfter.Delay);
Matrix3.Multiply(scaleMatrix, worldMatrix, out var scaledWorld);
Matrix3.Multiply(rotationMatrix, scaledWorld, out var matty);
handle.SetTransform(matty);
var offset = _barTexture.Height / scale * index;
// Use the sprite itself if we know its bounds. This means short or tall sprites don't get overlapped // Use the sprite itself if we know its bounds. This means short or tall sprites don't get overlapped
// by the bar. // by the bar.
float yOffset; float yOffset = sprite.Bounds.Height / 2f + 0.05f;
if (spriteQuery.TryGetComponent(comp.Owner, out var sprite))
{
yOffset = sprite.Bounds.Height / 2f + 0.05f;
}
else
{
yOffset = 0.5f;
}
// Position above the entity (we've already applied the matrix transform to the entity itself) // Position above the entity (we've already applied the matrix transform to the entity itself)
// Offset by the texture size for every do_after we have. // Offset by the texture size for every do_after we have.
@@ -86,33 +98,30 @@ public sealed class DoAfterOverlay : Overlay
// Draw the underlying bar texture // Draw the underlying bar texture
handle.DrawTexture(_barTexture, position); handle.DrawTexture(_barTexture, position);
// Draw the bar itself
var cancelled = doAfter.Cancelled;
Color color; Color color;
const float flashTime = 0.125f; float elapsedRatio;
// if we're cancelled then flick red / off. // if we're cancelled then flick red / off.
if (cancelled) if (doAfter.CancelledTime != null)
{ {
var flash = Math.Floor((float)doAfter.CancelledElapsed.TotalSeconds / flashTime) % 2 == 0; var elapsed = doAfter.CancelledTime.Value - doAfter.StartTime;
elapsedRatio = (float) Math.Min(1, elapsed.TotalSeconds / doAfter.Args.Delay.TotalSeconds);
var cancelElapsed = (time - doAfter.CancelledTime.Value).TotalSeconds;
var flash = Math.Floor(cancelElapsed / FlashTime) % 2 == 0;
color = new Color(1f, 0f, 0f, flash ? 1f : 0f); color = new Color(1f, 0f, 0f, flash ? 1f : 0f);
} }
else else
{ {
color = GetProgressColor(displayRatio); var elapsed = time - doAfter.StartTime;
elapsedRatio = (float) Math.Min(1, elapsed.TotalSeconds / doAfter.Args.Delay.TotalSeconds);
color = GetProgressColor(elapsedRatio);
} }
// Hardcoded width of the progress bar because it doesn't match the texture. var xProgress = (EndX - StartX) * elapsedRatio + StartX;
const float startX = 2f; var box = new Box2(new Vector2(StartX, 3f) / EyeManager.PixelsPerMeter, new Vector2(xProgress, 4f) / EyeManager.PixelsPerMeter);
const float endX = 22f;
var xProgress = (endX - startX) * displayRatio + startX;
var box = new Box2(new Vector2(startX, 3f) / EyeManager.PixelsPerMeter, new Vector2(xProgress, 4f) / EyeManager.PixelsPerMeter);
box = box.Translated(position); box = box.Translated(position);
handle.DrawRect(box, color); handle.DrawRect(box, color);
offset += _barTexture.Height / scale;
index++;
} }
} }

View File

@@ -1,9 +1,8 @@
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Hands.Components;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.Player; using Robust.Client.Player;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.DoAfter; namespace Content.Client.DoAfter;
@@ -16,19 +15,12 @@ public sealed class DoAfterSystem : SharedDoAfterSystem
[Dependency] private readonly IOverlayManager _overlay = default!; [Dependency] private readonly IOverlayManager _overlay = default!;
[Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly MetaDataSystem _metadata = default!;
/// <summary>
/// We'll use an excess time so stuff like finishing effects can show.
/// </summary>
public const float ExcessTime = 0.5f;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
UpdatesOutsidePrediction = true; _overlay.AddOverlay(new DoAfterOverlay(EntityManager, _prototype, GameTiming));
SubscribeNetworkEvent<CancelledDoAfterMessage>(OnCancelledDoAfter);
SubscribeLocalEvent<DoAfterComponent, ComponentHandleState>(OnDoAfterHandleState);
_overlay.AddOverlay(new DoAfterOverlay(EntityManager, _prototype));
} }
public override void Shutdown() public override void Shutdown()
@@ -37,147 +29,26 @@ public sealed class DoAfterSystem : SharedDoAfterSystem
_overlay.RemoveOverlay<DoAfterOverlay>(); _overlay.RemoveOverlay<DoAfterOverlay>();
} }
private void OnDoAfterHandleState(EntityUid uid, DoAfterComponent component, ref ComponentHandleState args)
{
if (args.Current is not DoAfterComponentState state)
return;
foreach (var (_, doAfter) in state.DoAfters)
{
if (component.DoAfters.ContainsKey(doAfter.ID))
continue;
component.DoAfters.Add(doAfter.ID, doAfter);
}
}
private void OnCancelledDoAfter(CancelledDoAfterMessage ev)
{
if (!TryComp<DoAfterComponent>(ev.Uid, out var doAfter))
return;
Cancel(doAfter, ev.ID);
}
/// <summary>
/// Remove a DoAfter without showing a cancellation graphic.
/// </summary>
public void Remove(DoAfterComponent component, Shared.DoAfter.DoAfter doAfter, bool found = false)
{
component.DoAfters.Remove(doAfter.ID);
component.CancelledDoAfters.Remove(doAfter.ID);
}
/// <summary>
/// Mark a DoAfter as cancelled and show a cancellation graphic.
/// </summary>
/// Actual removal is handled by DoAfterEntitySystem.
public void Cancel(DoAfterComponent component, byte id)
{
if (component.CancelledDoAfters.ContainsKey(id))
return;
if (!component.DoAfters.ContainsKey(id))
return;
var doAfterMessage = component.DoAfters[id];
doAfterMessage.Cancelled = true;
doAfterMessage.CancelledTime = GameTiming.CurTime;
component.CancelledDoAfters.Add(id, doAfterMessage);
}
// TODO separate DoAfter & ActiveDoAfter components for the entity query.
public override void Update(float frameTime) public override void Update(float frameTime)
{ {
if (!GameTiming.IsFirstTimePredicted) // Currently this only predicts do afters initiated by the player.
return;
// TODO maybe predict do-afters if the local player is the target of some other players do-after? Specifically
// ones that depend on the target not moving, because the cancellation of those do afters should be readily
// predictable by clients.
var playerEntity = _player.LocalPlayer?.ControlledEntity; var playerEntity = _player.LocalPlayer?.ControlledEntity;
foreach (var (comp, xform) in EntityQuery<DoAfterComponent, TransformComponent>()) if (!TryComp(playerEntity, out ActiveDoAfterComponent? active))
{ return;
var doAfters = comp.DoAfters;
if (doAfters.Count == 0) if (_metadata.EntityPaused(playerEntity.Value))
continue; return;
var userGrid = xform.Coordinates; var time = GameTiming.CurTime;
var toRemove = new RemQueue<Shared.DoAfter.DoAfter>(); var comp = Comp<DoAfterComponent>(playerEntity.Value);
var xformQuery = GetEntityQuery<TransformComponent>();
// Check cancellations / finishes var handsQuery = GetEntityQuery<SharedHandsComponent>();
foreach (var (id, doAfter) in doAfters) Update(playerEntity.Value, active, comp, time, xformQuery, handsQuery);
{
// If we've passed the final time (after the excess to show completion graphic) then remove.
if ((float)doAfter.Elapsed.TotalSeconds + (float)doAfter.CancelledElapsed.TotalSeconds >
doAfter.Delay + ExcessTime)
{
toRemove.Add(doAfter);
continue;
}
if (doAfter.Cancelled)
{
doAfter.CancelledElapsed = GameTiming.CurTime - doAfter.CancelledTime;
continue;
}
doAfter.Elapsed = GameTiming.CurTime - doAfter.StartTime;
// Well we finished so don't try to predict cancels.
if ((float)doAfter.Elapsed.TotalSeconds > doAfter.Delay)
continue;
// Predictions
if (comp.Owner != playerEntity)
continue;
// TODO: Add these back in when I work out some system for changing the accumulation rate
// based on ping. Right now these would show as cancelled near completion if we moved at the end
// despite succeeding.
continue;
if (doAfter.EventArgs.BreakOnUserMove)
{
if (!userGrid.InRange(EntityManager, doAfter.UserGrid, doAfter.EventArgs.MovementThreshold))
{
Cancel(comp, id);
continue;
}
}
if (doAfter.EventArgs.BreakOnTargetMove)
{
if (!Deleted(doAfter.EventArgs.Target) &&
!Transform(doAfter.EventArgs.Target.Value).Coordinates.InRange(EntityManager,
doAfter.TargetGrid,
doAfter.EventArgs.MovementThreshold))
{
Cancel(comp, id);
continue;
}
}
}
foreach (var doAfter in toRemove)
{
Remove(comp, doAfter);
}
// Remove cancelled DoAfters after ExcessTime has elapsed
var toRemoveCancelled = new RemQueue<Shared.DoAfter.DoAfter>();
foreach (var (_, doAfter) in comp.CancelledDoAfters)
{
var cancelledElapsed = (float)doAfter.CancelledElapsed.TotalSeconds;
if (cancelledElapsed > ExcessTime)
toRemoveCancelled.Add(doAfter);
}
foreach (var doAfter in toRemoveCancelled)
{
Remove(comp, doAfter);
}
}
} }
} }

View File

@@ -19,7 +19,6 @@ public sealed class CryoPodSystem: SharedCryoPodSystem
SubscribeLocalEvent<CryoPodComponent, GetVerbsEvent<AlternativeVerb>>(AddAlternativeVerbs); SubscribeLocalEvent<CryoPodComponent, GetVerbsEvent<AlternativeVerb>>(AddAlternativeVerbs);
SubscribeLocalEvent<CryoPodComponent, GotEmaggedEvent>(OnEmagged); SubscribeLocalEvent<CryoPodComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<CryoPodComponent, CryoPodPryFinished>(OnCryoPodPryFinished); SubscribeLocalEvent<CryoPodComponent, CryoPodPryFinished>(OnCryoPodPryFinished);
SubscribeLocalEvent<CryoPodComponent, CryoPodPryInterrupted>(OnCryoPodPryInterrupted);
SubscribeLocalEvent<CryoPodComponent, AppearanceChangeEvent>(OnAppearanceChange); SubscribeLocalEvent<CryoPodComponent, AppearanceChangeEvent>(OnAppearanceChange);
SubscribeLocalEvent<InsideCryoPodComponent, ComponentStartup>(OnCryoPodInsertion); SubscribeLocalEvent<InsideCryoPodComponent, ComponentStartup>(OnCryoPodInsertion);

View File

@@ -20,10 +20,22 @@ public sealed class AlertsUIController : UIController, IOnStateEntered<GameplayS
var gameplayStateLoad = UIManager.GetUIController<GameplayStateLoadController>(); var gameplayStateLoad = UIManager.GetUIController<GameplayStateLoadController>();
gameplayStateLoad.OnScreenLoad += OnScreenLoad; gameplayStateLoad.OnScreenLoad += OnScreenLoad;
gameplayStateLoad.OnScreenUnload += OnScreenUnload;
}
private void OnScreenUnload()
{
var widget = UI;
if (widget != null)
widget.AlertPressed -= OnAlertPressed;
} }
private void OnScreenLoad() private void OnScreenLoad()
{ {
var widget = UI;
if (widget != null)
widget.AlertPressed += OnAlertPressed;
SyncAlerts(); SyncAlerts();
} }
@@ -43,14 +55,6 @@ public sealed class AlertsUIController : UIController, IOnStateEntered<GameplayS
{ {
UI?.SyncControls(system, system.AlertOrder, e); UI?.SyncControls(system, system.AlertOrder, e);
} }
// The UI can change underneath us if the user switches between HUD layouts
// So ensure we're subscribed to the AlertPressed callback.
if (UI != null)
{
UI.AlertPressed -= OnAlertPressed; // Ensure we don't hook into the callback twice
UI.AlertPressed += OnAlertPressed;
}
} }
public void OnSystemLoaded(ClientAlertsSystem system) public void OnSystemLoaded(ClientAlertsSystem system)
@@ -65,13 +69,9 @@ public sealed class AlertsUIController : UIController, IOnStateEntered<GameplayS
system.ClearAlerts -= SystemOnClearAlerts; system.ClearAlerts -= SystemOnClearAlerts;
} }
public void OnStateEntered(GameplayState state) public void OnStateEntered(GameplayState state)
{ {
if (UI != null)
{
UI.AlertPressed += OnAlertPressed;
}
// initially populate the frame if system is available // initially populate the frame if system is available
SyncAlerts(); SyncAlerts();
} }

View File

@@ -1,12 +1,14 @@
using System.Threading; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.DoAfter;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using NUnit.Framework; using NUnit.Framework;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Reflection;
using Robust.Shared.Serialization;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.IntegrationTests.Tests.DoAfter namespace Content.IntegrationTests.Tests.DoAfter
{ {
@@ -22,24 +24,41 @@ namespace Content.IntegrationTests.Tests.DoAfter
- type: DoAfter - type: DoAfter
"; ";
public sealed class TestDoAfterSystem : EntitySystem private sealed class TestDoAfterEvent : DoAfterEvent
{ {
public override void Initialize() public override DoAfterEvent Clone()
{ {
SubscribeLocalEvent<DoAfterEvent<TestDoAfterData>>(OnTestDoAfterFinishEvent); return this;
} }
private void OnTestDoAfterFinishEvent(DoAfterEvent<TestDoAfterData> ev)
{
ev.AdditionalData.Cancelled = ev.Cancelled;
}
}
private sealed class TestDoAfterData
{
public bool Cancelled;
}; };
[Test]
public async Task TestSerializable()
{
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
await server.WaitIdleAsync();
var refMan = server.ResolveDependency<IReflectionManager>();
await server.WaitPost(() =>
{
Assert.Multiple(() =>
{
foreach (var type in refMan.GetAllChildren<DoAfterEvent>(true))
{
if (type.IsAbstract || type == typeof(TestDoAfterEvent))
continue;
Assert.That(type.HasCustomAttribute<NetSerializableAttribute>()
&& type.HasCustomAttribute<SerializableAttribute>(),
$"{nameof(DoAfterEvent)} is not NetSerializable. Event: {type.Name}");
}
});
});
await pairTracker.CleanReturnAsync();
}
[Test] [Test]
public async Task TestFinished() public async Task TestFinished()
{ {
@@ -48,21 +67,21 @@ namespace Content.IntegrationTests.Tests.DoAfter
await server.WaitIdleAsync(); await server.WaitIdleAsync();
var entityManager = server.ResolveDependency<IEntityManager>(); var entityManager = server.ResolveDependency<IEntityManager>();
var doAfterSystem = entityManager.EntitySysManager.GetEntitySystem<DoAfterSystem>(); var doAfterSystem = entityManager.EntitySysManager.GetEntitySystem<SharedDoAfterSystem>();
var data = new TestDoAfterData(); var ev = new TestDoAfterEvent();
// That it finishes successfully // That it finishes successfully
await server.WaitPost(() => await server.WaitPost(() =>
{ {
var tickTime = 1.0f / IoCManager.Resolve<IGameTiming>().TickRate; var tickTime = 1.0f / IoCManager.Resolve<IGameTiming>().TickRate;
var mob = entityManager.SpawnEntity("Dummy", MapCoordinates.Nullspace); var mob = entityManager.SpawnEntity("Dummy", MapCoordinates.Nullspace);
var cancelToken = new CancellationTokenSource(); var args = new DoAfterArgs(mob, tickTime / 2, ev, null) { Broadcast = true };
var args = new DoAfterEventArgs(mob, tickTime / 2, cancelToken.Token) { Broadcast = true }; Assert.That(doAfterSystem.TryStartDoAfter(args));
doAfterSystem.DoAfter(args, data); Assert.That(ev.Cancelled, Is.False);
}); });
await server.WaitRunTicks(1); await server.WaitRunTicks(1);
Assert.That(data.Cancelled, Is.False); Assert.That(ev.Cancelled, Is.False);
await pairTracker.CleanReturnAsync(); await pairTracker.CleanReturnAsync();
} }
@@ -73,22 +92,32 @@ namespace Content.IntegrationTests.Tests.DoAfter
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes}); await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server; var server = pairTracker.Pair.Server;
var entityManager = server.ResolveDependency<IEntityManager>(); var entityManager = server.ResolveDependency<IEntityManager>();
var doAfterSystem = entityManager.EntitySysManager.GetEntitySystem<DoAfterSystem>(); var doAfterSystem = entityManager.EntitySysManager.GetEntitySystem<SharedDoAfterSystem>();
var data = new TestDoAfterData(); DoAfterId? id = default;
var ev = new TestDoAfterEvent();
await server.WaitPost(() => await server.WaitPost(() =>
{ {
var tickTime = 1.0f / IoCManager.Resolve<IGameTiming>().TickRate; var tickTime = 1.0f / IoCManager.Resolve<IGameTiming>().TickRate;
var mob = entityManager.SpawnEntity("Dummy", MapCoordinates.Nullspace); var mob = entityManager.SpawnEntity("Dummy", MapCoordinates.Nullspace);
var cancelToken = new CancellationTokenSource(); var args = new DoAfterArgs(mob, tickTime * 2, ev, null) { Broadcast = true };
var args = new DoAfterEventArgs(mob, tickTime * 2, cancelToken.Token) { Broadcast = true };
doAfterSystem.DoAfter(args, data); if (!doAfterSystem.TryStartDoAfter(args, out id))
cancelToken.Cancel(); {
Assert.Fail();
return;
}
Assert.That(!ev.Cancelled);
doAfterSystem.Cancel(id);
Assert.That(ev.Cancelled);
}); });
await server.WaitRunTicks(3); await server.WaitRunTicks(3);
Assert.That(data.Cancelled, Is.True); Assert.That(ev.Cancelled);
await pairTracker.CleanReturnAsync(); await pairTracker.CleanReturnAsync();
} }

View File

@@ -1,5 +1,4 @@
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.DoAfter;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.UserInterface; using Content.Server.UserInterface;
using Content.Shared.AirlockPainter; using Content.Shared.AirlockPainter;
@@ -10,7 +9,6 @@ using Content.Shared.Doors.Components;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Player;
namespace Content.Server.AirlockPainter namespace Content.Server.AirlockPainter
{ {
@@ -22,7 +20,7 @@ namespace Content.Server.AirlockPainter
{ {
[Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
@@ -34,23 +32,21 @@ namespace Content.Server.AirlockPainter
SubscribeLocalEvent<AirlockPainterComponent, AfterInteractEvent>(AfterInteractOn); SubscribeLocalEvent<AirlockPainterComponent, AfterInteractEvent>(AfterInteractOn);
SubscribeLocalEvent<AirlockPainterComponent, ActivateInWorldEvent>(OnActivate); SubscribeLocalEvent<AirlockPainterComponent, ActivateInWorldEvent>(OnActivate);
SubscribeLocalEvent<AirlockPainterComponent, AirlockPainterSpritePickedMessage>(OnSpritePicked); SubscribeLocalEvent<AirlockPainterComponent, AirlockPainterSpritePickedMessage>(OnSpritePicked);
SubscribeLocalEvent<AirlockPainterComponent, DoAfterEvent<AirlockPainterData>>(OnDoAfter); SubscribeLocalEvent<AirlockPainterComponent, AirlockPainterDoAfterEvent>(OnDoAfter);
} }
private void OnDoAfter(EntityUid uid, AirlockPainterComponent component, DoAfterEvent<AirlockPainterData> args) private void OnDoAfter(EntityUid uid, AirlockPainterComponent component, AirlockPainterDoAfterEvent args)
{
if (args.Handled || args.Cancelled)
{ {
component.IsSpraying = false; component.IsSpraying = false;
if (args.Handled || args.Cancelled)
return; return;
}
if (args.Args.Target != null) if (args.Args.Target != null)
{ {
_audio.Play(component.SpraySound, Filter.Pvs(uid, entityManager:EntityManager), uid, true); _audio.PlayPvs(component.SpraySound, uid);
_appearance.SetData(args.Args.Target.Value, DoorVisuals.BaseRSI, args.AdditionalData.Sprite); _appearance.SetData(args.Args.Target.Value, DoorVisuals.BaseRSI, args.Sprite);
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.Args.User):user} painted {ToPrettyString(args.Args.Target.Value):target}"); _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.Args.User):user} painted {ToPrettyString(args.Args.Target.Value):target}");
component.IsSpraying = false;
} }
args.Handled = true; args.Handled = true;
@@ -88,16 +84,14 @@ namespace Content.Server.AirlockPainter
} }
component.IsSpraying = true; component.IsSpraying = true;
var airlockPainterData = new AirlockPainterData(sprite); var doAfterEventArgs = new DoAfterArgs(args.User, component.SprayTime, new AirlockPainterDoAfterEvent(sprite), uid, target: target, used: uid)
var doAfterEventArgs = new DoAfterEventArgs(args.User, component.SprayTime, target:target, used:uid)
{ {
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true,
NeedHand = true, NeedHand = true,
}; };
_doAfterSystem.DoAfter(doAfterEventArgs, airlockPainterData); _doAfterSystem.TryStartDoAfter(doAfterEventArgs);
// Log attempt // Log attempt
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.User):user} is painting {ToPrettyString(uid):target} to '{style}' at {Transform(uid).Coordinates:targetlocation}"); _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.User):user} is painting {ToPrettyString(uid):target} to '{style}' at {Transform(uid).Coordinates:targetlocation}");
@@ -118,10 +112,5 @@ namespace Content.Server.AirlockPainter
_userInterfaceSystem.TrySetUiState(uid, AirlockPainterUiKey.Key, _userInterfaceSystem.TrySetUiState(uid, AirlockPainterUiKey.Key,
new AirlockPainterBoundUserInterfaceState(component.Index)); new AirlockPainterBoundUserInterfaceState(component.Index));
} }
private record struct AirlockPainterData(string Sprite)
{
public string Sprite = Sprite;
}
} }
} }

View File

@@ -18,7 +18,7 @@ public sealed class RemoveEnsnare : IAlertClick
if (!entManager.TryGetComponent(ensnare, out EnsnaringComponent? ensnaringComponent)) if (!entManager.TryGetComponent(ensnare, out EnsnaringComponent? ensnaringComponent))
return; return;
entManager.EntitySysManager.GetEntitySystem<EnsnareableSystem>().TryFree(player, ensnare, ensnaringComponent); entManager.EntitySysManager.GetEntitySystem<EnsnareableSystem>().TryFree(player, player, ensnare, ensnaringComponent);
} }
} }
} }

View File

@@ -37,7 +37,5 @@ namespace Content.Server.Animals.Components
public float UpdateRate = 5; public float UpdateRate = 5;
public float AccumulatedFrameTime; public float AccumulatedFrameTime;
public bool BeingMilked;
} }
} }

View File

@@ -1,13 +1,13 @@
using Content.Server.Animals.Components; using Content.Server.Animals.Components;
using Content.Server.Chemistry.Components.SolutionManager; using Content.Server.Chemistry.Components.SolutionManager;
using Content.Server.Chemistry.EntitySystems; using Content.Server.Chemistry.EntitySystems;
using Content.Server.DoAfter;
using Content.Server.Nutrition.Components; using Content.Server.Nutrition.Components;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.Components;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Udder;
using Content.Shared.Verbs; using Content.Shared.Verbs;
namespace Content.Server.Animals.Systems namespace Content.Server.Animals.Systems
@@ -18,7 +18,7 @@ namespace Content.Server.Animals.Systems
internal sealed class UdderSystem : EntitySystem internal sealed class UdderSystem : EntitySystem
{ {
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
public override void Initialize() public override void Initialize()
@@ -26,7 +26,7 @@ namespace Content.Server.Animals.Systems
base.Initialize(); base.Initialize();
SubscribeLocalEvent<UdderComponent, GetVerbsEvent<AlternativeVerb>>(AddMilkVerb); SubscribeLocalEvent<UdderComponent, GetVerbsEvent<AlternativeVerb>>(AddMilkVerb);
SubscribeLocalEvent<UdderComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<UdderComponent, MilkingDoAfterEvent>(OnDoAfter);
} }
public override void Update(float frameTime) public override void Update(float frameTime)
{ {
@@ -64,38 +64,21 @@ namespace Content.Server.Animals.Systems
if (!Resolve(uid, ref udder)) if (!Resolve(uid, ref udder))
return; return;
if (udder.BeingMilked) var doargs = new DoAfterArgs(userUid, 5, new MilkingDoAfterEvent(), uid, uid, used: containerUid)
{
_popupSystem.PopupEntity(Loc.GetString("udder-system-already-milking"), uid, userUid);
return;
}
udder.BeingMilked = true;
var doargs = new DoAfterEventArgs(userUid, 5, target:uid, used:containerUid)
{ {
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
MovementThreshold = 1.0f MovementThreshold = 1.0f,
}; };
_doAfterSystem.DoAfter(doargs); _doAfterSystem.TryStartDoAfter(doargs);
} }
private void OnDoAfter(EntityUid uid, UdderComponent component, DoAfterEvent args) private void OnDoAfter(EntityUid uid, UdderComponent component, MilkingDoAfterEvent args)
{ {
if (args.Cancelled) if (args.Cancelled || args.Handled || args.Args.Used == null)
{
component.BeingMilked = false;
return; return;
}
if (args.Handled || args.Args.Used == null)
return;
component.BeingMilked = false;
if (!_solutionContainerSystem.TryGetSolution(uid, component.TargetSolutionName, out var solution)) if (!_solutionContainerSystem.TryGetSolution(uid, component.TargetSolutionName, out var solution))
return; return;
@@ -103,6 +86,7 @@ namespace Content.Server.Animals.Systems
if (!_solutionContainerSystem.TryGetRefillableSolution(args.Args.Used.Value, out var targetSolution)) if (!_solutionContainerSystem.TryGetRefillableSolution(args.Args.Used.Value, out var targetSolution))
return; return;
args.Handled = true;
var quantity = solution.Volume; var quantity = solution.Volume;
if(quantity == 0) if(quantity == 0)
{ {
@@ -118,8 +102,6 @@ namespace Content.Server.Animals.Systems
_popupSystem.PopupEntity(Loc.GetString("udder-system-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), uid, _popupSystem.PopupEntity(Loc.GetString("udder-system-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), uid,
args.Args.User, PopupType.Medium); args.Args.User, PopupType.Medium);
args.Handled = true;
} }
private void AddMilkVerb(EntityUid uid, UdderComponent component, GetVerbsEvent<AlternativeVerb> args) private void AddMilkVerb(EntityUid uid, UdderComponent component, GetVerbsEvent<AlternativeVerb> args)

View File

@@ -17,7 +17,7 @@ public sealed partial class AnomalySystem
{ {
SubscribeLocalEvent<AnomalyScannerComponent, BoundUIOpenedEvent>(OnScannerUiOpened); SubscribeLocalEvent<AnomalyScannerComponent, BoundUIOpenedEvent>(OnScannerUiOpened);
SubscribeLocalEvent<AnomalyScannerComponent, AfterInteractEvent>(OnScannerAfterInteract); SubscribeLocalEvent<AnomalyScannerComponent, AfterInteractEvent>(OnScannerAfterInteract);
SubscribeLocalEvent<AnomalyScannerComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<AnomalyScannerComponent, ScannerDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<AnomalyShutdownEvent>(OnScannerAnomalyShutdown); SubscribeLocalEvent<AnomalyShutdownEvent>(OnScannerAnomalyShutdown);
SubscribeLocalEvent<AnomalySeverityChangedEvent>(OnScannerAnomalySeverityChanged); SubscribeLocalEvent<AnomalySeverityChangedEvent>(OnScannerAnomalySeverityChanged);
@@ -81,7 +81,7 @@ public sealed partial class AnomalySystem
if (!HasComp<AnomalyComponent>(target)) if (!HasComp<AnomalyComponent>(target))
return; return;
_doAfter.DoAfter(new DoAfterEventArgs(args.User, component.ScanDoAfterDuration, target:target, used:uid) _doAfter.TryStartDoAfter(new DoAfterArgs(args.User, component.ScanDoAfterDuration, new ScannerDoAfterEvent(), uid, target: target, used: uid)
{ {
DistanceThreshold = 2f DistanceThreshold = 2f
}); });

View File

@@ -1,12 +1,12 @@
using Content.Server.Anomaly.Components; using Content.Server.Anomaly.Components;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Audio; using Content.Server.Audio;
using Content.Server.DoAfter;
using Content.Server.Explosion.EntitySystems; using Content.Server.Explosion.EntitySystems;
using Content.Server.Materials; using Content.Server.Materials;
using Content.Server.Radio.EntitySystems; using Content.Server.Radio.EntitySystems;
using Content.Shared.Anomaly; using Content.Shared.Anomaly;
using Content.Shared.Anomaly.Components; using Content.Shared.Anomaly.Components;
using Content.Shared.DoAfter;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Events;
@@ -24,7 +24,7 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
[Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly AmbientSoundSystem _ambient = default!; [Dependency] private readonly AmbientSoundSystem _ambient = default!;
[Dependency] private readonly AtmosphereSystem _atmosphere = default!; [Dependency] private readonly AtmosphereSystem _atmosphere = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly ExplosionSystem _explosion = default!; [Dependency] private readonly ExplosionSystem _explosion = default!;
[Dependency] private readonly MaterialStorageSystem _material = default!; [Dependency] private readonly MaterialStorageSystem _material = default!;
[Dependency] private readonly RadioSystem _radio = default!; [Dependency] private readonly RadioSystem _radio = default!;

View File

@@ -9,8 +9,8 @@ using Robust.Shared.Containers;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.DoAfter;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Internals;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Server.Body.Systems; namespace Content.Server.Body.Systems;
@@ -20,7 +20,7 @@ public sealed class InternalsSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _protoManager = default!; [Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly AlertsSystem _alerts = default!; [Dependency] private readonly AlertsSystem _alerts = default!;
[Dependency] private readonly AtmosphereSystem _atmos = default!; [Dependency] private readonly AtmosphereSystem _atmos = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly GasTankSystem _gasTank = default!; [Dependency] private readonly GasTankSystem _gasTank = default!;
[Dependency] private readonly HandsSystem _hands = default!; [Dependency] private readonly HandsSystem _hands = default!;
[Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly InventorySystem _inventory = default!;
@@ -34,7 +34,7 @@ public sealed class InternalsSystem : EntitySystem
SubscribeLocalEvent<InternalsComponent, ComponentStartup>(OnInternalsStartup); SubscribeLocalEvent<InternalsComponent, ComponentStartup>(OnInternalsStartup);
SubscribeLocalEvent<InternalsComponent, ComponentShutdown>(OnInternalsShutdown); SubscribeLocalEvent<InternalsComponent, ComponentShutdown>(OnInternalsShutdown);
SubscribeLocalEvent<InternalsComponent, GetVerbsEvent<InteractionVerb>>(OnGetInteractionVerbs); SubscribeLocalEvent<InternalsComponent, GetVerbsEvent<InteractionVerb>>(OnGetInteractionVerbs);
SubscribeLocalEvent<InternalsComponent, DoAfterEvent<InternalsData>>(OnDoAfter); SubscribeLocalEvent<InternalsComponent, InternalsDoAfterEvent>(OnDoAfter);
} }
private void OnGetInteractionVerbs(EntityUid uid, InternalsComponent component, GetVerbsEvent<InteractionVerb> args) private void OnGetInteractionVerbs(EntityUid uid, InternalsComponent component, GetVerbsEvent<InteractionVerb> args)
@@ -85,24 +85,19 @@ public sealed class InternalsSystem : EntitySystem
var isUser = uid == user; var isUser = uid == user;
var internalsData = new InternalsData();
if (!force) if (!force)
{ {
// Is the target not you? If yes, use a do-after to give them time to respond. // Is the target not you? If yes, use a do-after to give them time to respond.
//If no, do a short delay. There's no reason it should be beyond 1 second. //If no, do a short delay. There's no reason it should be beyond 1 second.
var delay = !isUser ? internals.Delay : 1.0f; var delay = !isUser ? internals.Delay : 1.0f;
_doAfter.DoAfter(new DoAfterEventArgs(user, delay, target:uid) _doAfter.TryStartDoAfter(new DoAfterArgs(user, delay, new InternalsDoAfterEvent(), uid, target: uid)
{ {
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
MovementThreshold = 0.1f, MovementThreshold = 0.1f,
RaiseOnUser = isUser, });
RaiseOnTarget = !isUser
}, internalsData);
return; return;
} }
@@ -110,12 +105,12 @@ public sealed class InternalsSystem : EntitySystem
_gasTank.ConnectToInternals(tank); _gasTank.ConnectToInternals(tank);
} }
private void OnDoAfter(EntityUid uid, InternalsComponent component, DoAfterEvent<InternalsData> args) private void OnDoAfter(EntityUid uid, InternalsComponent component, InternalsDoAfterEvent args)
{ {
if (args.Cancelled || args.Handled) if (args.Cancelled || args.Handled)
return; return;
ToggleInternals(uid, args.Args.User, true, component); ToggleInternals(uid, args.User, true, component);
args.Handled = true; args.Handled = true;
} }
@@ -266,9 +261,4 @@ public sealed class InternalsSystem : EntitySystem
return null; return null;
} }
private record struct InternalsData
{
}
} }

View File

@@ -1,15 +1,15 @@
using Content.Server.Botany.Components; using Content.Server.Botany.Components;
using Content.Server.DoAfter;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Swab;
namespace Content.Server.Botany.Systems; namespace Content.Server.Botany.Systems;
public sealed class BotanySwabSystem : EntitySystem public sealed class BotanySwabSystem : EntitySystem
{ {
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly MutationSystem _mutationSystem = default!; [Dependency] private readonly MutationSystem _mutationSystem = default!;
@@ -18,7 +18,7 @@ public sealed class BotanySwabSystem : EntitySystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<BotanySwabComponent, ExaminedEvent>(OnExamined); SubscribeLocalEvent<BotanySwabComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<BotanySwabComponent, AfterInteractEvent>(OnAfterInteract); SubscribeLocalEvent<BotanySwabComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<BotanySwabComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<BotanySwabComponent, BotanySwabDoAfterEvent>(OnDoAfter);
} }
/// <summary> /// <summary>
@@ -44,12 +44,11 @@ public sealed class BotanySwabSystem : EntitySystem
if (args.Target == null || !args.CanReach || !HasComp<PlantHolderComponent>(args.Target)) if (args.Target == null || !args.CanReach || !HasComp<PlantHolderComponent>(args.Target))
return; return;
_doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, swab.SwabDelay, target: args.Target, used: uid) _doAfterSystem.TryStartDoAfter(new DoAfterArgs(args.User, swab.SwabDelay, new BotanySwabDoAfterEvent(), uid, target: args.Target, used: uid)
{ {
Broadcast = true, Broadcast = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnStun = true,
NeedHand = true NeedHand = true
}); });
} }
@@ -57,9 +56,9 @@ public sealed class BotanySwabSystem : EntitySystem
/// <summary> /// <summary>
/// Save seed data or cross-pollenate. /// Save seed data or cross-pollenate.
/// </summary> /// </summary>
private void OnDoAfter(EntityUid uid, BotanySwabComponent component, DoAfterEvent args) private void OnDoAfter(EntityUid uid, BotanySwabComponent swab, DoAfterEvent args)
{ {
if (args.Cancelled || args.Handled || !TryComp<PlantHolderComponent>(args.Args.Target, out var plant) || !TryComp<BotanySwabComponent>(args.Args.Used, out var swab)) if (args.Cancelled || args.Handled || !TryComp<PlantHolderComponent>(args.Args.Target, out var plant))
return; return;
if (swab.SeedData == null) if (swab.SeedData == null)

View File

@@ -1,6 +1,7 @@
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Components.SolutionManager; using Content.Server.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database; using Content.Shared.Database;
@@ -28,7 +29,7 @@ public sealed partial class ChemistrySystem
{ {
SubscribeLocalEvent<InjectorComponent, GetVerbsEvent<AlternativeVerb>>(AddSetTransferVerbs); SubscribeLocalEvent<InjectorComponent, GetVerbsEvent<AlternativeVerb>>(AddSetTransferVerbs);
SubscribeLocalEvent<InjectorComponent, SolutionChangedEvent>(OnSolutionChange); SubscribeLocalEvent<InjectorComponent, SolutionChangedEvent>(OnSolutionChange);
SubscribeLocalEvent<InjectorComponent, DoAfterEvent>(OnInjectDoAfter); SubscribeLocalEvent<InjectorComponent, InjectorDoAfterEvent>(OnInjectDoAfter);
SubscribeLocalEvent<InjectorComponent, ComponentStartup>(OnInjectorStartup); SubscribeLocalEvent<InjectorComponent, ComponentStartup>(OnInjectorStartup);
SubscribeLocalEvent<InjectorComponent, UseInHandEvent>(OnInjectorUse); SubscribeLocalEvent<InjectorComponent, UseInHandEvent>(OnInjectorUse);
SubscribeLocalEvent<InjectorComponent, AfterInteractEvent>(OnInjectorAfterInteract); SubscribeLocalEvent<InjectorComponent, AfterInteractEvent>(OnInjectorAfterInteract);
@@ -129,18 +130,10 @@ public sealed partial class ChemistrySystem
private void OnInjectDoAfter(EntityUid uid, InjectorComponent component, DoAfterEvent args) private void OnInjectDoAfter(EntityUid uid, InjectorComponent component, DoAfterEvent args)
{ {
if (args.Cancelled) if (args.Cancelled || args.Handled || args.Args.Target == null)
{
component.IsInjecting = false;
return;
}
if (args.Handled || args.Args.Target == null)
return; return;
UseInjector(args.Args.Target.Value, args.Args.User, uid, component); UseInjector(args.Args.Target.Value, args.Args.User, uid, component);
component.IsInjecting = false;
args.Handled = true; args.Handled = true;
} }
@@ -223,10 +216,6 @@ public sealed partial class ChemistrySystem
if (!_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution)) if (!_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution))
return; return;
//If it found it's injecting
if (component.IsInjecting)
return;
var actualDelay = MathF.Max(component.Delay, 1f); var actualDelay = MathF.Max(component.Delay, 1f);
// Injections take 1 second longer per additional 5u // Injections take 1 second longer per additional 5u
@@ -269,17 +258,12 @@ public sealed partial class ChemistrySystem
_adminLogger.Add(LogType.Ingestion, $"{EntityManager.ToPrettyString(user):user} is attempting to inject themselves with a solution {SolutionContainerSystem.ToPrettyString(solution):solution}."); _adminLogger.Add(LogType.Ingestion, $"{EntityManager.ToPrettyString(user):user} is attempting to inject themselves with a solution {SolutionContainerSystem.ToPrettyString(solution):solution}.");
} }
component.IsInjecting = true; _doAfter.TryStartDoAfter(new DoAfterArgs(user, actualDelay, new InjectorDoAfterEvent(), injector, target: target, used: injector)
_doAfter.DoAfter(new DoAfterEventArgs(user, actualDelay, target:target, used:injector)
{ {
RaiseOnTarget = isTarget,
RaiseOnUser = !isTarget,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
MovementThreshold = 0.1f MovementThreshold = 0.1f,
}); });
} }
@@ -434,4 +418,5 @@ public sealed partial class ChemistrySystem
Dirty(component); Dirty(component);
AfterDraw(component, injector); AfterDraw(component, injector);
} }
} }

View File

@@ -1,10 +1,10 @@
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Body.Systems; using Content.Server.Body.Systems;
using Content.Server.DoAfter;
using Content.Server.Interaction; using Content.Server.Interaction;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.CombatMode; using Content.Shared.CombatMode;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.DoAfter;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
namespace Content.Server.Chemistry.EntitySystems; namespace Content.Server.Chemistry.EntitySystems;
@@ -15,7 +15,7 @@ public sealed partial class ChemistrySystem : EntitySystem
[Dependency] private readonly IEntityManager _entMan = default!; [Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly InteractionSystem _interaction = default!; [Dependency] private readonly InteractionSystem _interaction = default!;
[Dependency] private readonly BloodstreamSystem _blood = default!; [Dependency] private readonly BloodstreamSystem _blood = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly ReactiveSystem _reactiveSystem = default!; [Dependency] private readonly ReactiveSystem _reactiveSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;

View File

@@ -317,11 +317,11 @@ public sealed partial class SolutionContainerSystem : EntitySystem
return true; return true;
} }
public bool TryGetSolution(EntityUid uid, string name, public bool TryGetSolution([NotNullWhen(true)] EntityUid? uid, string name,
[NotNullWhen(true)] out Solution? solution, [NotNullWhen(true)] out Solution? solution,
SolutionContainerManagerComponent? solutionsMgr = null) SolutionContainerManagerComponent? solutionsMgr = null)
{ {
if (!Resolve(uid, ref solutionsMgr, false)) if (uid == null || !Resolve(uid.Value, ref solutionsMgr, false))
{ {
solution = null; solution = null;
return false; return false;

View File

@@ -1,6 +1,5 @@
using Content.Server.Body.Systems; using Content.Server.Body.Systems;
using Content.Server.Climbing.Components; using Content.Server.Climbing.Components;
using Content.Server.DoAfter;
using Content.Server.Interaction; using Content.Server.Interaction;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.Stunnable; using Content.Server.Stunnable;
@@ -36,7 +35,7 @@ public sealed class ClimbSystem : SharedClimbSystem
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly BodySystem _bodySystem = default!; [Dependency] private readonly BodySystem _bodySystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly FixtureSystem _fixtureSystem = default!; [Dependency] private readonly FixtureSystem _fixtureSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly InteractionSystem _interactionSystem = default!; [Dependency] private readonly InteractionSystem _interactionSystem = default!;
@@ -57,7 +56,7 @@ public sealed class ClimbSystem : SharedClimbSystem
SubscribeLocalEvent<ClimbableComponent, GetVerbsEvent<AlternativeVerb>>(AddClimbableVerb); SubscribeLocalEvent<ClimbableComponent, GetVerbsEvent<AlternativeVerb>>(AddClimbableVerb);
SubscribeLocalEvent<ClimbableComponent, DragDropTargetEvent>(OnClimbableDragDrop); SubscribeLocalEvent<ClimbableComponent, DragDropTargetEvent>(OnClimbableDragDrop);
SubscribeLocalEvent<ClimbingComponent, DoAfterEvent<ClimbExtraEvent>>(OnDoAfter); SubscribeLocalEvent<ClimbingComponent, ClimbDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<ClimbingComponent, EndCollideEvent>(OnClimbEndCollide); SubscribeLocalEvent<ClimbingComponent, EndCollideEvent>(OnClimbEndCollide);
SubscribeLocalEvent<ClimbingComponent, BuckleChangeEvent>(OnBuckleChange); SubscribeLocalEvent<ClimbingComponent, BuckleChangeEvent>(OnBuckleChange);
SubscribeLocalEvent<ClimbingComponent, ComponentGetState>(OnClimbingGetState); SubscribeLocalEvent<ClimbingComponent, ComponentGetState>(OnClimbingGetState);
@@ -114,22 +113,17 @@ public sealed class ClimbSystem : SharedClimbSystem
if (_bonkSystem.TryBonk(entityToMove, climbable)) if (_bonkSystem.TryBonk(entityToMove, climbable))
return; return;
var ev = new ClimbExtraEvent(); var args = new DoAfterArgs(user, component.ClimbDelay, new ClimbDoAfterEvent(), entityToMove, target: climbable, used: entityToMove)
var args = new DoAfterEventArgs(user, component.ClimbDelay, target: climbable, used: entityToMove)
{ {
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnDamage = true, BreakOnDamage = true
BreakOnStun = true,
RaiseOnUser = false,
RaiseOnTarget = false,
}; };
_doAfterSystem.DoAfter(args, ev); _doAfterSystem.TryStartDoAfter(args);
} }
private void OnDoAfter(EntityUid uid, ClimbingComponent component, DoAfterEvent<ClimbExtraEvent> args) private void OnDoAfter(EntityUid uid, ClimbingComponent component, ClimbDoAfterEvent args)
{ {
if (args.Handled || args.Cancelled || args.Args.Target == null || args.Args.Used == null) if (args.Handled || args.Cancelled || args.Args.Target == null || args.Args.Used == null)
return; return;
@@ -436,10 +430,6 @@ public sealed class ClimbSystem : SharedClimbSystem
_fixtureRemoveQueue.Clear(); _fixtureRemoveQueue.Clear();
} }
private sealed class ClimbExtraEvent : EntityEventArgs
{
//Honestly this is only here because otherwise this activates on every single doafter on a human
}
} }
/// <summary> /// <summary>

View File

@@ -5,6 +5,7 @@ using Content.Server.Pulling;
using Content.Shared.Construction.Components; using Content.Shared.Construction.Components;
using Content.Shared.Construction.EntitySystems; using Content.Shared.Construction.EntitySystems;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.DoAfter;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Pulling.Components; using Content.Shared.Pulling.Components;
using Content.Shared.Tools; using Content.Shared.Tools;
@@ -40,23 +41,29 @@ namespace Content.Server.Construction
private void OnUnanchorComplete(EntityUid uid, AnchorableComponent component, TryUnanchorCompletedEvent args) private void OnUnanchorComplete(EntityUid uid, AnchorableComponent component, TryUnanchorCompletedEvent args)
{ {
if (args.Cancelled || args.Used is not { } used)
return;
var xform = Transform(uid); var xform = Transform(uid);
RaiseLocalEvent(uid, new BeforeUnanchoredEvent(args.User, args.Using)); RaiseLocalEvent(uid, new BeforeUnanchoredEvent(args.User, used));
xform.Anchored = false; xform.Anchored = false;
RaiseLocalEvent(uid, new UserUnanchoredEvent(args.User, args.Using)); RaiseLocalEvent(uid, new UserUnanchoredEvent(args.User, used));
_popup.PopupEntity(Loc.GetString("anchorable-unanchored"), uid); _popup.PopupEntity(Loc.GetString("anchorable-unanchored"), uid);
_adminLogger.Add( _adminLogger.Add(
LogType.Unanchor, LogType.Unanchor,
LogImpact.Low, LogImpact.Low,
$"{EntityManager.ToPrettyString(args.User):user} unanchored {EntityManager.ToPrettyString(uid):anchored} using {EntityManager.ToPrettyString(args.Using):using}" $"{EntityManager.ToPrettyString(args.User):user} unanchored {EntityManager.ToPrettyString(uid):anchored} using {EntityManager.ToPrettyString(used):using}"
); );
} }
private void OnAnchorComplete(EntityUid uid, AnchorableComponent component, TryAnchorCompletedEvent args) private void OnAnchorComplete(EntityUid uid, AnchorableComponent component, TryAnchorCompletedEvent args)
{ {
if (args.Cancelled || args.Used is not { } used)
return;
var xform = Transform(uid); var xform = Transform(uid);
if (TryComp<PhysicsComponent>(uid, out var anchorBody) && if (TryComp<PhysicsComponent>(uid, out var anchorBody) &&
!TileFree(xform.Coordinates, anchorBody)) !TileFree(xform.Coordinates, anchorBody))
@@ -78,16 +85,16 @@ namespace Content.Server.Construction
if (component.Snap) if (component.Snap)
xform.Coordinates = xform.Coordinates.SnapToGrid(EntityManager, _mapManager); xform.Coordinates = xform.Coordinates.SnapToGrid(EntityManager, _mapManager);
RaiseLocalEvent(uid, new BeforeAnchoredEvent(args.User, args.Using)); RaiseLocalEvent(uid, new BeforeAnchoredEvent(args.User, used));
xform.Anchored = true; xform.Anchored = true;
RaiseLocalEvent(uid, new UserAnchoredEvent(args.User, args.Using)); RaiseLocalEvent(uid, new UserAnchoredEvent(args.User, used));
_popup.PopupEntity(Loc.GetString("anchorable-anchored"), uid); _popup.PopupEntity(Loc.GetString("anchorable-anchored"), uid);
_adminLogger.Add( _adminLogger.Add(
LogType.Anchor, LogType.Anchor,
LogImpact.Low, LogImpact.Low,
$"{EntityManager.ToPrettyString(args.User):user} anchored {EntityManager.ToPrettyString(uid):anchored} using {EntityManager.ToPrettyString(args.Using):using}" $"{EntityManager.ToPrettyString(args.User):user} anchored {EntityManager.ToPrettyString(uid):anchored} using {EntityManager.ToPrettyString(used):using}"
); );
} }
@@ -182,8 +189,7 @@ namespace Content.Server.Construction
return; return;
} }
var toolEvData = new ToolEventData(new TryAnchorCompletedEvent(userUid, usingUid), targetEntity:uid); _tool.UseTool(usingUid, userUid, uid, anchorable.Delay, usingTool.Qualities, new TryAnchorCompletedEvent());
_tool.UseTool(usingUid, userUid, uid, anchorable.Delay, usingTool.Qualities, toolEvData);
} }
/// <summary> /// <summary>
@@ -204,8 +210,7 @@ namespace Content.Server.Construction
if (!Valid(uid, userUid, usingUid, false)) if (!Valid(uid, userUid, usingUid, false))
return; return;
var toolEvData = new ToolEventData(new TryUnanchorCompletedEvent(userUid, usingUid), targetEntity:uid); _tool.UseTool(usingUid, userUid, uid, anchorable.Delay, usingTool.Qualities, new TryUnanchorCompletedEvent());
_tool.UseTool(usingUid, userUid, uid, anchorable.Delay, usingTool.Qualities, toolEvData);
} }
/// <summary> /// <summary>
@@ -236,32 +241,5 @@ namespace Content.Server.Construction
_adminLogger.Add(LogType.Anchor, LogImpact.Low, $"{ToPrettyString(userUid):user} is trying to anchor {ToPrettyString(uid):entity} to {transform.Coordinates:targetlocation}"); _adminLogger.Add(LogType.Anchor, LogImpact.Low, $"{ToPrettyString(userUid):user} is trying to anchor {ToPrettyString(uid):entity} to {transform.Coordinates:targetlocation}");
} }
} }
private abstract class AnchorEvent : EntityEventArgs
{
public EntityUid User;
public EntityUid Using;
protected AnchorEvent(EntityUid userUid, EntityUid usingUid)
{
User = userUid;
Using = usingUid;
}
}
private sealed class TryUnanchorCompletedEvent : AnchorEvent
{
public TryUnanchorCompletedEvent(EntityUid userUid, EntityUid usingUid) : base(userUid, usingUid)
{
}
}
private sealed class TryAnchorCompletedEvent : AnchorEvent
{
public TryAnchorCompletedEvent(EntityUid userUid, EntityUid usingUid) : base(userUid, usingUid)
{
}
}
} }
} }

View File

@@ -1,4 +1,5 @@
using Content.Shared.Construction.Prototypes; using Content.Shared.Construction.Prototypes;
using Content.Shared.DoAfter;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Construction.Components namespace Content.Server.Construction.Components
@@ -33,10 +34,12 @@ namespace Content.Server.Construction.Components
[DataField("deconstructionTarget")] [DataField("deconstructionTarget")]
public string? DeconstructionNode { get; set; } = "start"; public string? DeconstructionNode { get; set; } = "start";
[ViewVariables] [DataField("doAfter")]
public bool WaitingDoAfter { get; set; } = false; public DoAfterId? DoAfter;
[ViewVariables] [ViewVariables]
// TODO Force flush interaction queue before serializing to YAML.
// Otherwise you can end up with entities stuck in invalid states (e.g., waiting for DoAfters).
public readonly Queue<object> InteractionQueue = new(); public readonly Queue<object> InteractionQueue = new();
} }
} }

View File

@@ -21,7 +21,5 @@ namespace Content.Server.Construction.Components
[DataField("qualityNeeded", customTypeSerializer:typeof(PrototypeIdSerializer<ToolQualityPrototype>))] [DataField("qualityNeeded", customTypeSerializer:typeof(PrototypeIdSerializer<ToolQualityPrototype>))]
public string QualityNeeded = "Welding"; public string QualityNeeded = "Welding";
public bool BeingWelded;
} }
} }

View File

@@ -227,10 +227,9 @@ namespace Content.Server.Construction
return null; return null;
} }
var doAfterArgs = new DoAfterEventArgs(user, doAfterTime) var doAfterArgs = new DoAfterArgs(user, doAfterTime, new AwaitedDoAfterEvent(), null)
{ {
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true,
BreakOnTargetMove = false, BreakOnTargetMove = false,
BreakOnUserMove = true, BreakOnUserMove = true,
NeedHand = false, NeedHand = false,

View File

@@ -9,6 +9,7 @@ using Content.Shared.DoAfter;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Tools.Components; using Content.Shared.Tools.Components;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Map;
using Robust.Shared.Utility; using Robust.Shared.Utility;
#if EXCEPTION_TOLERANCE #if EXCEPTION_TOLERANCE
// ReSharper disable once RedundantUsingDirective // ReSharper disable once RedundantUsingDirective
@@ -29,19 +30,7 @@ namespace Content.Server.Construction
private void InitializeInteractions() private void InitializeInteractions()
{ {
#region DoAfter Subscriptions SubscribeLocalEvent<ConstructionComponent, ConstructionInteractDoAfterEvent>(EnqueueEvent);
// DoAfter handling.
// The ConstructionDoAfter events are meant to be raised either directed or broadcast.
// If they're raised broadcast, we will re-raise them as directed on the target.
// This allows us to easily use the DoAfter system for our purposes.
SubscribeLocalEvent<ConstructionDoAfterComplete>(OnDoAfterComplete);
SubscribeLocalEvent<ConstructionDoAfterCancelled>(OnDoAfterCancelled);
SubscribeLocalEvent<ConstructionComponent, ConstructionDoAfterComplete>(EnqueueEvent);
SubscribeLocalEvent<ConstructionComponent, ConstructionDoAfterCancelled>(EnqueueEvent);
SubscribeLocalEvent<ConstructionComponent, DoAfterEvent<ConstructionData>>(OnDoAfter);
#endregion
// Event handling. Add your subscriptions here! Just make sure they're all handled by EnqueueEvent. // Event handling. Add your subscriptions here! Just make sure they're all handled by EnqueueEvent.
SubscribeLocalEvent<ConstructionComponent, InteractUsingEvent>(EnqueueEvent, new []{typeof(AnchorableSystem)}); SubscribeLocalEvent<ConstructionComponent, InteractUsingEvent>(EnqueueEvent, new []{typeof(AnchorableSystem)});
@@ -158,11 +147,13 @@ namespace Content.Server.Construction
if (!CheckConditions(uid, edge.Conditions)) if (!CheckConditions(uid, edge.Conditions))
return HandleResult.False; return HandleResult.False;
// We can only perform the "step completed" logic if this returns true. var handle = HandleStep(uid, ev, step, validation, out var user, construction);
if (HandleStep(uid, ev, step, validation, out var user, construction) if (handle is not HandleResult.True)
is var handle and not HandleResult.True)
return handle; return handle;
// Handle step should never handle the interaction during validation.
DebugTools.Assert(!validation);
// We increase the step index, meaning we move to the next step! // We increase the step index, meaning we move to the next step!
construction.StepIndex++; construction.StepIndex++;
@@ -198,10 +189,12 @@ namespace Content.Server.Construction
// Let HandleInteraction actually handle the event for this step. // Let HandleInteraction actually handle the event for this step.
// We can only perform the rest of our logic if it returns true. // We can only perform the rest of our logic if it returns true.
if (HandleInteraction(uid, ev, step, validation, out user, construction) var handle = HandleInteraction(uid, ev, step, validation, out user, construction);
is var handle and not HandleResult.True) if (handle is not HandleResult.True)
return handle; return handle;
DebugTools.Assert(!validation);
// Actually perform the step completion actions, since the step was handled correctly. // Actually perform the step completion actions, since the step was handled correctly.
PerformActions(uid, user, step.Completed); PerformActions(uid, user, step.Completed);
@@ -225,48 +218,29 @@ namespace Content.Server.Construction
return HandleResult.False; return HandleResult.False;
// Whether this event is being re-handled after a DoAfter or not. Check DoAfterState for more info. // Whether this event is being re-handled after a DoAfter or not. Check DoAfterState for more info.
var doAfterState = validation ? DoAfterState.Validation : DoAfterState.None; var doAfterState = DoAfterState.None;
// Custom data from a prior HandleInteraction where a DoAfter was called...
object? doAfterData = null;
// The DoAfter events can only perform special logic when we're not validating events. // The DoAfter events can only perform special logic when we're not validating events.
if (ev is ConstructionInteractDoAfterEvent interactDoAfter)
{
if (!validation) if (!validation)
{ construction.DoAfter = null;
// Some events are handled specially... Such as doAfter.
switch (ev)
{
case ConstructionDoAfterComplete complete:
{
// DoAfter completed!
ev = complete.WrappedEvent;
doAfterState = DoAfterState.Completed;
doAfterData = complete.CustomData;
construction.WaitingDoAfter = false;
break;
}
case ConstructionDoAfterCancelled cancelled: if (interactDoAfter.Cancelled)
{
// DoAfter failed!
ev = cancelled.WrappedEvent;
doAfterState = DoAfterState.Cancelled;
doAfterData = cancelled.CustomData;
construction.WaitingDoAfter = false;
break;
}
}
}
// Can't perform any interactions while we're waiting for a DoAfter...
// This also makes any event validation fail.
if (construction.WaitingDoAfter)
return HandleResult.False; return HandleResult.False;
ev = new InteractUsingEvent(
interactDoAfter.User,
interactDoAfter.Used!.Value,
uid,
interactDoAfter.ClickLocation);
doAfterState = DoAfterState.Completed;
}
// The cases in this switch will handle the interaction and return // The cases in this switch will handle the interaction and return
switch (step) switch (step)
{ {
// --- CONSTRUCTION STEP EVENT HANDLING START --- // --- CONSTRUCTION STEP EVENT HANDLING START ---
#region Construction Step Event Handling #region Construction Step Event Handling
// So you want to create your own custom step for construction? // So you want to create your own custom step for construction?
@@ -282,14 +256,16 @@ namespace Content.Server.Construction
if (ev is not InteractUsingEvent interactUsing) if (ev is not InteractUsingEvent interactUsing)
break; break;
if (construction.DoAfter != null && !validation)
{
_doAfterSystem.Cancel(construction.DoAfter);
return HandleResult.False;
}
// TODO: Sanity checks. // TODO: Sanity checks.
user = interactUsing.User; user = interactUsing.User;
// If this step's DoAfter was cancelled, we just fail the interaction.
if (doAfterState == DoAfterState.Cancelled)
return HandleResult.False;
var insert = interactUsing.Used; var insert = interactUsing.Used;
// Since many things inherit this step, we delegate the "is this entity valid?" logic to them. // Since many things inherit this step, we delegate the "is this entity valid?" logic to them.
@@ -298,29 +274,34 @@ namespace Content.Server.Construction
return HandleResult.False; return HandleResult.False;
// If we're only testing whether this step would be handled by the given event, then we're done. // If we're only testing whether this step would be handled by the given event, then we're done.
if (doAfterState == DoAfterState.Validation) if (validation)
return HandleResult.Validated; return HandleResult.Validated;
// If we still haven't completed this step's DoAfter... // If we still haven't completed this step's DoAfter...
if (doAfterState == DoAfterState.None && insertStep.DoAfter > 0) if (doAfterState == DoAfterState.None && insertStep.DoAfter > 0)
{ {
// These events will be broadcast and handled by this very same system, that will var doAfterEv = new ConstructionInteractDoAfterEvent(interactUsing);
// raise them directed to the target. These events wrap the original event.
var constructionData = new ConstructionData(new ConstructionDoAfterComplete(uid, ev), new ConstructionDoAfterCancelled(uid, ev)); var doAfterEventArgs = new DoAfterArgs(interactUsing.User, step.DoAfter, doAfterEv, uid, uid, interactUsing.Used)
var doAfterEventArgs = new DoAfterEventArgs(interactUsing.User, step.DoAfter, target: interactUsing.Target)
{ {
BreakOnDamage = false, BreakOnDamage = false,
BreakOnStun = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
NeedHand = true NeedHand = true
}; };
_doAfterSystem.DoAfter(doAfterEventArgs, constructionData); var started = _doAfterSystem.TryStartDoAfter(doAfterEventArgs, out construction.DoAfter);
// To properly signal that we're waiting for a DoAfter, we have to set the flag on the component if (!started)
// and then also return the DoAfter HandleResult. return HandleResult.False;
construction.WaitingDoAfter = true;
#if DEBUG
// Verify that the resulting DoAfter event will be handled by the current construction state.
// if it can't what is even the point of raising this DoAfter?
doAfterEv.DoAfter = new(default, doAfterEventArgs, default);
var result = HandleInteraction(uid, doAfterEv, step, validation: true, out _, construction);
DebugTools.Assert(result == HandleResult.Validated);
#endif
return HandleResult.DoAfter; return HandleResult.DoAfter;
} }
@@ -362,40 +343,47 @@ namespace Content.Server.Construction
if (ev is not InteractUsingEvent interactUsing) if (ev is not InteractUsingEvent interactUsing)
break; break;
if (construction.DoAfter != null && !validation)
{
_doAfterSystem.Cancel(construction.DoAfter);
return HandleResult.False;
}
// TODO: Sanity checks. // TODO: Sanity checks.
user = interactUsing.User; user = interactUsing.User;
// If we're validating whether this event handles the step... // If we're validating whether this event handles the step...
if (doAfterState == DoAfterState.Validation) if (validation)
{ {
// Then we only really need to check whether the tool entity has that quality or not. // Then we only really need to check whether the tool entity has that quality or not.
// TODO fuel consumption?
return _toolSystem.HasQuality(interactUsing.Used, toolInsertStep.Tool) return _toolSystem.HasQuality(interactUsing.Used, toolInsertStep.Tool)
? HandleResult.Validated : HandleResult.False; ? HandleResult.Validated
: HandleResult.False;
} }
// If we're handling an event after its DoAfter finished... // If we're handling an event after its DoAfter finished...
if (doAfterState != DoAfterState.None) if (doAfterState == DoAfterState.Completed)
return doAfterState == DoAfterState.Completed ? HandleResult.True : HandleResult.False;
var toolEvData = new ToolEventData(new ConstructionDoAfterComplete(uid, ev), toolInsertStep.Fuel, new ConstructionDoAfterCancelled(uid, ev));
if(!_toolSystem.UseTool(interactUsing.Used, interactUsing.User, uid, toolInsertStep.DoAfter, new [] {toolInsertStep.Tool}, toolEvData))
return HandleResult.False;
// In the case we're not waiting for a doAfter, then this step is complete!
if (toolInsertStep.DoAfter <= 0)
return HandleResult.True; return HandleResult.True;
construction.WaitingDoAfter = true; var result = _toolSystem.UseTool(
return HandleResult.DoAfter; interactUsing.Used,
interactUsing.User,
uid,
TimeSpan.FromSeconds(toolInsertStep.DoAfter),
new [] { toolInsertStep.Tool },
new ConstructionInteractDoAfterEvent(interactUsing),
out construction.DoAfter,
fuel: toolInsertStep.Fuel);
return construction.DoAfter != null ? HandleResult.DoAfter : HandleResult.False;
} }
case TemperatureConstructionGraphStep temperatureChangeStep: case TemperatureConstructionGraphStep temperatureChangeStep:
{ {
if (ev is not OnTemperatureChangeEvent) { if (ev is not OnTemperatureChangeEvent)
break; break;
}
if (TryComp<TemperatureComponent>(uid, out var tempComp)) if (TryComp<TemperatureComponent>(uid, out var tempComp))
{ {
@@ -550,7 +538,8 @@ namespace Content.Server.Construction
/// in which case they will also be set as handled.</remarks> /// in which case they will also be set as handled.</remarks>
private void EnqueueEvent(EntityUid uid, ConstructionComponent construction, object args) private void EnqueueEvent(EntityUid uid, ConstructionComponent construction, object args)
{ {
// Handled events get treated specially. // For handled events, we will check if the event leads to a valid construction interaction.
// If it does, we mark the event as handled and then enqueue it as normal.
if (args is HandledEntityEventArgs handled) if (args is HandledEntityEventArgs handled)
{ {
// If they're already handled, we do nothing. // If they're already handled, we do nothing.
@@ -572,95 +561,6 @@ namespace Content.Server.Construction
if (_queuedUpdates.Add(uid)) if (_queuedUpdates.Add(uid))
_constructionUpdateQueue.Enqueue(uid); _constructionUpdateQueue.Enqueue(uid);
} }
private void OnDoAfter(EntityUid uid, ConstructionComponent component, DoAfterEvent<ConstructionData> args)
{
if (!Exists(args.Args.Target) || args.Handled)
return;
if (args.Cancelled)
{
RaiseLocalEvent(args.Args.Target.Value, args.AdditionalData.CancelEvent);
args.Handled = true;
return;
}
RaiseLocalEvent(args.Args.Target.Value, args.AdditionalData.CompleteEvent);
args.Handled = true;
}
private void OnDoAfterComplete(ConstructionDoAfterComplete ev)
{
// Make extra sure the target entity exists...
if (!Exists(ev.TargetUid))
return;
// Re-raise this event, but directed on the target UID.
RaiseLocalEvent(ev.TargetUid, ev, false);
}
private void OnDoAfterCancelled(ConstructionDoAfterCancelled ev)
{
// Make extra sure the target entity exists...
if (!Exists(ev.TargetUid))
return;
// Re-raise this event, but directed on the target UID.
RaiseLocalEvent(ev.TargetUid, ev, false);
}
#endregion
#region Event Definitions
private sealed class ConstructionData
{
public readonly object CompleteEvent;
public readonly object CancelEvent;
public ConstructionData(object completeEvent, object cancelEvent)
{
CompleteEvent = completeEvent;
CancelEvent = cancelEvent;
}
}
/// <summary>
/// This event signals that a construction interaction's DoAfter has completed successfully.
/// This wraps the original event and also keeps some custom data that event handlers might need.
/// </summary>
private sealed class ConstructionDoAfterComplete : EntityEventArgs
{
public readonly EntityUid TargetUid;
public readonly object WrappedEvent;
public readonly object? CustomData;
public ConstructionDoAfterComplete(EntityUid targetUid, object wrappedEvent, object? customData = null)
{
TargetUid = targetUid;
WrappedEvent = wrappedEvent;
CustomData = customData;
}
}
/// <summary>
/// This event signals that a construction interaction's DoAfter has failed or has been cancelled.
/// This wraps the original event and also keeps some custom data that event handlers might need.
/// </summary>
private sealed class ConstructionDoAfterCancelled : EntityEventArgs
{
public readonly EntityUid TargetUid;
public readonly object WrappedEvent;
public readonly object? CustomData;
public ConstructionDoAfterCancelled(EntityUid targetUid, object wrappedEvent, object? customData = null)
{
TargetUid = targetUid;
WrappedEvent = wrappedEvent;
CustomData = customData;
}
}
#endregion #endregion
#region Internal Enum Definitions #region Internal Enum Definitions
@@ -676,23 +576,11 @@ namespace Content.Server.Construction
/// </summary> /// </summary>
None, None,
/// <summary>
/// If Validation, we want to validate whether the specified event would handle the step or not.
/// Will NOT modify the construction state at all.
/// </summary>
Validation,
/// <summary> /// <summary>
/// If Completed, this is the second (and last) time we're seeing this event, and /// If Completed, this is the second (and last) time we're seeing this event, and
/// the doAfter that was called the first time successfully completed. Handle completion logic now. /// the doAfter that was called the first time successfully completed. Handle completion logic now.
/// </summary> /// </summary>
Completed, Completed
/// <summary>
/// If Cancelled, this is the second (and last) time we're seeing this event, and
/// the doAfter that was called the first time was cancelled. Handle cleanup logic now.
/// </summary>
Cancelled
} }
/// <summary> /// <summary>

View File

@@ -1,7 +1,7 @@
using Content.Server.Construction.Components; using Content.Server.Construction.Components;
using Content.Server.DoAfter;
using Content.Server.Stack; using Content.Server.Stack;
using Content.Shared.Construction; using Content.Shared.Construction;
using Content.Shared.DoAfter;
using Content.Shared.Tools; using Content.Shared.Tools;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.Containers; using Robust.Server.Containers;
@@ -19,7 +19,7 @@ namespace Content.Server.Construction
[Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly ContainerSystem _container = default!; [Dependency] private readonly ContainerSystem _container = default!;
[Dependency] private readonly StackSystem _stackSystem = default!; [Dependency] private readonly StackSystem _stackSystem = default!;
[Dependency] private readonly SharedToolSystem _toolSystem = default!; [Dependency] private readonly SharedToolSystem _toolSystem = default!;

View File

@@ -1,10 +1,10 @@
using System.Linq; using System.Linq;
using Content.Server.Construction.Components; using Content.Server.Construction.Components;
using Content.Server.DoAfter;
using Content.Server.Storage.Components; using Content.Server.Storage.Components;
using Content.Server.Storage.EntitySystems; using Content.Server.Storage.EntitySystems;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Construction.Components; using Content.Shared.Construction.Components;
using Content.Shared.Exchanger;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Popups; using Content.Shared.Popups;
using Robust.Shared.Containers; using Robust.Shared.Containers;
@@ -16,7 +16,7 @@ namespace Content.Server.Construction;
public sealed class PartExchangerSystem : EntitySystem public sealed class PartExchangerSystem : EntitySystem
{ {
[Dependency] private readonly ConstructionSystem _construction = default!; [Dependency] private readonly ConstructionSystem _construction = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
@@ -26,18 +26,14 @@ public sealed class PartExchangerSystem : EntitySystem
public override void Initialize() public override void Initialize()
{ {
SubscribeLocalEvent<PartExchangerComponent, AfterInteractEvent>(OnAfterInteract); SubscribeLocalEvent<PartExchangerComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<PartExchangerComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<PartExchangerComponent, ExchangerDoAfterEvent>(OnDoAfter);
} }
private void OnDoAfter(EntityUid uid, PartExchangerComponent component, DoAfterEvent args) private void OnDoAfter(EntityUid uid, PartExchangerComponent component, DoAfterEvent args)
{ {
component.AudioStream?.Stop();
if (args.Cancelled || args.Handled || args.Args.Target == null) if (args.Cancelled || args.Handled || args.Args.Target == null)
{
component.AudioStream?.Stop();
return; return;
}
component.AudioStream?.Stop();
if (!TryComp<MachineComponent>(args.Args.Target.Value, out var machine)) if (!TryComp<MachineComponent>(args.Args.Target.Value, out var machine))
return; return;
@@ -112,11 +108,11 @@ public sealed class PartExchangerSystem : EntitySystem
component.AudioStream = _audio.PlayPvs(component.ExchangeSound, uid); component.AudioStream = _audio.PlayPvs(component.ExchangeSound, uid);
_doAfter.DoAfter(new DoAfterEventArgs(args.User, component.ExchangeDuration, target:args.Target, used:args.Used) _doAfter.TryStartDoAfter(new DoAfterArgs(args.User, component.ExchangeDuration, new ExchangerDoAfterEvent(), uid, target: args.Target, used: uid)
{ {
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true,
BreakOnUserMove = true BreakOnUserMove = true
}); });
} }
} }

View File

@@ -1,9 +1,11 @@
using Content.Server.Construction.Components; using Content.Server.Construction.Components;
using Content.Server.Stack; using Content.Server.Stack;
using Content.Shared.Construction;
using Content.Shared.DoAfter;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Stacks; using Content.Shared.Stacks;
using Content.Shared.Tools; using Content.Shared.Tools;
using Content.Shared.Tools.Components; using Robust.Shared.Serialization;
namespace Content.Server.Construction namespace Content.Server.Construction
{ {
@@ -15,29 +17,22 @@ namespace Content.Server.Construction
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<WelderRefinableComponent, InteractUsingEvent>(OnInteractUsing); SubscribeLocalEvent<WelderRefinableComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<WelderRefinableComponent, WelderRefineDoAfterEvent>(OnDoAfter);
} }
private async void OnInteractUsing(EntityUid uid, WelderRefinableComponent component, InteractUsingEvent args) private void OnInteractUsing(EntityUid uid, WelderRefinableComponent component, InteractUsingEvent args)
{ {
// check if object is welder if (args.Handled)
if (!HasComp<ToolComponent>(args.Used))
return; return;
// check if someone is already welding object args.Handled = _toolSystem.UseTool(args.Used, args.User, uid, component.RefineTime, component.QualityNeeded, new WelderRefineDoAfterEvent(), component.RefineFuel);
if (component.BeingWelded)
return;
component.BeingWelded = true;
var toolEvData = new ToolEventData(null);
if (!_toolSystem.UseTool(args.Used, args.User, uid, component.RefineTime, component.QualityNeeded, toolEvData, component.RefineFuel))
{
// failed to veld - abort refine
component.BeingWelded = false;
return;
} }
private void OnDoAfter(EntityUid uid, WelderRefinableComponent component, WelderRefineDoAfterEvent args)
{
if (args.Cancelled)
return;
// get last owner coordinates and delete it // get last owner coordinates and delete it
var resultPosition = Transform(uid).Coordinates; var resultPosition = Transform(uid).Coordinates;
EntityManager.DeleteEntity(uid); EntityManager.DeleteEntity(uid);
@@ -50,7 +45,7 @@ namespace Content.Server.Construction
// TODO: If something has a stack... Just use a prototype with a single thing in the stack. // TODO: If something has a stack... Just use a prototype with a single thing in the stack.
// This is not a good way to do it. // This is not a good way to do it.
if (TryComp<StackComponent?>(droppedEnt, out var stack)) if (TryComp<StackComponent?>(droppedEnt, out var stack))
_stackSystem.SetCount(droppedEnt,1, stack); _stackSystem.SetCount(droppedEnt, 1, stack);
} }
} }
} }

View File

@@ -18,7 +18,7 @@ namespace Content.Server.Cuffs
private void OnHandcuffGetState(EntityUid uid, HandcuffComponent component, ref ComponentGetState args) private void OnHandcuffGetState(EntityUid uid, HandcuffComponent component, ref ComponentGetState args)
{ {
args.State = new HandcuffComponentState(component.OverlayIconState, component.Cuffing); args.State = new HandcuffComponentState(component.OverlayIconState);
} }
private void OnCuffableGetState(EntityUid uid, CuffableComponent component, ref ComponentGetState args) private void OnCuffableGetState(EntityUid uid, CuffableComponent component, ref ComponentGetState args)
@@ -33,7 +33,6 @@ namespace Content.Server.Cuffs
TryComp(component.LastAddedCuffs, out cuffs); TryComp(component.LastAddedCuffs, out cuffs);
args.State = new CuffableComponentState(component.CuffedHandCount, args.State = new CuffableComponentState(component.CuffedHandCount,
component.CanStillInteract, component.CanStillInteract,
component.Uncuffing,
cuffs?.CuffedRSI, cuffs?.CuffedRSI,
$"{cuffs?.OverlayIconState}-{component.CuffedHandCount}", $"{cuffs?.OverlayIconState}-{component.CuffedHandCount}",
cuffs?.Color); cuffs?.Color);

View File

@@ -3,7 +3,6 @@ using Content.Shared.Disease;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Server.DoAfter;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.Hands.Components; using Content.Server.Hands.Components;
using Content.Server.Nutrition.EntitySystems; using Content.Server.Nutrition.EntitySystems;
@@ -18,7 +17,7 @@ using Content.Shared.Tools.Components;
using Content.Server.Station.Systems; using Content.Server.Station.Systems;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Robust.Server.GameObjects; using Content.Shared.Swab;
namespace Content.Server.Disease namespace Content.Server.Disease
{ {
@@ -27,7 +26,7 @@ namespace Content.Server.Disease
/// </summary> /// </summary>
public sealed class DiseaseDiagnosisSystem : EntitySystem public sealed class DiseaseDiagnosisSystem : EntitySystem
{ {
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!;
@@ -47,7 +46,7 @@ namespace Content.Server.Disease
// Private Events // Private Events
SubscribeLocalEvent<DiseaseDiagnoserComponent, DiseaseMachineFinishedEvent>(OnDiagnoserFinished); SubscribeLocalEvent<DiseaseDiagnoserComponent, DiseaseMachineFinishedEvent>(OnDiagnoserFinished);
SubscribeLocalEvent<DiseaseVaccineCreatorComponent, DiseaseMachineFinishedEvent>(OnVaccinatorFinished); SubscribeLocalEvent<DiseaseVaccineCreatorComponent, DiseaseMachineFinishedEvent>(OnVaccinatorFinished);
SubscribeLocalEvent<DiseaseSwabComponent, DoAfterEvent>(OnSwabDoAfter); SubscribeLocalEvent<DiseaseSwabComponent, DiseaseSwabDoAfterEvent>(OnSwabDoAfter);
} }
private Queue<EntityUid> AddQueue = new(); private Queue<EntityUid> AddQueue = new();
@@ -116,15 +115,10 @@ namespace Content.Server.Disease
return; return;
} }
var isTarget = args.User != args.Target; _doAfterSystem.TryStartDoAfter(new DoAfterArgs(args.User, swab.SwabDelay, new DiseaseSwabDoAfterEvent(), uid, target: args.Target, used: uid)
_doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, swab.SwabDelay, target: args.Target, used: uid)
{ {
RaiseOnTarget = isTarget,
RaiseOnUser = !isTarget,
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnStun = true,
NeedHand = true NeedHand = true
}); });
} }

View File

@@ -1,7 +1,6 @@
using Content.Server.Body.Systems; using Content.Server.Body.Systems;
using Content.Server.Chat.Systems; using Content.Server.Chat.Systems;
using Content.Server.Disease.Components; using Content.Server.Disease.Components;
using Content.Server.DoAfter;
using Content.Server.Nutrition.EntitySystems; using Content.Server.Nutrition.EntitySystems;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Clothing.Components; using Content.Shared.Clothing.Components;
@@ -37,7 +36,7 @@ namespace Content.Server.Disease
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ISerializationManager _serializationManager = default!; [Dependency] private readonly ISerializationManager _serializationManager = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
@@ -58,7 +57,7 @@ namespace Content.Server.Disease
// Handling stuff from other systems // Handling stuff from other systems
SubscribeLocalEvent<DiseaseCarrierComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier); SubscribeLocalEvent<DiseaseCarrierComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
// Private events stuff // Private events stuff
SubscribeLocalEvent<DiseaseVaccineComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<DiseaseVaccineComponent, VaccineDoAfterEvent>(OnDoAfter);
} }
private Queue<EntityUid> AddQueue = new(); private Queue<EntityUid> AddQueue = new();
@@ -276,20 +275,21 @@ namespace Content.Server.Disease
/// </summary> /// </summary>
private void OnAfterInteract(EntityUid uid, DiseaseVaccineComponent vaxx, AfterInteractEvent args) private void OnAfterInteract(EntityUid uid, DiseaseVaccineComponent vaxx, AfterInteractEvent args)
{ {
if (args.Target == null || !args.CanReach) if (args.Target == null || !args.CanReach || args.Handled)
return; return;
args.Handled = true;
if (vaxx.Used) if (vaxx.Used)
{ {
_popupSystem.PopupEntity(Loc.GetString("vaxx-already-used"), args.User, args.User); _popupSystem.PopupEntity(Loc.GetString("vaxx-already-used"), args.User, args.User);
return; return;
} }
_doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, vaxx.InjectDelay, target: args.Target, used:uid) _doAfterSystem.TryStartDoAfter(new DoAfterArgs(args.User, vaxx.InjectDelay, new VaccineDoAfterEvent(), uid, target: args.Target, used: uid)
{ {
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnStun = true,
NeedHand = true NeedHand = true
}); });
} }

View File

@@ -4,7 +4,6 @@ using Content.Server.Administration.Logs;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Disposal.Tube.Components; using Content.Server.Disposal.Tube.Components;
using Content.Server.Disposal.Unit.Components; using Content.Server.Disposal.Unit.Components;
using Content.Server.DoAfter;
using Content.Server.Hands.Components; using Content.Server.Hands.Components;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.Power.Components; using Content.Server.Power.Components;
@@ -42,7 +41,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly AppearanceSystem _appearance = default!; [Dependency] private readonly AppearanceSystem _appearance = default!;
[Dependency] private readonly AtmosphereSystem _atmosSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
@@ -79,7 +78,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
SubscribeLocalEvent<DisposalUnitComponent, GetVerbsEvent<Verb>>(AddClimbInsideVerb); SubscribeLocalEvent<DisposalUnitComponent, GetVerbsEvent<Verb>>(AddClimbInsideVerb);
// Units // Units
SubscribeLocalEvent<DisposalUnitComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<DisposalUnitComponent, DisposalDoAfterEvent>(OnDoAfter);
//UI //UI
SubscribeLocalEvent<DisposalUnitComponent, SharedDisposalUnitComponent.UiButtonPressedMessage>(OnUiButtonPressed); SubscribeLocalEvent<DisposalUnitComponent, SharedDisposalUnitComponent.UiButtonPressedMessage>(OnUiButtonPressed);
@@ -489,19 +488,15 @@ namespace Content.Server.Disposal.Unit.EntitySystems
// Can't check if our target AND disposals moves currently so we'll just check target. // Can't check if our target AND disposals moves currently so we'll just check target.
// if you really want to check if disposals moves then add a predicate. // if you really want to check if disposals moves then add a predicate.
var doAfterArgs = new DoAfterEventArgs(userId.Value, delay, target:toInsertId, used:unitId) var doAfterArgs = new DoAfterArgs(userId.Value, delay, new DisposalDoAfterEvent(), unitId, target: toInsertId, used: unitId)
{ {
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
NeedHand = false, NeedHand = false
RaiseOnTarget = false,
RaiseOnUser = false,
RaiseOnUsed = true,
}; };
_doAfterSystem.DoAfter(doAfterArgs); _doAfterSystem.TryStartDoAfter(doAfterArgs);
return true; return true;
} }

View File

@@ -1,5 +1,4 @@
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Mobs;
using JetBrains.Annotations; using JetBrains.Annotations;
namespace Content.Server.DoAfter; namespace Content.Server.DoAfter;

View File

@@ -20,6 +20,7 @@ using Content.Server.Power.EntitySystems;
using Content.Shared.Tools; using Content.Shared.Tools;
using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Events;
using Content.Shared.DoAfter;
namespace Content.Server.Doors.Systems; namespace Content.Server.Doors.Systems;
@@ -41,8 +42,7 @@ public sealed class DoorSystem : SharedDoorSystem
// Mob prying doors // Mob prying doors
SubscribeLocalEvent<DoorComponent, GetVerbsEvent<AlternativeVerb>>(OnDoorAltVerb); SubscribeLocalEvent<DoorComponent, GetVerbsEvent<AlternativeVerb>>(OnDoorAltVerb);
SubscribeLocalEvent<DoorComponent, PryFinishedEvent>(OnPryFinished); SubscribeLocalEvent<DoorComponent, DoorPryDoAfterEvent>(OnPryFinished);
SubscribeLocalEvent<DoorComponent, PryCancelledEvent>(OnPryCancelled);
SubscribeLocalEvent<DoorComponent, WeldableAttemptEvent>(OnWeldAttempt); SubscribeLocalEvent<DoorComponent, WeldableAttemptEvent>(OnWeldAttempt);
SubscribeLocalEvent<DoorComponent, WeldableChangedEvent>(OnWeldChanged); SubscribeLocalEvent<DoorComponent, WeldableChangedEvent>(OnWeldChanged);
SubscribeLocalEvent<DoorComponent, GotEmaggedEvent>(OnEmagged); SubscribeLocalEvent<DoorComponent, GotEmaggedEvent>(OnEmagged);
@@ -174,9 +174,6 @@ public sealed class DoorSystem : SharedDoorSystem
/// </summary> /// </summary>
public bool TryPryDoor(EntityUid target, EntityUid tool, EntityUid user, DoorComponent door, bool force = false) public bool TryPryDoor(EntityUid target, EntityUid tool, EntityUid user, DoorComponent door, bool force = false)
{ {
if (door.BeingPried)
return false;
if (door.State == DoorState.Welded) if (door.State == DoorState.Welded)
return false; return false;
@@ -194,20 +191,14 @@ public sealed class DoorSystem : SharedDoorSystem
var modEv = new DoorGetPryTimeModifierEvent(user); var modEv = new DoorGetPryTimeModifierEvent(user);
RaiseLocalEvent(target, modEv, false); RaiseLocalEvent(target, modEv, false);
door.BeingPried = true; _toolSystem.UseTool(tool, user, target, modEv.PryTimeModifier * door.PryTime, door.PryingQuality, new DoorPryDoAfterEvent());
var toolEvData = new ToolEventData(new PryFinishedEvent(), cancelledEv: new PryCancelledEvent(),targetEntity: target);
_toolSystem.UseTool(tool, user, target, modEv.PryTimeModifier * door.PryTime, new[] { door.PryingQuality }, toolEvData);
return true; // we might not actually succeeded, but a do-after has started return true; // we might not actually succeeded, but a do-after has started
} }
private void OnPryCancelled(EntityUid uid, DoorComponent door, PryCancelledEvent args) private void OnPryFinished(EntityUid uid, DoorComponent door, DoAfterEvent args)
{ {
door.BeingPried = false; if (args.Cancelled)
} return;
private void OnPryFinished(EntityUid uid, DoorComponent door, PryFinishedEvent args)
{
door.BeingPried = false;
if (door.State == DoorState.Closed) if (door.State == DoorState.Closed)
StartOpening(uid, door); StartOpening(uid, door);
@@ -309,6 +300,3 @@ public sealed class DoorSystem : SharedDoorSystem
} }
} }
public sealed class PryFinishedEvent : EntityEventArgs { }
public sealed class PryCancelledEvent : EntityEventArgs { }

View File

@@ -1,11 +1,9 @@
using Content.Server.Body.Systems; using Content.Server.Body.Systems;
using Content.Server.DoAfter;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Actions; using Content.Shared.Actions;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Player; using Robust.Shared.Player;
using System.Threading;
using Content.Server.Chat.Systems; using Content.Server.Chat.Systems;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules;
@@ -33,7 +31,7 @@ namespace Content.Server.Dragon
[Dependency] private readonly ITileDefinitionManager _tileDef = default!; [Dependency] private readonly ITileDefinitionManager _tileDef = default!;
[Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!; [Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!; [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!; [Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
@@ -63,7 +61,7 @@ namespace Content.Server.Dragon
SubscribeLocalEvent<DragonComponent, DragonSpawnRiftActionEvent>(OnDragonRift); SubscribeLocalEvent<DragonComponent, DragonSpawnRiftActionEvent>(OnDragonRift);
SubscribeLocalEvent<DragonComponent, RefreshMovementSpeedModifiersEvent>(OnDragonMove); SubscribeLocalEvent<DragonComponent, RefreshMovementSpeedModifiersEvent>(OnDragonMove);
SubscribeLocalEvent<DragonComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<DragonComponent, DragonDevourDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<DragonComponent, MobStateChangedEvent>(OnMobStateChanged); SubscribeLocalEvent<DragonComponent, MobStateChangedEvent>(OnMobStateChanged);
@@ -75,7 +73,7 @@ namespace Content.Server.Dragon
SubscribeLocalEvent<RoundEndTextAppendEvent>(OnRiftRoundEnd); SubscribeLocalEvent<RoundEndTextAppendEvent>(OnRiftRoundEnd);
} }
private void OnDoAfter(EntityUid uid, DragonComponent component, DoAfterEvent args) private void OnDoAfter(EntityUid uid, DragonComponent component, DragonDevourDoAfterEvent args)
{ {
if (args.Handled || args.Cancelled) if (args.Handled || args.Cancelled)
return; return;
@@ -95,8 +93,7 @@ namespace Content.Server.Dragon
else if (args.Args.Target != null) else if (args.Args.Target != null)
EntityManager.QueueDeleteEntity(args.Args.Target.Value); EntityManager.QueueDeleteEntity(args.Args.Target.Value);
if (component.SoundDevour != null) _audioSystem.PlayPvs(component.SoundDevour, uid);
_audioSystem.PlayPvs(component.SoundDevour, uid, component.SoundDevour.Params);
} }
public override void Update(float frameTime) public override void Update(float frameTime)
@@ -355,11 +352,10 @@ namespace Content.Server.Dragon
case MobState.Critical: case MobState.Critical:
case MobState.Dead: case MobState.Dead:
_doAfterSystem.DoAfter(new DoAfterEventArgs(uid, component.DevourTime, target:target) _doAfterSystem.TryStartDoAfter(new DoAfterArgs(uid, component.DevourTime, new DragonDevourDoAfterEvent(), uid, target: target, used: uid)
{ {
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnStun = true,
}); });
break; break;
default: default:
@@ -375,11 +371,10 @@ namespace Content.Server.Dragon
if (component.SoundStructureDevour != null) if (component.SoundStructureDevour != null)
_audioSystem.PlayPvs(component.SoundStructureDevour, uid, component.SoundStructureDevour.Params); _audioSystem.PlayPvs(component.SoundStructureDevour, uid, component.SoundStructureDevour.Params);
_doAfterSystem.DoAfter(new DoAfterEventArgs(uid, component.StructureDevourTime, target:target) _doAfterSystem.TryStartDoAfter(new DoAfterArgs(uid, component.StructureDevourTime, new DragonDevourDoAfterEvent(), uid, target: target, used: uid)
{ {
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnStun = true,
}); });
} }
} }

View File

@@ -12,7 +12,5 @@ namespace Content.Server.Engineering.Components
[DataField("doAfter")] [DataField("doAfter")]
public float DoAfterTime = 0; public float DoAfterTime = 0;
public CancellationTokenSource TokenSource { get; } = new();
} }
} }

View File

@@ -1,4 +1,3 @@
using Content.Server.DoAfter;
using Content.Server.Engineering.Components; using Content.Server.Engineering.Components;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Hands.EntitySystems; using Content.Shared.Hands.EntitySystems;
@@ -41,18 +40,16 @@ namespace Content.Server.Engineering.EntitySystems
if (string.IsNullOrEmpty(component.Prototype)) if (string.IsNullOrEmpty(component.Prototype))
return; return;
if (component.DoAfterTime > 0 && TryGet<DoAfterSystem>(out var doAfterSystem)) if (component.DoAfterTime > 0 && TryGet<SharedDoAfterSystem>(out var doAfterSystem))
{ {
var doAfterArgs = new DoAfterEventArgs(user, component.DoAfterTime, component.TokenSource.Token) var doAfterArgs = new DoAfterArgs(user, component.DoAfterTime, new AwaitedDoAfterEvent(), null)
{ {
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnStun = true,
}; };
var result = await doAfterSystem.WaitDoAfter(doAfterArgs); var result = await doAfterSystem.WaitDoAfter(doAfterArgs);
if (result != DoAfterStatus.Finished) if (result != DoAfterStatus.Finished)
return; return;
component.TokenSource.Cancel();
} }
if (component.Deleted || Deleted(component.Owner)) if (component.Deleted || Deleted(component.Owner))

View File

@@ -1,5 +1,4 @@
using Content.Server.Coordinates.Helpers; using Content.Server.Coordinates.Helpers;
using Content.Server.DoAfter;
using Content.Server.Engineering.Components; using Content.Server.Engineering.Components;
using Content.Server.Stack; using Content.Server.Stack;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
@@ -15,7 +14,7 @@ namespace Content.Server.Engineering.EntitySystems
public sealed class SpawnAfterInteractSystem : EntitySystem public sealed class SpawnAfterInteractSystem : EntitySystem
{ {
[Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly StackSystem _stackSystem = default!; [Dependency] private readonly StackSystem _stackSystem = default!;
public override void Initialize() public override void Initialize()
@@ -46,11 +45,9 @@ namespace Content.Server.Engineering.EntitySystems
if (component.DoAfterTime > 0) if (component.DoAfterTime > 0)
{ {
var doAfterArgs = new DoAfterEventArgs(args.User, component.DoAfterTime) var doAfterArgs = new DoAfterArgs(args.User, component.DoAfterTime, new AwaitedDoAfterEvent(), null)
{ {
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnStun = true,
PostCheck = IsTileClear,
}; };
var result = await _doAfterSystem.WaitDoAfter(doAfterArgs); var result = await _doAfterSystem.WaitDoAfter(doAfterArgs);
@@ -58,7 +55,7 @@ namespace Content.Server.Engineering.EntitySystems
return; return;
} }
if (component.Deleted || Deleted(component.Owner)) if (component.Deleted || !IsTileClear())
return; return;
if (EntityManager.TryGetComponent<StackComponent?>(component.Owner, out var stackComp) if (EntityManager.TryGetComponent<StackComponent?>(component.Owner, out var stackComp)

View File

@@ -1,7 +1,6 @@
using System.Threading;
using Content.Server.DoAfter;
using Content.Shared.Alert; using Content.Shared.Alert;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Ensnaring;
using Content.Shared.Ensnaring.Components; using Content.Shared.Ensnaring.Components;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.StepTrigger.Systems; using Content.Shared.StepTrigger.Systems;
@@ -11,7 +10,7 @@ namespace Content.Server.Ensnaring;
public sealed partial class EnsnareableSystem public sealed partial class EnsnareableSystem
{ {
[Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly AlertsSystem _alerts = default!; [Dependency] private readonly AlertsSystem _alerts = default!;
public void InitializeEnsnaring() public void InitializeEnsnaring()
@@ -74,40 +73,35 @@ public sealed partial class EnsnareableSystem
/// <summary> /// <summary>
/// Used where you want to try to free an entity with the <see cref="EnsnareableComponent"/> /// Used where you want to try to free an entity with the <see cref="EnsnareableComponent"/>
/// </summary> /// </summary>
/// <param name="target">The entity that will be free</param> /// <param name="target">The entity that will be freed</param>
/// <param name="user">The entity that is freeing the target</param>
/// <param name="ensnare">The entity used to ensnare</param> /// <param name="ensnare">The entity used to ensnare</param>
/// <param name="component">The ensnaring component</param> /// <param name="component">The ensnaring component</param>
public void TryFree(EntityUid target, EntityUid ensnare, EnsnaringComponent component, EntityUid? user = null) public void TryFree(EntityUid target, EntityUid user, EntityUid ensnare, EnsnaringComponent component)
{ {
//Don't do anything if they don't have the ensnareable component. //Don't do anything if they don't have the ensnareable component.
if (!HasComp<EnsnareableComponent>(target)) if (!HasComp<EnsnareableComponent>(target))
return; return;
var isOwner = !(user != null && target != user); var freeTime = user == target ? component.BreakoutTime : component.FreeTime;
var freeTime = isOwner ? component.BreakoutTime : component.FreeTime; var breakOnMove = user != target || !component.CanMoveBreakout;
bool breakOnMove;
if (isOwner) var doAfterEventArgs = new DoAfterArgs(user, freeTime, new EnsnareableDoAfterEvent(), target, target: target, used: ensnare)
breakOnMove = !component.CanMoveBreakout;
else
breakOnMove = true;
var doAfterEventArgs = new DoAfterEventArgs(target, freeTime, target: target, used:ensnare)
{ {
BreakOnUserMove = breakOnMove, BreakOnUserMove = breakOnMove,
BreakOnTargetMove = breakOnMove, BreakOnTargetMove = breakOnMove,
BreakOnDamage = false, BreakOnDamage = false,
BreakOnStun = true, NeedHand = true,
NeedHand = true BlockDuplicate = true,
}; };
_doAfter.DoAfter(doAfterEventArgs); if (!_doAfter.TryStartDoAfter(doAfterEventArgs))
return;
if (isOwner) if (user == target)
_popup.PopupEntity(Loc.GetString("ensnare-component-try-free", ("ensnare", ensnare)), target, target); _popup.PopupEntity(Loc.GetString("ensnare-component-try-free", ("ensnare", ensnare)), target, target);
else
if (!isOwner && user != null) _popup.PopupEntity(Loc.GetString("ensnare-component-try-free-other", ("ensnare", ensnare), ("user", Identity.Entity(target, EntityManager))), user, user);
_popup.PopupEntity(Loc.GetString("ensnare-component-try-free-other", ("ensnare", ensnare), ("user", Identity.Entity(target, EntityManager))), user.Value, user.Value);
} }
/// <summary> /// <summary>

View File

@@ -20,7 +20,7 @@ public sealed partial class EnsnareableSystem : SharedEnsnareableSystem
InitializeEnsnaring(); InitializeEnsnaring();
SubscribeLocalEvent<EnsnareableComponent, ComponentInit>(OnEnsnareableInit); SubscribeLocalEvent<EnsnareableComponent, ComponentInit>(OnEnsnareableInit);
SubscribeLocalEvent<EnsnareableComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<EnsnareableComponent, EnsnareableDoAfterEvent>(OnDoAfter);
} }
private void OnEnsnareableInit(EntityUid uid, EnsnareableComponent component, ComponentInit args) private void OnEnsnareableInit(EntityUid uid, EnsnareableComponent component, ComponentInit args)

View File

@@ -1,7 +1,5 @@
using System.Linq;
using Content.Server.Chemistry.Components.SolutionManager; using Content.Server.Chemistry.Components.SolutionManager;
using Content.Server.Chemistry.EntitySystems; using Content.Server.Chemistry.EntitySystems;
using Content.Server.DoAfter;
using Content.Server.Fluids.Components; using Content.Server.Fluids.Components;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
@@ -20,7 +18,7 @@ namespace Content.Server.Fluids.EntitySystems;
[UsedImplicitly] [UsedImplicitly]
public sealed class MoppingSystem : SharedMoppingSystem public sealed class MoppingSystem : SharedMoppingSystem
{ {
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SpillableSystem _spillableSystem = default!; [Dependency] private readonly SpillableSystem _spillableSystem = default!;
[Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly TagSystem _tagSystem = default!;
[Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IMapManager _mapManager = default!;
@@ -35,8 +33,8 @@ public sealed class MoppingSystem : SharedMoppingSystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<AbsorbentComponent, ComponentInit>(OnAbsorbentInit); SubscribeLocalEvent<AbsorbentComponent, ComponentInit>(OnAbsorbentInit);
SubscribeLocalEvent<AbsorbentComponent, AfterInteractEvent>(OnAfterInteract); SubscribeLocalEvent<AbsorbentComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<AbsorbentComponent, AbsorbantDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<AbsorbentComponent, SolutionChangedEvent>(OnAbsorbentSolutionChange); SubscribeLocalEvent<AbsorbentComponent, SolutionChangedEvent>(OnAbsorbentSolutionChange);
SubscribeLocalEvent<AbsorbentComponent, DoAfterEvent<AbsorbantData>>(OnDoAfter);
} }
private void OnAbsorbentInit(EntityUid uid, AbsorbentComponent component, ComponentInit args) private void OnAbsorbentInit(EntityUid uid, AbsorbentComponent component, ComponentInit args)
@@ -256,48 +254,34 @@ public sealed class MoppingSystem : SharedMoppingSystem
if (!component.InteractingEntities.Add(target)) if (!component.InteractingEntities.Add(target))
return; return;
var aborbantData = new AbsorbantData(targetSolution, msg, sfx, transferAmount); var ev = new AbsorbantDoAfterEvent(targetSolution, msg, sfx, transferAmount);
var doAfterArgs = new DoAfterEventArgs(user, delay, target: target, used:used) var doAfterArgs = new DoAfterArgs(user, delay, ev, used, target: target, used: used)
{ {
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnStun = true,
BreakOnDamage = true, BreakOnDamage = true,
MovementThreshold = 0.2f MovementThreshold = 0.2f
}; };
_doAfterSystem.DoAfter(doAfterArgs, aborbantData); _doAfterSystem.TryStartDoAfter(doAfterArgs);
} }
private void OnDoAfter(EntityUid uid, AbsorbentComponent component, DoAfterEvent<AbsorbantData> args) private void OnDoAfter(EntityUid uid, AbsorbentComponent component, AbsorbantDoAfterEvent args)
{ {
if (args.Args.Target == null) if (args.Target == null)
return; return;
if (args.Cancelled) component.InteractingEntities.Remove(args.Target.Value);
{
//Remove the interacting entities or else it breaks the mop
component.InteractingEntities.Remove(args.Args.Target.Value);
return;
}
if (args.Handled) if (args.Cancelled || args.Handled)
return; return;
_audio.PlayPvs(args.AdditionalData.Sound, uid); _audio.PlayPvs(args.Sound, uid);
_popups.PopupEntity(Loc.GetString(args.AdditionalData.Message, ("target", args.Args.Target.Value), ("used", uid)), uid); _popups.PopupEntity(Loc.GetString(args.Message, ("target", args.Target.Value), ("used", uid)), uid);
_solutionSystem.TryTransferSolution(args.Args.Target.Value, uid, args.AdditionalData.TargetSolution, _solutionSystem.TryTransferSolution(args.Target.Value, uid, args.TargetSolution,
AbsorbentComponent.SolutionName, args.AdditionalData.TransferAmount); AbsorbentComponent.SolutionName, args.TransferAmount);
component.InteractingEntities.Remove(args.Args.Target.Value); component.InteractingEntities.Remove(args.Target.Value);
args.Handled = true; args.Handled = true;
} }
private record struct AbsorbantData(string TargetSolution, string Message, SoundSpecifier Sound, FixedPoint2 TransferAmount)
{
public readonly string TargetSolution = TargetSolution;
public readonly string Message = Message;
public readonly SoundSpecifier Sound = Sound;
public readonly FixedPoint2 TransferAmount = TransferAmount;
}
} }

View File

@@ -1,6 +1,5 @@
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Chemistry.EntitySystems; using Content.Server.Chemistry.EntitySystems;
using Content.Server.DoAfter;
using Content.Server.Fluids.Components; using Content.Server.Fluids.Components;
using Content.Server.Nutrition.Components; using Content.Server.Nutrition.Components;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
@@ -17,6 +16,7 @@ using Robust.Shared.Prototypes;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Spillable;
namespace Content.Server.Fluids.EntitySystems; namespace Content.Server.Fluids.EntitySystems;
@@ -29,7 +29,7 @@ public sealed class SpillableSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly EntityLookupSystem _entityLookup = default!; [Dependency] private readonly EntityLookupSystem _entityLookup = default!;
[Dependency] private readonly IAdminLogManager _adminLogger= default!; [Dependency] private readonly IAdminLogManager _adminLogger= default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -38,7 +38,7 @@ public sealed class SpillableSystem : EntitySystem
SubscribeLocalEvent<SpillableComponent, GetVerbsEvent<Verb>>(AddSpillVerb); SubscribeLocalEvent<SpillableComponent, GetVerbsEvent<Verb>>(AddSpillVerb);
SubscribeLocalEvent<SpillableComponent, GotEquippedEvent>(OnGotEquipped); SubscribeLocalEvent<SpillableComponent, GotEquippedEvent>(OnGotEquipped);
SubscribeLocalEvent<SpillableComponent, SolutionSpikeOverflowEvent>(OnSpikeOverflow); SubscribeLocalEvent<SpillableComponent, SolutionSpikeOverflowEvent>(OnSpikeOverflow);
SubscribeLocalEvent<SpillableComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<SpillableComponent, SpillDoAfterEvent>(OnDoAfter);
} }
private void OnSpikeOverflow(EntityUid uid, SpillableComponent component, SolutionSpikeOverflowEvent args) private void OnSpikeOverflow(EntityUid uid, SpillableComponent component, SolutionSpikeOverflowEvent args)
@@ -128,29 +128,17 @@ public sealed class SpillableSystem : EntitySystem
Verb verb = new(); Verb verb = new();
verb.Text = Loc.GetString("spill-target-verb-get-data-text"); verb.Text = Loc.GetString("spill-target-verb-get-data-text");
// TODO VERB ICONS spill icon? pouring out a glass/beaker? // TODO VERB ICONS spill icon? pouring out a glass/beaker?
if (component.SpillDelay == null)
{
verb.Act = () => verb.Act = () =>
{ {
var puddleSolution = _solutionContainerSystem.SplitSolution(args.Target, _doAfterSystem.TryStartDoAfter(new DoAfterArgs(args.User, component.SpillDelay ?? 0, new SpillDoAfterEvent(), uid, target: uid)
solution, solution.Volume);
SpillAt(puddleSolution, Transform(args.Target).Coordinates, "PuddleSmear");
};
}
else
{
verb.Act = () =>
{
_doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, component.SpillDelay.Value, target:uid)
{ {
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true, NeedHand = true,
NeedHand = true
}); });
}; };
}
verb.Impact = LogImpact.Medium; // dangerous reagent reaction are logged separately. verb.Impact = LogImpact.Medium; // dangerous reagent reaction are logged separately.
verb.DoContactInteraction = true; verb.DoContactInteraction = true;
args.Verbs.Add(verb); args.Verbs.Add(verb);

View File

@@ -1,11 +1,10 @@
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Server.DoAfter;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Forensics;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Robust.Shared.Serialization;
namespace Content.Server.Forensics namespace Content.Server.Forensics
{ {
@@ -14,7 +13,7 @@ namespace Content.Server.Forensics
/// </summary> /// </summary>
public sealed class ForensicPadSystem : EntitySystem public sealed class ForensicPadSystem : EntitySystem
{ {
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
@@ -23,7 +22,7 @@ namespace Content.Server.Forensics
base.Initialize(); base.Initialize();
SubscribeLocalEvent<ForensicPadComponent, ExaminedEvent>(OnExamined); SubscribeLocalEvent<ForensicPadComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<ForensicPadComponent, AfterInteractEvent>(OnAfterInteract); SubscribeLocalEvent<ForensicPadComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<ForensicPadComponent, DoAfterEvent<ForensicPadData>>(OnDoAfter); SubscribeLocalEvent<ForensicPadComponent, ForensicPadDoAfterEvent>(OnDoAfter);
} }
private void OnExamined(EntityUid uid, ForensicPadComponent component, ExaminedEvent args) private void OnExamined(EntityUid uid, ForensicPadComponent component, ExaminedEvent args)
@@ -79,25 +78,21 @@ namespace Content.Server.Forensics
private void StartScan(EntityUid used, EntityUid user, EntityUid target, ForensicPadComponent pad, string sample) private void StartScan(EntityUid used, EntityUid user, EntityUid target, ForensicPadComponent pad, string sample)
{ {
var padData = new ForensicPadData(sample); var ev = new ForensicPadDoAfterEvent(sample);
var doAfterEventArgs = new DoAfterEventArgs(user, pad.ScanDelay, target: target, used: used) var doAfterEventArgs = new DoAfterArgs(user, pad.ScanDelay, ev, used, target: target, used: used)
{ {
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnStun = true, NeedHand = true
NeedHand = true,
RaiseOnUser = false
}; };
_doAfterSystem.DoAfter(doAfterEventArgs, padData); _doAfterSystem.TryStartDoAfter(doAfterEventArgs);
} }
private void OnDoAfter(EntityUid uid, ForensicPadComponent component, DoAfterEvent<ForensicPadData> args) private void OnDoAfter(EntityUid uid, ForensicPadComponent padComponent, ForensicPadDoAfterEvent args)
{ {
if (args.Handled if (args.Handled || args.Cancelled)
|| args.Cancelled
|| !EntityManager.TryGetComponent(args.Args.Used, out ForensicPadComponent? padComponent))
{ {
return; return;
} }
@@ -110,20 +105,10 @@ namespace Content.Server.Forensics
MetaData(uid).EntityName = Loc.GetString("forensic-pad-gloves-name", ("entity", args.Args.Target)); MetaData(uid).EntityName = Loc.GetString("forensic-pad-gloves-name", ("entity", args.Args.Target));
} }
padComponent.Sample = args.AdditionalData.Sample; padComponent.Sample = args.Sample;
padComponent.Used = true; padComponent.Used = true;
args.Handled = true; args.Handled = true;
} }
private sealed class ForensicPadData
{
public string Sample;
public ForensicPadData(string sample)
{
Sample = sample;
}
}
} }
} }

View File

@@ -3,7 +3,6 @@ using System.Text; // todo: remove this stinky LINQy
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Content.Server.DoAfter;
using Content.Server.Paper; using Content.Server.Paper;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.UserInterface; using Content.Server.UserInterface;
@@ -18,7 +17,7 @@ namespace Content.Server.Forensics
public sealed class ForensicScannerSystem : EntitySystem public sealed class ForensicScannerSystem : EntitySystem
{ {
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly PaperSystem _paperSystem = default!; [Dependency] private readonly PaperSystem _paperSystem = default!;
@@ -39,7 +38,7 @@ namespace Content.Server.Forensics
SubscribeLocalEvent<ForensicScannerComponent, GetVerbsEvent<UtilityVerb>>(OnUtilityVerb); SubscribeLocalEvent<ForensicScannerComponent, GetVerbsEvent<UtilityVerb>>(OnUtilityVerb);
SubscribeLocalEvent<ForensicScannerComponent, ForensicScannerPrintMessage>(OnPrint); SubscribeLocalEvent<ForensicScannerComponent, ForensicScannerPrintMessage>(OnPrint);
SubscribeLocalEvent<ForensicScannerComponent, ForensicScannerClearMessage>(OnClear); SubscribeLocalEvent<ForensicScannerComponent, ForensicScannerClearMessage>(OnClear);
SubscribeLocalEvent<ForensicScannerComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<ForensicScannerComponent, ForensicScannerDoAfterEvent>(OnDoAfter);
} }
private void UpdateUserInterface(EntityUid uid, ForensicScannerComponent component) private void UpdateUserInterface(EntityUid uid, ForensicScannerComponent component)
@@ -91,11 +90,10 @@ namespace Content.Server.Forensics
/// </remarks> /// </remarks>
private void StartScan(EntityUid uid, ForensicScannerComponent component, EntityUid user, EntityUid target) private void StartScan(EntityUid uid, ForensicScannerComponent component, EntityUid user, EntityUid target)
{ {
_doAfterSystem.DoAfter(new DoAfterEventArgs(user, component.ScanDelay, target: target, used: uid) _doAfterSystem.TryStartDoAfter(new DoAfterArgs(user, component.ScanDelay, new ForensicScannerDoAfterEvent(), uid, target: target, used: uid)
{ {
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnStun = true,
NeedHand = true NeedHand = true
}); });
} }

View File

@@ -32,6 +32,7 @@ namespace Content.Server.Gatherable.Components
public int MaxGatheringEntities = 1; public int MaxGatheringEntities = 1;
[ViewVariables] [ViewVariables]
[DataField("gatheringEntities")]
public readonly List<EntityUid> GatheringEntities = new(); public readonly List<EntityUid> GatheringEntities = new();
} }
} }

View File

@@ -1,11 +1,8 @@
using System.Threading;
using Content.Server.Destructible; using Content.Server.Destructible;
using Content.Server.DoAfter;
using Content.Server.Gatherable.Components; using Content.Server.Gatherable.Components;
using Content.Shared.Damage;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Destructible;
using Content.Shared.EntityList; using Content.Shared.EntityList;
using Content.Shared.Gatherable;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Tag; using Content.Shared.Tag;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -17,9 +14,8 @@ public sealed class GatherableSystem : EntitySystem
{ {
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly DestructibleSystem _destructible = default!; [Dependency] private readonly DestructibleSystem _destructible = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly TagSystem _tagSystem = default!;
@@ -28,7 +24,7 @@ public sealed class GatherableSystem : EntitySystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<GatherableComponent, InteractUsingEvent>(OnInteractUsing); SubscribeLocalEvent<GatherableComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<GatherableComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<GatherableComponent, GatherableDoAfterEvent>(OnDoAfter);
} }
private void OnInteractUsing(EntityUid uid, GatherableComponent component, InteractUsingEvent args) private void OnInteractUsing(EntityUid uid, GatherableComponent component, InteractUsingEvent args)
@@ -44,33 +40,29 @@ public sealed class GatherableSystem : EntitySystem
var damageTime = (damageRequired / tool.Damage.Total).Float(); var damageTime = (damageRequired / tool.Damage.Total).Float();
damageTime = Math.Max(1f, damageTime); damageTime = Math.Max(1f, damageTime);
var doAfter = new DoAfterEventArgs(args.User, damageTime, target: uid, used: args.Used) var doAfter = new DoAfterArgs(args.User, damageTime, new GatherableDoAfterEvent(), uid, target: uid, used: args.Used)
{ {
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
MovementThreshold = 0.25f, MovementThreshold = 0.25f,
}; };
_doAfterSystem.DoAfter(doAfter); _doAfterSystem.TryStartDoAfter(doAfter);
} }
private void OnDoAfter(EntityUid uid, GatherableComponent component, DoAfterEvent args) private void OnDoAfter(EntityUid uid, GatherableComponent component, GatherableDoAfterEvent args)
{ {
if(!TryComp<GatheringToolComponent>(args.Args.Used, out var tool) || args.Args.Target == null) if(!TryComp<GatheringToolComponent>(args.Args.Used, out var tool) || args.Args.Target == null)
return; return;
if (args.Handled || args.Cancelled)
{
tool.GatheringEntities.Remove(args.Args.Target.Value); tool.GatheringEntities.Remove(args.Args.Target.Value);
if (args.Handled || args.Cancelled)
return; return;
}
// Complete the gathering process // Complete the gathering process
_destructible.DestroyEntity(args.Args.Target.Value); _destructible.DestroyEntity(args.Args.Target.Value);
_audio.PlayPvs(tool.GatheringSound, args.Args.Target.Value); _audio.PlayPvs(tool.GatheringSound, args.Args.Target.Value);
tool.GatheringEntities.Remove(args.Args.Target.Value);
// Spawn the loot! // Spawn the loot!
if (component.MappedLoot == null) if (component.MappedLoot == null)

View File

@@ -28,7 +28,5 @@ namespace Content.Server.Guardian
/// </summary> /// </summary>
[DataField("delay")] [DataField("delay")]
public float InjectionDelay = 5f; public float InjectionDelay = 5f;
public bool Injecting = false;
} }
} }

View File

@@ -1,10 +1,10 @@
using Content.Server.DoAfter;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Actions; using Content.Shared.Actions;
using Content.Shared.Audio; using Content.Shared.Audio;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Guardian;
using Content.Shared.Hands.EntitySystems; using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Interaction.Events; using Content.Shared.Interaction.Events;
@@ -22,7 +22,7 @@ namespace Content.Server.Guardian
/// </summary> /// </summary>
public sealed class GuardianSystem : EntitySystem public sealed class GuardianSystem : EntitySystem
{ {
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly DamageableSystem _damageSystem = default!; [Dependency] private readonly DamageableSystem _damageSystem = default!;
[Dependency] private readonly SharedActionsSystem _actionSystem = default!; [Dependency] private readonly SharedActionsSystem _actionSystem = default!;
@@ -35,7 +35,7 @@ namespace Content.Server.Guardian
SubscribeLocalEvent<GuardianCreatorComponent, UseInHandEvent>(OnCreatorUse); SubscribeLocalEvent<GuardianCreatorComponent, UseInHandEvent>(OnCreatorUse);
SubscribeLocalEvent<GuardianCreatorComponent, AfterInteractEvent>(OnCreatorInteract); SubscribeLocalEvent<GuardianCreatorComponent, AfterInteractEvent>(OnCreatorInteract);
SubscribeLocalEvent<GuardianCreatorComponent, ExaminedEvent>(OnCreatorExamine); SubscribeLocalEvent<GuardianCreatorComponent, ExaminedEvent>(OnCreatorExamine);
SubscribeLocalEvent<GuardianCreatorComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<GuardianCreatorComponent, GuardianCreatorDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<GuardianComponent, MoveEvent>(OnGuardianMove); SubscribeLocalEvent<GuardianComponent, MoveEvent>(OnGuardianMove);
SubscribeLocalEvent<GuardianComponent, DamageChangedEvent>(OnGuardianDamaged); SubscribeLocalEvent<GuardianComponent, DamageChangedEvent>(OnGuardianDamaged);
@@ -161,12 +161,7 @@ namespace Content.Server.Guardian
return; return;
} }
if (component.Injecting) _doAfterSystem.TryStartDoAfter(new DoAfterArgs(user, component.InjectionDelay, new GuardianCreatorDoAfterEvent(), injector, target: target, used: injector)
return;
component.Injecting = true;
_doAfterSystem.DoAfter(new DoAfterEventArgs(user, component.InjectionDelay, target: target, used: injector)
{ {
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true BreakOnUserMove = true
@@ -179,10 +174,7 @@ namespace Content.Server.Guardian
return; return;
if (args.Cancelled || component.Deleted || component.Used || !_handsSystem.IsHolding(args.Args.User, uid, out _) || HasComp<GuardianHostComponent>(args.Args.Target)) if (args.Cancelled || component.Deleted || component.Used || !_handsSystem.IsHolding(args.Args.User, uid, out _) || HasComp<GuardianHostComponent>(args.Args.Target))
{
component.Injecting = false;
return; return;
}
var hostXform = Transform(args.Args.Target.Value); var hostXform = Transform(args.Args.Target.Value);
var host = EnsureComp<GuardianHostComponent>(args.Args.Target.Value); var host = EnsureComp<GuardianHostComponent>(args.Args.Target.Value);

View File

@@ -1,9 +1,6 @@
using System.Threading;
using Content.Server.DoAfter;
using Content.Server.Guardian; using Content.Server.Guardian;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Hands;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.Implants; using Content.Shared.Implants;
using Content.Shared.Implants.Components; using Content.Shared.Implants.Components;
@@ -18,7 +15,7 @@ namespace Content.Server.Implants;
public sealed partial class ImplanterSystem : SharedImplanterSystem public sealed partial class ImplanterSystem : SharedImplanterSystem
{ {
[Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedContainerSystem _container = default!;
public override void Initialize() public override void Initialize()
@@ -26,12 +23,11 @@ public sealed partial class ImplanterSystem : SharedImplanterSystem
base.Initialize(); base.Initialize();
InitializeImplanted(); InitializeImplanted();
SubscribeLocalEvent<ImplanterComponent, HandDeselectedEvent>(OnHandDeselect);
SubscribeLocalEvent<ImplanterComponent, AfterInteractEvent>(OnImplanterAfterInteract); SubscribeLocalEvent<ImplanterComponent, AfterInteractEvent>(OnImplanterAfterInteract);
SubscribeLocalEvent<ImplanterComponent, ComponentGetState>(OnImplanterGetState); SubscribeLocalEvent<ImplanterComponent, ComponentGetState>(OnImplanterGetState);
SubscribeLocalEvent<ImplanterComponent, DoAfterEvent<ImplantEvent>>(OnImplant); SubscribeLocalEvent<ImplanterComponent, ImplantEvent>(OnImplant);
SubscribeLocalEvent<ImplanterComponent, DoAfterEvent<DrawEvent>>(OnDraw); SubscribeLocalEvent<ImplanterComponent, DrawEvent>(OnDraw);
} }
private void OnImplanterAfterInteract(EntityUid uid, ImplanterComponent component, AfterInteractEvent args) private void OnImplanterAfterInteract(EntityUid uid, ImplanterComponent component, AfterInteractEvent args)
@@ -62,12 +58,6 @@ public sealed partial class ImplanterSystem : SharedImplanterSystem
args.Handled = true; args.Handled = true;
} }
private void OnHandDeselect(EntityUid uid, ImplanterComponent component, HandDeselectedEvent args)
{
component.CancelToken?.Cancel();
component.CancelToken = null;
}
/// <summary> /// <summary>
/// Attempt to implant someone else. /// Attempt to implant someone else.
/// </summary> /// </summary>
@@ -77,27 +67,21 @@ public sealed partial class ImplanterSystem : SharedImplanterSystem
/// <param name="implanter">The implanter being used</param> /// <param name="implanter">The implanter being used</param>
public void TryImplant(ImplanterComponent component, EntityUid user, EntityUid target, EntityUid implanter) public void TryImplant(ImplanterComponent component, EntityUid user, EntityUid target, EntityUid implanter)
{ {
if (component.CancelToken != null) var args = new DoAfterArgs(user, component.ImplantTime, new ImplantEvent(), implanter, target: target, used: implanter)
{
BreakOnUserMove = true,
BreakOnTargetMove = true,
BreakOnDamage = true,
NeedHand = true,
};
if (!_doAfter.TryStartDoAfter(args))
return; return;
_popup.PopupEntity(Loc.GetString("injector-component-injecting-user"), target, user); _popup.PopupEntity(Loc.GetString("injector-component-injecting-user"), target, user);
var userName = Identity.Entity(user, EntityManager); var userName = Identity.Entity(user, EntityManager);
_popup.PopupEntity(Loc.GetString("implanter-component-implanting-target", ("user", userName)), user, target, PopupType.LargeCaution); _popup.PopupEntity(Loc.GetString("implanter-component-implanting-target", ("user", userName)), user, target, PopupType.LargeCaution);
component.CancelToken?.Cancel();
component.CancelToken = new CancellationTokenSource();
var implantEvent = new ImplantEvent();
_doAfter.DoAfter(new DoAfterEventArgs(user, component.ImplantTime, component.CancelToken.Token,target:target, used:implanter)
{
BreakOnUserMove = true,
BreakOnTargetMove = true,
BreakOnDamage = true,
BreakOnStun = true,
NeedHand = true
}, implantEvent);
} }
/// <summary> /// <summary>
@@ -110,21 +94,17 @@ public sealed partial class ImplanterSystem : SharedImplanterSystem
//TODO: Remove when surgery is in //TODO: Remove when surgery is in
public void TryDraw(ImplanterComponent component, EntityUid user, EntityUid target, EntityUid implanter) public void TryDraw(ImplanterComponent component, EntityUid user, EntityUid target, EntityUid implanter)
{ {
_popup.PopupEntity(Loc.GetString("injector-component-injecting-user"), target, user); var args = new DoAfterArgs(user, component.DrawTime, new DrawEvent(), implanter, target: target, used: implanter)
component.CancelToken?.Cancel();
component.CancelToken = new CancellationTokenSource();
var drawEvent = new DrawEvent();
_doAfter.DoAfter(new DoAfterEventArgs(user, component.DrawTime, target:target,used:implanter)
{ {
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true, NeedHand = true,
NeedHand = true };
}, drawEvent);
if (_doAfter.TryStartDoAfter(args))
_popup.PopupEntity(Loc.GetString("injector-component-injecting-user"), target, user);
} }
private void OnImplanterGetState(EntityUid uid, ImplanterComponent component, ref ComponentGetState args) private void OnImplanterGetState(EntityUid uid, ImplanterComponent component, ref ComponentGetState args)
@@ -132,47 +112,23 @@ public sealed partial class ImplanterSystem : SharedImplanterSystem
args.State = new ImplanterComponentState(component.CurrentMode, component.ImplantOnly); args.State = new ImplanterComponentState(component.CurrentMode, component.ImplantOnly);
} }
private void OnImplant(EntityUid uid, ImplanterComponent component, DoAfterEvent<ImplantEvent> args) private void OnImplant(EntityUid uid, ImplanterComponent component, ImplantEvent args)
{ {
if (args.Cancelled) if (args.Cancelled || args.Handled || args.Target == null || args.Used == null)
{
component.CancelToken = null;
return;
}
if (args.Handled || args.Args.Target == null || args.Args.Used == null)
return; return;
Implant(args.Args.Used.Value, args.Args.Target.Value, component); Implant(args.Used.Value, args.Target.Value, component);
args.Handled = true; args.Handled = true;
component.CancelToken = null;
} }
private void OnDraw(EntityUid uid, ImplanterComponent component, DoAfterEvent<DrawEvent> args) private void OnDraw(EntityUid uid, ImplanterComponent component, DrawEvent args)
{ {
if (args.Cancelled) if (args.Cancelled || args.Handled || args.Used == null || args.Target == null)
{
component.CancelToken = null;
return;
}
if (args.Handled || args.Args.Used == null || args.Args.Target == null)
return; return;
Draw(args.Args.Used.Value, args.Args.User, args.Args.Target.Value, component); Draw(args.Used.Value, args.User, args.Target.Value, component);
args.Handled = true; args.Handled = true;
component.CancelToken = null;
}
private sealed class ImplantEvent : EntityEventArgs
{
}
private sealed class DrawEvent : EntityEventArgs
{
} }
} }

View File

@@ -1,5 +1,4 @@
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.DoAfter;
using Content.Server.Kitchen.Components; using Content.Server.Kitchen.Components;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Database; using Content.Shared.Database;
@@ -25,7 +24,7 @@ namespace Content.Server.Kitchen.EntitySystems
public sealed class KitchenSpikeSystem : SharedKitchenSpikeSystem public sealed class KitchenSpikeSystem : SharedKitchenSpikeSystem
{ {
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly IAdminLogManager _logger = default!; [Dependency] private readonly IAdminLogManager _logger = default!;
[Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
@@ -43,7 +42,7 @@ namespace Content.Server.Kitchen.EntitySystems
SubscribeLocalEvent<KitchenSpikeComponent, DragDropTargetEvent>(OnDragDrop); SubscribeLocalEvent<KitchenSpikeComponent, DragDropTargetEvent>(OnDragDrop);
//DoAfter //DoAfter
SubscribeLocalEvent<KitchenSpikeComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<KitchenSpikeComponent, SpikeDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<KitchenSpikeComponent, SuicideEvent>(OnSuicide); SubscribeLocalEvent<KitchenSpikeComponent, SuicideEvent>(OnSuicide);
@@ -251,16 +250,15 @@ namespace Content.Server.Kitchen.EntitySystems
butcherable.BeingButchered = true; butcherable.BeingButchered = true;
component.InUse = true; component.InUse = true;
var doAfterArgs = new DoAfterEventArgs(userUid, component.SpikeDelay + butcherable.ButcherDelay, target:victimUid, used:uid) var doAfterArgs = new DoAfterArgs(userUid, component.SpikeDelay + butcherable.ButcherDelay, new SpikeDoAfterEvent(), uid, target: victimUid, used: uid)
{ {
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true,
NeedHand = true NeedHand = true
}; };
_doAfter.DoAfter(doAfterArgs); _doAfter.TryStartDoAfter(doAfterArgs);
return true; return true;
} }

View File

@@ -1,5 +1,4 @@
using Content.Server.Body.Systems; using Content.Server.Body.Systems;
using Content.Server.DoAfter;
using Content.Server.Kitchen.Components; using Content.Server.Kitchen.Components;
using Content.Shared.Body.Components; using Content.Shared.Body.Components;
using Content.Shared.Interaction; using Content.Shared.Interaction;
@@ -9,6 +8,7 @@ using Content.Shared.Storage;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Content.Shared.Destructible; using Content.Shared.Destructible;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Kitchen;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Robust.Server.Containers; using Robust.Server.Containers;
@@ -21,7 +21,7 @@ public sealed class SharpSystem : EntitySystem
{ {
[Dependency] private readonly BodySystem _bodySystem = default!; [Dependency] private readonly BodySystem _bodySystem = default!;
[Dependency] private readonly SharedDestructibleSystem _destructibleSystem = default!; [Dependency] private readonly SharedDestructibleSystem _destructibleSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly ContainerSystem _containerSystem = default!; [Dependency] private readonly ContainerSystem _containerSystem = default!;
[Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
@@ -32,7 +32,7 @@ public sealed class SharpSystem : EntitySystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<SharpComponent, AfterInteractEvent>(OnAfterInteract); SubscribeLocalEvent<SharpComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<SharpComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<SharpComponent, SharpDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<ButcherableComponent, GetVerbsEvent<InteractionVerb>>(OnGetInteractionVerbs); SubscribeLocalEvent<ButcherableComponent, GetVerbsEvent<InteractionVerb>>(OnGetInteractionVerbs);
} }
@@ -63,16 +63,15 @@ public sealed class SharpSystem : EntitySystem
return; return;
var doAfter = var doAfter =
new DoAfterEventArgs(user, sharp.ButcherDelayModifier * butcher.ButcherDelay, target: target, used: knife) new DoAfterArgs(user, sharp.ButcherDelayModifier * butcher.ButcherDelay, new SharpDoAfterEvent(), knife, target: target, used: knife)
{ {
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true,
NeedHand = true NeedHand = true
}; };
_doAfterSystem.DoAfter(doAfter); _doAfterSystem.TryStartDoAfter(doAfter);
} }
private void OnDoAfter(EntityUid uid, SharpComponent component, DoAfterEvent args) private void OnDoAfter(EntityUid uid, SharpComponent component, DoAfterEvent args)

View File

@@ -1,7 +1,6 @@
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork;
using Content.Server.DeviceNetwork.Systems; using Content.Server.DeviceNetwork.Systems;
using Content.Server.DoAfter;
using Content.Server.Ghost; using Content.Server.Ghost;
using Content.Server.Light.Components; using Content.Server.Light.Components;
using Content.Server.MachineLinking.Events; using Content.Server.MachineLinking.Events;
@@ -40,7 +39,7 @@ namespace Content.Server.Light.EntitySystems
[Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly SignalLinkerSystem _signalSystem = default!; [Dependency] private readonly SignalLinkerSystem _signalSystem = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
@@ -63,8 +62,7 @@ namespace Content.Server.Light.EntitySystems
SubscribeLocalEvent<PoweredLightComponent, PowerChangedEvent>(OnPowerChanged); SubscribeLocalEvent<PoweredLightComponent, PowerChangedEvent>(OnPowerChanged);
SubscribeLocalEvent<PoweredLightComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<PoweredLightComponent, PoweredLightDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<PoweredLightComponent, EmpPulseEvent>(OnEmpPulse); SubscribeLocalEvent<PoweredLightComponent, EmpPulseEvent>(OnEmpPulse);
} }
@@ -140,11 +138,10 @@ namespace Content.Server.Light.EntitySystems
} }
// removing a working bulb, so require a delay // removing a working bulb, so require a delay
_doAfterSystem.DoAfter(new DoAfterEventArgs(userUid, light.EjectBulbDelay, target:uid) _doAfterSystem.TryStartDoAfter(new DoAfterArgs(userUid, light.EjectBulbDelay, new PoweredLightDoAfterEvent(), uid, target: uid)
{ {
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true
}); });
args.Handled = true; args.Handled = true;

View File

@@ -1,8 +1,6 @@
using System.Threading;
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Body.Systems; using Content.Server.Body.Systems;
using Content.Server.Coordinates.Helpers; using Content.Server.Coordinates.Helpers;
using Content.Server.DoAfter;
using Content.Server.Doors.Systems; using Content.Server.Doors.Systems;
using Content.Server.Magic.Events; using Content.Server.Magic.Events;
using Content.Server.Weapons.Ranged.Systems; using Content.Server.Weapons.Ranged.Systems;
@@ -13,6 +11,7 @@ using Content.Shared.DoAfter;
using Content.Shared.Doors.Components; using Content.Shared.Doors.Components;
using Content.Shared.Doors.Systems; using Content.Shared.Doors.Systems;
using Content.Shared.Interaction.Events; using Content.Shared.Interaction.Events;
using Content.Shared.Magic;
using Content.Shared.Maps; using Content.Shared.Maps;
using Content.Shared.Physics; using Content.Shared.Physics;
using Content.Shared.Spawners.Components; using Content.Shared.Spawners.Components;
@@ -42,7 +41,7 @@ public sealed class MagicSystem : EntitySystem
[Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SharedDoorSystem _doorSystem = default!; [Dependency] private readonly SharedDoorSystem _doorSystem = default!;
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!; [Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly GunSystem _gunSystem = default!; [Dependency] private readonly GunSystem _gunSystem = default!;
[Dependency] private readonly PhysicsSystem _physics = default!; [Dependency] private readonly PhysicsSystem _physics = default!;
[Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
@@ -54,7 +53,7 @@ public sealed class MagicSystem : EntitySystem
SubscribeLocalEvent<SpellbookComponent, ComponentInit>(OnInit); SubscribeLocalEvent<SpellbookComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<SpellbookComponent, UseInHandEvent>(OnUse); SubscribeLocalEvent<SpellbookComponent, UseInHandEvent>(OnUse);
SubscribeLocalEvent<SpellbookComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<SpellbookComponent, SpellbookDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<InstantSpawnSpellEvent>(OnInstantSpawn); SubscribeLocalEvent<InstantSpawnSpellEvent>(OnInstantSpawn);
SubscribeLocalEvent<TeleportSpellEvent>(OnTeleportSpell); SubscribeLocalEvent<TeleportSpellEvent>(OnTeleportSpell);
@@ -111,16 +110,15 @@ public sealed class MagicSystem : EntitySystem
private void AttemptLearn(EntityUid uid, SpellbookComponent component, UseInHandEvent args) private void AttemptLearn(EntityUid uid, SpellbookComponent component, UseInHandEvent args)
{ {
var doAfterEventArgs = new DoAfterEventArgs(args.User, component.LearnTime, target:uid) var doAfterEventArgs = new DoAfterArgs(args.User, component.LearnTime, new SpellbookDoAfterEvent(), uid, target: uid)
{ {
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true,
NeedHand = true //What, are you going to read with your eyes only?? NeedHand = true //What, are you going to read with your eyes only??
}; };
_doAfter.DoAfter(doAfterEventArgs); _doAfter.TryStartDoAfter(doAfterEventArgs);
} }
#region Spells #region Spells

View File

@@ -1,11 +1,9 @@
using System.Linq; using System.Linq;
using Content.Server.DoAfter;
using Content.Server.Interaction; using Content.Server.Interaction;
using Content.Server.Mech.Components; using Content.Server.Mech.Components;
using Content.Server.Mech.Equipment.Components; using Content.Server.Mech.Equipment.Components;
using Content.Server.Mech.Systems; using Content.Server.Mech.Systems;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Construction.Components;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Mech; using Content.Shared.Mech;
using Content.Shared.Mech.Equipment.Components; using Content.Shared.Mech.Equipment.Components;
@@ -26,7 +24,7 @@ public sealed class MechGrabberSystem : EntitySystem
{ {
[Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly MechSystem _mech = default!; [Dependency] private readonly MechSystem _mech = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly InteractionSystem _interaction = default!; [Dependency] private readonly InteractionSystem _interaction = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly TransformSystem _transform = default!;
@@ -41,7 +39,7 @@ public sealed class MechGrabberSystem : EntitySystem
SubscribeLocalEvent<MechGrabberComponent, AttemptRemoveMechEquipmentEvent>(OnAttemptRemove); SubscribeLocalEvent<MechGrabberComponent, AttemptRemoveMechEquipmentEvent>(OnAttemptRemove);
SubscribeLocalEvent<MechGrabberComponent, InteractNoHandEvent>(OnInteract); SubscribeLocalEvent<MechGrabberComponent, InteractNoHandEvent>(OnInteract);
SubscribeLocalEvent<MechGrabberComponent, DoAfterEvent>(OnMechGrab); SubscribeLocalEvent<MechGrabberComponent, GrabberDoAfterEvent>(OnMechGrab);
} }
private void OnGrabberMessage(EntityUid uid, MechGrabberComponent component, MechEquipmentUiMessageRelayEvent args) private void OnGrabberMessage(EntityUid uid, MechGrabberComponent component, MechEquipmentUiMessageRelayEvent args)
@@ -150,7 +148,7 @@ public sealed class MechGrabberSystem : EntitySystem
args.Handled = true; args.Handled = true;
component.AudioStream = _audio.PlayPvs(component.GrabSound, uid); component.AudioStream = _audio.PlayPvs(component.GrabSound, uid);
_doAfter.DoAfter(new DoAfterEventArgs(args.User, component.GrabDelay, target:target, used:uid) _doAfter.TryStartDoAfter(new DoAfterArgs(args.User, component.GrabDelay, new GrabberDoAfterEvent(), uid, target: target, used: uid)
{ {
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true BreakOnUserMove = true

View File

@@ -1,5 +1,4 @@
using Content.Server.DoAfter; using Content.Server.Mech.Components;
using Content.Server.Mech.Components;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Interaction; using Content.Shared.Interaction;
@@ -13,14 +12,14 @@ namespace Content.Server.Mech.Systems;
public sealed class MechEquipmentSystem : EntitySystem public sealed class MechEquipmentSystem : EntitySystem
{ {
[Dependency] private readonly MechSystem _mech = default!; [Dependency] private readonly MechSystem _mech = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly PopupSystem _popup = default!;
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
{ {
SubscribeLocalEvent<MechEquipmentComponent, AfterInteractEvent>(OnUsed); SubscribeLocalEvent<MechEquipmentComponent, AfterInteractEvent>(OnUsed);
SubscribeLocalEvent<MechEquipmentComponent, DoAfterEvent<InsertEquipmentEvent>>(OnInsertEquipment); SubscribeLocalEvent<MechEquipmentComponent, InsertEquipmentEvent>(OnInsertEquipment);
} }
private void OnUsed(EntityUid uid, MechEquipmentComponent component, AfterInteractEvent args) private void OnUsed(EntityUid uid, MechEquipmentComponent component, AfterInteractEvent args)
@@ -46,18 +45,16 @@ public sealed class MechEquipmentSystem : EntitySystem
_popup.PopupEntity(Loc.GetString("mech-equipment-begin-install", ("item", uid)), mech); _popup.PopupEntity(Loc.GetString("mech-equipment-begin-install", ("item", uid)), mech);
var insertEquipment = new InsertEquipmentEvent(); var doAfterEventArgs = new DoAfterArgs(args.User, component.InstallDuration, new InsertEquipmentEvent(), uid, target: mech, used: uid)
var doAfterEventArgs = new DoAfterEventArgs(args.User, component.InstallDuration, target: mech, used: uid)
{ {
BreakOnStun = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true BreakOnUserMove = true
}; };
_doAfter.DoAfter(doAfterEventArgs, insertEquipment); _doAfter.TryStartDoAfter(doAfterEventArgs);
} }
private void OnInsertEquipment(EntityUid uid, MechEquipmentComponent component, DoAfterEvent<InsertEquipmentEvent> args) private void OnInsertEquipment(EntityUid uid, MechEquipmentComponent component, InsertEquipmentEvent args)
{ {
if (args.Handled || args.Cancelled || args.Args.Target == null) if (args.Handled || args.Cancelled || args.Args.Target == null)
return; return;
@@ -67,9 +64,4 @@ public sealed class MechEquipmentSystem : EntitySystem
args.Handled = true; args.Handled = true;
} }
private sealed class InsertEquipmentEvent : EntityEventArgs
{
}
} }

View File

@@ -1,6 +1,5 @@
using System.Linq; using System.Linq;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.DoAfter;
using Content.Server.Mech.Components; using Content.Server.Mech.Components;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
@@ -27,7 +26,7 @@ public sealed class MechSystem : SharedMechSystem
[Dependency] private readonly AtmosphereSystem _atmosphere = default!; [Dependency] private readonly AtmosphereSystem _atmosphere = default!;
[Dependency] private readonly ContainerSystem _container = default!; [Dependency] private readonly ContainerSystem _container = default!;
[Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly IMapManager _map = default!; [Dependency] private readonly IMapManager _map = default!;
[Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!; [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
@@ -45,9 +44,9 @@ public sealed class MechSystem : SharedMechSystem
SubscribeLocalEvent<MechComponent, MapInitEvent>(OnMapInit); SubscribeLocalEvent<MechComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<MechComponent, GetVerbsEvent<AlternativeVerb>>(OnAlternativeVerb); SubscribeLocalEvent<MechComponent, GetVerbsEvent<AlternativeVerb>>(OnAlternativeVerb);
SubscribeLocalEvent<MechComponent, MechOpenUiEvent>(OnOpenUi); SubscribeLocalEvent<MechComponent, MechOpenUiEvent>(OnOpenUi);
SubscribeLocalEvent<MechComponent, DoAfterEvent<RemoveBatteryEvent>>(OnRemoveBattery); SubscribeLocalEvent<MechComponent, RemoveBatteryEvent>(OnRemoveBattery);
SubscribeLocalEvent<MechComponent, DoAfterEvent<MechEntryEvent>>(OnMechEntry); SubscribeLocalEvent<MechComponent, MechEntryEvent>(OnMechEntry);
SubscribeLocalEvent<MechComponent, DoAfterEvent<MechExitEvent>>(OnMechExit); SubscribeLocalEvent<MechComponent, MechExitEvent>(OnMechExit);
SubscribeLocalEvent<MechComponent, DamageChangedEvent>(OnDamageChanged); SubscribeLocalEvent<MechComponent, DamageChangedEvent>(OnDamageChanged);
SubscribeLocalEvent<MechComponent, MechEquipmentRemoveMessage>(OnRemoveEquipmentMessage); SubscribeLocalEvent<MechComponent, MechEquipmentRemoveMessage>(OnRemoveEquipmentMessage);
@@ -84,22 +83,17 @@ public sealed class MechSystem : SharedMechSystem
if (TryComp<ToolComponent>(args.Used, out var tool) && tool.Qualities.Contains("Prying") && component.BatterySlot.ContainedEntity != null) if (TryComp<ToolComponent>(args.Used, out var tool) && tool.Qualities.Contains("Prying") && component.BatterySlot.ContainedEntity != null)
{ {
var removeBattery = new RemoveBatteryEvent(); var doAfterEventArgs = new DoAfterArgs(args.User, component.BatteryRemovalDelay, new RemoveBatteryEvent(), uid, target: uid, used: args.Target)
var doAfterEventArgs = new DoAfterEventArgs(args.User, component.BatteryRemovalDelay, target: uid, used: args.Target)
{ {
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
RaiseOnTarget = true,
RaiseOnUsed = false,
RaiseOnUser = false,
}; };
_doAfter.DoAfter(doAfterEventArgs, removeBattery); _doAfter.TryStartDoAfter(doAfterEventArgs);
} }
} }
private void OnRemoveBattery(EntityUid uid, MechComponent component, DoAfterEvent<RemoveBatteryEvent> args) private void OnRemoveBattery(EntityUid uid, MechComponent component, RemoveBatteryEvent args)
{ {
if (args.Cancelled || args.Handled) if (args.Cancelled || args.Handled)
return; return;
@@ -166,17 +160,12 @@ public sealed class MechSystem : SharedMechSystem
Text = Loc.GetString("mech-verb-enter"), Text = Loc.GetString("mech-verb-enter"),
Act = () => Act = () =>
{ {
var mechEntryEvent = new MechEntryEvent(); var doAfterEventArgs = new DoAfterArgs(args.User, component.EntryDelay, new MechEntryEvent(), uid, target: uid)
var doAfterEventArgs = new DoAfterEventArgs(args.User, component.EntryDelay, target: uid)
{ {
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnStun = true,
RaiseOnTarget = true,
RaiseOnUsed = false,
RaiseOnUser = false,
}; };
_doAfter.DoAfter(doAfterEventArgs, mechEntryEvent); _doAfter.TryStartDoAfter(doAfterEventArgs);
} }
}; };
var openUiVerb = new AlternativeVerb //can't hijack someone else's mech var openUiVerb = new AlternativeVerb //can't hijack someone else's mech
@@ -201,25 +190,20 @@ public sealed class MechSystem : SharedMechSystem
return; return;
} }
var mechExitEvent = new MechExitEvent(); var doAfterEventArgs = new DoAfterArgs(args.User, component.ExitDelay, new MechExitEvent(), uid, target: uid)
var doAfterEventArgs = new DoAfterEventArgs(args.User, component.ExitDelay, target: uid)
{ {
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnStun = true,
RaiseOnTarget = true,
RaiseOnUsed = false,
RaiseOnUser = false,
}; };
_doAfter.DoAfter(doAfterEventArgs, mechExitEvent); _doAfter.TryStartDoAfter(doAfterEventArgs);
} }
}; };
args.Verbs.Add(ejectVerb); args.Verbs.Add(ejectVerb);
} }
} }
private void OnMechEntry(EntityUid uid, MechComponent component, DoAfterEvent<MechEntryEvent> args) private void OnMechEntry(EntityUid uid, MechComponent component, MechEntryEvent args)
{ {
if (args.Cancelled || args.Handled) if (args.Cancelled || args.Handled)
return; return;
@@ -230,7 +214,7 @@ public sealed class MechSystem : SharedMechSystem
args.Handled = true; args.Handled = true;
} }
private void OnMechExit(EntityUid uid, MechComponent component, DoAfterEvent<MechExitEvent> args) private void OnMechExit(EntityUid uid, MechComponent component, MechExitEvent args)
{ {
if (args.Cancelled || args.Handled) if (args.Cancelled || args.Handled)
return; return;
@@ -454,27 +438,4 @@ public sealed class MechSystem : SharedMechSystem
args.Handled = true; args.Handled = true;
} }
#endregion #endregion
/// <summary>
/// Event raised when the battery is successfully removed from the mech,
/// on both success and failure
/// </summary>
private sealed class RemoveBatteryEvent : EntityEventArgs
{
}
/// <summary>
/// Event raised when a person enters a mech, on both success and failure
/// </summary>
private sealed class MechEntryEvent : EntityEventArgs
{
}
/// <summary>
/// Event raised when a person removes someone from a mech,
/// on both success and failure
/// </summary>
private sealed class MechExitEvent : EntityEventArgs
{
}
} }

View File

@@ -1,4 +1,3 @@
using System.Threading;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Audio; using Content.Shared.Audio;
using Content.Shared.Jittering; using Content.Shared.Jittering;
@@ -14,7 +13,6 @@ using Content.Server.Fluids.EntitySystems;
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Climbing; using Content.Server.Climbing;
using Content.Server.Construction; using Content.Server.Construction;
using Content.Server.DoAfter;
using Content.Server.Materials; using Content.Server.Materials;
using Content.Server.Mind.Components; using Content.Server.Mind.Components;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
@@ -27,7 +25,7 @@ using Robust.Shared.Random;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Components;
using Content.Shared.Humanoid; using Content.Shared.Medical;
namespace Content.Server.Medical.BiomassReclaimer namespace Content.Server.Medical.BiomassReclaimer
{ {
@@ -43,7 +41,7 @@ namespace Content.Server.Medical.BiomassReclaimer
[Dependency] private readonly ThrowingSystem _throwing = default!; [Dependency] private readonly ThrowingSystem _throwing = default!;
[Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly MaterialStorageSystem _material = default!; [Dependency] private readonly MaterialStorageSystem _material = default!;
@@ -97,7 +95,7 @@ namespace Content.Server.Medical.BiomassReclaimer
SubscribeLocalEvent<BiomassReclaimerComponent, UpgradeExamineEvent>(OnUpgradeExamine); SubscribeLocalEvent<BiomassReclaimerComponent, UpgradeExamineEvent>(OnUpgradeExamine);
SubscribeLocalEvent<BiomassReclaimerComponent, PowerChangedEvent>(OnPowerChanged); SubscribeLocalEvent<BiomassReclaimerComponent, PowerChangedEvent>(OnPowerChanged);
SubscribeLocalEvent<BiomassReclaimerComponent, SuicideEvent>(OnSuicide); SubscribeLocalEvent<BiomassReclaimerComponent, SuicideEvent>(OnSuicide);
SubscribeLocalEvent<BiomassReclaimerComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<BiomassReclaimerComponent, ReclaimerDoAfterEvent>(OnDoAfter);
} }
private void OnSuicide(EntityUid uid, BiomassReclaimerComponent component, SuicideEvent args) private void OnSuicide(EntityUid uid, BiomassReclaimerComponent component, SuicideEvent args)
@@ -152,11 +150,10 @@ namespace Content.Server.Medical.BiomassReclaimer
if (!HasComp<MobStateComponent>(args.Used) || !CanGib(uid, args.Used, component)) if (!HasComp<MobStateComponent>(args.Used) || !CanGib(uid, args.Used, component))
return; return;
_doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, 7f, target:args.Target, used:args.Used) _doAfterSystem.TryStartDoAfter(new DoAfterArgs(args.User, 7f, new ReclaimerDoAfterEvent(), uid, target: args.Target, used: args.Used)
{ {
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnStun = true,
NeedHand = true NeedHand = true
}); });
} }

View File

@@ -1,6 +1,6 @@
using System.Threading;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes; using Content.Shared.Damage.Prototypes;
using Content.Shared.DoAfter;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
@@ -39,12 +39,6 @@ namespace Content.Server.Medical.Components
[DataField("delay")] [DataField("delay")]
public float Delay = 3f; public float Delay = 3f;
/// <summary>
/// Cancel token to prevent rapid healing
/// </summary>
[DataField("cancelToken")]
public CancellationTokenSource? CancelToken;
/// <summary> /// <summary>
/// Delay multiplier when healing yourself. /// Delay multiplier when healing yourself.
/// </summary> /// </summary>

View File

@@ -7,7 +7,6 @@ using Content.Server.Body.Systems;
using Content.Server.Chemistry.Components.SolutionManager; using Content.Server.Chemistry.Components.SolutionManager;
using Content.Server.Chemistry.EntitySystems; using Content.Server.Chemistry.EntitySystems;
using Content.Server.Climbing; using Content.Server.Climbing;
using Content.Server.DoAfter;
using Content.Server.Medical.Components; using Content.Server.Medical.Components;
using Content.Server.NodeContainer; using Content.Server.NodeContainer;
using Content.Server.NodeContainer.NodeGroups; using Content.Server.NodeContainer.NodeGroups;
@@ -26,7 +25,6 @@ using Content.Shared.Interaction;
using Content.Shared.Medical.Cryogenics; using Content.Shared.Medical.Cryogenics;
using Content.Shared.MedicalScanner; using Content.Shared.MedicalScanner;
using Content.Shared.Tools; using Content.Shared.Tools;
using Content.Shared.Tools.Components;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Timing; using Robust.Shared.Timing;
@@ -42,7 +40,7 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!; [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly SharedToolSystem _toolSystem = default!; [Dependency] private readonly SharedToolSystem _toolSystem = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
@@ -56,9 +54,8 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem
SubscribeLocalEvent<CryoPodComponent, ComponentInit>(OnComponentInit); SubscribeLocalEvent<CryoPodComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<CryoPodComponent, GetVerbsEvent<AlternativeVerb>>(AddAlternativeVerbs); SubscribeLocalEvent<CryoPodComponent, GetVerbsEvent<AlternativeVerb>>(AddAlternativeVerbs);
SubscribeLocalEvent<CryoPodComponent, GotEmaggedEvent>(OnEmagged); SubscribeLocalEvent<CryoPodComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<CryoPodComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<CryoPodComponent, CryoPodDragFinished>(OnDragFinished);
SubscribeLocalEvent<CryoPodComponent, CryoPodPryFinished>(OnCryoPodPryFinished); SubscribeLocalEvent<CryoPodComponent, CryoPodPryFinished>(OnCryoPodPryFinished);
SubscribeLocalEvent<CryoPodComponent, CryoPodPryInterrupted>(OnCryoPodPryInterrupted);
SubscribeLocalEvent<CryoPodComponent, AtmosDeviceUpdateEvent>(OnCryoPodUpdateAtmosphere); SubscribeLocalEvent<CryoPodComponent, AtmosDeviceUpdateEvent>(OnCryoPodUpdateAtmosphere);
SubscribeLocalEvent<CryoPodComponent, DragDropTargetEvent>(HandleDragDropOn); SubscribeLocalEvent<CryoPodComponent, DragDropTargetEvent>(HandleDragDropOn);
@@ -130,25 +127,23 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem
if (cryoPodComponent.BodyContainer.ContainedEntity != null) if (cryoPodComponent.BodyContainer.ContainedEntity != null)
return; return;
var doAfterArgs = new DoAfterEventArgs(args.User, cryoPodComponent.EntryDelay, target:args.Dragged, used:uid) var doAfterArgs = new DoAfterArgs(args.User, cryoPodComponent.EntryDelay, new CryoPodDragFinished(), uid, target: args.Dragged, used: uid)
{ {
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
NeedHand = false, NeedHand = false,
}; };
_doAfterSystem.DoAfter(doAfterArgs); _doAfterSystem.TryStartDoAfter(doAfterArgs);
args.Handled = true; args.Handled = true;
} }
private void OnDoAfter(EntityUid uid, CryoPodComponent component, DoAfterEvent args) private void OnDragFinished(EntityUid uid, CryoPodComponent component, CryoPodDragFinished args)
{ {
if (args.Cancelled || args.Handled || args.Args.Target == null) if (args.Cancelled || args.Handled || args.Args.Target == null)
return; return;
InsertBody(uid, args.Args.Target.Value, component); InsertBody(uid, args.Args.Target.Value, component);
args.Handled = true; args.Handled = true;
} }
@@ -179,18 +174,7 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem
if (args.Handled || !cryoPodComponent.Locked || cryoPodComponent.BodyContainer.ContainedEntity == null) if (args.Handled || !cryoPodComponent.Locked || cryoPodComponent.BodyContainer.ContainedEntity == null)
return; return;
if (TryComp(args.Used, out ToolComponent? tool) args.Handled = _toolSystem.UseTool(args.Used, args.User, uid, cryoPodComponent.PryDelay, "Prying", new CryoPodPryFinished());
&& tool.Qualities.Contains("Prying")) // Why aren't those enums?
{
if (cryoPodComponent.IsPrying)
return;
cryoPodComponent.IsPrying = true;
var toolEvData = new ToolEventData(new CryoPodPryFinished(), targetEntity:uid);
_toolSystem.UseTool(args.Used, args.User, uid, cryoPodComponent.PryDelay, new [] {"Prying"}, toolEvData);
args.Handled = true;
}
} }
private void OnExamined(EntityUid uid, CryoPodComponent component, ExaminedEvent args) private void OnExamined(EntityUid uid, CryoPodComponent component, ExaminedEvent args)

View File

@@ -1,15 +1,15 @@
using System.Threading;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Body.Systems; using Content.Server.Body.Systems;
using Content.Server.DoAfter;
using Content.Server.Medical.Components; using Content.Server.Medical.Components;
using Content.Server.Stack; using Content.Server.Stack;
using Content.Shared.Audio; using Content.Shared.Audio;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.FixedPoint;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Interaction.Events; using Content.Shared.Interaction.Events;
using Content.Shared.Medical;
using Content.Shared.Mobs; using Content.Shared.Mobs;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
@@ -24,7 +24,7 @@ public sealed class HealingSystem : EntitySystem
[Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!; [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly StackSystem _stacks = default!; [Dependency] private readonly StackSystem _stacks = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
@@ -36,45 +36,43 @@ public sealed class HealingSystem : EntitySystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<HealingComponent, UseInHandEvent>(OnHealingUse); SubscribeLocalEvent<HealingComponent, UseInHandEvent>(OnHealingUse);
SubscribeLocalEvent<HealingComponent, AfterInteractEvent>(OnHealingAfterInteract); SubscribeLocalEvent<HealingComponent, AfterInteractEvent>(OnHealingAfterInteract);
SubscribeLocalEvent<DamageableComponent, DoAfterEvent<HealingData>>(OnDoAfter); SubscribeLocalEvent<DamageableComponent, HealingDoAfterEvent>(OnDoAfter);
} }
private void OnDoAfter(EntityUid uid, DamageableComponent component, DoAfterEvent<HealingData> args) private void OnDoAfter(EntityUid uid, DamageableComponent component, HealingDoAfterEvent args)
{ {
if (args.Cancelled) if (!TryComp(args.Used, out HealingComponent? healing))
{
args.AdditionalData.HealingComponent.CancelToken = null;
return; return;
}
if (args.Handled || args.Cancelled || _mobStateSystem.IsDead(uid) || args.Args.Used == null) if (args.Handled || args.Cancelled || _mobStateSystem.IsDead(uid))
return; return;
if (component.DamageContainerID is not null && !component.DamageContainerID.Equals(component.DamageContainerID)) if (component.DamageContainerID is not null && !component.DamageContainerID.Equals(component.DamageContainerID))
return; return;
// Heal some bloodloss damage. // Heal some bloodloss damage.
if (args.AdditionalData.HealingComponent.BloodlossModifier != 0) if (healing.BloodlossModifier != 0)
_bloodstreamSystem.TryModifyBleedAmount(uid, args.AdditionalData.HealingComponent.BloodlossModifier); _bloodstreamSystem.TryModifyBleedAmount(uid, healing.BloodlossModifier);
var healed = _damageable.TryChangeDamage(uid, args.AdditionalData.HealingComponent.Damage, true, origin: args.Args.User); var healed = _damageable.TryChangeDamage(uid, healing.Damage, true, origin: args.Args.User);
if (healed == null) if (healed == null && healing.BloodlossModifier != 0)
return; return;
var total = healed?.Total ?? FixedPoint2.Zero;
// Reverify that we can heal the damage. // Reverify that we can heal the damage.
_stacks.Use(args.Args.Used.Value, 1, args.AdditionalData.Stack); _stacks.Use(args.Used.Value, 1);
if (uid != args.Args.User)
_adminLogger.Add(LogType.Healed, $"{EntityManager.ToPrettyString(args.Args.User):user} healed {EntityManager.ToPrettyString(uid):target} for {healed.Total:damage} damage");
if (uid != args.User)
_adminLogger.Add(LogType.Healed,
$"{EntityManager.ToPrettyString(args.User):user} healed {EntityManager.ToPrettyString(uid):target} for {total:damage} damage");
else else
_adminLogger.Add(LogType.Healed, $"{EntityManager.ToPrettyString(args.Args.User):user} healed themselves for {healed.Total:damage} damage"); _adminLogger.Add(LogType.Healed,
$"{EntityManager.ToPrettyString(args.User):user} healed themselves for {total:damage} damage");
if (args.AdditionalData.HealingComponent.HealingEndSound != null) _audio.PlayPvs(healing.HealingEndSound, uid, AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f));
_audio.PlayPvs(args.AdditionalData.HealingComponent.HealingEndSound, uid, AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f));
args.AdditionalData.HealingComponent.CancelToken = null;
args.Handled = true; args.Handled = true;
} }
@@ -98,13 +96,14 @@ public sealed class HealingSystem : EntitySystem
private bool TryHeal(EntityUid uid, EntityUid user, EntityUid target, HealingComponent component) private bool TryHeal(EntityUid uid, EntityUid user, EntityUid target, HealingComponent component)
{ {
if (_mobStateSystem.IsDead(target) || !TryComp<DamageableComponent>(target, out var targetDamage) || component.CancelToken != null) if (_mobStateSystem.IsDead(target) || !TryComp<DamageableComponent>(target, out var targetDamage))
return false; return false;
if (targetDamage.TotalDamage == 0) if (targetDamage.TotalDamage == 0)
return false; return false;
if (component.DamageContainerID is not null && !component.DamageContainerID.Equals(targetDamage.DamageContainerID)) if (component.DamageContainerID is not null &&
!component.DamageContainerID.Equals(targetDamage.DamageContainerID))
return false; return false;
if (user != target && !_interactionSystem.InRangeUnobstructed(user, target, popup: true)) if (user != target && !_interactionSystem.InRangeUnobstructed(user, target, popup: true))
@@ -114,7 +113,8 @@ public sealed class HealingSystem : EntitySystem
return false; return false;
if (component.HealingBeginSound != null) if (component.HealingBeginSound != null)
_audio.PlayPvs(component.HealingBeginSound, uid, AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f)); _audio.PlayPvs(component.HealingBeginSound, uid,
AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f));
var isNotSelf = user != target; var isNotSelf = user != target;
@@ -122,27 +122,18 @@ public sealed class HealingSystem : EntitySystem
? component.Delay ? component.Delay
: component.Delay * GetScaledHealingPenalty(user, component); : component.Delay * GetScaledHealingPenalty(user, component);
component.CancelToken = new CancellationTokenSource(); var doAfterEventArgs =
new DoAfterArgs(user, delay, new HealingDoAfterEvent(), target, target: target, used: uid)
var healingData = new HealingData(component, stack);
var doAfterEventArgs = new DoAfterEventArgs(user, delay, cancelToken: component.CancelToken.Token,target: target, used: uid)
{ {
//Raise the event on the target if it's not self, otherwise raise it on self. //Raise the event on the target if it's not self, otherwise raise it on self.
RaiseOnTarget = isNotSelf,
RaiseOnUser = !isNotSelf,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
// Didn't break on damage as they may be trying to prevent it and // Didn't break on damage as they may be trying to prevent it and
// not being able to heal your own ticking damage would be frustrating. // not being able to heal your own ticking damage would be frustrating.
BreakOnStun = true,
NeedHand = true, NeedHand = true,
// Juusstt in case damageble gets removed it avoids having to re-cancel the token. Won't need this when DoAfterEvent<T> gets added.
PostCheck = () => true
}; };
_doAfter.DoAfter(doAfterEventArgs, healingData); _doAfter.TryStartDoAfter(doAfterEventArgs);
return true; return true;
} }
@@ -155,7 +146,8 @@ public sealed class HealingSystem : EntitySystem
public float GetScaledHealingPenalty(EntityUid uid, HealingComponent component) public float GetScaledHealingPenalty(EntityUid uid, HealingComponent component)
{ {
var output = component.Delay; var output = component.Delay;
if (!TryComp<MobThresholdsComponent>(uid, out var mobThreshold) || !TryComp<DamageableComponent>(uid, out var damageable)) if (!TryComp<MobThresholdsComponent>(uid, out var mobThreshold) ||
!TryComp<DamageableComponent>(uid, out var damageable))
return output; return output;
if (!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Critical, out var amount, mobThreshold)) if (!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Critical, out var amount, mobThreshold))
return 1; return 1;
@@ -165,10 +157,4 @@ public sealed class HealingSystem : EntitySystem
var modifier = percentDamage * (component.SelfHealPenaltyMultiplier - 1) + 1; var modifier = percentDamage * (component.SelfHealPenaltyMultiplier - 1) + 1;
return Math.Max(modifier, 1); return Math.Max(modifier, 1);
} }
private record struct HealingData(HealingComponent HealingComponent, StackComponent Stack)
{
public HealingComponent HealingComponent = HealingComponent;
public StackComponent Stack = Stack;
}
} }

View File

@@ -1,4 +1,3 @@
using Content.Server.DoAfter;
using Content.Server.Medical.Components; using Content.Server.Medical.Components;
using Content.Server.Disease; using Content.Server.Disease;
using Content.Server.Popups; using Content.Server.Popups;
@@ -6,6 +5,7 @@ using Content.Shared.Damage;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.MedicalScanner;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using static Content.Shared.MedicalScanner.SharedHealthAnalyzerComponent; using static Content.Shared.MedicalScanner.SharedHealthAnalyzerComponent;
@@ -16,7 +16,7 @@ namespace Content.Server.Medical
{ {
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly DiseaseSystem _disease = default!; [Dependency] private readonly DiseaseSystem _disease = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
@@ -25,7 +25,7 @@ namespace Content.Server.Medical
base.Initialize(); base.Initialize();
SubscribeLocalEvent<HealthAnalyzerComponent, ActivateInWorldEvent>(HandleActivateInWorld); SubscribeLocalEvent<HealthAnalyzerComponent, ActivateInWorldEvent>(HandleActivateInWorld);
SubscribeLocalEvent<HealthAnalyzerComponent, AfterInteractEvent>(OnAfterInteract); SubscribeLocalEvent<HealthAnalyzerComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<HealthAnalyzerComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<HealthAnalyzerComponent, HealthAnalyzerDoAfterEvent>(OnDoAfter);
} }
private void HandleActivateInWorld(EntityUid uid, HealthAnalyzerComponent healthAnalyzer, ActivateInWorldEvent args) private void HandleActivateInWorld(EntityUid uid, HealthAnalyzerComponent healthAnalyzer, ActivateInWorldEvent args)
@@ -40,11 +40,10 @@ namespace Content.Server.Medical
_audio.PlayPvs(healthAnalyzer.ScanningBeginSound, uid); _audio.PlayPvs(healthAnalyzer.ScanningBeginSound, uid);
_doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, healthAnalyzer.ScanDelay, target: args.Target, used:uid) _doAfterSystem.TryStartDoAfter(new DoAfterArgs(args.User, healthAnalyzer.ScanDelay, new HealthAnalyzerDoAfterEvent(), uid, target: args.Target, used: uid)
{ {
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnStun = true,
NeedHand = true NeedHand = true
}); });
} }

View File

@@ -1,5 +1,4 @@
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.DoAfter;
using Content.Server.Medical.Components; using Content.Server.Medical.Components;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Actions; using Content.Shared.Actions;
@@ -11,6 +10,7 @@ using Content.Shared.Verbs;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Medical;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Server.Medical namespace Content.Server.Medical
@@ -18,7 +18,7 @@ namespace Content.Server.Medical
public sealed class StethoscopeSystem : EntitySystem public sealed class StethoscopeSystem : EntitySystem
{ {
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
public override void Initialize() public override void Initialize()
@@ -29,7 +29,7 @@ namespace Content.Server.Medical
SubscribeLocalEvent<WearingStethoscopeComponent, GetVerbsEvent<InnateVerb>>(AddStethoscopeVerb); SubscribeLocalEvent<WearingStethoscopeComponent, GetVerbsEvent<InnateVerb>>(AddStethoscopeVerb);
SubscribeLocalEvent<StethoscopeComponent, GetItemActionsEvent>(OnGetActions); SubscribeLocalEvent<StethoscopeComponent, GetItemActionsEvent>(OnGetActions);
SubscribeLocalEvent<StethoscopeComponent, StethoscopeActionEvent>(OnStethoscopeAction); SubscribeLocalEvent<StethoscopeComponent, StethoscopeActionEvent>(OnStethoscopeAction);
SubscribeLocalEvent<StethoscopeComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<StethoscopeComponent, StethoscopeDoAfterEvent>(OnDoAfter);
} }
/// <summary> /// <summary>
@@ -103,11 +103,10 @@ namespace Content.Server.Medical
// construct the doafter and start it // construct the doafter and start it
private void StartListening(EntityUid scope, EntityUid user, EntityUid target, StethoscopeComponent comp) private void StartListening(EntityUid scope, EntityUid user, EntityUid target, StethoscopeComponent comp)
{ {
_doAfterSystem.DoAfter(new DoAfterEventArgs(user, comp.Delay, target: target, used:scope) _doAfterSystem.TryStartDoAfter(new DoAfterArgs(user, comp.Delay, new StethoscopeDoAfterEvent(), scope, target: target, used: scope)
{ {
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnStun = true,
NeedHand = true NeedHand = true
}); });
} }

View File

@@ -99,7 +99,8 @@ public sealed partial class NPCSteeringSystem
if (doorQuery.TryGetComponent(ent, out var door) && door.State != DoorState.Open) if (doorQuery.TryGetComponent(ent, out var door) && door.State != DoorState.Open)
{ {
// TODO: Use the verb. // TODO: Use the verb.
if (door.State != DoorState.Opening && !door.BeingPried)
if (door.State != DoorState.Opening)
_doors.TryPryDoor(ent, uid, uid, door, true); _doors.TryPryDoor(ent, uid, uid, door, true);
return SteeringObstacleStatus.Continuing; return SteeringObstacleStatus.Continuing;

View File

@@ -2,7 +2,6 @@ using Content.Server.AlertLevel;
using Content.Server.Audio; using Content.Server.Audio;
using Content.Server.Chat.Systems; using Content.Server.Chat.Systems;
using Content.Server.Coordinates.Helpers; using Content.Server.Coordinates.Helpers;
using Content.Server.DoAfter;
using Content.Server.Explosion.EntitySystems; using Content.Server.Explosion.EntitySystems;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.Station.Systems; using Content.Server.Station.Systems;
@@ -28,7 +27,7 @@ namespace Content.Server.Nuke
[Dependency] private readonly StationSystem _stationSystem = default!; [Dependency] private readonly StationSystem _stationSystem = default!;
[Dependency] private readonly ServerGlobalSoundSystem _soundSystem = default!; [Dependency] private readonly ServerGlobalSoundSystem _soundSystem = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!; [Dependency] private readonly ChatSystem _chatSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!;
@@ -67,7 +66,7 @@ namespace Content.Server.Nuke
SubscribeLocalEvent<NukeComponent, NukeKeypadEnterMessage>(OnEnterButtonPressed); SubscribeLocalEvent<NukeComponent, NukeKeypadEnterMessage>(OnEnterButtonPressed);
// Doafter events // Doafter events
SubscribeLocalEvent<NukeComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<NukeComponent, NukeDisarmDoAfterEvent>(OnDoAfter);
} }
private void OnInit(EntityUid uid, NukeComponent component, ComponentInit args) private void OnInit(EntityUid uid, NukeComponent component, ComponentInit args)
@@ -559,16 +558,17 @@ namespace Content.Server.Nuke
private void DisarmBombDoafter(EntityUid uid, EntityUid user, NukeComponent nuke) private void DisarmBombDoafter(EntityUid uid, EntityUid user, NukeComponent nuke)
{ {
var doafter = new DoAfterEventArgs(user, nuke.DisarmDoafterLength, target: uid) var doafter = new DoAfterArgs(user, nuke.DisarmDoafterLength, new NukeDisarmDoAfterEvent(), uid, target: uid)
{ {
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
NeedHand = true NeedHand = true
}; };
_doAfterSystem.DoAfter(doafter); if (!_doAfterSystem.TryStartDoAfter(doafter))
return;
_popups.PopupEntity(Loc.GetString("nuke-component-doafter-warning"), user, _popups.PopupEntity(Loc.GetString("nuke-component-doafter-warning"), user,
user, PopupType.LargeCaution); user, PopupType.LargeCaution);
} }

View File

@@ -1,4 +1,5 @@
using Content.Server.Nutrition.EntitySystems; using Content.Server.Nutrition.EntitySystems;
using Content.Shared.DoAfter;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Audio; using Robust.Shared.Audio;
@@ -34,19 +35,6 @@ namespace Content.Server.Nutrition.Components
[DataField("burstSound")] [DataField("burstSound")]
public SoundSpecifier BurstSound = new SoundPathSpecifier("/Audio/Effects/flash_bang.ogg"); public SoundSpecifier BurstSound = new SoundPathSpecifier("/Audio/Effects/flash_bang.ogg");
/// <summary>
/// Is this drink being forced on someone else?
/// </summary>
/// <returns></returns>
[DataField("forceDrink")]
public bool ForceDrink;
/// <summary>
/// Is the entity currently drinking or trying to make someone else drink?
/// </summary>
[DataField("drinking")]
public bool Drinking;
/// <summary> /// <summary>
/// How long it takes to drink this yourself. /// How long it takes to drink this yourself.
/// </summary> /// </summary>

View File

@@ -1,5 +1,6 @@
using Content.Server.Chemistry.EntitySystems; using Content.Server.Chemistry.EntitySystems;
using Content.Server.Nutrition.EntitySystems; using Content.Server.Nutrition.EntitySystems;
using Content.Shared.DoAfter;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -40,18 +41,6 @@ namespace Content.Server.Nutrition.Components
[DataField("eatMessage")] [DataField("eatMessage")]
public string EatMessage = "food-nom"; public string EatMessage = "food-nom";
/// <summary>
/// Is this entity being forcefed?
/// </summary>
[DataField("forceFeed")]
public bool ForceFeed;
/// <summary>
/// Is this entity eating or being fed?
/// </summary>
[DataField(("eating"))]
public bool Eating;
/// <summary> /// <summary>
/// How long it takes to eat the food personally. /// How long it takes to eat the food personally.
/// </summary> /// </summary>

View File

@@ -2,7 +2,6 @@ using Content.Server.Body.Components;
using Content.Server.Body.Systems; using Content.Server.Body.Systems;
using Content.Server.Chemistry.Components.SolutionManager; using Content.Server.Chemistry.Components.SolutionManager;
using Content.Server.Chemistry.EntitySystems; using Content.Server.Chemistry.EntitySystems;
using Content.Server.DoAfter;
using Content.Server.Fluids.EntitySystems; using Content.Server.Fluids.EntitySystems;
using Content.Server.Forensics; using Content.Server.Forensics;
using Content.Server.Nutrition.Components; using Content.Server.Nutrition.Components;
@@ -10,7 +9,6 @@ using Content.Server.Popups;
using Content.Shared.Administration.Logs; using Content.Shared.Administration.Logs;
using Content.Shared.Body.Components; using Content.Shared.Body.Components;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
@@ -21,6 +19,7 @@ using Content.Shared.Interaction;
using Content.Shared.Interaction.Events; using Content.Shared.Interaction.Events;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Content.Shared.Nutrition;
using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.Components;
using Content.Shared.Throwing; using Content.Shared.Throwing;
using Content.Shared.Verbs; using Content.Shared.Verbs;
@@ -42,7 +41,7 @@ namespace Content.Server.Nutrition.EntitySystems
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly BodySystem _bodySystem = default!; [Dependency] private readonly BodySystem _bodySystem = default!;
[Dependency] private readonly StomachSystem _stomachSystem = default!; [Dependency] private readonly StomachSystem _stomachSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
[Dependency] private readonly SpillableSystem _spillableSystem = default!; [Dependency] private readonly SpillableSystem _spillableSystem = default!;
@@ -55,6 +54,7 @@ namespace Content.Server.Nutrition.EntitySystems
{ {
base.Initialize(); base.Initialize();
// TODO add InteractNoHandEvent for entities like mice.
SubscribeLocalEvent<DrinkComponent, SolutionChangedEvent>(OnSolutionChange); SubscribeLocalEvent<DrinkComponent, SolutionChangedEvent>(OnSolutionChange);
SubscribeLocalEvent<DrinkComponent, ComponentInit>(OnDrinkInit); SubscribeLocalEvent<DrinkComponent, ComponentInit>(OnDrinkInit);
SubscribeLocalEvent<DrinkComponent, LandEvent>(HandleLand); SubscribeLocalEvent<DrinkComponent, LandEvent>(HandleLand);
@@ -63,7 +63,7 @@ namespace Content.Server.Nutrition.EntitySystems
SubscribeLocalEvent<DrinkComponent, GetVerbsEvent<AlternativeVerb>>(AddDrinkVerb); SubscribeLocalEvent<DrinkComponent, GetVerbsEvent<AlternativeVerb>>(AddDrinkVerb);
SubscribeLocalEvent<DrinkComponent, ExaminedEvent>(OnExamined); SubscribeLocalEvent<DrinkComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<DrinkComponent, SolutionTransferAttemptEvent>(OnTransferAttempt); SubscribeLocalEvent<DrinkComponent, SolutionTransferAttemptEvent>(OnTransferAttempt);
SubscribeLocalEvent<DrinkComponent, DoAfterEvent<DrinkData>>(OnDoAfter); SubscribeLocalEvent<DrinkComponent, ConsumeDoAfterEvent>(OnDoAfter);
} }
public bool IsEmpty(EntityUid uid, DrinkComponent? component = null) public bool IsEmpty(EntityUid uid, DrinkComponent? component = null)
@@ -218,7 +218,7 @@ namespace Content.Server.Nutrition.EntitySystems
private bool TryDrink(EntityUid user, EntityUid target, DrinkComponent drink, EntityUid item) private bool TryDrink(EntityUid user, EntityUid target, DrinkComponent drink, EntityUid item)
{ {
if (!EntityManager.HasComponent<BodyComponent>(target) || drink.Drinking) if (!EntityManager.HasComponent<BodyComponent>(target))
return false; return false;
if (!drink.Opened) if (!drink.Opened)
@@ -236,16 +236,18 @@ namespace Content.Server.Nutrition.EntitySystems
return true; return true;
} }
if (drinkSolution.Name == null)
return false;
if (_foodSystem.IsMouthBlocked(target, user)) if (_foodSystem.IsMouthBlocked(target, user))
return true; return true;
if (!_interactionSystem.InRangeUnobstructed(user, item, popup: true)) if (!_interactionSystem.InRangeUnobstructed(user, item, popup: true))
return true; return true;
drink.Drinking = true; var forceDrink = user != target;
drink.ForceDrink = user != target;
if (drink.ForceDrink) if (forceDrink)
{ {
var userName = Identity.Entity(user, EntityManager); var userName = Identity.Entity(user, EntityManager);
@@ -263,53 +265,51 @@ namespace Content.Server.Nutrition.EntitySystems
var flavors = _flavorProfileSystem.GetLocalizedFlavorsMessage(user, drinkSolution); var flavors = _flavorProfileSystem.GetLocalizedFlavorsMessage(user, drinkSolution);
var drinkData = new DrinkData(drinkSolution, flavors); var doAfterEventArgs = new DoAfterArgs(
user,
var doAfterEventArgs = new DoAfterEventArgs(user, drink.ForceDrink ? drink.ForceFeedDelay : drink.Delay, forceDrink ? drink.ForceFeedDelay : drink.Delay,
target: target, used: item) new ConsumeDoAfterEvent(drinkSolution.Name, flavors),
eventTarget: item,
target: target,
used: item)
{ {
RaiseOnTarget = user != target, BreakOnUserMove = forceDrink,
RaiseOnUser = false,
BreakOnUserMove = drink.ForceDrink,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true, BreakOnTargetMove = forceDrink,
BreakOnTargetMove = drink.ForceDrink,
MovementThreshold = 0.01f, MovementThreshold = 0.01f,
DistanceThreshold = 1.0f, DistanceThreshold = 1.0f,
NeedHand = true // Mice and the like can eat without hands.
// TODO maybe set this based on some CanEatWithoutHands event or component?
NeedHand = forceDrink,
}; };
_doAfterSystem.DoAfter(doAfterEventArgs, drinkData); _doAfterSystem.TryStartDoAfter(doAfterEventArgs);
return true; return true;
} }
/// <summary> /// <summary>
/// Raised directed at a victim when someone has force fed them a drink. /// Raised directed at a victim when someone has force fed them a drink.
/// </summary> /// </summary>
private void OnDoAfter(EntityUid uid, DrinkComponent component, DoAfterEvent<DrinkData> args) private void OnDoAfter(EntityUid uid, DrinkComponent component, ConsumeDoAfterEvent args)
{ {
if (args.Cancelled) if (args.Handled || args.Cancelled || component.Deleted)
{
component.ForceDrink = false;
component.Drinking = false;
return;
}
if (args.Handled || component.Deleted)
return; return;
if (!TryComp<BodyComponent>(args.Args.Target, out var body)) if (!TryComp<BodyComponent>(args.Args.Target, out var body))
return; return;
component.Drinking = false; if (!_solutionContainerSystem.TryGetSolution(args.Used, args.Solution, out var solution))
return;
var transferAmount = FixedPoint2.Min(component.TransferAmount, args.AdditionalData.DrinkSolution.Volume); var transferAmount = FixedPoint2.Min(component.TransferAmount, solution.Volume);
var drained = _solutionContainerSystem.Drain(uid, args.AdditionalData.DrinkSolution, transferAmount); var drained = _solutionContainerSystem.Drain(uid, solution, transferAmount);
var forceDrink = args.User != args.Target;
//var forceDrink = args.Args.Target.Value != args.Args.User;
if (!_bodySystem.TryGetBodyOrganComponents<StomachComponent>(args.Args.Target.Value, out var stomachs, body)) if (!_bodySystem.TryGetBodyOrganComponents<StomachComponent>(args.Args.Target.Value, out var stomachs, body))
{ {
_popupSystem.PopupEntity(component.ForceDrink ? Loc.GetString("drink-component-try-use-drink-cannot-drink-other") : Loc.GetString("drink-component-try-use-drink-had-enough"), args.Args.Target.Value, args.Args.User); _popupSystem.PopupEntity(forceDrink ? Loc.GetString("drink-component-try-use-drink-cannot-drink-other") : Loc.GetString("drink-component-try-use-drink-had-enough"), args.Args.Target.Value, args.Args.User);
if (HasComp<RefillableSolutionComponent>(args.Args.Target.Value)) if (HasComp<RefillableSolutionComponent>(args.Args.Target.Value))
{ {
@@ -318,7 +318,7 @@ namespace Content.Server.Nutrition.EntitySystems
return; return;
} }
_solutionContainerSystem.Refill(args.Args.Target.Value, args.AdditionalData.DrinkSolution, drained); _solutionContainerSystem.Refill(args.Args.Target.Value, solution, drained);
args.Handled = true; args.Handled = true;
return; return;
} }
@@ -330,21 +330,21 @@ namespace Content.Server.Nutrition.EntitySystems
{ {
_popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough"), args.Args.Target.Value, args.Args.Target.Value); _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough"), args.Args.Target.Value, args.Args.Target.Value);
if (component.ForceDrink) if (forceDrink)
{ {
_popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough-other"), args.Args.Target.Value, args.Args.User); _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough-other"), args.Args.Target.Value, args.Args.User);
_spillableSystem.SpillAt(args.Args.Target.Value, drained, "PuddleSmear"); _spillableSystem.SpillAt(args.Args.Target.Value, drained, "PuddleSmear");
} }
else else
_solutionContainerSystem.TryAddSolution(uid, args.AdditionalData.DrinkSolution, drained); _solutionContainerSystem.TryAddSolution(uid, solution, drained);
args.Handled = true; args.Handled = true;
return; return;
} }
var flavors = args.AdditionalData.FlavorMessage; var flavors = args.FlavorMessage;
if (component.ForceDrink) if (forceDrink)
{ {
var targetName = Identity.Entity(args.Args.Target.Value, EntityManager); var targetName = Identity.Entity(args.Args.Target.Value, EntityManager);
var userName = Identity.Entity(args.Args.User, EntityManager); var userName = Identity.Entity(args.Args.User, EntityManager);
@@ -372,11 +372,9 @@ namespace Content.Server.Nutrition.EntitySystems
_audio.PlayPvs(_audio.GetSound(component.UseSound), args.Args.Target.Value, AudioParams.Default.WithVolume(-2f)); _audio.PlayPvs(_audio.GetSound(component.UseSound), args.Args.Target.Value, AudioParams.Default.WithVolume(-2f));
_reaction.DoEntityReaction(args.Args.Target.Value, args.AdditionalData.DrinkSolution, ReactionMethod.Ingestion); _reaction.DoEntityReaction(args.Args.Target.Value, solution, ReactionMethod.Ingestion);
//TODO: Grab the stomach UIDs somehow without using Owner //TODO: Grab the stomach UIDs somehow without using Owner
_stomachSystem.TryTransferSolution(firstStomach.Value.Comp.Owner, drained, firstStomach.Value.Comp); _stomachSystem.TryTransferSolution(firstStomach.Value.Comp.Owner, drained, firstStomach.Value.Comp);
component.ForceDrink = false;
args.Handled = true; args.Handled = true;
var comp = EnsureComp<ForensicsComponent>(uid); var comp = EnsureComp<ForensicsComponent>(uid);
@@ -421,11 +419,5 @@ namespace Content.Server.Nutrition.EntitySystems
return remainingString; return remainingString;
} }
private record struct DrinkData(Solution DrinkSolution, string FlavorMessage)
{
public readonly Solution DrinkSolution = DrinkSolution;
public readonly string FlavorMessage = FlavorMessage;
}
} }
} }

View File

@@ -1,14 +1,12 @@
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Body.Systems; using Content.Server.Body.Systems;
using Content.Server.Chemistry.EntitySystems; using Content.Server.Chemistry.EntitySystems;
using Content.Server.DoAfter;
using Content.Server.Hands.Components; using Content.Server.Hands.Components;
using Content.Server.Nutrition.Components; using Content.Server.Nutrition.Components;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Administration.Logs; using Content.Shared.Administration.Logs;
using Content.Shared.Body.Components; using Content.Shared.Body.Components;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
@@ -20,6 +18,7 @@ using Content.Shared.Interaction.Events;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Content.Shared.Nutrition;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Player; using Robust.Shared.Player;
@@ -39,7 +38,7 @@ namespace Content.Server.Nutrition.EntitySystems
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
[Dependency] private readonly UtensilSystem _utensilSystem = default!; [Dependency] private readonly UtensilSystem _utensilSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
@@ -51,10 +50,11 @@ namespace Content.Server.Nutrition.EntitySystems
{ {
base.Initialize(); base.Initialize();
// TODO add InteractNoHandEvent for entities like mice.
SubscribeLocalEvent<FoodComponent, UseInHandEvent>(OnUseFoodInHand); SubscribeLocalEvent<FoodComponent, UseInHandEvent>(OnUseFoodInHand);
SubscribeLocalEvent<FoodComponent, AfterInteractEvent>(OnFeedFood); SubscribeLocalEvent<FoodComponent, AfterInteractEvent>(OnFeedFood);
SubscribeLocalEvent<FoodComponent, GetVerbsEvent<AlternativeVerb>>(AddEatVerb); SubscribeLocalEvent<FoodComponent, GetVerbsEvent<AlternativeVerb>>(AddEatVerb);
SubscribeLocalEvent<FoodComponent, DoAfterEvent<FoodData>>(OnDoAfter); SubscribeLocalEvent<FoodComponent, ConsumeDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<InventoryComponent, IngestionAttemptEvent>(OnInventoryIngestAttempt); SubscribeLocalEvent<InventoryComponent, IngestionAttemptEvent>(OnInventoryIngestAttempt);
} }
@@ -66,7 +66,8 @@ namespace Content.Server.Nutrition.EntitySystems
if (ev.Handled) if (ev.Handled)
return; return;
ev.Handled = TryFeed(ev.User, ev.User, uid, foodComponent); ev.Handled = true;
TryFeed(ev.User, ev.User, uid, foodComponent);
} }
/// <summary> /// <summary>
@@ -77,7 +78,8 @@ namespace Content.Server.Nutrition.EntitySystems
if (args.Handled || args.Target == null || !args.CanReach) if (args.Handled || args.Target == null || !args.CanReach)
return; return;
args.Handled = TryFeed(args.User, args.Target.Value, uid, foodComponent); args.Handled = true;
TryFeed(args.User, args.Target.Value, uid, foodComponent);
} }
public bool TryFeed(EntityUid user, EntityUid target, EntityUid food, FoodComponent foodComp) public bool TryFeed(EntityUid user, EntityUid target, EntityUid food, FoodComponent foodComp)
@@ -87,10 +89,10 @@ namespace Content.Server.Nutrition.EntitySystems
return false; return false;
// Target can't be fed or they're already eating // Target can't be fed or they're already eating
if (!EntityManager.HasComponent<BodyComponent>(target) || foodComp.Eating) if (!EntityManager.HasComponent<BodyComponent>(target))
return false; return false;
if (!_solutionContainerSystem.TryGetSolution(food, foodComp.SolutionName, out var foodSolution)) if (!_solutionContainerSystem.TryGetSolution(food, foodComp.SolutionName, out var foodSolution) || foodSolution.Name == null)
return false; return false;
var flavors = _flavorProfileSystem.GetLocalizedFlavorsMessage(food, user, foodSolution); var flavors = _flavorProfileSystem.GetLocalizedFlavorsMessage(food, user, foodSolution);
@@ -105,16 +107,15 @@ namespace Content.Server.Nutrition.EntitySystems
if (IsMouthBlocked(target, user)) if (IsMouthBlocked(target, user))
return false; return false;
if (!TryGetRequiredUtensils(user, foodComp, out var utensils))
return false;
if (!_interactionSystem.InRangeUnobstructed(user, food, popup: true)) if (!_interactionSystem.InRangeUnobstructed(user, food, popup: true))
return true; return true;
foodComp.Eating = true; if (!TryGetRequiredUtensils(user, foodComp, out _))
foodComp.ForceFeed = user != target; return true;
if (foodComp.ForceFeed) var forceFeed = user != target;
if (forceFeed)
{ {
var userName = Identity.Entity(user, EntityManager); var userName = Identity.Entity(user, EntityManager);
_popupSystem.PopupEntity(Loc.GetString("food-system-force-feed", ("user", userName)), _popupSystem.PopupEntity(Loc.GetString("food-system-force-feed", ("user", userName)),
@@ -129,38 +130,31 @@ namespace Content.Server.Nutrition.EntitySystems
_adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(target):target} is eating {ToPrettyString(food):food} {SolutionContainerSystem.ToPrettyString(foodSolution)}"); _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(target):target} is eating {ToPrettyString(food):food} {SolutionContainerSystem.ToPrettyString(foodSolution)}");
} }
var foodData = new FoodData(foodSolution, flavors, utensils); var doAfterEventArgs = new DoAfterArgs(
user,
var doAfterEventArgs = new DoAfterEventArgs(user, foodComp.ForceFeed ? foodComp.ForceFeedDelay : foodComp.Delay, target: target, used: food) forceFeed ? foodComp.ForceFeedDelay : foodComp.Delay,
new ConsumeDoAfterEvent(foodSolution.Name, flavors),
eventTarget: food,
target: target,
used: food)
{ {
RaiseOnTarget = foodComp.ForceFeed, BreakOnUserMove = forceFeed,
RaiseOnUser = false, //causes a crash if mice eat if true
BreakOnUserMove = foodComp.ForceFeed,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true, BreakOnTargetMove = forceFeed,
BreakOnTargetMove = foodComp.ForceFeed,
MovementThreshold = 0.01f, MovementThreshold = 0.01f,
DistanceThreshold = 1.0f, DistanceThreshold = 1.0f,
NeedHand = true // Mice and the like can eat without hands.
// TODO maybe set this based on some CanEatWithoutHands event or component?
NeedHand = forceFeed,
}; };
_doAfterSystem.DoAfter(doAfterEventArgs, foodData); _doAfterSystem.TryStartDoAfter(doAfterEventArgs);
return true; return true;
} }
private void OnDoAfter(EntityUid uid, FoodComponent component, DoAfterEvent<FoodData> args) private void OnDoAfter(EntityUid uid, FoodComponent component, ConsumeDoAfterEvent args)
{ {
//Prevents the target from being force fed food but allows the user to chow down if (args.Cancelled || args.Handled || component.Deleted || args.Args.Target == null)
if (args.Cancelled)
{
component.Eating = false;
component.ForceFeed = false;
return;
}
if (args.Handled || component.Deleted || args.Args.Target == null)
return; return;
if (!TryComp<BodyComponent>(args.Args.Target.Value, out var body)) if (!TryComp<BodyComponent>(args.Args.Target.Value, out var body))
@@ -169,29 +163,36 @@ namespace Content.Server.Nutrition.EntitySystems
if (!_bodySystem.TryGetBodyOrganComponents<StomachComponent>(args.Args.Target.Value, out var stomachs, body)) if (!_bodySystem.TryGetBodyOrganComponents<StomachComponent>(args.Args.Target.Value, out var stomachs, body))
return; return;
component.Eating = false; if (!_solutionContainerSystem.TryGetSolution(args.Used, args.Solution, out var solution))
return;
var transferAmount = component.TransferAmount != null ? FixedPoint2.Min((FixedPoint2) component.TransferAmount, args.AdditionalData.FoodSolution.Volume) : args.AdditionalData.FoodSolution.Volume; if (!TryGetRequiredUtensils(args.User, component, out var utensils))
return;
var split = _solutionContainerSystem.SplitSolution(uid, args.AdditionalData.FoodSolution, transferAmount); args.Handled = true;
var transferAmount = component.TransferAmount != null ? FixedPoint2.Min((FixedPoint2) component.TransferAmount, solution.Volume) : solution.Volume;
var split = _solutionContainerSystem.SplitSolution(uid, solution, transferAmount);
//TODO: Get the stomach UID somehow without nabbing owner //TODO: Get the stomach UID somehow without nabbing owner
var firstStomach = stomachs.FirstOrNull(stomach => _stomachSystem.CanTransferSolution(stomach.Comp.Owner, split)); var firstStomach = stomachs.FirstOrNull(stomach => _stomachSystem.CanTransferSolution(stomach.Comp.Owner, split));
var forceFeed = args.User != args.Target;
// No stomach so just popup a message that they can't eat. // No stomach so just popup a message that they can't eat.
if (firstStomach == null) if (firstStomach == null)
{ {
_solutionContainerSystem.TryAddSolution(uid, args.AdditionalData.FoodSolution, split); _solutionContainerSystem.TryAddSolution(uid, solution, split);
_popupSystem.PopupEntity(component.ForceFeed ? Loc.GetString("food-system-you-cannot-eat-any-more-other") : Loc.GetString("food-system-you-cannot-eat-any-more"), args.Args.Target.Value, args.Args.User); _popupSystem.PopupEntity(forceFeed ? Loc.GetString("food-system-you-cannot-eat-any-more-other") : Loc.GetString("food-system-you-cannot-eat-any-more"), args.Args.Target.Value, args.Args.User);
args.Handled = true;
return; return;
} }
_reaction.DoEntityReaction(args.Args.Target.Value, args.AdditionalData.FoodSolution, ReactionMethod.Ingestion); _reaction.DoEntityReaction(args.Args.Target.Value, solution, ReactionMethod.Ingestion);
_stomachSystem.TryTransferSolution(firstStomach.Value.Comp.Owner, split, firstStomach.Value.Comp); _stomachSystem.TryTransferSolution(firstStomach.Value.Comp.Owner, split, firstStomach.Value.Comp);
var flavors = args.AdditionalData.FlavorMessage; var flavors = args.FlavorMessage;
if (component.ForceFeed) if (forceFeed)
{ {
var targetName = Identity.Entity(args.Args.Target.Value, EntityManager); var targetName = Identity.Entity(args.Args.Target.Value, EntityManager);
var userName = Identity.Entity(args.Args.User, EntityManager); var userName = Identity.Entity(args.Args.User, EntityManager);
@@ -202,7 +203,6 @@ namespace Content.Server.Nutrition.EntitySystems
// log successful force feed // log successful force feed
_adminLogger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(uid):user} forced {ToPrettyString(args.Args.User):target} to eat {ToPrettyString(uid):food}"); _adminLogger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(uid):user} forced {ToPrettyString(args.Args.User):target} to eat {ToPrettyString(uid):food}");
component.ForceFeed = false;
} }
else else
{ {
@@ -215,26 +215,19 @@ namespace Content.Server.Nutrition.EntitySystems
_audio.Play(component.UseSound, Filter.Pvs(args.Args.Target.Value), args.Args.Target.Value, true, AudioParams.Default.WithVolume(-1f)); _audio.Play(component.UseSound, Filter.Pvs(args.Args.Target.Value), args.Args.Target.Value, true, AudioParams.Default.WithVolume(-1f));
// Try to break all used utensils // Try to break all used utensils
//TODO: Replace utensil owner with actual UID foreach (var utensil in utensils)
foreach (var utensil in args.AdditionalData.Utensils)
{ {
_utensilSystem.TryBreak(utensil.Owner, args.Args.User); _utensilSystem.TryBreak(utensil, args.Args.User);
} }
if (component.UsesRemaining > 0) if (component.UsesRemaining > 0)
{
args.Handled = true;
return; return;
}
if (string.IsNullOrEmpty(component.TrashPrototype)) if (string.IsNullOrEmpty(component.TrashPrototype))
EntityManager.QueueDeleteEntity(uid); EntityManager.QueueDeleteEntity(uid);
else else
DeleteAndSpawnTrash(component, uid, args.Args.User); DeleteAndSpawnTrash(component, uid, args.Args.User);
args.Handled = true;
} }
private void DeleteAndSpawnTrash(FoodComponent component, EntityUid food, EntityUid? user = null) private void DeleteAndSpawnTrash(FoodComponent component, EntityUid food, EntityUid? user = null)
@@ -329,9 +322,9 @@ namespace Content.Server.Nutrition.EntitySystems
} }
private bool TryGetRequiredUtensils(EntityUid user, FoodComponent component, private bool TryGetRequiredUtensils(EntityUid user, FoodComponent component,
out List<UtensilComponent> utensils, HandsComponent? hands = null) out List<EntityUid> utensils, HandsComponent? hands = null)
{ {
utensils = new List<UtensilComponent>(); utensils = new List<EntityUid>();
if (component.Utensil != UtensilType.None) if (component.Utensil != UtensilType.None)
return true; return true;
@@ -352,7 +345,7 @@ namespace Content.Server.Nutrition.EntitySystems
{ {
// Add to used list // Add to used list
usedTypes |= utensil.Types; usedTypes |= utensil.Types;
utensils.Add(utensil); utensils.Add(item);
} }
} }
@@ -415,12 +408,5 @@ namespace Content.Server.Nutrition.EntitySystems
return attempt.Cancelled; return attempt.Cancelled;
} }
private record struct FoodData(Solution FoodSolution, string FlavorMessage, List<UtensilComponent> Utensils)
{
public readonly Solution FoodSolution = FoodSolution;
public readonly string FlavorMessage = FlavorMessage;
public readonly List<UtensilComponent> Utensils = Utensils;
}
} }
} }

View File

@@ -4,11 +4,13 @@ using Content.Server.Power.Components;
using Content.Shared.Access.Components; using Content.Shared.Access.Components;
using Content.Shared.Access.Systems; using Content.Shared.Access.Systems;
using Content.Shared.APC; using Content.Shared.APC;
using Content.Shared.DoAfter;
using Content.Shared.Emag.Components; using Content.Shared.Emag.Components;
using Content.Shared.Emag.Systems; using Content.Shared.Emag.Systems;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Power;
using Content.Shared.Tools; using Content.Shared.Tools;
using Content.Shared.Tools.Components; using Content.Shared.Tools.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
@@ -28,6 +30,7 @@ namespace Content.Server.Power.EntitySystems
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly SharedToolSystem _toolSystem = default!; [Dependency] private readonly SharedToolSystem _toolSystem = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
private const float ScrewTime = 2f; private const float ScrewTime = 2f;
@@ -43,7 +46,7 @@ namespace Content.Server.Power.EntitySystems
SubscribeLocalEvent<ApcComponent, ApcToggleMainBreakerMessage>(OnToggleMainBreaker); SubscribeLocalEvent<ApcComponent, ApcToggleMainBreakerMessage>(OnToggleMainBreaker);
SubscribeLocalEvent<ApcComponent, GotEmaggedEvent>(OnEmagged); SubscribeLocalEvent<ApcComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<ApcToolFinishedEvent>(OnToolFinished); SubscribeLocalEvent<ApcComponent, ApcToolFinishedEvent>(OnToolFinished);
SubscribeLocalEvent<ApcComponent, InteractUsingEvent>(OnInteractUsing); SubscribeLocalEvent<ApcComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<ApcComponent, ExaminedEvent>(OnExamine); SubscribeLocalEvent<ApcComponent, ExaminedEvent>(OnExamine);
@@ -220,27 +223,21 @@ namespace Content.Server.Power.EntitySystems
if (!EntityManager.TryGetComponent(args.Used, out ToolComponent? tool)) if (!EntityManager.TryGetComponent(args.Used, out ToolComponent? tool))
return; return;
var toolEvData = new ToolEventData(new ApcToolFinishedEvent(uid), fuel: 0f); if (_toolSystem.UseTool(args.Used, args.User, uid, ScrewTime, "Screwing", new ApcToolFinishedEvent(), toolComponent:tool))
if (_toolSystem.UseTool(args.Used, args.User, uid, ScrewTime, new [] { "Screwing" }, toolEvData, toolComponent:tool))
args.Handled = true; args.Handled = true;
} }
private void OnToolFinished(ApcToolFinishedEvent args) private void OnToolFinished(EntityUid uid, ApcComponent component, ApcToolFinishedEvent args)
{ {
if (!EntityManager.TryGetComponent(args.Target, out ApcComponent? component)) if (!args.Cancelled)
return; return;
component.IsApcOpen = !component.IsApcOpen; component.IsApcOpen = !component.IsApcOpen;
UpdatePanelAppearance(uid, apc: component);
if (TryComp(args.Target, out AppearanceComponent? appearance)) // this will play on top of the normal screw driver tool sound.
{ var sound = component.IsApcOpen ? component.ScrewdriverOpenSound : component.ScrewdriverCloseSound;
UpdatePanelAppearance(args.Target, appearance); _audio.PlayPvs(sound, uid);
}
if (component.IsApcOpen)
SoundSystem.Play(component.ScrewdriverOpenSound.GetSound(), Filter.Pvs(args.Target), args.Target);
else
SoundSystem.Play(component.ScrewdriverCloseSound.GetSound(), Filter.Pvs(args.Target), args.Target);
} }
private void UpdatePanelAppearance(EntityUid uid, AppearanceComponent? appearance = null, ApcComponent? apc = null) private void UpdatePanelAppearance(EntityUid uid, AppearanceComponent? appearance = null, ApcComponent? apc = null)
@@ -251,16 +248,6 @@ namespace Content.Server.Power.EntitySystems
_appearance.SetData(uid, ApcVisuals.PanelState, GetPanelState(apc), appearance); _appearance.SetData(uid, ApcVisuals.PanelState, GetPanelState(apc), appearance);
} }
private sealed class ApcToolFinishedEvent : EntityEventArgs
{
public EntityUid Target { get; }
public ApcToolFinishedEvent(EntityUid target)
{
Target = target;
}
}
private void OnExamine(EntityUid uid, ApcComponent component, ExaminedEvent args) private void OnExamine(EntityUid uid, ApcComponent component, ExaminedEvent args)
{ {
args.PushMarkup(Loc.GetString(component.IsApcOpen args.PushMarkup(Loc.GetString(component.IsApcOpen

View File

@@ -3,6 +3,7 @@ using Content.Server.Electrocution;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Stack; using Content.Server.Stack;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.DoAfter;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Tools; using Content.Shared.Tools;
using Content.Shared.Tools.Components; using Content.Shared.Tools.Components;
@@ -26,7 +27,7 @@ public sealed partial class CableSystem : EntitySystem
InitializeCablePlacer(); InitializeCablePlacer();
SubscribeLocalEvent<CableComponent, InteractUsingEvent>(OnInteractUsing); SubscribeLocalEvent<CableComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<CableComponent, CuttingFinishedEvent>(OnCableCut); SubscribeLocalEvent<CableComponent, CableCuttingFinishedEvent>(OnCableCut);
// Shouldn't need re-anchoring. // Shouldn't need re-anchoring.
SubscribeLocalEvent<CableComponent, AnchorStateChangedEvent>(OnAnchorChanged); SubscribeLocalEvent<CableComponent, AnchorStateChangedEvent>(OnAnchorChanged);
} }
@@ -36,12 +37,14 @@ public sealed partial class CableSystem : EntitySystem
if (args.Handled) if (args.Handled)
return; return;
var toolEvData = new ToolEventData(new CuttingFinishedEvent(args.User), targetEntity: uid); args.Handled = _toolSystem.UseTool(args.Used, args.User, uid, cable.CuttingDelay, cable.CuttingQuality, new CableCuttingFinishedEvent());
args.Handled = _toolSystem.UseTool(args.Used, args.User, uid, cable.CuttingDelay, new[] { cable.CuttingQuality }, toolEvData);
} }
private void OnCableCut(EntityUid uid, CableComponent cable, CuttingFinishedEvent args) private void OnCableCut(EntityUid uid, CableComponent cable, DoAfterEvent args)
{ {
if (args.Cancelled)
return;
if (_electrocutionSystem.TryDoElectrifiedAct(uid, args.User)) if (_electrocutionSystem.TryDoElectrifiedAct(uid, args.User))
return; return;
@@ -67,13 +70,3 @@ public sealed partial class CableSystem : EntitySystem
QueueDel(uid); QueueDel(uid);
} }
} }
public sealed class CuttingFinishedEvent : EntityEventArgs
{
public EntityUid User;
public CuttingFinishedEvent(EntityUid user)
{
User = user;
}
}

View File

@@ -39,7 +39,5 @@ namespace Content.Server.RCD.Components
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite)] [DataField("ammo")] [ViewVariables(VVAccess.ReadWrite)] [DataField("ammo")]
public int CurrentAmmo = DefaultAmmoCount; public int CurrentAmmo = DefaultAmmoCount;
public CancellationTokenSource? CancelToken = null;
} }
} }

View File

@@ -1,6 +1,4 @@
using System.Threading;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.DoAfter;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.RCD.Components; using Content.Server.RCD.Components;
using Content.Shared.Database; using Content.Shared.Database;
@@ -23,7 +21,7 @@ namespace Content.Server.RCD.Systems
[Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly TagSystem _tagSystem = default!;
@@ -59,14 +57,6 @@ namespace Content.Server.RCD.Systems
if (args.Handled || !args.CanReach) if (args.Handled || !args.CanReach)
return; return;
if (rcd.CancelToken != null)
{
rcd.CancelToken?.Cancel();
rcd.CancelToken = null;
args.Handled = true;
return;
}
if (!args.ClickLocation.IsValid(EntityManager)) return; if (!args.ClickLocation.IsValid(EntityManager)) return;
var clickLocationMod = args.ClickLocation; var clickLocationMod = args.ClickLocation;
@@ -96,19 +86,15 @@ namespace Content.Server.RCD.Systems
var user = args.User; var user = args.User;
//Using an RCD isn't instantaneous //Using an RCD isn't instantaneous
rcd.CancelToken = new CancellationTokenSource(); var doAfterEventArgs = new DoAfterArgs(user, rcd.Delay, new AwaitedDoAfterEvent(), null, target: args.Target)
var doAfterEventArgs = new DoAfterEventArgs(user, rcd.Delay, rcd.CancelToken.Token, args.Target)
{ {
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true,
NeedHand = true, NeedHand = true,
ExtraCheck = () => IsRCDStillValid(rcd, args, mapGrid, tile, 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); var result = await _doAfterSystem.WaitDoAfter(doAfterEventArgs);
rcd.CancelToken = null;
if (result == DoAfterStatus.Cancelled) if (result == DoAfterStatus.Cancelled)
return; return;

View File

@@ -1,14 +1,16 @@
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.DoAfter;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Repairable;
using Content.Shared.Tools; using Content.Shared.Tools;
using Content.Shared.Tools.Components; using Content.Shared.Tools.Components;
namespace Content.Server.Repairable namespace Content.Server.Repairable
{ {
public sealed class RepairableSystem : EntitySystem public sealed class RepairableSystem : SharedRepairableSystem
{ {
[Dependency] private readonly SharedToolSystem _toolSystem = default!; [Dependency] private readonly SharedToolSystem _toolSystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!;
@@ -22,6 +24,9 @@ namespace Content.Server.Repairable
private void OnRepairFinished(EntityUid uid, RepairableComponent component, RepairFinishedEvent args) private void OnRepairFinished(EntityUid uid, RepairableComponent component, RepairFinishedEvent args)
{ {
if (args.Cancelled)
return;
if (!EntityManager.TryGetComponent(uid, out DamageableComponent? damageable) || damageable.TotalDamage == 0) if (!EntityManager.TryGetComponent(uid, out DamageableComponent? damageable) || damageable.TotalDamage == 0)
return; return;
@@ -38,15 +43,17 @@ namespace Content.Server.Repairable
_adminLogger.Add(LogType.Healed, $"{ToPrettyString(args.User):user} repaired {ToPrettyString(uid):target} back to full health"); _adminLogger.Add(LogType.Healed, $"{ToPrettyString(args.User):user} repaired {ToPrettyString(uid):target} back to full health");
} }
uid.PopupMessage(args.User, uid.PopupMessage(args.User,
Loc.GetString("comp-repairable-repair", Loc.GetString("comp-repairable-repair",
("target", uid), ("target", uid),
("tool", args.Used))); ("tool", args.Used!)));
} }
public async void Repair(EntityUid uid, RepairableComponent component, InteractUsingEvent args) public async void Repair(EntityUid uid, RepairableComponent component, InteractUsingEvent args)
{ {
if (args.Handled)
return;
// Only try repair the target if it is damaged // Only try repair the target if it is damaged
if (!EntityManager.TryGetComponent(uid, out DamageableComponent? damageable) || damageable.TotalDamage == 0) if (!EntityManager.TryGetComponent(uid, out DamageableComponent? damageable) || damageable.TotalDamage == 0)
return; return;
@@ -57,25 +64,8 @@ namespace Content.Server.Repairable
if (args.User == args.Target) if (args.User == args.Target)
delay *= component.SelfRepairPenalty; delay *= component.SelfRepairPenalty;
var toolEvData = new ToolEventData(new RepairFinishedEvent(args.User, args.Used), component.FuelCost, targetEntity:uid);
// Can the tool actually repair this, does it have enough fuel? // Can the tool actually repair this, does it have enough fuel?
if (!_toolSystem.UseTool(args.Used, args.User, uid, delay, component.QualityNeeded, toolEvData, component.FuelCost)) args.Handled = !_toolSystem.UseTool(args.Used, args.User, uid, delay, component.QualityNeeded, new RepairFinishedEvent(), component.FuelCost);
return;
args.Handled = true;
}
}
public sealed class RepairFinishedEvent : EntityEventArgs
{
public EntityUid User;
public EntityUid Used;
public RepairFinishedEvent(EntityUid user, EntityUid used)
{
User = user;
Used = used;
} }
} }
} }

View File

@@ -1,4 +1,4 @@
using System.Threading; using Content.Shared.DoAfter;
namespace Content.Server.Resist; namespace Content.Server.Resist;
@@ -11,8 +11,8 @@ public sealed class CanEscapeInventoryComponent : Component
[DataField("baseResistTime")] [DataField("baseResistTime")]
public float BaseResistTime = 5f; public float BaseResistTime = 5f;
[DataField("isEscaping")] public bool IsEscaping => DoAfter != null;
public bool IsEscaping;
public CancellationTokenSource? CancelToken; [DataField("doAfter")]
public DoAfterId? DoAfter;
} }

View File

@@ -1,5 +1,3 @@
using System.Threading;
using Content.Server.DoAfter;
using Content.Server.Contests; using Content.Server.Contests;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Content.Server.Popups; using Content.Server.Popups;
@@ -10,12 +8,13 @@ using Content.Shared.ActionBlocker;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Movement.Events; using Content.Shared.Movement.Events;
using Content.Shared.Interaction.Events; using Content.Shared.Interaction.Events;
using Content.Shared.Resist;
namespace Content.Server.Resist; namespace Content.Server.Resist;
public sealed class EscapeInventorySystem : EntitySystem public sealed class EscapeInventorySystem : EntitySystem
{ {
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
@@ -32,7 +31,7 @@ public sealed class EscapeInventorySystem : EntitySystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<CanEscapeInventoryComponent, MoveInputEvent>(OnRelayMovement); SubscribeLocalEvent<CanEscapeInventoryComponent, MoveInputEvent>(OnRelayMovement);
SubscribeLocalEvent<CanEscapeInventoryComponent, DoAfterEvent<EscapeInventoryEvent>>(OnEscape); SubscribeLocalEvent<CanEscapeInventoryComponent, EscapeInventoryEvent>(OnEscape);
SubscribeLocalEvent<CanEscapeInventoryComponent, DroppedEvent>(OnDropped); SubscribeLocalEvent<CanEscapeInventoryComponent, DroppedEvent>(OnDropped);
} }
@@ -69,50 +68,37 @@ public sealed class EscapeInventorySystem : EntitySystem
if (component.IsEscaping) if (component.IsEscaping)
return; return;
component.CancelToken = new CancellationTokenSource(); var doAfterEventArgs = new DoAfterArgs(user, component.BaseResistTime * multiplier, new EscapeInventoryEvent(), user, target: container)
component.IsEscaping = true;
var escapeEvent = new EscapeInventoryEvent();
var doAfterEventArgs = new DoAfterEventArgs(user, component.BaseResistTime * multiplier, cancelToken: component.CancelToken.Token, target:container)
{ {
BreakOnTargetMove = false, BreakOnTargetMove = false,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true,
NeedHand = false NeedHand = false
}; };
if (!_doAfterSystem.TryStartDoAfter(doAfterEventArgs, out component.DoAfter))
return;
Dirty(component);
_popupSystem.PopupEntity(Loc.GetString("escape-inventory-component-start-resisting"), user, user); _popupSystem.PopupEntity(Loc.GetString("escape-inventory-component-start-resisting"), user, user);
_popupSystem.PopupEntity(Loc.GetString("escape-inventory-component-start-resisting-target"), container, container); _popupSystem.PopupEntity(Loc.GetString("escape-inventory-component-start-resisting-target"), container, container);
_doAfterSystem.DoAfter(doAfterEventArgs, escapeEvent);
} }
private void OnEscape(EntityUid uid, CanEscapeInventoryComponent component, DoAfterEvent<EscapeInventoryEvent> args) private void OnEscape(EntityUid uid, CanEscapeInventoryComponent component, EscapeInventoryEvent args)
{ {
if (args.Cancelled) component.DoAfter = null;
{ Dirty(component);
component.CancelToken = null;
component.IsEscaping = false;
return;
}
if (args.Handled) if (args.Handled || args.Cancelled)
return; return;
Transform(uid).AttachParentToContainerOrGrid(EntityManager); Transform(uid).AttachParentToContainerOrGrid(EntityManager);
component.CancelToken = null;
component.IsEscaping = false;
args.Handled = true; args.Handled = true;
} }
private void OnDropped(EntityUid uid, CanEscapeInventoryComponent component, DroppedEvent args) private void OnDropped(EntityUid uid, CanEscapeInventoryComponent component, DroppedEvent args)
{ {
component.CancelToken?.Cancel(); if (component.DoAfter != null)
component.CancelToken = null; _doAfterSystem.Cancel(component.DoAfter);
}
private sealed class EscapeInventoryEvent : EntityEventArgs
{
} }
} }

View File

@@ -17,6 +17,4 @@ public sealed class ResistLockerComponent : Component
/// </summary> /// </summary>
[ViewVariables] [ViewVariables]
public bool IsResisting = false; public bool IsResisting = false;
public CancellationTokenSource? CancelToken;
} }

View File

@@ -1,5 +1,3 @@
using System.Threading;
using Content.Server.DoAfter;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.Storage.Components; using Content.Server.Storage.Components;
using Content.Server.Storage.EntitySystems; using Content.Server.Storage.EntitySystems;
@@ -7,13 +5,13 @@ using Content.Shared.DoAfter;
using Content.Shared.Lock; using Content.Shared.Lock;
using Content.Shared.Movement.Events; using Content.Shared.Movement.Events;
using Content.Shared.Popups; using Content.Shared.Popups;
using Robust.Shared.Containers; using Content.Shared.Resist;
namespace Content.Server.Resist; namespace Content.Server.Resist;
public sealed class ResistLockerSystem : EntitySystem public sealed class ResistLockerSystem : EntitySystem
{ {
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly LockSystem _lockSystem = default!; [Dependency] private readonly LockSystem _lockSystem = default!;
[Dependency] private readonly EntityStorageSystem _entityStorage = default!; [Dependency] private readonly EntityStorageSystem _entityStorage = default!;
@@ -22,8 +20,7 @@ public sealed class ResistLockerSystem : EntitySystem
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<ResistLockerComponent, ContainerRelayMovementEntityEvent>(OnRelayMovement); SubscribeLocalEvent<ResistLockerComponent, ContainerRelayMovementEntityEvent>(OnRelayMovement);
SubscribeLocalEvent<ResistLockerComponent, DoAfterEvent<LockerDoAfterData>>(OnDoAfter); SubscribeLocalEvent<ResistLockerComponent, ResistLockerDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<ResistLockerComponent, EntRemovedFromContainerMessage>(OnRemoved);
} }
private void OnRelayMovement(EntityUid uid, ResistLockerComponent component, ref ContainerRelayMovementEntityEvent args) private void OnRelayMovement(EntityUid uid, ResistLockerComponent component, ref ContainerRelayMovementEntityEvent args)
@@ -45,34 +42,24 @@ public sealed class ResistLockerSystem : EntitySystem
if (!Resolve(target, ref storageComponent, ref resistLockerComponent)) if (!Resolve(target, ref storageComponent, ref resistLockerComponent))
return; return;
resistLockerComponent.CancelToken = new CancellationTokenSource(); var doAfterEventArgs = new DoAfterArgs(user, resistLockerComponent.ResistTime, new ResistLockerDoAfterEvent(), target, target: target)
var doAfterEventArgs = new DoAfterEventArgs(user, resistLockerComponent.ResistTime, cancelToken:resistLockerComponent.CancelToken.Token, target:target)
{ {
BreakOnTargetMove = false, BreakOnTargetMove = false,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true,
NeedHand = false //No hands 'cause we be kickin' NeedHand = false //No hands 'cause we be kickin'
}; };
resistLockerComponent.IsResisting = true; resistLockerComponent.IsResisting = true;
_popupSystem.PopupEntity(Loc.GetString("resist-locker-component-start-resisting"), user, user, PopupType.Large); _popupSystem.PopupEntity(Loc.GetString("resist-locker-component-start-resisting"), user, user, PopupType.Large);
_doAfterSystem.DoAfter(doAfterEventArgs, new LockerDoAfterData()); _doAfterSystem.TryStartDoAfter(doAfterEventArgs);
} }
private void OnRemoved(EntityUid uid, ResistLockerComponent component, EntRemovedFromContainerMessage args) private void OnDoAfter(EntityUid uid, ResistLockerComponent component, DoAfterEvent args)
{
component.CancelToken?.Cancel();
component.CancelToken = null;
}
private void OnDoAfter(EntityUid uid, ResistLockerComponent component, DoAfterEvent<LockerDoAfterData> args)
{ {
if (args.Cancelled) if (args.Cancelled)
{ {
component.IsResisting = false; component.IsResisting = false;
component.CancelToken = null;
_popupSystem.PopupEntity(Loc.GetString("resist-locker-component-resist-interrupted"), args.Args.User, args.Args.User, PopupType.Medium); _popupSystem.PopupEntity(Loc.GetString("resist-locker-component-resist-interrupted"), args.Args.User, args.Args.User, PopupType.Medium);
return; return;
} }
@@ -93,11 +80,6 @@ public sealed class ResistLockerSystem : EntitySystem
_entityStorage.TryOpenStorage(args.Args.User, uid); _entityStorage.TryOpenStorage(args.Args.User, uid);
} }
component.CancelToken = null;
args.Handled = true; args.Handled = true;
} }
private struct LockerDoAfterData
{
}
} }

View File

@@ -26,6 +26,7 @@ using Content.Shared.Mobs;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Content.Shared.Revenant.Components; using Content.Shared.Revenant.Components;
using Content.Shared.Revenant.EntitySystems;
using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Components;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -46,8 +47,8 @@ public sealed partial class RevenantSystem
private void InitializeAbilities() private void InitializeAbilities()
{ {
SubscribeLocalEvent<RevenantComponent, InteractNoHandEvent>(OnInteract); SubscribeLocalEvent<RevenantComponent, InteractNoHandEvent>(OnInteract);
SubscribeLocalEvent<RevenantComponent, DoAfterEvent<SoulEvent>>(OnSoulSearch); SubscribeLocalEvent<RevenantComponent, SoulEvent>(OnSoulSearch);
SubscribeLocalEvent<RevenantComponent, DoAfterEvent<HarvestEvent>>(OnHarvest); SubscribeLocalEvent<RevenantComponent, HarvestEvent>(OnHarvest);
SubscribeLocalEvent<RevenantComponent, RevenantDefileActionEvent>(OnDefileAction); SubscribeLocalEvent<RevenantComponent, RevenantDefileActionEvent>(OnDefileAction);
SubscribeLocalEvent<RevenantComponent, RevenantOverloadLightsActionEvent>(OnOverloadLightsAction); SubscribeLocalEvent<RevenantComponent, RevenantOverloadLightsActionEvent>(OnOverloadLightsAction);
@@ -84,17 +85,19 @@ public sealed partial class RevenantSystem
private void BeginSoulSearchDoAfter(EntityUid uid, EntityUid target, RevenantComponent revenant) private void BeginSoulSearchDoAfter(EntityUid uid, EntityUid target, RevenantComponent revenant)
{ {
_popup.PopupEntity(Loc.GetString("revenant-soul-searching", ("target", target)), uid, uid, PopupType.Medium); var searchDoAfter = new DoAfterArgs(uid, revenant.SoulSearchDuration, new SoulEvent(), uid, target: target)
var soulSearchEvent = new SoulEvent();
var searchDoAfter = new DoAfterEventArgs(uid, revenant.SoulSearchDuration, target:target)
{ {
BreakOnUserMove = true, BreakOnUserMove = true,
DistanceThreshold = 2 DistanceThreshold = 2
}; };
_doAfter.DoAfter(searchDoAfter, soulSearchEvent);
if (!_doAfter.TryStartDoAfter(searchDoAfter))
return;
_popup.PopupEntity(Loc.GetString("revenant-soul-searching", ("target", target)), uid, uid, PopupType.Medium);
} }
private void OnSoulSearch(EntityUid uid, RevenantComponent component, DoAfterEvent<SoulEvent> args) private void OnSoulSearch(EntityUid uid, RevenantComponent component, SoulEvent args)
{ {
if (args.Handled || args.Cancelled) if (args.Handled || args.Cancelled)
return; return;
@@ -135,25 +138,25 @@ public sealed partial class RevenantSystem
return; return;
} }
var harvestEvent = new HarvestEvent(); var doAfter = new DoAfterArgs(uid, revenant.HarvestDebuffs.X, new HarvestEvent(), uid, target: target)
var doAfter = new DoAfterEventArgs(uid, revenant.HarvestDebuffs.X, target:target)
{ {
DistanceThreshold = 2, DistanceThreshold = 2,
BreakOnUserMove = true, BreakOnUserMove = true,
NeedHand = false RequireCanInteract = false, // stuns itself
}; };
if (!_doAfter.TryStartDoAfter(doAfter))
return;
_appearance.SetData(uid, RevenantVisuals.Harvesting, true); _appearance.SetData(uid, RevenantVisuals.Harvesting, true);
_popup.PopupEntity(Loc.GetString("revenant-soul-begin-harvest", ("target", target)), _popup.PopupEntity(Loc.GetString("revenant-soul-begin-harvest", ("target", target)),
target, PopupType.Large); target, PopupType.Large);
TryUseAbility(uid, revenant, 0, revenant.HarvestDebuffs); TryUseAbility(uid, revenant, 0, revenant.HarvestDebuffs);
_doAfter.DoAfter(doAfter, harvestEvent);
} }
private void OnHarvest(EntityUid uid, RevenantComponent component, DoAfterEvent<HarvestEvent> args) private void OnHarvest(EntityUid uid, RevenantComponent component, HarvestEvent args)
{ {
if (args.Cancelled) if (args.Cancelled)
{ {
@@ -327,14 +330,4 @@ public sealed partial class RevenantSystem
_emag.DoEmagEffect(ent, ent); //it emags itself. spooky. _emag.DoEmagEffect(ent, ent); //it emags itself. spooky.
} }
} }
private sealed class SoulEvent : EntityEventArgs
{
}
private sealed class HarvestEvent : EntityEventArgs
{
}
} }

View File

@@ -3,7 +3,6 @@ using Content.Shared.Popups;
using Content.Shared.Alert; using Content.Shared.Alert;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Server.DoAfter;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Shared.Stunnable; using Content.Shared.Stunnable;
using Content.Shared.Revenant; using Content.Shared.Revenant;
@@ -17,6 +16,7 @@ using Content.Shared.Actions.ActionTypes;
using Content.Shared.Tag; using Content.Shared.Tag;
using Content.Server.Store.Components; using Content.Server.Store.Components;
using Content.Server.Store.Systems; using Content.Server.Store.Systems;
using Content.Shared.DoAfter;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Maps; using Content.Shared.Maps;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
@@ -32,7 +32,7 @@ public sealed partial class RevenantSystem : EntitySystem
[Dependency] private readonly ActionsSystem _action = default!; [Dependency] private readonly ActionsSystem _action = default!;
[Dependency] private readonly AlertsSystem _alerts = default!; [Dependency] private readonly AlertsSystem _alerts = default!;
[Dependency] private readonly DamageableSystem _damage = default!; [Dependency] private readonly DamageableSystem _damage = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly PhysicsSystem _physics = default!; [Dependency] private readonly PhysicsSystem _physics = default!;

View File

@@ -1,22 +1,20 @@
using Content.Server.DoAfter;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.Sticky.Components; using Content.Server.Sticky.Components;
using Content.Server.Sticky.Events; using Content.Server.Sticky.Events;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Hands.EntitySystems; using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Sticky;
using Content.Shared.Sticky.Components; using Content.Shared.Sticky.Components;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Robust.Server.GameObjects;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Player;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Server.Sticky.Systems; namespace Content.Server.Sticky.Systems;
public sealed class StickySystem : EntitySystem public sealed class StickySystem : EntitySystem
{ {
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
@@ -28,7 +26,7 @@ public sealed class StickySystem : EntitySystem
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<StickyComponent, DoAfterEvent>(OnStickSuccessful); SubscribeLocalEvent<StickyComponent, StickyDoAfterEvent>(OnStickFinished);
SubscribeLocalEvent<StickyComponent, AfterInteractEvent>(OnAfterInteract); SubscribeLocalEvent<StickyComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<StickyComponent, GetVerbsEvent<Verb>>(AddUnstickVerb); SubscribeLocalEvent<StickyComponent, GetVerbsEvent<Verb>>(AddUnstickVerb);
} }
@@ -88,9 +86,8 @@ public sealed class StickySystem : EntitySystem
component.Stick = true; component.Stick = true;
// start sticking object to target // start sticking object to target
_doAfterSystem.DoAfter(new DoAfterEventArgs(user, delay, target: target, used: uid) _doAfterSystem.TryStartDoAfter(new DoAfterArgs(user, delay, new StickyDoAfterEvent(), uid, target: target, used: uid)
{ {
BreakOnStun = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
NeedHand = true NeedHand = true
@@ -105,14 +102,13 @@ public sealed class StickySystem : EntitySystem
return true; return true;
} }
private void OnStickSuccessful(EntityUid uid, StickyComponent component, DoAfterEvent args) private void OnStickFinished(EntityUid uid, StickyComponent component, DoAfterEvent args)
{ {
if (args.Handled || args.Cancelled || args.Args.Target == null) if (args.Handled || args.Cancelled || args.Args.Target == null)
return; return;
if (component.Stick) if (component.Stick)
StickToEntity(uid, args.Args.Target.Value, args.Args.User, component); StickToEntity(uid, args.Args.Target.Value, args.Args.User, component);
else else
UnstickFromEntity(uid, args.Args.User, component); UnstickFromEntity(uid, args.Args.User, component);
@@ -137,9 +133,8 @@ public sealed class StickySystem : EntitySystem
component.Stick = false; component.Stick = false;
// start unsticking object // start unsticking object
_doAfterSystem.DoAfter(new DoAfterEventArgs(user, delay, target: uid) _doAfterSystem.TryStartDoAfter(new DoAfterArgs(user, delay, new StickyDoAfterEvent(), uid, target: uid)
{ {
BreakOnStun = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
NeedHand = true NeedHand = true

View File

@@ -1,5 +1,4 @@
using System.Linq; using System.Linq;
using Content.Server.DoAfter;
using Content.Server.Explosion.EntitySystems; using Content.Server.Explosion.EntitySystems;
using Content.Server.Mind.Components; using Content.Server.Mind.Components;
using Content.Server.Resist; using Content.Server.Resist;
@@ -11,6 +10,7 @@ using Content.Shared.Coordinates;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Lock; using Content.Shared.Lock;
using Content.Shared.Storage.Components; using Content.Shared.Storage.Components;
using Content.Shared.Storage.EntitySystems;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Timing; using Robust.Shared.Timing;
@@ -23,7 +23,7 @@ public sealed class BluespaceLockerSystem : EntitySystem
[Dependency] private readonly EntityStorageSystem _entityStorage = default!; [Dependency] private readonly EntityStorageSystem _entityStorage = default!;
[Dependency] private readonly WeldableSystem _weldableSystem = default!; [Dependency] private readonly WeldableSystem _weldableSystem = default!;
[Dependency] private readonly LockSystem _lockSystem = default!; [Dependency] private readonly LockSystem _lockSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly ExplosionSystem _explosionSystem = default!; [Dependency] private readonly ExplosionSystem _explosionSystem = default!;
public override void Initialize() public override void Initialize()
@@ -33,7 +33,7 @@ public sealed class BluespaceLockerSystem : EntitySystem
SubscribeLocalEvent<BluespaceLockerComponent, ComponentStartup>(OnStartup); SubscribeLocalEvent<BluespaceLockerComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<BluespaceLockerComponent, StorageBeforeOpenEvent>(PreOpen); SubscribeLocalEvent<BluespaceLockerComponent, StorageBeforeOpenEvent>(PreOpen);
SubscribeLocalEvent<BluespaceLockerComponent, StorageAfterCloseEvent>(PostClose); SubscribeLocalEvent<BluespaceLockerComponent, StorageAfterCloseEvent>(PostClose);
SubscribeLocalEvent<BluespaceLockerComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<BluespaceLockerComponent, BluespaceLockerDoAfterEvent>(OnDoAfter);
} }
private void OnStartup(EntityUid uid, BluespaceLockerComponent component, ComponentStartup args) private void OnStartup(EntityUid uid, BluespaceLockerComponent component, ComponentStartup args)
@@ -284,7 +284,7 @@ public sealed class BluespaceLockerSystem : EntitySystem
{ {
EnsureComp<DoAfterComponent>(uid); EnsureComp<DoAfterComponent>(uid);
_doAfterSystem.DoAfter(new DoAfterEventArgs(uid, component.BehaviorProperties.Delay)); _doAfterSystem.TryStartDoAfter(new DoAfterArgs(uid, component.BehaviorProperties.Delay, new BluespaceLockerDoAfterEvent(), uid));
return; return;
} }

View File

@@ -1,11 +1,10 @@
using System.Threading;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Server.Storage.Components; using Content.Server.Storage.Components;
using Content.Shared.Storage.Components; using Content.Shared.Storage.Components;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Content.Server.Disposal.Unit.Components; using Content.Server.Disposal.Unit.Components;
using Content.Server.Disposal.Unit.EntitySystems; using Content.Server.Disposal.Unit.EntitySystems;
using Content.Server.DoAfter; using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Placeable; using Content.Shared.Placeable;
using Content.Shared.Storage; using Content.Shared.Storage;
@@ -17,7 +16,7 @@ namespace Content.Server.Storage.EntitySystems
{ {
public sealed class DumpableSystem : EntitySystem public sealed class DumpableSystem : EntitySystem
{ {
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly DisposalUnitSystem _disposalUnitSystem = default!; [Dependency] private readonly DisposalUnitSystem _disposalUnitSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedContainerSystem _container = default!;
@@ -28,16 +27,19 @@ namespace Content.Server.Storage.EntitySystems
SubscribeLocalEvent<DumpableComponent, AfterInteractEvent>(OnAfterInteract, after: new[]{ typeof(StorageSystem) }); SubscribeLocalEvent<DumpableComponent, AfterInteractEvent>(OnAfterInteract, after: new[]{ typeof(StorageSystem) });
SubscribeLocalEvent<DumpableComponent, GetVerbsEvent<AlternativeVerb>>(AddDumpVerb); SubscribeLocalEvent<DumpableComponent, GetVerbsEvent<AlternativeVerb>>(AddDumpVerb);
SubscribeLocalEvent<DumpableComponent, GetVerbsEvent<UtilityVerb>>(AddUtilityVerbs); SubscribeLocalEvent<DumpableComponent, GetVerbsEvent<UtilityVerb>>(AddUtilityVerbs);
SubscribeLocalEvent<DumpableComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<DumpableComponent, DumpableDoAfterEvent>(OnDoAfter);
} }
private void OnAfterInteract(EntityUid uid, DumpableComponent component, AfterInteractEvent args) private void OnAfterInteract(EntityUid uid, DumpableComponent component, AfterInteractEvent args)
{ {
if (!args.CanReach) if (!args.CanReach || args.Handled)
return;
if (!HasComp<DisposalUnitComponent>(args.Target) && !HasComp<PlaceableSurfaceComponent>(args.Target))
return; return;
if (HasComp<DisposalUnitComponent>(args.Target) || HasComp<PlaceableSurfaceComponent>(args.Target))
StartDoAfter(uid, args.Target.Value, args.User, component); StartDoAfter(uid, args.Target.Value, args.User, component);
args.Handled = true;
} }
private void AddDumpVerb(EntityUid uid, DumpableComponent dumpable, GetVerbsEvent<AlternativeVerb> args) private void AddDumpVerb(EntityUid uid, DumpableComponent dumpable, GetVerbsEvent<AlternativeVerb> args)
@@ -52,7 +54,7 @@ namespace Content.Server.Storage.EntitySystems
{ {
Act = () => Act = () =>
{ {
StartDoAfter(uid, null, args.User, dumpable);//Had multiplier of 0.6f StartDoAfter(uid, args.Target, args.User, dumpable);//Had multiplier of 0.6f
}, },
Text = Loc.GetString("dump-verb-name"), Text = Loc.GetString("dump-verb-name"),
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/drop.svg.192dpi.png")), Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/drop.svg.192dpi.png")),
@@ -104,12 +106,10 @@ namespace Content.Server.Storage.EntitySystems
float delay = storage.StoredEntities.Count * (float) dumpable.DelayPerItem.TotalSeconds * dumpable.Multiplier; float delay = storage.StoredEntities.Count * (float) dumpable.DelayPerItem.TotalSeconds * dumpable.Multiplier;
_doAfterSystem.DoAfter(new DoAfterEventArgs(userUid, delay, target: targetUid, used: storageUid) _doAfterSystem.TryStartDoAfter(new DoAfterArgs(userUid, delay, new DumpableDoAfterEvent(), storageUid, target: targetUid, used: storageUid)
{ {
RaiseOnTarget = false,
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnStun = true,
NeedHand = true NeedHand = true
}); });
} }

View File

@@ -12,8 +12,6 @@ using Robust.Shared.Containers;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using System.Threading;
using Content.Server.DoAfter;
using Content.Server.Interaction; using Content.Server.Interaction;
using Content.Shared.Hands.EntitySystems; using Content.Shared.Hands.EntitySystems;
using Content.Shared.Item; using Content.Shared.Item;
@@ -33,7 +31,6 @@ using Content.Shared.Containers.ItemSlots;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Implants.Components; using Content.Shared.Implants.Components;
using Content.Shared.Lock; using Content.Shared.Lock;
using Content.Shared.Movement.Events;
using Content.Server.Ghost.Components; using Content.Server.Ghost.Components;
using Content.Server.Administration.Managers; using Content.Server.Administration.Managers;
using Content.Shared.Administration; using Content.Shared.Administration;
@@ -47,7 +44,7 @@ namespace Content.Server.Storage.EntitySystems
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IAdminManager _admin = default!; [Dependency] private readonly IAdminManager _admin = default!;
[Dependency] private readonly ContainerSystem _containerSystem = default!; [Dependency] private readonly ContainerSystem _containerSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!; [Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!;
[Dependency] private readonly EntityStorageSystem _entityStorage = default!; [Dependency] private readonly EntityStorageSystem _entityStorage = default!;
[Dependency] private readonly InteractionSystem _interactionSystem = default!; [Dependency] private readonly InteractionSystem _interactionSystem = default!;
@@ -81,7 +78,7 @@ namespace Content.Server.Storage.EntitySystems
SubscribeLocalEvent<ServerStorageComponent, BoundUIClosedEvent>(OnBoundUIClosed); SubscribeLocalEvent<ServerStorageComponent, BoundUIClosedEvent>(OnBoundUIClosed);
SubscribeLocalEvent<ServerStorageComponent, EntRemovedFromContainerMessage>(OnStorageItemRemoved); SubscribeLocalEvent<ServerStorageComponent, EntRemovedFromContainerMessage>(OnStorageItemRemoved);
SubscribeLocalEvent<ServerStorageComponent, DoAfterEvent<StorageData>>(OnDoAfter); SubscribeLocalEvent<ServerStorageComponent, AreaPickupDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<StorageFillComponent, MapInitEvent>(OnStorageFillMapInit); SubscribeLocalEvent<StorageFillComponent, MapInitEvent>(OnStorageFillMapInit);
} }
@@ -234,16 +231,14 @@ namespace Content.Server.Storage.EntitySystems
//If there's only one then let's be generous //If there's only one then let's be generous
if (validStorables.Count > 1) if (validStorables.Count > 1)
{ {
var storageData = new StorageData(validStorables); var doAfterArgs = new DoAfterArgs(args.User, 0.2f * validStorables.Count, new AreaPickupDoAfterEvent(validStorables), uid, target: uid)
var doAfterArgs = new DoAfterEventArgs(args.User, 0.2f * validStorables.Count, target: uid)
{ {
BreakOnStun = true,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnUserMove = true, BreakOnUserMove = true,
NeedHand = true NeedHand = true
}; };
_doAfterSystem.DoAfter(doAfterArgs, storageData); _doAfterSystem.TryStartDoAfter(doAfterArgs);
} }
return; return;
@@ -278,7 +273,7 @@ namespace Content.Server.Storage.EntitySystems
} }
} }
private void OnDoAfter(EntityUid uid, ServerStorageComponent component, DoAfterEvent<StorageData> args) private void OnDoAfter(EntityUid uid, ServerStorageComponent component, AreaPickupDoAfterEvent args)
{ {
if (args.Handled || args.Cancelled) if (args.Handled || args.Cancelled)
return; return;
@@ -289,7 +284,7 @@ namespace Content.Server.Storage.EntitySystems
var xformQuery = GetEntityQuery<TransformComponent>(); var xformQuery = GetEntityQuery<TransformComponent>();
xformQuery.TryGetComponent(uid, out var xform); xformQuery.TryGetComponent(uid, out var xform);
foreach (var entity in args.AdditionalData.ValidStorables) foreach (var entity in args.Entities)
{ {
// Check again, situation may have changed for some entities, but we'll still pick up any that are valid // Check again, situation may have changed for some entities, but we'll still pick up any that are valid
if (_containerSystem.IsEntityInContainer(entity) if (_containerSystem.IsEntityInContainer(entity)
@@ -667,10 +662,5 @@ namespace Content.Server.Storage.EntitySystems
_popupSystem.PopupEntity(Loc.GetString(message), player, player); _popupSystem.PopupEntity(Loc.GetString(message), player, player);
} }
private record struct StorageData(List<EntityUid> validStorables)
{
public List<EntityUid> ValidStorables = validStorables;
}
} }
} }

View File

@@ -1,5 +1,4 @@
using System.Linq; using System.Linq;
using Content.Server.DoAfter;
using Content.Server.Ensnaring; using Content.Server.Ensnaring;
using Content.Server.Hands.Components; using Content.Server.Hands.Components;
using Content.Shared.CombatMode; using Content.Shared.CombatMode;
@@ -12,7 +11,6 @@ using Content.Shared.Popups;
using Content.Shared.Strip.Components; using Content.Shared.Strip.Components;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using System.Threading;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Shared.Cuffs; using Content.Shared.Cuffs;
using Content.Shared.Cuffs.Components; using Content.Shared.Cuffs.Components;
@@ -30,8 +28,8 @@ namespace Content.Server.Strip
[Dependency] private readonly SharedCuffableSystem _cuffable = default!; [Dependency] private readonly SharedCuffableSystem _cuffable = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly EnsnareableSystem _ensnaring = default!; [Dependency] private readonly EnsnareableSystem _ensnaring = default!;
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!;
@@ -61,12 +59,12 @@ namespace Content.Server.Strip
if (!TryComp<EnsnaringComponent>(entity, out var ensnaring)) if (!TryComp<EnsnaringComponent>(entity, out var ensnaring))
continue; continue;
_ensnaring.TryFree(uid, entity, ensnaring, user); _ensnaring.TryFree(uid, user, entity, ensnaring);
return; return;
} }
} }
private void OnStripButtonPressed(EntityUid uid, StrippableComponent component, StrippingSlotButtonPressed args) private void OnStripButtonPressed(EntityUid target, StrippableComponent component, StrippingSlotButtonPressed args)
{ {
if (args.Session.AttachedEntity is not {Valid: true} user || if (args.Session.AttachedEntity is not {Valid: true} user ||
!TryComp<HandsComponent>(user, out var userHands)) !TryComp<HandsComponent>(user, out var userHands))
@@ -74,19 +72,19 @@ namespace Content.Server.Strip
if (args.IsHand) if (args.IsHand)
{ {
StripHand(uid, user, args.Slot, component, userHands); StripHand(target, user, args.Slot, component, userHands);
return; return;
} }
if (!TryComp<InventoryComponent>(component.Owner, out var inventory)) if (!TryComp<InventoryComponent>(target, out var inventory))
return; return;
var hasEnt = _inventorySystem.TryGetSlotEntity(component.Owner, args.Slot, out _, inventory); var hasEnt = _inventorySystem.TryGetSlotEntity(target, args.Slot, out var held, inventory);
if (userHands.ActiveHandEntity != null && !hasEnt) if (userHands.ActiveHandEntity != null && !hasEnt)
PlaceActiveHandItemInInventory(user, args.Slot, component); PlaceActiveHandItemInInventory(user, target, userHands.ActiveHandEntity.Value, args.Slot, component);
else if (userHands.ActiveHandEntity == null && hasEnt) else if (userHands.ActiveHandEntity == null && hasEnt)
TakeItemFromInventory(user, args.Slot, component); TakeItemFromInventory(user, target, held!.Value, args.Slot, component);
} }
private void StripHand(EntityUid target, EntityUid user, string handId, StrippableComponent component, HandsComponent userHands) private void StripHand(EntityUid target, EntityUid user, string handId, StrippableComponent component, HandsComponent userHands)
@@ -104,10 +102,10 @@ namespace Content.Server.Strip
return; return;
} }
if (hand.IsEmpty && userHands.ActiveHandEntity != null) if (userHands.ActiveHandEntity != null && hand.HeldEntity == null)
PlaceActiveHandItemInHands(user, handId, component); PlaceActiveHandItemInHands(user, target, userHands.ActiveHandEntity.Value, handId, component);
else if (!hand.IsEmpty && userHands.ActiveHandEntity == null) else if (userHands.ActiveHandEntity == null && hand.HeldEntity != null)
TakeItemFromHands(user, handId, component); TakeItemFromHands(user,target, hand.HeldEntity.Value, handId, component);
} }
public override void StartOpeningStripper(EntityUid user, StrippableComponent component, bool openInCombat = false) public override void StartOpeningStripper(EntityUid user, StrippableComponent component, bool openInCombat = false)
@@ -166,287 +164,291 @@ namespace Content.Server.Strip
if (args.Target == args.User) if (args.Target == args.User)
return; return;
if (!HasComp<ActorComponent>(args.User)) if (!TryComp<ActorComponent>(args.User, out var actor))
return; return;
args.Handled = true;
StartOpeningStripper(args.User, component); StartOpeningStripper(args.User, component);
} }
/// <summary> /// <summary>
/// Places item in user's active hand to an inventory slot. /// Places item in user's active hand to an inventory slot.
/// </summary> /// </summary>
private async void PlaceActiveHandItemInInventory(EntityUid user, string slot, StrippableComponent component) private async void PlaceActiveHandItemInInventory(
EntityUid user,
EntityUid target,
EntityUid held,
string slot,
StrippableComponent component)
{ {
var userHands = Comp<HandsComponent>(user); var userHands = Comp<HandsComponent>(user);
bool Check() bool Check()
{ {
if (userHands.ActiveHand?.HeldEntity is not { } held) if (userHands.ActiveHandEntity != held)
return false;
if (!_handsSystem.CanDropHeld(user, userHands.ActiveHand!))
{ {
user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything")); _popup.PopupCursor(Loc.GetString("strippable-component-cannot-drop"), user);
return false; return false;
} }
if (!_handsSystem.CanDropHeld(user, userHands.ActiveHand)) if (_inventorySystem.TryGetSlotEntity(target, slot, out _))
{ {
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop")); _popup.PopupCursor(Loc.GetString("strippable-component-item-slot-occupied",("owner", target)), user);
return false; return false;
} }
if (!_inventorySystem.HasSlot(component.Owner, slot)) if (!_inventorySystem.CanEquip(user, target, held, slot, out _))
return false;
if (_inventorySystem.TryGetSlotEntity(component.Owner, slot, out _))
{ {
user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-occupied",("owner", component.Owner))); _popup.PopupCursor(Loc.GetString("strippable-component-cannot-equip-message",("owner", target)), user);
return false;
}
if (!_inventorySystem.CanEquip(user, component.Owner, held, slot, out _))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-equip-message",("owner", component.Owner)));
return false; return false;
} }
return true; return true;
} }
if (!_inventorySystem.TryGetSlot(component.Owner, slot, out var slotDef)) if (!_inventorySystem.TryGetSlot(target, slot, out var slotDef))
{ {
Logger.Error($"{ToPrettyString(user)} attempted to place an item in a non-existent inventory slot ({slot}) on {ToPrettyString(component.Owner)}"); Logger.Error($"{ToPrettyString(user)} attempted to place an item in a non-existent inventory slot ({slot}) on {ToPrettyString(target)}");
return; return;
} }
var (time, stealth) = GetStripTimeModifiers(user, component.Owner, slotDef.StripTime); var userEv = new BeforeStripEvent(slotDef.StripTime);
RaiseLocalEvent(user, userEv);
var ev = new BeforeGettingStrippedEvent(userEv.Time, userEv.Stealth);
RaiseLocalEvent(target, ev);
var doAfterArgs = new DoAfterEventArgs(user, time, CancellationToken.None, component.Owner) var doAfterArgs = new DoAfterArgs(user, ev.Time, new AwaitedDoAfterEvent(), null, target: target, used: held)
{ {
ExtraCheck = Check, ExtraCheck = Check,
BreakOnStun = true, AttemptFrequency = AttemptFrequency.EveryTick,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
NeedHand = true, NeedHand = true,
DuplicateCondition = DuplicateConditions.SameTool // Block any other DoAfters featuring this same entity.
}; };
if (!stealth && Check() && userHands.ActiveHandEntity != null) if (!ev.Stealth && Check() && userHands.ActiveHandEntity != null)
{ {
var message = Loc.GetString("strippable-component-alert-owner-insert", var message = Loc.GetString("strippable-component-alert-owner-insert",
("user", Identity.Entity(user, EntityManager)), ("item", userHands.ActiveHandEntity)); ("user", Identity.Entity(user, EntityManager)), ("item", userHands.ActiveHandEntity));
_popupSystem.PopupEntity(message, component.Owner, component.Owner, PopupType.Large); _popup.PopupEntity(message, target, target, PopupType.Large);
} }
var result = await _doAfterSystem.WaitDoAfter(doAfterArgs); var result = await _doAfter.WaitDoAfter(doAfterArgs);
if (result != DoAfterStatus.Finished) return; if (result != DoAfterStatus.Finished) return;
if (userHands.ActiveHand?.HeldEntity is { } held DebugTools.Assert(userHands.ActiveHand?.HeldEntity == held);
&& _handsSystem.TryDrop(user, userHands.ActiveHand, handsComp: userHands))
{
_inventorySystem.TryEquip(user, component.Owner, held, slot);
_adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):user} has placed the item {ToPrettyString(held):item} in {ToPrettyString(component.Owner):target}'s {slot} slot"); if (_handsSystem.TryDrop(user, handsComp: userHands))
{
_inventorySystem.TryEquip(user, target, held, slot);
_adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):user} has placed the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s {slot} slot");
} }
} }
/// <summary> /// <summary>
/// Places item in user's active hand in one of the entity's hands. /// Places item in user's active hand in one of the entity's hands.
/// </summary> /// </summary>
private async void PlaceActiveHandItemInHands(EntityUid user, string handName, StrippableComponent component) private async void PlaceActiveHandItemInHands(
EntityUid user,
EntityUid target,
EntityUid held,
string handName,
StrippableComponent component)
{ {
var hands = Comp<HandsComponent>(component.Owner); var hands = Comp<HandsComponent>(target);
var userHands = Comp<HandsComponent>(user); var userHands = Comp<HandsComponent>(user);
bool Check() bool Check()
{ {
if (userHands.ActiveHandEntity == null) if (userHands.ActiveHandEntity != held)
{
user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything"));
return false; return false;
}
if (!_handsSystem.CanDropHeld(user, userHands.ActiveHand!)) if (!_handsSystem.CanDropHeld(user, userHands.ActiveHand!))
{ {
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop")); _popup.PopupCursor(Loc.GetString("strippable-component-cannot-drop"), user);
return false; return false;
} }
if (!hands.Hands.TryGetValue(handName, out var hand) if (!hands.Hands.TryGetValue(handName, out var hand)
|| !_handsSystem.CanPickupToHand(component.Owner, userHands.ActiveHandEntity.Value, hand, checkActionBlocker: false, hands)) || !_handsSystem.CanPickupToHand(target, userHands.ActiveHandEntity.Value, hand, checkActionBlocker: false, hands))
{ {
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-put-message",("owner", component.Owner))); _popup.PopupCursor(Loc.GetString("strippable-component-cannot-put-message",("owner", target)), user);
return false; return false;
} }
return true; return true;
} }
var (time, stealth) = GetStripTimeModifiers(user, component.Owner, component.HandStripDelay); var userEv = new BeforeStripEvent(component.HandStripDelay);
RaiseLocalEvent(user, userEv);
var ev = new BeforeGettingStrippedEvent(userEv.Time, userEv.Stealth);
RaiseLocalEvent(target, ev);
var doAfterArgs = new DoAfterEventArgs(user, time, CancellationToken.None, component.Owner) var doAfterArgs = new DoAfterArgs(user, ev.Time, new AwaitedDoAfterEvent(), null, target: target, used: held)
{ {
ExtraCheck = Check, ExtraCheck = Check,
BreakOnStun = true, AttemptFrequency = AttemptFrequency.EveryTick,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
NeedHand = true, NeedHand = true,
DuplicateCondition = DuplicateConditions.SameTool
}; };
if (!stealth var result = await _doAfter.WaitDoAfter(doAfterArgs);
&& Check()
&& userHands.Hands.TryGetValue(handName, out var handSlot)
&& handSlot.HeldEntity != null)
{
_popupSystem.PopupEntity(
Loc.GetString("strippable-component-alert-owner-insert",
("user", Identity.Entity(user, EntityManager)),
("item", handSlot.HeldEntity)),
component.Owner, component.Owner, PopupType.Large);
}
var result = await _doAfterSystem.WaitDoAfter(doAfterArgs);
if (result != DoAfterStatus.Finished) return; if (result != DoAfterStatus.Finished) return;
if (userHands.ActiveHandEntity is not { } held)
return;
_handsSystem.TryDrop(user, checkActionBlocker: false, handsComp: userHands); _handsSystem.TryDrop(user, checkActionBlocker: false, handsComp: userHands);
_handsSystem.TryPickup(component.Owner, held, handName, checkActionBlocker: false, animateUser: true, animate: !stealth, handsComp: hands); _handsSystem.TryPickup(target, held, handName, checkActionBlocker: false, animateUser: true, handsComp: hands);
_adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):user} has placed the item {ToPrettyString(held):item} in {ToPrettyString(component.Owner):target}'s hands"); _adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):user} has placed the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s hands");
// hand update will trigger strippable update // hand update will trigger strippable update
} }
/// <summary> /// <summary>
/// Takes an item from the inventory and places it in the user's active hand. /// Takes an item from the inventory and places it in the user's active hand.
/// </summary> /// </summary>
private async void TakeItemFromInventory(EntityUid user, string slot, StrippableComponent component) private async void TakeItemFromInventory(
EntityUid user,
EntityUid target,
EntityUid item,
string slot,
StrippableComponent component)
{ {
bool Check() bool Check()
{ {
if (!_inventorySystem.HasSlot(component.Owner, slot)) if (!_inventorySystem.TryGetSlotEntity(target, slot, out var ent) && ent == item)
return false;
if (!_inventorySystem.TryGetSlotEntity(component.Owner, slot, out _))
{ {
user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", component.Owner))); _popup.PopupCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", target)), user);
return false; return false;
} }
if (!_inventorySystem.CanUnequip(user, component.Owner, slot, out var reason)) if (!_inventorySystem.CanUnequip(user, target, slot, out var reason))
{ {
user.PopupMessageCursor(reason); _popup.PopupCursor(reason, user);
return false; return false;
} }
return true; return true;
} }
if (!_inventorySystem.TryGetSlot(component.Owner, slot, out var slotDef)) if (!_inventorySystem.TryGetSlot(target, slot, out var slotDef))
{ {
Logger.Error($"{ToPrettyString(user)} attempted to take an item from a non-existent inventory slot ({slot}) on {ToPrettyString(component.Owner)}"); Logger.Error($"{ToPrettyString(user)} attempted to take an item from a non-existent inventory slot ({slot}) on {ToPrettyString(target)}");
return; return;
} }
var (time, stealth) = GetStripTimeModifiers(user, component.Owner, slotDef.StripTime); var userEv = new BeforeStripEvent(slotDef.StripTime);
RaiseLocalEvent(user, userEv);
var ev = new BeforeGettingStrippedEvent(userEv.Time, userEv.Stealth);
RaiseLocalEvent(target, ev);
var doAfterArgs = new DoAfterEventArgs(user, time, CancellationToken.None, component.Owner) var doAfterArgs = new DoAfterArgs(user, ev.Time, new AwaitedDoAfterEvent(), null, target: target, used: item)
{ {
ExtraCheck = Check, ExtraCheck = Check,
BreakOnStun = true, AttemptFrequency = AttemptFrequency.EveryTick,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
NeedHand = true,
BreakOnHandChange = false, // allow simultaneously removing multiple items.
DuplicateCondition = DuplicateConditions.SameTool
}; };
if (!stealth && Check()) if (!ev.Stealth && Check())
{ {
if (slotDef.StripHidden) if (slotDef.StripHidden)
{ {
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-hidden", ("slot", slot)), component.Owner, _popup.PopupEntity(Loc.GetString("strippable-component-alert-owner-hidden", ("slot", slot)), target,
component.Owner, PopupType.Large); target, PopupType.Large);
} }
else if (_inventorySystem.TryGetSlotEntity(component.Owner, slot, out var slotItem)) else if (_inventorySystem.TryGetSlotEntity(component.Owner, slot, out var slotItem))
{ {
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner", ("user", Identity.Entity(user, EntityManager)), ("item", slotItem)), component.Owner, _popup.PopupEntity(Loc.GetString("strippable-component-alert-owner", ("user", Identity.Entity(user, EntityManager)), ("item", slotItem)), target,
component.Owner, PopupType.Large); target, PopupType.Large);
} }
} }
var result = await _doAfterSystem.WaitDoAfter(doAfterArgs); var result = await _doAfter.WaitDoAfter(doAfterArgs);
if (result != DoAfterStatus.Finished) return; if (result != DoAfterStatus.Finished) return;
if (_inventorySystem.TryGetSlotEntity(component.Owner, slot, out var item) && _inventorySystem.TryUnequip(user, component.Owner, slot)) if (!_inventorySystem.TryUnequip(user, component.Owner, slot))
{ return;
// Raise a dropped event, so that things like gas tank internals properly deactivate when stripping
RaiseLocalEvent(item.Value, new DroppedEvent(user), true); // Raise a dropped event, so that things like gas tank internals properly deactivate when stripping
RaiseLocalEvent(item, new DroppedEvent(user), true);
_handsSystem.PickupOrDrop(user, item);
_adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):user} has stripped the item {ToPrettyString(item):item} from {ToPrettyString(target):target}");
_handsSystem.PickupOrDrop(user, item.Value, animate: !stealth);
_adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):user} has stripped the item {ToPrettyString(item.Value):item} from {ToPrettyString(component.Owner):target}");
}
} }
/// <summary> /// <summary>
/// Takes an item from a hand and places it in the user's active hand. /// Takes an item from a hand and places it in the user's active hand.
/// </summary> /// </summary>
private async void TakeItemFromHands(EntityUid user, string handName, StrippableComponent component) private async void TakeItemFromHands(EntityUid user, EntityUid target, EntityUid item, string handName, StrippableComponent component)
{ {
var hands = Comp<HandsComponent>(component.Owner); var hands = Comp<HandsComponent>(target);
var userHands = Comp<HandsComponent>(user); var userHands = Comp<HandsComponent>(user);
bool Check() bool Check()
{ {
if (!hands.Hands.TryGetValue(handName, out var hand) || hand.HeldEntity == null) if (!hands.Hands.TryGetValue(handName, out var hand) || hand.HeldEntity != item)
{ {
user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-free-message",("owner", component.Owner))); _popup.PopupCursor(Loc.GetString("strippable-component-item-slot-free-message",("owner", target)), user);
return false; return false;
} }
if (HasComp<HandVirtualItemComponent>(hand.HeldEntity)) if (HasComp<HandVirtualItemComponent>(hand.HeldEntity))
return false; return false;
if (!_handsSystem.CanDropHeld(component.Owner, hand, false)) if (!_handsSystem.CanDropHeld(target, hand, false))
{ {
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop-message",("owner", component.Owner))); _popup.PopupCursor(Loc.GetString("strippable-component-cannot-drop-message",("owner", target)), user);
return false; return false;
} }
return true; return true;
} }
var (time, stealth) = GetStripTimeModifiers(user, component.Owner, component.HandStripDelay); var userEv = new BeforeStripEvent(component.HandStripDelay);
RaiseLocalEvent(user, userEv);
var ev = new BeforeGettingStrippedEvent(userEv.Time, userEv.Stealth);
RaiseLocalEvent(target, ev);
var doAfterArgs = new DoAfterEventArgs(user, time, CancellationToken.None, component.Owner) var doAfterArgs = new DoAfterArgs(user, ev.Time, new AwaitedDoAfterEvent(), null, target: target, used: item)
{ {
ExtraCheck = Check, ExtraCheck = Check,
BreakOnStun = true, AttemptFrequency = AttemptFrequency.EveryTick,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
NeedHand = true,
BreakOnHandChange = false, // allow simultaneously removing multiple items.
DuplicateCondition = DuplicateConditions.SameTool
}; };
if (!stealth if (Check() && hands.Hands.TryGetValue(handName, out var handSlot) && handSlot.HeldEntity != null)
&& Check()
&& hands.Hands.TryGetValue(handName, out var handSlot)
&& handSlot.HeldEntity != null)
{ {
_popupSystem.PopupEntity( _popup.PopupEntity(
Loc.GetString("strippable-component-alert-owner", Loc.GetString("strippable-component-alert-owner",
("user", Identity.Entity(user, EntityManager)), ("user", Identity.Entity(user, EntityManager)), ("item", item)),
("item", handSlot.HeldEntity)), component.Owner,
component.Owner, component.Owner); component.Owner);
} }
var result = await _doAfterSystem.WaitDoAfter(doAfterArgs); var result = await _doAfter.WaitDoAfter(doAfterArgs);
if (result != DoAfterStatus.Finished) return; if (result != DoAfterStatus.Finished) return;
if (!hands.Hands.TryGetValue(handName, out var hand) || hand.HeldEntity is not { } held) _handsSystem.TryDrop(target, item, checkActionBlocker: false, handsComp: hands);
return; _handsSystem.PickupOrDrop(user, item, handsComp: userHands);
_handsSystem.TryDrop(component.Owner, hand, checkActionBlocker: false, handsComp: hands);
_handsSystem.PickupOrDrop(user, held, handsComp: userHands, animate: !stealth);
// hand update will trigger strippable update // hand update will trigger strippable update
_adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):user} has stripped the item {ToPrettyString(held):item} from {ToPrettyString(component.Owner):target}"); _adminLogger.Add(LogType.Stripping, LogImpact.Medium,
$"{ToPrettyString(user):user} has stripped the item {ToPrettyString(item):item} from {ToPrettyString(target):target}");
} }
} }
} }

View File

@@ -1,5 +1,4 @@
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.DoAfter;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Interaction.Events; using Content.Shared.Interaction.Events;
@@ -17,14 +16,14 @@ public sealed class HandTeleporterSystem : EntitySystem
[Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly LinkedEntitySystem _link = default!; [Dependency] private readonly LinkedEntitySystem _link = default!;
[Dependency] private readonly AudioSystem _audio = default!; [Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly DoAfterSystem _doafter = default!; [Dependency] private readonly SharedDoAfterSystem _doafter = default!;
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
{ {
SubscribeLocalEvent<HandTeleporterComponent, UseInHandEvent>(OnUseInHand); SubscribeLocalEvent<HandTeleporterComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<HandTeleporterComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<HandTeleporterComponent, TeleporterDoAfterEvent>(OnDoAfter);
} }
private void OnDoAfter(EntityUid uid, HandTeleporterComponent component, DoAfterEvent args) private void OnDoAfter(EntityUid uid, HandTeleporterComponent component, DoAfterEvent args)
@@ -56,15 +55,14 @@ public sealed class HandTeleporterSystem : EntitySystem
if (xform.ParentUid != xform.GridUid) if (xform.ParentUid != xform.GridUid)
return; return;
var doafterArgs = new DoAfterEventArgs(args.User, component.PortalCreationDelay, used: uid) var doafterArgs = new DoAfterArgs(args.User, component.PortalCreationDelay, new TeleporterDoAfterEvent(), uid, used: uid)
{ {
BreakOnDamage = true, BreakOnDamage = true,
BreakOnStun = true,
BreakOnUserMove = true, BreakOnUserMove = true,
MovementThreshold = 0.5f, MovementThreshold = 0.5f,
}; };
_doafter.DoAfter(doafterArgs); _doafter.TryStartDoAfter(doafterArgs);
} }
} }

View File

@@ -6,6 +6,7 @@ using Content.Server.Storage.EntitySystems;
using Content.Shared.Body.Components; using Content.Shared.Body.Components;
using Content.Shared.Body.Part; using Content.Shared.Body.Part;
using Content.Shared.Buckle.Components; using Content.Shared.Buckle.Components;
using Content.Shared.DoAfter;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.Interaction; using Content.Shared.Interaction;
@@ -39,8 +40,7 @@ namespace Content.Server.Toilet
SubscribeLocalEvent<ToiletComponent, InteractHandEvent>(OnInteractHand, new []{typeof(BuckleSystem)}); SubscribeLocalEvent<ToiletComponent, InteractHandEvent>(OnInteractHand, new []{typeof(BuckleSystem)});
SubscribeLocalEvent<ToiletComponent, ExaminedEvent>(OnExamine); SubscribeLocalEvent<ToiletComponent, ExaminedEvent>(OnExamine);
SubscribeLocalEvent<ToiletComponent, SuicideEvent>(OnSuicide); SubscribeLocalEvent<ToiletComponent, SuicideEvent>(OnSuicide);
SubscribeLocalEvent<ToiletPryFinished>(OnToiletPried); SubscribeLocalEvent<ToiletComponent, ToiletPryDoAfterEvent>(OnToiletPried);
SubscribeLocalEvent<ToiletPryInterrupted>(OnToiletInterrupt);
} }
private void OnSuicide(EntityUid uid, ToiletComponent component, SuicideEvent args) private void OnSuicide(EntityUid uid, ToiletComponent component, SuicideEvent args)
@@ -94,29 +94,15 @@ namespace Content.Server.Toilet
return; return;
// are player trying place or lift of cistern lid? // are player trying place or lift of cistern lid?
if (EntityManager.TryGetComponent(args.Used, out ToolComponent? tool) if (_toolSystem.UseTool(args.Used, args.User, uid, component.PryLidTime, component.PryingQuality, new ToiletPryDoAfterEvent()))
&& tool.Qualities.Contains(component.PryingQuality))
{ {
// check if someone is already prying this toilet
if (component.IsPrying)
return;
component.IsPrying = true;
var toolEvData = new ToolEventData(new ToiletPryFinished(uid));
// try to pry toilet cistern
if (!_toolSystem.UseTool(args.Used, args.User, uid, component.PryLidTime, new [] {component.PryingQuality}, toolEvData))
{
component.IsPrying = false;
return;
}
args.Handled = true; args.Handled = true;
} }
// maybe player trying to hide something inside cistern? // maybe player trying to hide something inside cistern?
else if (component.LidOpen) else if (component.LidOpen)
{ {
args.Handled = _secretStash.TryHideItem(uid, args.User, args.Used); args.Handled = true;
_secretStash.TryHideItem(uid, args.User, args.Used);
} }
} }
@@ -160,22 +146,13 @@ namespace Content.Server.Toilet
} }
} }
private void OnToiletInterrupt(ToiletPryInterrupted ev) private void OnToiletPried(EntityUid uid, ToiletComponent toilet, ToiletPryDoAfterEvent args)
{ {
if (!EntityManager.TryGetComponent(ev.Uid, out ToiletComponent? toilet)) if (args.Cancelled)
return; return;
toilet.IsPrying = false;
}
private void OnToiletPried(ToiletPryFinished ev)
{
if (!EntityManager.TryGetComponent(ev.Uid, out ToiletComponent? toilet))
return;
toilet.IsPrying = false;
toilet.LidOpen = !toilet.LidOpen; toilet.LidOpen = !toilet.LidOpen;
UpdateSprite(ev.Uid, toilet); UpdateSprite(uid, toilet);
} }
public void ToggleToiletSeat(EntityUid uid, ToiletComponent? component = null) public void ToggleToiletSeat(EntityUid uid, ToiletComponent? component = null)
@@ -197,24 +174,4 @@ namespace Content.Server.Toilet
_appearance.SetData(uid, ToiletVisuals.SeatUp, component.IsSeatUp, appearance); _appearance.SetData(uid, ToiletVisuals.SeatUp, component.IsSeatUp, appearance);
} }
} }
public sealed class ToiletPryFinished : EntityEventArgs
{
public EntityUid Uid;
public ToiletPryFinished(EntityUid uid)
{
Uid = uid;
}
}
public sealed class ToiletPryInterrupted : EntityEventArgs
{
public EntityUid Uid;
public ToiletPryInterrupted(EntityUid uid)
{
Uid = uid;
}
}
} }

View File

@@ -6,9 +6,6 @@ namespace Content.Server.Tools.Components;
[RegisterComponent] [RegisterComponent]
public sealed class LatticeCuttingComponent : Component public sealed class LatticeCuttingComponent : Component
{ {
[DataField("toolComponentNeeded")]
public bool ToolComponentNeeded = true;
[DataField("qualityNeeded", customTypeSerializer:typeof(PrototypeIdSerializer<ToolQualityPrototype>))] [DataField("qualityNeeded", customTypeSerializer:typeof(PrototypeIdSerializer<ToolQualityPrototype>))]
public string QualityNeeded = "Cutting"; public string QualityNeeded = "Cutting";

View File

@@ -15,8 +15,5 @@ namespace Content.Server.Tools.Components
[DataField("delay")] [DataField("delay")]
public float Delay = 1f; public float Delay = 1f;
[DataField("cancelToken")]
public CancellationTokenSource? CancelToken;
} }
} }

View File

@@ -47,15 +47,9 @@ public sealed class WeldableComponent : SharedWeldableComponent
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public string? WeldedExamineMessage = "weldable-component-examine-is-welded"; public string? WeldedExamineMessage = "weldable-component-examine-is-welded";
/// <summary>
/// Whether something is currently using a welder on this so DoAfter isn't spammed.
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
public bool BeingWelded;
/// <summary> /// <summary>
/// Is this entity currently welded shut? /// Is this entity currently welded shut?
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadOnly)] [DataField("isWelded")]
public bool IsWelded; public bool IsWelded;
} }

View File

@@ -1,10 +1,12 @@
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Tools.Components; using Content.Server.Tools.Components;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.DoAfter;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Tools; using Content.Shared.Tools;
using Content.Shared.Tools.Components; using Content.Shared.Tools.Components;
using Content.Shared.Tools.Systems;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Physics.Systems; using Robust.Shared.Physics.Systems;
@@ -22,7 +24,6 @@ public sealed class WeldableSystem : EntitySystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<WeldableComponent, InteractUsingEvent>(OnInteractUsing); SubscribeLocalEvent<WeldableComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<WeldableComponent, WeldFinishedEvent>(OnWeldFinished); SubscribeLocalEvent<WeldableComponent, WeldFinishedEvent>(OnWeldFinished);
SubscribeLocalEvent<WeldableComponent, WeldCancelledEvent>(OnWeldCanceled);
SubscribeLocalEvent<LayerChangeOnWeldComponent, WeldableChangedEvent>(OnWeldChanged); SubscribeLocalEvent<LayerChangeOnWeldComponent, WeldableChangedEvent>(OnWeldChanged);
SubscribeLocalEvent<WeldableComponent, ExaminedEvent>(OnExamine); SubscribeLocalEvent<WeldableComponent, ExaminedEvent>(OnExamine);
} }
@@ -47,9 +48,7 @@ public sealed class WeldableSystem : EntitySystem
return false; return false;
// Basic checks // Basic checks
if (!component.Weldable || component.BeingWelded) if (!component.Weldable)
return false;
if (!_toolSystem.HasQuality(tool, component.WeldingQuality))
return false; return false;
// Other component systems // Other component systems
@@ -69,8 +68,8 @@ public sealed class WeldableSystem : EntitySystem
if (!CanWeld(uid, tool, user, component)) if (!CanWeld(uid, tool, user, component))
return false; return false;
var toolEvData = new ToolEventData(new WeldFinishedEvent(user, tool), cancelledEv: new WeldCancelledEvent(),targetEntity: uid); if (!_toolSystem.UseTool(tool, user, uid, component.WeldingTime.Seconds, component.WeldingQuality, new WeldFinishedEvent(), fuel: component.FuelConsumption))
component.BeingWelded = _toolSystem.UseTool(tool, user, uid, component.WeldingTime.Seconds, new[] { component.WeldingQuality }, toolEvData, fuel: component.FuelConsumption); return false;
// Log attempt // Log attempt
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(user):user} is {(component.IsWelded ? "un" : "")}welding {ToPrettyString(uid):target} at {Transform(uid).Coordinates:targetlocation}"); _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(user):user} is {(component.IsWelded ? "un" : "")}welding {ToPrettyString(uid):target} at {Transform(uid).Coordinates:targetlocation}");
@@ -80,10 +79,11 @@ public sealed class WeldableSystem : EntitySystem
private void OnWeldFinished(EntityUid uid, WeldableComponent component, WeldFinishedEvent args) private void OnWeldFinished(EntityUid uid, WeldableComponent component, WeldFinishedEvent args)
{ {
component.BeingWelded = false; if (args.Cancelled || args.Used == null)
return;
// Check if target is still valid // Check if target is still valid
if (!CanWeld(uid, args.Tool, args.User, component)) if (!CanWeld(uid, args.Used.Value, args.User, component))
return; return;
component.IsWelded = !component.IsWelded; component.IsWelded = !component.IsWelded;
@@ -95,11 +95,6 @@ public sealed class WeldableSystem : EntitySystem
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.User):user} {(!component.IsWelded ? "un" : "")}welded {ToPrettyString(uid):target}"); _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.User):user} {(!component.IsWelded ? "un" : "")}welded {ToPrettyString(uid):target}");
} }
private void OnWeldCanceled(EntityUid uid, WeldableComponent component, WeldCancelledEvent args)
{
component.BeingWelded = false;
}
private void OnWeldChanged(EntityUid uid, LayerChangeOnWeldComponent component, WeldableChangedEvent args) private void OnWeldChanged(EntityUid uid, LayerChangeOnWeldComponent component, WeldableChangedEvent args)
{ {
if (!TryComp<FixturesComponent>(uid, out var fixtures)) if (!TryComp<FixturesComponent>(uid, out var fixtures))
@@ -148,30 +143,6 @@ public sealed class WeldableSystem : EntitySystem
return; return;
component.WeldingTime = time; component.WeldingTime = time;
} }
/// <summary>
/// Raised after welding do_after has finished. It doesn't guarantee success,
/// use <see cref="WeldableChangedEvent"/> to get updated status.
/// </summary>
private sealed class WeldFinishedEvent : EntityEventArgs
{
public readonly EntityUid User;
public readonly EntityUid Tool;
public WeldFinishedEvent(EntityUid user, EntityUid tool)
{
User = user;
Tool = tool;
}
}
/// <summary>
/// Raised when entity welding has failed.
/// </summary>
private sealed class WeldCancelledEvent : EntityEventArgs
{
}
} }
/// <summary> /// <summary>

View File

@@ -2,6 +2,7 @@
using Content.Server.Maps; using Content.Server.Maps;
using Content.Server.Tools.Components; using Content.Server.Tools.Components;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.DoAfter;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Maps; using Content.Shared.Maps;
using Content.Shared.Tools.Components; using Content.Shared.Tools.Components;
@@ -22,6 +23,9 @@ public sealed partial class ToolSystem
private void OnLatticeCutComplete(EntityUid uid, LatticeCuttingComponent component, LatticeCuttingCompleteEvent args) private void OnLatticeCutComplete(EntityUid uid, LatticeCuttingComponent component, LatticeCuttingCompleteEvent args)
{ {
if (args.Cancelled)
return;
var gridUid = args.Coordinates.GetGridUid(EntityManager); var gridUid = args.Coordinates.GetGridUid(EntityManager);
if (gridUid == null) if (gridUid == null)
return; return;
@@ -50,10 +54,6 @@ public sealed partial class ToolSystem
private bool TryCut(EntityUid toolEntity, EntityUid user, LatticeCuttingComponent component, EntityCoordinates clickLocation) private bool TryCut(EntityUid toolEntity, EntityUid user, LatticeCuttingComponent component, EntityCoordinates clickLocation)
{ {
ToolComponent? tool = null;
if (component.ToolComponentNeeded && !TryComp<ToolComponent?>(toolEntity, out tool))
return false;
if (!_mapManager.TryGetGrid(clickLocation.GetGridUid(EntityManager), out var mapGrid)) if (!_mapManager.TryGetGrid(clickLocation.GetGridUid(EntityManager), out var mapGrid))
return false; return false;
@@ -71,24 +71,8 @@ public sealed partial class ToolSystem
|| tile.IsBlockedTurf(true)) || tile.IsBlockedTurf(true))
return false; return false;
var toolEvData = new ToolEventData(new LatticeCuttingCompleteEvent(clickLocation, user), targetEntity: toolEntity); var ev = new LatticeCuttingCompleteEvent(clickLocation);
return UseTool(toolEntity, user, toolEntity, component.Delay, component.QualityNeeded, ev);
if (!UseTool(toolEntity, user, null, component.Delay, new[] {component.QualityNeeded}, toolEvData, toolComponent: tool))
return false;
return true;
}
private sealed class LatticeCuttingCompleteEvent : EntityEventArgs
{
public EntityCoordinates Coordinates;
public EntityUid User;
public LatticeCuttingCompleteEvent(EntityCoordinates coordinates, EntityUid user)
{
Coordinates = coordinates;
User = user;
}
} }
} }

View File

@@ -1,6 +1,7 @@
using System.Threading; using System.Threading;
using Content.Server.Fluids.Components; using Content.Server.Fluids.Components;
using Content.Server.Tools.Components; using Content.Server.Tools.Components;
using Content.Shared.DoAfter;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Maps; using Content.Shared.Maps;
using Content.Shared.Tools.Components; using Content.Shared.Tools.Components;
@@ -18,8 +19,7 @@ public sealed partial class ToolSystem
private void InitializeTilePrying() private void InitializeTilePrying()
{ {
SubscribeLocalEvent<TilePryingComponent, AfterInteractEvent>(OnTilePryingAfterInteract); SubscribeLocalEvent<TilePryingComponent, AfterInteractEvent>(OnTilePryingAfterInteract);
SubscribeLocalEvent<TilePryingComponent, TilePryingCompleteEvent>(OnTilePryComplete); SubscribeLocalEvent<TilePryingComponent, TilePryingDoAfterEvent>(OnTilePryComplete);
SubscribeLocalEvent<TilePryingComponent, TilePryingCancelledEvent>(OnTilePryCancelled);
} }
private void OnTilePryingAfterInteract(EntityUid uid, TilePryingComponent component, AfterInteractEvent args) private void OnTilePryingAfterInteract(EntityUid uid, TilePryingComponent component, AfterInteractEvent args)
@@ -30,9 +30,11 @@ public sealed partial class ToolSystem
args.Handled = true; args.Handled = true;
} }
private void OnTilePryComplete(EntityUid uid, TilePryingComponent component, TilePryingCompleteEvent args) private void OnTilePryComplete(EntityUid uid, TilePryingComponent component, TilePryingDoAfterEvent args)
{ {
component.CancelToken = null; if (args.Cancelled)
return;
var gridUid = args.Coordinates.GetGridUid(EntityManager); var gridUid = args.Coordinates.GetGridUid(EntityManager);
if (!_mapManager.TryGetGrid(gridUid, out var grid)) if (!_mapManager.TryGetGrid(gridUid, out var grid))
{ {
@@ -44,14 +46,9 @@ public sealed partial class ToolSystem
_tile.PryTile(tile); _tile.PryTile(tile);
} }
private void OnTilePryCancelled(EntityUid uid, TilePryingComponent component, TilePryingCancelledEvent args)
{
component.CancelToken = null;
}
private bool TryPryTile(EntityUid toolEntity, EntityUid user, TilePryingComponent component, EntityCoordinates clickLocation) private bool TryPryTile(EntityUid toolEntity, EntityUid user, TilePryingComponent component, EntityCoordinates clickLocation)
{ {
if (!TryComp<ToolComponent?>(toolEntity, out var tool) && component.ToolComponentNeeded || component.CancelToken != null) if (!TryComp<ToolComponent?>(toolEntity, out var tool) && component.ToolComponentNeeded)
return false; return false;
if (!_mapManager.TryGetGrid(clickLocation.GetGridUid(EntityManager), out var mapGrid)) if (!_mapManager.TryGetGrid(clickLocation.GetGridUid(EntityManager), out var mapGrid))
@@ -69,28 +66,8 @@ public sealed partial class ToolSystem
if (!tileDef.CanCrowbar) if (!tileDef.CanCrowbar)
return false; return false;
component.CancelToken = new CancellationTokenSource(); var ev = new TilePryingDoAfterEvent(clickLocation);
var toolEvData = new ToolEventData(new TilePryingCompleteEvent(clickLocation), cancelledEv:new TilePryingCancelledEvent() ,targetEntity:toolEntity);
if (!UseTool(toolEntity, user, null, component.Delay, new[] { component.QualityNeeded }, toolEvData, toolComponent: tool, cancelToken: component.CancelToken))
return false;
return true;
}
private sealed class TilePryingCompleteEvent : EntityEventArgs
{
public readonly EntityCoordinates Coordinates;
public TilePryingCompleteEvent(EntityCoordinates coordinates)
{
Coordinates = coordinates;
}
}
private sealed class TilePryingCancelledEvent : EntityEventArgs
{
return UseTool(toolEntity, user, toolEntity, component.Delay, component.QualityNeeded, ev, toolComponent: tool);
} }
} }

View File

@@ -4,6 +4,7 @@ using Content.Server.Chemistry.Components.SolutionManager;
using Content.Server.Chemistry.EntitySystems; using Content.Server.Chemistry.EntitySystems;
using Content.Server.Tools.Components; using Content.Server.Tools.Components;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.DoAfter;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Interaction; using Content.Shared.Interaction;
@@ -15,6 +16,7 @@ using Content.Shared.Weapons.Melee.Events;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Utility;
namespace Content.Server.Tools namespace Content.Server.Tools
{ {
@@ -38,8 +40,8 @@ namespace Content.Server.Tools
SubscribeLocalEvent<WelderComponent, SolutionChangedEvent>(OnWelderSolutionChange); SubscribeLocalEvent<WelderComponent, SolutionChangedEvent>(OnWelderSolutionChange);
SubscribeLocalEvent<WelderComponent, ActivateInWorldEvent>(OnWelderActivate); SubscribeLocalEvent<WelderComponent, ActivateInWorldEvent>(OnWelderActivate);
SubscribeLocalEvent<WelderComponent, AfterInteractEvent>(OnWelderAfterInteract); SubscribeLocalEvent<WelderComponent, AfterInteractEvent>(OnWelderAfterInteract);
SubscribeLocalEvent<WelderComponent, ToolUseAttemptEvent>(OnWelderToolUseAttempt); SubscribeLocalEvent<WelderComponent, DoAfterAttemptEvent<ToolDoAfterEvent>>(OnWelderToolUseAttempt);
SubscribeLocalEvent<WelderComponent, ToolUseFinishAttemptEvent>(OnWelderToolUseFinishAttempt); SubscribeLocalEvent<WelderComponent, ToolDoAfterEvent>(OnWelderDoAfter);
SubscribeLocalEvent<WelderComponent, ComponentShutdown>(OnWelderShutdown); SubscribeLocalEvent<WelderComponent, ComponentShutdown>(OnWelderShutdown);
SubscribeLocalEvent<WelderComponent, ComponentGetState>(OnWelderGetState); SubscribeLocalEvent<WelderComponent, ComponentGetState>(OnWelderGetState);
SubscribeLocalEvent<WelderComponent, MeleeHitEvent>(OnMeleeHit); SubscribeLocalEvent<WelderComponent, MeleeHitEvent>(OnMeleeHit);
@@ -262,56 +264,36 @@ namespace Content.Server.Tools
args.Handled = true; args.Handled = true;
} }
private void OnWelderToolUseAttempt(EntityUid uid, WelderComponent welder, ToolUseAttemptEvent args) private void OnWelderToolUseAttempt(EntityUid uid, WelderComponent welder, DoAfterAttemptEvent<ToolDoAfterEvent> args)
{ {
if (args.Cancelled) DebugTools.Assert(args.Event.Fuel > 0);
return; var user = args.DoAfter.Args.User;
if (!welder.Lit) if (!welder.Lit)
{ {
_popupSystem.PopupEntity(Loc.GetString("welder-component-welder-not-lit-message"), uid, args.User); _popupSystem.PopupEntity(Loc.GetString("welder-component-welder-not-lit-message"), uid, user);
args.Cancel(); args.Cancel();
return; return;
} }
var (fuel, _) = GetWelderFuelAndCapacity(uid, welder); var (fuel, _) = GetWelderFuelAndCapacity(uid, welder);
if (FixedPoint2.New(args.Fuel) > fuel) if (FixedPoint2.New(args.Event.Fuel) > fuel)
{ {
_popupSystem.PopupEntity(Loc.GetString("welder-component-cannot-weld-message"), uid, args.User); _popupSystem.PopupEntity(Loc.GetString("welder-component-cannot-weld-message"), uid, user);
args.Cancel(); args.Cancel();
} }
} }
private void OnWelderToolUseFinishAttempt(EntityUid uid, WelderComponent welder, ToolUseFinishAttemptEvent args) private void OnWelderDoAfter(EntityUid uid, WelderComponent welder, ToolDoAfterEvent args)
{ {
if (args.Cancelled) if (args.Cancelled)
return; return;
if (!welder.Lit)
{
_popupSystem.PopupEntity(Loc.GetString("welder-component-welder-not-lit-message"), uid, args.User);
args.Cancel();
return;
}
var (fuel, _) = GetWelderFuelAndCapacity(uid, welder);
var neededFuel = FixedPoint2.New(args.Fuel);
if (neededFuel > fuel)
{
_popupSystem.PopupEntity(Loc.GetString("welder-component-cannot-weld-message"), uid, args.User);
args.Cancel();
}
if (!_solutionContainerSystem.TryGetSolution(uid, welder.FuelSolution, out var solution)) if (!_solutionContainerSystem.TryGetSolution(uid, welder.FuelSolution, out var solution))
{
args.Cancel();
return; return;
}
solution.RemoveReagent(welder.FuelReagent, neededFuel); solution.RemoveReagent(welder.FuelReagent, FixedPoint2.New(args.Fuel));
_entityManager.Dirty(welder); _entityManager.Dirty(welder);
} }

View File

@@ -1,6 +1,5 @@
using System.Linq; using System.Linq;
using Content.Server.Cargo.Systems; using Content.Server.Cargo.Systems;
using Content.Server.DoAfter;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Popups; using Content.Shared.Popups;
@@ -16,7 +15,7 @@ namespace Content.Server.VendingMachines.Restock
{ {
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly AudioSystem _audioSystem = default!; [Dependency] private readonly AudioSystem _audioSystem = default!;
[Dependency] private readonly PricingSystem _pricingSystem = default!; [Dependency] private readonly PricingSystem _pricingSystem = default!;
@@ -65,7 +64,7 @@ namespace Content.Server.VendingMachines.Restock
private void OnAfterInteract(EntityUid uid, VendingMachineRestockComponent component, AfterInteractEvent args) private void OnAfterInteract(EntityUid uid, VendingMachineRestockComponent component, AfterInteractEvent args)
{ {
if (args.Target == null || !args.CanReach) if (args.Target == null || !args.CanReach || args.Handled)
return; return;
if (!TryComp<VendingMachineComponent>(args.Target, out var machineComponent)) if (!TryComp<VendingMachineComponent>(args.Target, out var machineComponent))
@@ -77,14 +76,19 @@ namespace Content.Server.VendingMachines.Restock
if (!TryAccessMachine(uid, component, machineComponent, args.User, args.Target.Value)) if (!TryAccessMachine(uid, component, machineComponent, args.User, args.Target.Value))
return; return;
_doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, (float) component.RestockDelay.TotalSeconds, target:args.Target, used:uid) args.Handled = true;
var doAfterArgs = new DoAfterArgs(args.User, (float) component.RestockDelay.TotalSeconds, new RestockDoAfterEvent(), args.Target,
target: args.Target, used: uid)
{ {
BreakOnTargetMove = true, BreakOnTargetMove = true,
BreakOnUserMove = true, BreakOnUserMove = true,
BreakOnStun = true,
BreakOnDamage = true, BreakOnDamage = true,
NeedHand = true NeedHand = true
}); };
if (!_doAfterSystem.TryStartDoAfter(doAfterArgs))
return;
_popupSystem.PopupEntity(Loc.GetString("vending-machine-restock-start", ("this", uid), ("user", args.User), ("target", args.Target)), _popupSystem.PopupEntity(Loc.GetString("vending-machine-restock-start", ("this", uid), ("user", args.User), ("target", args.Target)),
args.User, args.User,

View File

@@ -55,7 +55,7 @@ namespace Content.Server.VendingMachines
SubscribeLocalEvent<VendingMachineComponent, VendingMachineSelfDispenseEvent>(OnSelfDispense); SubscribeLocalEvent<VendingMachineComponent, VendingMachineSelfDispenseEvent>(OnSelfDispense);
SubscribeLocalEvent<VendingMachineComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<VendingMachineComponent, RestockDoAfterEvent>(OnDoAfter);
} }
private void OnVendingPrice(EntityUid uid, VendingMachineComponent component, ref PriceCalculationEvent args) private void OnVendingPrice(EntityUid uid, VendingMachineComponent component, ref PriceCalculationEvent args)

View File

@@ -1,4 +1,3 @@
using Content.Server.DoAfter;
using Content.Server.Hands.Components; using Content.Server.Hands.Components;
using Content.Server.Hands.Systems; using Content.Server.Hands.Systems;
using Content.Server.Wieldable.Components; using Content.Server.Wieldable.Components;
@@ -13,13 +12,14 @@ using Robust.Shared.Player;
using Content.Server.Actions.Events; using Content.Server.Actions.Events;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Weapons.Melee.Events; using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Wieldable;
namespace Content.Server.Wieldable namespace Content.Server.Wieldable
{ {
public sealed class WieldableSystem : EntitySystem public sealed class WieldableSystem : EntitySystem
{ {
[Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly HandVirtualItemSystem _virtualItemSystem = default!; [Dependency] private readonly HandVirtualItemSystem _virtualItemSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly SharedItemSystem _itemSystem = default!; [Dependency] private readonly SharedItemSystem _itemSystem = default!;
@@ -31,7 +31,7 @@ namespace Content.Server.Wieldable
base.Initialize(); base.Initialize();
SubscribeLocalEvent<WieldableComponent, UseInHandEvent>(OnUseInHand); SubscribeLocalEvent<WieldableComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<WieldableComponent, DoAfterEvent>(OnDoAfter); SubscribeLocalEvent<WieldableComponent, WieldableDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<WieldableComponent, ItemUnwieldedEvent>(OnItemUnwielded); SubscribeLocalEvent<WieldableComponent, ItemUnwieldedEvent>(OnItemUnwielded);
SubscribeLocalEvent<WieldableComponent, GotUnequippedHandEvent>(OnItemLeaveHand); SubscribeLocalEvent<WieldableComponent, GotUnequippedHandEvent>(OnItemLeaveHand);
SubscribeLocalEvent<WieldableComponent, VirtualItemDeletedEvent>(OnVirtualItemDeleted); SubscribeLocalEvent<WieldableComponent, VirtualItemDeletedEvent>(OnVirtualItemDeleted);
@@ -126,15 +126,13 @@ namespace Content.Server.Wieldable
if (ev.Cancelled) if (ev.Cancelled)
return; return;
var doargs = new DoAfterEventArgs(user, component.WieldTime, used:used) var doargs = new DoAfterArgs(user, component.WieldTime, new WieldableDoAfterEvent(), used, used: used)
{ {
BreakOnUserMove = false, BreakOnUserMove = false,
BreakOnDamage = true, BreakOnDamage = true
BreakOnStun = true,
BreakOnTargetMove = true
}; };
_doAfter.DoAfter(doargs); _doAfter.TryStartDoAfter(doargs);
} }
/// <summary> /// <summary>

View File

@@ -2,7 +2,6 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.DoAfter;
using Content.Server.Hands.Components; using Content.Server.Hands.Components;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
@@ -24,15 +23,14 @@ public sealed class WiresSystem : SharedWiresSystem
{ {
[Dependency] private readonly IPrototypeManager _protoMan = default!; [Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly SharedToolSystem _toolSystem = default!; [Dependency] private readonly SharedToolSystem _toolSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly IRobustRandom _random = default!;
private IRobustRandom _random = new RobustRandom();
// This is where all the wire layouts are stored. // This is where all the wire layouts are stored.
[ViewVariables] private readonly Dictionary<string, WireLayout> _layouts = new(); [ViewVariables] private readonly Dictionary<string, WireLayout> _layouts = new();
@@ -48,15 +46,14 @@ public sealed class WiresSystem : SharedWiresSystem
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset); SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
// this is a broadcast event // this is a broadcast event
SubscribeLocalEvent<WireToolFinishedEvent>(OnToolFinished); SubscribeLocalEvent<WiresPanelComponent, WirePanelDoAfterEvent>(OnPanelDoAfter);
SubscribeLocalEvent<WireToolCanceledEvent>(OnToolCanceled);
SubscribeLocalEvent<WiresComponent, ComponentStartup>(OnWiresStartup); SubscribeLocalEvent<WiresComponent, ComponentStartup>(OnWiresStartup);
SubscribeLocalEvent<WiresComponent, WiresActionMessage>(OnWiresActionMessage); SubscribeLocalEvent<WiresComponent, WiresActionMessage>(OnWiresActionMessage);
SubscribeLocalEvent<WiresComponent, InteractUsingEvent>(OnInteractUsing); SubscribeLocalEvent<WiresComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<WiresComponent, MapInitEvent>(OnMapInit); SubscribeLocalEvent<WiresComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<WiresComponent, TimedWireEvent>(OnTimedWire); SubscribeLocalEvent<WiresComponent, TimedWireEvent>(OnTimedWire);
SubscribeLocalEvent<WiresComponent, PowerChangedEvent>(OnWiresPowered); SubscribeLocalEvent<WiresComponent, PowerChangedEvent>(OnWiresPowered);
SubscribeLocalEvent<WiresComponent, DoAfterEvent<WireExtraData>>(OnDoAfter); SubscribeLocalEvent<WiresComponent, WireDoAfterEvent>(OnDoAfter);
} }
private void SetOrCreateWireLayout(EntityUid uid, WiresComponent? wires = null) private void SetOrCreateWireLayout(EntityUid uid, WiresComponent? wires = null)
@@ -437,86 +434,64 @@ public sealed class WiresSystem : SharedWiresSystem
TryDoWireAction(uid, player, activeHandEntity, args.Id, args.Action, component, tool); TryDoWireAction(uid, player, activeHandEntity, args.Id, args.Action, component, tool);
} }
private void OnDoAfter(EntityUid uid, WiresComponent component, DoAfterEvent<WireExtraData> args) private void OnDoAfter(EntityUid uid, WiresComponent component, WireDoAfterEvent args)
{ {
if (args.Cancelled) if (args.Cancelled)
{ {
component.WiresQueue.Remove(args.AdditionalData.Id); component.WiresQueue.Remove(args.Id);
return; return;
} }
if (args.Handled || args.Args.Target == null || args.Args.Used == null) if (args.Handled || args.Args.Target == null || args.Args.Used == null)
return; return;
UpdateWires(args.Args.Target.Value, args.Args.User, args.Args.Used.Value, args.AdditionalData.Id, args.AdditionalData.Action, component); UpdateWires(args.Args.Target.Value, args.Args.User, args.Args.Used.Value, args.Id, args.Action, component);
args.Handled = true; args.Handled = true;
} }
private void OnInteractUsing(EntityUid uid, WiresComponent component, InteractUsingEvent args) private void OnInteractUsing(EntityUid uid, WiresComponent component, InteractUsingEvent args)
{ {
if (args.Handled)
return;
if (!TryComp<ToolComponent>(args.Used, out var tool) || !TryComp<WiresPanelComponent>(uid, out var panel)) if (!TryComp<ToolComponent>(args.Used, out var tool) || !TryComp<WiresPanelComponent>(uid, out var panel))
return; return;
if (panel.Open && if (panel.Open &&
(_toolSystem.HasQuality(args.Used, "Cutting", tool) || (_toolSystem.HasQuality(args.Used, "Cutting", tool) ||
_toolSystem.HasQuality(args.Used, "Pulsing", tool))) _toolSystem.HasQuality(args.Used, "Pulsing", tool)))
{ {
if (EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) if (TryComp(args.User, out ActorComponent? actor))
{ _uiSystem.TryOpen(uid, WiresUiKey.Key, actor.PlayerSession);
var ui = _uiSystem.GetUiOrNull(uid, WiresUiKey.Key);
if (ui != null)
_uiSystem.OpenUi(ui, actor.PlayerSession);
args.Handled = true;
} }
} else if (_toolSystem.UseTool(args.Used, args.User, uid, ScrewTime, "Screwing", new WirePanelDoAfterEvent(), toolComponent: tool))
else if (!panel.IsScrewing && _toolSystem.HasQuality(args.Used, "Screwing", tool))
{ {
var toolEvData = new ToolEventData(new WireToolFinishedEvent(uid, args.User), cancelledEv: new WireToolCanceledEvent(uid)); _adminLogger.Add(LogType.Action, LogImpact.Low,
$"{ToPrettyString(args.User):user} is screwing {ToPrettyString(uid):target}'s {(panel.Open ? "open" : "closed")} maintenance panel at {Transform(uid).Coordinates:targetlocation}");
panel.IsScrewing = _toolSystem.UseTool(args.Used, args.User, uid, ScrewTime, new[] { "Screwing" }, toolEvData, toolComponent: tool);
args.Handled = panel.IsScrewing;
// Log attempt
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.User):user} is screwing {ToPrettyString(uid):target}'s {(panel.Open ? "open" : "closed")} maintenance panel at {Transform(uid).Coordinates:targetlocation}");
} }
} }
private void OnToolFinished(WireToolFinishedEvent args) private void OnPanelDoAfter(EntityUid uid, WiresPanelComponent panel, WirePanelDoAfterEvent args)
{ {
if (!TryComp<WiresPanelComponent>((args.Target), out var panel)) if (args.Cancelled)
return; return;
panel.IsScrewing = false; TogglePanel(uid, panel, !panel.Open);
TogglePanel(args.Target, panel, !panel.Open); UpdateAppearance(uid, panel);
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.User):user} screwed {ToPrettyString(uid):target}'s maintenance panel {(panel.Open ? "open" : "closed")}");
// Log success
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.User):user} screwed {ToPrettyString(args.Target):target}'s maintenance panel {(panel.Open ? "open" : "closed")}");
if (panel.Open) if (panel.Open)
{ {
_audio.PlayPvs(panel.ScrewdriverOpenSound, args.Target); _audio.PlayPvs(panel.ScrewdriverOpenSound, uid);
} }
else else
{ {
_audio.PlayPvs(panel.ScrewdriverCloseSound, args.Target); _audio.PlayPvs(panel.ScrewdriverCloseSound, uid);
var ui = _uiSystem.GetUiOrNull(args.Target, WiresUiKey.Key); _uiSystem.TryCloseAll(uid, WiresUiKey.Key);
if (ui != null)
{
_uiSystem.CloseAll(ui);
} }
} }
Dirty(panel);
}
private void OnToolCanceled(WireToolCanceledEvent ev)
{
if (!TryComp<WiresPanelComponent>(ev.Target, out var component))
return;
component.IsScrewing = false;
}
private void OnMapInit(EntityUid uid, WiresComponent component, MapInitEvent args) private void OnMapInit(EntityUid uid, WiresComponent component, MapInitEvent args)
{ {
EnsureComp<WiresPanelComponent>(uid); EnsureComp<WiresPanelComponent>(uid);
@@ -662,16 +637,16 @@ public sealed class WiresSystem : SharedWiresSystem
_appearance.SetData(uid, WiresVisuals.MaintenancePanelState, panel.Open && panel.Visible, appearance); _appearance.SetData(uid, WiresVisuals.MaintenancePanelState, panel.Open && panel.Visible, appearance);
} }
private void TryDoWireAction(EntityUid used, EntityUid user, EntityUid toolEntity, int id, WiresAction action, WiresComponent? wires = null, ToolComponent? tool = null) private void TryDoWireAction(EntityUid target, EntityUid user, EntityUid toolEntity, int id, WiresAction action, WiresComponent? wires = null, ToolComponent? tool = null)
{ {
if (!Resolve(used, ref wires) if (!Resolve(target, ref wires)
|| !Resolve(toolEntity, ref tool)) || !Resolve(toolEntity, ref tool))
return; return;
if (wires.WiresQueue.Contains(id)) if (wires.WiresQueue.Contains(id))
return; return;
var wire = TryGetWire(used, id, wires); var wire = TryGetWire(target, id, wires);
if (wire == null) if (wire == null)
return; return;
@@ -726,29 +701,21 @@ public sealed class WiresSystem : SharedWiresSystem
if (_toolTime > 0f) if (_toolTime > 0f)
{ {
var data = new WireExtraData(action, id); var args = new DoAfterArgs(user, _toolTime, new WireDoAfterEvent(action, id), target, target: target, used: toolEntity)
var args = new DoAfterEventArgs(user, _toolTime, target: used, used: toolEntity)
{ {
NeedHand = true, NeedHand = true,
BreakOnStun = true,
BreakOnDamage = true, BreakOnDamage = true,
BreakOnUserMove = true BreakOnUserMove = true
}; };
_doAfter.DoAfter(args, data); _doAfter.TryStartDoAfter(args);
} }
else else
{ {
UpdateWires(used, user, toolEntity, id, action, wires); UpdateWires(target, user, toolEntity, id, action, wires);
} }
} }
private record struct WireExtraData(WiresAction Action, int Id)
{
public WiresAction Action = Action;
public int Id = Id;
}
private void UpdateWires(EntityUid used, EntityUid user, EntityUid toolEntity, int id, WiresAction action, WiresComponent? wires = null, ToolComponent? tool = null) private void UpdateWires(EntityUid used, EntityUid user, EntityUid toolEntity, int id, WiresAction action, WiresComponent? wires = null, ToolComponent? tool = null)
{ {
if (!Resolve(used, ref wires)) if (!Resolve(used, ref wires))
@@ -786,7 +753,7 @@ public sealed class WiresSystem : SharedWiresSystem
break; break;
} }
_toolSystem.PlayToolSound(toolEntity, tool); _toolSystem.PlayToolSound(toolEntity, tool, user);
if (wire.Action == null || wire.Action.Cut(user, wire)) if (wire.Action == null || wire.Action.Cut(user, wire))
{ {
wire.IsCut = true; wire.IsCut = true;
@@ -807,7 +774,7 @@ public sealed class WiresSystem : SharedWiresSystem
break; break;
} }
_toolSystem.PlayToolSound(toolEntity, tool); _toolSystem.PlayToolSound(toolEntity, tool, user);
if (wire.Action == null || wire.Action.Mend(user, wire)) if (wire.Action == null || wire.Action.Mend(user, wire))
{ {
wire.IsCut = false; wire.IsCut = false;
@@ -921,25 +888,8 @@ public sealed class WiresSystem : SharedWiresSystem
private void Reset(RoundRestartCleanupEvent args) private void Reset(RoundRestartCleanupEvent args)
{ {
_layouts.Clear(); _layouts.Clear();
_random = new RobustRandom();
} }
#endregion #endregion
#region Events
private sealed class WireToolFinishedEvent : EntityEventArgs
{
public EntityUid User { get; }
public EntityUid Target { get; }
public WireToolFinishedEvent(EntityUid target, EntityUid user)
{
Target = target;
User = user;
}
}
public record struct WireToolCanceledEvent(EntityUid Target);
#endregion
} }
public sealed class Wire public sealed class Wire

View File

@@ -72,7 +72,7 @@ namespace Content.Shared.ActionBlocker
if (ev.Cancelled) if (ev.Cancelled)
return false; return false;
if (target == null) if (target == null || target == user)
return true; return true;
var targetEv = new GettingInteractedWithAttemptEvent(user, target); var targetEv = new GettingInteractedWithAttemptEvent(user, target);

View File

@@ -1,3 +1,4 @@
using Content.Shared.DoAfter;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.AirlockPainter namespace Content.Shared.AirlockPainter
@@ -29,4 +30,22 @@ namespace Content.Shared.AirlockPainter
SelectedStyle = selectedStyle; SelectedStyle = selectedStyle;
} }
} }
[Serializable, NetSerializable]
public sealed class AirlockPainterDoAfterEvent : DoAfterEvent
{
[DataField("sprite", required: true)]
public readonly string Sprite = default!;
private AirlockPainterDoAfterEvent()
{
}
public AirlockPainterDoAfterEvent(string sprite)
{
Sprite = sprite;
}
public override DoAfterEvent Clone() => this;
}
} }

View File

@@ -0,0 +1,9 @@
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;
namespace Content.Shared.Anomaly;
[Serializable, NetSerializable]
public sealed class ScannerDoAfterEvent : SimpleDoAfterEvent
{
}

View File

@@ -1,21 +1,21 @@
using Content.Shared.DoAfter;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.Chemistry.Components namespace Content.Shared.Chemistry.Components
{ {
[Serializable, NetSerializable]
public sealed class InjectorDoAfterEvent : SimpleDoAfterEvent
{
}
/// <summary> /// <summary>
/// Shared class for injectors & syringes /// Shared class for injectors & syringes
/// </summary> /// </summary>
[NetworkedComponent, ComponentProtoName("Injector")] [NetworkedComponent, ComponentProtoName("Injector")]
public abstract class SharedInjectorComponent : Component public abstract class SharedInjectorComponent : Component
{ {
/// <summary>
/// Checks to see if the entity being injected
/// </summary>
[DataField("isInjecting")]
public bool IsInjecting;
/// <summary> /// <summary>
/// Component data used for net updates. Used by client for item status ui /// Component data used for net updates. Used by client for item status ui
/// </summary> /// </summary>
@@ -26,7 +26,8 @@ namespace Content.Shared.Chemistry.Components
public FixedPoint2 TotalVolume { get; } public FixedPoint2 TotalVolume { get; }
public InjectorToggleMode CurrentMode { get; } public InjectorToggleMode CurrentMode { get; }
public InjectorComponentState(FixedPoint2 currentVolume, FixedPoint2 totalVolume, InjectorToggleMode currentMode) public InjectorComponentState(FixedPoint2 currentVolume, FixedPoint2 totalVolume,
InjectorToggleMode currentMode)
{ {
CurrentVolume = currentVolume; CurrentVolume = currentVolume;
TotalVolume = totalVolume; TotalVolume = totalVolume;

View File

@@ -1,5 +1,7 @@
using Content.Shared.DoAfter;
using Content.Shared.DragDrop; using Content.Shared.DragDrop;
using Content.Shared.Movement.Events; using Content.Shared.Movement.Events;
using Robust.Shared.Serialization;
namespace Content.Shared.Climbing; namespace Content.Shared.Climbing;
@@ -24,4 +26,9 @@ public abstract class SharedClimbSystem : EntitySystem
{ {
args.CanDrop = HasComp<ClimbingComponent>(args.Dragged); args.CanDrop = HasComp<ClimbingComponent>(args.Dragged);
} }
[Serializable, NetSerializable]
protected sealed class ClimbDoAfterEvent : SimpleDoAfterEvent
{
}
} }

Some files were not shown because too many files have changed in this diff Show More