diff --git a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs index dc86580369..c4fef410c4 100644 --- a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs @@ -6,6 +6,7 @@ using Content.Shared.Hands.Components; using Content.Shared.Mobs.Components; using Content.Shared.StatusEffect; using Content.Shared.Weapons.Melee; +using Content.Shared.Weapons.Melee.Components; using Content.Shared.Weapons.Melee.Events; using Content.Shared.Weapons.Ranged.Components; using Robust.Client.GameObjects; @@ -89,16 +90,6 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem // TODO using targeted actions while combat mode is enabled should NOT trigger attacks. - // TODO: Need to make alt-fire melee its own component I guess? - // Melee and guns share a lot in the middle but share virtually nothing at the start and end so - // it's kinda tricky. - // I think as long as we make secondaries their own component it's probably fine - // as long as guncomp has an alt-use key then it shouldn't be too much of a PITA to deal with. - if (TryComp(weaponUid, out var gun) && gun.UseKey) - { - return; - } - var mousePos = _eyeManager.PixelToMap(_inputManager.MouseScreenPosition); if (mousePos.MapId == MapId.Nullspace) @@ -116,6 +107,30 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem { coordinates = TransformSystem.ToCoordinates(_map.GetMap(mousePos.MapId), mousePos); } + + // If the gun has AltFireComponent, it can be used to attack. + if (TryComp(weaponUid, out var gun) && gun.UseKey) + { + if (!TryComp(weaponUid, out var altFireComponent) || altDown != BoundKeyState.Down) + return; + + switch(altFireComponent.AttackType) + { + case AltFireAttackType.Light: + ClientLightAttack(entity, mousePos, coordinates, weaponUid, weapon); + break; + + case AltFireAttackType.Heavy: + ClientHeavyAttack(entity, coordinates, weaponUid, weapon); + break; + + case AltFireAttackType.Disarm: + ClientDisarm(entity, mousePos, coordinates); + break; + } + + return; + } // Heavy attack. if (altDown == BoundKeyState.Down) @@ -123,14 +138,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem // If it's an unarmed attack then do a disarm if (weapon.AltDisarm && weaponUid == entity) { - EntityUid? target = null; - - if (_stateManager.CurrentState is GameplayStateBase screen) - { - target = screen.GetClickedEntity(mousePos); - } - - EntityManager.RaisePredictiveEvent(new DisarmAttackEvent(GetNetEntity(target), GetNetCoordinates(coordinates))); + ClientDisarm(entity, mousePos, coordinates); return; } @@ -140,28 +148,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem // Light attack if (useDown == BoundKeyState.Down) - { - var attackerPos = TransformSystem.GetMapCoordinates(entity); - - if (mousePos.MapId != attackerPos.MapId || - (attackerPos.Position - mousePos.Position).Length() > weapon.Range) - { - return; - } - - EntityUid? target = null; - - if (_stateManager.CurrentState is GameplayStateBase screen) - { - target = screen.GetClickedEntity(mousePos); - } - - // Don't light-attack if interaction will be handling this instead - if (Interaction.CombatModeCanHandInteract(entity, target)) - return; - - RaisePredictiveEvent(new LightAttackEvent(GetNetEntity(target), GetNetEntity(weaponUid), GetNetCoordinates(coordinates))); - } + ClientLightAttack(entity, mousePos, coordinates, weaponUid, weapon); } protected override bool InRange(EntityUid user, EntityUid target, float range, ICommonSession? session) @@ -235,6 +222,35 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem var entities = GetNetEntityList(ArcRayCast(userPos, direction.ToWorldAngle(), component.Angle, distance, userXform.MapID, user).ToList()); RaisePredictiveEvent(new HeavyAttackEvent(GetNetEntity(meleeUid), entities.GetRange(0, Math.Min(MaxTargets, entities.Count)), GetNetCoordinates(coordinates))); } + + private void ClientDisarm(EntityUid attacker, MapCoordinates mousePos, EntityCoordinates coordinates) + { + EntityUid? target = null; + + if (_stateManager.CurrentState is GameplayStateBase screen) + target = screen.GetClickedEntity(mousePos); + + RaisePredictiveEvent(new DisarmAttackEvent(GetNetEntity(target), GetNetCoordinates(coordinates))); + } + + private void ClientLightAttack(EntityUid attacker, MapCoordinates mousePos, EntityCoordinates coordinates, EntityUid weaponUid, MeleeWeaponComponent meleeComponent) + { + var attackerPos = TransformSystem.GetMapCoordinates(attacker); + + if (mousePos.MapId != attackerPos.MapId || (attackerPos.Position - mousePos.Position).Length() > meleeComponent.Range) + return; + + EntityUid? target = null; + + if (_stateManager.CurrentState is GameplayStateBase screen) + target = screen.GetClickedEntity(mousePos); + + // Don't light-attack if interaction will be handling this instead + if (Interaction.CombatModeCanHandInteract(attacker, target)) + return; + + RaisePredictiveEvent(new LightAttackEvent(GetNetEntity(target), GetNetEntity(weaponUid), GetNetCoordinates(coordinates))); + } private void OnMeleeLunge(MeleeLungeEvent ev) { diff --git a/Content.Shared/Weapons/Melee/Components/AltFireMeleeComponent.cs b/Content.Shared/Weapons/Melee/Components/AltFireMeleeComponent.cs new file mode 100644 index 0000000000..998d8f63f4 --- /dev/null +++ b/Content.Shared/Weapons/Melee/Components/AltFireMeleeComponent.cs @@ -0,0 +1,22 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Melee.Components; + +/// +/// This is used to allow ranged weapons to make melee attacks by right-clicking. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedMeleeWeaponSystem))] +public sealed partial class AltFireMeleeComponent : Component +{ + [DataField, AutoNetworkedField] + public AltFireAttackType AttackType = AltFireAttackType.Light; +} + + +[Flags] +public enum AltFireAttackType : byte +{ + Light = 0, // Standard single-target attack. + Heavy = 1 << 0, // Wide swing. + Disarm = 1 << 1 +}