Files
OldThink/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs

859 lines
31 KiB
C#
Raw Normal View History

2020-06-21 18:31:56 -07:00
using System;
using System.Linq;
using System.Threading.Tasks;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Pulling;
using Content.Server.GameObjects.Components.Timing;
Add changing the amount of hands on the GUI depending on your body parts (#1406) * Multiple hands in gui first pass * Remove IHandsComponent interface * Create hand class and more hand textures * Refactor ServerHandsComponent to use a single list of hands * Seal SharedHand * Fix picked up items not showing on top of the hand buttons * Remove HandsGui buttons and panels dictionaries * Fix items in hands rendering * Fix wrong hand container comparison * Fix not updating the location of duplicate hands * Change ClientHandsComponent to use a SortedList instead of a dictionary * More merge conflict fixes * Change SortedList to List * Fix hand button order * Add item tooltip for more than 2 hands and updating when removing hands * Add add hand and remove hand command * Merge conflict fixes * Remove nullable reference type from ContainerSlot * Fix texture errors * Fix error when reaching 0 hands * Fix error when swapping hands with no hands * Merged remove hand methods * Fix item panel texture errors * Merge conflict fixes * Fix addhand and removehand command descriptions * Add properly displaying tooltips for 2 hands * Make hand indexes and locations consistent across the client and server * Add dropping held entity if a hand is removed * Change hand location to be calculated by index * Made different hand gui updates more consistent * Remove human body yml testing changes * Sanitize addhand and removehand commands * Merge conflict fixes * Remove testing changes * Revert body system changes * Add missing imports * Remove obsolete hands parameter in yml files * Fix broken import * Fix startup error and adding and removing hands on the same tick * Make hand container id use an uint In case someone gets more than 2 billion hands * Rename hand component files * Make hands state use an array
2020-07-25 15:11:16 +02:00
using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.GameObjects.EntitySystemMessages;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Input;
using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.Utility;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Players;
namespace Content.Server.GameObjects.EntitySystems.Click
{
/// <summary>
/// Governs interactions during clicking on entities
/// </summary>
[UsedImplicitly]
public sealed class InteractionSystem : SharedInteractionSystem
{
[Dependency] private readonly IEntityManager _entityManager = default!;
public override void Initialize()
{
SubscribeNetworkEvent<DragDropMessage>(HandleDragDropMessage);
CommandBinds.Builder
.Bind(EngineKeyFunctions.Use,
new PointerInputCmdHandler(HandleClientUseItemInHand))
.Bind(ContentKeyFunctions.WideAttack,
new PointerInputCmdHandler(HandleWideAttack))
.Bind(ContentKeyFunctions.ActivateItemInWorld,
new PointerInputCmdHandler(HandleActivateItemInWorld))
Add pulling (#1409) * Initial framework for pulling. * Make it possible to pull items via (temporary) keybind Ctrl+Click, make items follow the player correctly. * Make other objects pullable, implement functionality for moving an object being pulled, make only one object able to be pulled at a time. * Make sure that MoveTo won't allow collisions with the player * Update everything to work with the new physics engine * Update Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs Co-authored-by: ComicIronic <comicironic@gmail.com> * Physics update and convert to direct type casts * Add notnull checks * Add pull keybinds to the tutorial window * Move PullController to shared * Fix pulled items getting left behind * Fix moving pulled objects into walls * Remove flooring of coordinates when moving pulled objects * Add missing null check in PutInHand * Change pulling keybind to control and throwing to alt * Change PhysicsComponent references to IPhysicsComponent * Add trying to pull a pulled entity disabling the pull * Add pulled status effect * Fix merge conflicts * Merge fixes * Make players pullable * Fix being able to pull yourself * Change pull moving to use a velocity * Update pulled and pulling icons to not be buckle A tragedy * Make pulled and pulling icons more consistent * Remove empty not pulled and not pulling images * Pulled icon update * Pulled icon update * Add clicking pulling status effect to stop the pull * Fix spacewalking when pulling * Merge conflict fixes * Add a pull verb * Fix nullable error * Add pulling through the entity drop down menu Co-authored-by: Jackson Lewis <inquisitivepenguin@protonmail.com> Co-authored-by: ComicIronic <comicironic@gmail.com>
2020-07-27 00:54:32 +02:00
.Bind(ContentKeyFunctions.TryPullObject, new PointerInputCmdHandler(HandleTryPullObject))
.Register<InteractionSystem>();
}
public override void Shutdown()
{
CommandBinds.Unregister<InteractionSystem>();
base.Shutdown();
}
private void HandleDragDropMessage(DragDropMessage msg, EntitySessionEventArgs args)
{
var performer = args.SenderSession.AttachedEntity;
if (!EntityManager.TryGetEntity(msg.Dropped, out var dropped)) return;
if (!EntityManager.TryGetEntity(msg.Target, out var target)) return;
var interactionArgs = new DragDropEventArgs(performer, msg.DropLocation, dropped, target);
// must be in range of both the target and the object they are drag / dropping
if (!interactionArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return;
// trigger dragdrops on the dropped entity
foreach (var dragDrop in dropped.GetAllComponents<IDraggable>())
{
if (dragDrop.CanDrop(interactionArgs) &&
dragDrop.Drop(interactionArgs))
Add the trash man (#1367) * Add disposal.rsi * Rename disposal resource to disposal.rsi and create basic components * Add disposal nets * Add pushing entities along the disposal network * Add disposal unit * Unregister disposable component * Add flush and selfinsert verbs to disposal unit * Add gradual disposals movement * Fix being able to walk through space for a while after exiting disposals * Multiply disposals speed by 10 And fix early returns when moving an entity * Rename Disposable component to InDisposals * Remove DisposalNet and add on anchor events * Remove anchored events, moved to interfaces * Code cleanup * Fix adjacent tubes' connections when a tube connects * Fix jittery movement in disposals * Remove Logger.Debug call * Move disposals updates to InDisposalsComponent * Fix adjacent connection valid directions check * Disposal tubes now throw you out where they are facing * Add disposal unit exit cooldown * Set different disposal pipe sprite state depending on anchored value * Add recycler * Add recycler animation * Add bloody texture to the recycler when grinding a living being * Add PowerDevice component to the disposal unit * Made the Recycler center on the grid * Add disposal junction * Add picking a random direction if junction is entered from the output side * Add disposal flush and clang sounds Taken from VGStation * Move disposal flush and clang sound file names to exposedata * Add disposalsmap.yml to test with * Add summaries to DisposalUnit fields * Add sideDegrees yaml property to disposal junctions * Fix outdated usings * Add conveyor resources * Update RobustToolbox * More merge fixes Add conveyor collision masks * Add ConveyorComponent * Fix crash when reentering a body * Merge branch 'master' into disposals-1147 * Reduce recycler bounds, set hard to false, add summary and expose "safe" to yaml * Move IAnchored and IUnAnchored to AnchorableComponent * Update power components and remove old disposals map * Remove redundant sprite layers * Add tile pry command * Fix tilepry command * Fix DisposalJunctionComponent missing a component reference * Add anchor by radius command * Add Y-Junctions * Add disposal bend * Add unanchor command * Change DisposalJunction prototypes to specify their angles * Fix disposal units being hidden below the floor * Removed IAnhored and IUnAnchored interfaces * Replace CanBeNull annotation with nullable reference types * Update showwires command * Add recycler recycling items * Added angle and speed properties to ConveyorComponent * Fix conveyort textures * Add animation to the disposal unit * Fix anchor and unanchor commands sometimes not finding any entities * Fix not reading flush_time from disposal unit prototype * Fix merge conflict wrong using * Fix disposal, recycling and conveyor texture paths Delete diverters * Update visualizer names * Add DisposableComponent, change drag and drop to work with multiple components Ignoreinsideblocker client side for drag and drops, like on the server Add more comments * Add conveyor belts properly moving entities on top * Anchorr wires * Change conveyor bounds to 0.49 * Anchor catwalks, airlocks, gravity generators, low walls, wires and windows * Add starting/stopping conveyors * Add reversed conveyors * Add conveyor switches * Move InDisposalsComponent code to DisposableComponent * Add ExitVector method to tubes * Fix not updating tube references when disconnecting one * Replace IoCManager call with dependency * Add tubes disconnecting if they move too far apart from one another * Move disposals action blocking to shared * Add rotating and flipping pipes * Make conveyor intersection calculations approximate * Fix 1% chance of the server crashing when initializing the map Happens when emergency lockers remove themselves * Add disposal unit interface * Make disposal units refuse items if not powered * Make disposal tubes hide only when anchored * Make disposal junction arrows visible to mere mortals * Add disposal tubes breaking * Add tubeconnections command * Add missing verb attribute * Add flipped disposal junction * Add ids and linking to conveyors and switches * Add conveyor switch prying and placing * Add anchoring conveyor switches and refactor placing them * Add missing serializable attributes from DisposableComponentState * Make conveyor speed VV ReadWrite * Change drawdepth of conveyors to FloorObjects * Make conveyor anchored check consistent * Remove anchoring interaction from switches * Add conveyor switch id syncing and move switches slightly when pried * Make entities in containers not able to be moved by conveyors * Add conveyor and switches loose textures * Merge conflict fixes * Add disposal unit test * Add flushing test to disposal unit test * Add disposal unit flush fail test * Add disposals to the saltern map * Fix saltern disposal junctions * Add power checks to the recycler * Fix disposal unit placement in maintenance closet * Remove disposal junctions from saltern * Readd junctions to saltern * Add the chemmaster to saltern at the request of Ike * Move the chemistry disposal unit * Fix casing of disposal flush sound * More merge conflict fixes * Fix a compiler warning. * Remove popup invocation from buckle * Remove showPopup parameter from InteractionChecks * Remove unnecessary physics components Fixes the physics system dying * Replace PhysicsComponent usages with CollidableComponent * Update existing code for the new controller system * Change conveyors to use a VirtualController instead of teleporting the entity * Remove visualizer 2d suffix and update physics code * Transition code to new controller system * Fix shuttles not moving * Fix throwing * Fix guns * Change hands to use physics.Stop() and remove item fumble method * Add syncing conveyor switches states * Fix the recycler wanting to be a conveyor too hard * Fix showwires > showsubfloor rename in mapping command * Fix wifi air conveyors * Fix test error * Add showsubfloorforever command Changes drawdepth of the relevant entities * Disable opening the disposal unit interface while inside * Add closing the disposal unit interface when getting inside * Add closing the interface when the disposal unit component is removed * Add removing entities on disposal unit component removal * Delay disposal unit flush and fix serialization * Implement pressure in disposal units * Fix chain engaging a disposal unit * Implement states to the disposal unit * Fix missing imports from merge conflict * Update Content.Server/GameObjects/Components/Conveyor/ConveyorComponent.cs Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com> * Address some reviews * Fix za buildo * Use container helper to detach disposables * Make conveyors use the construction system * Make conveyor groups and syncing sane * Make flip flip brave * Add activate interface to conveyor switches * Fix not removing the switch from its group when it's deleted * Fix not registering conveyors and switches on initialize * Stop using 0 as null * Disconnect conveyors and switches when disposing of a group * Make disposal units not able to be exited when flushing * Make disposal units flush after a configurable 30 seconds * Add handle and light layers to the disposal unit * Merge engaging and flushing * Update saltern.yml * I love using 0 as null * Make disposal unit visual layers make sense * Remove duplicate remove method in disposal units and update light * Replace DisposableComponent with disposal holders * Fix disposal holders deleting their contents on deletion * Account for disposal unit pressure in tests and make a failed flush autoengage * Rename disposable to holder * Fix junction connections * Disable self insert and flush verbs when inside a disposal unit * Fix spamming the engage button making the animation reset * Make the recycler take materials into account properly Fix cablestack1 not existing * Merge conflict fixes * Fix pipes not being saved anchored * Change conveyors and groups to not use an id Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com> Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com>
2020-07-30 23:45:28 +02:00
{
return;
}
}
// trigger dragdropons on the targeted entity
foreach (var dragDropOn in target.GetAllComponents<IDragDropOn>())
{
Add the trash man (#1367) * Add disposal.rsi * Rename disposal resource to disposal.rsi and create basic components * Add disposal nets * Add pushing entities along the disposal network * Add disposal unit * Unregister disposable component * Add flush and selfinsert verbs to disposal unit * Add gradual disposals movement * Fix being able to walk through space for a while after exiting disposals * Multiply disposals speed by 10 And fix early returns when moving an entity * Rename Disposable component to InDisposals * Remove DisposalNet and add on anchor events * Remove anchored events, moved to interfaces * Code cleanup * Fix adjacent tubes' connections when a tube connects * Fix jittery movement in disposals * Remove Logger.Debug call * Move disposals updates to InDisposalsComponent * Fix adjacent connection valid directions check * Disposal tubes now throw you out where they are facing * Add disposal unit exit cooldown * Set different disposal pipe sprite state depending on anchored value * Add recycler * Add recycler animation * Add bloody texture to the recycler when grinding a living being * Add PowerDevice component to the disposal unit * Made the Recycler center on the grid * Add disposal junction * Add picking a random direction if junction is entered from the output side * Add disposal flush and clang sounds Taken from VGStation * Move disposal flush and clang sound file names to exposedata * Add disposalsmap.yml to test with * Add summaries to DisposalUnit fields * Add sideDegrees yaml property to disposal junctions * Fix outdated usings * Add conveyor resources * Update RobustToolbox * More merge fixes Add conveyor collision masks * Add ConveyorComponent * Fix crash when reentering a body * Merge branch 'master' into disposals-1147 * Reduce recycler bounds, set hard to false, add summary and expose "safe" to yaml * Move IAnchored and IUnAnchored to AnchorableComponent * Update power components and remove old disposals map * Remove redundant sprite layers * Add tile pry command * Fix tilepry command * Fix DisposalJunctionComponent missing a component reference * Add anchor by radius command * Add Y-Junctions * Add disposal bend * Add unanchor command * Change DisposalJunction prototypes to specify their angles * Fix disposal units being hidden below the floor * Removed IAnhored and IUnAnchored interfaces * Replace CanBeNull annotation with nullable reference types * Update showwires command * Add recycler recycling items * Added angle and speed properties to ConveyorComponent * Fix conveyort textures * Add animation to the disposal unit * Fix anchor and unanchor commands sometimes not finding any entities * Fix not reading flush_time from disposal unit prototype * Fix merge conflict wrong using * Fix disposal, recycling and conveyor texture paths Delete diverters * Update visualizer names * Add DisposableComponent, change drag and drop to work with multiple components Ignoreinsideblocker client side for drag and drops, like on the server Add more comments * Add conveyor belts properly moving entities on top * Anchorr wires * Change conveyor bounds to 0.49 * Anchor catwalks, airlocks, gravity generators, low walls, wires and windows * Add starting/stopping conveyors * Add reversed conveyors * Add conveyor switches * Move InDisposalsComponent code to DisposableComponent * Add ExitVector method to tubes * Fix not updating tube references when disconnecting one * Replace IoCManager call with dependency * Add tubes disconnecting if they move too far apart from one another * Move disposals action blocking to shared * Add rotating and flipping pipes * Make conveyor intersection calculations approximate * Fix 1% chance of the server crashing when initializing the map Happens when emergency lockers remove themselves * Add disposal unit interface * Make disposal units refuse items if not powered * Make disposal tubes hide only when anchored * Make disposal junction arrows visible to mere mortals * Add disposal tubes breaking * Add tubeconnections command * Add missing verb attribute * Add flipped disposal junction * Add ids and linking to conveyors and switches * Add conveyor switch prying and placing * Add anchoring conveyor switches and refactor placing them * Add missing serializable attributes from DisposableComponentState * Make conveyor speed VV ReadWrite * Change drawdepth of conveyors to FloorObjects * Make conveyor anchored check consistent * Remove anchoring interaction from switches * Add conveyor switch id syncing and move switches slightly when pried * Make entities in containers not able to be moved by conveyors * Add conveyor and switches loose textures * Merge conflict fixes * Add disposal unit test * Add flushing test to disposal unit test * Add disposal unit flush fail test * Add disposals to the saltern map * Fix saltern disposal junctions * Add power checks to the recycler * Fix disposal unit placement in maintenance closet * Remove disposal junctions from saltern * Readd junctions to saltern * Add the chemmaster to saltern at the request of Ike * Move the chemistry disposal unit * Fix casing of disposal flush sound * More merge conflict fixes * Fix a compiler warning. * Remove popup invocation from buckle * Remove showPopup parameter from InteractionChecks * Remove unnecessary physics components Fixes the physics system dying * Replace PhysicsComponent usages with CollidableComponent * Update existing code for the new controller system * Change conveyors to use a VirtualController instead of teleporting the entity * Remove visualizer 2d suffix and update physics code * Transition code to new controller system * Fix shuttles not moving * Fix throwing * Fix guns * Change hands to use physics.Stop() and remove item fumble method * Add syncing conveyor switches states * Fix the recycler wanting to be a conveyor too hard * Fix showwires > showsubfloor rename in mapping command * Fix wifi air conveyors * Fix test error * Add showsubfloorforever command Changes drawdepth of the relevant entities * Disable opening the disposal unit interface while inside * Add closing the disposal unit interface when getting inside * Add closing the interface when the disposal unit component is removed * Add removing entities on disposal unit component removal * Delay disposal unit flush and fix serialization * Implement pressure in disposal units * Fix chain engaging a disposal unit * Implement states to the disposal unit * Fix missing imports from merge conflict * Update Content.Server/GameObjects/Components/Conveyor/ConveyorComponent.cs Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com> * Address some reviews * Fix za buildo * Use container helper to detach disposables * Make conveyors use the construction system * Make conveyor groups and syncing sane * Make flip flip brave * Add activate interface to conveyor switches * Fix not removing the switch from its group when it's deleted * Fix not registering conveyors and switches on initialize * Stop using 0 as null * Disconnect conveyors and switches when disposing of a group * Make disposal units not able to be exited when flushing * Make disposal units flush after a configurable 30 seconds * Add handle and light layers to the disposal unit * Merge engaging and flushing * Update saltern.yml * I love using 0 as null * Make disposal unit visual layers make sense * Remove duplicate remove method in disposal units and update light * Replace DisposableComponent with disposal holders * Fix disposal holders deleting their contents on deletion * Account for disposal unit pressure in tests and make a failed flush autoengage * Rename disposable to holder * Fix junction connections * Disable self insert and flush verbs when inside a disposal unit * Fix spamming the engage button making the animation reset * Make the recycler take materials into account properly Fix cablestack1 not existing * Merge conflict fixes * Fix pipes not being saved anchored * Change conveyors and groups to not use an id Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com> Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com>
2020-07-30 23:45:28 +02:00
if (dragDropOn.CanDragDropOn(interactionArgs) &&
dragDropOn.DragDropOn(interactionArgs))
{
return;
}
}
}
private bool HandleActivateItemInWorld(ICommonSession session, EntityCoordinates coords, EntityUid uid)
{
if (!EntityManager.TryGetEntity(uid, out var used))
return false;
var playerEnt = ((IPlayerSession) session).AttachedEntity;
if (playerEnt == null || !playerEnt.IsValid())
{
return false;
}
if (!playerEnt.Transform.Coordinates.InRange(EntityManager, used.Transform.Coordinates, InteractionRange))
{
return false;
}
InteractionActivate(playerEnt, used);
return true;
}
/// <summary>
/// Activates the Activate behavior of an object
/// Verifies that the user is capable of doing the use interaction first
/// </summary>
/// <param name="user"></param>
/// <param name="used"></param>
public void TryInteractionActivate(IEntity user, IEntity used)
{
if (user != null && used != null && ActionBlockerSystem.CanUse(user))
{
InteractionActivate(user, used);
}
}
private void InteractionActivate(IEntity user, IEntity used)
{
var activateMsg = new ActivateInWorldMessage(user, used);
RaiseLocalEvent(activateMsg);
if (activateMsg.Handled)
{
return;
}
if (!used.TryGetComponent(out IActivate activateComp))
{
return;
}
// all activates should only fire when in range / unbostructed
var activateEventArgs = new ActivateEventArgs { User = user, Target = used };
if (activateEventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true))
{
activateComp.Activate(activateEventArgs);
}
}
private bool HandleWideAttack(ICommonSession session, EntityCoordinates coords, EntityUid uid)
{
// client sanitization
if (!coords.IsValid(_entityManager))
{
Logger.InfoS("system.interaction", $"Invalid Coordinates: client={session}, coords={coords}");
return true;
}
if (uid.IsClientSide())
{
Logger.WarningS("system.interaction",
$"Client sent attack with client-side entity. Session={session}, Uid={uid}");
return true;
}
var userEntity = ((IPlayerSession) session).AttachedEntity;
if (userEntity == null || !userEntity.IsValid())
{
return true;
}
if (userEntity.TryGetComponent(out CombatModeComponent combatMode) && combatMode.IsInCombatMode)
{
2020-08-31 18:55:42 +02:00
DoAttack(userEntity, coords, true);
}
return true;
}
/// <summary>
/// Entity will try and use their active hand at the target location.
/// Don't use for players
/// </summary>
/// <param name="entity"></param>
/// <param name="coords"></param>
/// <param name="uid"></param>
internal void UseItemInHand(IEntity entity, EntityCoordinates coords, EntityUid uid)
{
if (entity.HasComponent<BasicActorComponent>())
{
throw new InvalidOperationException();
}
if (entity.TryGetComponent(out CombatModeComponent combatMode) && combatMode.IsInCombatMode)
{
DoAttack(entity, coords, false, uid);
}
else
{
UserInteraction(entity, coords, uid);
}
}
private bool HandleClientUseItemInHand(ICommonSession session, EntityCoordinates coords, EntityUid uid)
{
// client sanitization
if (!coords.IsValid(_entityManager))
{
Logger.InfoS("system.interaction", $"Invalid Coordinates: client={session}, coords={coords}");
return true;
}
if (uid.IsClientSide())
{
Logger.WarningS("system.interaction",
$"Client sent interaction with client-side entity. Session={session}, Uid={uid}");
return true;
}
var userEntity = ((IPlayerSession) session).AttachedEntity;
if (userEntity == null || !userEntity.IsValid())
{
return true;
}
if (userEntity.TryGetComponent(out CombatModeComponent combat) && combat.IsInCombatMode)
2020-08-31 18:55:42 +02:00
DoAttack(userEntity, coords, false, uid);
else
UserInteraction(userEntity, coords, uid);
return true;
}
private bool HandleTryPullObject(ICommonSession session, EntityCoordinates coords, EntityUid uid)
Add pulling (#1409) * Initial framework for pulling. * Make it possible to pull items via (temporary) keybind Ctrl+Click, make items follow the player correctly. * Make other objects pullable, implement functionality for moving an object being pulled, make only one object able to be pulled at a time. * Make sure that MoveTo won't allow collisions with the player * Update everything to work with the new physics engine * Update Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs Co-authored-by: ComicIronic <comicironic@gmail.com> * Physics update and convert to direct type casts * Add notnull checks * Add pull keybinds to the tutorial window * Move PullController to shared * Fix pulled items getting left behind * Fix moving pulled objects into walls * Remove flooring of coordinates when moving pulled objects * Add missing null check in PutInHand * Change pulling keybind to control and throwing to alt * Change PhysicsComponent references to IPhysicsComponent * Add trying to pull a pulled entity disabling the pull * Add pulled status effect * Fix merge conflicts * Merge fixes * Make players pullable * Fix being able to pull yourself * Change pull moving to use a velocity * Update pulled and pulling icons to not be buckle A tragedy * Make pulled and pulling icons more consistent * Remove empty not pulled and not pulling images * Pulled icon update * Pulled icon update * Add clicking pulling status effect to stop the pull * Fix spacewalking when pulling * Merge conflict fixes * Add a pull verb * Fix nullable error * Add pulling through the entity drop down menu Co-authored-by: Jackson Lewis <inquisitivepenguin@protonmail.com> Co-authored-by: ComicIronic <comicironic@gmail.com>
2020-07-27 00:54:32 +02:00
{
// client sanitization
if (!coords.IsValid(_entityManager))
Add pulling (#1409) * Initial framework for pulling. * Make it possible to pull items via (temporary) keybind Ctrl+Click, make items follow the player correctly. * Make other objects pullable, implement functionality for moving an object being pulled, make only one object able to be pulled at a time. * Make sure that MoveTo won't allow collisions with the player * Update everything to work with the new physics engine * Update Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs Co-authored-by: ComicIronic <comicironic@gmail.com> * Physics update and convert to direct type casts * Add notnull checks * Add pull keybinds to the tutorial window * Move PullController to shared * Fix pulled items getting left behind * Fix moving pulled objects into walls * Remove flooring of coordinates when moving pulled objects * Add missing null check in PutInHand * Change pulling keybind to control and throwing to alt * Change PhysicsComponent references to IPhysicsComponent * Add trying to pull a pulled entity disabling the pull * Add pulled status effect * Fix merge conflicts * Merge fixes * Make players pullable * Fix being able to pull yourself * Change pull moving to use a velocity * Update pulled and pulling icons to not be buckle A tragedy * Make pulled and pulling icons more consistent * Remove empty not pulled and not pulling images * Pulled icon update * Pulled icon update * Add clicking pulling status effect to stop the pull * Fix spacewalking when pulling * Merge conflict fixes * Add a pull verb * Fix nullable error * Add pulling through the entity drop down menu Co-authored-by: Jackson Lewis <inquisitivepenguin@protonmail.com> Co-authored-by: ComicIronic <comicironic@gmail.com>
2020-07-27 00:54:32 +02:00
{
Logger.InfoS("system.interaction", $"Invalid Coordinates for pulling: client={session}, coords={coords}");
return false;
}
if (uid.IsClientSide())
{
Logger.WarningS("system.interaction",
$"Client sent pull interaction with client-side entity. Session={session}, Uid={uid}");
return false;
}
var player = session.AttachedEntity;
if (player == null)
{
Logger.WarningS("system.interaction",
$"Client sent pulling interaction with no attached entity. Session={session}, Uid={uid}");
return false;
}
if (!EntityManager.TryGetEntity(uid, out var pulledObject))
{
return false;
}
if (player == pulledObject)
{
return false;
}
if (!pulledObject.TryGetComponent(out PullableComponent pull))
Add pulling (#1409) * Initial framework for pulling. * Make it possible to pull items via (temporary) keybind Ctrl+Click, make items follow the player correctly. * Make other objects pullable, implement functionality for moving an object being pulled, make only one object able to be pulled at a time. * Make sure that MoveTo won't allow collisions with the player * Update everything to work with the new physics engine * Update Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs Co-authored-by: ComicIronic <comicironic@gmail.com> * Physics update and convert to direct type casts * Add notnull checks * Add pull keybinds to the tutorial window * Move PullController to shared * Fix pulled items getting left behind * Fix moving pulled objects into walls * Remove flooring of coordinates when moving pulled objects * Add missing null check in PutInHand * Change pulling keybind to control and throwing to alt * Change PhysicsComponent references to IPhysicsComponent * Add trying to pull a pulled entity disabling the pull * Add pulled status effect * Fix merge conflicts * Merge fixes * Make players pullable * Fix being able to pull yourself * Change pull moving to use a velocity * Update pulled and pulling icons to not be buckle A tragedy * Make pulled and pulling icons more consistent * Remove empty not pulled and not pulling images * Pulled icon update * Pulled icon update * Add clicking pulling status effect to stop the pull * Fix spacewalking when pulling * Merge conflict fixes * Add a pull verb * Fix nullable error * Add pulling through the entity drop down menu Co-authored-by: Jackson Lewis <inquisitivepenguin@protonmail.com> Co-authored-by: ComicIronic <comicironic@gmail.com>
2020-07-27 00:54:32 +02:00
{
return false;
}
var dist = player.Transform.Coordinates.Position - pulledObject.Transform.Coordinates.Position;
Add pulling (#1409) * Initial framework for pulling. * Make it possible to pull items via (temporary) keybind Ctrl+Click, make items follow the player correctly. * Make other objects pullable, implement functionality for moving an object being pulled, make only one object able to be pulled at a time. * Make sure that MoveTo won't allow collisions with the player * Update everything to work with the new physics engine * Update Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs Co-authored-by: ComicIronic <comicironic@gmail.com> * Physics update and convert to direct type casts * Add notnull checks * Add pull keybinds to the tutorial window * Move PullController to shared * Fix pulled items getting left behind * Fix moving pulled objects into walls * Remove flooring of coordinates when moving pulled objects * Add missing null check in PutInHand * Change pulling keybind to control and throwing to alt * Change PhysicsComponent references to IPhysicsComponent * Add trying to pull a pulled entity disabling the pull * Add pulled status effect * Fix merge conflicts * Merge fixes * Make players pullable * Fix being able to pull yourself * Change pull moving to use a velocity * Update pulled and pulling icons to not be buckle A tragedy * Make pulled and pulling icons more consistent * Remove empty not pulled and not pulling images * Pulled icon update * Pulled icon update * Add clicking pulling status effect to stop the pull * Fix spacewalking when pulling * Merge conflict fixes * Add a pull verb * Fix nullable error * Add pulling through the entity drop down menu Co-authored-by: Jackson Lewis <inquisitivepenguin@protonmail.com> Co-authored-by: ComicIronic <comicironic@gmail.com>
2020-07-27 00:54:32 +02:00
if (dist.LengthSquared > InteractionRangeSquared)
{
return false;
}
return pull.TogglePull(player);
Add pulling (#1409) * Initial framework for pulling. * Make it possible to pull items via (temporary) keybind Ctrl+Click, make items follow the player correctly. * Make other objects pullable, implement functionality for moving an object being pulled, make only one object able to be pulled at a time. * Make sure that MoveTo won't allow collisions with the player * Update everything to work with the new physics engine * Update Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs Co-authored-by: ComicIronic <comicironic@gmail.com> * Physics update and convert to direct type casts * Add notnull checks * Add pull keybinds to the tutorial window * Move PullController to shared * Fix pulled items getting left behind * Fix moving pulled objects into walls * Remove flooring of coordinates when moving pulled objects * Add missing null check in PutInHand * Change pulling keybind to control and throwing to alt * Change PhysicsComponent references to IPhysicsComponent * Add trying to pull a pulled entity disabling the pull * Add pulled status effect * Fix merge conflicts * Merge fixes * Make players pullable * Fix being able to pull yourself * Change pull moving to use a velocity * Update pulled and pulling icons to not be buckle A tragedy * Make pulled and pulling icons more consistent * Remove empty not pulled and not pulling images * Pulled icon update * Pulled icon update * Add clicking pulling status effect to stop the pull * Fix spacewalking when pulling * Merge conflict fixes * Add a pull verb * Fix nullable error * Add pulling through the entity drop down menu Co-authored-by: Jackson Lewis <inquisitivepenguin@protonmail.com> Co-authored-by: ComicIronic <comicironic@gmail.com>
2020-07-27 00:54:32 +02:00
}
private async void UserInteraction(IEntity player, EntityCoordinates coordinates, EntityUid clickedUid)
{
// Get entity clicked upon from UID if valid UID, if not assume no entity clicked upon and null
if (!EntityManager.TryGetEntity(clickedUid, out var attacked))
{
attacked = null;
}
// Verify player has a transform component
if (!player.TryGetComponent<ITransformComponent>(out var playerTransform))
{
return;
}
// Verify player is on the same map as the entity he clicked on
if (coordinates.GetMapId(_entityManager) != playerTransform.MapID)
{
Logger.WarningS("system.interaction",
$"Player named {player.Name} clicked on a map he isn't located on");
return;
}
// Verify player has a hand, and find what object he is currently holding in his active hand
if (!player.TryGetComponent<IHandsComponent>(out var hands))
{
return;
}
var item = hands.GetActiveHand?.Owner;
if (ActionBlockerSystem.CanChangeDirection(player))
{
var diff = coordinates.ToMapPos(EntityManager) - playerTransform.MapPosition.Position;
if (diff.LengthSquared > 0.01f)
{
playerTransform.LocalRotation = new Angle(diff);
}
}
2020-05-13 19:26:39 +02:00
2020-07-07 01:40:08 +02:00
if (!ActionBlockerSystem.CanInteract(player))
{
return;
}
// If in a container
if (player.IsInContainer())
{
return;
}
2020-07-08 15:37:18 +02:00
// In a container where the attacked entity is not the container's owner
if (player.TryGetContainer(out var playerContainer) &&
2020-07-08 15:37:18 +02:00
attacked != playerContainer.Owner)
{
2020-07-08 15:37:18 +02:00
// Either the attacked entity is null, not contained or in a different container
if (attacked == null ||
!attacked.TryGetContainer(out var attackedContainer) ||
2020-07-08 15:37:18 +02:00
attackedContainer != playerContainer)
{
return;
}
}
// TODO: Check if client should be able to see that object to click on it in the first place
// Clicked on empty space behavior, try using ranged attack
if (attacked == null)
{
if (item != null)
{
2020-05-23 17:23:25 +02:00
// After attack: Check if we clicked on an empty location, if so the only interaction we can do is AfterInteract
var distSqrt = (playerTransform.WorldPosition - coordinates.ToMapPos(EntityManager)).LengthSquared;
InteractAfter(player, item, coordinates, distSqrt <= InteractionRangeSquared);
}
return;
}
// Verify attacked object is on the map if we managed to click on it somehow
if (!attacked.Transform.IsMapTransform)
{
Logger.WarningS("system.interaction",
$"Player named {player.Name} clicked on object {attacked.Name} that isn't currently on the map somehow");
return;
}
2020-05-23 17:23:25 +02:00
// RangedInteract/AfterInteract: Check distance between user and clicked item, if too large parse it in the ranged function
// TODO: have range based upon the item being used? or base it upon some variables of the player himself?
var distance = (playerTransform.WorldPosition - attacked.Transform.WorldPosition).LengthSquared;
if (distance > InteractionRangeSquared)
{
if (item != null)
{
RangedInteraction(player, item, attacked, coordinates);
return;
}
2020-05-23 17:23:25 +02:00
return; // Add some form of ranged InteractHand here if you need it someday, or perhaps just ways to modify the range of InteractHand
}
// We are close to the nearby object and the object isn't contained in our active hand
2020-05-23 17:23:25 +02:00
// InteractUsing/AfterInteract: We will either use the item on the nearby object
if (item != null)
{
await Interaction(player, item, attacked, coordinates);
}
2020-05-23 17:23:25 +02:00
// InteractHand/Activate: Since our hand is empty we will use InteractHand/Activate
else
{
Interaction(player, attacked);
}
}
/// <summary>
2020-05-23 17:23:25 +02:00
/// We didn't click on any entity, try doing an AfterInteract on the click location
/// </summary>
private void InteractAfter(IEntity user, IEntity weapon, EntityCoordinates clickLocation, bool canReach)
{
var message = new AfterInteractMessage(user, weapon, null, clickLocation, canReach);
RaiseLocalEvent(message);
if (message.Handled)
{
return;
}
2020-05-23 17:23:25 +02:00
var afterInteracts = weapon.GetAllComponents<IAfterInteract>().ToList();
var afterInteractEventArgs = new AfterInteractEventArgs { User = user, ClickLocation = clickLocation, CanReach = canReach };
2020-05-23 17:23:25 +02:00
foreach (var afterInteract in afterInteracts)
{
2020-05-23 17:23:25 +02:00
afterInteract.AfterInteract(afterInteractEventArgs);
}
}
/// <summary>
/// Uses a weapon/object on an entity
2020-05-23 17:23:25 +02:00
/// Finds components with the InteractUsing interface and calls their function
/// </summary>
public async Task Interaction(IEntity user, IEntity weapon, IEntity attacked, EntityCoordinates clickLocation)
{
var attackMsg = new InteractUsingMessage(user, weapon, attacked, clickLocation);
RaiseLocalEvent(attackMsg);
if (attackMsg.Handled)
{
return;
}
var attackBys = attacked.GetAllComponents<IInteractUsing>().OrderByDescending(x => x.Priority);
2020-05-23 17:23:25 +02:00
var attackByEventArgs = new InteractUsingEventArgs
{
2020-05-23 17:23:25 +02:00
User = user, ClickLocation = clickLocation, Using = weapon, Target = attacked
};
// all AttackBys should only happen when in range / unobstructed, so no range check is needed
if (attackByEventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true))
{
foreach (var attackBy in attackBys)
{
if (await attackBy.InteractUsing(attackByEventArgs))
{
2020-05-23 17:23:25 +02:00
// If an InteractUsing returns a status completion we finish our attack
return;
}
}
}
var afterAtkMsg = new AfterInteractMessage(user, weapon, attacked, clickLocation, true);
RaiseLocalEvent(afterAtkMsg);
if (afterAtkMsg.Handled)
{
return;
}
// If we aren't directly attacking the nearby object, lets see if our item has an after attack we can do
2020-05-23 17:23:25 +02:00
var afterAttacks = weapon.GetAllComponents<IAfterInteract>().ToList();
var afterAttackEventArgs = new AfterInteractEventArgs
{
User = user, ClickLocation = clickLocation, Target = attacked, CanReach = true
};
foreach (var afterAttack in afterAttacks)
{
2020-05-23 17:23:25 +02:00
afterAttack.AfterInteract(afterAttackEventArgs);
}
}
/// <summary>
/// Uses an empty hand on an entity
2020-05-23 17:23:25 +02:00
/// Finds components with the InteractHand interface and calls their function
/// </summary>
public void Interaction(IEntity user, IEntity attacked)
{
var message = new AttackHandMessage(user, attacked);
RaiseLocalEvent(message);
if (message.Handled)
{
return;
}
2020-05-23 17:23:25 +02:00
var attackHands = attacked.GetAllComponents<IInteractHand>().ToList();
var attackHandEventArgs = new InteractHandEventArgs { User = user, Target = attacked };
// all attackHands should only fire when in range / unobstructed
if (attackHandEventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true))
{
foreach (var attackHand in attackHands)
{
2020-05-23 17:23:25 +02:00
if (attackHand.InteractHand(attackHandEventArgs))
{
2020-05-23 17:23:25 +02:00
// If an InteractHand returns a status completion we finish our attack
return;
}
}
}
// Else we run Activate.
InteractionActivate(user, attacked);
}
/// <summary>
/// Activates the Use behavior of an object
/// Verifies that the user is capable of doing the use interaction first
/// </summary>
/// <param name="user"></param>
/// <param name="used"></param>
public void TryUseInteraction(IEntity user, IEntity used)
{
if (user != null && used != null && ActionBlockerSystem.CanUse(user))
{
UseInteraction(user, used);
}
}
/// <summary>
/// Activates/Uses an object in control/possession of a user
/// If the item has the IUse interface on one of its components we use the object in our hand
/// </summary>
public void UseInteraction(IEntity user, IEntity used)
{
if (used.TryGetComponent<UseDelayComponent>(out var delayComponent))
{
if (delayComponent.ActiveDelay)
return;
else
delayComponent.BeginDelay();
}
var useMsg = new UseInHandMessage(user, used);
RaiseLocalEvent(useMsg);
if (useMsg.Handled)
{
return;
}
var uses = used.GetAllComponents<IUse>().ToList();
// Try to use item on any components which have the interface
foreach (var use in uses)
{
if (use.UseEntity(new UseEntityEventArgs { User = user }))
{
// If a Use returns a status completion we finish our attack
return;
}
}
}
/// <summary>
/// Activates the Throw behavior of an object
/// Verifies that the user is capable of doing the throw interaction first
/// </summary>
public bool TryThrowInteraction(IEntity user, IEntity item)
{
if (user == null || item == null || !ActionBlockerSystem.CanThrow(user)) return false;
ThrownInteraction(user, item);
return true;
}
/// <summary>
/// Calls Thrown on all components that implement the IThrown interface
/// on an entity that has been thrown.
/// </summary>
public void ThrownInteraction(IEntity user, IEntity thrown)
{
var throwMsg = new ThrownMessage(user, thrown);
RaiseLocalEvent(throwMsg);
if (throwMsg.Handled)
{
return;
}
var comps = thrown.GetAllComponents<IThrown>().ToList();
// Call Thrown on all components that implement the interface
foreach (var comp in comps)
{
comp.Thrown(new ThrownEventArgs(user));
}
}
/// <summary>
/// Calls Land on all components that implement the ILand interface
/// on an entity that has landed after being thrown.
/// </summary>
public void LandInteraction(IEntity user, IEntity landing, EntityCoordinates landLocation)
{
var landMsg = new LandMessage(user, landing, landLocation);
RaiseLocalEvent(landMsg);
if (landMsg.Handled)
{
return;
}
var comps = landing.GetAllComponents<ILand>().ToList();
// Call Land on all components that implement the interface
foreach (var comp in comps)
{
comp.Land(new LandEventArgs(user, landLocation));
}
}
/// <summary>
/// Calls ThrowCollide on all components that implement the IThrowCollide interface
/// on a thrown entity and the target entity it hit.
/// </summary>
public void ThrowCollideInteraction(IEntity user, IEntity thrown, IEntity target, EntityCoordinates location)
{
var collideMsg = new ThrowCollideMessage(user, thrown, target, location);
RaiseLocalEvent(collideMsg);
if (collideMsg.Handled)
{
return;
}
var eventArgs = new ThrowCollideEventArgs(user, thrown, target, location);
foreach (var comp in thrown.GetAllComponents<IThrowCollide>().ToArray())
{
comp.DoHit(eventArgs);
}
foreach (var comp in target.GetAllComponents<IThrowCollide>().ToArray())
{
comp.HitBy(eventArgs);
}
}
/// <summary>
/// Calls Equipped on all components that implement the IEquipped interface
/// on an entity that has been equipped.
/// </summary>
public void EquippedInteraction(IEntity user, IEntity equipped, EquipmentSlotDefines.Slots slot)
{
var equipMsg = new EquippedMessage(user, equipped, slot);
RaiseLocalEvent(equipMsg);
if (equipMsg.Handled)
{
return;
}
var comps = equipped.GetAllComponents<IEquipped>().ToList();
// Call Thrown on all components that implement the interface
foreach (var comp in comps)
{
comp.Equipped(new EquippedEventArgs(user, slot));
}
}
/// <summary>
/// Calls Unequipped on all components that implement the IUnequipped interface
/// on an entity that has been equipped.
/// </summary>
public void UnequippedInteraction(IEntity user, IEntity equipped, EquipmentSlotDefines.Slots slot)
{
var unequipMsg = new UnequippedMessage(user, equipped, slot);
RaiseLocalEvent(unequipMsg);
if (unequipMsg.Handled)
{
return;
}
var comps = equipped.GetAllComponents<IUnequipped>().ToList();
// Call Thrown on all components that implement the interface
foreach (var comp in comps)
{
comp.Unequipped(new UnequippedEventArgs(user, slot));
}
}
/// <summary>
/// Activates the Dropped behavior of an object
/// Verifies that the user is capable of doing the drop interaction first
/// </summary>
public bool TryDroppedInteraction(IEntity user, IEntity item)
{
if (user == null || item == null || !ActionBlockerSystem.CanDrop(user)) return false;
DroppedInteraction(user, item);
return true;
}
/// <summary>
/// Calls Dropped on all components that implement the IDropped interface
/// on an entity that has been dropped.
/// </summary>
public void DroppedInteraction(IEntity user, IEntity item)
{
var dropMsg = new DroppedMessage(user, item);
RaiseLocalEvent(dropMsg);
if (dropMsg.Handled)
{
return;
}
var comps = item.GetAllComponents<IDropped>().ToList();
// Call Land on all components that implement the interface
foreach (var comp in comps)
{
comp.Dropped(new DroppedEventArgs(user));
}
}
/// <summary>
/// Calls HandSelected on all components that implement the IHandSelected interface
/// on an item entity on a hand that has just been selected.
/// </summary>
public void HandSelectedInteraction(IEntity user, IEntity item)
{
var handSelectedMsg = new HandSelectedMessage(user, item);
RaiseLocalEvent(handSelectedMsg);
if (handSelectedMsg.Handled)
{
return;
}
var comps = item.GetAllComponents<IHandSelected>().ToList();
// Call Land on all components that implement the interface
foreach (var comp in comps)
{
comp.HandSelected(new HandSelectedEventArgs(user));
}
}
/// <summary>
/// Calls HandDeselected on all components that implement the IHandDeselected interface
/// on an item entity on a hand that has just been deselected.
/// </summary>
public void HandDeselectedInteraction(IEntity user, IEntity item)
{
var handDeselectedMsg = new HandDeselectedMessage(user, item);
RaiseLocalEvent(handDeselectedMsg);
if (handDeselectedMsg.Handled)
{
return;
}
var comps = item.GetAllComponents<IHandDeselected>().ToList();
// Call Land on all components that implement the interface
foreach (var comp in comps)
{
comp.HandDeselected(new HandDeselectedEventArgs(user));
}
}
/// <summary>
/// Will have two behaviors, either "uses" the weapon at range on the entity if it is capable of accepting that action
/// Or it will use the weapon itself on the position clicked, regardless of what was there
/// </summary>
public void RangedInteraction(IEntity user, IEntity weapon, IEntity attacked, EntityCoordinates clickLocation)
{
var rangedMsg = new RangedInteractMessage(user, weapon, attacked, clickLocation);
RaiseLocalEvent(rangedMsg);
if (rangedMsg.Handled)
return;
2020-05-23 17:23:25 +02:00
var rangedAttackBys = attacked.GetAllComponents<IRangedInteract>().ToList();
var rangedAttackByEventArgs = new RangedInteractEventArgs
{
2020-05-23 17:23:25 +02:00
User = user, Using = weapon, ClickLocation = clickLocation
};
// See if we have a ranged attack interaction
foreach (var t in rangedAttackBys)
{
2020-05-23 17:23:25 +02:00
if (t.RangedInteract(rangedAttackByEventArgs))
{
2020-05-23 17:23:25 +02:00
// If an InteractUsing returns a status completion we finish our attack
return;
}
}
var afterAtkMsg = new AfterInteractMessage(user, weapon, attacked, clickLocation, false);
RaiseLocalEvent(afterAtkMsg);
if (afterAtkMsg.Handled)
return;
2020-05-23 17:23:25 +02:00
var afterAttacks = weapon.GetAllComponents<IAfterInteract>().ToList();
var afterAttackEventArgs = new AfterInteractEventArgs
{
User = user, ClickLocation = clickLocation, Target = attacked, CanReach = false
};
//See if we have a ranged attack interaction
foreach (var afterAttack in afterAttacks)
{
2020-05-23 17:23:25 +02:00
afterAttack.AfterInteract(afterAttackEventArgs);
}
}
private void DoAttack(IEntity player, EntityCoordinates coordinates, bool wideAttack, EntityUid target = default)
{
// Verify player is on the same map as the entity he clicked on
if (coordinates.GetMapId(EntityManager) != player.Transform.MapID)
{
Logger.WarningS("system.interaction",
$"Player named {player.Name} clicked on a map he isn't located on");
return;
}
2020-08-31 18:55:42 +02:00
if (!ActionBlockerSystem.CanAttack(player) ||
(!wideAttack && !player.InRangeUnobstructed(coordinates, ignoreInsideBlocker: true)))
{
return;
}
2020-08-31 18:55:42 +02:00
var eventArgs = new AttackEventArgs(player, coordinates, wideAttack, target);
2020-06-21 18:31:56 -07:00
// Verify player has a hand, and find what object he is currently holding in his active hand
if (player.TryGetComponent<IHandsComponent>(out var hands))
{
2020-06-21 18:31:56 -07:00
var item = hands.GetActiveHand?.Owner;
if (item != null)
{
foreach (var attackComponent in item.GetAllComponents<IAttack>())
{
if (wideAttack ? attackComponent.WideAttack(eventArgs) : attackComponent.ClickAttack(eventArgs))
2020-08-31 18:55:42 +02:00
return;
2020-06-21 18:31:56 -07:00
}
}
else
{
// We pick up items if our hand is empty, even if we're in combat mode.
if(EntityManager.TryGetEntity(target, out var targetEnt))
{
if (targetEnt.HasComponent<ItemComponent>())
{
Interaction(player, targetEnt);
return;
}
}
}
}
2020-06-21 18:31:56 -07:00
foreach (var attackComponent in player.GetAllComponents<IAttack>())
{
2020-08-31 18:55:42 +02:00
if (wideAttack)
attackComponent.WideAttack(eventArgs);
else
attackComponent.ClickAttack(eventArgs);
}
}
}
}