diff --git a/Content.Client/Input/ContentContexts.cs b/Content.Client/Input/ContentContexts.cs index 1e32bfee30..0afb8b0767 100644 --- a/Content.Client/Input/ContentContexts.cs +++ b/Content.Client/Input/ContentContexts.cs @@ -24,6 +24,7 @@ namespace Content.Client.Input human.AddFunction(ContentKeyFunctions.ActivateItemInWorld); human.AddFunction(ContentKeyFunctions.ThrowItemInHand); human.AddFunction(ContentKeyFunctions.OpenContextMenu); + human.AddFunction(ContentKeyFunctions.ToggleCombatMode); var ghost = contexts.New("ghost", "common"); ghost.AddFunction(EngineKeyFunctions.MoveUp); diff --git a/Content.Server/EntryPoint.cs b/Content.Server/EntryPoint.cs index 1b930d76fe..f1109b69e2 100644 --- a/Content.Server/EntryPoint.cs +++ b/Content.Server/EntryPoint.cs @@ -186,6 +186,8 @@ namespace Content.Server factory.Register(); factory.Register(); + factory.Register(); + IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); diff --git a/Content.Server/GameObjects/Components/Mobs/CombatModeComponent.cs b/Content.Server/GameObjects/Components/Mobs/CombatModeComponent.cs new file mode 100644 index 0000000000..3b962f03d7 --- /dev/null +++ b/Content.Server/GameObjects/Components/Mobs/CombatModeComponent.cs @@ -0,0 +1,18 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.ViewVariables; + +namespace Content.Server.GameObjects.Components.Mobs +{ + /// + /// Stores whether an entity is in "combat mode" + /// This is used to differentiate between regular item interactions or + /// using *everything* as a weapon. + /// + public sealed class CombatModeComponent : Component + { + public override string Name => "CombatMode"; + + [ViewVariables(VVAccess.ReadWrite)] + public bool IsInCombatMode { get; set; } + } +} diff --git a/Content.Server/GameObjects/Components/Weapon/Melee/MeleeWeaponComponent.cs b/Content.Server/GameObjects/Components/Weapon/Melee/MeleeWeaponComponent.cs index 18b3304576..e37772a956 100644 --- a/Content.Server/GameObjects/Components/Weapon/Melee/MeleeWeaponComponent.cs +++ b/Content.Server/GameObjects/Components/Weapon/Melee/MeleeWeaponComponent.cs @@ -9,7 +9,7 @@ using Robust.Shared.Interfaces.Map; namespace Content.Server.GameObjects.Components.Weapon.Melee { - public class MeleeWeaponComponent : Component, IAfterAttack + public class MeleeWeaponComponent : Component, IAttack { #pragma warning disable 649 [Dependency] private readonly IMapManager _mapManager; @@ -31,7 +31,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee serializer.DataField(ref ArcWidth, "arcwidth", 90); } - void IAfterAttack.AfterAttack(AfterAttackEventArgs eventArgs) + void IAttack.Attack(AttackEventArgs eventArgs) { var location = eventArgs.User.Transform.GridPosition; var angle = new Angle(eventArgs.ClickLocation.ToWorld(_mapManager).Position - location.ToWorld(_mapManager).Position); @@ -42,9 +42,9 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee if (!entity.Transform.IsMapTransform || entity == eventArgs.User) continue; - if (entity.TryGetComponent(out DamageableComponent damagecomponent)) + if (entity.TryGetComponent(out DamageableComponent damageComponent)) { - damagecomponent.TakeDamage(DamageType.Brute, Damage); + damageComponent.TakeDamage(DamageType.Brute, Damage); } } } diff --git a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs index 602b359cb6..762fd5c5bc 100644 --- a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using Content.Server.GameObjects.Components.Mobs; using Content.Server.Interfaces.GameObjects; using Content.Shared.Input; using JetBrains.Annotations; @@ -124,6 +125,23 @@ namespace Content.Server.GameObjects.EntitySystems public IEntity User { get; set; } } + 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; } + } + /// /// Governs interactions during clicking on entities /// @@ -143,7 +161,7 @@ namespace Content.Server.GameObjects.EntitySystems inputSys.BindMap.BindFunction(ContentKeyFunctions.UseItemInHand, new PointerInputCmdHandler(HandleUseItemInHand)); inputSys.BindMap.BindFunction(ContentKeyFunctions.ActivateItemInWorld, - new PointerInputCmdHandler((HandleActivateItemInWorld))); + new PointerInputCmdHandler(HandleActivateItemInWorld)); } public void HandleActivateItemInWorld(ICommonSession session, GridCoordinates coords, EntityUid uid) @@ -199,7 +217,16 @@ namespace Content.Server.GameObjects.EntitySystems return; } - UserInteraction(((IPlayerSession) session).AttachedEntity, coords, uid); + var userEntity = ((IPlayerSession) session).AttachedEntity; + + if (userEntity.TryGetComponent(out CombatModeComponent combatMode) && combatMode.IsInCombatMode) + { + DoAttack(userEntity, coords, uid); + } + else + { + UserInteraction(userEntity, coords, uid); + } } private void UserInteraction(IEntity player, GridCoordinates coordinates, EntityUid clickedUid) @@ -479,6 +506,37 @@ namespace Content.Server.GameObjects.EntitySystems afterAttack.AfterAttack(afterAttackEventArgs); } } + + private void DoAttack(IEntity player, GridCoordinates coordinates, EntityUid uid) + { + // Verify player is on the same map as the entity he clicked on + if (_mapManager.GetGrid(coordinates.GridID).ParentMap.Index != 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.CanInteract(player) || item == null) + { + return; + } + + var eventArgs = new AttackEventArgs(player, coordinates); + foreach (var attackComponent in item.GetAllComponents()) + { + attackComponent.Attack(eventArgs); + } + } } /// diff --git a/Content.Server/GameObjects/EntitySystems/CombatModeSystem.cs b/Content.Server/GameObjects/EntitySystems/CombatModeSystem.cs new file mode 100644 index 0000000000..2e94e1eead --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/CombatModeSystem.cs @@ -0,0 +1,35 @@ +using Content.Server.GameObjects.Components.Mobs; +using Content.Shared.Input; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Server.Interfaces.Player; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Input; +using Robust.Shared.Players; + +namespace Content.Server.GameObjects.EntitySystems +{ + public sealed class CombatModeSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + + var inputSystem = EntitySystemManager.GetEntitySystem(); + inputSystem.BindMap.BindFunction(ContentKeyFunctions.ToggleCombatMode, + InputCmdHandler.FromDelegate(CombatModeToggled)); + } + + private void CombatModeToggled(ICommonSession session) + { + var playerSession = (IPlayerSession) session; + + if (playerSession.AttachedEntity == null || + !playerSession.AttachedEntity.TryGetComponent(out CombatModeComponent combatModeComponent)) + { + return; + } + + combatModeComponent.IsInCombatMode = !combatModeComponent.IsInCombatMode; + } + } +} diff --git a/Content.Shared/Input/ContentKeyFunctions.cs b/Content.Shared/Input/ContentKeyFunctions.cs index 64074f43db..1b357b02fc 100644 --- a/Content.Shared/Input/ContentKeyFunctions.cs +++ b/Content.Shared/Input/ContentKeyFunctions.cs @@ -15,5 +15,6 @@ namespace Content.Shared.Input public static readonly BoundKeyFunction ThrowItemInHand = "ThrowItemInHand"; public static readonly BoundKeyFunction OpenContextMenu = "OpenContextMenu"; public static readonly BoundKeyFunction FocusChat = "FocusChatWindow"; + public static readonly BoundKeyFunction ToggleCombatMode = "ToggleCombatMode"; } } diff --git a/Resources/Prototypes/Entities/Mobs.yml b/Resources/Prototypes/Entities/Mobs.yml index 3f8c8e439b..d78b70406d 100644 --- a/Resources/Prototypes/Entities/Mobs.yml +++ b/Resources/Prototypes/Entities/Mobs.yml @@ -64,6 +64,8 @@ visuals: - type: SpeciesVisualizer2D + - type: CombatMode + - type: entity id: MobObserver name: Observer diff --git a/Resources/keybinds.yml b/Resources/keybinds.yml index b0b87c8a81..fe384557e3 100644 --- a/Resources/keybinds.yml +++ b/Resources/keybinds.yml @@ -73,3 +73,6 @@ binds: - function: OpenContextMenu key: MouseRight type: state +- function: ToggleCombatMode + type: Toggle + key: Tab