diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index e01dba3311..8804b81dad 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -58,7 +58,6 @@ namespace Content.Client.Entry "FloorTile", "ShuttleController", "HumanInventoryController", - "UseDelay", "Pourable", "Paper", "Write", diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs index 04bf2a1335..b2807b1ea8 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs @@ -248,7 +248,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems if (!args.User.InRangeUnobstructed(canister, SharedInteractionSystem.InteractionRange, popup: true)) return; - if (!hands.TryPutEntityIntoContainer(args.Used, container)) + if (!hands.Drop(args.Used, container)) return; args.Handled = true; diff --git a/Content.Server/Hands/Components/HandsComponent.cs b/Content.Server/Hands/Components/HandsComponent.cs index c333627b31..021ebe3a01 100644 --- a/Content.Server/Hands/Components/HandsComponent.cs +++ b/Content.Server/Hands/Components/HandsComponent.cs @@ -15,7 +15,6 @@ using Content.Shared.Pulling.Components; using Content.Shared.Sound; using Robust.Server.GameObjects; using Robust.Shared.Audio; -using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Localization; @@ -50,44 +49,6 @@ namespace Content.Server.Hands.Components } } - protected override void DoEquippedHandInteraction(IEntity entity, HandState handState) - { - _entitySystemManager.GetEntitySystem().EquippedHandInteraction(Owner, entity, handState); - } - - protected override void DoDroppedInteraction(IEntity heldEntity, bool intentionalDrop) - { - _entitySystemManager.GetEntitySystem().DroppedInteraction(Owner, heldEntity, intentionalDrop); - } - - protected override void DoHandSelectedInteraction(IEntity entity) - { - _entitySystemManager.GetEntitySystem().HandSelectedInteraction(Owner, entity); - } - - protected override void DoHandDeselectedInteraction(IEntity entity) - { - _entitySystemManager.GetEntitySystem().HandDeselectedInteraction(Owner, entity); - } - - protected override async void DoInteraction(IEntity activeHeldEntity, IEntity heldEntity) - { - await _entitySystemManager.GetEntitySystem() - .InteractUsing(Owner, activeHeldEntity, heldEntity, EntityCoordinates.Invalid); - } - - protected override void DoActivate(IEntity heldEntity) - { - _entitySystemManager.GetEntitySystem() - .TryInteractionActivate(Owner, heldEntity); - } - - protected override void DoUse(IEntity heldEntity, bool altInteract = false) - { - _entitySystemManager.GetEntitySystem() - .TryUseInteraction(Owner, heldEntity, altInteract); - } - protected override void HandlePickupAnimation(IEntity entity) { var initialPosition = EntityCoordinates.FromMap(Owner.Transform.Parent?.Owner ?? Owner, entity.Transform.MapPosition); @@ -194,38 +155,6 @@ namespace Content.Server.Hands.Components } } - /// - /// Attempts to use the active held item. - /// - public void ActivateItem() - { - UseActiveHeldEntity(); - } - - /// - /// Tries to drop the contents of a hand directly under the player. - /// - public bool Drop(string handName, bool checkActionBlocker = true, bool intentionalDrop = true) - { - return TryDropHandToFloor(handName, checkActionBlocker, intentionalDrop); - } - - /// - /// Tries to drop an entity in a hand directly under the player. - /// - public bool Drop(IEntity entity, bool checkActionBlocker = true, bool intentionalDrop = true) - { - return TryDropEntityToFloor(entity, checkActionBlocker, intentionalDrop); - } - - /// - /// Tries to unequip contents of a hand directly into a container. - /// - public bool Drop(IEntity entity, BaseContainer targetContainer, bool checkActionBlocker = true) - { - return TryPutEntityIntoContainer(entity, targetContainer, checkActionBlocker); - } - /// /// Tries to get the ItemComponent on the entity held by a hand. /// @@ -292,26 +221,6 @@ namespace Content.Server.Hands.Components } return false; } - - /// - /// Attempts to put an item into the active hand, or any other hand if it cannot. - /// - public bool PutInHand(ItemComponent item, bool checkActionBlocker = true) - { - return TryPutInActiveHandOrAny(item.Owner, checkActionBlocker); - } - - /// - /// Puts an item any hand, prefering the active hand, or puts it on the floor under the player. - /// - public void PutInHandOrDrop(ItemComponent item, bool checkActionBlocker = true) - { - var entity = item.Owner; - - if (!TryPutInActiveHandOrAny(entity, checkActionBlocker)) - entity.Transform.Coordinates = Owner.Transform.Coordinates; - } - #endregion } } diff --git a/Content.Server/Hands/Components/IHandsComponent.cs b/Content.Server/Hands/Components/IHandsComponent.cs index 6bdda53d93..68a9dac83d 100644 --- a/Content.Server/Hands/Components/IHandsComponent.cs +++ b/Content.Server/Hands/Components/IHandsComponent.cs @@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis; using Content.Server.Items; using Content.Shared.ActionBlocker; using Content.Shared.Hands.Components; +using Content.Shared.Item; using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -58,7 +59,7 @@ namespace Content.Server.Hands.Components /// The item to put in a hand. /// Whether to perform an ActionBlocker check to the entity. /// True if the item was inserted, false otherwise. - bool PutInHand(ItemComponent item, bool mobCheck = true); + bool PutInHand(SharedItemComponent item, bool mobCheck = true); /// /// Checks to see if an item can be put in any hand. diff --git a/Content.Server/Hands/Systems/HandsSystem.cs b/Content.Server/Hands/Systems/HandsSystem.cs index 002e1766b2..74f963201c 100644 --- a/Content.Server/Hands/Systems/HandsSystem.cs +++ b/Content.Server/Hands/Systems/HandsSystem.cs @@ -140,7 +140,7 @@ namespace Content.Server.Hands.Systems if (!TryGetHandsComp(args.SenderSession, out var hands)) return; - hands.UseActiveHeldEntity(); + hands.ActivateItem(); } private void HandleInteractUsingInHand(ClientInteractUsingInHandMsg msg, EntitySessionEventArgs args) @@ -212,7 +212,7 @@ namespace Content.Server.Hands.Systems if (!TryGetHandsComp(session, out var hands)) return; - hands.UseActiveHeldEntity(); + hands.ActivateItem(); } private void HandleAltActivateItem(ICommonSession? session) @@ -220,7 +220,7 @@ namespace Content.Server.Hands.Systems if (!TryGetHandsComp(session, out var hands)) return; - hands.UseActiveHeldEntity(altInteract: true); + hands.ActivateItem(altInteract: true); } private bool HandleThrowItem(ICommonSession? session, EntityCoordinates coords, EntityUid uid) @@ -247,7 +247,7 @@ namespace Content.Server.Hands.Systems throwEnt = splitStack; } - else if (!hands.TryDropEntityToFloor(throwEnt)) + else if (!hands.Drop(throwEnt)) return false; var direction = coords.ToMapPos(EntityManager) - playerEnt.Transform.WorldPosition; diff --git a/Content.Server/Interaction/InteractionSystem.cs b/Content.Server/Interaction/InteractionSystem.cs index ecc47406bf..bdd9509f7c 100644 --- a/Content.Server/Interaction/InteractionSystem.cs +++ b/Content.Server/Interaction/InteractionSystem.cs @@ -7,21 +7,16 @@ using Content.Server.CombatMode; using Content.Server.Hands.Components; using Content.Server.Items; using Content.Server.Pulling; -using Content.Server.Timing; using Content.Server.Verbs; using Content.Shared.ActionBlocker; using Content.Shared.DragDrop; -using Content.Shared.Hands; -using Content.Shared.Hands.Components; using Content.Shared.Input; using Content.Shared.Interaction; using Content.Shared.Interaction.Helpers; -using Content.Shared.Inventory; using Content.Shared.Popups; using Content.Shared.Pulling.Components; using Content.Shared.Rotatable; -using Content.Shared.Throwing; -using Content.Shared.Verbs; +using Content.Shared.Timing; using Content.Shared.Weapons.Melee; using JetBrains.Annotations; using Robust.Server.GameObjects; @@ -196,47 +191,6 @@ namespace Content.Server.Interaction InteractionActivate(user, used); return true; } - - /// - /// Activates the IActivate behavior of an object - /// Verifies that the user is capable of doing the use interaction first - /// - public void TryInteractionActivate(IEntity? user, IEntity? used) - { - if (user == null || used == null) - return; - - InteractionActivate(user, used); - } - - private void InteractionActivate(IEntity user, IEntity used) - { - if (used.TryGetComponent(out var delayComponent)) - { - if (delayComponent.ActiveDelay) - return; - - delayComponent.BeginDelay(); - } - - if (!_actionBlockerSystem.CanInteract(user) || ! _actionBlockerSystem.CanUse(user)) - return; - - // all activates should only fire when in range / unobstructed - if (!InRangeUnobstructed(user, used, ignoreInsideBlocker: true, popup: true)) - return; - - var activateMsg = new ActivateInWorldEvent(user, used); - RaiseLocalEvent(used.Uid, activateMsg); - if (activateMsg.Handled) - return; - - if (!used.TryGetComponent(out IActivate? activateComp)) - return; - - var activateEventArgs = new ActivateEventArgs(user, used); - activateComp.Activate(activateEventArgs); - } #endregion private bool HandleWideAttack(ICommonSession? session, EntityCoordinates coords, EntityUid uid) @@ -437,95 +391,6 @@ namespace Content.Server.Interaction } } - /// - /// We didn't click on any entity, try doing an AfterInteract on the click location - /// - private async Task InteractDoAfter(IEntity user, IEntity used, IEntity? target, EntityCoordinates clickLocation, bool canReach) - { - var afterInteractEvent = new AfterInteractEvent(user, used, target, clickLocation, canReach); - RaiseLocalEvent(used.Uid, afterInteractEvent, false); - if (afterInteractEvent.Handled) - return true; - - var afterInteractEventArgs = new AfterInteractEventArgs(user, clickLocation, target, canReach); - var afterInteracts = used.GetAllComponents().OrderByDescending(x => x.Priority).ToList(); - - foreach (var afterInteract in afterInteracts) - { - if (await afterInteract.AfterInteract(afterInteractEventArgs)) - return true; - } - - return false; - } - - private async Task InteractDoBefore( - IEntity user, - IEntity used, - IEntity? target, - EntityCoordinates clickLocation, - bool canReach) - { - var ev = new BeforeInteractEvent(user, used, target, clickLocation, canReach); - RaiseLocalEvent(used.Uid, ev, false); - return ev.Handled; - } - - /// - /// Uses a item/object on an entity - /// Finds components with the InteractUsing interface and calls their function - /// NOTE: Does not have an InRangeUnobstructed check - /// - public async Task InteractUsing(IEntity user, IEntity used, IEntity target, EntityCoordinates clickLocation) - { - if (!_actionBlockerSystem.CanInteract(user)) - return; - - if (await InteractDoBefore(user, used, target, clickLocation, true)) - return; - - // all interactions should only happen when in range / unobstructed, so no range check is needed - var interactUsingEvent = new InteractUsingEvent(user, used, target, clickLocation); - RaiseLocalEvent(target.Uid, interactUsingEvent); - if (interactUsingEvent.Handled) - return; - - var interactUsingEventArgs = new InteractUsingEventArgs(user, clickLocation, used, target); - - var interactUsings = target.GetAllComponents().OrderByDescending(x => x.Priority); - foreach (var interactUsing in interactUsings) - { - // If an InteractUsing returns a status completion we finish our interaction - if (await interactUsing.InteractUsing(interactUsingEventArgs)) - return; - } - - // If we aren't directly interacting with the nearby object, lets see if our item has an after interact we can do - await InteractDoAfter(user, used, target, clickLocation, true); - } - - /// - /// Alternative interactions on an entity. - /// - /// - /// Uses the context menu verb list, and acts out the highest priority alternative interaction verb. - /// - public void AltInteract(IEntity user, IEntity target) - { - // Get list of alt-interact verbs - GetAlternativeVerbsEvent getVerbEvent = new(user, target); - RaiseLocalEvent(target.Uid, getVerbEvent); - - foreach (var verb in getVerbEvent.Verbs) - { - if (verb.Disabled) - continue; - - _verbSystem.TryExecuteVerb(verb); - break; - } - } - /// /// Uses an empty hand on an entity /// Finds components with the InteractHand interface and calls their function @@ -556,252 +421,6 @@ namespace Content.Server.Interaction InteractionActivate(user, target); } - #region Hands - #region Use - /// - /// Activates the IUse behaviors of an entity - /// Verifies that the user is capable of doing the use interaction first - /// - /// - /// - public void TryUseInteraction(IEntity user, IEntity used, bool altInteract = false) - { - if (user != null && used != null && _actionBlockerSystem.CanUse(user)) - { - if (altInteract) - AltInteract(user, used); - else - UseInteraction(user, used); - } - } - - /// - /// Activates the IUse behaviors of an entity without first checking - /// if the user is capable of doing the use interaction. - /// - public void UseInteraction(IEntity user, IEntity used) - { - if (used.TryGetComponent(out var delayComponent)) - { - if (delayComponent.ActiveDelay) - return; - - delayComponent.BeginDelay(); - } - - var useMsg = new UseInHandEvent(user, used); - RaiseLocalEvent(used.Uid, useMsg); - if (useMsg.Handled) - return; - - var uses = used.GetAllComponents().ToList(); - - // Try to use item on any components which have the interface - foreach (var use in uses) - { - // If a Use returns a status completion we finish our interaction - if (use.UseEntity(new UseEntityEventArgs(user))) - return; - } - } - #endregion - - #region Throw - /// - /// Activates the Throw behavior of an object - /// Verifies that the user is capable of doing the throw interaction first - /// - public bool TryThrowInteraction(IEntity user, IEntity item) - { - if (user == null || item == null || !_actionBlockerSystem.CanThrow(user)) return false; - - ThrownInteraction(user, item); - return true; - } - - /// - /// Calls Thrown on all components that implement the IThrown interface - /// on an entity that has been thrown. - /// - public void ThrownInteraction(IEntity user, IEntity thrown) - { - var throwMsg = new ThrownEvent(user, thrown); - RaiseLocalEvent(thrown.Uid, throwMsg); - if (throwMsg.Handled) - return; - - var comps = thrown.GetAllComponents().ToList(); - var args = new ThrownEventArgs(user); - - // Call Thrown on all components that implement the interface - foreach (var comp in comps) - { - comp.Thrown(args); - } - } - #endregion - - #region Equip - /// - /// Calls Equipped on all components that implement the IEquipped interface - /// on an entity that has been equipped. - /// - public void EquippedInteraction(IEntity user, IEntity equipped, EquipmentSlotDefines.Slots slot) - { - var equipMsg = new EquippedEvent(user, equipped, slot); - RaiseLocalEvent(equipped.Uid, equipMsg); - if (equipMsg.Handled) - return; - - var comps = equipped.GetAllComponents().ToList(); - - // Call Thrown on all components that implement the interface - foreach (var comp in comps) - { - comp.Equipped(new EquippedEventArgs(user, slot)); - } - } - - /// - /// Calls Unequipped on all components that implement the IUnequipped interface - /// on an entity that has been equipped. - /// - public void UnequippedInteraction(IEntity user, IEntity equipped, EquipmentSlotDefines.Slots slot) - { - var unequipMsg = new UnequippedEvent(user, equipped, slot); - RaiseLocalEvent(equipped.Uid, unequipMsg); - if (unequipMsg.Handled) - return; - - var comps = equipped.GetAllComponents().ToList(); - - // Call Thrown on all components that implement the interface - foreach (var comp in comps) - { - comp.Unequipped(new UnequippedEventArgs(user, slot)); - } - } - - #region Equip Hand - /// - /// Calls EquippedHand on all components that implement the IEquippedHand interface - /// on an item. - /// - public void EquippedHandInteraction(IEntity user, IEntity item, HandState hand) - { - var equippedHandMessage = new EquippedHandEvent(user, item, hand); - RaiseLocalEvent(item.Uid, equippedHandMessage); - if (equippedHandMessage.Handled) - return; - - var comps = item.GetAllComponents().ToList(); - - foreach (var comp in comps) - { - comp.EquippedHand(new EquippedHandEventArgs(user, hand)); - } - } - - /// - /// Calls UnequippedHand on all components that implement the IUnequippedHand interface - /// on an item. - /// - public void UnequippedHandInteraction(IEntity user, IEntity item, HandState hand) - { - var unequippedHandMessage = new UnequippedHandEvent(user, item, hand); - RaiseLocalEvent(item.Uid, unequippedHandMessage); - if (unequippedHandMessage.Handled) - return; - - var comps = item.GetAllComponents().ToList(); - - foreach (var comp in comps) - { - comp.UnequippedHand(new UnequippedHandEventArgs(user, hand)); - } - } - #endregion - #endregion - - #region Drop - /// - /// Activates the Dropped behavior of an object - /// Verifies that the user is capable of doing the drop interaction first - /// - public bool TryDroppedInteraction(IEntity user, IEntity item, bool intentional) - { - if (user == null || item == null || !_actionBlockerSystem.CanDrop(user)) return false; - - DroppedInteraction(user, item, intentional); - return true; - } - - /// - /// Calls Dropped on all components that implement the IDropped interface - /// on an entity that has been dropped. - /// - public void DroppedInteraction(IEntity user, IEntity item, bool intentional) - { - var dropMsg = new DroppedEvent(user, item, intentional); - RaiseLocalEvent(item.Uid, dropMsg); - if (dropMsg.Handled) - return; - - item.Transform.LocalRotation = intentional ? Angle.Zero : (_random.Next(0, 100) / 100f) * MathHelper.TwoPi; - - var comps = item.GetAllComponents().ToList(); - - // Call Land on all components that implement the interface - foreach (var comp in comps) - { - comp.Dropped(new DroppedEventArgs(user, intentional)); - } - } - #endregion - - #region Hand Selected - /// - /// Calls HandSelected on all components that implement the IHandSelected interface - /// on an item entity on a hand that has just been selected. - /// - public void HandSelectedInteraction(IEntity user, IEntity item) - { - var handSelectedMsg = new HandSelectedEvent(user, item); - RaiseLocalEvent(item.Uid, handSelectedMsg); - if (handSelectedMsg.Handled) - return; - - var comps = item.GetAllComponents().ToList(); - - // Call Land on all components that implement the interface - foreach (var comp in comps) - { - comp.HandSelected(new HandSelectedEventArgs(user)); - } - } - - /// - /// Calls HandDeselected on all components that implement the IHandDeselected interface - /// on an item entity on a hand that has just been deselected. - /// - public void HandDeselectedInteraction(IEntity user, IEntity item) - { - var handDeselectedMsg = new HandDeselectedEvent(user, item); - RaiseLocalEvent(item.Uid, handDeselectedMsg); - if (handDeselectedMsg.Handled) - return; - - var comps = item.GetAllComponents().ToList(); - - // Call Land on all components that implement the interface - foreach (var comp in comps) - { - comp.HandDeselected(new HandDeselectedEventArgs(user)); - } - } - #endregion - #endregion - /// /// Will have two behaviors, either "uses" the used entity at range on the target entity if it is capable of accepting that action /// Or it will use the used entity itself on the position clicked, regardless of what was there diff --git a/Content.Shared/Containers/ItemSlot/SharedItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/SharedItemSlotsSystem.cs index 91f88cefd9..6b6cdb30e3 100644 --- a/Content.Shared/Containers/ItemSlot/SharedItemSlotsSystem.cs +++ b/Content.Shared/Containers/ItemSlot/SharedItemSlotsSystem.cs @@ -144,7 +144,7 @@ namespace Content.Shared.Containers.ItemSlots swap = slot.ContainerSlot.ContainedEntity; // return if user can't drop active item in hand - if (!hands.TryDropEntityToFloor(item)) + if (!hands.Drop(item)) return true; // swap item in hand and item in slot diff --git a/Content.Shared/Hands/Components/SharedHandsComponent.cs b/Content.Shared/Hands/Components/SharedHandsComponent.cs index bdca7b6410..80278e6357 100644 --- a/Content.Shared/Hands/Components/SharedHandsComponent.cs +++ b/Content.Shared/Hands/Components/SharedHandsComponent.cs @@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.ActionBlocker; using Content.Shared.Interaction; +using Content.Shared.Item; using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.GameStates; @@ -331,7 +332,7 @@ namespace Content.Shared.Hands.Components /// /// Attempts to move a held item from a hand into a container that is not another hand, without dropping it on the floor inbetween. /// - public bool TryPutEntityIntoContainer(IEntity entity, BaseContainer targetContainer, bool checkActionBlocker = true) + public bool Drop(IEntity entity, BaseContainer targetContainer, bool checkActionBlocker = true) { if (!TryGetHandHoldingEntity(entity, out var hand)) return false; @@ -346,7 +347,7 @@ namespace Content.Shared.Hands.Components /// /// Tries to drop the contents of a hand directly under the player. /// - public bool TryDropHandToFloor(string handName, bool checkActionBlocker = true, bool intentionalDrop = true) + public bool Drop(string handName, bool checkActionBlocker = true, bool intentionalDrop = true) { if (!TryGetHand(handName, out var hand)) return false; @@ -357,7 +358,7 @@ namespace Content.Shared.Hands.Components /// /// Tries to drop a held entity directly under the player. /// - public bool TryDropEntityToFloor(IEntity entity, bool checkActionBlocker = true, bool intentionalDrop = true) + public bool Drop(IEntity entity, bool checkActionBlocker = true, bool intentionalDrop = true) { if (!TryGetHandHoldingEntity(entity, out var hand)) return false; @@ -452,7 +453,7 @@ namespace Content.Shared.Hands.Components RemoveHeldEntityFromHand(hand); - DoDroppedInteraction(heldEntity, intentionalDrop); + EntitySystem.Get().DroppedInteraction(Owner, heldEntity, intentionalDrop); heldEntity.Transform.WorldPosition = GetFinalDropCoordinates(targetDropLocation); @@ -622,7 +623,7 @@ namespace Content.Shared.Hands.Components return; } - DoEquippedHandInteraction(entity, hand.ToHandState()); + EntitySystem.Get().EquippedHandInteraction(Owner, entity, hand.ToHandState()); if (hand.Name == ActiveHand) SelectActiveHeldEntity(); @@ -673,7 +674,7 @@ namespace Content.Shared.Hands.Components /// /// Attempts to interact with the item in a hand using the active held item. /// - public void InteractHandWithActiveHand(string handName) + public async void InteractHandWithActiveHand(string handName) { if (!TryGetActiveHeldEntity(out var activeHeldEntity)) return; @@ -684,15 +685,17 @@ namespace Content.Shared.Hands.Components if (activeHeldEntity == heldEntity) return; - DoInteraction(activeHeldEntity, heldEntity); + await EntitySystem.Get() + .InteractUsing(Owner, activeHeldEntity, heldEntity, EntityCoordinates.Invalid); } - public void UseActiveHeldEntity(bool altInteract = false) + public void ActivateItem(bool altInteract = false) { if (!TryGetActiveHeldEntity(out var heldEntity)) return; - DoUse(heldEntity, altInteract); + EntitySystem.Get() + .TryUseInteraction(Owner, heldEntity, altInteract); } public void ActivateHeldEntity(string handName) @@ -700,7 +703,8 @@ namespace Content.Shared.Hands.Components if (!TryGetHeldEntity(handName, out var heldEntity)) return; - DoActivate(heldEntity); + EntitySystem.Get() + .TryInteractionActivate(Owner, heldEntity); } /// @@ -730,13 +734,13 @@ namespace Content.Shared.Hands.Components private void DeselectActiveHeldEntity() { if (TryGetActiveHeldEntity(out var entity)) - DoHandDeselectedInteraction(entity); + EntitySystem.Get().HandDeselectedInteraction(Owner, entity); } private void SelectActiveHeldEntity() { if (TryGetActiveHeldEntity(out var entity)) - DoHandSelectedInteraction(entity); + EntitySystem.Get().HandSelectedInteraction(Owner, entity); } private void HandCountChanged() @@ -744,6 +748,25 @@ namespace Content.Shared.Hands.Components Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new HandCountChangedEvent(Owner)); } + /// + /// Tries to pick up an entity into the active hand. If it cannot, tries to pick up the entity into each other hand. + /// + public bool PutInHand(SharedItemComponent item, bool checkActionBlocker = true) + { + return TryPutInActiveHandOrAny(item.Owner, checkActionBlocker); + } + + /// + /// Puts an item any hand, prefering the active hand, or puts it on the floor under the player. + /// + public void PutInHandOrDrop(SharedItemComponent item, bool checkActionBlocker = true) + { + var entity = item.Owner; + + if (!TryPutInActiveHandOrAny(entity, checkActionBlocker)) + entity.Transform.Coordinates = Owner.Transform.Coordinates; + } + /// /// Tries to pick up an entity into the active hand. If it cannot, tries to pick up the entity into each other hand. /// @@ -786,20 +809,6 @@ namespace Content.Shared.Hands.Components protected virtual void OnHeldEntityRemovedFromHand(IEntity heldEntity, HandState handState) { } - protected virtual void DoDroppedInteraction(IEntity heldEntity, bool intentionalDrop) { } - - protected virtual void DoEquippedHandInteraction(IEntity entity, HandState handState) { } - - protected virtual void DoHandSelectedInteraction(IEntity entity) { } - - protected virtual void DoHandDeselectedInteraction(IEntity entity) { } - - protected virtual void DoInteraction(IEntity activeHeldEntity, IEntity heldEntity) { } - - protected virtual void DoUse(IEntity heldEntity, bool altInteract = false) { } - - protected virtual void DoActivate(IEntity heldEntity) { } - protected virtual void HandlePickupAnimation(IEntity entity) { } } diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index ea97f78c0f..a800653f45 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -1,13 +1,23 @@ using System; using System.Linq; +using System.Threading.Tasks; +using Content.Shared.ActionBlocker; +using Content.Shared.Hands; +using Content.Shared.Hands.Components; +using Content.Shared.Inventory; using Content.Shared.Physics; using Content.Shared.Popups; +using Content.Shared.Throwing; +using Content.Shared.Timing; +using Content.Shared.Verbs; using JetBrains.Annotations; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Map; +using Robust.Shared.Maths; using Robust.Shared.Physics; +using Robust.Shared.Random; using Robust.Shared.Serialization; namespace Content.Shared.Interaction @@ -19,6 +29,9 @@ namespace Content.Shared.Interaction public abstract class SharedInteractionSystem : EntitySystem { [Dependency] private readonly SharedPhysicsSystem _sharedBroadphaseSystem = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; + [Dependency] private readonly SharedVerbSystem _verbSystem = default!; public const float InteractionRange = 2; public const float InteractionRangeSquared = InteractionRange * InteractionRange; @@ -338,6 +351,384 @@ namespace Content.Shared.Interaction return inRange; } + + public async Task InteractDoBefore( + IEntity user, + IEntity used, + IEntity? target, + EntityCoordinates clickLocation, + bool canReach) + { + var ev = new BeforeInteractEvent(user, used, target, clickLocation, canReach); + RaiseLocalEvent(used.Uid, ev, false); + return ev.Handled; + } + + /// + /// Uses a item/object on an entity + /// Finds components with the InteractUsing interface and calls their function + /// NOTE: Does not have an InRangeUnobstructed check + /// + public async Task InteractUsing(IEntity user, IEntity used, IEntity target, EntityCoordinates clickLocation) + { + if (!_actionBlockerSystem.CanInteract(user)) + return; + + if (await InteractDoBefore(user, used, target, clickLocation, true)) + return; + + // all interactions should only happen when in range / unobstructed, so no range check is needed + var interactUsingEvent = new InteractUsingEvent(user, used, target, clickLocation); + RaiseLocalEvent(target.Uid, interactUsingEvent); + if (interactUsingEvent.Handled) + return; + + var interactUsingEventArgs = new InteractUsingEventArgs(user, clickLocation, used, target); + + var interactUsings = target.GetAllComponents().OrderByDescending(x => x.Priority); + foreach (var interactUsing in interactUsings) + { + // If an InteractUsing returns a status completion we finish our interaction + if (await interactUsing.InteractUsing(interactUsingEventArgs)) + return; + } + + // If we aren't directly interacting with the nearby object, lets see if our item has an after interact we can do + await InteractDoAfter(user, used, target, clickLocation, true); + } + + /// + /// We didn't click on any entity, try doing an AfterInteract on the click location + /// + public async Task InteractDoAfter(IEntity user, IEntity used, IEntity? target, EntityCoordinates clickLocation, bool canReach) + { + var afterInteractEvent = new AfterInteractEvent(user, used, target, clickLocation, canReach); + RaiseLocalEvent(used.Uid, afterInteractEvent, false); + if (afterInteractEvent.Handled) + return true; + + var afterInteractEventArgs = new AfterInteractEventArgs(user, clickLocation, target, canReach); + var afterInteracts = used.GetAllComponents().OrderByDescending(x => x.Priority).ToList(); + + foreach (var afterInteract in afterInteracts) + { + if (await afterInteract.AfterInteract(afterInteractEventArgs)) + return true; + } + + return false; + } + + #region ActivateItemInWorld + /// + /// Activates the IActivate behavior of an object + /// Verifies that the user is capable of doing the use interaction first + /// + public void TryInteractionActivate(IEntity? user, IEntity? used) + { + if (user == null || used == null) + return; + + InteractionActivate(user, used); + } + + protected void InteractionActivate(IEntity user, IEntity used) + { + if (used.TryGetComponent(out var delayComponent)) + { + if (delayComponent.ActiveDelay) + return; + + delayComponent.BeginDelay(); + } + + if (!_actionBlockerSystem.CanInteract(user) || !_actionBlockerSystem.CanUse(user)) + return; + + // all activates should only fire when in range / unobstructed + if (!InRangeUnobstructed(user, used, ignoreInsideBlocker: true, popup: true)) + return; + + var activateMsg = new ActivateInWorldEvent(user, used); + RaiseLocalEvent(used.Uid, activateMsg); + if (activateMsg.Handled) + return; + + if (!used.TryGetComponent(out IActivate? activateComp)) + return; + + var activateEventArgs = new ActivateEventArgs(user, used); + activateComp.Activate(activateEventArgs); + } + #endregion + + #region Hands + #region Use + /// + /// Activates the IUse behaviors of an entity + /// Verifies that the user is capable of doing the use interaction first + /// + /// + /// + public void TryUseInteraction(IEntity user, IEntity used, bool altInteract = false) + { + if (user != null && used != null && _actionBlockerSystem.CanUse(user)) + { + if (altInteract) + AltInteract(user, used); + else + UseInteraction(user, used); + } + } + + /// + /// Activates the IUse behaviors of an entity without first checking + /// if the user is capable of doing the use interaction. + /// + public void UseInteraction(IEntity user, IEntity used) + { + if (used.TryGetComponent(out var delayComponent)) + { + if (delayComponent.ActiveDelay) + return; + + delayComponent.BeginDelay(); + } + + var useMsg = new UseInHandEvent(user, used); + RaiseLocalEvent(used.Uid, useMsg); + if (useMsg.Handled) + return; + + var uses = used.GetAllComponents().ToList(); + + // Try to use item on any components which have the interface + foreach (var use in uses) + { + // If a Use returns a status completion we finish our interaction + if (use.UseEntity(new UseEntityEventArgs(user))) + return; + } + } + + /// + /// Alternative interactions on an entity. + /// + /// + /// Uses the context menu verb list, and acts out the highest priority alternative interaction verb. + /// + public void AltInteract(IEntity user, IEntity target) + { + // Get list of alt-interact verbs + GetAlternativeVerbsEvent getVerbEvent = new(user, target); + RaiseLocalEvent(target.Uid, getVerbEvent); + + foreach (var verb in getVerbEvent.Verbs) + { + if (verb.Disabled) + continue; + + _verbSystem.TryExecuteVerb(verb); + break; + } + } + #endregion + + #region Throw + /// + /// Activates the Throw behavior of an object + /// Verifies that the user is capable of doing the throw interaction first + /// + public bool TryThrowInteraction(IEntity user, IEntity item) + { + if (user == null || item == null || !_actionBlockerSystem.CanThrow(user)) return false; + + ThrownInteraction(user, item); + return true; + } + + /// + /// Calls Thrown on all components that implement the IThrown interface + /// on an entity that has been thrown. + /// + public void ThrownInteraction(IEntity user, IEntity thrown) + { + var throwMsg = new ThrownEvent(user, thrown); + RaiseLocalEvent(thrown.Uid, throwMsg); + if (throwMsg.Handled) + return; + + var comps = thrown.GetAllComponents().ToList(); + var args = new ThrownEventArgs(user); + + // Call Thrown on all components that implement the interface + foreach (var comp in comps) + { + comp.Thrown(args); + } + } + #endregion + + #region Equip + /// + /// Calls Equipped on all components that implement the IEquipped interface + /// on an entity that has been equipped. + /// + public void EquippedInteraction(IEntity user, IEntity equipped, EquipmentSlotDefines.Slots slot) + { + var equipMsg = new EquippedEvent(user, equipped, slot); + RaiseLocalEvent(equipped.Uid, equipMsg); + if (equipMsg.Handled) + return; + + var comps = equipped.GetAllComponents().ToList(); + + // Call Thrown on all components that implement the interface + foreach (var comp in comps) + { + comp.Equipped(new EquippedEventArgs(user, slot)); + } + } + + /// + /// Calls Unequipped on all components that implement the IUnequipped interface + /// on an entity that has been equipped. + /// + public void UnequippedInteraction(IEntity user, IEntity equipped, EquipmentSlotDefines.Slots slot) + { + var unequipMsg = new UnequippedEvent(user, equipped, slot); + RaiseLocalEvent(equipped.Uid, unequipMsg); + if (unequipMsg.Handled) + return; + + var comps = equipped.GetAllComponents().ToList(); + + // Call Thrown on all components that implement the interface + foreach (var comp in comps) + { + comp.Unequipped(new UnequippedEventArgs(user, slot)); + } + } + + #region Equip Hand + /// + /// Calls EquippedHand on all components that implement the IEquippedHand interface + /// on an item. + /// + public void EquippedHandInteraction(IEntity user, IEntity item, HandState hand) + { + var equippedHandMessage = new EquippedHandEvent(user, item, hand); + RaiseLocalEvent(item.Uid, equippedHandMessage); + if (equippedHandMessage.Handled) + return; + + var comps = item.GetAllComponents().ToList(); + + foreach (var comp in comps) + { + comp.EquippedHand(new EquippedHandEventArgs(user, hand)); + } + } + + /// + /// Calls UnequippedHand on all components that implement the IUnequippedHand interface + /// on an item. + /// + public void UnequippedHandInteraction(IEntity user, IEntity item, HandState hand) + { + var unequippedHandMessage = new UnequippedHandEvent(user, item, hand); + RaiseLocalEvent(item.Uid, unequippedHandMessage); + if (unequippedHandMessage.Handled) + return; + + var comps = item.GetAllComponents().ToList(); + + foreach (var comp in comps) + { + comp.UnequippedHand(new UnequippedHandEventArgs(user, hand)); + } + } + #endregion + #endregion + + #region Drop + /// + /// Activates the Dropped behavior of an object + /// Verifies that the user is capable of doing the drop interaction first + /// + public bool TryDroppedInteraction(IEntity user, IEntity item, bool intentional) + { + if (user == null || item == null || !_actionBlockerSystem.CanDrop(user)) return false; + + DroppedInteraction(user, item, intentional); + return true; + } + + /// + /// Calls Dropped on all components that implement the IDropped interface + /// on an entity that has been dropped. + /// + public void DroppedInteraction(IEntity user, IEntity item, bool intentional) + { + var dropMsg = new DroppedEvent(user, item, intentional); + RaiseLocalEvent(item.Uid, dropMsg); + if (dropMsg.Handled) + return; + + item.Transform.LocalRotation = intentional ? Angle.Zero : (_random.Next(0, 100) / 100f) * MathHelper.TwoPi; + + var comps = item.GetAllComponents().ToList(); + + // Call Land on all components that implement the interface + foreach (var comp in comps) + { + comp.Dropped(new DroppedEventArgs(user, intentional)); + } + } + #endregion + + #region Hand Selected + /// + /// Calls HandSelected on all components that implement the IHandSelected interface + /// on an item entity on a hand that has just been selected. + /// + public void HandSelectedInteraction(IEntity user, IEntity item) + { + var handSelectedMsg = new HandSelectedEvent(user, item); + RaiseLocalEvent(item.Uid, handSelectedMsg); + if (handSelectedMsg.Handled) + return; + + var comps = item.GetAllComponents().ToList(); + + // Call Land on all components that implement the interface + foreach (var comp in comps) + { + comp.HandSelected(new HandSelectedEventArgs(user)); + } + } + + /// + /// Calls HandDeselected on all components that implement the IHandDeselected interface + /// on an item entity on a hand that has just been deselected. + /// + public void HandDeselectedInteraction(IEntity user, IEntity item) + { + var handDeselectedMsg = new HandDeselectedEvent(user, item); + RaiseLocalEvent(item.Uid, handDeselectedMsg); + if (handDeselectedMsg.Handled) + return; + + var comps = item.GetAllComponents().ToList(); + + // Call Land on all components that implement the interface + foreach (var comp in comps) + { + comp.HandDeselected(new HandDeselectedEventArgs(user)); + } + } + #endregion + #endregion } /// diff --git a/Content.Server/Timing/UseDelayComponent.cs b/Content.Shared/Timing/UseDelayComponent.cs similarity index 97% rename from Content.Server/Timing/UseDelayComponent.cs rename to Content.Shared/Timing/UseDelayComponent.cs index e43575948e..79793296a2 100644 --- a/Content.Server/Timing/UseDelayComponent.cs +++ b/Content.Shared/Timing/UseDelayComponent.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using Content.Shared.Cooldown; using Robust.Shared.GameObjects; @@ -7,7 +7,7 @@ using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Timing; using Robust.Shared.ViewVariables; -namespace Content.Server.Timing +namespace Content.Shared.Timing { /// /// Timer that creates a cooldown each time an object is activated/used