Added a component that lets ranged weapons make melee attacks. (#29484)

This commit is contained in:
Sigil
2025-02-20 14:52:28 +07:00
committed by GitHub
parent 1079da4600
commit 3ce81d35be
2 changed files with 78 additions and 40 deletions

View File

@@ -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<GunComponent>(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<GunComponent>(weaponUid, out var gun) && gun.UseKey)
{
if (!TryComp<AltFireMeleeComponent>(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)
{

View File

@@ -0,0 +1,22 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Weapons.Melee.Components;
/// <summary>
/// This is used to allow ranged weapons to make melee attacks by right-clicking.
/// </summary>
[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
}