diff --git a/Content.Client/Guidebook/GuidebookSystem.cs b/Content.Client/Guidebook/GuidebookSystem.cs index 0aa2c85142..86dcf76942 100644 --- a/Content.Client/Guidebook/GuidebookSystem.cs +++ b/Content.Client/Guidebook/GuidebookSystem.cs @@ -148,7 +148,7 @@ public sealed class GuidebookSystem : EntitySystem public void FakeClientActivateInWorld(EntityUid activated) { - var activateMsg = new ActivateInWorldEvent(GetGuidebookUser(), activated); + var activateMsg = new ActivateInWorldEvent(GetGuidebookUser(), activated, true); RaiseLocalEvent(activated, activateMsg); } diff --git a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs index 7c700d9fb8..57ac63b124 100644 --- a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs +++ b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs @@ -29,6 +29,7 @@ namespace Content.IntegrationTests.Tests.Buckle components: - type: Buckle - type: Hands + - type: ComplexInteraction - type: InputMover - type: Body prototype: Human diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs b/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs index c6a8e618cc..0ac6b68a3e 100644 --- a/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs +++ b/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs @@ -24,6 +24,7 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking components: - type: Cuffable - type: Hands + - type: ComplexInteraction - type: Body prototype: Human diff --git a/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs b/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs index 4415eddf37..2b844d34f0 100644 --- a/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs +++ b/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs @@ -4,6 +4,7 @@ using Content.Server.Interaction; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; +using Content.Shared.Interaction.Components; using Content.Shared.Item; using Robust.Shared.Containers; using Robust.Shared.GameObjects; @@ -64,6 +65,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click { user = sEntities.SpawnEntity(null, coords); sEntities.EnsureComponent(user); + sEntities.EnsureComponent(user); handSys.AddHand(user, "hand", HandLocation.Left); target = sEntities.SpawnEntity(null, coords); item = sEntities.SpawnEntity(null, coords); @@ -205,6 +207,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click { user = sEntities.SpawnEntity(null, coords); sEntities.EnsureComponent(user); + sEntities.EnsureComponent(user); handSys.AddHand(user, "hand", HandLocation.Left); target = sEntities.SpawnEntity(null, new MapCoordinates(new Vector2(SharedInteractionSystem.InteractionRange - 0.1f, 0), mapId)); item = sEntities.SpawnEntity(null, coords); @@ -347,6 +350,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click { user = sEntities.SpawnEntity(null, coords); sEntities.EnsureComponent(user); + sEntities.EnsureComponent(user); handSys.AddHand(user, "hand", HandLocation.Left); target = sEntities.SpawnEntity(null, coords); item = sEntities.SpawnEntity(null, coords); diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs index 42f64b344c..e5f794feaa 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs @@ -137,6 +137,7 @@ public abstract partial class InteractionTest prototype: Aghost - type: DoAfter - type: Hands + - type: ComplexInteraction - type: MindContainer - type: Stripping - type: Tag diff --git a/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs b/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs index 99481db70e..3cceaefbdc 100644 --- a/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs +++ b/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs @@ -27,6 +27,7 @@ namespace Content.IntegrationTests.Tests id: HumanVendingDummy components: - type: Hands + - type: ComplexInteraction - type: Body prototype: Human diff --git a/Content.Server/Actions/ActionOnInteractSystem.cs b/Content.Server/Actions/ActionOnInteractSystem.cs index b6eec0ce0f..2868585859 100644 --- a/Content.Server/Actions/ActionOnInteractSystem.cs +++ b/Content.Server/Actions/ActionOnInteractSystem.cs @@ -39,7 +39,7 @@ public sealed class ActionOnInteractSystem : EntitySystem private void OnActivate(EntityUid uid, ActionOnInteractComponent component, ActivateInWorldEvent args) { - if (args.Handled) + if (args.Handled || !args.Complex) return; if (component.ActionEntities is not {} actionEnts) diff --git a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs index b6e26435a7..dd0ac25d70 100644 --- a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs +++ b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs @@ -155,7 +155,7 @@ namespace Content.Server.Atmos.EntitySystems private void OnExtinguishActivateInWorld(EntityUid uid, ExtinguishOnInteractComponent component, ActivateInWorldEvent args) { - if (args.Handled) + if (args.Handled || !args.Complex) return; if (!TryComp(uid, out FlammableComponent? flammable)) diff --git a/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs b/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs index 881f54512a..f4650861db 100644 --- a/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs @@ -246,6 +246,9 @@ public sealed class AirAlarmSystem : EntitySystem private void OnActivate(EntityUid uid, AirAlarmComponent component, ActivateInWorldEvent args) { + if (!args.Complex) + return; + if (TryComp(uid, out var panel) && panel.Open) { args.Handled = false; diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressurePumpSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressurePumpSystem.cs index 83b7b67ba4..871c84e058 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressurePumpSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressurePumpSystem.cs @@ -103,6 +103,9 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems private void OnPumpActivate(EntityUid uid, GasPressurePumpComponent pump, ActivateInWorldEvent args) { + if (args.Handled || !args.Complex) + return; + if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) return; diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs index ed7567428e..4aeba2f8fe 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs @@ -52,8 +52,12 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems private void OnActivate(EntityUid uid, GasValveComponent component, ActivateInWorldEvent args) { + if (args.Handled || !args.Complex) + return; + Toggle(uid, component); _audio.PlayPvs(component.ValveSound, uid, AudioParams.Default.WithVariation(0.25f)); + args.Handled = true; } public void Set(EntityUid uid, GasValveComponent component, bool value) diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs index cbcd1f4fa3..d9fbeb474e 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs @@ -133,6 +133,9 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems private void OnPumpActivate(EntityUid uid, GasVolumePumpComponent pump, ActivateInWorldEvent args) { + if (args.Handled || !args.Complex) + return; + if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) return; diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs index 007d304e98..752d1e9eb8 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs @@ -99,6 +99,9 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems private void OnFilterActivate(EntityUid uid, GasFilterComponent filter, ActivateInWorldEvent args) { + if (args.Handled || !args.Complex) + return; + if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) return; diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs index 4ab8572843..178caeaa4a 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs @@ -139,6 +139,9 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems private void OnMixerActivate(EntityUid uid, GasMixerComponent mixer, ActivateInWorldEvent args) { + if (args.Handled || !args.Complex) + return; + if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) return; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs index e279db09aa..584928def7 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs @@ -201,6 +201,9 @@ public sealed class GasCanisterSystem : EntitySystem private void OnCanisterActivate(EntityUid uid, GasCanisterComponent component, ActivateInWorldEvent args) { + if (!args.Complex) + return; + if (!TryComp(args.User, out var actor)) return; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs index 834a1dfb0b..6203918517 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs @@ -33,8 +33,12 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems private void OnActivate(EntityUid uid, GasOutletInjectorComponent component, ActivateInWorldEvent args) { + if (args.Handled || !args.Complex) + return; + component.Enabled = !component.Enabled; UpdateAppearance(uid, component); + args.Handled = true; } public void UpdateAppearance(EntityUid uid, GasOutletInjectorComponent component, AppearanceComponent? appearance = null) diff --git a/Content.Server/CardboardBox/CardboardBoxSystem.cs b/Content.Server/CardboardBox/CardboardBoxSystem.cs index b9c9427d5c..836dc485d9 100644 --- a/Content.Server/CardboardBox/CardboardBoxSystem.cs +++ b/Content.Server/CardboardBox/CardboardBoxSystem.cs @@ -36,7 +36,6 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem SubscribeLocalEvent(AfterStorageClosed); SubscribeLocalEvent(OnGetAdditionalAccess); SubscribeLocalEvent(OnInteracted); - SubscribeLocalEvent(OnNoHandInteracted); SubscribeLocalEvent(OnEntInserted); SubscribeLocalEvent(OnEntRemoved); @@ -45,9 +44,18 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem private void OnInteracted(EntityUid uid, CardboardBoxComponent component, ActivateInWorldEvent args) { + if (args.Handled) + return; + if (!TryComp(uid, out var box)) return; + if (!args.Complex) + { + if (box.Open || !box.Contents.Contains(args.User)) + return; + } + args.Handled = true; _storage.ToggleOpen(args.User, uid, box); @@ -58,15 +66,6 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem } } - private void OnNoHandInteracted(EntityUid uid, CardboardBoxComponent component, InteractedNoHandEvent args) - { - //Free the mice please - if (!TryComp(uid, out var box) || box.Open || !box.Contents.Contains(args.User)) - return; - - _storage.OpenStorage(uid); - } - private void OnGetAdditionalAccess(EntityUid uid, CardboardBoxComponent component, ref GetAdditionalAccessEvent args) { if (component.Mover == null) diff --git a/Content.Server/DeviceLinking/Systems/SignalSwitchSystem.cs b/Content.Server/DeviceLinking/Systems/SignalSwitchSystem.cs index f6469d68b9..67fad29d93 100644 --- a/Content.Server/DeviceLinking/Systems/SignalSwitchSystem.cs +++ b/Content.Server/DeviceLinking/Systems/SignalSwitchSystem.cs @@ -26,7 +26,7 @@ public sealed class SignalSwitchSystem : EntitySystem private void OnActivated(EntityUid uid, SignalSwitchComponent comp, ActivateInWorldEvent args) { - if (args.Handled) + if (args.Handled || !args.Complex) return; comp.State = !comp.State; diff --git a/Content.Server/Disposal/Mailing/MailingUnitSystem.cs b/Content.Server/Disposal/Mailing/MailingUnitSystem.cs index 8e9c9e4ba7..e1fbdbf089 100644 --- a/Content.Server/Disposal/Mailing/MailingUnitSystem.cs +++ b/Content.Server/Disposal/Mailing/MailingUnitSystem.cs @@ -152,6 +152,9 @@ public sealed class MailingUnitSystem : EntitySystem private void HandleActivate(EntityUid uid, MailingUnitComponent component, ActivateInWorldEvent args) { + if (args.Handled || !args.Complex) + return; + if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) { return; diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs index 1f3b9703d4..e63c03bd5e 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs @@ -263,6 +263,9 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem private void OnActivate(EntityUid uid, SharedDisposalUnitComponent component, ActivateInWorldEvent args) { + if (args.Handled || !args.Complex) + return; + if (!TryComp(args.User, out ActorComponent? actor)) { return; diff --git a/Content.Server/Doors/Systems/AirlockSystem.cs b/Content.Server/Doors/Systems/AirlockSystem.cs index 71f9347e9e..fd5d3a9ceb 100644 --- a/Content.Server/Doors/Systems/AirlockSystem.cs +++ b/Content.Server/Doors/Systems/AirlockSystem.cs @@ -67,6 +67,9 @@ public sealed class AirlockSystem : SharedAirlockSystem private void OnActivate(EntityUid uid, AirlockComponent component, ActivateInWorldEvent args) { + if (args.Handled || !args.Complex) + return; + if (TryComp(uid, out var panel) && panel.Open && TryComp(args.User, out var actor)) diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs index 7c6b5df7f1..e03b8aff54 100644 --- a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs @@ -218,6 +218,9 @@ namespace Content.Server.Explosion.EntitySystems private void OnActivate(EntityUid uid, TriggerOnActivateComponent component, ActivateInWorldEvent args) { + if (args.Handled || !args.Complex) + return; + Trigger(uid, args.User); args.Handled = true; } diff --git a/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs b/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs index d88f46968a..a3717fd94c 100644 --- a/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs +++ b/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs @@ -34,7 +34,7 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem base.Initialize(); SubscribeLocalEvent(OnAbsorbentInit); SubscribeLocalEvent(OnAfterInteract); - SubscribeLocalEvent(OnInteractNoHand); + SubscribeLocalEvent(OnActivateInWorld); SubscribeLocalEvent(OnAbsorbentSolutionChange); } @@ -84,12 +84,12 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem Dirty(uid, component); } - private void OnInteractNoHand(EntityUid uid, AbsorbentComponent component, InteractNoHandEvent args) + private void OnActivateInWorld(EntityUid uid, AbsorbentComponent component, UserActivateInWorldEvent args) { - if (args.Handled || args.Target == null) + if (args.Handled) return; - Mop(uid, args.Target.Value, uid, component); + Mop(uid, args.Target, uid, component); args.Handled = true; } diff --git a/Content.Server/Gatherable/GatherableSystem.cs b/Content.Server/Gatherable/GatherableSystem.cs index 44e60cb102..e24b0da593 100644 --- a/Content.Server/Gatherable/GatherableSystem.cs +++ b/Content.Server/Gatherable/GatherableSystem.cs @@ -38,10 +38,14 @@ public sealed partial class GatherableSystem : EntitySystem private void OnActivate(Entity gatherable, ref ActivateInWorldEvent args) { + if (args.Handled || !args.Complex) + return; + if (gatherable.Comp.ToolWhitelist?.IsValid(args.User, EntityManager) != true) return; Gather(gatherable, args.User); + args.Handled = true; } public void Gather(EntityUid gatheredUid, EntityUid? gatherer = null, GatherableComponent? component = null) diff --git a/Content.Server/Light/EntitySystems/HandheldLightSystem.cs b/Content.Server/Light/EntitySystems/HandheldLightSystem.cs index 813f8c407b..a5a41bcc10 100644 --- a/Content.Server/Light/EntitySystems/HandheldLightSystem.cs +++ b/Content.Server/Light/EntitySystems/HandheldLightSystem.cs @@ -126,7 +126,7 @@ namespace Content.Server.Light.EntitySystems private void OnActivate(Entity ent, ref ActivateInWorldEvent args) { - if (args.Handled || !ent.Comp.ToggleOnInteract) + if (args.Handled || !args.Complex || !ent.Comp.ToggleOnInteract) return; if (ToggleStatus(args.User, ent)) diff --git a/Content.Server/Mech/Equipment/EntitySystems/MechGrabberSystem.cs b/Content.Server/Mech/Equipment/EntitySystems/MechGrabberSystem.cs index 4b2ba8ffbd..c5c44056b9 100644 --- a/Content.Server/Mech/Equipment/EntitySystems/MechGrabberSystem.cs +++ b/Content.Server/Mech/Equipment/EntitySystems/MechGrabberSystem.cs @@ -40,7 +40,7 @@ public sealed class MechGrabberSystem : EntitySystem SubscribeLocalEvent(OnEquipmentRemoved); SubscribeLocalEvent(OnAttemptRemove); - SubscribeLocalEvent(OnInteract); + SubscribeLocalEvent(OnInteract); SubscribeLocalEvent(OnMechGrab); } @@ -123,10 +123,11 @@ public sealed class MechGrabberSystem : EntitySystem args.States.Add(GetNetEntity(uid), state); } - private void OnInteract(EntityUid uid, MechGrabberComponent component, InteractNoHandEvent args) + private void OnInteract(EntityUid uid, MechGrabberComponent component, UserActivateInWorldEvent args) { - if (args.Handled || args.Target is not {} target) + if (args.Handled) return; + var target = args.Target; if (args.Target == args.User || component.DoAfter != null) return; diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Cigar.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Cigar.cs index 4e672444d1..510b9552a3 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Cigar.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Cigar.cs @@ -18,7 +18,7 @@ namespace Content.Server.Nutrition.EntitySystems private void OnCigarActivatedEvent(Entity entity, ref ActivateInWorldEvent args) { - if (args.Handled) + if (args.Handled || !args.Complex) return; if (!EntityManager.TryGetComponent(entity, out SmokableComponent? smokable)) diff --git a/Content.Server/Pinpointer/PinpointerSystem.cs b/Content.Server/Pinpointer/PinpointerSystem.cs index be9a715d5d..eebf9cbbfd 100644 --- a/Content.Server/Pinpointer/PinpointerSystem.cs +++ b/Content.Server/Pinpointer/PinpointerSystem.cs @@ -45,10 +45,15 @@ public sealed class PinpointerSystem : SharedPinpointerSystem private void OnActivate(EntityUid uid, PinpointerComponent component, ActivateInWorldEvent args) { + if (args.Handled || !args.Complex) + return; + TogglePinpointer(uid, component); if (!component.CanRetarget) LocateTarget(uid, component); + + args.Handled = true; } private void OnLocateTarget(ref FTLCompletedEvent ev) diff --git a/Content.Server/Radiation/Systems/GeigerSystem.cs b/Content.Server/Radiation/Systems/GeigerSystem.cs index f889336a06..a0bc5dd739 100644 --- a/Content.Server/Radiation/Systems/GeigerSystem.cs +++ b/Content.Server/Radiation/Systems/GeigerSystem.cs @@ -35,7 +35,7 @@ public sealed class GeigerSystem : SharedGeigerSystem private void OnActivate(Entity geiger, ref ActivateInWorldEvent args) { - if (args.Handled || geiger.Comp.AttachedToSuit) + if (args.Handled || !args.Complex || geiger.Comp.AttachedToSuit) return; args.Handled = true; diff --git a/Content.Server/Radio/EntitySystems/JammerSystem.cs b/Content.Server/Radio/EntitySystems/JammerSystem.cs index 4f58cb21e1..223d0e47c0 100644 --- a/Content.Server/Radio/EntitySystems/JammerSystem.cs +++ b/Content.Server/Radio/EntitySystems/JammerSystem.cs @@ -67,6 +67,9 @@ public sealed class JammerSystem : SharedJammerSystem private void OnActivate(EntityUid uid, RadioJammerComponent comp, ActivateInWorldEvent args) { + if (args.Handled || !args.Complex) + return; + var activated = !HasComp(uid) && _powerCell.TryGetBatteryFromSlot(uid, out var battery) && battery.CurrentCharge > GetCurrentWattage(comp); diff --git a/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs b/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs index 56c5d8e548..8484fb2336 100644 --- a/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs +++ b/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs @@ -81,6 +81,9 @@ public sealed class RadioDeviceSystem : EntitySystem #region Toggling private void OnActivateMicrophone(EntityUid uid, RadioMicrophoneComponent component, ActivateInWorldEvent args) { + if (!args.Complex) + return; + if (!component.ToggleOnInteract) return; @@ -90,6 +93,9 @@ public sealed class RadioDeviceSystem : EntitySystem private void OnActivateSpeaker(EntityUid uid, RadioSpeakerComponent component, ActivateInWorldEvent args) { + if (!args.Complex) + return; + if (!component.ToggleOnInteract) return; diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs index 4106630d52..fd7d15ea5d 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs @@ -43,7 +43,7 @@ public sealed partial class RevenantSystem private void InitializeAbilities() { - SubscribeLocalEvent(OnInteract); + SubscribeLocalEvent(OnInteract); SubscribeLocalEvent(OnSoulSearch); SubscribeLocalEvent(OnHarvest); @@ -53,11 +53,14 @@ public sealed partial class RevenantSystem SubscribeLocalEvent(OnMalfunctionAction); } - private void OnInteract(EntityUid uid, RevenantComponent component, InteractNoHandEvent args) + private void OnInteract(EntityUid uid, RevenantComponent component, UserActivateInWorldEvent args) { - if (args.Target == args.User || args.Target == null) + if (args.Handled) return; - var target = args.Target.Value; + + if (args.Target == args.User) + return; + var target = args.Target; if (HasComp(target)) { @@ -78,6 +81,8 @@ public sealed partial class RevenantSystem { BeginHarvestDoAfter(uid, target, component, essence); } + + args.Handled = true; } private void BeginSoulSearchDoAfter(EntityUid uid, EntityUid target, RevenantComponent revenant) diff --git a/Content.Server/Shuttles/Systems/ThrusterSystem.cs b/Content.Server/Shuttles/Systems/ThrusterSystem.cs index 74c42ccbc5..e82235e44f 100644 --- a/Content.Server/Shuttles/Systems/ThrusterSystem.cs +++ b/Content.Server/Shuttles/Systems/ThrusterSystem.cs @@ -128,15 +128,20 @@ public sealed class ThrusterSystem : EntitySystem private void OnActivateThruster(EntityUid uid, ThrusterComponent component, ActivateInWorldEvent args) { + if (args.Handled || !args.Complex) + return; + component.Enabled ^= true; if (!component.Enabled) { DisableThruster(uid, component); + args.Handled = true; } else if (CanEnable(uid, component)) { EnableThruster(uid, component); + args.Handled = true; } } diff --git a/Content.Server/Tabletop/TabletopSystem.cs b/Content.Server/Tabletop/TabletopSystem.cs index 4376ec4bc6..caa319a0b7 100644 --- a/Content.Server/Tabletop/TabletopSystem.cs +++ b/Content.Server/Tabletop/TabletopSystem.cs @@ -141,6 +141,9 @@ namespace Content.Server.Tabletop private void OnTabletopActivate(EntityUid uid, TabletopGameComponent component, ActivateInWorldEvent args) { + if (args.Handled || !args.Complex) + return; + // Check that a player is attached to the entity. if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) return; diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs index a2c13ed71c..0a745d5fc7 100644 --- a/Content.Server/Zombies/ZombieSystem.Transform.cs +++ b/Content.Server/Zombies/ZombieSystem.Transform.cs @@ -21,6 +21,7 @@ using Content.Shared.Damage; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Humanoid; +using Content.Shared.Interaction.Components; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; @@ -106,6 +107,7 @@ namespace Content.Server.Zombies RemComp(target); RemComp(target); RemComp(target); + RemComp(target); //funny voice var accentType = "zombie"; diff --git a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs index 47b3997806..d2883b5ef5 100644 --- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs +++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs @@ -96,9 +96,9 @@ namespace Content.Shared.ActionBlocker /// involve using a held entity. In the majority of cases, systems that provide interactions will not need /// to check this themselves. /// - public bool CanUseHeldEntity(EntityUid user) + public bool CanUseHeldEntity(EntityUid user, EntityUid used) { - var ev = new UseAttemptEvent(user); + var ev = new UseAttemptEvent(user, used); RaiseLocalEvent(user, ev); return !ev.Cancelled; diff --git a/Content.Shared/Burial/BurialSystem.cs b/Content.Shared/Burial/BurialSystem.cs index 326272cc7d..45a89f3be9 100644 --- a/Content.Shared/Burial/BurialSystem.cs +++ b/Content.Shared/Burial/BurialSystem.cs @@ -83,10 +83,11 @@ public sealed class BurialSystem : EntitySystem private void OnActivate(EntityUid uid, GraveComponent component, ActivateInWorldEvent args) { - if (args.Handled) + if (args.Handled || !args.Complex) return; _popupSystem.PopupClient(Loc.GetString("grave-digging-requires-tool", ("grave", args.Target)), uid, args.User); + args.Handled = true; } private void OnGraveDigging(EntityUid uid, GraveComponent component, GraveDiggingDoAfterEvent args) diff --git a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerMixerSystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerMixerSystem.cs index 8795706612..c8e8e89ce5 100644 --- a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerMixerSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerMixerSystem.cs @@ -31,7 +31,11 @@ public abstract class SharedSolutionContainerMixerSystem : EntitySystem private void OnActivateInWorld(Entity entity, ref ActivateInWorldEvent args) { + if (args.Handled || !args.Complex) + return; + TryStartMix(entity, args.User); + args.Handled = true; } private void OnRemoveAttempt(Entity ent, ref ContainerIsRemovingAttemptEvent args) diff --git a/Content.Shared/DeviceLinking/Systems/TwoWayLeverSystem.cs b/Content.Shared/DeviceLinking/Systems/TwoWayLeverSystem.cs index c8783b05fc..7e665dc190 100644 --- a/Content.Shared/DeviceLinking/Systems/TwoWayLeverSystem.cs +++ b/Content.Shared/DeviceLinking/Systems/TwoWayLeverSystem.cs @@ -28,7 +28,7 @@ namespace Content.Shared.DeviceLinking.Systems private void OnActivated(EntityUid uid, TwoWayLeverComponent component, ActivateInWorldEvent args) { - if (args.Handled) + if (args.Handled || !args.Complex) return; component.State = component.State switch diff --git a/Content.Shared/Doors/Systems/SharedDoorSystem.cs b/Content.Shared/Doors/Systems/SharedDoorSystem.cs index 20456c1477..ab2df72568 100644 --- a/Content.Shared/Doors/Systems/SharedDoorSystem.cs +++ b/Content.Shared/Doors/Systems/SharedDoorSystem.cs @@ -215,7 +215,7 @@ public abstract partial class SharedDoorSystem : EntitySystem #region Interactions protected void OnActivate(EntityUid uid, DoorComponent door, ActivateInWorldEvent args) { - if (args.Handled || !door.ClickOpen) + if (args.Handled || !args.Complex || !door.ClickOpen) return; if (!TryToggleDoor(uid, door, args.User, predicted: true)) diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs index 6d4d332479..ae22efcd6a 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs @@ -3,6 +3,7 @@ using Content.Shared.Examine; using Content.Shared.Hands.Components; using Content.Shared.IdentityManagement; using Content.Shared.Input; +using Content.Shared.Interaction; using Content.Shared.Inventory.VirtualItem; using Content.Shared.Localizations; using Robust.Shared.Input.Binding; @@ -23,6 +24,7 @@ public abstract partial class SharedHandsSystem : EntitySystem SubscribeAllEvent(HandleMoveItemFromHand); SubscribeAllEvent(HandleHandAltInteract); + SubscribeLocalEvent(OnGetUsedEntity); SubscribeLocalEvent(HandleExamined); CommandBinds.Builder @@ -181,6 +183,18 @@ public abstract partial class SharedHandsSystem : EntitySystem return true; } + private void OnGetUsedEntity(EntityUid uid, HandsComponent component, ref GetUsedEntityEvent args) + { + if (args.Handled) + return; + + // TODO: this pattern is super uncommon, but it might be worth changing GetUsedEntityEvent to be recursive. + if (TryComp(component.ActiveHandEntity, out var virtualItem)) + args.Used = virtualItem.BlockingEntity; + else + args.Used = component.ActiveHandEntity; + } + //TODO: Actually shows all items/clothing/etc. private void HandleExamined(EntityUid examinedUid, HandsComponent handsComp, ExaminedEvent args) { diff --git a/Content.Shared/Interaction/ActivateInWorldEvent.cs b/Content.Shared/Interaction/ActivateInWorldEvent.cs index 9dbd636c48..f7a1b7a799 100644 --- a/Content.Shared/Interaction/ActivateInWorldEvent.cs +++ b/Content.Shared/Interaction/ActivateInWorldEvent.cs @@ -18,14 +18,49 @@ public sealed class ActivateInWorldEvent : HandledEntityEventArgs, ITargetedInte /// public EntityUid Target { get; } + /// + /// Whether or not can perform complex interactions or only basic ones. + /// + public bool Complex; + /// /// Set to true when the activation is logged by a specific logger. /// public bool WasLogged { get; set; } - public ActivateInWorldEvent(EntityUid user, EntityUid target) + public ActivateInWorldEvent(EntityUid user, EntityUid target, bool complex) { User = user; Target = target; + Complex = complex; + } +} + +/// +/// Event raised on the user when it activates something in the world +/// +[PublicAPI] +public sealed class UserActivateInWorldEvent : HandledEntityEventArgs, ITargetedInteractEventArgs +{ + /// + /// Entity that activated the target world entity. + /// + public EntityUid User { get; } + + /// + /// Entity that was activated in the world. + /// + public EntityUid Target { get; } + + /// + /// Whether or not can perform complex interactions or only basic ones. + /// + public bool Complex; + + public UserActivateInWorldEvent(EntityUid user, EntityUid target, bool complex) + { + User = user; + Target = target; + Complex = complex; } } diff --git a/Content.Shared/Interaction/Components/ComplexInteractionComponent.cs b/Content.Shared/Interaction/Components/ComplexInteractionComponent.cs new file mode 100644 index 0000000000..ae7d65de36 --- /dev/null +++ b/Content.Shared/Interaction/Components/ComplexInteractionComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Interaction.Components; + +/// +/// This is used for identifying entities as being able to use complex interactions with the environment. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedInteractionSystem))] +public sealed partial class ComplexInteractionComponent : Component; diff --git a/Content.Shared/Interaction/Events/UseAttemptEvent.cs b/Content.Shared/Interaction/Events/UseAttemptEvent.cs index 3db185ed17..c28f2b6517 100644 --- a/Content.Shared/Interaction/Events/UseAttemptEvent.cs +++ b/Content.Shared/Interaction/Events/UseAttemptEvent.cs @@ -1,12 +1,9 @@ namespace Content.Shared.Interaction.Events { - public sealed class UseAttemptEvent : CancellableEntityEventArgs + public sealed class UseAttemptEvent(EntityUid uid, EntityUid used) : CancellableEntityEventArgs { - public UseAttemptEvent(EntityUid uid) - { - Uid = uid; - } + public EntityUid Uid { get; } = uid; - public EntityUid Uid { get; } + public EntityUid Used = used; } } diff --git a/Content.Shared/Interaction/InteractHand.cs b/Content.Shared/Interaction/InteractHand.cs index 63ea3b6f30..1d2df4c28b 100644 --- a/Content.Shared/Interaction/InteractHand.cs +++ b/Content.Shared/Interaction/InteractHand.cs @@ -51,58 +51,4 @@ namespace Content.Shared.Interaction Target = target; } } - - /// - /// Low-level interaction event used for entities without hands. - /// - /// - /// SHIT IS CURSED. - /// - //TODO: KILLLLLLL - public sealed class InteractNoHandEvent : HandledEntityEventArgs - { - /// - /// Entity that triggered the interaction. - /// - public EntityUid User; - - /// - /// Entity that was interacted on. - /// - public EntityUid? Target; - - public EntityCoordinates ClickLocation; - - public InteractNoHandEvent(EntityUid user, EntityUid? target, EntityCoordinates clickLocation) - { - User = user; - Target = target; - ClickLocation = clickLocation; - } - } - - /// - /// Reverse of the InteractNoHandEvent - raised on what was interacted on, rather than the other way around. - /// - public sealed class InteractedNoHandEvent : HandledEntityEventArgs - { - /// - /// Entity that was interacted on - /// - public EntityUid Target; - - /// - /// Entity that triggered this interaction - /// - public EntityUid User; - - public EntityCoordinates ClickLocation; - - public InteractedNoHandEvent(EntityUid target, EntityUid user, EntityCoordinates clickLocation) - { - Target = target; - User = user; - ClickLocation = clickLocation; - } - } } diff --git a/Content.Shared/Interaction/InteractionPopupSystem.cs b/Content.Shared/Interaction/InteractionPopupSystem.cs index 030bc0ae6b..d956ff3d95 100644 --- a/Content.Shared/Interaction/InteractionPopupSystem.cs +++ b/Content.Shared/Interaction/InteractionPopupSystem.cs @@ -32,6 +32,9 @@ public sealed class InteractionPopupSystem : EntitySystem private void OnActivateInWorld(EntityUid uid, InteractionPopupComponent component, ActivateInWorldEvent args) { + if (!args.Complex) + return; + if (!component.OnActivate) return; diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index bcde27ceba..1e4c49211f 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -76,6 +76,7 @@ namespace Content.Shared.Interaction private EntityQuery _wallMountQuery; private EntityQuery _delayQuery; private EntityQuery _uiQuery; + private EntityQuery _complexInteractionQuery; private const CollisionGroup InRangeUnobstructedMask = CollisionGroup.Impassable | CollisionGroup.InteractImpassable; @@ -98,6 +99,7 @@ namespace Content.Shared.Interaction _wallMountQuery = GetEntityQuery(); _delayQuery = GetEntityQuery(); _uiQuery = GetEntityQuery(); + _complexInteractionQuery = GetEntityQuery(); SubscribeLocalEvent(HandleUserInterfaceRangeCheck); SubscribeLocalEvent(OnBoundInterfaceInteractAttempt); @@ -360,8 +362,13 @@ namespace Content.Shared.Interaction // TODO this needs to be handled better. This probably bypasses many complex can-interact checks in weird roundabout ways. if (_actionBlockerSystem.CanInteract(user, target)) { - UserInteraction(relay.RelayEntity.Value, coordinates, target, altInteract, checkCanInteract, - checkAccess, checkCanUse); + UserInteraction(relay.RelayEntity.Value, + coordinates, + target, + altInteract, + checkCanInteract, + checkAccess, + checkCanUse); return; } } @@ -398,25 +405,10 @@ namespace Content.Shared.Interaction ? !checkAccess || InRangeUnobstructed(user, coordinates) : !checkAccess || InRangeUnobstructed(user, target.Value); // permits interactions with wall mounted entities - // Does the user have hands? - if (!_handsQuery.TryComp(user, out var hands) || hands.ActiveHand == null) - { - var ev = new InteractNoHandEvent(user, target, coordinates); - RaiseLocalEvent(user, ev); - - if (target != null) - { - var interactedEv = new InteractedNoHandEvent(target.Value, user, coordinates); - RaiseLocalEvent(target.Value, interactedEv); - DoContactInteraction(user, target.Value, ev); - } - return; - } - // empty-hand interactions // combat mode hand interactions will always be true here -- since // they check this earlier before returning in - if (hands.ActiveHandEntity is not { } held) + if (!TryGetUsedEntity(user, out var used, checkCanUse)) { if (inRangeUnobstructed && target != null) InteractHand(user, target.Value); @@ -424,11 +416,7 @@ namespace Content.Shared.Interaction return; } - // Can the user use the held entity? - if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user)) - return; - - if (target == held) + if (target == used) { UseInHandInteraction(user, target.Value, checkCanUse: false, checkCanInteract: false); return; @@ -438,7 +426,7 @@ namespace Content.Shared.Interaction { InteractUsing( user, - held, + used.Value, target.Value, coordinates, checkCanInteract: false, @@ -449,7 +437,7 @@ namespace Content.Shared.Interaction InteractUsingRanged( user, - held, + used.Value, target, coordinates, inRangeUnobstructed); @@ -457,6 +445,18 @@ namespace Content.Shared.Interaction public void InteractHand(EntityUid user, EntityUid target) { + var complexInteractions = SupportsComplexInteractions(user); + if (!complexInteractions) + { + InteractionActivate(user, + target, + checkCanInteract: false, + checkUseDelay: true, + checkAccess: false, + complexInteractions: complexInteractions); + return; + } + // allow for special logic before main interaction var ev = new BeforeInteractHandEvent(target); RaiseLocalEvent(user, ev); @@ -475,10 +475,12 @@ namespace Content.Shared.Interaction return; // Else we run Activate. - InteractionActivate(user, target, + InteractionActivate(user, + target, checkCanInteract: false, checkUseDelay: true, - checkAccess: false); + checkAccess: false, + complexInteractions: complexInteractions); } public void InteractUsingRanged(EntityUid user, EntityUid used, EntityUid? target, @@ -921,7 +923,7 @@ namespace Content.Shared.Interaction if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target)) return; - if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user)) + if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user, used)) return; if (RangedInteractDoBefore(user, used, target, clickLocation, true)) @@ -1001,7 +1003,8 @@ namespace Content.Shared.Interaction EntityUid used, bool checkCanInteract = true, bool checkUseDelay = true, - bool checkAccess = true) + bool checkAccess = true, + bool? complexInteractions = null) { _delayQuery.TryComp(used, out var delayComponent); if (checkUseDelay && delayComponent != null && _useDelay.IsDelayed((used, delayComponent))) @@ -1018,13 +1021,12 @@ namespace Content.Shared.Interaction if (checkAccess && !IsAccessible(user, used)) return false; - // Does the user have hands? - if (!_handsQuery.HasComp(user)) - return false; - - var activateMsg = new ActivateInWorldEvent(user, used); + complexInteractions ??= SupportsComplexInteractions(user); + var activateMsg = new ActivateInWorldEvent(user, used, complexInteractions.Value); RaiseLocalEvent(used, activateMsg, true); - if (!activateMsg.Handled) + var userEv = new UserActivateInWorldEvent(user, used, complexInteractions.Value); + RaiseLocalEvent(user, userEv, true); + if (!activateMsg.Handled && !userEv.Handled) return false; DoContactInteraction(user, used, activateMsg); @@ -1059,7 +1061,7 @@ namespace Content.Shared.Interaction if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used)) return false; - if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user)) + if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user, used)) return false; var useMsg = new UseInHandEvent(user); @@ -1259,6 +1261,39 @@ namespace Content.Shared.Interaction ? BoundUserInterfaceRangeResult.Pass : BoundUserInterfaceRangeResult.Fail; } + + /// + /// Gets the entity that is currently being "used" for the interaction. + /// In most cases, this refers to the entity in the character's active hand. + /// + /// If there is an entity being used. + public bool TryGetUsedEntity(EntityUid user, [NotNullWhen(true)] out EntityUid? used, bool checkCanUse = true) + { + var ev = new GetUsedEntityEvent(); + RaiseLocalEvent(user, ref ev); + + used = ev.Used; + if (!ev.Handled) + return false; + + // Can the user use the held entity? + if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user, ev.Used!.Value)) + { + used = null; + return false; + } + + return ev.Handled; + } + + /// + /// Checks if a given entity is able to do specific complex interactions. + /// This is used to gate manipulation to general humanoids. If a mouse shouldn't be able to do something, then it's complex. + /// + public bool SupportsComplexInteractions(EntityUid user) + { + return _complexInteractionQuery.HasComp(user); + } } /// @@ -1284,6 +1319,24 @@ namespace Content.Shared.Interaction } } + /// + /// Raised directed by-ref on an entity to determine what item will be used in interactions. + /// + [ByRefEvent] + public record struct GetUsedEntityEvent() + { + public EntityUid? Used = null; + + public bool Handled => Used != null; + }; + + /// + /// Raised directed by-ref on an item and a user to determine if interactions can occur. + /// + /// Whether the hand interaction should be cancelled. + [ByRefEvent] + public record struct AttemptUseInteractEvent(EntityUid User, EntityUid Used, bool Cancelled = false); + /// /// Raised directed by-ref on an item to determine if hand interactions should go through. /// Defaults to allowing hand interactions to go through. Cancel to force the item to be attacked instead. diff --git a/Content.Shared/Lock/LockSystem.cs b/Content.Shared/Lock/LockSystem.cs index a25dcea8a7..bf4af1cb9a 100644 --- a/Content.Shared/Lock/LockSystem.cs +++ b/Content.Shared/Lock/LockSystem.cs @@ -62,7 +62,7 @@ public sealed class LockSystem : EntitySystem private void OnActivated(EntityUid uid, LockComponent lockComp, ActivateInWorldEvent args) { - if (args.Handled) + if (args.Handled || !args.Complex) return; // Only attempt an unlock by default on Activate diff --git a/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs b/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs index 97b5bfeba6..73b7c0847f 100644 --- a/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs +++ b/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs @@ -43,7 +43,7 @@ public abstract class SharedMechSystem : EntitySystem { SubscribeLocalEvent(OnToggleEquipmentAction); SubscribeLocalEvent(OnEjectPilotEvent); - SubscribeLocalEvent(RelayInteractionEvent); + SubscribeLocalEvent(RelayInteractionEvent); SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnDestruction); SubscribeLocalEvent(OnGetAdditionalAccess); @@ -71,7 +71,7 @@ public abstract class SharedMechSystem : EntitySystem TryEject(uid, component); } - private void RelayInteractionEvent(EntityUid uid, MechComponent component, InteractNoHandEvent args) + private void RelayInteractionEvent(EntityUid uid, MechComponent component, UserActivateInWorldEvent args) { var pilot = component.PilotSlot.ContainedEntity; if (pilot == null) diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs index 372dc8a75d..b718c3a690 100644 --- a/Content.Shared/Projectiles/SharedProjectileSystem.cs +++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs @@ -47,7 +47,7 @@ public abstract partial class SharedProjectileSystem : EntitySystem if (component.RemovalTime == null) return; - if (args.Handled || !TryComp(uid, out var physics) || physics.BodyType != BodyType.Static) + if (args.Handled || !args.Complex || !TryComp(uid, out var physics) || physics.BodyType != BodyType.Static) return; args.Handled = true; diff --git a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs index 636c603834..0576e46df4 100644 --- a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs @@ -89,7 +89,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem protected void OnInteract(EntityUid uid, SharedEntityStorageComponent component, ActivateInWorldEvent args) { - if (args.Handled) + if (args.Handled || !args.Complex) return; args.Handled = true; diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 18a8f20afc..ee087901f3 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -364,7 +364,7 @@ public abstract class SharedStorageSystem : EntitySystem /// private void OnActivate(EntityUid uid, StorageComponent storageComp, ActivateInWorldEvent args) { - if (args.Handled || !CanInteract(args.User, (uid, storageComp), storageComp.ClickInsert)) + if (args.Handled || !args.Complex || !CanInteract(args.User, (uid, storageComp), storageComp.ClickInsert)) return; // Toggle diff --git a/Content.Shared/Strip/SharedStrippableSystem.cs b/Content.Shared/Strip/SharedStrippableSystem.cs index 075cf81a4c..38e2f9fd7a 100644 --- a/Content.Shared/Strip/SharedStrippableSystem.cs +++ b/Content.Shared/Strip/SharedStrippableSystem.cs @@ -21,7 +21,7 @@ public abstract class SharedStrippableSystem : EntitySystem private void OnActivateInWorld(EntityUid uid, StrippableComponent component, ActivateInWorldEvent args) { - if (args.Handled || args.Target == args.User) + if (args.Handled || !args.Complex || args.Target == args.User) return; if (TryOpenStrippingUi(args.User, (uid, component))) diff --git a/Content.Shared/SubFloor/SharedTrayScannerSystem.cs b/Content.Shared/SubFloor/SharedTrayScannerSystem.cs index 6e8393036d..8903747e43 100644 --- a/Content.Shared/SubFloor/SharedTrayScannerSystem.cs +++ b/Content.Shared/SubFloor/SharedTrayScannerSystem.cs @@ -26,7 +26,11 @@ public abstract class SharedTrayScannerSystem : EntitySystem private void OnTrayScannerActivate(EntityUid uid, TrayScannerComponent scanner, ActivateInWorldEvent args) { + if (args.Handled || !args.Complex) + return; + SetScannerEnabled(uid, !scanner.Enabled, scanner); + args.Handled = true; } private void SetScannerEnabled(EntityUid uid, bool enabled, TrayScannerComponent? scanner = null) diff --git a/Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs b/Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs index 62c0b0f44e..bc73baa61a 100644 --- a/Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs +++ b/Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs @@ -101,6 +101,9 @@ public sealed class SwapTeleporterSystem : EntitySystem private void OnActivateInWorld(Entity ent, ref ActivateInWorldEvent args) { + if (args.Handled || !args.Complex) + return; + var (uid, comp) = ent; var user = args.User; if (comp.TeleportTime != null) @@ -130,6 +133,7 @@ public sealed class SwapTeleporterSystem : EntitySystem comp.NextTeleportUse = _timing.CurTime + comp.Cooldown; comp.TeleportTime = _timing.CurTime + comp.TeleportDelay; Dirty(uid, comp); + args.Handled = true; } public void DoTeleport(Entity ent) diff --git a/Content.Shared/Toilet/Systems/SharedToiletSystem.cs b/Content.Shared/Toilet/Systems/SharedToiletSystem.cs index 87df69e88d..f11af33552 100644 --- a/Content.Shared/Toilet/Systems/SharedToiletSystem.cs +++ b/Content.Shared/Toilet/Systems/SharedToiletSystem.cs @@ -79,7 +79,7 @@ namespace Content.Shared.Toilet.Systems private void OnActivateInWorld(EntityUid uid, ToiletComponent comp, ActivateInWorldEvent args) { - if (args.Handled) + if (args.Handled || !args.Complex) return; args.Handled = true; diff --git a/Content.Shared/Tools/Systems/SharedToolSystem.MultipleTool.cs b/Content.Shared/Tools/Systems/SharedToolSystem.MultipleTool.cs index 9114c62ade..d69f01d762 100644 --- a/Content.Shared/Tools/Systems/SharedToolSystem.MultipleTool.cs +++ b/Content.Shared/Tools/Systems/SharedToolSystem.MultipleTool.cs @@ -28,7 +28,7 @@ public abstract partial class SharedToolSystem private void OnMultipleToolActivated(EntityUid uid, MultipleToolComponent multiple, ActivateInWorldEvent args) { - if (args.Handled) + if (args.Handled || !args.Complex) return; args.Handled = CycleMultipleTool(uid, multiple, args.User); diff --git a/Content.Shared/UserInterface/ActivatableUISystem.cs b/Content.Shared/UserInterface/ActivatableUISystem.cs index c1822c4ee3..1bb11f337f 100644 --- a/Content.Shared/UserInterface/ActivatableUISystem.cs +++ b/Content.Shared/UserInterface/ActivatableUISystem.cs @@ -133,7 +133,7 @@ public sealed partial class ActivatableUISystem : EntitySystem private void OnActivate(EntityUid uid, ActivatableUIComponent component, ActivateInWorldEvent args) { - if (args.Handled) + if (args.Handled || !args.Complex) return; if (component.VerbOnly) diff --git a/Content.Shared/Verbs/SharedVerbSystem.cs b/Content.Shared/Verbs/SharedVerbSystem.cs index e78fe98f4c..319f927c7b 100644 --- a/Content.Shared/Verbs/SharedVerbSystem.cs +++ b/Content.Shared/Verbs/SharedVerbSystem.cs @@ -78,28 +78,8 @@ namespace Content.Shared.Verbs // call ActionBlocker checks, just cache it for the verb request. var canInteract = force || _actionBlockerSystem.CanInteract(user, target); - EntityUid? @using = null; - if (TryComp(user, out HandsComponent? hands) && (force || _actionBlockerSystem.CanUseHeldEntity(user))) - { - // if we don't actually have any hands, pass in a null value for the events. - if (hands.Count == 0) - { - hands = null; - } - else - { - @using = hands.ActiveHandEntity; - - // Check whether the "Held" entity is a virtual pull entity. If yes, set that as the entity being "Used". - // This allows you to do things like buckle a dragged person onto a surgery table, without click-dragging - // their sprite. - - if (TryComp(@using, out VirtualItemComponent? pull)) - { - @using = pull.BlockingEntity; - } - } - } + _interactionSystem.TryGetUsedEntity(user, out var @using); + TryComp(user, out var hands); // TODO: fix this garbage and use proper generics or reflection or something else, not this. if (types.Contains(typeof(InteractionVerb))) diff --git a/Content.Shared/Weapons/Misc/SharedGrapplingGunSystem.cs b/Content.Shared/Weapons/Misc/SharedGrapplingGunSystem.cs index 41e1895da2..6feffffbe3 100644 --- a/Content.Shared/Weapons/Misc/SharedGrapplingGunSystem.cs +++ b/Content.Shared/Weapons/Misc/SharedGrapplingGunSystem.cs @@ -116,7 +116,7 @@ public abstract class SharedGrapplingGunSystem : EntitySystem private void OnGunActivate(EntityUid uid, GrapplingGunComponent component, ActivateInWorldEvent args) { - if (!Timing.IsFirstTimePredicted || args.Handled) + if (!Timing.IsFirstTimePredicted || args.Handled || !args.Complex) return; if (Deleted(component.Projectile)) diff --git a/Content.Shared/Weapons/Misc/SharedTetherGunSystem.Force.cs b/Content.Shared/Weapons/Misc/SharedTetherGunSystem.Force.cs index eec115b02d..932ef70460 100644 --- a/Content.Shared/Weapons/Misc/SharedTetherGunSystem.Force.cs +++ b/Content.Shared/Weapons/Misc/SharedTetherGunSystem.Force.cs @@ -14,6 +14,9 @@ public abstract partial class SharedTetherGunSystem private void OnForceActivate(EntityUid uid, ForceGunComponent component, ActivateInWorldEvent args) { + if (!args.Complex) + return; + StopTether(uid, component); } diff --git a/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs b/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs index ad2249bfdd..3d7f9df458 100644 --- a/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs +++ b/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs @@ -152,6 +152,9 @@ public abstract partial class SharedTetherGunSystem : EntitySystem private void OnTetherActivate(EntityUid uid, TetherGunComponent component, ActivateInWorldEvent args) { + if (!args.Complex) + return; + StopTether(uid, component); } diff --git a/Content.Shared/Weapons/Ranged/Systems/BatteryWeaponFireModesSystem.cs b/Content.Shared/Weapons/Ranged/Systems/BatteryWeaponFireModesSystem.cs index 68fb2f27c9..8e44576d28 100644 --- a/Content.Shared/Weapons/Ranged/Systems/BatteryWeaponFireModesSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/BatteryWeaponFireModesSystem.cs @@ -75,6 +75,9 @@ public sealed class BatteryWeaponFireModesSystem : EntitySystem private void OnInteractHandEvent(EntityUid uid, BatteryWeaponFireModesComponent component, ActivateInWorldEvent args) { + if (!args.Complex) + return; + if (component.FireModes.Count < 2) return; diff --git a/Content.Shared/Weapons/Ranged/Systems/RechargeCycleAmmoSystem.cs b/Content.Shared/Weapons/Ranged/Systems/RechargeCycleAmmoSystem.cs index a014f8e5c7..ee5ca2174f 100644 --- a/Content.Shared/Weapons/Ranged/Systems/RechargeCycleAmmoSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/RechargeCycleAmmoSystem.cs @@ -18,6 +18,9 @@ public sealed class RechargeCycleAmmoSystem : EntitySystem private void OnRechargeCycled(EntityUid uid, RechargeCycleAmmoComponent component, ActivateInWorldEvent args) { + if (!args.Complex) + return; + if (!TryComp(uid, out var basic) || args.Handled) return; diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs index c421c92a9f..adae26a223 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs @@ -51,7 +51,7 @@ public abstract partial class SharedGunSystem /// private void OnChamberActivate(EntityUid uid, ChamberMagazineAmmoProviderComponent component, ActivateInWorldEvent args) { - if (args.Handled) + if (args.Handled || !args.Complex) return; args.Handled = true; diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index 71f5596ccf..8bf55e48bd 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -84,6 +84,7 @@ - type: Hands showInHands: false disableExplosionRecursion: true + - type: ComplexInteraction - type: IntrinsicRadioReceiver - type: IntrinsicRadioTransmitter channels: diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index a79d96065a..b5f69e6f56 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -1200,6 +1200,7 @@ state: "creampie_human" visible: false - type: Hands + - type: ComplexInteraction - type: GenericVisualizer visuals: enum.CreamPiedVisuals.Creamed: diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/mimic.yml b/Resources/Prototypes/Entities/Mobs/NPCs/mimic.yml index 657ac466f8..285e75bca0 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/mimic.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/mimic.yml @@ -12,7 +12,6 @@ - type: NpcFactionMember factions: - SimpleHostile - - type: Hands - type: Sprite drawdepth: Mobs sprite: Structures/Machines/VendingMachines/cola.rsi diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml index de3a282eeb..a3d4eafacb 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml @@ -40,6 +40,7 @@ factions: - Xeno - type: Hands + - type: ComplexInteraction - type: Sprite drawdepth: Mobs sprite: Mobs/Aliens/Xenos/burrower.rsi diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index b294729e07..0fa85fc84f 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -18,6 +18,7 @@ canInteract: true - type: GhostHearing - type: Hands + - type: ComplexInteraction - type: Puller - type: CombatMode - type: Physics diff --git a/Resources/Prototypes/Entities/Mobs/Player/guardian.yml b/Resources/Prototypes/Entities/Mobs/Player/guardian.yml index c7b4464350..80ee2c55b8 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/guardian.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/guardian.yml @@ -225,6 +225,7 @@ - type: Inventory templateId: holoclown - type: Hands + - type: ComplexInteraction - type: Clumsy clumsyDamage: types: diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index 01b938ffa8..a49f8ff34c 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -149,6 +149,7 @@ - type: Identity - type: IdExaminable - type: Hands + - type: ComplexInteraction - type: Internals - type: Inventory - type: InventorySlots @@ -309,6 +310,7 @@ abstract: true components: - type: Hands + - type: ComplexInteraction - type: Inventory - type: InventorySlots - type: ContainerContainer