diff --git a/.github/workflows/build-docfx.yml b/.github/workflows/build-docfx.yml index ca1a6f0af12..1c4b543743d 100644 --- a/.github/workflows/build-docfx.yml +++ b/.github/workflows/build-docfx.yml @@ -21,7 +21,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v3.2.0 with: - dotnet-version: 8.0.x + dotnet-version: 7.0.x - name: Install dependencies run: dotnet restore diff --git a/.github/workflows/build-map-renderer.yml b/.github/workflows/build-map-renderer.yml index 35aed1a7f7f..e921bd2558c 100644 --- a/.github/workflows/build-map-renderer.yml +++ b/.github/workflows/build-map-renderer.yml @@ -36,7 +36,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v3.2.0 with: - dotnet-version: 8.0.x + dotnet-version: 7.0.x - name: Install dependencies run: dotnet restore diff --git a/.github/workflows/build-test-debug.yml b/.github/workflows/build-test-debug.yml index 47f9fd1a514..9abd4fbe17e 100644 --- a/.github/workflows/build-test-debug.yml +++ b/.github/workflows/build-test-debug.yml @@ -36,7 +36,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v3.2.0 with: - dotnet-version: 8.0.x + dotnet-version: 7.0.x - name: Install dependencies run: dotnet restore diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1ff4c49d901..32c4954f9f1 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -22,7 +22,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v3.2.0 with: - dotnet-version: 8.0.x + dotnet-version: 7.0.x - name: Get Engine Tag run: | diff --git a/.github/workflows/test-packaging.yml b/.github/workflows/test-packaging.yml index 2dce502697d..b22f307de57 100644 --- a/.github/workflows/test-packaging.yml +++ b/.github/workflows/test-packaging.yml @@ -51,7 +51,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v3.2.0 with: - dotnet-version: 8.0.x + dotnet-version: 7.0.x - name: Install dependencies run: dotnet restore @@ -64,7 +64,7 @@ jobs: - name: Package client run: dotnet run --project Content.Packaging client --no-wipe-release - + - name: Update Build Info run: Tools/gen_build_info.py diff --git a/.github/workflows/update-credits.yml b/.github/workflows/update-credits.yml index fec053dc1b9..e0333096c41 100644 --- a/.github/workflows/update-credits.yml +++ b/.github/workflows/update-credits.yml @@ -23,30 +23,10 @@ jobs: # TODO #- name: Get this week's Patreons - # run: Tools/script2dumppatreons > Resources/Credits/Patrons.yml - - # MAKE SURE YOU ENABLED "Allow GitHub Actions to create and approve pull requests" IN YOUR ACTIONS, OTHERWISE IT WILL MOST LIKELY FAIL + # run: Tools/script2dumppatreons > Resources/Credits/Patrons.yml - - # For this you can use a pat token of an account with direct push access to the repo if you have protected branches. - # Uncomment this and comment the other line if you do this. - # https://github.com/stefanzweifel/git-auto-commit-action#push-to-protected-branches - - #- name: Commit new credit files - # uses: stefanzweifel/git-auto-commit-action@v4 - # with: - # commit_message: Update Credits - # commit_author: PJBot - - # This will make a PR - - name: Set current date as env variable - run: echo "NOW=$(date +'%Y-%m-%dT%H-%M-%S')" >> $GITHUB_ENV - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v5 + - name: Commit new credit files + uses: stefanzweifel/git-auto-commit-action@v4 with: - commit-message: Update Credits - title: Update Credits - body: This is an automated Pull Request. This PR updates the github contributors in the credits section. - author: DeltaV-Bot - branch: automated/credits-${{env.NOW}} + commit_message: Update Credits + commit_author: DeltaV-Bot diff --git a/.github/workflows/yaml-linter.yml b/.github/workflows/yaml-linter.yml index 691eb29f1de..254384acff0 100644 --- a/.github/workflows/yaml-linter.yml +++ b/.github/workflows/yaml-linter.yml @@ -26,7 +26,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v3.2.0 with: - dotnet-version: 8.0.x + dotnet-version: 7.0.x - name: Install dependencies run: dotnet restore - name: Build diff --git a/Content.Benchmarks/Content.Benchmarks.csproj b/Content.Benchmarks/Content.Benchmarks.csproj index 049d6f5b6f4..a8b255c71b0 100644 --- a/Content.Benchmarks/Content.Benchmarks.csproj +++ b/Content.Benchmarks/Content.Benchmarks.csproj @@ -8,7 +8,7 @@ false Exe true - 12 + 11 diff --git a/Content.Benchmarks/EntityQueryBenchmark.cs b/Content.Benchmarks/EntityQueryBenchmark.cs deleted file mode 100644 index cef6a5e35c5..00000000000 --- a/Content.Benchmarks/EntityQueryBenchmark.cs +++ /dev/null @@ -1,137 +0,0 @@ -#nullable enable -using System; -using System.Threading.Tasks; -using BenchmarkDotNet.Attributes; -using Content.IntegrationTests; -using Content.IntegrationTests.Pair; -using Content.Shared.Clothing.Components; -using Content.Shared.Item; -using Robust.Server.GameObjects; -using Robust.Shared; -using Robust.Shared.Analyzers; -using Robust.Shared.GameObjects; -using Robust.Shared.Map; -using Robust.Shared.Random; - -namespace Content.Benchmarks; - -[Virtual] -public class EntityQueryBenchmark -{ - public const string Map = "Maps/atlas.yml"; - - private TestPair _pair = default!; - private IEntityManager _entMan = default!; - private MapId _mapId = new MapId(10); - private EntityQuery _clothingQuery; - - [GlobalSetup] - public void Setup() - { - ProgramShared.PathOffset = "../../../../"; - PoolManager.Startup(null); - - _pair = PoolManager.GetServerClient().GetAwaiter().GetResult(); - _entMan = _pair.Server.ResolveDependency(); - - _pair.Server.ResolveDependency().SetSeed(42); - _pair.Server.WaitPost(() => - { - var success = _entMan.System().TryLoad(_mapId, Map, out _); - if (!success) - throw new Exception("Map load failed"); - _pair.Server.MapMan.DoMapInitialize(_mapId); - }).GetAwaiter().GetResult(); - - _clothingQuery = _entMan.GetEntityQuery(); - - // Apparently ~40% of entities are items, and 1 in 6 of those are clothing. - /* - var entCount = _entMan.EntityCount; - var itemCount = _entMan.Count(); - var clothingCount = _entMan.Count(); - var itemRatio = (float) itemCount / entCount; - var clothingRatio = (float) clothingCount / entCount; - Console.WriteLine($"Entities: {entCount}. Items: {itemRatio:P2}. Clothing: {clothingRatio:P2}."); - */ - } - - [GlobalCleanup] - public async Task Cleanup() - { - await _pair.DisposeAsync(); - PoolManager.Shutdown(); - } - - [Benchmark] - public int HasComponent() - { - var hashCode = 0; - var enumerator = _entMan.AllEntityQueryEnumerator(); - while (enumerator.MoveNext(out var uid, out var _)) - { - if (_entMan.HasComponent(uid)) - hashCode = HashCode.Combine(hashCode, uid.Id); - } - - return hashCode; - } - - [Benchmark] - public int HasComponentQuery() - { - var hashCode = 0; - var enumerator = _entMan.AllEntityQueryEnumerator(); - while (enumerator.MoveNext(out var uid, out var _)) - { - if (_clothingQuery.HasComponent(uid)) - hashCode = HashCode.Combine(hashCode, uid.Id); - } - - return hashCode; - } - - [Benchmark] - public int TryGetComponent() - { - var hashCode = 0; - var enumerator = _entMan.AllEntityQueryEnumerator(); - while (enumerator.MoveNext(out var uid, out var _)) - { - if (_entMan.TryGetComponent(uid, out ClothingComponent? clothing)) - hashCode = HashCode.Combine(hashCode, clothing.GetHashCode()); - } - - return hashCode; - } - - [Benchmark] - public int TryGetComponentQuery() - { - var hashCode = 0; - var enumerator = _entMan.AllEntityQueryEnumerator(); - while (enumerator.MoveNext(out var uid, out var _)) - { - if (_clothingQuery.TryGetComponent(uid, out var clothing)) - hashCode = HashCode.Combine(hashCode, clothing.GetHashCode()); - } - - return hashCode; - } - - /// - /// Enumerate all entities with both an item and clothing component. - /// - [Benchmark] - public int Enumerator() - { - var hashCode = 0; - var enumerator = _entMan.AllEntityQueryEnumerator(); - while (enumerator.MoveNext(out var _, out var clothing)) - { - hashCode = HashCode.Combine(hashCode, clothing.GetHashCode()); - } - - return hashCode; - } -} diff --git a/Content.Client/Actions/ActionsSystem.cs b/Content.Client/Actions/ActionsSystem.cs index 508f3404bac..b67c1bd5b97 100644 --- a/Content.Client/Actions/ActionsSystem.cs +++ b/Content.Client/Actions/ActionsSystem.cs @@ -87,8 +87,6 @@ private void BaseHandleState(EntityUid uid, BaseActionComponent component, Ba component.Cooldown = state.Cooldown; component.UseDelay = state.UseDelay; component.Charges = state.Charges; - component.MaxCharges = state.MaxCharges; - component.RenewCharges = state.RenewCharges; component.Container = EnsureEntity(state.Container, uid); component.EntityIcon = EnsureEntity(state.EntityIcon, uid); component.CheckCanInteract = state.CheckCanInteract; diff --git a/Content.Client/Actions/UI/ActionAlertTooltip.cs b/Content.Client/Actions/UI/ActionAlertTooltip.cs index ddc498b6e91..f48350d7722 100644 --- a/Content.Client/Actions/UI/ActionAlertTooltip.cs +++ b/Content.Client/Actions/UI/ActionAlertTooltip.cs @@ -21,7 +21,7 @@ public sealed class ActionAlertTooltip : PanelContainer /// public (TimeSpan Start, TimeSpan End)? Cooldown { get; set; } - public ActionAlertTooltip(FormattedMessage name, FormattedMessage? desc, string? requires = null, FormattedMessage? charges = null) + public ActionAlertTooltip(FormattedMessage name, FormattedMessage? desc, string? requires = null) { _gameTiming = IoCManager.Resolve(); @@ -52,17 +52,6 @@ public ActionAlertTooltip(FormattedMessage name, FormattedMessage? desc, string? vbox.AddChild(description); } - if (charges != null && !string.IsNullOrWhiteSpace(charges.ToString())) - { - var chargesLabel = new RichTextLabel - { - MaxWidth = TooltipTextMaxWidth, - StyleClasses = { StyleNano.StyleClassTooltipActionCharges } - }; - chargesLabel.SetMessage(charges); - vbox.AddChild(chargesLabel); - } - vbox.AddChild(_cooldownLabel = new RichTextLabel { MaxWidth = TooltipTextMaxWidth, diff --git a/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs b/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs index 050262cc991..39749f8ac6f 100644 --- a/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs +++ b/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs @@ -122,7 +122,7 @@ private void GenerateButton(ListData data, ListContainerButton button) } } }); - + button.EnableAllKeybinds = true; button.AddStyleClass(ListContainer.StyleClassListContainerButton); } } diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml index 0f6975e3656..92d5278ab5c 100644 --- a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml +++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml @@ -1,5 +1,6 @@  + xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls" + EnableAllKeybinds="True"> + xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls" + EnableAllKeybinds="True"> + - + diff --git a/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTabHeader.xaml.cs b/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTabHeader.xaml.cs index cf7ceff23c4..98de6dafa98 100644 --- a/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTabHeader.xaml.cs +++ b/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTabHeader.xaml.cs @@ -7,7 +7,7 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab; [GenerateTypedNameReferences] -public sealed partial class PlayerTabHeader : Control +public sealed partial class PlayerTabHeader : ContainerButton { public event Action
? OnHeaderClicked; diff --git a/Content.Client/Atmos/EntitySystems/AtmosDebugOverlaySystem.cs b/Content.Client/Atmos/EntitySystems/AtmosDebugOverlaySystem.cs index b63d274bdca..c849abf70ef 100644 --- a/Content.Client/Atmos/EntitySystems/AtmosDebugOverlaySystem.cs +++ b/Content.Client/Atmos/EntitySystems/AtmosDebugOverlaySystem.cs @@ -1,16 +1,22 @@ +using System.Collections.Generic; using Content.Client.Atmos.Overlays; using Content.Shared.Atmos; using Content.Shared.Atmos.EntitySystems; using Content.Shared.GameTicking; using JetBrains.Annotations; using Robust.Client.Graphics; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Maths; namespace Content.Client.Atmos.EntitySystems { [UsedImplicitly] internal sealed class AtmosDebugOverlaySystem : SharedAtmosDebugOverlaySystem { - public readonly Dictionary TileData = new(); + + private readonly Dictionary _tileData = + new(); // Configuration set by debug commands and used by AtmosDebugOverlay { /// Value source for display @@ -42,20 +48,20 @@ public override void Initialize() private void OnGridRemoved(GridRemovalEvent ev) { - if (TileData.ContainsKey(ev.EntityUid)) + if (_tileData.ContainsKey(ev.EntityUid)) { - TileData.Remove(ev.EntityUid); + _tileData.Remove(ev.EntityUid); } } private void HandleAtmosDebugOverlayMessage(AtmosDebugOverlayMessage message) { - TileData[GetEntity(message.GridId)] = message; + _tileData[GetEntity(message.GridId)] = message; } private void HandleAtmosDebugOverlayDisableMessage(AtmosDebugOverlayDisableMessage ev) { - TileData.Clear(); + _tileData.Clear(); } public override void Shutdown() @@ -68,12 +74,24 @@ public override void Shutdown() public void Reset(RoundRestartCleanupEvent ev) { - TileData.Clear(); + _tileData.Clear(); } public bool HasData(EntityUid gridId) { - return TileData.ContainsKey(gridId); + return _tileData.ContainsKey(gridId); + } + + public AtmosDebugOverlayData? GetData(EntityUid gridIndex, Vector2i indices) + { + if (!_tileData.TryGetValue(gridIndex, out var srcMsg)) + return null; + + var relative = indices - srcMsg.BaseIdx; + if (relative.X < 0 || relative.Y < 0 || relative.X >= LocalViewRange || relative.Y >= LocalViewRange) + return null; + + return srcMsg.OverlayData[relative.X + relative.Y * LocalViewRange]; } } diff --git a/Content.Client/Atmos/Overlays/AtmosDebugOverlay.cs b/Content.Client/Atmos/Overlays/AtmosDebugOverlay.cs index fcf3b04e530..72adf276bff 100644 --- a/Content.Client/Atmos/Overlays/AtmosDebugOverlay.cs +++ b/Content.Client/Atmos/Overlays/AtmosDebugOverlay.cs @@ -1,272 +1,187 @@ -using System.Linq; using System.Numerics; using Content.Client.Atmos.EntitySystems; -using Content.Client.Resources; using Content.Shared.Atmos; +using Content.Shared.Atmos.EntitySystems; using Robust.Client.Graphics; -using Robust.Client.Input; -using Robust.Client.ResourceManagement; -using Robust.Client.UserInterface; -using Robust.Client.UserInterface.CustomControls; using Robust.Shared.Enums; using Robust.Shared.Map; using Robust.Shared.Map.Components; -using AtmosDebugOverlayData = Content.Shared.Atmos.EntitySystems.SharedAtmosDebugOverlaySystem.AtmosDebugOverlayData; -using DebugMessage = Content.Shared.Atmos.EntitySystems.SharedAtmosDebugOverlaySystem.AtmosDebugOverlayMessage; -namespace Content.Client.Atmos.Overlays; - - -public sealed class AtmosDebugOverlay : Overlay +namespace Content.Client.Atmos.Overlays { - [Dependency] private readonly IEntityManager _entManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly IInputManager _input = default!; - [Dependency] private readonly IUserInterfaceManager _ui = default!; - [Dependency] private readonly IResourceCache _cache = default!; - private readonly SharedTransformSystem _transform; - private readonly AtmosDebugOverlaySystem _system; - private readonly SharedMapSystem _map; - private readonly Font _font; - private List<(Entity, DebugMessage)> _grids = new(); - - public override OverlaySpace Space => OverlaySpace.WorldSpace | OverlaySpace.ScreenSpace; - - internal AtmosDebugOverlay(AtmosDebugOverlaySystem system) - { - IoCManager.InjectDependencies(this); - - _system = system; - _transform = _entManager.System(); - _map = _entManager.System(); - _font = _cache.GetFont("/Fonts/NotoSans/NotoSans-Regular.ttf", 12); - } - - protected override void Draw(in OverlayDrawArgs args) - { - if (args.Space == OverlaySpace.ScreenSpace) - { - DrawTooltip(args); - return; - } - - var handle = args.WorldHandle; - GetGrids(args.MapId, args.WorldBounds); - - // IF YOU ARE ABOUT TO INTRODUCE CHUNKING OR SOME OTHER OPTIMIZATION INTO THIS CODE: - // -- THINK! -- - // 1. "Is this going to make a critical atmos debugging tool harder to debug itself?" - // 2. "Is this going to do anything that could cause the atmos debugging tool to use resources, server-side or client-side, when nobody's using it?" - // 3. "Is this going to make it harder for atmos programmers to add data that may not be chunk-friendly into the atmos debugger?" - // Nanotrasen needs YOU! to avoid premature optimization in critical debugging tools - 20kdc - - foreach (var (grid, msg) in _grids) - { - handle.SetTransform(_transform.GetWorldMatrix(grid)); - DrawData(msg, handle); - } - - handle.SetTransform(Matrix3.Identity); - } - - private void DrawData(DebugMessage msg, - DrawingHandleWorld handle) - { - foreach (var data in msg.OverlayData) - { - if (data != null) - DrawGridTile(data.Value, handle); - } - } - - private void DrawGridTile(AtmosDebugOverlayData data, - DrawingHandleWorld handle) - { - DrawFill(data, handle); - DrawBlocked(data, handle); - } - - private void DrawFill(AtmosDebugOverlayData data, DrawingHandleWorld handle) + public sealed class AtmosDebugOverlay : Overlay { - var tile = data.Indices; - var fill = GetFillData(data); - var interp = (fill - _system.CfgBase) / _system.CfgScale; - - Color res; - if (_system.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.LimeGreen, interp * 2); - } - else - { - res = Color.InterpolateBetween(Color.LimeGreen, Color.Blue, (interp - 0.5f) * 2); - } - } + private readonly AtmosDebugOverlaySystem _atmosDebugOverlaySystem; - res = res.WithAlpha(0.75f); - handle.DrawRect(Box2.FromDimensions(new Vector2(tile.X, tile.Y), new Vector2(1, 1)), res); - } + [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly IMapManager _mapManager = default!; - private float GetFillData(AtmosDebugOverlayData data) - { - if (data.Moles == null) - return 0; + public override OverlaySpace Space => OverlaySpace.WorldSpace; + private List> _grids = new(); - switch (_system.CfgMode) + internal AtmosDebugOverlay(AtmosDebugOverlaySystem system) { - case AtmosDebugOverlayMode.TotalMoles: - var total = 0f; - foreach (var f in data.Moles) - { - total += f; - } - - return total; - case AtmosDebugOverlayMode.GasMoles: - return data.Moles[_system.CfgSpecificGas]; - default: - return data.Temperature; - } - } - - private void DrawBlocked(AtmosDebugOverlayData data, DrawingHandleWorld handle) - { - var tile = data.Indices; - var tileCentre = tile + 0.5f * Vector2.One; - CheckAndShowBlockDir(data, handle, AtmosDirection.North, tileCentre); - CheckAndShowBlockDir(data, handle, AtmosDirection.South, tileCentre); - CheckAndShowBlockDir(data, handle, AtmosDirection.East, tileCentre); - CheckAndShowBlockDir(data, handle, AtmosDirection.West, tileCentre); + IoCManager.InjectDependencies(this); - // -- Pressure Direction -- - if (data.PressureDirection != AtmosDirection.Invalid) - { - DrawPressureDirection(handle, data.PressureDirection, tileCentre, Color.Blue); - } - else if (data.LastPressureDirection != AtmosDirection.Invalid) - { - DrawPressureDirection(handle, data.LastPressureDirection, tileCentre, Color.LightGray); + _atmosDebugOverlaySystem = system; } - // -- Excited Groups -- - if (data.InExcitedGroup is {} grp) + protected override void Draw(in OverlayDrawArgs args) { - var basisA = tile; - var basisB = tile + new Vector2(1.0f, 1.0f); - var basisC = tile + new Vector2(0.0f, 1.0f); - var basisD = tile + new Vector2(1.0f, 0.0f); - var color = Color.White // Use first three nibbles for an unique color... Good enough? - .WithRed(grp & 0x000F) - .WithGreen((grp & 0x00F0) >> 4) - .WithBlue((grp & 0x0F00) >> 8); - handle.DrawLine(basisA, basisB, color); - handle.DrawLine(basisC, basisD, color); - } - - if (data.IsSpace) - handle.DrawCircle(tileCentre, 0.15f, Color.Yellow); + var drawHandle = args.WorldHandle; - if (data.MapAtmosphere) - handle.DrawCircle(tileCentre, 0.1f, Color.Orange); - - if (data.NoGrid) - handle.DrawCircle(tileCentre, 0.05f, Color.Black); - } - - private void CheckAndShowBlockDir(AtmosDebugOverlayData data, DrawingHandleWorld handle, AtmosDirection dir, - Vector2 tileCentre) - { - if (!data.BlockDirection.HasFlag(dir)) - return; + var mapId = args.Viewport.Eye!.Position.MapId; + var worldBounds = args.WorldBounds; - // Account for South being 0. - var atmosAngle = dir.ToAngle() - Angle.FromDegrees(90); - var atmosAngleOfs = atmosAngle.ToVec() * 0.45f; - var atmosAngleOfsR90 = new Vector2(atmosAngleOfs.Y, -atmosAngleOfs.X); - var basisA = tileCentre + atmosAngleOfs - atmosAngleOfsR90; - var basisB = tileCentre + atmosAngleOfs + atmosAngleOfsR90; - handle.DrawLine(basisA, basisB, Color.Azure); - } + // IF YOU ARE ABOUT TO INTRODUCE CHUNKING OR SOME OTHER OPTIMIZATION INTO THIS CODE: + // -- THINK! -- + // 1. "Is this going to make a critical atmos debugging tool harder to debug itself?" + // 2. "Is this going to do anything that could cause the atmos debugging tool to use resources, server-side or client-side, when nobody's using it?" + // 3. "Is this going to make it harder for atmos programmers to add data that may not be chunk-friendly into the atmos debugger?" + // Nanotrasen needs YOU! to avoid premature optimization in critical debugging tools - 20kdc - private void DrawPressureDirection( - DrawingHandleWorld handle, - AtmosDirection d, - Vector2 center, - Color color) - { - // Account for South being 0. - var atmosAngle = d.ToAngle() - Angle.FromDegrees(90); - var atmosAngleOfs = atmosAngle.ToVec() * 0.4f; - handle.DrawLine(center, center + atmosAngleOfs, color); - } + _grids.Clear(); - private void DrawTooltip(in OverlayDrawArgs args) - { - var handle = args.ScreenHandle; - var mousePos = _input.MouseScreenPosition; - if (!mousePos.IsValid) - return; + _mapManager.FindGridsIntersecting(mapId, worldBounds, ref _grids, (EntityUid uid, MapGridComponent grid, + ref List> state) => + { + state.Add((uid, grid)); + return true; + }); - if (_ui.MouseGetControl(mousePos) is not IViewportControl viewport) - return; + foreach (var (uid, mapGrid) in _grids) + { + if (!_atmosDebugOverlaySystem.HasData(uid) || + !_entManager.TryGetComponent(uid, out var xform)) + continue; - var coords= viewport.PixelToMap(mousePos.Position); - var box = Box2.CenteredAround(coords.Position, 3 * Vector2.One); - GetGrids(coords.MapId, new Box2Rotated(box)); + drawHandle.SetTransform(xform.WorldMatrix); - foreach (var (grid, msg) in _grids) - { - var index = _map.WorldToTile(grid, grid, coords.Position); - foreach (var data in msg.OverlayData) - { - if (data?.Indices == index) + for (var pass = 0; pass < 2; pass++) { - DrawTooltip(handle, mousePos.Position, data.Value); - return; + foreach (var tile in mapGrid.GetTilesIntersecting(worldBounds)) + { + var dataMaybeNull = _atmosDebugOverlaySystem.GetData(uid, tile.GridIndices); + if (dataMaybeNull != null) + { + var data = (SharedAtmosDebugOverlaySystem.AtmosDebugOverlayData) dataMaybeNull; + if (pass == 0) + { + // -- Mole Count -- + float total = 0; + switch (_atmosDebugOverlaySystem.CfgMode) + { + case AtmosDebugOverlayMode.TotalMoles: + foreach (var 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 - _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.LimeGreen, interp * 2); + } + else + { + res = Color.InterpolateBetween(Color.LimeGreen, Color.Blue, (interp - 0.5f) * 2); + } + } + res = res.WithAlpha(0.75f); + drawHandle.DrawRect(Box2.FromDimensions(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)) + { + // Account for South being 0. + var atmosAngle = dir.ToAngle() - Angle.FromDegrees(90); + 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 = tileCentre + atmosAngleOfs - atmosAngleOfsR90; + var basisB = tileCentre + atmosAngleOfs + atmosAngleOfsR90; + drawHandle.DrawLine(basisA, basisB, Color.Azure); + } + } + CheckAndShowBlockDir(AtmosDirection.North); + CheckAndShowBlockDir(AtmosDirection.South); + CheckAndShowBlockDir(AtmosDirection.East); + CheckAndShowBlockDir(AtmosDirection.West); + + void DrawPressureDirection( + DrawingHandleWorld handle, + AtmosDirection d, + TileRef t, + Color color) + { + // Account for South being 0. + var atmosAngle = d.ToAngle() - Angle.FromDegrees(90); + var atmosAngleOfs = atmosAngle.ToVec() * 0.4f; + var tileCentre = new Vector2(t.X + 0.5f, t.Y + 0.5f); + var basisA = tileCentre; + var basisB = tileCentre + atmosAngleOfs; + handle.DrawLine(basisA, basisB, color); + } + + // -- Pressure Direction -- + if (data.PressureDirection != AtmosDirection.Invalid) + { + DrawPressureDirection(drawHandle, data.PressureDirection, tile, Color.Blue); + } + else if (data.LastPressureDirection != AtmosDirection.Invalid) + { + DrawPressureDirection(drawHandle, data.LastPressureDirection, tile, Color.LightGray); + } + + var tilePos = new Vector2(tile.X, tile.Y); + + // -- Excited Groups -- + if (data.InExcitedGroup != 0) + { + var basisA = tilePos; + var basisB = tilePos + new Vector2(1.0f, 1.0f); + var basisC = tilePos + new Vector2(0.0f, 1.0f); + var basisD = tilePos + new Vector2(1.0f, 0.0f); + var color = Color.White // Use first three nibbles for an unique color... Good enough? + .WithRed( data.InExcitedGroup & 0x000F) + .WithGreen((data.InExcitedGroup & 0x00F0) >>4) + .WithBlue( (data.InExcitedGroup & 0x0F00) >>8); + drawHandle.DrawLine(basisA, basisB, color); + drawHandle.DrawLine(basisC, basisD, color); + } + + // -- Space Tiles -- + if (data.IsSpace) + { + drawHandle.DrawCircle(tilePos + Vector2.One/2, 0.125f, Color.Orange); + } + } + } + } } } - } - } - - private void DrawTooltip(DrawingHandleScreen handle, Vector2 pos, AtmosDebugOverlayData data) - { - var lineHeight = _font.GetLineHeight(1f); - var offset = new Vector2(0, lineHeight); - var moles = data.Moles == null - ? "No Air" - : data.Moles.Sum().ToString(); - - handle.DrawString(_font, pos, $"Moles: {moles}"); - pos += offset; - handle.DrawString(_font, pos, $"Temp: {data.Temperature}"); - pos += offset; - handle.DrawString(_font, pos, $"Excited: {data.InExcitedGroup?.ToString() ?? "None"}"); - pos += offset; - handle.DrawString(_font, pos, $"Space: {data.IsSpace}"); - pos += offset; - handle.DrawString(_font, pos, $"Map: {data.MapAtmosphere}"); - pos += offset; - handle.DrawString(_font, pos, $"NoGrid: {data.NoGrid}"); - } - - private void GetGrids(MapId mapId, Box2Rotated box) - { - _grids.Clear(); - _mapManager.FindGridsIntersecting(mapId, box, ref _grids, (EntityUid uid, MapGridComponent grid, - ref List<(Entity, DebugMessage)> state) => - { - if (_system.TileData.TryGetValue(uid, out var data)) - state.Add(((uid, grid), data)); - return true; - }); + drawHandle.SetTransform(Matrix3.Identity); + } } } diff --git a/Content.Client/Audio/AmbientSoundSystem.cs b/Content.Client/Audio/AmbientSoundSystem.cs index d66ee434a29..10065b6583e 100644 --- a/Content.Client/Audio/AmbientSoundSystem.cs +++ b/Content.Client/Audio/AmbientSoundSystem.cs @@ -101,7 +101,7 @@ public override void Initialize() _cfg.OnValueChanged(CCVars.AmbientCooldown, SetCooldown, true); _cfg.OnValueChanged(CCVars.MaxAmbientSources, SetAmbientCount, true); _cfg.OnValueChanged(CCVars.AmbientRange, SetAmbientRange, true); - _cfg.OnValueChanged(CCVars.AmbienceVolume, SetAmbienceGain, true); + _cfg.OnValueChanged(CCVars.AmbienceVolume, SetAmbienceVolume, true); SubscribeLocalEvent(OnShutdown); } @@ -116,9 +116,9 @@ private void OnShutdown(EntityUid uid, AmbientSoundComponent component, Componen _playingCount.Remove(sound.Path); } - private void SetAmbienceGain(float value) + private void SetAmbienceVolume(float value) { - _ambienceVolume = SharedAudioSystem.GainToVolume(value); + _ambienceVolume = value; foreach (var (comp, values) in _playingSounds) { @@ -141,7 +141,7 @@ public override void Shutdown() _cfg.UnsubValueChanged(CCVars.AmbientCooldown, SetCooldown); _cfg.UnsubValueChanged(CCVars.MaxAmbientSources, SetAmbientCount); _cfg.UnsubValueChanged(CCVars.AmbientRange, SetAmbientRange); - _cfg.UnsubValueChanged(CCVars.AmbienceVolume, SetAmbienceGain); + _cfg.UnsubValueChanged(CCVars.AmbienceVolume, SetAmbienceVolume); } private int PlayingCount(string countSound) diff --git a/Content.Client/Audio/BackgroundAudioSystem.cs b/Content.Client/Audio/BackgroundAudioSystem.cs index 09ac1efcd65..a26603bf746 100644 --- a/Content.Client/Audio/BackgroundAudioSystem.cs +++ b/Content.Client/Audio/BackgroundAudioSystem.cs @@ -14,9 +14,6 @@ namespace Content.Client.Audio; [UsedImplicitly] public sealed class BackgroundAudioSystem : EntitySystem { - /* - * TODO: Nuke this system and merge into contentaudiosystem - */ [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly IBaseClient _client = default!; [Dependency] private readonly IConfigurationManager _configManager = default!; @@ -25,7 +22,7 @@ public sealed class BackgroundAudioSystem : EntitySystem private readonly AudioParams _lobbyParams = new(-5f, 1, "Master", 0, 0, 0, true, 0f); - public EntityUid? LobbyStream; + private EntityUid? _lobbyStream; public override void Initialize() { @@ -112,7 +109,7 @@ public void RestartLobbyMusic() public void StartLobbyMusic() { - if (LobbyStream != null || !_configManager.GetCVar(CCVars.LobbyMusicEnabled)) + if (_lobbyStream != null || !_configManager.GetCVar(CCVars.LobbyMusicEnabled)) return; var file = _gameTicker.LobbySong; @@ -121,12 +118,12 @@ public void StartLobbyMusic() return; } - LobbyStream = _audio.PlayGlobal(file, Filter.Local(), false, - _lobbyParams.WithVolume(_lobbyParams.Volume + SharedAudioSystem.GainToVolume(_configManager.GetCVar(CCVars.LobbyMusicVolume))))?.Entity; + _lobbyStream = _audio.PlayGlobal(file, Filter.Local(), false, + _lobbyParams.WithVolume(_lobbyParams.Volume + _configManager.GetCVar(CCVars.LobbyMusicVolume)))?.Entity; } private void EndLobbyMusic() { - LobbyStream = _audio.Stop(LobbyStream); + _lobbyStream = _audio.Stop(_lobbyStream); } } diff --git a/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs b/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs index 0fc0c18b62b..1860d81118c 100644 --- a/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs +++ b/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs @@ -75,7 +75,7 @@ private void InitializeAmbientMusic() private void AmbienceCVarChanged(float obj) { - _volumeSlider = SharedAudioSystem.GainToVolume(obj); + _volumeSlider = obj; if (_ambientMusicStream != null && _musicProto != null) { @@ -159,7 +159,7 @@ private void UpdateAmbientMusic() // Update still runs in lobby so just ignore it. if (_state.CurrentState is not GameplayState) { - Audio.Stop(_ambientMusicStream); + FadeOut(_ambientMusicStream); _ambientMusicStream = null; _musicProto = null; return; diff --git a/Content.Client/Audio/ContentAudioSystem.cs b/Content.Client/Audio/ContentAudioSystem.cs index 603b1086d8b..726493fdab1 100644 --- a/Content.Client/Audio/ContentAudioSystem.cs +++ b/Content.Client/Audio/ContentAudioSystem.cs @@ -1,8 +1,5 @@ using Content.Shared.Audio; -using Content.Shared.CCVar; -using Content.Shared.GameTicking; using Robust.Client.GameObjects; -using Robust.Shared; using Robust.Shared.Audio; using AudioComponent = Robust.Shared.Audio.Components.AudioComponent; @@ -21,41 +18,11 @@ public sealed partial class ContentAudioSystem : SharedContentAudioSystem private const float MinVolume = -32f; private const float DefaultDuration = 2f; - /* - * Gain multipliers for specific audio sliders. - * The float value will get multiplied by this when setting - * i.e. a gain of 0.5f x 3 will equal 1.5f which is supported in OpenAL. - */ - - public const float MasterVolumeMultiplier = 3f; - public const float MidiVolumeMultiplier = 0.25f; - public const float AmbienceMultiplier = 3f; - public const float AmbientMusicMultiplier = 3f; - public const float LobbyMultiplier = 3f; - public override void Initialize() { base.Initialize(); UpdatesOutsidePrediction = true; InitializeAmbientMusic(); - SubscribeNetworkEvent(OnRoundCleanup); - } - - private void OnRoundCleanup(RoundRestartCleanupEvent ev) - { - _fadingOut.Clear(); - - // Preserve lobby music but everything else should get dumped. - var lobbyStream = EntityManager.System().LobbyStream; - TryComp(lobbyStream, out AudioComponent? audioComp); - var oldGain = audioComp?.Gain; - - SilenceAudio(); - - if (oldGain != null) - { - Audio.SetGain(lobbyStream, oldGain.Value, audioComp); - } } public override void Shutdown() diff --git a/Content.Client/Chat/UI/SpeechBubble.cs b/Content.Client/Chat/UI/SpeechBubble.cs index 91e8e5a90f0..46a9f053921 100644 --- a/Content.Client/Chat/UI/SpeechBubble.cs +++ b/Content.Client/Chat/UI/SpeechBubble.cs @@ -1,11 +1,8 @@ using System.Numerics; using Content.Client.Chat.Managers; -using Content.Shared.CCVar; -using Content.Shared.Chat; using Robust.Client.Graphics; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; -using Robust.Shared.Configuration; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -13,10 +10,6 @@ namespace Content.Client.Chat.UI { public abstract class SpeechBubble : Control { - [Dependency] private readonly IEyeManager _eyeManager = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; - [Dependency] protected readonly IConfigurationManager ConfigManager = default!; - public enum SpeechType : byte { Emote, @@ -41,12 +34,10 @@ public enum SpeechType : byte /// private const float EntityVerticalOffset = 0.5f; - /// - /// The default maximum width for speech bubbles. - /// - public const float SpeechMaxWidth = 256; - + private readonly IEyeManager _eyeManager; private readonly EntityUid _senderEntity; + private readonly IChatManager _chatManager; + private readonly IEntityManager _entityManager; private float _timeLeft = TotalTime; @@ -58,36 +49,38 @@ public enum SpeechType : byte // man down public event Action? OnDied; - public static SpeechBubble CreateSpeechBubble(SpeechType type, ChatMessage message, EntityUid senderEntity) + public static SpeechBubble CreateSpeechBubble(SpeechType type, string text, EntityUid senderEntity, IEyeManager eyeManager, IChatManager chatManager, IEntityManager entityManager) { switch (type) { case SpeechType.Emote: - return new TextSpeechBubble(message, senderEntity, "emoteBox"); + return new TextSpeechBubble(text, senderEntity, eyeManager, chatManager, entityManager, "emoteBox"); case SpeechType.Say: - return new FancyTextSpeechBubble(message, senderEntity, "sayBox"); + return new TextSpeechBubble(text, senderEntity, eyeManager, chatManager, entityManager, "sayBox"); case SpeechType.Whisper: - return new FancyTextSpeechBubble(message, senderEntity, "whisperBox"); + return new TextSpeechBubble(text, senderEntity, eyeManager, chatManager, entityManager, "whisperBox"); case SpeechType.Looc: - return new TextSpeechBubble(message, senderEntity, "emoteBox", Color.FromHex("#48d1cc")); + return new TextSpeechBubble(text, senderEntity, eyeManager, chatManager, entityManager, "emoteBox", Color.FromHex("#48d1cc")); default: throw new ArgumentOutOfRangeException(); } } - public SpeechBubble(ChatMessage message, EntityUid senderEntity, string speechStyleClass, Color? fontColor = null) + public SpeechBubble(string text, EntityUid senderEntity, IEyeManager eyeManager, IChatManager chatManager, IEntityManager entityManager, string speechStyleClass, Color? fontColor = null) { - IoCManager.InjectDependencies(this); + _chatManager = chatManager; _senderEntity = senderEntity; + _eyeManager = eyeManager; + _entityManager = entityManager; // Use text clipping so new messages don't overlap old ones being pushed up. RectClipContent = true; - var bubble = BuildBubble(message, speechStyleClass, fontColor); + var bubble = BuildBubble(text, speechStyleClass, fontColor); AddChild(bubble); @@ -98,7 +91,7 @@ public SpeechBubble(ChatMessage message, EntityUid senderEntity, string speechSt _verticalOffsetAchieved = -ContentSize.Y; } - protected abstract Control BuildBubble(ChatMessage message, string speechStyleClass, Color? fontColor = null); + protected abstract Control BuildBubble(string text, string speechStyleClass, Color? fontColor = null); protected override void FrameUpdate(FrameEventArgs args) { @@ -172,127 +165,39 @@ public void FadeNow() _timeLeft = FadeTime; } } - - protected FormattedMessage FormatSpeech(string message, Color? fontColor = null) - { - var msg = new FormattedMessage(); - if (fontColor != null) - msg.PushColor(fontColor.Value); - msg.AddMarkup(message); - return msg; - } - - protected string ExtractSpeechSubstring(ChatMessage message, string tag) - { - var rawmsg = message.WrappedMessage; - var tagStart = rawmsg.IndexOf($"[{tag}]"); - var tagEnd = rawmsg.IndexOf($"[/{tag}]"); - if (tagStart < 0 || tagEnd < 0) //the above return -1 if the tag's not found, which in turn will cause the below to throw an exception. a blank speech bubble is far more noticeably broken than the bubble not appearing at all -bhijn - return ""; - tagStart += tag.Length + 2; - return rawmsg.Substring(tagStart, tagEnd - tagStart); - } - - protected FormattedMessage ExtractAndFormatSpeechSubstring(ChatMessage message, string tag, Color? fontColor = null) - { - return FormatSpeech(ExtractSpeechSubstring(message, tag), fontColor); - } - } public sealed class TextSpeechBubble : SpeechBubble { - public TextSpeechBubble(ChatMessage message, EntityUid senderEntity, string speechStyleClass, Color? fontColor = null) - : base(message, senderEntity, speechStyleClass, fontColor) + public TextSpeechBubble(string text, EntityUid senderEntity, IEyeManager eyeManager, IChatManager chatManager, IEntityManager entityManager, string speechStyleClass, Color? fontColor = null) + : base(text, senderEntity, eyeManager, chatManager, entityManager, speechStyleClass, fontColor) { } - protected override Control BuildBubble(ChatMessage message, string speechStyleClass, Color? fontColor = null) + protected override Control BuildBubble(string text, string speechStyleClass, Color? fontColor = null) { var label = new RichTextLabel { - MaxWidth = SpeechMaxWidth, - }; - - label.SetMessage(FormatSpeech(message.WrappedMessage, fontColor)); - - var panel = new PanelContainer - { - StyleClasses = { "speechBox", speechStyleClass }, - Children = { label }, - ModulateSelfOverride = Color.White.WithAlpha(0.75f) + MaxWidth = 256, }; - return panel; - } - } - - public sealed class FancyTextSpeechBubble : SpeechBubble - { - - public FancyTextSpeechBubble(ChatMessage message, EntityUid senderEntity, string speechStyleClass, Color? fontColor = null) - : base(message, senderEntity, speechStyleClass, fontColor) - { - } - - protected override Control BuildBubble(ChatMessage message, string speechStyleClass, Color? fontColor = null) - { - if (!ConfigManager.GetCVar(CCVars.ChatEnableFancyBubbles)) + if (fontColor != null) { - var label = new RichTextLabel - { - MaxWidth = SpeechMaxWidth - }; - - label.SetMessage(ExtractAndFormatSpeechSubstring(message, "BubbleContent", fontColor)); - - var unfanciedPanel = new PanelContainer - { - StyleClasses = { "speechBox", speechStyleClass }, - Children = { label }, - ModulateSelfOverride = Color.White.WithAlpha(0.75f) - }; - return unfanciedPanel; + var msg = new FormattedMessage(); + msg.PushColor(fontColor.Value); + msg.AddMarkup(text); + label.SetMessage(msg); } - - var bubbleHeader = new RichTextLabel - { - Margin = new Thickness(1, 1, 1, 1) - }; - - var bubbleContent = new RichTextLabel - { - MaxWidth = SpeechMaxWidth, - Margin = new Thickness(2, 6, 2, 2) - }; - - //We'll be honest. *Yes* this is hacky. Doing this in a cleaner way would require a bottom-up refactor of how saycode handles sending chat messages. -Myr - bubbleHeader.SetMessage(ExtractAndFormatSpeechSubstring(message, "BubbleHeader", fontColor)); - bubbleContent.SetMessage(ExtractAndFormatSpeechSubstring(message, "BubbleContent", fontColor)); - - //As for below: Some day this could probably be converted to xaml. But that is not today. -Myr - var mainPanel = new PanelContainer - { - StyleClasses = { "speechBox", speechStyleClass }, - Children = { bubbleContent }, - ModulateSelfOverride = Color.White.WithAlpha(0.75f), - HorizontalAlignment = HAlignment.Center, - VerticalAlignment = VAlignment.Bottom, - Margin = new Thickness(4, 14, 4, 2) - }; - - var headerPanel = new PanelContainer + else { - StyleClasses = { "speechBox", speechStyleClass }, - Children = { bubbleHeader }, - ModulateSelfOverride = Color.White.WithAlpha(ConfigManager.GetCVar(CCVars.ChatFancyNameBackground) ? 0.75f : 0f), - HorizontalAlignment = HAlignment.Center, - VerticalAlignment = VAlignment.Top - }; + label.SetMessage(text); + } var panel = new PanelContainer { - Children = { mainPanel, headerPanel } + StyleClasses = { "speechBox", speechStyleClass }, + Children = { label }, + ModulateSelfOverride = Color.White.WithAlpha(0.75f) }; return panel; diff --git a/Content.Client/Chemistry/EntitySystems/SolutionContainerMixerSystem.cs b/Content.Client/Chemistry/EntitySystems/SolutionContainerMixerSystem.cs deleted file mode 100644 index b6401c113de..00000000000 --- a/Content.Client/Chemistry/EntitySystems/SolutionContainerMixerSystem.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Content.Shared.Chemistry.EntitySystems; - -namespace Content.Client.Chemistry.EntitySystems; - -/// -public sealed class SolutionContainerMixerSystem : SharedSolutionContainerMixerSystem -{ - -} diff --git a/Content.Client/Clothing/ClientClothingSystem.cs b/Content.Client/Clothing/ClientClothingSystem.cs index 979f7430e17..b16e14d6535 100644 --- a/Content.Client/Clothing/ClientClothingSystem.cs +++ b/Content.Client/Clothing/ClientClothingSystem.cs @@ -202,15 +202,17 @@ private void OnDidUnequip(EntityUid uid, SpriteComponent component, DidUnequipEv revealedLayers.Clear(); } - public void InitClothing(EntityUid uid, InventoryComponent component) + public void InitClothing(EntityUid uid, InventoryComponent? component = null, SpriteComponent? sprite = null) { - if (!TryComp(uid, out SpriteComponent? sprite)) + if (!Resolve(uid, ref sprite, ref component) || !_inventorySystem.TryGetSlots(uid, out var slots, component)) return; - var enumerator = _inventorySystem.GetSlotEnumerator((uid, component)); - while (enumerator.NextItem(out var item, out var slot)) + foreach (var slot in slots) { - RenderEquipment(uid, item, slot.Name, component, sprite); + if (!_inventorySystem.TryGetSlotContainer(uid, slot.Name, out var containerSlot, out _, component) || + !containerSlot.ContainedEntity.HasValue) continue; + + RenderEquipment(uid, containerSlot.ContainedEntity.Value, slot.Name, component, sprite); } } diff --git a/Content.Client/Construction/ConstructionSystem.cs b/Content.Client/Construction/ConstructionSystem.cs index 4035c68cc75..d0a14860f11 100644 --- a/Content.Client/Construction/ConstructionSystem.cs +++ b/Content.Client/Construction/ConstructionSystem.cs @@ -2,7 +2,6 @@ using Content.Client.Popups; using Content.Shared.Construction; using Content.Shared.Construction.Prototypes; -using Content.Shared.Construction.Steps; using Content.Shared.Examine; using Content.Shared.Input; using Content.Shared.Interaction; @@ -98,11 +97,7 @@ private void HandleConstructionGhostExamined(EntityUid uid, ConstructionGhostCom return; } - foreach (ConstructionGraphStep step in edge.Steps) - { - args.Message.PushNewline(); - step.DoExamine(args); - } + edge.Steps[0].DoExamine(args); } public event EventHandler? CraftingAvailabilityChanged; diff --git a/Content.Client/Explosion/ExplosionOverlaySystem.cs b/Content.Client/Explosion/ExplosionOverlaySystem.cs index 064b068a97e..60208ea1a0d 100644 --- a/Content.Client/Explosion/ExplosionOverlaySystem.cs +++ b/Content.Client/Explosion/ExplosionOverlaySystem.cs @@ -19,6 +19,11 @@ public sealed class ExplosionOverlaySystem : EntitySystem [Dependency] private readonly IOverlayManager _overlayMan = default!; [Dependency] private readonly SharedPointLightSystem _lights = default!; + /// + /// For how many seconds should an explosion stay on-screen once it has finished expanding? + /// + public float ExplosionPersistence = 0.3f; + public override void Initialize() { base.Initialize(); diff --git a/Content.Client/Eye/Blinding/BlurryVisionOverlay.cs b/Content.Client/Eye/Blinding/BlurryVisionOverlay.cs index 7d0e7916da2..94590b54a50 100644 --- a/Content.Client/Eye/Blinding/BlurryVisionOverlay.cs +++ b/Content.Client/Eye/Blinding/BlurryVisionOverlay.cs @@ -54,7 +54,7 @@ protected override void Draw(in OverlayDrawArgs args) // Maybe gradually shrink the view-size? // Make the effect only apply to the edge of the viewport? // Actually make it blurry?? - var opacity = 1f * _magnitude / BlurryVisionComponent.MaxMagnitude; + var opacity = 0.75f * _magnitude / BlurryVisionComponent.MaxMagnitude; var worldHandle = args.WorldHandle; var viewport = args.WorldBounds; worldHandle.SetTransform(Matrix3.Identity); diff --git a/Content.Client/Forensics/ForensicScannerMenu.xaml.cs b/Content.Client/Forensics/ForensicScannerMenu.xaml.cs index 8b6152c8612..84ffd7969e7 100644 --- a/Content.Client/Forensics/ForensicScannerMenu.xaml.cs +++ b/Content.Client/Forensics/ForensicScannerMenu.xaml.cs @@ -58,12 +58,6 @@ public void UpdateState(ForensicScannerBoundUserInterfaceState msg) { text.AppendLine(dna); } - text.AppendLine(); - text.AppendLine(Loc.GetString("forensic-scanner-interface-residues")); - foreach (var residue in msg.Residues) - { - text.AppendLine(residue); - } Diagnostics.Text = text.ToString(); } } diff --git a/Content.Client/Guidebook/Controls/GuideReagentEmbed.xaml b/Content.Client/Guidebook/Controls/GuideReagentEmbed.xaml index 7b1beeeb650..f83eb8c2407 100644 --- a/Content.Client/Guidebook/Controls/GuideReagentEmbed.xaml +++ b/Content.Client/Guidebook/Controls/GuideReagentEmbed.xaml @@ -14,12 +14,29 @@ - + HorizontalExpand="True"> + + + + + + + + + + diff --git a/Content.Client/Guidebook/Controls/GuideReagentEmbed.xaml.cs b/Content.Client/Guidebook/Controls/GuideReagentEmbed.xaml.cs index cf5a1b6e59c..5c2caf2230f 100644 --- a/Content.Client/Guidebook/Controls/GuideReagentEmbed.xaml.cs +++ b/Content.Client/Guidebook/Controls/GuideReagentEmbed.xaml.cs @@ -6,10 +6,8 @@ using Content.Client.UserInterface.ControlExtensions; using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Localizations; using JetBrains.Annotations; using Robust.Client.AutoGenerated; -using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; @@ -55,7 +53,7 @@ public bool CheckMatchesSearch(string query) public void SetHiddenState(bool state, string query) { - Visible = CheckMatchesSearch(query) ? state : !state; + this.Visible = CheckMatchesSearch(query) ? state : !state; } public bool TryParseTag(Dictionary args, [NotNullWhen(true)] out Control? control) @@ -86,11 +84,7 @@ private void GenerateControl(ReagentPrototype reagent) BackgroundColor = reagent.SubstanceColor }; - var r = reagent.SubstanceColor.R; - var g = reagent.SubstanceColor.G; - var b = reagent.SubstanceColor.B; - - var textColor = 0.2126f * r + 0.7152f * g + 0.0722f * b > 0.5 + var textColor = Color.ToHsl(reagent.SubstanceColor).Z > 0.45 ? Color.Black : Color.White; @@ -98,19 +92,49 @@ private void GenerateControl(ReagentPrototype reagent) ("color", textColor), ("name", reagent.LocalizedName))); #region Recipe - var reactions = _prototype.EnumeratePrototypes() - .Where(p => p.Products.ContainsKey(reagent.ID)) - .OrderBy(p => p.Priority) - .ThenBy(p => p.Products.Count) - .ToList(); + // by default, we assume that the reaction has the same ID as the reagent. + // if this isn't true, we'll loop through reactions. + if (!_prototype.TryIndex(reagent.ID, out var reactionPrototype)) + { + reactionPrototype = _prototype.EnumeratePrototypes() + .FirstOrDefault(p => p.Products.ContainsKey(reagent.ID)); + } - if (reactions.Any()) + if (reactionPrototype != null) { - foreach (var reactionPrototype in reactions) + var reactantMsg = new FormattedMessage(); + var reactantsCount = reactionPrototype.Reactants.Count; + var i = 0; + foreach (var (product, reactant) in reactionPrototype.Reactants) + { + reactantMsg.AddMarkup(Loc.GetString("guidebook-reagent-recipes-reagent-display", + ("reagent", _prototype.Index(product).LocalizedName), ("ratio", reactant.Amount))); + i++; + if (i < reactantsCount) + reactantMsg.PushNewline(); + } + reactantMsg.Pop(); + ReactantsLabel.SetMessage(reactantMsg); + + if (reactionPrototype.MinimumTemperature > 0.0f) { - var ctrl = GetRecipeGuide(reactionPrototype); - RecipesDescriptionContainer.AddChild(ctrl); + MixLabel.Text = Loc.GetString("guidebook-reagent-recipes-mix-and-heat", + ("temperature", reactionPrototype.MinimumTemperature)); } + + var productMsg = new FormattedMessage(); + var productCount = reactionPrototype.Products.Count; + var u = 0; + foreach (var (product, ratio) in reactionPrototype.Products) + { + productMsg.AddMarkup(Loc.GetString("guidebook-reagent-recipes-reagent-display", + ("reagent", _prototype.Index(product).LocalizedName), ("ratio", ratio))); + u++; + if (u < productCount) + productMsg.PushNewline(); + } + productMsg.Pop(); + ProductsLabel.SetMessage(productMsg); } else { @@ -162,69 +186,8 @@ private void GenerateControl(ReagentPrototype reagent) FormattedMessage description = new(); description.AddText(reagent.LocalizedDescription); description.PushNewline(); - description.AddMarkup(Loc.GetString("guidebook-reagent-physical-description", + description.AddText(Loc.GetString("guidebook-reagent-physical-description", ("description", reagent.LocalizedPhysicalDescription))); ReagentDescription.SetMessage(description); } - - private GuideReagentReaction GetRecipeGuide(ReactionPrototype reactionPrototype) - { - var control = new GuideReagentReaction(); - - var reactantMsg = new FormattedMessage(); - var reactantsCount = reactionPrototype.Reactants.Count; - var i = 0; - foreach (var (product, reactant) in reactionPrototype.Reactants) - { - reactantMsg.AddMarkup(Loc.GetString("guidebook-reagent-recipes-reagent-display", - ("reagent", _prototype.Index(product).LocalizedName), ("ratio", reactant.Amount))); - i++; - if (i < reactantsCount) - reactantMsg.PushNewline(); - } - reactantMsg.Pop(); - control.ReactantsLabel.SetMessage(reactantMsg); - - var productMsg = new FormattedMessage(); - var productCount = reactionPrototype.Products.Count; - var u = 0; - foreach (var (product, ratio) in reactionPrototype.Products) - { - productMsg.AddMarkup(Loc.GetString("guidebook-reagent-recipes-reagent-display", - ("reagent", _prototype.Index(product).LocalizedName), ("ratio", ratio))); - u++; - if (u < productCount) - productMsg.PushNewline(); - } - productMsg.Pop(); - control.ProductsLabel.SetMessage(productMsg); - - var mixingCategories = new List(); - if (reactionPrototype.MixingCategories != null) - { - foreach (var category in reactionPrototype.MixingCategories) - { - mixingCategories.Add(_prototype.Index(category)); - } - } - - // only use the first one for the icon. - if (mixingCategories.FirstOrDefault() is { } primaryCategory) - { - control.MixTexture.Texture = _systemManager.GetEntitySystem().Frame0(primaryCategory.Icon); - } - - var mixingVerb = mixingCategories.Count == 0 - ? Loc.GetString("guidebook-reagent-recipes-mix") - : ContentLocalizationManager.FormatList(mixingCategories.Select(p => Loc.GetString(p.VerbText)).ToList()); - - var text = Loc.GetString("guidebook-reagent-recipes-mix-info", - ("verb", mixingVerb), - ("minTemp", reactionPrototype.MinimumTemperature), - ("maxTemp", reactionPrototype.MaximumTemperature), - ("hasMax", !float.IsPositiveInfinity(reactionPrototype.MaximumTemperature))); - - control.MixLabel.SetMarkup(text); - return control; - } } diff --git a/Content.Client/Guidebook/Controls/GuideReagentReaction.xaml b/Content.Client/Guidebook/Controls/GuideReagentReaction.xaml deleted file mode 100644 index 69c14a59af7..00000000000 --- a/Content.Client/Guidebook/Controls/GuideReagentReaction.xaml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - diff --git a/Content.Client/Guidebook/Controls/GuideReagentReaction.xaml.cs b/Content.Client/Guidebook/Controls/GuideReagentReaction.xaml.cs deleted file mode 100644 index fbc6bf13fc4..00000000000 --- a/Content.Client/Guidebook/Controls/GuideReagentReaction.xaml.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Content.Client.UserInterface.ControlExtensions; -using JetBrains.Annotations; -using Robust.Client.AutoGenerated; -using Robust.Client.UserInterface.Controls; - -namespace Content.Client.Guidebook.Controls; - -[UsedImplicitly, GenerateTypedNameReferences] -public sealed partial class GuideReagentReaction : BoxContainer, ISearchableControl -{ - public bool CheckMatchesSearch(string query) - { - return this.ChildrenContainText(query); - } - - public void SetHiddenState(bool state, string query) - { - Visible = CheckMatchesSearch(query) ? state : !state; - } -} diff --git a/Content.Client/Info/PlaytimeStats/PlaytimeStatsEntry.xaml b/Content.Client/Info/PlaytimeStats/PlaytimeStatsEntry.xaml index 0f973b76890..97a66e5cc24 100644 --- a/Content.Client/Info/PlaytimeStats/PlaytimeStatsEntry.xaml +++ b/Content.Client/Info/PlaytimeStats/PlaytimeStatsEntry.xaml @@ -1,5 +1,6 @@ + xmlns:customControls1="clr-namespace:Content.Client.Administration.UI.CustomControls" + EnableAllKeybinds="True"> ? OnHeaderClicked; private SortDirection _roleDirection = SortDirection.Ascending; diff --git a/Content.Client/Info/PlaytimeStats/PlaytimeStatsHeader.xaml b/Content.Client/Info/PlaytimeStats/PlaytimeStatsHeader.xaml index d83e794472c..4cf4d8e2ccc 100644 --- a/Content.Client/Info/PlaytimeStats/PlaytimeStatsHeader.xaml +++ b/Content.Client/Info/PlaytimeStats/PlaytimeStatsHeader.xaml @@ -1,5 +1,6 @@ - + @@ -25,4 +26,4 @@ - + diff --git a/Content.Client/Inventory/ClientInventorySystem.cs b/Content.Client/Inventory/ClientInventorySystem.cs index d4615210f24..6976a8b5bc9 100644 --- a/Content.Client/Inventory/ClientInventorySystem.cs +++ b/Content.Client/Inventory/ClientInventorySystem.cs @@ -1,7 +1,9 @@ using Content.Client.Clothing; using Content.Client.Examine; +using Content.Client.UserInterface.Controls; using Content.Client.Verbs.UI; using Content.Shared.Clothing.Components; +using Content.Shared.Hands.Components; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Inventory; @@ -13,12 +15,14 @@ using Robust.Shared.Containers; using Robust.Shared.Input.Binding; using Robust.Shared.Player; +using Robust.Shared.Prototypes; namespace Content.Client.Inventory { [UsedImplicitly] public sealed class ClientInventorySystem : InventorySystem { + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IUserInterfaceManager _ui = default!; @@ -85,7 +89,7 @@ private void OnUseInHand(EntityUid uid, ClothingComponent component, UseInHandEv private void OnDidUnequip(InventorySlotsComponent component, DidUnequipEvent args) { UpdateSlot(args.Equipee, component, args.Slot); - if (args.Equipee != _playerManager.LocalEntity) + if (args.Equipee != _playerManager.LocalPlayer?.ControlledEntity) return; var update = new SlotSpriteUpdate(null, args.SlotGroup, args.Slot, false); OnSpriteUpdate?.Invoke(update); @@ -94,7 +98,7 @@ private void OnDidUnequip(InventorySlotsComponent component, DidUnequipEvent arg private void OnDidEquip(InventorySlotsComponent component, DidEquipEvent args) { UpdateSlot(args.Equipee, component, args.Slot); - if (args.Equipee != _playerManager.LocalEntity) + if (args.Equipee != _playerManager.LocalPlayer?.ControlledEntity) return; var update = new SlotSpriteUpdate(args.Equipment, args.SlotGroup, args.Slot, HasComp(args.Equipment)); @@ -103,8 +107,10 @@ private void OnDidEquip(InventorySlotsComponent component, DidEquipEvent args) private void OnShutdown(EntityUid uid, InventoryComponent component, ComponentShutdown args) { - if (uid == _playerManager.LocalEntity) - OnUnlinkInventory?.Invoke(); + if (uid != _playerManager.LocalPlayer?.ControlledEntity) + return; + + OnUnlinkInventory?.Invoke(); } private void OnPlayerDetached(EntityUid uid, InventorySlotsComponent component, LocalPlayerDetachedEvent args) @@ -145,10 +151,13 @@ protected override void OnInit(EntityUid uid, InventoryComponent component, Comp base.OnInit(uid, component, args); _clothingVisualsSystem.InitClothing(uid, component); - if (!TryComp(uid, out InventorySlotsComponent? inventorySlots)) + if (!_prototypeManager.TryIndex(component.TemplateId, out InventoryTemplatePrototype? invTemplate) || + !TryComp(uid, out InventorySlotsComponent? inventorySlots)) + { return; + } - foreach (var slot in component.Slots) + foreach (var slot in invTemplate.Slots) { TryAddSlotDef(uid, inventorySlots, slot); } @@ -156,7 +165,7 @@ protected override void OnInit(EntityUid uid, InventoryComponent component, Comp public void ReloadInventory(InventorySlotsComponent? component = null) { - var player = _playerManager.LocalEntity; + var player = _playerManager.LocalPlayer?.ControlledEntity; if (player == null || !Resolve(player.Value, ref component, false)) { return; @@ -170,7 +179,7 @@ public void SetSlotHighlight(EntityUid owner, InventorySlotsComponent component, { var oldData = component.SlotData[slotName]; var newData = component.SlotData[slotName] = new SlotData(oldData, state); - if (owner == _playerManager.LocalEntity) + if (owner == _playerManager.LocalPlayer?.ControlledEntity) EntitySlotUpdate?.Invoke(newData); } @@ -189,7 +198,7 @@ public void UpdateSlot(EntityUid owner, InventorySlotsComponent component, strin var newData = component.SlotData[slotName] = new SlotData(component.SlotData[slotName], newHighlight, newBlocked); - if (owner == _playerManager.LocalEntity) + if (owner == _playerManager.LocalPlayer?.ControlledEntity) EntitySlotUpdate?.Invoke(newData); } @@ -199,11 +208,48 @@ public bool TryAddSlotDef(EntityUid owner, InventorySlotsComponent component, Sl if (!component.SlotData.TryAdd(newSlotDef.Name, newSlotData)) return false; - if (owner == _playerManager.LocalEntity) + if (owner == _playerManager.LocalPlayer?.ControlledEntity) OnSlotAdded?.Invoke(newSlotData); return true; } + public void RemoveSlotDef(EntityUid owner, InventorySlotsComponent component, SlotData slotData) + { + if (component.SlotData.Remove(slotData.SlotName)) + { + if (owner == _playerManager.LocalPlayer?.ControlledEntity) + OnSlotRemoved?.Invoke(slotData); + } + } + + public void RemoveSlotDef(EntityUid owner, InventorySlotsComponent component, string slotName) + { + if (!component.SlotData.TryGetValue(slotName, out var slotData)) + return; + + component.SlotData.Remove(slotName); + + if (owner == _playerManager.LocalPlayer?.ControlledEntity) + OnSlotRemoved?.Invoke(slotData); + } + + // TODO hud refactor This should also live in a UI Controller + private void HoverInSlotButton(EntityUid uid, string slot, SlotControl control, + InventoryComponent? inventoryComponent = null, HandsComponent? hands = null) + { + if (!Resolve(uid, ref inventoryComponent)) + return; + + if (!Resolve(uid, ref hands, false)) + return; + + if (hands.ActiveHandEntity is not EntityUid heldEntity) + return; + + if (!TryGetSlotContainer(uid, slot, out var containerSlot, out var slotDef, inventoryComponent)) + return; + } + public void UIInventoryActivate(string slot) { EntityManager.RaisePredictiveEvent(new UseSlotNetworkMessage(slot)); diff --git a/Content.Client/Inventory/StrippableBoundUserInterface.cs b/Content.Client/Inventory/StrippableBoundUserInterface.cs index 9fbb64309fb..6dbb8f3c221 100644 --- a/Content.Client/Inventory/StrippableBoundUserInterface.cs +++ b/Content.Client/Inventory/StrippableBoundUserInterface.cs @@ -90,11 +90,11 @@ public void UpdateMenu() _strippingMenu.ClearButtons(); - if (EntMan.TryGetComponent(Owner, out var inv)) + if (EntMan.TryGetComponent(Owner, out var inv) && _protoMan.TryIndex(inv.TemplateId, out var template)) { - foreach (var slot in inv.Slots) + foreach (var slot in template.Slots) { - AddInventoryButton(Owner, slot.Name, inv); + AddInventoryButton(Owner, slot.Name, template, inv); } } @@ -190,7 +190,7 @@ private void SlotPressed(GUIBoundKeyEventArgs ev, SlotControl slot) _ui.GetUIController().OpenVerbMenu(slot.Entity.Value); } - private void AddInventoryButton(EntityUid invUid, string slotId, InventoryComponent inv) + private void AddInventoryButton(EntityUid invUid, string slotId, InventoryTemplatePrototype _, InventoryComponent inv) { if (!_inv.TryGetSlotContainer(invUid, slotId, out var container, out var slotDef, inv)) return; diff --git a/Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.cs b/Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.cs index 86989cd5614..31db2c9c536 100644 --- a/Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.cs +++ b/Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.cs @@ -119,7 +119,7 @@ public void UpdateUI() OverrideDirection = Direction.South, Scale = new Vector2(4f, 4f), MaxSize = new Vector2(112, 112), - Stretch = SpriteView.StretchMode.Fill, + Stretch = SpriteView.StretchMode.None, }; spriteView.SetEntity(_previewDummy.Value); _viewBox.AddChild(spriteView); diff --git a/Content.Client/Medical/CrewMonitoring/CrewMonitoringBoundUserInterface.cs b/Content.Client/Medical/CrewMonitoring/CrewMonitoringBoundUserInterface.cs index 39788809871..fc632575c73 100644 --- a/Content.Client/Medical/CrewMonitoring/CrewMonitoringBoundUserInterface.cs +++ b/Content.Client/Medical/CrewMonitoring/CrewMonitoringBoundUserInterface.cs @@ -1,56 +1,53 @@ using Content.Shared.Medical.CrewMonitoring; +using Robust.Client.GameObjects; -namespace Content.Client.Medical.CrewMonitoring; - -public sealed class CrewMonitoringBoundUserInterface : BoundUserInterface +namespace Content.Client.Medical.CrewMonitoring { - [ViewVariables] - private CrewMonitoringWindow? _menu; - - public CrewMonitoringBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + public sealed class CrewMonitoringBoundUserInterface : BoundUserInterface { - } + [ViewVariables] + private CrewMonitoringWindow? _menu; - protected override void Open() - { - EntityUid? gridUid = null; - string stationName = string.Empty; + public CrewMonitoringBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } - if (EntMan.TryGetComponent(Owner, out var xform)) + protected override void Open() { - gridUid = xform.GridUid; + EntityUid? gridUid = null; - if (EntMan.TryGetComponent(gridUid, out var metaData)) + if (EntMan.TryGetComponent(Owner, out var xform)) { - stationName = metaData.EntityName; + gridUid = xform.GridUid; } - } - _menu = new CrewMonitoringWindow(stationName, gridUid); + _menu = new CrewMonitoringWindow(gridUid); - _menu.OpenCentered(); - _menu.OnClose += Close; - } - - protected override void UpdateState(BoundUserInterfaceState state) - { - base.UpdateState(state); + _menu.OpenCentered(); + _menu.OnClose += Close; + } - switch (state) + protected override void UpdateState(BoundUserInterfaceState state) { - case CrewMonitoringState st: - EntMan.TryGetComponent(Owner, out var xform); - _menu?.ShowSensors(st.Sensors, Owner, xform?.Coordinates); - break; + base.UpdateState(state); + + switch (state) + { + case CrewMonitoringState st: + EntMan.TryGetComponent(Owner, out var xform); + + _menu?.ShowSensors(st.Sensors, xform?.Coordinates, st.Snap, st.Precision); + break; + } } - } - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - return; + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!disposing) + return; - _menu?.Dispose(); + _menu?.Dispose(); + } } } diff --git a/Content.Client/Medical/CrewMonitoring/CrewMonitoringNavMapControl.cs b/Content.Client/Medical/CrewMonitoring/CrewMonitoringNavMapControl.cs deleted file mode 100644 index e6adf13bed4..00000000000 --- a/Content.Client/Medical/CrewMonitoring/CrewMonitoringNavMapControl.cs +++ /dev/null @@ -1,79 +0,0 @@ -using Content.Client.Pinpointer.UI; -using Robust.Client.Graphics; -using Robust.Client.UserInterface.Controls; - -namespace Content.Client.Medical.CrewMonitoring; - -public sealed partial class CrewMonitoringNavMapControl : NavMapControl -{ - public NetEntity? Focus; - public Dictionary LocalizedNames = new(); - - private Color _backgroundColor; - private Label _trackedEntityLabel; - private PanelContainer _trackedEntityPanel; - - public CrewMonitoringNavMapControl() : base() - { - WallColor = new Color(192, 122, 196); - TileColor = new(71, 42, 72); - - _backgroundColor = Color.FromSrgb(TileColor.WithAlpha(0.8f)); - - _trackedEntityLabel = new Label - { - Margin = new Thickness(10f, 8f), - HorizontalAlignment = HAlignment.Center, - VerticalAlignment = VAlignment.Center, - Modulate = Color.White, - }; - - _trackedEntityPanel = new PanelContainer - { - PanelOverride = new StyleBoxFlat - { - BackgroundColor = _backgroundColor, - }, - - Margin = new Thickness(5f, 10f), - HorizontalAlignment = HAlignment.Left, - VerticalAlignment = VAlignment.Bottom, - Visible = false, - }; - - _trackedEntityPanel.AddChild(_trackedEntityLabel); - this.AddChild(_trackedEntityPanel); - } - - protected override void Draw(DrawingHandleScreen handle) - { - base.Draw(handle); - - if (Focus == null) - { - _trackedEntityLabel.Text = string.Empty; - _trackedEntityPanel.Visible = false; - - return; - } - - foreach ((var netEntity, var blip) in TrackedEntities) - { - if (netEntity != Focus) - continue; - - if (!LocalizedNames.TryGetValue(netEntity, out var name)) - name = "Unknown"; - - var message = name + "\nLocation: [x = " + MathF.Round(blip.Coordinates.X) + ", y = " + MathF.Round(blip.Coordinates.Y) + "]"; - - _trackedEntityLabel.Text = message; - _trackedEntityPanel.Visible = true; - - return; - } - - _trackedEntityLabel.Text = string.Empty; - _trackedEntityPanel.Visible = false; - } -} diff --git a/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml b/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml index 80bf5a3f8b9..559a12d63bc 100644 --- a/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml +++ b/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml @@ -1,49 +1,39 @@ - - - - - - - - - - - - - - - - + SetSize="1130 700" + MinSize="1130 700"> + + + + + + + diff --git a/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml.cs b/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml.cs index d8c87899db4..ff08af6bb6b 100644 --- a/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml.cs +++ b/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml.cs @@ -1,437 +1,275 @@ -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Numerics; -using Content.Client.Pinpointer.UI; using Content.Client.Stylesheets; using Content.Client.UserInterface.Controls; using Content.Shared.Medical.SuitSensor; -using Content.Shared.StatusIcon; using Robust.Client.AutoGenerated; -using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Map; -using Robust.Shared.Prototypes; using Robust.Shared.Timing; -using Robust.Shared.Utility; using static Robust.Client.UserInterface.Controls.BoxContainer; -namespace Content.Client.Medical.CrewMonitoring; - -[GenerateTypedNameReferences] -public sealed partial class CrewMonitoringWindow : FancyWindow +namespace Content.Client.Medical.CrewMonitoring { - private List _rowsContent = new(); - private readonly IEntityManager _entManager; - private readonly IPrototypeManager _prototypeManager; - private readonly SpriteSystem _spriteSystem; - - private NetEntity? _trackedEntity; - private bool _tryToScrollToListFocus; - private Texture? _blipTexture; - - public CrewMonitoringWindow(string stationName, EntityUid? mapUid) - { - RobustXamlLoader.Load(this); - - _entManager = IoCManager.Resolve(); - _prototypeManager = IoCManager.Resolve(); - _spriteSystem = _entManager.System(); - - _blipTexture = _spriteSystem.Frame0(new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png"))); - - if (_entManager.TryGetComponent(mapUid, out var xform)) - NavMap.MapUid = xform.GridUid; - - else - NavMap.Visible = false; - - StationName.AddStyleClass("LabelBig"); - StationName.Text = stationName; - - NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap; - NavMap.ForceNavMapUpdate(); - } - - protected override void FrameUpdate(FrameEventArgs args) + [GenerateTypedNameReferences] + public sealed partial class CrewMonitoringWindow : FancyWindow { - base.FrameUpdate(args); + private List _rowsContent = new(); + private List<(DirectionIcon Icon, Vector2 Position)> _directionIcons = new(); + private readonly IEntityManager _entManager; + private readonly IEyeManager _eye; + private EntityUid? _stationUid; + private CrewMonitoringButton? _trackedButton; - if (_tryToScrollToListFocus) - TryToScrollToFocus(); - } + public static int IconSize = 16; // XAML has a `VSeparationOverride` of 20 for each row. - public void ShowSensors(List sensors, EntityUid monitor, EntityCoordinates? monitorCoords) - { - ClearOutDatedData(); - - // No server label - if (sensors.Count == 0) + public CrewMonitoringWindow(EntityUid? mapUid) { - NoServerLabel.Visible = true; - return; - } - - NoServerLabel.Visible = false; - - // Order sensor data - var orderedSensors = sensors.OrderBy(n => n.Name).OrderBy(j => j.Job); - var assignedSensors = new HashSet(); - var departments = sensors.SelectMany(d => d.JobDepartments).Distinct().OrderBy(n => n); + RobustXamlLoader.Load(this); + _eye = IoCManager.Resolve(); + _entManager = IoCManager.Resolve(); + _stationUid = mapUid; - // Create department labels and populate lists - foreach (var department in departments) - { - var departmentSensors = orderedSensors.Where(d => d.JobDepartments.Contains(department)); - - if (departmentSensors == null || !departmentSensors.Any()) - continue; - - foreach (var sensor in departmentSensors) - assignedSensors.Add(sensor); - - if (SensorsTable.ChildCount > 0) + if (_entManager.TryGetComponent(mapUid, out var xform)) { - var spacer = new Control() - { - SetHeight = 20, - }; - - SensorsTable.AddChild(spacer); - _rowsContent.Add(spacer); + NavMap.MapUid = xform.GridUid; } - - var deparmentLabel = new RichTextLabel() + else { - Margin = new Thickness(10, 0), - HorizontalExpand = true, - }; - - deparmentLabel.SetMessage(department); - deparmentLabel.StyleClasses.Add(StyleNano.StyleClassTooltipActionDescription); - - SensorsTable.AddChild(deparmentLabel); - _rowsContent.Add(deparmentLabel); - - PopulateDepartmentList(departmentSensors); - } - - // Account for any non-station users - var remainingSensors = orderedSensors.Except(assignedSensors); - - if (remainingSensors.Any()) - { - var spacer = new Control() - { - SetHeight = 20, - }; - - SensorsTable.AddChild(spacer); - _rowsContent.Add(spacer); - - var deparmentLabel = new RichTextLabel() - { - Margin = new Thickness(10, 0), - HorizontalExpand = true, - }; - - deparmentLabel.SetMessage(Loc.GetString("crew-monitoring-user-interface-no-department")); - deparmentLabel.StyleClasses.Add(StyleNano.StyleClassTooltipActionDescription); - - SensorsTable.AddChild(deparmentLabel); - _rowsContent.Add(deparmentLabel); - - PopulateDepartmentList(remainingSensors); + NavMap.Visible = false; + SetSize = new Vector2(775, 400); + MinSize = SetSize; + } } - // Show monitor on nav map - if (monitorCoords != null && _blipTexture != null) + public void ShowSensors(List stSensors, EntityCoordinates? monitorCoords, bool snap, float precision) { - NavMap.TrackedEntities[_entManager.GetNetEntity(monitor)] = new NavMapBlip(monitorCoords.Value, _blipTexture, Color.Cyan, true, false); - } - } - - private void PopulateDepartmentList(IEnumerable departmentSensors) - { - // Populate departments - foreach (var sensor in departmentSensors) - { - var coordinates = _entManager.GetCoordinates(sensor.Coordinates); - - // Add a button that will hold a username and other details - NavMap.LocalizedNames.TryAdd(sensor.SuitSensorUid, sensor.Name + ", " + sensor.Job); - - var sensorButton = new CrewMonitoringButton() - { - SuitSensorUid = sensor.SuitSensorUid, - Coordinates = coordinates, - Disabled = (coordinates == null), - HorizontalExpand = true, - }; - - if (sensor.SuitSensorUid == _trackedEntity) - sensorButton.AddStyleClass(StyleNano.StyleClassButtonColorGreen); - - SensorsTable.AddChild(sensorButton); - _rowsContent.Add(sensorButton); - - // Primary container to hold the button UI elements - var mainContainer = new BoxContainer() - { - Orientation = LayoutOrientation.Horizontal, - HorizontalExpand = true, - }; + ClearAllSensors(); - sensorButton.AddChild(mainContainer); + var monitorCoordsInStationSpace = _stationUid != null ? monitorCoords?.WithEntityId(_stationUid.Value, _entManager).Position : null; - // User status container - var statusContainer = new BoxContainer() + // TODO scroll container + // TODO filter by name & occupation + // TODO make each row a xaml-control. Get rid of some of this c# control creation. + if (stSensors.Count == 0) { - SizeFlagsStretchRatio = 1.25f, - Orientation = LayoutOrientation.Horizontal, - HorizontalExpand = true, - }; - - mainContainer.AddChild(statusContainer); - - // Suit coords indicator - var suitCoordsIndicator = new TextureRect() - { - Texture = _blipTexture, - TextureScale = new Vector2(0.25f, 0.25f), - Modulate = coordinates != null ? Color.LimeGreen : Color.DarkRed, - HorizontalAlignment = HAlignment.Center, - VerticalAlignment = VAlignment.Center, - }; - - statusContainer.AddChild(suitCoordsIndicator); - - // Specify texture for the user status icon - var specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "alive"); - - if (!sensor.IsAlive) - { - specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "dead"); + NoServerLabel.Visible = true; + return; } + NoServerLabel.Visible = false; - else if (sensor.TotalDamage != null) + // add a row for each sensor + foreach (var sensor in stSensors.OrderBy(a => a.Name)) { - var index = MathF.Round(4f * (sensor.TotalDamage.Value / 100f)); + var sensorEntity = _entManager.GetEntity(sensor.SuitSensorUid); + var coordinates = _entManager.GetCoordinates(sensor.Coordinates); - if (index >= 5) - specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "critical"); - - else - specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "health" + index); - } + // add button with username + var nameButton = new CrewMonitoringButton() + { + SuitSensorUid = sensorEntity, + Coordinates = coordinates, + Text = sensor.Name, + Margin = new Thickness(5f, 5f), + }; + if (sensorEntity == _trackedButton?.SuitSensorUid) + nameButton.AddStyleClass(StyleNano.StyleClassButtonColorGreen); + SetColorLabel(nameButton.Label, sensor.TotalDamage, sensor.IsAlive); + SensorsTable.AddChild(nameButton); + _rowsContent.Add(nameButton); + + // add users job + // format: JobName + var jobLabel = new Label() + { + Text = sensor.Job, + HorizontalExpand = true + }; + SetColorLabel(jobLabel, sensor.TotalDamage, sensor.IsAlive); + SensorsTable.AddChild(jobLabel); + _rowsContent.Add(jobLabel); + + // add users status and damage + // format: IsAlive (TotalDamage) + var statusText = Loc.GetString(sensor.IsAlive ? + "crew-monitoring-user-interface-alive" : + "crew-monitoring-user-interface-dead"); + if (sensor.TotalDamage != null) + { + statusText += $" ({sensor.TotalDamage})"; + } + var statusLabel = new Label() + { + Text = statusText + }; + SetColorLabel(statusLabel, sensor.TotalDamage, sensor.IsAlive); + SensorsTable.AddChild(statusLabel); + _rowsContent.Add(statusLabel); - // Status icon - var statusIcon = new AnimatedTextureRect - { - HorizontalAlignment = HAlignment.Center, - VerticalAlignment = VAlignment.Center, - Margin = new Thickness(0, 1, 3, 0), - }; + // add users positions + // format: (x, y) + var box = GetPositionBox(sensor, monitorCoordsInStationSpace ?? Vector2.Zero, snap, precision); - statusIcon.SetFromSpriteSpecifier(specifier); - statusIcon.DisplayRect.TextureScale = new Vector2(2f, 2f); + SensorsTable.AddChild(box); + _rowsContent.Add(box); - statusContainer.AddChild(statusIcon); + if (coordinates != null && NavMap.Visible) + { + NavMap.TrackedCoordinates.TryAdd(coordinates.Value, + (true, sensorEntity == _trackedButton?.SuitSensorUid ? StyleNano.PointGreen : StyleNano.PointRed)); - // User name - var nameLabel = new Label() - { - Text = sensor.Name, - HorizontalExpand = true, - ClipText = true, - }; + nameButton.OnButtonUp += args => + { + if (_trackedButton != null && _trackedButton?.Coordinates != null) + //Make previous point red + NavMap.TrackedCoordinates[_trackedButton.Coordinates.Value] = (true, StyleNano.PointRed); - statusContainer.AddChild(nameLabel); + NavMap.TrackedCoordinates[coordinates.Value] = (true, StyleNano.PointGreen); + NavMap.CenterToCoordinates(coordinates.Value); - // User job container - var jobContainer = new BoxContainer() - { - Orientation = LayoutOrientation.Horizontal, - HorizontalExpand = true, - }; + nameButton.AddStyleClass(StyleNano.StyleClassButtonColorGreen); + if (_trackedButton != null) + { //Make previous button default + var previosButton = SensorsTable.GetChild(_trackedButton.IndexInTable); + previosButton.RemoveStyleClass(StyleNano.StyleClassButtonColorGreen); + } + _trackedButton = nameButton; + _trackedButton.IndexInTable = nameButton.GetPositionInParent(); + }; + } + } + // Show monitor point + if (monitorCoords != null) + NavMap.TrackedCoordinates.Add(monitorCoords.Value, (true, StyleNano.PointMagenta)); + } - mainContainer.AddChild(jobContainer); + private BoxContainer GetPositionBox(SuitSensorStatus sensor, Vector2 monitorCoordsInStationSpace, bool snap, float precision) + { + EntityCoordinates? coordinates = _entManager.GetCoordinates(sensor.Coordinates); + var box = new BoxContainer() { Orientation = LayoutOrientation.Horizontal }; - // Job icon - if (_prototypeManager.TryIndex(sensor.JobIcon, out var proto)) + if (coordinates == null || _stationUid == null) { - var jobIcon = new TextureRect() + var dirIcon = new DirectionIcon() { - TextureScale = new Vector2(2f, 2f), - Stretch = TextureRect.StretchMode.KeepCentered, - Texture = _spriteSystem.Frame0(proto.Icon), - Margin = new Thickness(5, 0, 5, 0), + SetSize = new Vector2(IconSize, IconSize), + Margin = new(0, 0, 4, 0) }; - - jobContainer.AddChild(jobIcon); + box.AddChild(dirIcon); + box.AddChild(new Label() { Text = Loc.GetString("crew-monitoring-user-interface-no-info") }); } - - // Job name - var jobLabel = new Label() + else { - Text = sensor.Job, - HorizontalExpand = true, - ClipText = true, - }; + var local = coordinates.Value.WithEntityId(_stationUid.Value, _entManager).Position; - jobContainer.AddChild(jobLabel); - - // Add user coordinates to the navmap - if (coordinates != null && NavMap.Visible && _blipTexture != null) - { - NavMap.TrackedEntities.TryAdd(sensor.SuitSensorUid, - new NavMapBlip - (coordinates.Value, - _blipTexture, - (_trackedEntity == null || sensor.SuitSensorUid == _trackedEntity) ? Color.LimeGreen : Color.LimeGreen * Color.DimGray, - sensor.SuitSensorUid == _trackedEntity)); - - NavMap.Focus = _trackedEntity; - - // On button up - sensorButton.OnButtonUp += args => + var displayPos = local.Floored(); + var dirIcon = new DirectionIcon(snap, precision) { - var prevTrackedEntity = _trackedEntity; - - if (_trackedEntity == sensor.SuitSensorUid) - { - _trackedEntity = null; - } - - else - { - _trackedEntity = sensor.SuitSensorUid; - NavMap.CenterToCoordinates(coordinates.Value); - } - - NavMap.Focus = _trackedEntity; - - UpdateSensorsTable(_trackedEntity, prevTrackedEntity); + SetSize = new Vector2(IconSize, IconSize), + Margin = new(0, 0, 4, 0) }; + box.AddChild(dirIcon); + Label label = new Label() { Text = displayPos.ToString() }; + SetColorLabel(label, sensor.TotalDamage, sensor.IsAlive); + box.AddChild(label); + _directionIcons.Add((dirIcon, local - monitorCoordsInStationSpace)); } - } - } - - private void SetTrackedEntityFromNavMap(NetEntity? netEntity) - { - var prevTrackedEntity = _trackedEntity; - _trackedEntity = netEntity; - if (_trackedEntity == prevTrackedEntity) - prevTrackedEntity = null; - - NavMap.Focus = _trackedEntity; - _tryToScrollToListFocus = true; - - UpdateSensorsTable(_trackedEntity, prevTrackedEntity); - } + return box; + } - private void UpdateSensorsTable(NetEntity? currTrackedEntity, NetEntity? prevTrackedEntity) - { - foreach (var sensor in SensorsTable.Children) + protected override void FrameUpdate(FrameEventArgs args) { - if (sensor is not CrewMonitoringButton) - continue; - - var castSensor = (CrewMonitoringButton) sensor; - - if (castSensor.SuitSensorUid == prevTrackedEntity) - castSensor.RemoveStyleClass(StyleNano.StyleClassButtonColorGreen); - - else if (castSensor.SuitSensorUid == currTrackedEntity) - castSensor.AddStyleClass(StyleNano.StyleClassButtonColorGreen); - - if (castSensor?.Coordinates == null) - continue; - - if (NavMap.TrackedEntities.TryGetValue(castSensor.SuitSensorUid, out var data)) + // the window is separate from any specific viewport, so there is no real way to get an eye-rotation without + // using IEyeManager. Eventually this will have to be reworked for a station AI with multi-viewports. + // (From the future: Or alternatively, just disable the angular offset for station AIs?) + + // An offsetAngle of zero here perfectly aligns directions to the station map. + // Note that the "relative angle" does this weird inverse-inverse thing. + // Could recalculate it all in world coordinates and then pass in eye directly... or do this. + var offsetAngle = Angle.Zero; + if (_entManager.TryGetComponent(_stationUid, out var xform)) { - data = new NavMapBlip - (data.Coordinates, - data.Texture, - (currTrackedEntity == null || castSensor.SuitSensorUid == currTrackedEntity) ? Color.LimeGreen : Color.LimeGreen * Color.DimGray, - castSensor.SuitSensorUid == currTrackedEntity); + // Apply the offset relative to the eye. + // For a station at 45 degrees rotation, the current eye rotation is -45 degrees. + // TODO: This feels sketchy. Is there something underlying wrong with eye rotation? + offsetAngle = -(_eye.CurrentEye.Rotation + xform.WorldRotation); + } - NavMap.TrackedEntities[castSensor.SuitSensorUid] = data; + foreach (var (icon, pos) in _directionIcons) + { + icon.UpdateDirection(pos, offsetAngle); } } - } - private void TryToScrollToFocus() - { - if (!_tryToScrollToListFocus) - return; - - if (!TryGetVerticalScrollbar(SensorScroller, out var vScrollbar)) - return; - - if (TryGetNextScrollPosition(out float? nextScrollPosition)) + private void ClearAllSensors() { - vScrollbar.ValueTarget = nextScrollPosition.Value; - - if (MathHelper.CloseToPercent(vScrollbar.Value, vScrollbar.ValueTarget)) + foreach (var child in _rowsContent) { - _tryToScrollToListFocus = false; - return; + SensorsTable.RemoveChild(child); } + _rowsContent.Clear(); + _directionIcons.Clear(); + NavMap.TrackedCoordinates.Clear(); } - } - - private bool TryGetVerticalScrollbar(ScrollContainer scroll, [NotNullWhen(true)] out VScrollBar? vScrollBar) - { - vScrollBar = null; - foreach (var child in scroll.Children) + private void SetColorLabel(Label label, int? totalDamage, bool isAlive) { - if (child is not VScrollBar) - continue; + var startColor = Color.White; + var critColor = Color.Yellow; + var endColor = Color.Red; - vScrollBar = (VScrollBar) child; - return true; - } + if (!isAlive) + { + label.FontColorOverride = endColor; + return; + } - return false; - } + //Convert from null to regular int + int damage; + if (totalDamage == null) return; + else damage = (int) totalDamage; - private bool TryGetNextScrollPosition([NotNullWhen(true)] out float? nextScrollPosition) - { - nextScrollPosition = 0; + if (damage <= 0) + { + label.FontColorOverride = startColor; + } + else if (damage >= 200) + { + label.FontColorOverride = endColor; + } + else if (damage >= 0 && damage <= 100) + { + label.FontColorOverride = GetColorLerp(startColor, critColor, damage); + } + else if (damage >= 100 && damage <= 200) + { + //We need a number from 0 to 100. Divide the number from 100 to 200 by 2 + damage /= 2; + label.FontColorOverride = GetColorLerp(critColor, endColor, damage); + } + } - foreach (var sensor in SensorsTable.Children) + private Color GetColorLerp(Color startColor, Color endColor, int damage) { - if (sensor is CrewMonitoringButton && - ((CrewMonitoringButton) sensor).SuitSensorUid == _trackedEntity) - return true; - - nextScrollPosition += sensor.Height; + //Smooth transition from one color to another depending on the percentage + var t = damage / 100f; + var r = MathHelper.Lerp(startColor.R, endColor.R, t); + var g = MathHelper.Lerp(startColor.G, endColor.G, t); + var b = MathHelper.Lerp(startColor.B, endColor.B, t); + var a = MathHelper.Lerp(startColor.A, endColor.A, t); + + return new Color(r, g, b, a); } - - // Failed to find control - nextScrollPosition = null; - - return false; } - private void ClearOutDatedData() + public sealed class CrewMonitoringButton : Button { - SensorsTable.RemoveAllChildren(); - _rowsContent.Clear(); - NavMap.TrackedCoordinates.Clear(); - NavMap.TrackedEntities.Clear(); - NavMap.LocalizedNames.Clear(); + public int IndexInTable; + public EntityUid? SuitSensorUid; + public EntityCoordinates? Coordinates; } } - -public sealed class CrewMonitoringButton : Button -{ - public int IndexInTable; - public NetEntity SuitSensorUid; - public EntityCoordinates? Coordinates; -} diff --git a/Content.Client/Options/UI/OptionsMenu.xaml b/Content.Client/Options/UI/OptionsMenu.xaml index ab3b88ca4e6..5d028879fe8 100644 --- a/Content.Client/Options/UI/OptionsMenu.xaml +++ b/Content.Client/Options/UI/OptionsMenu.xaml @@ -3,7 +3,6 @@ Title="{Loc 'ui-options-title'}" MinSize="800 450"> - diff --git a/Content.Client/Options/UI/OptionsMenu.xaml.cs b/Content.Client/Options/UI/OptionsMenu.xaml.cs index c3a8e664705..1a924d2af17 100644 --- a/Content.Client/Options/UI/OptionsMenu.xaml.cs +++ b/Content.Client/Options/UI/OptionsMenu.xaml.cs @@ -15,11 +15,10 @@ public OptionsMenu() RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); - Tabs.SetTabTitle(0, Loc.GetString("ui-options-tab-misc")); - Tabs.SetTabTitle(1, Loc.GetString("ui-options-tab-graphics")); - Tabs.SetTabTitle(2, Loc.GetString("ui-options-tab-controls")); - Tabs.SetTabTitle(3, Loc.GetString("ui-options-tab-audio")); - Tabs.SetTabTitle(4, Loc.GetString("ui-options-tab-network")); + Tabs.SetTabTitle(0, Loc.GetString("ui-options-tab-graphics")); + Tabs.SetTabTitle(1, Loc.GetString("ui-options-tab-controls")); + Tabs.SetTabTitle(2, Loc.GetString("ui-options-tab-audio")); + Tabs.SetTabTitle(3, Loc.GetString("ui-options-tab-network")); UpdateTabs(); } diff --git a/Content.Client/Options/UI/Tabs/AudioTab.xaml.cs b/Content.Client/Options/UI/Tabs/AudioTab.xaml.cs index 80e13d4a43e..6a9928b8bdd 100644 --- a/Content.Client/Options/UI/Tabs/AudioTab.xaml.cs +++ b/Content.Client/Options/UI/Tabs/AudioTab.xaml.cs @@ -1,4 +1,3 @@ -using Content.Client.Audio; using Content.Shared.CCVar; using Robust.Client.Audio; using Robust.Client.AutoGenerated; @@ -8,7 +7,6 @@ using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Shared; -using Robust.Shared.Audio.Systems; using Robust.Shared.Configuration; using Range = Robust.Client.UserInterface.Controls.Range; @@ -18,14 +16,14 @@ namespace Content.Client.Options.UI.Tabs public sealed partial class AudioTab : Control { [Dependency] private readonly IConfigurationManager _cfg = default!; - private readonly IAudioManager _audio; + private readonly AudioSystem _audio; public AudioTab() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); - _audio = IoCManager.Resolve(); + _audio = IoCManager.Resolve().System(); LobbyMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.LobbyMusicEnabled); RestartSoundsCheckBox.Pressed = _cfg.GetCVar(CCVars.RestartSoundsEnabled); EventMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.EventMusicEnabled); @@ -84,7 +82,7 @@ private void OnAmbienceSoundsSliderChanged(Range obj) private void OnMasterVolumeSliderChanged(Range range) { - _audio.SetMasterGain(MasterVolumeSlider.Value / 100f * ContentAudioSystem.MasterVolumeMultiplier); + _audio.SetMasterVolume(MasterVolumeSlider.Value / 100); UpdateChanges(); } @@ -113,16 +111,15 @@ private void OnAdminSoundsCheckToggled(BaseButton.ButtonEventArgs args) private void OnApplyButtonPressed(BaseButton.ButtonEventArgs args) { - _cfg.SetCVar(CVars.AudioMasterVolume, MasterVolumeSlider.Value / 100f * ContentAudioSystem.MasterVolumeMultiplier); + _cfg.SetCVar(CVars.AudioMasterVolume, LV100ToDB(MasterVolumeSlider.Value, CCVars.MasterMultiplier)); // Want the CVar updated values to have the multiplier applied // For the UI we just display 0-100 still elsewhere - _cfg.SetCVar(CVars.MidiVolume, MidiVolumeSlider.Value / 100f * ContentAudioSystem.MidiVolumeMultiplier); - _cfg.SetCVar(CCVars.AmbienceVolume, AmbienceVolumeSlider.Value / 100f * ContentAudioSystem.AmbienceMultiplier); - _cfg.SetCVar(CCVars.AmbientMusicVolume, AmbientMusicVolumeSlider.Value / 100f * ContentAudioSystem.AmbientMusicMultiplier); - _cfg.SetCVar(CCVars.LobbyMusicVolume, LobbyVolumeSlider.Value / 100f * ContentAudioSystem.LobbyMultiplier); + _cfg.SetCVar(CVars.MidiVolume, LV100ToDB(MidiVolumeSlider.Value, CCVars.MidiMultiplier)); + _cfg.SetCVar(CCVars.AmbienceVolume, LV100ToDB(AmbienceVolumeSlider.Value, CCVars.AmbienceMultiplier)); + _cfg.SetCVar(CCVars.AmbientMusicVolume, LV100ToDB(AmbientMusicVolumeSlider.Value, CCVars.AmbientMusicMultiplier)); + _cfg.SetCVar(CCVars.LobbyMusicVolume, LV100ToDB(LobbyVolumeSlider.Value)); _cfg.SetCVar(CCVars.MaxAmbientSources, (int)AmbienceSoundsSlider.Value); - _cfg.SetCVar(CCVars.LobbyMusicEnabled, LobbyMusicCheckBox.Pressed); _cfg.SetCVar(CCVars.RestartSoundsEnabled, RestartSoundsCheckBox.Pressed); _cfg.SetCVar(CCVars.EventMusicEnabled, EventMusicCheckBox.Pressed); @@ -138,14 +135,13 @@ private void OnResetButtonPressed(BaseButton.ButtonEventArgs args) private void Reset() { - MasterVolumeSlider.Value = _cfg.GetCVar(CVars.AudioMasterVolume) * 100f / ContentAudioSystem.MasterVolumeMultiplier; - MidiVolumeSlider.Value = _cfg.GetCVar(CVars.MidiVolume) * 100f / ContentAudioSystem.MidiVolumeMultiplier; - AmbienceVolumeSlider.Value = _cfg.GetCVar(CCVars.AmbienceVolume) * 100f / ContentAudioSystem.AmbienceMultiplier; - AmbientMusicVolumeSlider.Value = _cfg.GetCVar(CCVars.AmbientMusicVolume) * 100f / ContentAudioSystem.AmbientMusicMultiplier; - LobbyVolumeSlider.Value = _cfg.GetCVar(CCVars.LobbyMusicVolume) * 100f / ContentAudioSystem.LobbyMultiplier; - + MasterVolumeSlider.Value = DBToLV100(_cfg.GetCVar(CVars.AudioMasterVolume), CCVars.MasterMultiplier); + MidiVolumeSlider.Value = DBToLV100(_cfg.GetCVar(CVars.MidiVolume), CCVars.MidiMultiplier); + AmbienceVolumeSlider.Value = DBToLV100(_cfg.GetCVar(CCVars.AmbienceVolume), CCVars.AmbienceMultiplier); + AmbientMusicVolumeSlider.Value = + DBToLV100(_cfg.GetCVar(CCVars.AmbientMusicVolume), CCVars.AmbientMusicMultiplier); + LobbyVolumeSlider.Value = DBToLV100(_cfg.GetCVar(CCVars.LobbyMusicVolume)); AmbienceSoundsSlider.Value = _cfg.GetCVar(CCVars.MaxAmbientSources); - LobbyMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.LobbyMusicEnabled); RestartSoundsCheckBox.Pressed = _cfg.GetCVar(CCVars.RestartSoundsEnabled); EventMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.EventMusicEnabled); @@ -153,20 +149,33 @@ private void Reset() UpdateChanges(); } + // Note: Rather than moving these functions somewhere, instead switch MidiManager to using linear units rather than dB + // Do be sure to rename the setting though + private float DBToLV100(float db, float multiplier = 1f) + { + var beri = (float) (Math.Pow(10, db / 10) * 100 / multiplier); + return beri; + } + + private float LV100ToDB(float lv100, float multiplier = 1f) + { + // Saving negative infinity doesn't work, so use -10000000 instead (MidiManager does it) + var weh = MathF.Max(-10000000, (float) (Math.Log(lv100 * multiplier / 100, 10) * 10)); + return weh; + } + private void UpdateChanges() { - // y'all need jesus. var isMasterVolumeSame = - Math.Abs(MasterVolumeSlider.Value - _cfg.GetCVar(CVars.AudioMasterVolume) * 100f / ContentAudioSystem.MasterVolumeMultiplier) < 0.01f; + Math.Abs(MasterVolumeSlider.Value - DBToLV100(_cfg.GetCVar(CVars.AudioMasterVolume), CCVars.MasterMultiplier)) < 0.01f; var isMidiVolumeSame = - Math.Abs(MidiVolumeSlider.Value - _cfg.GetCVar(CVars.MidiVolume) * 100f / ContentAudioSystem.MidiVolumeMultiplier) < 0.01f; + Math.Abs(MidiVolumeSlider.Value - DBToLV100(_cfg.GetCVar(CVars.MidiVolume), CCVars.MidiMultiplier)) < 0.01f; var isAmbientVolumeSame = - Math.Abs(AmbienceVolumeSlider.Value - _cfg.GetCVar(CCVars.AmbienceVolume) * 100f / ContentAudioSystem.AmbienceMultiplier) < 0.01f; + Math.Abs(AmbienceVolumeSlider.Value - DBToLV100(_cfg.GetCVar(CCVars.AmbienceVolume), CCVars.AmbienceMultiplier)) < 0.01f; var isAmbientMusicVolumeSame = - Math.Abs(AmbientMusicVolumeSlider.Value - _cfg.GetCVar(CCVars.AmbientMusicVolume) * 100f / ContentAudioSystem.AmbientMusicMultiplier) < 0.01f; + Math.Abs(AmbientMusicVolumeSlider.Value - DBToLV100(_cfg.GetCVar(CCVars.AmbientMusicVolume), CCVars.AmbientMusicMultiplier)) < 0.01f; var isLobbyVolumeSame = - Math.Abs(LobbyVolumeSlider.Value - _cfg.GetCVar(CCVars.LobbyMusicVolume) * 100f / ContentAudioSystem.LobbyMultiplier) < 0.01f; - + Math.Abs(LobbyVolumeSlider.Value - DBToLV100(_cfg.GetCVar(CCVars.LobbyMusicVolume))) < 0.01f; var isAmbientSoundsSame = (int)AmbienceSoundsSlider.Value == _cfg.GetCVar(CCVars.MaxAmbientSources); var isLobbySame = LobbyMusicCheckBox.Pressed == _cfg.GetCVar(CCVars.LobbyMusicEnabled); var isRestartSoundsSame = RestartSoundsCheckBox.Pressed == _cfg.GetCVar(CCVars.RestartSoundsEnabled); diff --git a/Content.Client/Options/UI/Tabs/GraphicsTab.xaml b/Content.Client/Options/UI/Tabs/GraphicsTab.xaml index 74a0c78c526..f759c78eca8 100644 --- a/Content.Client/Options/UI/Tabs/GraphicsTab.xaml +++ b/Content.Client/Options/UI/Tabs/GraphicsTab.xaml @@ -20,6 +20,9 @@ + + + diff --git a/Content.Client/Options/UI/Tabs/GraphicsTab.xaml.cs b/Content.Client/Options/UI/Tabs/GraphicsTab.xaml.cs index 1773b2abe5d..e64838ba752 100644 --- a/Content.Client/Options/UI/Tabs/GraphicsTab.xaml.cs +++ b/Content.Client/Options/UI/Tabs/GraphicsTab.xaml.cs @@ -101,6 +101,9 @@ public GraphicsTab() UpdateApplyButton(); }; + ShowHeldItemCheckBox.OnToggled += OnCheckBoxToggled; + ShowCombatModeIndicatorsCheckBox.OnToggled += OnCheckBoxToggled; + ShowLoocAboveHeadCheckBox.OnToggled += OnCheckBoxToggled; IntegerScalingCheckBox.OnToggled += OnCheckBoxToggled; ViewportLowResCheckBox.OnToggled += OnCheckBoxToggled; ParallaxLowQualityCheckBox.OnToggled += OnCheckBoxToggled; @@ -117,6 +120,9 @@ public GraphicsTab() ViewportLowResCheckBox.Pressed = !_cfg.GetCVar(CCVars.ViewportScaleRender); ParallaxLowQualityCheckBox.Pressed = _cfg.GetCVar(CCVars.ParallaxLowQuality); FpsCounterCheckBox.Pressed = _cfg.GetCVar(CCVars.HudFpsCounterVisible); + ShowHeldItemCheckBox.Pressed = _cfg.GetCVar(CCVars.HudHeldItemShow); + ShowCombatModeIndicatorsCheckBox.Pressed = _cfg.GetCVar(CCVars.CombatModeIndicatorsPointShow); + ShowLoocAboveHeadCheckBox.Pressed = _cfg.GetCVar(CCVars.LoocAboveHeadShow); ViewportWidthSlider.Value = _cfg.GetCVar(CCVars.ViewportWidth); _cfg.OnValueChanged(CCVars.ViewportMinimumWidth, _ => UpdateViewportWidthRange()); @@ -162,6 +168,9 @@ private void OnApplyButtonPressed(BaseButton.ButtonEventArgs args) IntegerScalingCheckBox.Pressed ? CCVars.ViewportSnapToleranceMargin.DefaultValue : 0); _cfg.SetCVar(CCVars.ViewportScaleRender, !ViewportLowResCheckBox.Pressed); _cfg.SetCVar(CCVars.ParallaxLowQuality, ParallaxLowQualityCheckBox.Pressed); + _cfg.SetCVar(CCVars.HudHeldItemShow, ShowHeldItemCheckBox.Pressed); + _cfg.SetCVar(CCVars.CombatModeIndicatorsPointShow, ShowCombatModeIndicatorsCheckBox.Pressed); + _cfg.SetCVar(CCVars.LoocAboveHeadShow, ShowLoocAboveHeadCheckBox.Pressed); _cfg.SetCVar(CCVars.HudFpsCounterVisible, FpsCounterCheckBox.Pressed); _cfg.SetCVar(CCVars.ViewportWidth, (int) ViewportWidthSlider.Value); @@ -197,6 +206,9 @@ private void UpdateApplyButton() var isIntegerScalingSame = IntegerScalingCheckBox.Pressed == (_cfg.GetCVar(CCVars.ViewportSnapToleranceMargin) != 0); var isVPResSame = ViewportLowResCheckBox.Pressed == !_cfg.GetCVar(CCVars.ViewportScaleRender); var isPLQSame = ParallaxLowQualityCheckBox.Pressed == _cfg.GetCVar(CCVars.ParallaxLowQuality); + var isShowHeldItemSame = ShowHeldItemCheckBox.Pressed == _cfg.GetCVar(CCVars.HudHeldItemShow); + var isCombatModeIndicatorsSame = ShowCombatModeIndicatorsCheckBox.Pressed == _cfg.GetCVar(CCVars.CombatModeIndicatorsPointShow); + var isLoocShowSame = ShowLoocAboveHeadCheckBox.Pressed == _cfg.GetCVar(CCVars.LoocAboveHeadShow); var isFpsCounterVisibleSame = FpsCounterCheckBox.Pressed == _cfg.GetCVar(CCVars.HudFpsCounterVisible); var isWidthSame = (int) ViewportWidthSlider.Value == _cfg.GetCVar(CCVars.ViewportWidth); var isLayoutSame = HudLayoutOption.SelectedMetadata is string opt && opt == _cfg.GetCVar(CCVars.UILayout); @@ -211,6 +223,9 @@ private void UpdateApplyButton() isVPResSame && isPLQSame && isHudThemeSame && + isShowHeldItemSame && + isCombatModeIndicatorsSame && + isLoocShowSame && isFpsCounterVisibleSame && isWidthSame && isLayoutSame; diff --git a/Content.Client/Options/UI/Tabs/MiscTab.xaml b/Content.Client/Options/UI/Tabs/MiscTab.xaml deleted file mode 100644 index 8097578d8e8..00000000000 --- a/Content.Client/Options/UI/Tabs/MiscTab.xaml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - -