diff --git a/Content.Client/Atmos/AtmosDebugOverlay.cs b/Content.Client/Atmos/AtmosDebugOverlay.cs index 5ce73a66a0..31ce537e7f 100644 --- a/Content.Client/Atmos/AtmosDebugOverlay.cs +++ b/Content.Client/Atmos/AtmosDebugOverlay.cs @@ -54,7 +54,7 @@ namespace Content.Client.Atmos var gridBounds = new Box2(mapGrid.WorldToLocal(worldBounds.BottomLeft), mapGrid.WorldToLocal(worldBounds.TopRight)); - for (var pass = 0; pass < 3; pass++) + for (var pass = 0; pass < 2; pass++) { foreach (var tile in mapGrid.GetTilesIntersecting(gridBounds)) { @@ -64,17 +64,65 @@ namespace Content.Client.Atmos var data = (SharedAtmosDebugOverlaySystem.AtmosDebugOverlayData) dataMaybeNull!; if (pass == 0) { + // -- Mole Count -- float total = 0; - foreach (float f in data.Moles) - { - total += f; + switch (_atmosDebugOverlaySystem.CfgMode) { + case AtmosDebugOverlayMode.TotalMoles: + foreach (float f in data.Moles) + { + total += f; + } + break; + case AtmosDebugOverlayMode.GasMoles: + total = data.Moles[_atmosDebugOverlaySystem.CfgSpecificGas]; + break; + case AtmosDebugOverlayMode.Temperature: + total = data.Temperature; + break; } - var interp = total / (Atmospherics.MolesCellStandard * 2); - var res = Color.InterpolateBetween(Color.Red, Color.Green, interp).WithAlpha(0.75f); + var interp = ((total - _atmosDebugOverlaySystem.CfgBase) / _atmosDebugOverlaySystem.CfgScale); + Color res; + if (_atmosDebugOverlaySystem.CfgCBM) + { + // Greyscale interpolation + res = Color.InterpolateBetween(Color.Black, Color.White, interp); + } + else + { + // Red-Green-Blue interpolation + if (interp < 0.5f) + { + res = Color.InterpolateBetween(Color.Red, Color.Green, interp * 2); + } + else + { + res = Color.InterpolateBetween(Color.Green, Color.Blue, (interp - 0.5f) * 2); + } + } + res = res.WithAlpha(0.75f); drawHandle.DrawRect(Box2.FromDimensions(mapGrid.LocalToWorld(new Vector2(tile.X, tile.Y)), new Vector2(1, 1)), res); } else if (pass == 1) { + // -- Blocked Directions -- + void CheckAndShowBlockDir(AtmosDirection dir) + { + if (data.BlockDirection.HasFlag(dir)) + { + var atmosAngle = dir.ToAngle(); + var atmosAngleOfs = atmosAngle.ToVec() * 0.45f; + var atmosAngleOfsR90 = new Vector2(atmosAngleOfs.Y, -atmosAngleOfs.X); + var tileCentre = new Vector2(tile.X + 0.5f, tile.Y + 0.5f); + var basisA = mapGrid.LocalToWorld(tileCentre + atmosAngleOfs - atmosAngleOfsR90); + var basisB = mapGrid.LocalToWorld(tileCentre + atmosAngleOfs + atmosAngleOfsR90); + drawHandle.DrawLine(basisA, basisB, Color.Azure); + } + } + CheckAndShowBlockDir(AtmosDirection.North); + CheckAndShowBlockDir(AtmosDirection.South); + CheckAndShowBlockDir(AtmosDirection.East); + CheckAndShowBlockDir(AtmosDirection.West); + // -- Pressure Direction -- if (data.PressureDirection != AtmosDirection.Invalid) { var atmosAngle = data.PressureDirection.ToAngle(); @@ -84,9 +132,7 @@ namespace Content.Client.Atmos var basisB = mapGrid.LocalToWorld(tileCentre + atmosAngleOfs); drawHandle.DrawLine(basisA, basisB, Color.Blue); } - } - else if (pass == 2) - { + // -- Excited Groups -- if (data.InExcitedGroup) { var tilePos = new Vector2(tile.X, tile.Y); diff --git a/Content.Client/Commands/AtmosDebugCommands.cs b/Content.Client/Commands/AtmosDebugCommands.cs new file mode 100644 index 0000000000..823d1183e7 --- /dev/null +++ b/Content.Client/Commands/AtmosDebugCommands.cs @@ -0,0 +1,125 @@ +using JetBrains.Annotations; +using Robust.Client.Interfaces.Console; +using Content.Client.GameObjects.EntitySystems; +using Robust.Shared.GameObjects.Systems; +using Content.Shared.Atmos; +using System; + +namespace Content.Client.Commands +{ + [UsedImplicitly] + internal sealed class AtvRangeCommand : IConsoleCommand + { + public string Command => "atvrange"; + public string Description => "Sets the atmos debug range (as two floats, start [red] and end [blue])"; + public string Help => "atvrange "; + public bool Execute(IDebugConsole console, params string[] args) + { + if (args.Length != 2) + { + console.AddLine(Help); + return false; + } + if (!float.TryParse(args[0], out var xStart)) + { + console.AddLine("Bad float START"); + return false; + } + if (!float.TryParse(args[1], out var xEnd)) + { + console.AddLine("Bad float END"); + return false; + } + if (xStart == xEnd) + { + console.AddLine("Scale cannot be zero, as this would cause a division by zero in AtmosDebugOverlay."); + return false; + } + var sys = EntitySystem.Get(); + sys.CfgBase = xStart; + sys.CfgScale = xEnd - xStart; + return false; + } + } + + [UsedImplicitly] + internal sealed class AtvModeCommand : IConsoleCommand + { + public string Command => "atvmode"; + public string Description => "Sets the atmos debug mode. This will automatically reset the scale."; + public string Help => "atvmode []"; + public bool Execute(IDebugConsole console, params string[] args) + { + if (args.Length < 1) + { + console.AddLine(Help); + return false; + } + if (!Enum.TryParse(args[0], out var xMode)) + { + console.AddLine("Invalid mode"); + return false; + } + int xSpecificGas = 0; + float xBase = 0; + float xScale = Atmospherics.MolesCellStandard * 2; + if (xMode == AtmosDebugOverlayMode.GasMoles) + { + if (args.Length != 2) + { + console.AddLine("A target gas must be provided for this mode."); + return false; + } + if (!AtmosCommandUtils.TryParseGasID(args[1], out xSpecificGas)) + { + console.AddLine("Gas ID not parsable or out of range."); + return false; + } + } + else + { + if (args.Length != 1) + { + console.AddLine("No further information is required for this mode."); + return false; + } + if (xMode == AtmosDebugOverlayMode.Temperature) + { + // Red is 100C, Green is 20C, Blue is -60C + xBase = Atmospherics.T20C + 80; + xScale = -160; + } + } + var sys = EntitySystem.Get(); + sys.CfgMode = xMode; + sys.CfgSpecificGas = xSpecificGas; + sys.CfgBase = xBase; + sys.CfgScale = xScale; + return false; + } + } + + [UsedImplicitly] + internal sealed class AtvCBMCommand : IConsoleCommand + { + public string Command => "atvcbm"; + public string Description => "Changes from red/green/blue to greyscale"; + public string Help => "atvcbm "; + public bool Execute(IDebugConsole console, params string[] args) + { + if (args.Length != 1) + { + console.AddLine(Help); + return false; + } + if (!bool.TryParse(args[0], out var xFlag)) + { + console.AddLine("Invalid flag"); + return false; + } + var sys = EntitySystem.Get(); + sys.CfgCBM = xFlag; + return false; + } + } +} diff --git a/Content.Client/GameObjects/EntitySystems/AtmosDebugOverlaySystem.cs b/Content.Client/GameObjects/EntitySystems/AtmosDebugOverlaySystem.cs index 411e63b285..4a3fa1ed35 100644 --- a/Content.Client/GameObjects/EntitySystems/AtmosDebugOverlaySystem.cs +++ b/Content.Client/GameObjects/EntitySystems/AtmosDebugOverlaySystem.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Content.Client.Atmos; using Content.Shared.GameObjects.EntitySystems.Atmos; +using Content.Shared.Atmos; using Content.Shared.GameTicking; using JetBrains.Annotations; using Robust.Client.Interfaces.Graphics.Overlays; @@ -20,6 +21,19 @@ namespace Content.Client.GameObjects.EntitySystems private readonly Dictionary _tileData = new(); + // Configuration set by debug commands and used by AtmosDebugOverlay { + /// Value source for display + public AtmosDebugOverlayMode CfgMode; + /// This is subtracted from value (applied before CfgScale) + public float CfgBase = 0; + /// The value is divided by this (applied after CfgBase) + public float CfgScale = Atmospherics.MolesCellStandard * 2; + /// Gas ID used by GasMoles mode + public int CfgSpecificGas = 0; + /// Uses black-to-white interpolation (as opposed to red-green-blue) for colourblind users + public bool CfgCBM = false; + // } + public override void Initialize() { base.Initialize(); @@ -83,4 +97,11 @@ namespace Content.Client.GameObjects.EntitySystems return srcMsg.OverlayData[relative.X + (relative.Y * LocalViewRange)]; } } + + internal enum AtmosDebugOverlayMode + { + TotalMoles, + GasMoles, + Temperature + } } diff --git a/Content.Server/Atmos/AtmosCommands.cs b/Content.Server/Atmos/AtmosCommands.cs index 19b3151f4a..546b2e4b63 100644 --- a/Content.Server/Atmos/AtmosCommands.cs +++ b/Content.Server/Atmos/AtmosCommands.cs @@ -144,20 +144,13 @@ namespace Content.Server.Atmos public string Help => "addgas "; public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args) { - var gasId = -1; - var gas = (Gas) (-1); if (args.Length < 5) return; if(!int.TryParse(args[0], out var x) || !int.TryParse(args[1], out var y) || !int.TryParse(args[2], out var id) - || !(int.TryParse(args[3], out gasId) || Enum.TryParse(args[3], true, out gas)) + || !(AtmosCommandUtils.TryParseGasID(args[3], out var gasId)) || !float.TryParse(args[4], out var moles)) return; - if (gas != (Gas) (-1)) - { - gasId = (int)gas; - } - var gridId = new GridId(id); var mapMan = IoCManager.Resolve(); @@ -198,14 +191,7 @@ namespace Content.Server.Atmos return; } - if (gasId != -1) - { - tile.Air.AdjustMoles(gasId, moles); - gam.Invalidate(indices); - return; - } - - tile.Air.AdjustMoles(gas, moles); + tile.Air.AdjustMoles(gasId, moles); gam.Invalidate(indices); } } @@ -218,16 +204,11 @@ namespace Content.Server.Atmos public string Help => "fillgas "; public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args) { - var gasId = -1; - var gas = (Gas) (-1); if (args.Length < 3) return; if(!int.TryParse(args[0], out var id) - || !(int.TryParse(args[1], out gasId) || Enum.TryParse(args[1], true, out gas)) + || !(AtmosCommandUtils.TryParseGasID(args[1], out var gasId)) || !float.TryParse(args[2], out var moles)) return; - if (gas != (Gas) (-1)) - gasId = (int)gas; - var gridId = new GridId(id); var mapMan = IoCManager.Resolve(); @@ -256,14 +237,7 @@ namespace Content.Server.Atmos foreach (var tile in gam) { - if (gasId != -1) - { - tile.Air?.AdjustMoles(gasId, moles); - gam.Invalidate(tile.GridIndices); - continue; - } - - tile.Air?.AdjustMoles(gas, moles); + tile.Air?.AdjustMoles(gasId, moles); gam.Invalidate(tile.GridIndices); } } diff --git a/Content.Server/GameObjects/EntitySystems/Atmos/AtmosDebugOverlaySystem.cs b/Content.Server/GameObjects/EntitySystems/Atmos/AtmosDebugOverlaySystem.cs index 04e782b05d..c663740a83 100644 --- a/Content.Server/GameObjects/EntitySystems/Atmos/AtmosDebugOverlaySystem.cs +++ b/Content.Server/GameObjects/EntitySystems/Atmos/AtmosDebugOverlaySystem.cs @@ -104,7 +104,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos var gases = new float[Atmospherics.TotalNumberOfGases]; if (tile?.Air == null) { - return new AtmosDebugOverlayData(0, gases, AtmosDirection.Invalid, false); + return new AtmosDebugOverlayData(0, gases, AtmosDirection.Invalid, false, tile?.BlockedAirflow ?? AtmosDirection.Invalid); } else { @@ -112,7 +112,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos { gases[i] = tile.Air.GetMoles(i); } - return new AtmosDebugOverlayData(tile.Air.Temperature, gases, tile.PressureDirectionForDebugOverlay, tile.ExcitedGroup != null); + return new AtmosDebugOverlayData(tile.Air.Temperature, gases, tile.PressureDirectionForDebugOverlay, tile.ExcitedGroup != null, tile.BlockedAirflow); } } diff --git a/Content.Shared/Atmos/AtmosCommandUtils.cs b/Content.Shared/Atmos/AtmosCommandUtils.cs new file mode 100644 index 0000000000..4190efbbf7 --- /dev/null +++ b/Content.Shared/Atmos/AtmosCommandUtils.cs @@ -0,0 +1,28 @@ +#nullable enable +using System; +using Content.Shared.Atmos; + +namespace Content.Shared.Atmos +{ + public class AtmosCommandUtils + { + /// + /// Gas ID parser for atmospherics commands. + /// This is so there's a central place for this logic for if the Gas enum gets removed. + /// + public static bool TryParseGasID(string str, out int x) + { + x = -1; + if (Enum.TryParse(str, true, out var gas)) + { + x = (int) gas; + } + else + { + if (!int.TryParse(str, out x)) + return false; + } + return ((x >= 0) && (x < Atmospherics.TotalNumberOfGases)); + } + } +} \ No newline at end of file diff --git a/Content.Shared/GameObjects/EntitySystems/Atmos/SharedAtmosDebugOverlaySystem.cs b/Content.Shared/GameObjects/EntitySystems/Atmos/SharedAtmosDebugOverlaySystem.cs index 1d13d02e54..f65dffc54b 100644 --- a/Content.Shared/GameObjects/EntitySystems/Atmos/SharedAtmosDebugOverlaySystem.cs +++ b/Content.Shared/GameObjects/EntitySystems/Atmos/SharedAtmosDebugOverlaySystem.cs @@ -23,13 +23,15 @@ namespace Content.Shared.GameObjects.EntitySystems.Atmos public readonly float[] Moles; public readonly AtmosDirection PressureDirection; public readonly bool InExcitedGroup; + public readonly AtmosDirection BlockDirection; - public AtmosDebugOverlayData(float temperature, float[] moles, AtmosDirection pressureDirection, bool inExcited) + public AtmosDebugOverlayData(float temperature, float[] moles, AtmosDirection pressureDirection, bool inExcited, AtmosDirection blockDirection) { Temperature = temperature; Moles = moles; PressureDirection = pressureDirection; InExcitedGroup = inExcited; + BlockDirection = blockDirection; } }