using System; using System.Collections.Generic; using System.Linq; using Content.Server.GameObjects.Components.Interactable; using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Timing; using Content.Server.Interfaces.GameObjects; using Content.Shared.GameObjects.Components.Interactable; using Content.Shared.GameObjects.Components.Inventory; using Content.Shared.Input; using Content.Shared.Physics; using JetBrains.Annotations; using Robust.Server.GameObjects.EntitySystems; using Robust.Server.Interfaces.Player; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Components; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Input; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects.Components; using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Physics; 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 { /// /// This interface gives components behavior when being clicked on or "attacked" by a user with an object in their hand /// public interface IAttackBy { /// /// Called when using one object on another /// bool AttackBy(AttackByEventArgs eventArgs); } public class AttackByEventArgs : EventArgs { public IEntity User { get; set; } public GridCoordinates ClickLocation { get; set; } public IEntity AttackWith { get; set; } } #region Tools public class ToolActEventArgs : EventArgs { public IEntity User { get; set; } public GridCoordinates ClickLocation { get; set; } public IEntity AttackWith { get; set; } public ToolComponent ToolComponent => AttackWith.GetComponent(); public virtual Tool Behavior { get; } } /// /// This interface gives components behavior when being clicked on or "attacked" by a user with a wrench in their hand /// public interface IWrenchAct { /// /// Called when using a wrench on an entity /// bool WrenchAct(WrenchActEventArgs eventArgs); } public class WrenchActEventArgs : ToolActEventArgs { public override Tool Behavior => Tool.Wrench; } /// /// This interface gives components behavior when being clicked on or "attacked" by a user with a crowbar in their hand /// public interface ICrowbarAct { /// /// Called when using a wrench on an entity /// bool CrowbarAct(CrowbarActEventArgs eventArgs); } public class CrowbarActEventArgs : ToolActEventArgs { public override Tool Behavior => Tool.Crowbar; } /// /// This interface gives components behavior when being clicked on or "attacked" by a user with a screwdriver in their hand /// public interface IScrewdriverAct { /// /// Called when using a wrench on an entity /// bool ScrewdriverAct(ScrewdriverActEventArgs eventArgs); } public class ScrewdriverActEventArgs : ToolActEventArgs { public override Tool Behavior => Tool.Screwdriver; } /// /// This interface gives components behavior when being clicked on or "attacked" by a user with a wirecutter in their hand /// public interface IWirecutterAct { /// /// Called when using a wrench on an entity /// bool WirecutterAct(WirecutterActEventArgs eventArgs); } public class WirecutterActEventArgs : ToolActEventArgs { public override Tool Behavior => Tool.Wirecutter; } /// /// This interface gives components behavior when being clicked on or "attacked" by a user with a welder in their hand /// public interface IWelderAct { /// /// Called when using a wrench on an entity /// bool WelderAct(WelderActEventArgs eventArgs); } public class WelderActEventArgs : ToolActEventArgs { public override Tool Behavior => Tool.Welder; public WelderComponent WelderComponent => (WelderComponent)ToolComponent; public bool Lit { get; set; } public float Fuel { get; set; } public float FuelCapacity { get; set; } } /// /// This interface gives components behavior when being clicked on or "attacked" by a user with a multitool in their hand /// public interface IMultitoolAct { /// /// Called when using a wrench on an entity /// bool MultitoolAct(MultitoolActEventArgs eventArgs); } public class MultitoolActEventArgs : ToolActEventArgs { public override Tool Behavior => Tool.Multitool; } #endregion /// /// This interface gives components behavior when being clicked on or "attacked" by a user with an empty hand /// public interface IAttackHand { /// /// Called when a player directly interacts with an empty hand /// bool AttackHand(AttackHandEventArgs eventArgs); } public class AttackHandEventArgs : EventArgs { public IEntity User { get; set; } } /// /// This interface gives components behavior when being clicked by objects outside the range of direct use /// public interface IRangedAttackBy { /// /// Called when we try to interact with an entity out of range /// /// bool RangedAttackBy(RangedAttackByEventArgs eventArgs); } [PublicAPI] public class RangedAttackByEventArgs : EventArgs { public IEntity User { get; set; } public IEntity Weapon { get; set; } public GridCoordinates ClickLocation { get; set; } } /// /// This interface gives components a behavior when clicking on another object and no interaction occurs /// Doesn't pass what you clicked on as an argument, but if it becomes necessary we can add it later /// public interface IAfterAttack { /// /// Called when we interact with nothing, or when we interact with an entity out of range that has no behavior /// void AfterAttack(AfterAttackEventArgs eventArgs); } public class AfterAttackEventArgs : EventArgs { public IEntity User { get; set; } public GridCoordinates ClickLocation { get; set; } public IEntity Attacked { get; set; } } /// /// This interface gives components behavior when using the entity in your hands /// public interface IUse { /// /// Called when we activate an object we are holding to use it /// /// bool UseEntity(UseEntityEventArgs eventArgs); } public class UseEntityEventArgs : EventArgs { public IEntity User { get; set; } } /// /// This interface gives components behavior when being activated in the world. /// public interface IActivate { /// /// Called when this component is activated by another entity. /// void Activate(ActivateEventArgs eventArgs); } public class ActivateEventArgs : EventArgs { public IEntity User { get; set; } } /// /// This interface gives components behavior when thrown. /// public interface IThrown { void Thrown(ThrownEventArgs eventArgs); } public class ThrownEventArgs : EventArgs { public ThrownEventArgs(IEntity user) { User = user; } public IEntity User { get; } } /// /// This interface gives components behavior when landing after being thrown. /// public interface ILand { void Land(LandEventArgs eventArgs); } public class LandEventArgs : EventArgs { public LandEventArgs(IEntity user, GridCoordinates landingLocation) { User = user; LandingLocation = landingLocation; } public IEntity User { get; } public GridCoordinates LandingLocation { get; } } /// /// This interface gives components behavior when their owner is put in an inventory slot. /// public interface IEquipped { void Equipped(EquippedEventArgs eventArgs); } public class EquippedEventArgs : EventArgs { public EquippedEventArgs(IEntity user, EquipmentSlotDefines.Slots slot) { User = user; Slot = slot; } public IEntity User { get; } public EquipmentSlotDefines.Slots Slot { get; } } /// /// This interface gives components behavior when their owner is removed from an inventory slot. /// public interface IUnequipped { void Unequipped(UnequippedEventArgs eventArgs); } public class UnequippedEventArgs : EventArgs { public UnequippedEventArgs(IEntity user, EquipmentSlotDefines.Slots slot) { User = user; Slot = slot; } public IEntity User { get; } public EquipmentSlotDefines.Slots Slot { get; } } /// /// This interface gives components behavior when being used to "attack". /// public interface IAttack { void Attack(AttackEventArgs eventArgs); } public class AttackEventArgs : EventArgs { public AttackEventArgs(IEntity user, GridCoordinates clickLocation) { User = user; ClickLocation = clickLocation; } public IEntity User { get; } public GridCoordinates ClickLocation { get; } } /// /// This interface gives components behavior when they're held on the selected hand. /// public interface IHandSelected { void HandSelected(HandSelectedEventArgs eventArgs); } public class HandSelectedEventArgs : EventArgs { public HandSelectedEventArgs(IEntity user) { User = user; } public IEntity User { get; } } /// /// This interface gives components behavior when they're held on a deselected hand. /// public interface IHandDeselected { void HandDeselected(HandDeselectedEventArgs eventArgs); } public class HandDeselectedEventArgs : EventArgs { public HandDeselectedEventArgs(IEntity user) { User = user; } public IEntity User { get; } } /// /// This interface gives components behavior when they're dropped by a mob. /// public interface IDropped { void Dropped(DroppedEventArgs eventArgs); } public class DroppedEventArgs : EventArgs { public DroppedEventArgs(IEntity user) { User = user; } public IEntity User { get; } } /// /// Governs interactions during clicking on entities /// [UsedImplicitly] public sealed class InteractionSystem : SharedInteractionSystem { #pragma warning disable 649 [Dependency] private readonly IMapManager _mapManager; [Dependency] private readonly IPhysicsManager _physicsManager; #pragma warning restore 649 public const float InteractionRange = 2; public const float InteractionRangeSquared = InteractionRange * InteractionRange; public override void Initialize() { var inputSys = EntitySystemManager.GetEntitySystem(); inputSys.BindMap.BindFunction(EngineKeyFunctions.Use, new PointerInputCmdHandler(HandleUseItemInHand)); inputSys.BindMap.BindFunction(ContentKeyFunctions.WideAttack, new PointerInputCmdHandler(HandleWideAttack)); inputSys.BindMap.BindFunction(ContentKeyFunctions.ActivateItemInWorld, new PointerInputCmdHandler(HandleActivateItemInWorld)); } private bool HandleActivateItemInWorld(ICommonSession session, GridCoordinates 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.GridPosition.InRange(_mapManager, used.Transform.GridPosition, InteractionRange)) { return false; } InteractionActivate(playerEnt, used); return true; } /// /// Activates the Activate 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 && 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; } activateComp.Activate(new ActivateEventArgs {User = user}); } private bool HandleWideAttack(ICommonSession session, GridCoordinates coords, EntityUid uid) { // client sanitization if (!_mapManager.GridExists(coords.GridID)) { 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) { DoAttack(userEntity, coords); } return true; } private bool HandleUseItemInHand(ICommonSession session, GridCoordinates coords, EntityUid uid) { // client sanitization if (!_mapManager.GridExists(coords.GridID)) { 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; } UserInteraction(userEntity, coords, uid); return true; } private void UserInteraction(IEntity player, GridCoordinates 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(out var playerTransform)) { return; } // Verify player is on the same map as the entity he clicked on if (_mapManager.GetGrid(coordinates.GridID).ParentMapId != 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(out var hands)) { return; } var item = hands.GetActiveHand?.Owner; if (!ActionBlockerSystem.CanInteract(player)) { return; } playerTransform.LocalRotation = new Angle(coordinates.ToMapPos(_mapManager) - playerTransform.MapPosition.Position); // 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) { // After attack: Check if we clicked on an empty location, if so the only interaction we can do is AfterAttack InteractAfterAttack(player, item, coordinates); } 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; } // Check if ClickLocation is in object bounds here, if not lets log as warning and see why if (attacked.TryGetComponent(out ICollidableComponent collideComp)) { if (!collideComp.WorldAABB.Contains(coordinates.ToMapPos(_mapManager))) { Logger.WarningS("system.interaction", $"Player {player.Name} clicked {attacked.Name} outside of its bounding box component somehow"); return; } } // RangedAttack/AfterAttack: 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; } return; // Add some form of ranged AttackHand here if you need it someday, or perhaps just ways to modify the range of AttackHand } // We are close to the nearby object and the object isn't contained in our active hand // AttackBy/AfterAttack: We will either use the item on the nearby object if (item != null) { Interaction(player, item, attacked, coordinates); } // AttackHand/Activate: Since our hand is empty we will use AttackHand/Activate else { Interaction(player, attacked); } } /// /// We didn't click on any entity, try doing an AfterAttack on the click location /// private void InteractAfterAttack(IEntity user, IEntity weapon, GridCoordinates clickLocation) { var message = new AfterAttackMessage(user, weapon, null, clickLocation); RaiseLocalEvent(message); if (message.Handled) { return; } var afterAttacks = weapon.GetAllComponents().ToList(); var afterAttackEventArgs = new AfterAttackEventArgs {User = user, ClickLocation = clickLocation}; foreach (var afterAttack in afterAttacks) { afterAttack.AfterAttack(afterAttackEventArgs); } } /// /// Uses a weapon/object on an entity /// Finds components with the AttackBy interface and calls their function /// public void Interaction(IEntity user, IEntity weapon, IEntity attacked, GridCoordinates clickLocation) { var attackMsg = new AttackByMessage(user, weapon, attacked, clickLocation); RaiseLocalEvent(attackMsg); if (attackMsg.Handled) { return; } var attackBys = attacked.GetAllComponents().ToList(); var attackByEventArgs = new AttackByEventArgs { User = user, ClickLocation = clickLocation, AttackWith = weapon }; foreach (var attackBy in attackBys) { if (attackBy.AttackBy(attackByEventArgs)) // If an AttackBy returns a status completion we finish our attack return; } var afterAtkMsg = new AfterAttackMessage(user, weapon, attacked, clickLocation); 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 var afterAttacks = weapon.GetAllComponents().ToList(); var afterAttackEventArgs = new AfterAttackEventArgs { User = user, ClickLocation = clickLocation, Attacked = attacked }; foreach (var afterAttack in afterAttacks) { afterAttack.AfterAttack(afterAttackEventArgs); } } /// /// Uses an empty hand on an entity /// Finds components with the AttackHand interface and calls their function /// public void Interaction(IEntity user, IEntity attacked) { var message = new AttackHandMessage(user, attacked); RaiseLocalEvent(message); if (message.Handled) { return; } var attackHands = attacked.GetAllComponents().ToList(); var attackHandEventArgs = new AttackHandEventArgs {User = user}; foreach (var attackHand in attackHands) { if (attackHand.AttackHand(attackHandEventArgs)) { // If an AttackHand returns a status completion we finish our attack return; } } // Else we run Activate. InteractionActivate(user, attacked); } /// /// Activates the Use behavior of an object /// Verifies that the user is capable of doing the use interaction first /// /// /// public void TryUseInteraction(IEntity user, IEntity used) { if (user != null && used != null && ActionBlockerSystem.CanUse(user)) { UseInteraction(user, used); } } /// /// 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 /// public void UseInteraction(IEntity user, IEntity used) { if (used.TryGetComponent(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().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; } } } /// /// 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 ThrownMessage(user, thrown); RaiseLocalEvent(throwMsg); if (throwMsg.Handled) { return; } var comps = thrown.GetAllComponents().ToList(); // Call Thrown on all components that implement the interface foreach (var comp in comps) { comp.Thrown(new ThrownEventArgs(user)); } } /// /// Calls Land on all components that implement the ILand interface /// on an entity that has landed after being thrown. /// public void LandInteraction(IEntity user, IEntity landing, GridCoordinates landLocation) { var landMsg = new LandMessage(user, landing, landLocation); RaiseLocalEvent(landMsg); if (landMsg.Handled) { return; } var comps = landing.GetAllComponents().ToList(); // Call Land on all components that implement the interface foreach (var comp in comps) { comp.Land(new LandEventArgs(user, landLocation)); } } /// /// 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 EquippedMessage(user, equipped, slot); RaiseLocalEvent(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 UnequippedMessage(user, equipped, slot); RaiseLocalEvent(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)); } } /// /// 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) { if (user == null || item == null || !ActionBlockerSystem.CanDrop(user)) return false; DroppedInteraction(user, item); 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) { var dropMsg = new DroppedMessage(user, item); RaiseLocalEvent(dropMsg); if (dropMsg.Handled) { return; } var comps = item.GetAllComponents().ToList(); // Call Land on all components that implement the interface foreach (var comp in comps) { comp.Dropped(new DroppedEventArgs(user)); } } /// /// 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 HandSelectedMessage(user, item); RaiseLocalEvent(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 HandDeselectedMessage(user, item); RaiseLocalEvent(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)); } } /// /// 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 /// public void RangedInteraction(IEntity user, IEntity weapon, IEntity attacked, GridCoordinates clickLocation) { var rangedMsg = new RangedAttackMessage(user, weapon, attacked, clickLocation); RaiseLocalEvent(rangedMsg); if (rangedMsg.Handled) return; var rangedAttackBys = attacked.GetAllComponents().ToList(); var rangedAttackByEventArgs = new RangedAttackByEventArgs { User = user, Weapon = weapon, ClickLocation = clickLocation }; // See if we have a ranged attack interaction foreach (var t in rangedAttackBys) { if (t.RangedAttackBy(rangedAttackByEventArgs)) { // If an AttackBy returns a status completion we finish our attack return; } } var afterAtkMsg = new AfterAttackMessage(user, weapon, attacked, clickLocation); RaiseLocalEvent(afterAtkMsg); if (afterAtkMsg.Handled) return; var afterAttacks = weapon.GetAllComponents().ToList(); var afterAttackEventArgs = new AfterAttackEventArgs { User = user, ClickLocation = clickLocation, Attacked = attacked }; //See if we have a ranged attack interaction foreach (var afterAttack in afterAttacks) { afterAttack.AfterAttack(afterAttackEventArgs); } } private void DoAttack(IEntity player, GridCoordinates coordinates) { // Verify player is on the same map as the entity he clicked on if (_mapManager.GetGrid(coordinates.GridID).ParentMapId != player.Transform.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(out var hands)) { return; } var item = hands.GetActiveHand?.Owner; // TODO: If item is null we need some kinda unarmed combat. if (!ActionBlockerSystem.CanAttack(player) || item == null) { return; } var eventArgs = new AttackEventArgs(player, coordinates); foreach (var attackComponent in item.GetAllComponents()) { attackComponent.Attack(eventArgs); } } } /// /// Raised when being clicked on or "attacked" by a user with an object in their hand /// [PublicAPI] public class AttackByMessage : EntitySystemMessage { /// /// If this message has already been "handled" by a previous system. /// public bool Handled { get; set; } /// /// Entity that triggered the attack. /// public IEntity User { get; } /// /// Entity that the User attacked with. /// public IEntity ItemInHand { get; } /// /// Entity that was attacked. /// public IEntity Attacked { get; } /// /// The original location that was clicked by the user. /// public GridCoordinates ClickLocation { get; } public AttackByMessage(IEntity user, IEntity itemInHand, IEntity attacked, GridCoordinates clickLocation) { User = user; ItemInHand = itemInHand; Attacked = attacked; ClickLocation = clickLocation; } } /// /// Raised when being clicked on or "attacked" by a user with an empty hand. /// [PublicAPI] public class AttackHandMessage : EntitySystemMessage { /// /// If this message has already been "handled" by a previous system. /// public bool Handled { get; set; } /// /// Entity that triggered the attack. /// public IEntity User { get; } /// /// Entity that was attacked. /// public IEntity Attacked { get; } public AttackHandMessage(IEntity user, IEntity attacked) { User = user; Attacked = attacked; } } /// /// Raised when being clicked by objects outside the range of direct use. /// [PublicAPI] public class RangedAttackMessage : EntitySystemMessage { /// /// If this message has already been "handled" by a previous system. /// public bool Handled { get; set; } /// /// Entity that triggered the attack. /// public IEntity User { get; } /// /// Entity that the User attacked with. /// public IEntity ItemInHand { get; set; } /// /// Entity that was attacked. /// public IEntity Attacked { get; } /// /// Location that the user clicked outside of their interaction range. /// public GridCoordinates ClickLocation { get; } public RangedAttackMessage(IEntity user, IEntity itemInHand, IEntity attacked, GridCoordinates clickLocation) { User = user; ItemInHand = itemInHand; ClickLocation = clickLocation; Attacked = attacked; } } /// /// Raised when clicking on another object and no attack event was handled. /// [PublicAPI] public class AfterAttackMessage : EntitySystemMessage { /// /// If this message has already been "handled" by a previous system. /// public bool Handled { get; set; } /// /// Entity that triggered the attack. /// public IEntity User { get; } /// /// Entity that the User attacked with. /// public IEntity ItemInHand { get; set; } /// /// Entity that was attacked. This can be null if the attack did not click on an entity. /// public IEntity Attacked { get; } /// /// Location that the user clicked outside of their interaction range. /// public GridCoordinates ClickLocation { get; } public AfterAttackMessage(IEntity user, IEntity itemInHand, IEntity attacked, GridCoordinates clickLocation) { User = user; Attacked = attacked; ClickLocation = clickLocation; ItemInHand = itemInHand; } } /// /// Raised when using the entity in your hands. /// [PublicAPI] public class UseInHandMessage : EntitySystemMessage { /// /// If this message has already been "handled" by a previous system. /// public bool Handled { get; set; } /// /// Entity holding the item in their hand. /// public IEntity User { get; } /// /// Item that was used. /// public IEntity Used { get; } public UseInHandMessage(IEntity user, IEntity used) { User = user; Used = used; } } /// /// Raised when throwing the entity in your hands. /// [PublicAPI] public class ThrownMessage : EntitySystemMessage { /// /// If this message has already been "handled" by a previous system. /// public bool Handled { get; set; } /// /// Entity that threw the item. /// public IEntity User { get; } /// /// Item that was thrown. /// public IEntity Thrown { get; } public ThrownMessage(IEntity user, IEntity thrown) { User = user; Thrown = thrown; } } /// /// Raised when an entity that was thrown lands. /// [PublicAPI] public class LandMessage : EntitySystemMessage { /// /// If this message has already been "handled" by a previous system. /// public bool Handled { get; set; } /// /// Entity that threw the item. /// public IEntity User { get; } /// /// Item that was thrown. /// public IEntity Thrown { get; } /// /// Location where the item landed. /// public GridCoordinates LandLocation { get; } public LandMessage(IEntity user, IEntity thrown, GridCoordinates landLocation) { User = user; Thrown = thrown; LandLocation = landLocation; } } /// /// Raised when equipping the entity in an inventory slot. /// [PublicAPI] public class EquippedMessage : EntitySystemMessage { /// /// If this message has already been "handled" by a previous system. /// public bool Handled { get; set; } /// /// Entity that equipped the item. /// public IEntity User { get; } /// /// Item that was equipped. /// public IEntity Equipped { get; } /// /// Slot where the item was placed. /// public EquipmentSlotDefines.Slots Slot { get; } public EquippedMessage(IEntity user, IEntity equipped, EquipmentSlotDefines.Slots slot) { User = user; Equipped = equipped; Slot = slot; } } /// /// Raised when removing the entity from an inventory slot. /// [PublicAPI] public class UnequippedMessage : EntitySystemMessage { /// /// If this message has already been "handled" by a previous system. /// public bool Handled { get; set; } /// /// Entity that equipped the item. /// public IEntity User { get; } /// /// Item that was equipped. /// public IEntity Equipped { get; } /// /// Slot where the item was removed from. /// public EquipmentSlotDefines.Slots Slot { get; } public UnequippedMessage(IEntity user, IEntity equipped, EquipmentSlotDefines.Slots slot) { User = user; Equipped = equipped; Slot = slot; } } /// /// Raised when an entity that was thrown lands. /// [PublicAPI] public class DroppedMessage : EntitySystemMessage { /// /// If this message has already been "handled" by a previous system. /// public bool Handled { get; set; } /// /// Entity that dropped the item. /// public IEntity User { get; } /// /// Item that was dropped. /// public IEntity Dropped { get; } public DroppedMessage(IEntity user, IEntity dropped) { User = user; Dropped = dropped; } } /// /// Raised when an entity item in a hand is selected. /// [PublicAPI] public class HandSelectedMessage : EntitySystemMessage { /// /// If this message has already been "handled" by a previous system. /// public bool Handled { get; set; } /// /// Entity that owns the selected hand. /// public IEntity User { get; } /// /// The item in question. /// public IEntity Item { get; } public HandSelectedMessage(IEntity user, IEntity item) { User = user; Item = item; } } /// /// Raised when an entity item in a hand is deselected. /// [PublicAPI] public class HandDeselectedMessage : EntitySystemMessage { /// /// If this message has already been "handled" by a previous system. /// public bool Handled { get; set; } /// /// Entity that owns the deselected hand. /// public IEntity User { get; } /// /// The item in question. /// public IEntity Item { get; } public HandDeselectedMessage(IEntity user, IEntity item) { User = user; Item = item; } } /// /// Raised when an entity is activated in the world. /// [PublicAPI] public class ActivateInWorldMessage : EntitySystemMessage { /// /// If this message has already been "handled" by a previous system. /// public bool Handled { get; set; } /// /// Entity that activated the world entity. /// public IEntity User { get; } /// /// Entity that was activated in the world. /// public IEntity Activated { get; } public ActivateInWorldMessage(IEntity user, IEntity activated) { User = user; Activated = activated; } } }