Decouple interactions from hands, cleanup old events, add new fears (#28393)

* ok basic shit

* second part

* pretend it isn't real it can't hurt you.

* 👁️ 👁️

* shadowcommander review
This commit is contained in:
Nemanja
2024-05-31 16:26:19 -04:00
committed by GitHub
parent c4291920a6
commit a1a8f04036
72 changed files with 312 additions and 165 deletions

View File

@@ -148,7 +148,7 @@ public sealed class GuidebookSystem : EntitySystem
public void FakeClientActivateInWorld(EntityUid activated) public void FakeClientActivateInWorld(EntityUid activated)
{ {
var activateMsg = new ActivateInWorldEvent(GetGuidebookUser(), activated); var activateMsg = new ActivateInWorldEvent(GetGuidebookUser(), activated, true);
RaiseLocalEvent(activated, activateMsg); RaiseLocalEvent(activated, activateMsg);
} }

View File

@@ -29,6 +29,7 @@ namespace Content.IntegrationTests.Tests.Buckle
components: components:
- type: Buckle - type: Buckle
- type: Hands - type: Hands
- type: ComplexInteraction
- type: InputMover - type: InputMover
- type: Body - type: Body
prototype: Human prototype: Human

View File

@@ -24,6 +24,7 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
components: components:
- type: Cuffable - type: Cuffable
- type: Hands - type: Hands
- type: ComplexInteraction
- type: Body - type: Body
prototype: Human prototype: Human

View File

@@ -4,6 +4,7 @@ using Content.Server.Interaction;
using Content.Shared.Hands.Components; using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems; using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Interaction.Components;
using Content.Shared.Item; using Content.Shared.Item;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -64,6 +65,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
{ {
user = sEntities.SpawnEntity(null, coords); user = sEntities.SpawnEntity(null, coords);
sEntities.EnsureComponent<HandsComponent>(user); sEntities.EnsureComponent<HandsComponent>(user);
sEntities.EnsureComponent<ComplexInteractionComponent>(user);
handSys.AddHand(user, "hand", HandLocation.Left); handSys.AddHand(user, "hand", HandLocation.Left);
target = sEntities.SpawnEntity(null, coords); target = sEntities.SpawnEntity(null, coords);
item = sEntities.SpawnEntity(null, coords); item = sEntities.SpawnEntity(null, coords);
@@ -205,6 +207,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
{ {
user = sEntities.SpawnEntity(null, coords); user = sEntities.SpawnEntity(null, coords);
sEntities.EnsureComponent<HandsComponent>(user); sEntities.EnsureComponent<HandsComponent>(user);
sEntities.EnsureComponent<ComplexInteractionComponent>(user);
handSys.AddHand(user, "hand", HandLocation.Left); handSys.AddHand(user, "hand", HandLocation.Left);
target = sEntities.SpawnEntity(null, new MapCoordinates(new Vector2(SharedInteractionSystem.InteractionRange - 0.1f, 0), mapId)); target = sEntities.SpawnEntity(null, new MapCoordinates(new Vector2(SharedInteractionSystem.InteractionRange - 0.1f, 0), mapId));
item = sEntities.SpawnEntity(null, coords); item = sEntities.SpawnEntity(null, coords);
@@ -347,6 +350,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
{ {
user = sEntities.SpawnEntity(null, coords); user = sEntities.SpawnEntity(null, coords);
sEntities.EnsureComponent<HandsComponent>(user); sEntities.EnsureComponent<HandsComponent>(user);
sEntities.EnsureComponent<ComplexInteractionComponent>(user);
handSys.AddHand(user, "hand", HandLocation.Left); handSys.AddHand(user, "hand", HandLocation.Left);
target = sEntities.SpawnEntity(null, coords); target = sEntities.SpawnEntity(null, coords);
item = sEntities.SpawnEntity(null, coords); item = sEntities.SpawnEntity(null, coords);

View File

@@ -137,6 +137,7 @@ public abstract partial class InteractionTest
prototype: Aghost prototype: Aghost
- type: DoAfter - type: DoAfter
- type: Hands - type: Hands
- type: ComplexInteraction
- type: MindContainer - type: MindContainer
- type: Stripping - type: Stripping
- type: Tag - type: Tag

View File

@@ -27,6 +27,7 @@ namespace Content.IntegrationTests.Tests
id: HumanVendingDummy id: HumanVendingDummy
components: components:
- type: Hands - type: Hands
- type: ComplexInteraction
- type: Body - type: Body
prototype: Human prototype: Human

View File

@@ -39,7 +39,7 @@ public sealed class ActionOnInteractSystem : EntitySystem
private void OnActivate(EntityUid uid, ActionOnInteractComponent component, ActivateInWorldEvent args) private void OnActivate(EntityUid uid, ActionOnInteractComponent component, ActivateInWorldEvent args)
{ {
if (args.Handled) if (args.Handled || !args.Complex)
return; return;
if (component.ActionEntities is not {} actionEnts) if (component.ActionEntities is not {} actionEnts)

View File

@@ -155,7 +155,7 @@ namespace Content.Server.Atmos.EntitySystems
private void OnExtinguishActivateInWorld(EntityUid uid, ExtinguishOnInteractComponent component, ActivateInWorldEvent args) private void OnExtinguishActivateInWorld(EntityUid uid, ExtinguishOnInteractComponent component, ActivateInWorldEvent args)
{ {
if (args.Handled) if (args.Handled || !args.Complex)
return; return;
if (!TryComp(uid, out FlammableComponent? flammable)) if (!TryComp(uid, out FlammableComponent? flammable))

View File

@@ -246,6 +246,9 @@ public sealed class AirAlarmSystem : EntitySystem
private void OnActivate(EntityUid uid, AirAlarmComponent component, ActivateInWorldEvent args) private void OnActivate(EntityUid uid, AirAlarmComponent component, ActivateInWorldEvent args)
{ {
if (!args.Complex)
return;
if (TryComp<WiresPanelComponent>(uid, out var panel) && panel.Open) if (TryComp<WiresPanelComponent>(uid, out var panel) && panel.Open)
{ {
args.Handled = false; args.Handled = false;

View File

@@ -103,6 +103,9 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
private void OnPumpActivate(EntityUid uid, GasPressurePumpComponent pump, ActivateInWorldEvent args) private void OnPumpActivate(EntityUid uid, GasPressurePumpComponent pump, ActivateInWorldEvent args)
{ {
if (args.Handled || !args.Complex)
return;
if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
return; return;

View File

@@ -52,8 +52,12 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
private void OnActivate(EntityUid uid, GasValveComponent component, ActivateInWorldEvent args) private void OnActivate(EntityUid uid, GasValveComponent component, ActivateInWorldEvent args)
{ {
if (args.Handled || !args.Complex)
return;
Toggle(uid, component); Toggle(uid, component);
_audio.PlayPvs(component.ValveSound, uid, AudioParams.Default.WithVariation(0.25f)); _audio.PlayPvs(component.ValveSound, uid, AudioParams.Default.WithVariation(0.25f));
args.Handled = true;
} }
public void Set(EntityUid uid, GasValveComponent component, bool value) public void Set(EntityUid uid, GasValveComponent component, bool value)

View File

@@ -133,6 +133,9 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
private void OnPumpActivate(EntityUid uid, GasVolumePumpComponent pump, ActivateInWorldEvent args) private void OnPumpActivate(EntityUid uid, GasVolumePumpComponent pump, ActivateInWorldEvent args)
{ {
if (args.Handled || !args.Complex)
return;
if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
return; return;

View File

@@ -99,6 +99,9 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems
private void OnFilterActivate(EntityUid uid, GasFilterComponent filter, ActivateInWorldEvent args) private void OnFilterActivate(EntityUid uid, GasFilterComponent filter, ActivateInWorldEvent args)
{ {
if (args.Handled || !args.Complex)
return;
if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
return; return;

View File

@@ -139,6 +139,9 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems
private void OnMixerActivate(EntityUid uid, GasMixerComponent mixer, ActivateInWorldEvent args) private void OnMixerActivate(EntityUid uid, GasMixerComponent mixer, ActivateInWorldEvent args)
{ {
if (args.Handled || !args.Complex)
return;
if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
return; return;

View File

@@ -201,6 +201,9 @@ public sealed class GasCanisterSystem : EntitySystem
private void OnCanisterActivate(EntityUid uid, GasCanisterComponent component, ActivateInWorldEvent args) private void OnCanisterActivate(EntityUid uid, GasCanisterComponent component, ActivateInWorldEvent args)
{ {
if (!args.Complex)
return;
if (!TryComp<ActorComponent>(args.User, out var actor)) if (!TryComp<ActorComponent>(args.User, out var actor))
return; return;

View File

@@ -33,8 +33,12 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
private void OnActivate(EntityUid uid, GasOutletInjectorComponent component, ActivateInWorldEvent args) private void OnActivate(EntityUid uid, GasOutletInjectorComponent component, ActivateInWorldEvent args)
{ {
if (args.Handled || !args.Complex)
return;
component.Enabled = !component.Enabled; component.Enabled = !component.Enabled;
UpdateAppearance(uid, component); UpdateAppearance(uid, component);
args.Handled = true;
} }
public void UpdateAppearance(EntityUid uid, GasOutletInjectorComponent component, AppearanceComponent? appearance = null) public void UpdateAppearance(EntityUid uid, GasOutletInjectorComponent component, AppearanceComponent? appearance = null)

View File

@@ -36,7 +36,6 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem
SubscribeLocalEvent<CardboardBoxComponent, StorageAfterCloseEvent>(AfterStorageClosed); SubscribeLocalEvent<CardboardBoxComponent, StorageAfterCloseEvent>(AfterStorageClosed);
SubscribeLocalEvent<CardboardBoxComponent, GetAdditionalAccessEvent>(OnGetAdditionalAccess); SubscribeLocalEvent<CardboardBoxComponent, GetAdditionalAccessEvent>(OnGetAdditionalAccess);
SubscribeLocalEvent<CardboardBoxComponent, ActivateInWorldEvent>(OnInteracted); SubscribeLocalEvent<CardboardBoxComponent, ActivateInWorldEvent>(OnInteracted);
SubscribeLocalEvent<CardboardBoxComponent, InteractedNoHandEvent>(OnNoHandInteracted);
SubscribeLocalEvent<CardboardBoxComponent, EntInsertedIntoContainerMessage>(OnEntInserted); SubscribeLocalEvent<CardboardBoxComponent, EntInsertedIntoContainerMessage>(OnEntInserted);
SubscribeLocalEvent<CardboardBoxComponent, EntRemovedFromContainerMessage>(OnEntRemoved); SubscribeLocalEvent<CardboardBoxComponent, EntRemovedFromContainerMessage>(OnEntRemoved);
@@ -45,9 +44,18 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem
private void OnInteracted(EntityUid uid, CardboardBoxComponent component, ActivateInWorldEvent args) private void OnInteracted(EntityUid uid, CardboardBoxComponent component, ActivateInWorldEvent args)
{ {
if (args.Handled)
return;
if (!TryComp<EntityStorageComponent>(uid, out var box)) if (!TryComp<EntityStorageComponent>(uid, out var box))
return; return;
if (!args.Complex)
{
if (box.Open || !box.Contents.Contains(args.User))
return;
}
args.Handled = true; args.Handled = true;
_storage.ToggleOpen(args.User, uid, box); _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<EntityStorageComponent>(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) private void OnGetAdditionalAccess(EntityUid uid, CardboardBoxComponent component, ref GetAdditionalAccessEvent args)
{ {
if (component.Mover == null) if (component.Mover == null)

View File

@@ -26,7 +26,7 @@ public sealed class SignalSwitchSystem : EntitySystem
private void OnActivated(EntityUid uid, SignalSwitchComponent comp, ActivateInWorldEvent args) private void OnActivated(EntityUid uid, SignalSwitchComponent comp, ActivateInWorldEvent args)
{ {
if (args.Handled) if (args.Handled || !args.Complex)
return; return;
comp.State = !comp.State; comp.State = !comp.State;

View File

@@ -152,6 +152,9 @@ public sealed class MailingUnitSystem : EntitySystem
private void HandleActivate(EntityUid uid, MailingUnitComponent component, ActivateInWorldEvent args) private void HandleActivate(EntityUid uid, MailingUnitComponent component, ActivateInWorldEvent args)
{ {
if (args.Handled || !args.Complex)
return;
if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
{ {
return; return;

View File

@@ -263,6 +263,9 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
private void OnActivate(EntityUid uid, SharedDisposalUnitComponent component, ActivateInWorldEvent args) private void OnActivate(EntityUid uid, SharedDisposalUnitComponent component, ActivateInWorldEvent args)
{ {
if (args.Handled || !args.Complex)
return;
if (!TryComp(args.User, out ActorComponent? actor)) if (!TryComp(args.User, out ActorComponent? actor))
{ {
return; return;

View File

@@ -67,6 +67,9 @@ public sealed class AirlockSystem : SharedAirlockSystem
private void OnActivate(EntityUid uid, AirlockComponent component, ActivateInWorldEvent args) private void OnActivate(EntityUid uid, AirlockComponent component, ActivateInWorldEvent args)
{ {
if (args.Handled || !args.Complex)
return;
if (TryComp<WiresPanelComponent>(uid, out var panel) && if (TryComp<WiresPanelComponent>(uid, out var panel) &&
panel.Open && panel.Open &&
TryComp<ActorComponent>(args.User, out var actor)) TryComp<ActorComponent>(args.User, out var actor))

View File

@@ -218,6 +218,9 @@ namespace Content.Server.Explosion.EntitySystems
private void OnActivate(EntityUid uid, TriggerOnActivateComponent component, ActivateInWorldEvent args) private void OnActivate(EntityUid uid, TriggerOnActivateComponent component, ActivateInWorldEvent args)
{ {
if (args.Handled || !args.Complex)
return;
Trigger(uid, args.User); Trigger(uid, args.User);
args.Handled = true; args.Handled = true;
} }

View File

@@ -34,7 +34,7 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<AbsorbentComponent, ComponentInit>(OnAbsorbentInit); SubscribeLocalEvent<AbsorbentComponent, ComponentInit>(OnAbsorbentInit);
SubscribeLocalEvent<AbsorbentComponent, AfterInteractEvent>(OnAfterInteract); SubscribeLocalEvent<AbsorbentComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<AbsorbentComponent, InteractNoHandEvent>(OnInteractNoHand); SubscribeLocalEvent<AbsorbentComponent, UserActivateInWorldEvent>(OnActivateInWorld);
SubscribeLocalEvent<AbsorbentComponent, SolutionContainerChangedEvent>(OnAbsorbentSolutionChange); SubscribeLocalEvent<AbsorbentComponent, SolutionContainerChangedEvent>(OnAbsorbentSolutionChange);
} }
@@ -84,12 +84,12 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
Dirty(uid, component); 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; return;
Mop(uid, args.Target.Value, uid, component); Mop(uid, args.Target, uid, component);
args.Handled = true; args.Handled = true;
} }

View File

@@ -38,10 +38,14 @@ public sealed partial class GatherableSystem : EntitySystem
private void OnActivate(Entity<GatherableComponent> gatherable, ref ActivateInWorldEvent args) private void OnActivate(Entity<GatherableComponent> gatherable, ref ActivateInWorldEvent args)
{ {
if (args.Handled || !args.Complex)
return;
if (gatherable.Comp.ToolWhitelist?.IsValid(args.User, EntityManager) != true) if (gatherable.Comp.ToolWhitelist?.IsValid(args.User, EntityManager) != true)
return; return;
Gather(gatherable, args.User); Gather(gatherable, args.User);
args.Handled = true;
} }
public void Gather(EntityUid gatheredUid, EntityUid? gatherer = null, GatherableComponent? component = null) public void Gather(EntityUid gatheredUid, EntityUid? gatherer = null, GatherableComponent? component = null)

View File

@@ -126,7 +126,7 @@ namespace Content.Server.Light.EntitySystems
private void OnActivate(Entity<HandheldLightComponent> ent, ref ActivateInWorldEvent args) private void OnActivate(Entity<HandheldLightComponent> ent, ref ActivateInWorldEvent args)
{ {
if (args.Handled || !ent.Comp.ToggleOnInteract) if (args.Handled || !args.Complex || !ent.Comp.ToggleOnInteract)
return; return;
if (ToggleStatus(args.User, ent)) if (ToggleStatus(args.User, ent))

View File

@@ -40,7 +40,7 @@ public sealed class MechGrabberSystem : EntitySystem
SubscribeLocalEvent<MechGrabberComponent, MechEquipmentRemovedEvent>(OnEquipmentRemoved); SubscribeLocalEvent<MechGrabberComponent, MechEquipmentRemovedEvent>(OnEquipmentRemoved);
SubscribeLocalEvent<MechGrabberComponent, AttemptRemoveMechEquipmentEvent>(OnAttemptRemove); SubscribeLocalEvent<MechGrabberComponent, AttemptRemoveMechEquipmentEvent>(OnAttemptRemove);
SubscribeLocalEvent<MechGrabberComponent, InteractNoHandEvent>(OnInteract); SubscribeLocalEvent<MechGrabberComponent, UserActivateInWorldEvent>(OnInteract);
SubscribeLocalEvent<MechGrabberComponent, GrabberDoAfterEvent>(OnMechGrab); SubscribeLocalEvent<MechGrabberComponent, GrabberDoAfterEvent>(OnMechGrab);
} }
@@ -123,10 +123,11 @@ public sealed class MechGrabberSystem : EntitySystem
args.States.Add(GetNetEntity(uid), state); 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; return;
var target = args.Target;
if (args.Target == args.User || component.DoAfter != null) if (args.Target == args.User || component.DoAfter != null)
return; return;

View File

@@ -18,7 +18,7 @@ namespace Content.Server.Nutrition.EntitySystems
private void OnCigarActivatedEvent(Entity<CigarComponent> entity, ref ActivateInWorldEvent args) private void OnCigarActivatedEvent(Entity<CigarComponent> entity, ref ActivateInWorldEvent args)
{ {
if (args.Handled) if (args.Handled || !args.Complex)
return; return;
if (!EntityManager.TryGetComponent(entity, out SmokableComponent? smokable)) if (!EntityManager.TryGetComponent(entity, out SmokableComponent? smokable))

View File

@@ -45,10 +45,15 @@ public sealed class PinpointerSystem : SharedPinpointerSystem
private void OnActivate(EntityUid uid, PinpointerComponent component, ActivateInWorldEvent args) private void OnActivate(EntityUid uid, PinpointerComponent component, ActivateInWorldEvent args)
{ {
if (args.Handled || !args.Complex)
return;
TogglePinpointer(uid, component); TogglePinpointer(uid, component);
if (!component.CanRetarget) if (!component.CanRetarget)
LocateTarget(uid, component); LocateTarget(uid, component);
args.Handled = true;
} }
private void OnLocateTarget(ref FTLCompletedEvent ev) private void OnLocateTarget(ref FTLCompletedEvent ev)

View File

@@ -35,7 +35,7 @@ public sealed class GeigerSystem : SharedGeigerSystem
private void OnActivate(Entity<GeigerComponent> geiger, ref ActivateInWorldEvent args) private void OnActivate(Entity<GeigerComponent> geiger, ref ActivateInWorldEvent args)
{ {
if (args.Handled || geiger.Comp.AttachedToSuit) if (args.Handled || !args.Complex || geiger.Comp.AttachedToSuit)
return; return;
args.Handled = true; args.Handled = true;

View File

@@ -67,6 +67,9 @@ public sealed class JammerSystem : SharedJammerSystem
private void OnActivate(EntityUid uid, RadioJammerComponent comp, ActivateInWorldEvent args) private void OnActivate(EntityUid uid, RadioJammerComponent comp, ActivateInWorldEvent args)
{ {
if (args.Handled || !args.Complex)
return;
var activated = !HasComp<ActiveRadioJammerComponent>(uid) && var activated = !HasComp<ActiveRadioJammerComponent>(uid) &&
_powerCell.TryGetBatteryFromSlot(uid, out var battery) && _powerCell.TryGetBatteryFromSlot(uid, out var battery) &&
battery.CurrentCharge > GetCurrentWattage(comp); battery.CurrentCharge > GetCurrentWattage(comp);

View File

@@ -81,6 +81,9 @@ public sealed class RadioDeviceSystem : EntitySystem
#region Toggling #region Toggling
private void OnActivateMicrophone(EntityUid uid, RadioMicrophoneComponent component, ActivateInWorldEvent args) private void OnActivateMicrophone(EntityUid uid, RadioMicrophoneComponent component, ActivateInWorldEvent args)
{ {
if (!args.Complex)
return;
if (!component.ToggleOnInteract) if (!component.ToggleOnInteract)
return; return;
@@ -90,6 +93,9 @@ public sealed class RadioDeviceSystem : EntitySystem
private void OnActivateSpeaker(EntityUid uid, RadioSpeakerComponent component, ActivateInWorldEvent args) private void OnActivateSpeaker(EntityUid uid, RadioSpeakerComponent component, ActivateInWorldEvent args)
{ {
if (!args.Complex)
return;
if (!component.ToggleOnInteract) if (!component.ToggleOnInteract)
return; return;

View File

@@ -43,7 +43,7 @@ public sealed partial class RevenantSystem
private void InitializeAbilities() private void InitializeAbilities()
{ {
SubscribeLocalEvent<RevenantComponent, InteractNoHandEvent>(OnInteract); SubscribeLocalEvent<RevenantComponent, UserActivateInWorldEvent>(OnInteract);
SubscribeLocalEvent<RevenantComponent, SoulEvent>(OnSoulSearch); SubscribeLocalEvent<RevenantComponent, SoulEvent>(OnSoulSearch);
SubscribeLocalEvent<RevenantComponent, HarvestEvent>(OnHarvest); SubscribeLocalEvent<RevenantComponent, HarvestEvent>(OnHarvest);
@@ -53,11 +53,14 @@ public sealed partial class RevenantSystem
SubscribeLocalEvent<RevenantComponent, RevenantMalfunctionActionEvent>(OnMalfunctionAction); SubscribeLocalEvent<RevenantComponent, RevenantMalfunctionActionEvent>(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; return;
var target = args.Target.Value;
if (args.Target == args.User)
return;
var target = args.Target;
if (HasComp<PoweredLightComponent>(target)) if (HasComp<PoweredLightComponent>(target))
{ {
@@ -78,6 +81,8 @@ public sealed partial class RevenantSystem
{ {
BeginHarvestDoAfter(uid, target, component, essence); BeginHarvestDoAfter(uid, target, component, essence);
} }
args.Handled = true;
} }
private void BeginSoulSearchDoAfter(EntityUid uid, EntityUid target, RevenantComponent revenant) private void BeginSoulSearchDoAfter(EntityUid uid, EntityUid target, RevenantComponent revenant)

View File

@@ -128,15 +128,20 @@ public sealed class ThrusterSystem : EntitySystem
private void OnActivateThruster(EntityUid uid, ThrusterComponent component, ActivateInWorldEvent args) private void OnActivateThruster(EntityUid uid, ThrusterComponent component, ActivateInWorldEvent args)
{ {
if (args.Handled || !args.Complex)
return;
component.Enabled ^= true; component.Enabled ^= true;
if (!component.Enabled) if (!component.Enabled)
{ {
DisableThruster(uid, component); DisableThruster(uid, component);
args.Handled = true;
} }
else if (CanEnable(uid, component)) else if (CanEnable(uid, component))
{ {
EnableThruster(uid, component); EnableThruster(uid, component);
args.Handled = true;
} }
} }

View File

@@ -141,6 +141,9 @@ namespace Content.Server.Tabletop
private void OnTabletopActivate(EntityUid uid, TabletopGameComponent component, ActivateInWorldEvent args) private void OnTabletopActivate(EntityUid uid, TabletopGameComponent component, ActivateInWorldEvent args)
{ {
if (args.Handled || !args.Complex)
return;
// Check that a player is attached to the entity. // Check that a player is attached to the entity.
if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
return; return;

View File

@@ -21,6 +21,7 @@ using Content.Shared.Damage;
using Content.Shared.Hands.Components; using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems; using Content.Shared.Hands.EntitySystems;
using Content.Shared.Humanoid; using Content.Shared.Humanoid;
using Content.Shared.Interaction.Components;
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;
@@ -106,6 +107,7 @@ namespace Content.Server.Zombies
RemComp<ReproductiveComponent>(target); RemComp<ReproductiveComponent>(target);
RemComp<ReproductivePartnerComponent>(target); RemComp<ReproductivePartnerComponent>(target);
RemComp<LegsParalyzedComponent>(target); RemComp<LegsParalyzedComponent>(target);
RemComp<ComplexInteractionComponent>(target);
//funny voice //funny voice
var accentType = "zombie"; var accentType = "zombie";

View File

@@ -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 /// involve using a held entity. In the majority of cases, systems that provide interactions will not need
/// to check this themselves. /// to check this themselves.
/// </remarks> /// </remarks>
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); RaiseLocalEvent(user, ev);
return !ev.Cancelled; return !ev.Cancelled;

View File

@@ -83,10 +83,11 @@ public sealed class BurialSystem : EntitySystem
private void OnActivate(EntityUid uid, GraveComponent component, ActivateInWorldEvent args) private void OnActivate(EntityUid uid, GraveComponent component, ActivateInWorldEvent args)
{ {
if (args.Handled) if (args.Handled || !args.Complex)
return; return;
_popupSystem.PopupClient(Loc.GetString("grave-digging-requires-tool", ("grave", args.Target)), uid, args.User); _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) private void OnGraveDigging(EntityUid uid, GraveComponent component, GraveDiggingDoAfterEvent args)

View File

@@ -31,7 +31,11 @@ public abstract class SharedSolutionContainerMixerSystem : EntitySystem
private void OnActivateInWorld(Entity<SolutionContainerMixerComponent> entity, ref ActivateInWorldEvent args) private void OnActivateInWorld(Entity<SolutionContainerMixerComponent> entity, ref ActivateInWorldEvent args)
{ {
if (args.Handled || !args.Complex)
return;
TryStartMix(entity, args.User); TryStartMix(entity, args.User);
args.Handled = true;
} }
private void OnRemoveAttempt(Entity<SolutionContainerMixerComponent> ent, ref ContainerIsRemovingAttemptEvent args) private void OnRemoveAttempt(Entity<SolutionContainerMixerComponent> ent, ref ContainerIsRemovingAttemptEvent args)

View File

@@ -28,7 +28,7 @@ namespace Content.Shared.DeviceLinking.Systems
private void OnActivated(EntityUid uid, TwoWayLeverComponent component, ActivateInWorldEvent args) private void OnActivated(EntityUid uid, TwoWayLeverComponent component, ActivateInWorldEvent args)
{ {
if (args.Handled) if (args.Handled || !args.Complex)
return; return;
component.State = component.State switch component.State = component.State switch

View File

@@ -215,7 +215,7 @@ public abstract partial class SharedDoorSystem : EntitySystem
#region Interactions #region Interactions
protected void OnActivate(EntityUid uid, DoorComponent door, ActivateInWorldEvent args) protected void OnActivate(EntityUid uid, DoorComponent door, ActivateInWorldEvent args)
{ {
if (args.Handled || !door.ClickOpen) if (args.Handled || !args.Complex || !door.ClickOpen)
return; return;
if (!TryToggleDoor(uid, door, args.User, predicted: true)) if (!TryToggleDoor(uid, door, args.User, predicted: true))

View File

@@ -3,6 +3,7 @@ using Content.Shared.Examine;
using Content.Shared.Hands.Components; using Content.Shared.Hands.Components;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.Input; using Content.Shared.Input;
using Content.Shared.Interaction;
using Content.Shared.Inventory.VirtualItem; using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Localizations; using Content.Shared.Localizations;
using Robust.Shared.Input.Binding; using Robust.Shared.Input.Binding;
@@ -23,6 +24,7 @@ public abstract partial class SharedHandsSystem : EntitySystem
SubscribeAllEvent<RequestMoveHandItemEvent>(HandleMoveItemFromHand); SubscribeAllEvent<RequestMoveHandItemEvent>(HandleMoveItemFromHand);
SubscribeAllEvent<RequestHandAltInteractEvent>(HandleHandAltInteract); SubscribeAllEvent<RequestHandAltInteractEvent>(HandleHandAltInteract);
SubscribeLocalEvent<HandsComponent, GetUsedEntityEvent>(OnGetUsedEntity);
SubscribeLocalEvent<HandsComponent, ExaminedEvent>(HandleExamined); SubscribeLocalEvent<HandsComponent, ExaminedEvent>(HandleExamined);
CommandBinds.Builder CommandBinds.Builder
@@ -181,6 +183,18 @@ public abstract partial class SharedHandsSystem : EntitySystem
return true; 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<VirtualItemComponent>(component.ActiveHandEntity, out var virtualItem))
args.Used = virtualItem.BlockingEntity;
else
args.Used = component.ActiveHandEntity;
}
//TODO: Actually shows all items/clothing/etc. //TODO: Actually shows all items/clothing/etc.
private void HandleExamined(EntityUid examinedUid, HandsComponent handsComp, ExaminedEvent args) private void HandleExamined(EntityUid examinedUid, HandsComponent handsComp, ExaminedEvent args)
{ {

View File

@@ -18,14 +18,49 @@ public sealed class ActivateInWorldEvent : HandledEntityEventArgs, ITargetedInte
/// </summary> /// </summary>
public EntityUid Target { get; } public EntityUid Target { get; }
/// <summary>
/// Whether or not <see cref="User"/> can perform complex interactions or only basic ones.
/// </summary>
public bool Complex;
/// <summary> /// <summary>
/// Set to true when the activation is logged by a specific logger. /// Set to true when the activation is logged by a specific logger.
/// </summary> /// </summary>
public bool WasLogged { get; set; } public bool WasLogged { get; set; }
public ActivateInWorldEvent(EntityUid user, EntityUid target) public ActivateInWorldEvent(EntityUid user, EntityUid target, bool complex)
{ {
User = user; User = user;
Target = target; Target = target;
Complex = complex;
}
}
/// <summary>
/// Event raised on the user when it activates something in the world
/// </summary>
[PublicAPI]
public sealed class UserActivateInWorldEvent : HandledEntityEventArgs, ITargetedInteractEventArgs
{
/// <summary>
/// Entity that activated the target world entity.
/// </summary>
public EntityUid User { get; }
/// <summary>
/// Entity that was activated in the world.
/// </summary>
public EntityUid Target { get; }
/// <summary>
/// Whether or not <see cref="User"/> can perform complex interactions or only basic ones.
/// </summary>
public bool Complex;
public UserActivateInWorldEvent(EntityUid user, EntityUid target, bool complex)
{
User = user;
Target = target;
Complex = complex;
} }
} }

View File

@@ -0,0 +1,9 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Interaction.Components;
/// <summary>
/// This is used for identifying entities as being able to use complex interactions with the environment.
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(SharedInteractionSystem))]
public sealed partial class ComplexInteractionComponent : Component;

View File

@@ -1,12 +1,9 @@
namespace Content.Shared.Interaction.Events namespace Content.Shared.Interaction.Events
{ {
public sealed class UseAttemptEvent : CancellableEntityEventArgs public sealed class UseAttemptEvent(EntityUid uid, EntityUid used) : CancellableEntityEventArgs
{ {
public UseAttemptEvent(EntityUid uid) public EntityUid Uid { get; } = uid;
{
Uid = uid;
}
public EntityUid Uid { get; } public EntityUid Used = used;
} }
} }

View File

@@ -51,58 +51,4 @@ namespace Content.Shared.Interaction
Target = target; Target = target;
} }
} }
/// <summary>
/// Low-level interaction event used for entities without hands.
/// </summary>
/// <remarks>
/// SHIT IS CURSED.
/// </remarks>
//TODO: KILLLLLLL
public sealed class InteractNoHandEvent : HandledEntityEventArgs
{
/// <summary>
/// Entity that triggered the interaction.
/// </summary>
public EntityUid User;
/// <summary>
/// Entity that was interacted on.
/// </summary>
public EntityUid? Target;
public EntityCoordinates ClickLocation;
public InteractNoHandEvent(EntityUid user, EntityUid? target, EntityCoordinates clickLocation)
{
User = user;
Target = target;
ClickLocation = clickLocation;
}
}
/// <summary>
/// Reverse of the InteractNoHandEvent - raised on what was interacted on, rather than the other way around.
/// </summary>
public sealed class InteractedNoHandEvent : HandledEntityEventArgs
{
/// <summary>
/// Entity that was interacted on
/// </summary>
public EntityUid Target;
/// <summary>
/// Entity that triggered this interaction
/// </summary>
public EntityUid User;
public EntityCoordinates ClickLocation;
public InteractedNoHandEvent(EntityUid target, EntityUid user, EntityCoordinates clickLocation)
{
Target = target;
User = user;
ClickLocation = clickLocation;
}
}
} }

View File

@@ -32,6 +32,9 @@ public sealed class InteractionPopupSystem : EntitySystem
private void OnActivateInWorld(EntityUid uid, InteractionPopupComponent component, ActivateInWorldEvent args) private void OnActivateInWorld(EntityUid uid, InteractionPopupComponent component, ActivateInWorldEvent args)
{ {
if (!args.Complex)
return;
if (!component.OnActivate) if (!component.OnActivate)
return; return;

View File

@@ -76,6 +76,7 @@ namespace Content.Shared.Interaction
private EntityQuery<WallMountComponent> _wallMountQuery; private EntityQuery<WallMountComponent> _wallMountQuery;
private EntityQuery<UseDelayComponent> _delayQuery; private EntityQuery<UseDelayComponent> _delayQuery;
private EntityQuery<ActivatableUIComponent> _uiQuery; private EntityQuery<ActivatableUIComponent> _uiQuery;
private EntityQuery<ComplexInteractionComponent> _complexInteractionQuery;
private const CollisionGroup InRangeUnobstructedMask = CollisionGroup.Impassable | CollisionGroup.InteractImpassable; private const CollisionGroup InRangeUnobstructedMask = CollisionGroup.Impassable | CollisionGroup.InteractImpassable;
@@ -98,6 +99,7 @@ namespace Content.Shared.Interaction
_wallMountQuery = GetEntityQuery<WallMountComponent>(); _wallMountQuery = GetEntityQuery<WallMountComponent>();
_delayQuery = GetEntityQuery<UseDelayComponent>(); _delayQuery = GetEntityQuery<UseDelayComponent>();
_uiQuery = GetEntityQuery<ActivatableUIComponent>(); _uiQuery = GetEntityQuery<ActivatableUIComponent>();
_complexInteractionQuery = GetEntityQuery<ComplexInteractionComponent>();
SubscribeLocalEvent<BoundUserInterfaceCheckRangeEvent>(HandleUserInterfaceRangeCheck); SubscribeLocalEvent<BoundUserInterfaceCheckRangeEvent>(HandleUserInterfaceRangeCheck);
SubscribeLocalEvent<BoundUserInterfaceMessageAttempt>(OnBoundInterfaceInteractAttempt); SubscribeLocalEvent<BoundUserInterfaceMessageAttempt>(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. // TODO this needs to be handled better. This probably bypasses many complex can-interact checks in weird roundabout ways.
if (_actionBlockerSystem.CanInteract(user, target)) if (_actionBlockerSystem.CanInteract(user, target))
{ {
UserInteraction(relay.RelayEntity.Value, coordinates, target, altInteract, checkCanInteract, UserInteraction(relay.RelayEntity.Value,
checkAccess, checkCanUse); coordinates,
target,
altInteract,
checkCanInteract,
checkAccess,
checkCanUse);
return; return;
} }
} }
@@ -398,25 +405,10 @@ namespace Content.Shared.Interaction
? !checkAccess || InRangeUnobstructed(user, coordinates) ? !checkAccess || InRangeUnobstructed(user, coordinates)
: !checkAccess || InRangeUnobstructed(user, target.Value); // permits interactions with wall mounted entities : !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 // empty-hand interactions
// combat mode hand interactions will always be true here -- since // combat mode hand interactions will always be true here -- since
// they check this earlier before returning in // they check this earlier before returning in
if (hands.ActiveHandEntity is not { } held) if (!TryGetUsedEntity(user, out var used, checkCanUse))
{ {
if (inRangeUnobstructed && target != null) if (inRangeUnobstructed && target != null)
InteractHand(user, target.Value); InteractHand(user, target.Value);
@@ -424,11 +416,7 @@ namespace Content.Shared.Interaction
return; return;
} }
// Can the user use the held entity? if (target == used)
if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user))
return;
if (target == held)
{ {
UseInHandInteraction(user, target.Value, checkCanUse: false, checkCanInteract: false); UseInHandInteraction(user, target.Value, checkCanUse: false, checkCanInteract: false);
return; return;
@@ -438,7 +426,7 @@ namespace Content.Shared.Interaction
{ {
InteractUsing( InteractUsing(
user, user,
held, used.Value,
target.Value, target.Value,
coordinates, coordinates,
checkCanInteract: false, checkCanInteract: false,
@@ -449,7 +437,7 @@ namespace Content.Shared.Interaction
InteractUsingRanged( InteractUsingRanged(
user, user,
held, used.Value,
target, target,
coordinates, coordinates,
inRangeUnobstructed); inRangeUnobstructed);
@@ -457,6 +445,18 @@ namespace Content.Shared.Interaction
public void InteractHand(EntityUid user, EntityUid target) 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 // allow for special logic before main interaction
var ev = new BeforeInteractHandEvent(target); var ev = new BeforeInteractHandEvent(target);
RaiseLocalEvent(user, ev); RaiseLocalEvent(user, ev);
@@ -475,10 +475,12 @@ namespace Content.Shared.Interaction
return; return;
// Else we run Activate. // Else we run Activate.
InteractionActivate(user, target, InteractionActivate(user,
target,
checkCanInteract: false, checkCanInteract: false,
checkUseDelay: true, checkUseDelay: true,
checkAccess: false); checkAccess: false,
complexInteractions: complexInteractions);
} }
public void InteractUsingRanged(EntityUid user, EntityUid used, EntityUid? target, public void InteractUsingRanged(EntityUid user, EntityUid used, EntityUid? target,
@@ -921,7 +923,7 @@ namespace Content.Shared.Interaction
if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target)) if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target))
return; return;
if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user)) if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user, used))
return; return;
if (RangedInteractDoBefore(user, used, target, clickLocation, true)) if (RangedInteractDoBefore(user, used, target, clickLocation, true))
@@ -1001,7 +1003,8 @@ namespace Content.Shared.Interaction
EntityUid used, EntityUid used,
bool checkCanInteract = true, bool checkCanInteract = true,
bool checkUseDelay = true, bool checkUseDelay = true,
bool checkAccess = true) bool checkAccess = true,
bool? complexInteractions = null)
{ {
_delayQuery.TryComp(used, out var delayComponent); _delayQuery.TryComp(used, out var delayComponent);
if (checkUseDelay && delayComponent != null && _useDelay.IsDelayed((used, delayComponent))) if (checkUseDelay && delayComponent != null && _useDelay.IsDelayed((used, delayComponent)))
@@ -1018,13 +1021,12 @@ namespace Content.Shared.Interaction
if (checkAccess && !IsAccessible(user, used)) if (checkAccess && !IsAccessible(user, used))
return false; return false;
// Does the user have hands? complexInteractions ??= SupportsComplexInteractions(user);
if (!_handsQuery.HasComp(user)) var activateMsg = new ActivateInWorldEvent(user, used, complexInteractions.Value);
return false;
var activateMsg = new ActivateInWorldEvent(user, used);
RaiseLocalEvent(used, activateMsg, true); 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; return false;
DoContactInteraction(user, used, activateMsg); DoContactInteraction(user, used, activateMsg);
@@ -1059,7 +1061,7 @@ namespace Content.Shared.Interaction
if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used)) if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used))
return false; return false;
if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user)) if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user, used))
return false; return false;
var useMsg = new UseInHandEvent(user); var useMsg = new UseInHandEvent(user);
@@ -1259,6 +1261,39 @@ namespace Content.Shared.Interaction
? BoundUserInterfaceRangeResult.Pass ? BoundUserInterfaceRangeResult.Pass
: BoundUserInterfaceRangeResult.Fail; : BoundUserInterfaceRangeResult.Fail;
} }
/// <summary>
/// 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.
/// </summary>
/// <returns>If there is an entity being used.</returns>
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;
}
/// <summary>
/// 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.
/// </summary>
public bool SupportsComplexInteractions(EntityUid user)
{
return _complexInteractionQuery.HasComp(user);
}
} }
/// <summary> /// <summary>
@@ -1284,6 +1319,24 @@ namespace Content.Shared.Interaction
} }
} }
/// <summary>
/// Raised directed by-ref on an entity to determine what item will be used in interactions.
/// </summary>
[ByRefEvent]
public record struct GetUsedEntityEvent()
{
public EntityUid? Used = null;
public bool Handled => Used != null;
};
/// <summary>
/// Raised directed by-ref on an item and a user to determine if interactions can occur.
/// </summary>
/// <param name="Cancelled">Whether the hand interaction should be cancelled.</param>
[ByRefEvent]
public record struct AttemptUseInteractEvent(EntityUid User, EntityUid Used, bool Cancelled = false);
/// <summary> /// <summary>
/// Raised directed by-ref on an item to determine if hand interactions should go through. /// 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. /// Defaults to allowing hand interactions to go through. Cancel to force the item to be attacked instead.

View File

@@ -62,7 +62,7 @@ public sealed class LockSystem : EntitySystem
private void OnActivated(EntityUid uid, LockComponent lockComp, ActivateInWorldEvent args) private void OnActivated(EntityUid uid, LockComponent lockComp, ActivateInWorldEvent args)
{ {
if (args.Handled) if (args.Handled || !args.Complex)
return; return;
// Only attempt an unlock by default on Activate // Only attempt an unlock by default on Activate

View File

@@ -43,7 +43,7 @@ public abstract class SharedMechSystem : EntitySystem
{ {
SubscribeLocalEvent<MechComponent, MechToggleEquipmentEvent>(OnToggleEquipmentAction); SubscribeLocalEvent<MechComponent, MechToggleEquipmentEvent>(OnToggleEquipmentAction);
SubscribeLocalEvent<MechComponent, MechEjectPilotEvent>(OnEjectPilotEvent); SubscribeLocalEvent<MechComponent, MechEjectPilotEvent>(OnEjectPilotEvent);
SubscribeLocalEvent<MechComponent, InteractNoHandEvent>(RelayInteractionEvent); SubscribeLocalEvent<MechComponent, UserActivateInWorldEvent>(RelayInteractionEvent);
SubscribeLocalEvent<MechComponent, ComponentStartup>(OnStartup); SubscribeLocalEvent<MechComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<MechComponent, DestructionEventArgs>(OnDestruction); SubscribeLocalEvent<MechComponent, DestructionEventArgs>(OnDestruction);
SubscribeLocalEvent<MechComponent, GetAdditionalAccessEvent>(OnGetAdditionalAccess); SubscribeLocalEvent<MechComponent, GetAdditionalAccessEvent>(OnGetAdditionalAccess);
@@ -71,7 +71,7 @@ public abstract class SharedMechSystem : EntitySystem
TryEject(uid, component); 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; var pilot = component.PilotSlot.ContainedEntity;
if (pilot == null) if (pilot == null)

View File

@@ -47,7 +47,7 @@ public abstract partial class SharedProjectileSystem : EntitySystem
if (component.RemovalTime == null) if (component.RemovalTime == null)
return; return;
if (args.Handled || !TryComp<PhysicsComponent>(uid, out var physics) || physics.BodyType != BodyType.Static) if (args.Handled || !args.Complex || !TryComp<PhysicsComponent>(uid, out var physics) || physics.BodyType != BodyType.Static)
return; return;
args.Handled = true; args.Handled = true;

View File

@@ -89,7 +89,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
protected void OnInteract(EntityUid uid, SharedEntityStorageComponent component, ActivateInWorldEvent args) protected void OnInteract(EntityUid uid, SharedEntityStorageComponent component, ActivateInWorldEvent args)
{ {
if (args.Handled) if (args.Handled || !args.Complex)
return; return;
args.Handled = true; args.Handled = true;

View File

@@ -364,7 +364,7 @@ public abstract class SharedStorageSystem : EntitySystem
/// </summary> /// </summary>
private void OnActivate(EntityUid uid, StorageComponent storageComp, ActivateInWorldEvent args) 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; return;
// Toggle // Toggle

View File

@@ -21,7 +21,7 @@ public abstract class SharedStrippableSystem : EntitySystem
private void OnActivateInWorld(EntityUid uid, StrippableComponent component, ActivateInWorldEvent args) 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; return;
if (TryOpenStrippingUi(args.User, (uid, component))) if (TryOpenStrippingUi(args.User, (uid, component)))

View File

@@ -26,7 +26,11 @@ public abstract class SharedTrayScannerSystem : EntitySystem
private void OnTrayScannerActivate(EntityUid uid, TrayScannerComponent scanner, ActivateInWorldEvent args) private void OnTrayScannerActivate(EntityUid uid, TrayScannerComponent scanner, ActivateInWorldEvent args)
{ {
if (args.Handled || !args.Complex)
return;
SetScannerEnabled(uid, !scanner.Enabled, scanner); SetScannerEnabled(uid, !scanner.Enabled, scanner);
args.Handled = true;
} }
private void SetScannerEnabled(EntityUid uid, bool enabled, TrayScannerComponent? scanner = null) private void SetScannerEnabled(EntityUid uid, bool enabled, TrayScannerComponent? scanner = null)

View File

@@ -101,6 +101,9 @@ public sealed class SwapTeleporterSystem : EntitySystem
private void OnActivateInWorld(Entity<SwapTeleporterComponent> ent, ref ActivateInWorldEvent args) private void OnActivateInWorld(Entity<SwapTeleporterComponent> ent, ref ActivateInWorldEvent args)
{ {
if (args.Handled || !args.Complex)
return;
var (uid, comp) = ent; var (uid, comp) = ent;
var user = args.User; var user = args.User;
if (comp.TeleportTime != null) if (comp.TeleportTime != null)
@@ -130,6 +133,7 @@ public sealed class SwapTeleporterSystem : EntitySystem
comp.NextTeleportUse = _timing.CurTime + comp.Cooldown; comp.NextTeleportUse = _timing.CurTime + comp.Cooldown;
comp.TeleportTime = _timing.CurTime + comp.TeleportDelay; comp.TeleportTime = _timing.CurTime + comp.TeleportDelay;
Dirty(uid, comp); Dirty(uid, comp);
args.Handled = true;
} }
public void DoTeleport(Entity<SwapTeleporterComponent, TransformComponent> ent) public void DoTeleport(Entity<SwapTeleporterComponent, TransformComponent> ent)

View File

@@ -79,7 +79,7 @@ namespace Content.Shared.Toilet.Systems
private void OnActivateInWorld(EntityUid uid, ToiletComponent comp, ActivateInWorldEvent args) private void OnActivateInWorld(EntityUid uid, ToiletComponent comp, ActivateInWorldEvent args)
{ {
if (args.Handled) if (args.Handled || !args.Complex)
return; return;
args.Handled = true; args.Handled = true;

View File

@@ -28,7 +28,7 @@ public abstract partial class SharedToolSystem
private void OnMultipleToolActivated(EntityUid uid, MultipleToolComponent multiple, ActivateInWorldEvent args) private void OnMultipleToolActivated(EntityUid uid, MultipleToolComponent multiple, ActivateInWorldEvent args)
{ {
if (args.Handled) if (args.Handled || !args.Complex)
return; return;
args.Handled = CycleMultipleTool(uid, multiple, args.User); args.Handled = CycleMultipleTool(uid, multiple, args.User);

View File

@@ -133,7 +133,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
private void OnActivate(EntityUid uid, ActivatableUIComponent component, ActivateInWorldEvent args) private void OnActivate(EntityUid uid, ActivatableUIComponent component, ActivateInWorldEvent args)
{ {
if (args.Handled) if (args.Handled || !args.Complex)
return; return;
if (component.VerbOnly) if (component.VerbOnly)

View File

@@ -78,28 +78,8 @@ namespace Content.Shared.Verbs
// call ActionBlocker checks, just cache it for the verb request. // call ActionBlocker checks, just cache it for the verb request.
var canInteract = force || _actionBlockerSystem.CanInteract(user, target); var canInteract = force || _actionBlockerSystem.CanInteract(user, target);
EntityUid? @using = null; _interactionSystem.TryGetUsedEntity(user, out var @using);
if (TryComp(user, out HandsComponent? hands) && (force || _actionBlockerSystem.CanUseHeldEntity(user))) TryComp<HandsComponent>(user, out var hands);
{
// 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;
}
}
}
// TODO: fix this garbage and use proper generics or reflection or something else, not this. // TODO: fix this garbage and use proper generics or reflection or something else, not this.
if (types.Contains(typeof(InteractionVerb))) if (types.Contains(typeof(InteractionVerb)))

View File

@@ -116,7 +116,7 @@ public abstract class SharedGrapplingGunSystem : EntitySystem
private void OnGunActivate(EntityUid uid, GrapplingGunComponent component, ActivateInWorldEvent args) private void OnGunActivate(EntityUid uid, GrapplingGunComponent component, ActivateInWorldEvent args)
{ {
if (!Timing.IsFirstTimePredicted || args.Handled) if (!Timing.IsFirstTimePredicted || args.Handled || !args.Complex)
return; return;
if (Deleted(component.Projectile)) if (Deleted(component.Projectile))

View File

@@ -14,6 +14,9 @@ public abstract partial class SharedTetherGunSystem
private void OnForceActivate(EntityUid uid, ForceGunComponent component, ActivateInWorldEvent args) private void OnForceActivate(EntityUid uid, ForceGunComponent component, ActivateInWorldEvent args)
{ {
if (!args.Complex)
return;
StopTether(uid, component); StopTether(uid, component);
} }

View File

@@ -152,6 +152,9 @@ public abstract partial class SharedTetherGunSystem : EntitySystem
private void OnTetherActivate(EntityUid uid, TetherGunComponent component, ActivateInWorldEvent args) private void OnTetherActivate(EntityUid uid, TetherGunComponent component, ActivateInWorldEvent args)
{ {
if (!args.Complex)
return;
StopTether(uid, component); StopTether(uid, component);
} }

View File

@@ -75,6 +75,9 @@ public sealed class BatteryWeaponFireModesSystem : EntitySystem
private void OnInteractHandEvent(EntityUid uid, BatteryWeaponFireModesComponent component, ActivateInWorldEvent args) private void OnInteractHandEvent(EntityUid uid, BatteryWeaponFireModesComponent component, ActivateInWorldEvent args)
{ {
if (!args.Complex)
return;
if (component.FireModes.Count < 2) if (component.FireModes.Count < 2)
return; return;

View File

@@ -18,6 +18,9 @@ public sealed class RechargeCycleAmmoSystem : EntitySystem
private void OnRechargeCycled(EntityUid uid, RechargeCycleAmmoComponent component, ActivateInWorldEvent args) private void OnRechargeCycled(EntityUid uid, RechargeCycleAmmoComponent component, ActivateInWorldEvent args)
{ {
if (!args.Complex)
return;
if (!TryComp<BasicEntityAmmoProviderComponent>(uid, out var basic) || args.Handled) if (!TryComp<BasicEntityAmmoProviderComponent>(uid, out var basic) || args.Handled)
return; return;

View File

@@ -51,7 +51,7 @@ public abstract partial class SharedGunSystem
/// </summary> /// </summary>
private void OnChamberActivate(EntityUid uid, ChamberMagazineAmmoProviderComponent component, ActivateInWorldEvent args) private void OnChamberActivate(EntityUid uid, ChamberMagazineAmmoProviderComponent component, ActivateInWorldEvent args)
{ {
if (args.Handled) if (args.Handled || !args.Complex)
return; return;
args.Handled = true; args.Handled = true;

View File

@@ -84,6 +84,7 @@
- type: Hands - type: Hands
showInHands: false showInHands: false
disableExplosionRecursion: true disableExplosionRecursion: true
- type: ComplexInteraction
- type: IntrinsicRadioReceiver - type: IntrinsicRadioReceiver
- type: IntrinsicRadioTransmitter - type: IntrinsicRadioTransmitter
channels: channels:

View File

@@ -1200,6 +1200,7 @@
state: "creampie_human" state: "creampie_human"
visible: false visible: false
- type: Hands - type: Hands
- type: ComplexInteraction
- type: GenericVisualizer - type: GenericVisualizer
visuals: visuals:
enum.CreamPiedVisuals.Creamed: enum.CreamPiedVisuals.Creamed:

View File

@@ -12,7 +12,6 @@
- type: NpcFactionMember - type: NpcFactionMember
factions: factions:
- SimpleHostile - SimpleHostile
- type: Hands
- type: Sprite - type: Sprite
drawdepth: Mobs drawdepth: Mobs
sprite: Structures/Machines/VendingMachines/cola.rsi sprite: Structures/Machines/VendingMachines/cola.rsi

View File

@@ -40,6 +40,7 @@
factions: factions:
- Xeno - Xeno
- type: Hands - type: Hands
- type: ComplexInteraction
- type: Sprite - type: Sprite
drawdepth: Mobs drawdepth: Mobs
sprite: Mobs/Aliens/Xenos/burrower.rsi sprite: Mobs/Aliens/Xenos/burrower.rsi

View File

@@ -18,6 +18,7 @@
canInteract: true canInteract: true
- type: GhostHearing - type: GhostHearing
- type: Hands - type: Hands
- type: ComplexInteraction
- type: Puller - type: Puller
- type: CombatMode - type: CombatMode
- type: Physics - type: Physics

View File

@@ -225,6 +225,7 @@
- type: Inventory - type: Inventory
templateId: holoclown templateId: holoclown
- type: Hands - type: Hands
- type: ComplexInteraction
- type: Clumsy - type: Clumsy
clumsyDamage: clumsyDamage:
types: types:

View File

@@ -149,6 +149,7 @@
- type: Identity - type: Identity
- type: IdExaminable - type: IdExaminable
- type: Hands - type: Hands
- type: ComplexInteraction
- type: Internals - type: Internals
- type: Inventory - type: Inventory
- type: InventorySlots - type: InventorySlots
@@ -309,6 +310,7 @@
abstract: true abstract: true
components: components:
- type: Hands - type: Hands
- type: ComplexInteraction
- type: Inventory - type: Inventory
- type: InventorySlots - type: InventorySlots
- type: ContainerContainer - type: ContainerContainer