Gun spread overlay (#8588)
This commit is contained in:
18
Content.Client/Weapons/Ranged/Commands/ShowSpreadCommand.cs
Normal file
18
Content.Client/Weapons/Ranged/Commands/ShowSpreadCommand.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using Content.Client.Weapons.Ranged.Systems;
|
||||||
|
using Robust.Shared.Console;
|
||||||
|
|
||||||
|
namespace Content.Client.Weapons.Ranged;
|
||||||
|
|
||||||
|
public sealed class ShowSpreadCommand : IConsoleCommand
|
||||||
|
{
|
||||||
|
public string Command => "showspread";
|
||||||
|
public string Description => $"Shows gun spread overlay for debugging";
|
||||||
|
public string Help => $"{Command}";
|
||||||
|
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||||
|
{
|
||||||
|
var system = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<GunSystem>();
|
||||||
|
system.SpreadOverlay ^= true;
|
||||||
|
|
||||||
|
shell.WriteLine($"Set spread overlay to {system.SpreadOverlay}");
|
||||||
|
}
|
||||||
|
}
|
||||||
76
Content.Client/Weapons/Ranged/GunSpreadOverlay.cs
Normal file
76
Content.Client/Weapons/Ranged/GunSpreadOverlay.cs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
using Content.Client.Weapons.Ranged.Systems;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.Input;
|
||||||
|
using Robust.Client.Player;
|
||||||
|
using Robust.Shared.Enums;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Client.Weapons.Ranged;
|
||||||
|
|
||||||
|
public sealed class GunSpreadOverlay : Overlay
|
||||||
|
{
|
||||||
|
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||||
|
|
||||||
|
private IEntityManager _entManager;
|
||||||
|
private IEyeManager _eye;
|
||||||
|
private IGameTiming _timing;
|
||||||
|
private IInputManager _input;
|
||||||
|
private IPlayerManager _player;
|
||||||
|
private GunSystem _guns;
|
||||||
|
|
||||||
|
public GunSpreadOverlay(IEntityManager entManager, IEyeManager eyeManager, IGameTiming timing, IInputManager input, IPlayerManager player, GunSystem system)
|
||||||
|
{
|
||||||
|
_entManager = entManager;
|
||||||
|
_eye = eyeManager;
|
||||||
|
_input = input;
|
||||||
|
_timing = timing;
|
||||||
|
_player = player;
|
||||||
|
_guns = system;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
|
{
|
||||||
|
var worldHandle = args.WorldHandle;
|
||||||
|
|
||||||
|
var player = _player.LocalPlayer?.ControlledEntity;
|
||||||
|
|
||||||
|
if (player == null ||
|
||||||
|
!_entManager.TryGetComponent<TransformComponent>(player, out var xform)) return;
|
||||||
|
|
||||||
|
var mapPos = xform.MapPosition;
|
||||||
|
|
||||||
|
if (mapPos.MapId == MapId.Nullspace) return;
|
||||||
|
|
||||||
|
var gun = _guns.GetGun(player.Value);
|
||||||
|
|
||||||
|
if (gun == null) return;
|
||||||
|
|
||||||
|
var mouseScreenPos = _input.MouseScreenPosition;
|
||||||
|
var mousePos = _eye.ScreenToMap(mouseScreenPos);
|
||||||
|
|
||||||
|
if (mapPos.MapId != mousePos.MapId) return;
|
||||||
|
|
||||||
|
// (☞゚ヮ゚)☞
|
||||||
|
var maxSpread = gun.MaxAngle;
|
||||||
|
var minSpread = gun.MinAngle;
|
||||||
|
var timeSinceLastFire = (_timing.CurTime - gun.NextFire).TotalSeconds;
|
||||||
|
var currentAngle = new Angle(MathHelper.Clamp(gun.CurrentAngle.Theta - gun.AngleDecay.Theta * timeSinceLastFire,
|
||||||
|
gun.MinAngle.Theta, gun.MaxAngle.Theta));
|
||||||
|
var direction = (mousePos.Position - mapPos.Position);
|
||||||
|
|
||||||
|
worldHandle.DrawLine(mapPos.Position, mousePos.Position + direction, Color.Orange);
|
||||||
|
|
||||||
|
// Show max spread either side
|
||||||
|
worldHandle.DrawLine(mapPos.Position, mousePos.Position + maxSpread.RotateVec(direction), Color.Red);
|
||||||
|
worldHandle.DrawLine(mapPos.Position, mousePos.Position + (-maxSpread).RotateVec(direction), Color.Red);
|
||||||
|
|
||||||
|
// Show min spread either side
|
||||||
|
worldHandle.DrawLine(mapPos.Position, mousePos.Position + minSpread.RotateVec(direction), Color.Green);
|
||||||
|
worldHandle.DrawLine(mapPos.Position, mousePos.Position + (-minSpread).RotateVec(direction), Color.Green);
|
||||||
|
|
||||||
|
// Show current angle
|
||||||
|
worldHandle.DrawLine(mapPos.Position, mousePos.Position + currentAngle.RotateVec(direction), Color.Yellow);
|
||||||
|
worldHandle.DrawLine(mapPos.Position, mousePos.Position + (-currentAngle).RotateVec(direction), Color.Yellow);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ using Robust.Shared.Audio;
|
|||||||
using Robust.Shared.Input;
|
using Robust.Shared.Input;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using SharedGunSystem = Content.Shared.Weapons.Ranged.Systems.SharedGunSystem;
|
using SharedGunSystem = Content.Shared.Weapons.Ranged.Systems.SharedGunSystem;
|
||||||
|
|
||||||
@@ -27,6 +28,34 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
[Dependency] private readonly EffectSystem _effects = default!;
|
[Dependency] private readonly EffectSystem _effects = default!;
|
||||||
[Dependency] private readonly InputSystem _inputSystem = default!;
|
[Dependency] private readonly InputSystem _inputSystem = default!;
|
||||||
|
|
||||||
|
public bool SpreadOverlay
|
||||||
|
{
|
||||||
|
get => _spreadOverlay;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_spreadOverlay == value) return;
|
||||||
|
_spreadOverlay = value;
|
||||||
|
var overlayManager = IoCManager.Resolve<IOverlayManager>();
|
||||||
|
|
||||||
|
if (_spreadOverlay)
|
||||||
|
{
|
||||||
|
overlayManager.AddOverlay(new GunSpreadOverlay(
|
||||||
|
EntityManager,
|
||||||
|
IoCManager.Resolve<IEyeManager>(),
|
||||||
|
IoCManager.Resolve<IGameTiming>(),
|
||||||
|
IoCManager.Resolve<IInputManager>(),
|
||||||
|
IoCManager.Resolve<IPlayerManager>(),
|
||||||
|
this));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
overlayManager.RemoveOverlay<GunSpreadOverlay>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _spreadOverlay;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|||||||
@@ -196,8 +196,10 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
component.LastFire = component.NextFire;
|
component.LastFire = component.NextFire;
|
||||||
|
|
||||||
// Convert it so angle can go either side.
|
// Convert it so angle can go either side.
|
||||||
var random = Random.NextGaussian(0, 0.5);
|
var random = Random.NextFloat(-0.5f, 0.5f);
|
||||||
|
var spread = component.CurrentAngle.Theta * random;
|
||||||
var angle = new Angle(direction.Theta + component.CurrentAngle.Theta * random);
|
var angle = new Angle(direction.Theta + component.CurrentAngle.Theta * random);
|
||||||
|
DebugTools.Assert(spread <= component.MaxAngle.Theta);
|
||||||
return angle;
|
return angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,13 +56,13 @@ public class GunComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum angle allowed for <see cref="CurrentAngle"/>
|
/// The maximum angle allowed for <see cref="CurrentAngle"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables, DataField("maxAngle")]
|
[ViewVariables(VVAccess.ReadWrite), DataField("maxAngle")]
|
||||||
public Angle MaxAngle = Angle.FromDegrees(2);
|
public Angle MaxAngle = Angle.FromDegrees(2);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The minimum angle allowed for <see cref="CurrentAngle"/>
|
/// The minimum angle allowed for <see cref="CurrentAngle"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables, DataField("minAngle")]
|
[ViewVariables(VVAccess.ReadWrite), DataField("minAngle")]
|
||||||
public Angle MinAngle = Angle.FromDegrees(1);
|
public Angle MinAngle = Angle.FromDegrees(1);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@@ -120,6 +120,10 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
args.State = new GunComponentState
|
args.State = new GunComponentState
|
||||||
{
|
{
|
||||||
|
FireRate = component.FireRate,
|
||||||
|
CurrentAngle = component.CurrentAngle,
|
||||||
|
MinAngle = component.MinAngle,
|
||||||
|
MaxAngle = component.MaxAngle,
|
||||||
NextFire = component.NextFire,
|
NextFire = component.NextFire,
|
||||||
ShotCounter = component.ShotCounter,
|
ShotCounter = component.ShotCounter,
|
||||||
SelectiveFire = component.SelectedMode,
|
SelectiveFire = component.SelectedMode,
|
||||||
@@ -132,13 +136,17 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
if (args.Current is not GunComponentState state) return;
|
if (args.Current is not GunComponentState state) return;
|
||||||
|
|
||||||
Sawmill.Debug($"Handle state: setting shot count from {component.ShotCounter} to {state.ShotCounter}");
|
Sawmill.Debug($"Handle state: setting shot count from {component.ShotCounter} to {state.ShotCounter}");
|
||||||
|
component.FireRate = state.FireRate;
|
||||||
|
component.CurrentAngle = state.CurrentAngle;
|
||||||
|
component.MinAngle = state.MinAngle;
|
||||||
|
component.MaxAngle = state.MaxAngle;
|
||||||
component.NextFire = state.NextFire;
|
component.NextFire = state.NextFire;
|
||||||
component.ShotCounter = state.ShotCounter;
|
component.ShotCounter = state.ShotCounter;
|
||||||
component.SelectedMode = state.SelectiveFire;
|
component.SelectedMode = state.SelectiveFire;
|
||||||
component.AvailableModes = state.AvailableSelectiveFire;
|
component.AvailableModes = state.AvailableSelectiveFire;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected GunComponent? GetGun(EntityUid entity)
|
public GunComponent? GetGun(EntityUid entity)
|
||||||
{
|
{
|
||||||
if (!EntityManager.TryGetComponent(entity, out SharedHandsComponent? hands) ||
|
if (!EntityManager.TryGetComponent(entity, out SharedHandsComponent? hands) ||
|
||||||
hands.ActiveHandEntity is not { } held)
|
hands.ActiveHandEntity is not { } held)
|
||||||
@@ -358,7 +366,11 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
protected sealed class GunComponentState : ComponentState
|
protected sealed class GunComponentState : ComponentState
|
||||||
{
|
{
|
||||||
|
public Angle CurrentAngle;
|
||||||
|
public Angle MinAngle;
|
||||||
|
public Angle MaxAngle;
|
||||||
public TimeSpan NextFire;
|
public TimeSpan NextFire;
|
||||||
|
public float FireRate;
|
||||||
public int ShotCounter;
|
public int ShotCounter;
|
||||||
public SelectiveFire SelectiveFire;
|
public SelectiveFire SelectiveFire;
|
||||||
public SelectiveFire AvailableSelectiveFire;
|
public SelectiveFire AvailableSelectiveFire;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
- toggledecals
|
- toggledecals
|
||||||
- nodevis
|
- nodevis
|
||||||
- nodevisfilter
|
- nodevisfilter
|
||||||
|
- showspread
|
||||||
- showambient
|
- showambient
|
||||||
|
|
||||||
- Flags: MAPPING
|
- Flags: MAPPING
|
||||||
|
|||||||
Reference in New Issue
Block a user