Merge branch 'master' of https://github.com/space-wizards/space-station-14 into map-load-refactor
This commit is contained in:
@@ -344,6 +344,9 @@ resharper_keep_existing_attribute_arrangement = true
|
|||||||
resharper_wrap_chained_binary_patterns = chop_if_long
|
resharper_wrap_chained_binary_patterns = chop_if_long
|
||||||
resharper_wrap_chained_method_calls = chop_if_long
|
resharper_wrap_chained_method_calls = chop_if_long
|
||||||
resharper_csharp_trailing_comma_in_multiline_lists = true
|
resharper_csharp_trailing_comma_in_multiline_lists = true
|
||||||
|
resharper_csharp_qualified_using_at_nested_scope = false
|
||||||
|
resharper_csharp_prefer_qualified_reference = false
|
||||||
|
resharper_csharp_allow_alias = false
|
||||||
|
|
||||||
[*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props}]
|
[*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props}]
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|||||||
5
.envrc
5
.envrc
@@ -1,4 +1,5 @@
|
|||||||
if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then
|
set -e
|
||||||
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc" "sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4="
|
if ! has nix_direnv_version || ! nix_direnv_version 3.0.6; then
|
||||||
|
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.6/direnvrc" "sha256-RYcUJaRMf8oF5LznDrlCXbkOQrywm0HDv1VjYGaJGdM="
|
||||||
fi
|
fi
|
||||||
use flake
|
use flake
|
||||||
|
|||||||
45
.github/workflows/publish-testing.yml
vendored
Normal file
45
.github/workflows/publish-testing.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
name: Publish Testing
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: publish-testing
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 10 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3.6.0
|
||||||
|
with:
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: Setup .NET Core
|
||||||
|
uses: actions/setup-dotnet@v3.2.0
|
||||||
|
with:
|
||||||
|
dotnet-version: 9.0.x
|
||||||
|
|
||||||
|
- name: Get Engine Tag
|
||||||
|
run: |
|
||||||
|
cd RobustToolbox
|
||||||
|
git fetch --depth=1
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: dotnet restore
|
||||||
|
|
||||||
|
- name: Build Packaging
|
||||||
|
run: dotnet build Content.Packaging --configuration Release --no-restore /m
|
||||||
|
|
||||||
|
- name: Package server
|
||||||
|
run: dotnet run --project Content.Packaging server --platform win-x64 --platform linux-x64 --platform osx-x64 --platform linux-arm64
|
||||||
|
|
||||||
|
- name: Package client
|
||||||
|
run: dotnet run --project Content.Packaging client --no-wipe-release
|
||||||
|
|
||||||
|
- name: Publish version
|
||||||
|
run: Tools/publish_multi_request.py --fork-id wizards-testing
|
||||||
|
env:
|
||||||
|
PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }}
|
||||||
|
GITHUB_REPOSITORY: ${{ vars.GITHUB_REPOSITORY }}
|
||||||
@@ -12,12 +12,12 @@ You want to handle the Build, Clean and Rebuild tasks to prevent missing task er
|
|||||||
If you want to learn more about these kinds of things, check out Microsoft's official documentation about MSBuild:
|
If you want to learn more about these kinds of things, check out Microsoft's official documentation about MSBuild:
|
||||||
https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild
|
https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild
|
||||||
-->
|
-->
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Python>python3</Python>
|
<Python>python3</Python>
|
||||||
<Python Condition="'$(OS)'=='Windows_NT' Or '$(OS)'=='Windows'">py -3</Python>
|
<Python Condition="'$(OS)'=='Windows_NT' Or '$(OS)'=='Windows'">py -3</Python>
|
||||||
<ProjectGuid>{C899FCA4-7037-4E49-ABC2-44DE72487110}</ProjectGuid>
|
<ProjectGuid>{C899FCA4-7037-4E49-ABC2-44DE72487110}</ProjectGuid>
|
||||||
<TargetFrameworkMoniker>.NETFramework, Version=v4.7.2</TargetFrameworkMoniker>
|
<TargetFramework>net4.7.2</TargetFramework>
|
||||||
<RestorePackages>false</RestorePackages>
|
<RestorePackages>false</RestorePackages>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public class MapLoadBenchmark
|
|||||||
PoolManager.Shutdown();
|
PoolManager.Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly string[] MapsSource = { "Empty", "Satlern", "Box", "Bagel", "Dev", "CentComm", "Core", "TestTeg", "Packed", "Omega", "Reach", "Meta", "Marathon", "MeteorArena", "Fland", "Oasis", "Cog" };
|
public static readonly string[] MapsSource = { "Empty", "Satlern", "Box", "Bagel", "Dev", "CentComm", "Core", "TestTeg", "Packed", "Omega", "Reach", "Meta", "Marathon", "MeteorArena", "Fland", "Oasis", "Convex"};
|
||||||
|
|
||||||
[ParamsSource(nameof(MapsSource))]
|
[ParamsSource(nameof(MapsSource))]
|
||||||
public string Map;
|
public string Map;
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ namespace Content.Client.Actions
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
component.Whitelist = state.Whitelist;
|
component.Whitelist = state.Whitelist;
|
||||||
|
component.Blacklist = state.Blacklist;
|
||||||
component.CanTargetSelf = state.CanTargetSelf;
|
component.CanTargetSelf = state.CanTargetSelf;
|
||||||
BaseHandleState<EntityTargetActionComponent>(uid, component, state);
|
BaseHandleState<EntityTargetActionComponent>(uid, component, state);
|
||||||
}
|
}
|
||||||
@@ -137,6 +138,7 @@ namespace Content.Client.Actions
|
|||||||
component.Priority = state.Priority;
|
component.Priority = state.Priority;
|
||||||
component.AttachedEntity = EnsureEntity<T>(state.AttachedEntity, uid);
|
component.AttachedEntity = EnsureEntity<T>(state.AttachedEntity, uid);
|
||||||
component.RaiseOnUser = state.RaiseOnUser;
|
component.RaiseOnUser = state.RaiseOnUser;
|
||||||
|
component.RaiseOnAction = state.RaiseOnAction;
|
||||||
component.AutoPopulate = state.AutoPopulate;
|
component.AutoPopulate = state.AutoPopulate;
|
||||||
component.Temporary = state.Temporary;
|
component.Temporary = state.Temporary;
|
||||||
component.ItemIconStyle = state.ItemIconStyle;
|
component.ItemIconStyle = state.ItemIconStyle;
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ public sealed class AtmosAlertsComputerBoundUserInterface : BoundUserInterface
|
|||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
_menu = new AtmosAlertsComputerWindow(this, Owner);
|
_menu = new AtmosAlertsComputerWindow(this, Owner);
|
||||||
_menu.OpenCentered();
|
_menu.OpenCentered();
|
||||||
_menu.OnClose += Close;
|
_menu.OnClose += Close;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ namespace Content.Client.Atmos.EntitySystems
|
|||||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||||
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
||||||
[Dependency] private readonly SpriteSystem _spriteSys = default!;
|
[Dependency] private readonly SpriteSystem _spriteSys = default!;
|
||||||
|
[Dependency] private readonly SharedTransformSystem _xformSys = default!;
|
||||||
|
|
||||||
private GasTileOverlay _overlay = default!;
|
private GasTileOverlay _overlay = default!;
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ namespace Content.Client.Atmos.EntitySystems
|
|||||||
SubscribeNetworkEvent<GasOverlayUpdateEvent>(HandleGasOverlayUpdate);
|
SubscribeNetworkEvent<GasOverlayUpdateEvent>(HandleGasOverlayUpdate);
|
||||||
SubscribeLocalEvent<GasTileOverlayComponent, ComponentHandleState>(OnHandleState);
|
SubscribeLocalEvent<GasTileOverlayComponent, ComponentHandleState>(OnHandleState);
|
||||||
|
|
||||||
_overlay = new GasTileOverlay(this, EntityManager, _resourceCache, ProtoMan, _spriteSys);
|
_overlay = new GasTileOverlay(this, EntityManager, _resourceCache, ProtoMan, _spriteSys, _xformSys);
|
||||||
_overlayMan.AddOverlay(_overlay);
|
_overlayMan.AddOverlay(_overlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ namespace Content.Client.Atmos.Overlays
|
|||||||
{
|
{
|
||||||
private readonly IEntityManager _entManager;
|
private readonly IEntityManager _entManager;
|
||||||
private readonly IMapManager _mapManager;
|
private readonly IMapManager _mapManager;
|
||||||
|
private readonly SharedTransformSystem _xformSys;
|
||||||
|
|
||||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceEntities | OverlaySpace.WorldSpaceBelowWorld;
|
public override OverlaySpace Space => OverlaySpace.WorldSpaceEntities | OverlaySpace.WorldSpaceBelowWorld;
|
||||||
private readonly ShaderInstance _shader;
|
private readonly ShaderInstance _shader;
|
||||||
@@ -46,10 +47,11 @@ namespace Content.Client.Atmos.Overlays
|
|||||||
|
|
||||||
public const int GasOverlayZIndex = (int) Shared.DrawDepth.DrawDepth.Effects; // Under ghosts, above mostly everything else
|
public const int GasOverlayZIndex = (int) Shared.DrawDepth.DrawDepth.Effects; // Under ghosts, above mostly everything else
|
||||||
|
|
||||||
public GasTileOverlay(GasTileOverlaySystem system, IEntityManager entManager, IResourceCache resourceCache, IPrototypeManager protoMan, SpriteSystem spriteSys)
|
public GasTileOverlay(GasTileOverlaySystem system, IEntityManager entManager, IResourceCache resourceCache, IPrototypeManager protoMan, SpriteSystem spriteSys, SharedTransformSystem xformSys)
|
||||||
{
|
{
|
||||||
_entManager = entManager;
|
_entManager = entManager;
|
||||||
_mapManager = IoCManager.Resolve<IMapManager>();
|
_mapManager = IoCManager.Resolve<IMapManager>();
|
||||||
|
_xformSys = xformSys;
|
||||||
_shader = protoMan.Index<ShaderPrototype>("unshaded").Instance();
|
_shader = protoMan.Index<ShaderPrototype>("unshaded").Instance();
|
||||||
ZIndex = GasOverlayZIndex;
|
ZIndex = GasOverlayZIndex;
|
||||||
|
|
||||||
@@ -158,7 +160,8 @@ namespace Content.Client.Atmos.Overlays
|
|||||||
_fireFrameCounter,
|
_fireFrameCounter,
|
||||||
_shader,
|
_shader,
|
||||||
overlayQuery,
|
overlayQuery,
|
||||||
xformQuery);
|
xformQuery,
|
||||||
|
_xformSys);
|
||||||
|
|
||||||
var mapUid = _mapManager.GetMapEntityId(args.MapId);
|
var mapUid = _mapManager.GetMapEntityId(args.MapId);
|
||||||
|
|
||||||
@@ -180,7 +183,8 @@ namespace Content.Client.Atmos.Overlays
|
|||||||
int[] fireFrameCounter,
|
int[] fireFrameCounter,
|
||||||
ShaderInstance shader,
|
ShaderInstance shader,
|
||||||
EntityQuery<GasTileOverlayComponent> overlayQuery,
|
EntityQuery<GasTileOverlayComponent> overlayQuery,
|
||||||
EntityQuery<TransformComponent> xformQuery) state) =>
|
EntityQuery<TransformComponent> xformQuery,
|
||||||
|
SharedTransformSystem xformSys) state) =>
|
||||||
{
|
{
|
||||||
if (!state.overlayQuery.TryGetComponent(uid, out var comp) ||
|
if (!state.overlayQuery.TryGetComponent(uid, out var comp) ||
|
||||||
!state.xformQuery.TryGetComponent(uid, out var gridXform))
|
!state.xformQuery.TryGetComponent(uid, out var gridXform))
|
||||||
@@ -188,7 +192,7 @@ namespace Content.Client.Atmos.Overlays
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var (_, _, worldMatrix, invMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv();
|
var (_, _, worldMatrix, invMatrix) = state.xformSys.GetWorldPositionRotationMatrixWithInv(gridXform);
|
||||||
state.drawHandle.SetTransform(worldMatrix);
|
state.drawHandle.SetTransform(worldMatrix);
|
||||||
var floatBounds = invMatrix.TransformBox(state.WorldBounds).Enlarged(grid.TileSize);
|
var floatBounds = invMatrix.TransformBox(state.WorldBounds).Enlarged(grid.TileSize);
|
||||||
var localBounds = new Box2i(
|
var localBounds = new Box2i(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<DefaultWindow xmlns="https://spacestation14.io"
|
<DefaultWindow xmlns="https://spacestation14.io"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
MinSize="280 160" Title="Temperature Control Unit">
|
MinSize="280 160" Title="{Loc comp-space-heater-ui-title}">
|
||||||
|
|
||||||
<BoxContainer Name="VboxContainer" Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10">
|
<BoxContainer Name="VboxContainer" Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10">
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,15 @@ using Content.Shared.Buckle;
|
|||||||
using Content.Shared.Buckle.Components;
|
using Content.Shared.Buckle.Components;
|
||||||
using Content.Shared.Rotation;
|
using Content.Shared.Rotation;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Client.Graphics;
|
||||||
|
|
||||||
namespace Content.Client.Buckle;
|
namespace Content.Client.Buckle;
|
||||||
|
|
||||||
internal sealed class BuckleSystem : SharedBuckleSystem
|
internal sealed class BuckleSystem : SharedBuckleSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly RotationVisualizerSystem _rotationVisualizerSystem = default!;
|
[Dependency] private readonly RotationVisualizerSystem _rotationVisualizerSystem = default!;
|
||||||
|
[Dependency] private readonly IEyeManager _eye = default!;
|
||||||
|
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -17,6 +19,8 @@ internal sealed class BuckleSystem : SharedBuckleSystem
|
|||||||
|
|
||||||
SubscribeLocalEvent<BuckleComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
SubscribeLocalEvent<BuckleComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||||
SubscribeLocalEvent<StrapComponent, MoveEvent>(OnStrapMoveEvent);
|
SubscribeLocalEvent<StrapComponent, MoveEvent>(OnStrapMoveEvent);
|
||||||
|
SubscribeLocalEvent<BuckleComponent, BuckledEvent>(OnBuckledEvent);
|
||||||
|
SubscribeLocalEvent<BuckleComponent, UnbuckledEvent>(OnUnbuckledEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStrapMoveEvent(EntityUid uid, StrapComponent component, ref MoveEvent args)
|
private void OnStrapMoveEvent(EntityUid uid, StrapComponent component, ref MoveEvent args)
|
||||||
@@ -28,13 +32,21 @@ internal sealed class BuckleSystem : SharedBuckleSystem
|
|||||||
// This code is garbage, it doesn't work with rotated viewports. I need to finally get around to reworking
|
// This code is garbage, it doesn't work with rotated viewports. I need to finally get around to reworking
|
||||||
// sprite rendering for entity layers & direction dependent sorting.
|
// sprite rendering for entity layers & direction dependent sorting.
|
||||||
|
|
||||||
|
// Future notes:
|
||||||
|
// Right now this doesn't handle: other grids, other grids rotating, the camera rotation changing, and many other fun rotation specific things
|
||||||
|
// The entire thing should be a concern of the engine, or something engine helps to implement properly.
|
||||||
|
// Give some of the sprite rotations their own drawdepth, maybe as an offset within the rsi, or something like this
|
||||||
|
// And we won't ever need to set the draw depth manually
|
||||||
|
|
||||||
if (args.NewRotation == args.OldRotation)
|
if (args.NewRotation == args.OldRotation)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!TryComp<SpriteComponent>(uid, out var strapSprite))
|
if (!TryComp<SpriteComponent>(uid, out var strapSprite))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var isNorth = Transform(uid).LocalRotation.GetCardinalDir() == Direction.North;
|
var angle = _xformSystem.GetWorldRotation(uid) + _eye.CurrentEye.Rotation; // Get true screen position, or close enough
|
||||||
|
|
||||||
|
var isNorth = angle.GetCardinalDir() == Direction.North;
|
||||||
foreach (var buckledEntity in component.BuckledEntities)
|
foreach (var buckledEntity in component.BuckledEntities)
|
||||||
{
|
{
|
||||||
if (!TryComp<BuckleComponent>(buckledEntity, out var buckle))
|
if (!TryComp<BuckleComponent>(buckledEntity, out var buckle))
|
||||||
@@ -45,6 +57,7 @@ internal sealed class BuckleSystem : SharedBuckleSystem
|
|||||||
|
|
||||||
if (isNorth)
|
if (isNorth)
|
||||||
{
|
{
|
||||||
|
// This will only assign if empty, it won't get overwritten by new depth on multiple calls, which do happen easily
|
||||||
buckle.OriginalDrawDepth ??= buckledSprite.DrawDepth;
|
buckle.OriginalDrawDepth ??= buckledSprite.DrawDepth;
|
||||||
buckledSprite.DrawDepth = strapSprite.DrawDepth - 1;
|
buckledSprite.DrawDepth = strapSprite.DrawDepth - 1;
|
||||||
}
|
}
|
||||||
@@ -56,6 +69,42 @@ internal sealed class BuckleSystem : SharedBuckleSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lower the draw depth of the buckled entity without needing for the strap entity to rotate/move.
|
||||||
|
/// Only do so when the entity is facing screen-local north
|
||||||
|
/// </summary>
|
||||||
|
private void OnBuckledEvent(Entity<BuckleComponent> ent, ref BuckledEvent args)
|
||||||
|
{
|
||||||
|
if (!TryComp<SpriteComponent>(args.Strap, out var strapSprite))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!TryComp<SpriteComponent>(ent.Owner, out var buckledSprite))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var angle = _xformSystem.GetWorldRotation(args.Strap) + _eye.CurrentEye.Rotation; // Get true screen position, or close enough
|
||||||
|
|
||||||
|
if (angle.GetCardinalDir() != Direction.North)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ent.Comp.OriginalDrawDepth ??= buckledSprite.DrawDepth;
|
||||||
|
buckledSprite.DrawDepth = strapSprite.DrawDepth - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Was the draw depth of the buckled entity lowered? Reset it upon unbuckling.
|
||||||
|
/// </summary>
|
||||||
|
private void OnUnbuckledEvent(Entity<BuckleComponent> ent, ref UnbuckledEvent args)
|
||||||
|
{
|
||||||
|
if (!TryComp<SpriteComponent>(ent.Owner, out var buckledSprite))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!ent.Comp.OriginalDrawDepth.HasValue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
buckledSprite.DrawDepth = ent.Comp.OriginalDrawDepth.Value;
|
||||||
|
ent.Comp.OriginalDrawDepth = null;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnAppearanceChange(EntityUid uid, BuckleComponent component, ref AppearanceChangeEvent args)
|
private void OnAppearanceChange(EntityUid uid, BuckleComponent component, ref AppearanceChangeEvent args)
|
||||||
{
|
{
|
||||||
if (!TryComp<RotationVisualsComponent>(uid, out var rotVisuals))
|
if (!TryComp<RotationVisualsComponent>(uid, out var rotVisuals))
|
||||||
|
|||||||
@@ -39,6 +39,6 @@ public sealed class CargoBountyConsoleBoundUserInterface : BoundUserInterface
|
|||||||
if (message is not CargoBountyConsoleState state)
|
if (message is not CargoBountyConsoleState state)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_menu?.UpdateEntries(state.Bounties, state.UntilNextSkip);
|
_menu?.UpdateEntries(state.Bounties, state.History, state.UntilNextSkip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
22
Content.Client/Cargo/UI/BountyHistoryEntry.xaml
Normal file
22
Content.Client/Cargo/UI/BountyHistoryEntry.xaml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<BoxContainer xmlns="https://spacestation14.io"
|
||||||
|
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||||
|
Margin="10 10 10 0"
|
||||||
|
HorizontalExpand="True">
|
||||||
|
<PanelContainer StyleClasses="AngleRect" HorizontalExpand="True">
|
||||||
|
<BoxContainer Orientation="Vertical"
|
||||||
|
HorizontalExpand="True">
|
||||||
|
<BoxContainer Orientation="Horizontal">
|
||||||
|
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
|
||||||
|
<RichTextLabel Name="RewardLabel"/>
|
||||||
|
<RichTextLabel Name="ManifestLabel"/>
|
||||||
|
</BoxContainer>
|
||||||
|
<BoxContainer Orientation="Vertical" MinWidth="120" Margin="0 0 10 0">
|
||||||
|
<RichTextLabel Name="TimestampLabel" HorizontalAlignment="Right" />
|
||||||
|
<RichTextLabel Name="IdLabel" HorizontalAlignment="Right" />
|
||||||
|
</BoxContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
<customControls:HSeparator Margin="5 10 5 10"/>
|
||||||
|
<RichTextLabel Name="NoticeLabel" />
|
||||||
|
</BoxContainer>
|
||||||
|
</PanelContainer>
|
||||||
|
</BoxContainer>
|
||||||
49
Content.Client/Cargo/UI/BountyHistoryEntry.xaml.cs
Normal file
49
Content.Client/Cargo/UI/BountyHistoryEntry.xaml.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using Content.Client.Message;
|
||||||
|
using Content.Shared.Cargo;
|
||||||
|
using Content.Shared.Cargo.Prototypes;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Client.Cargo.UI;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class BountyHistoryEntry : BoxContainer
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||||
|
|
||||||
|
public BountyHistoryEntry(CargoBountyHistoryData bounty)
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
|
if (!_prototype.TryIndex(bounty.Bounty, out var bountyPrototype))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var items = new List<string>();
|
||||||
|
foreach (var entry in bountyPrototype.Entries)
|
||||||
|
{
|
||||||
|
items.Add(Loc.GetString("bounty-console-manifest-entry",
|
||||||
|
("amount", entry.Amount),
|
||||||
|
("item", Loc.GetString(entry.Name))));
|
||||||
|
}
|
||||||
|
|
||||||
|
ManifestLabel.SetMarkup(Loc.GetString("bounty-console-manifest-label", ("item", string.Join(", ", items))));
|
||||||
|
RewardLabel.SetMarkup(Loc.GetString("bounty-console-reward-label", ("reward", bountyPrototype.Reward)));
|
||||||
|
IdLabel.SetMarkup(Loc.GetString("bounty-console-id-label", ("id", bounty.Id)));
|
||||||
|
|
||||||
|
TimestampLabel.SetMarkup(bounty.Timestamp.ToString(@"hh\:mm\:ss"));
|
||||||
|
|
||||||
|
if (bounty.Result == CargoBountyHistoryData.BountyResult.Completed)
|
||||||
|
{
|
||||||
|
NoticeLabel.SetMarkup(Loc.GetString("bounty-console-history-notice-completed-label"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NoticeLabel.SetMarkup(Loc.GetString("bounty-console-history-notice-skipped-label",
|
||||||
|
("id", bounty.ActorName ?? "")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,15 +11,28 @@
|
|||||||
<PanelContainer.PanelOverride>
|
<PanelContainer.PanelOverride>
|
||||||
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
||||||
</PanelContainer.PanelOverride>
|
</PanelContainer.PanelOverride>
|
||||||
|
<TabContainer Name="MasterTabContainer" VerticalExpand="True" HorizontalExpand="True">
|
||||||
<ScrollContainer HScrollEnabled="False"
|
<ScrollContainer HScrollEnabled="False"
|
||||||
HorizontalExpand="True"
|
HorizontalExpand="True"
|
||||||
VerticalExpand="True">
|
VerticalExpand="True">
|
||||||
<BoxContainer Name="BountyEntriesContainer"
|
<BoxContainer Name="BountyEntriesContainer"
|
||||||
Orientation="Vertical"
|
Orientation="Vertical"
|
||||||
VerticalExpand="True"
|
VerticalExpand="True"
|
||||||
HorizontalExpand="True">
|
HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
|
||||||
</ScrollContainer>
|
</ScrollContainer>
|
||||||
|
<ScrollContainer HScrollEnabled="False"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
VerticalExpand="True">
|
||||||
|
<Label Name="NoHistoryLabel"
|
||||||
|
Text="{Loc 'bounty-console-history-empty-label'}"
|
||||||
|
Visible="False"
|
||||||
|
Align="Center" />
|
||||||
|
<BoxContainer Name="BountyHistoryContainer"
|
||||||
|
Orientation="Vertical"
|
||||||
|
VerticalExpand="True"
|
||||||
|
HorizontalExpand="True" />
|
||||||
|
</ScrollContainer>
|
||||||
|
</TabContainer>
|
||||||
</PanelContainer>
|
</PanelContainer>
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
|
|||||||
@@ -15,9 +15,12 @@ public sealed partial class CargoBountyMenu : FancyWindow
|
|||||||
public CargoBountyMenu()
|
public CargoBountyMenu()
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
|
|
||||||
|
MasterTabContainer.SetTabTitle(0, Loc.GetString("bounty-console-tab-available-label"));
|
||||||
|
MasterTabContainer.SetTabTitle(1, Loc.GetString("bounty-console-tab-history-label"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateEntries(List<CargoBountyData> bounties, TimeSpan untilNextSkip)
|
public void UpdateEntries(List<CargoBountyData> bounties, List<CargoBountyHistoryData> history, TimeSpan untilNextSkip)
|
||||||
{
|
{
|
||||||
BountyEntriesContainer.Children.Clear();
|
BountyEntriesContainer.Children.Clear();
|
||||||
foreach (var b in bounties)
|
foreach (var b in bounties)
|
||||||
@@ -32,5 +35,21 @@ public sealed partial class CargoBountyMenu : FancyWindow
|
|||||||
{
|
{
|
||||||
MinHeight = 10
|
MinHeight = 10
|
||||||
});
|
});
|
||||||
|
|
||||||
|
BountyHistoryContainer.Children.Clear();
|
||||||
|
if (history.Count == 0)
|
||||||
|
{
|
||||||
|
NoHistoryLabel.Visible = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NoHistoryLabel.Visible = false;
|
||||||
|
|
||||||
|
// Show the history in reverse, so last entry is first in the list
|
||||||
|
for (var i = history.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
BountyHistoryContainer.AddChild(new BountyHistoryEntry(history[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ using JetBrains.Annotations;
|
|||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
|
|
||||||
namespace Content.Client.Changelog
|
namespace Content.Client.Changelog
|
||||||
@@ -15,8 +17,9 @@ namespace Content.Client.Changelog
|
|||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class ChangelogWindow : FancyWindow
|
public sealed partial class ChangelogWindow : FancyWindow
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IClientAdminManager _adminManager = default!;
|
|
||||||
[Dependency] private readonly ChangelogManager _changelog = default!;
|
[Dependency] private readonly ChangelogManager _changelog = default!;
|
||||||
|
[Dependency] private readonly IClientAdminManager _adminManager = default!;
|
||||||
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||||
|
|
||||||
public ChangelogWindow()
|
public ChangelogWindow()
|
||||||
{
|
{
|
||||||
@@ -67,8 +70,22 @@ namespace Content.Client.Changelog
|
|||||||
Tabs.SetTabTitle(i++, Loc.GetString($"changelog-tab-title-{changelog.Name}"));
|
Tabs.SetTabTitle(i++, Loc.GetString($"changelog-tab-title-{changelog.Name}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
var version = typeof(ChangelogWindow).Assembly.GetName().Version ?? new Version(1, 0);
|
// Try to get the current version from the build.json file
|
||||||
VersionLabel.Text = Loc.GetString("changelog-version-tag", ("version", version.ToString()));
|
var version = _cfg.GetCVar(CVars.BuildVersion);
|
||||||
|
var forkId = _cfg.GetCVar(CVars.BuildForkId);
|
||||||
|
|
||||||
|
var versionText = Loc.GetString("changelog-version-unknown");
|
||||||
|
|
||||||
|
// Make sure these aren't empty, like in a dev env
|
||||||
|
if (!string.IsNullOrEmpty(version) && !string.IsNullOrEmpty(forkId))
|
||||||
|
{
|
||||||
|
versionText = Loc.GetString("changelog-version-tag",
|
||||||
|
("fork", forkId),
|
||||||
|
("version", version[..7])); // Only show the first 7 characters
|
||||||
|
}
|
||||||
|
|
||||||
|
// if else statements are ugly, shut up
|
||||||
|
VersionLabel.Text = versionText;
|
||||||
|
|
||||||
TabsUpdated();
|
TabsUpdated();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<ui:RadialMenu xmlns="https://spacestation14.io"
|
<ui:RadialMenu xmlns="https://spacestation14.io"
|
||||||
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
|
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
BackButtonStyleClass="RadialMenuBackButton"
|
BackButtonStyleClass="RadialMenuBackButton"
|
||||||
CloseButtonStyleClass="RadialMenuCloseButton"
|
CloseButtonStyleClass="RadialMenuCloseButton"
|
||||||
@@ -7,25 +7,25 @@
|
|||||||
MinSize="450 450">
|
MinSize="450 450">
|
||||||
|
|
||||||
<!-- Main -->
|
<!-- Main -->
|
||||||
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" Radius="64" ReserveSpaceForHiddenChildren="False">
|
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100" ReserveSpaceForHiddenChildren="False">
|
||||||
<ui:RadialMenuTextureButton StyleClasses="RadialMenuButton" SetSize="64 64" ToolTip="{Loc 'emote-menu-category-general'}" TargetLayer="General" Visible="False">
|
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'emote-menu-category-general'}" TargetLayer="General" Visible="False">
|
||||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Clothing/Head/Soft/mimesoft.rsi/icon.png"/>
|
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Clothing/Head/Soft/mimesoft.rsi/icon.png"/>
|
||||||
</ui:RadialMenuTextureButton>
|
</ui:RadialMenuTextureButtonWithSector>
|
||||||
<ui:RadialMenuTextureButton StyleClasses="RadialMenuButton" SetSize="64 64" ToolTip="{Loc 'emote-menu-category-vocal'}" TargetLayer="Vocal" Visible="False">
|
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'emote-menu-category-vocal'}" TargetLayer="Vocal" Visible="False">
|
||||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Emotes/vocal.png"/>
|
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Emotes/vocal.png"/>
|
||||||
</ui:RadialMenuTextureButton>
|
</ui:RadialMenuTextureButtonWithSector>
|
||||||
<ui:RadialMenuTextureButton StyleClasses="RadialMenuButton" SetSize="64 64" ToolTip="{Loc 'emote-menu-category-hands'}" TargetLayer="Hands" Visible="False">
|
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'emote-menu-category-hands'}" TargetLayer="Hands" Visible="False">
|
||||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Clothing/Hands/Gloves/latex.rsi/icon.png"/>
|
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Clothing/Hands/Gloves/latex.rsi/icon.png"/>
|
||||||
</ui:RadialMenuTextureButton>
|
</ui:RadialMenuTextureButtonWithSector>
|
||||||
</ui:RadialContainer>
|
</ui:RadialContainer>
|
||||||
|
|
||||||
<!-- General -->
|
<!-- General -->
|
||||||
<ui:RadialContainer Name="General" VerticalExpand="True" HorizontalExpand="True" Radius="64"/>
|
<ui:RadialContainer Name="General" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
|
||||||
|
|
||||||
<!-- Vocal -->
|
<!-- Vocal -->
|
||||||
<ui:RadialContainer Name="Vocal" VerticalExpand="True" HorizontalExpand="True" Radius="64"/>
|
<ui:RadialContainer Name="Vocal" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
|
||||||
|
|
||||||
<!-- Hands -->
|
<!-- Hands -->
|
||||||
<ui:RadialContainer Name="Hands" VerticalExpand="True" HorizontalExpand="True" Radius="64"/>
|
<ui:RadialContainer Name="Hands" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
|
||||||
|
|
||||||
</ui:RadialMenu>
|
</ui:RadialMenu>
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ public sealed partial class EmotesMenu : RadialMenu
|
|||||||
|
|
||||||
var button = new EmoteMenuButton
|
var button = new EmoteMenuButton
|
||||||
{
|
{
|
||||||
StyleClasses = { "RadialMenuButton" },
|
|
||||||
SetSize = new Vector2(64f, 64f),
|
SetSize = new Vector2(64f, 64f),
|
||||||
ToolTip = Loc.GetString(emote.Name),
|
ToolTip = Loc.GetString(emote.Name),
|
||||||
ProtoId = emote.ID,
|
ProtoId = emote.ID,
|
||||||
@@ -106,7 +105,7 @@ public sealed partial class EmotesMenu : RadialMenu
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public sealed class EmoteMenuButton : RadialMenuTextureButton
|
public sealed class EmoteMenuButton : RadialMenuTextureButtonWithSector
|
||||||
{
|
{
|
||||||
public ProtoId<EmotePrototype> ProtoId { get; set; }
|
public ProtoId<EmotePrototype> ProtoId { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace Content.Client.Clickable
|
|||||||
"/Textures/Logo",
|
"/Textures/Logo",
|
||||||
};
|
};
|
||||||
|
|
||||||
private const float Threshold = 0.25f;
|
private const float Threshold = 0.1f;
|
||||||
private const int ClickRadius = 2;
|
private const int ClickRadius = 2;
|
||||||
|
|
||||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public sealed class CrewManifestSection : BoxContainer
|
|||||||
AddChild(new Label()
|
AddChild(new Label()
|
||||||
{
|
{
|
||||||
StyleClasses = { "LabelBig" },
|
StyleClasses = { "LabelBig" },
|
||||||
Text = Loc.GetString($"department-{section.ID}")
|
Text = Loc.GetString(section.Name)
|
||||||
});
|
});
|
||||||
|
|
||||||
var gridContainer = new GridContainer()
|
var gridContainer = new GridContainer()
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ public sealed class CriminalRecordsConsoleBoundUserInterface : BoundUserInterfac
|
|||||||
SendMessage(new CriminalRecordChangeStatus(status, null));
|
SendMessage(new CriminalRecordChangeStatus(status, null));
|
||||||
_window.OnDialogConfirmed += (status, reason) =>
|
_window.OnDialogConfirmed += (status, reason) =>
|
||||||
SendMessage(new CriminalRecordChangeStatus(status, reason));
|
SendMessage(new CriminalRecordChangeStatus(status, reason));
|
||||||
|
_window.OnStatusFilterPressed += (statusFilter) =>
|
||||||
|
SendMessage(new CriminalRecordSetStatusFilter(statusFilter));
|
||||||
_window.OnHistoryUpdated += UpdateHistory;
|
_window.OnHistoryUpdated += UpdateHistory;
|
||||||
_window.OnHistoryClosed += () => _historyWindow?.Close();
|
_window.OnHistoryClosed += () => _historyWindow?.Close();
|
||||||
_window.OnClose += Close;
|
_window.OnClose += Close;
|
||||||
|
|||||||
@@ -1,36 +1,140 @@
|
|||||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
Title="{Loc 'criminal-records-console-window-title'}"
|
Title="{Loc 'criminal-records-console-window-title'}"
|
||||||
MinSize="660 400">
|
MinSize="695 440">
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<!-- Record search bar
|
<BoxContainer Name="AllList"
|
||||||
TODO: make this into a control shared with general records -->
|
Orientation="Vertical"
|
||||||
<BoxContainer Margin="5 5 5 10" HorizontalExpand="true" VerticalAlignment="Center">
|
VerticalExpand="True"
|
||||||
<OptionButton Name="FilterType" MinWidth="200" Margin="0 0 10 0"/> <!-- Populated in constructor -->
|
HorizontalExpand="True"
|
||||||
<LineEdit Name="FilterText" PlaceHolder="{Loc 'criminal-records-filter-placeholder'}" HorizontalExpand="True"/>
|
Margin="8">
|
||||||
|
<!-- Record search bar -->
|
||||||
|
<BoxContainer Margin="5 5 5 10"
|
||||||
|
HorizontalExpand="true"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<OptionButton Name="FilterType"
|
||||||
|
MinWidth="250"
|
||||||
|
Margin="0 0 10 0" />
|
||||||
|
<!-- Populated in constructor -->
|
||||||
|
<LineEdit Name="FilterText"
|
||||||
|
PlaceHolder="{Loc 'criminal-records-filter-placeholder'}"
|
||||||
|
HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal" VerticalExpand="True">
|
<BoxContainer Orientation="Horizontal"
|
||||||
|
VerticalExpand="True">
|
||||||
<!-- Record listing -->
|
<!-- Record listing -->
|
||||||
<BoxContainer Orientation="Vertical" Margin="5" MinWidth="250" MaxWidth="250">
|
<BoxContainer Orientation="Vertical"
|
||||||
<Label Name="RecordListingTitle" Text="{Loc 'criminal-records-console-records-list-title'}" HorizontalExpand="True" Align="Center"/>
|
Margin="10 10"
|
||||||
<Label Name="NoRecords" Text="{Loc 'criminal-records-console-no-records'}" HorizontalExpand="True" Align="Center" FontColorOverride="DarkGray"/>
|
MinWidth="250"
|
||||||
|
MaxWidth="250">
|
||||||
|
<Label Name="RecordListingTitle"
|
||||||
|
Text="{Loc 'criminal-records-console-records-list-title'}"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
Align="Center" />
|
||||||
|
<Label Name="NoRecords"
|
||||||
|
Text="{Loc 'criminal-records-console-no-records'}"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
Align="Center"
|
||||||
|
FontColorOverride="DarkGray" />
|
||||||
<ScrollContainer VerticalExpand="True">
|
<ScrollContainer VerticalExpand="True">
|
||||||
<ItemList Name="RecordListing"/> <!-- Populated when loading state -->
|
<ItemList Name="RecordListing" />
|
||||||
|
<!-- Populated when loading state -->
|
||||||
</ScrollContainer>
|
</ScrollContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Label Name="RecordUnselected" Text="{Loc 'criminal-records-console-select-record-info'}" HorizontalExpand="True" Align="Center" FontColorOverride="DarkGray"/>
|
<Label Name="RecordUnselected"
|
||||||
|
Text="{Loc 'criminal-records-console-select-record-info'}"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
Align="Center"
|
||||||
|
FontColorOverride="DarkGray" />
|
||||||
<!-- Selected record info -->
|
<!-- Selected record info -->
|
||||||
<BoxContainer Name="PersonContainer" Orientation="Vertical" Margin="5" Visible="False">
|
<BoxContainer Name="PersonContainer"
|
||||||
<Label Name="PersonName" StyleClasses="LabelBig"/>
|
Orientation="Vertical"
|
||||||
<Label Name="PersonPrints"/>
|
VerticalExpand="True"
|
||||||
<Label Name="PersonDna"/>
|
HorizontalExpand="True"
|
||||||
<PanelContainer StyleClasses="LowDivider" Margin="0 5 0 5" />
|
Margin="5"
|
||||||
<BoxContainer Orientation="Horizontal" Margin="5 5 5 5">
|
Visible="False">
|
||||||
<Label Name="StatusLabel" Text="{Loc 'criminal-records-console-status'}" FontColorOverride="DarkGray"/>
|
<Label Name="PersonName"
|
||||||
<OptionButton Name="StatusOptionButton"/> <!-- Populated in constructor -->
|
Margin="0 0 0 5"
|
||||||
|
StyleClasses="LabelBig" />
|
||||||
|
<BoxContainer Orientation="Horizontal"
|
||||||
|
Margin="0 0 0 5">
|
||||||
|
<Label Text="{Loc 'crew-monitoring-user-interface-job'}"
|
||||||
|
FontColorOverride="DarkGray" />
|
||||||
|
<TextureRect Name="PersonJobIcon"
|
||||||
|
TextureScale="2 2"
|
||||||
|
Margin="6 0"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<Label Name="PersonJob" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<RichTextLabel Name="WantedReason" Visible="False"/>
|
<BoxContainer Orientation="Horizontal"
|
||||||
<Button Name="HistoryButton" Text="{Loc 'criminal-records-console-crime-history'}"/>
|
Margin="0 0 0 5">
|
||||||
|
<Label Text="{Loc 'general-station-record-prints-filter'}"
|
||||||
|
FontColorOverride="DarkGray" />
|
||||||
|
<Label Text=":"
|
||||||
|
Margin="0 0 6 0"
|
||||||
|
FontColorOverride="DarkGray" />
|
||||||
|
<Label Name="PersonPrints" />
|
||||||
|
</BoxContainer>
|
||||||
|
<BoxContainer Orientation="Horizontal"
|
||||||
|
Margin="0 0 0 5">
|
||||||
|
<Label Text="{Loc 'general-station-record-dna-filter'}"
|
||||||
|
FontColorOverride="DarkGray" />
|
||||||
|
<Label Text=":"
|
||||||
|
Margin="0 0 6 0"
|
||||||
|
FontColorOverride="DarkGray" />
|
||||||
|
<Label Name="PersonDna" />
|
||||||
|
</BoxContainer>
|
||||||
|
<PanelContainer StyleClasses="LowDivider"
|
||||||
|
Margin="0 5 0 5" />
|
||||||
|
<BoxContainer Orientation="Horizontal"
|
||||||
|
Margin="0 5 0 5">
|
||||||
|
<Label Name="StatusLabel"
|
||||||
|
Text="{Loc 'criminal-records-console-status'}"
|
||||||
|
FontColorOverride="DarkGray" />
|
||||||
|
<Label Text=":"
|
||||||
|
FontColorOverride="DarkGray" />
|
||||||
|
<Label Name="PersonStatus"
|
||||||
|
FontColorOverride="DarkGray" />
|
||||||
|
<AnimatedTextureRect Name="PersonStatusTX"
|
||||||
|
Margin="8 0" />
|
||||||
|
<OptionButton Name="StatusOptionButton"
|
||||||
|
MinWidth="130" />
|
||||||
|
<!-- Populated in constructor -->
|
||||||
|
</BoxContainer>
|
||||||
|
<RichTextLabel Name="WantedReason"
|
||||||
|
Visible="False"
|
||||||
|
MaxWidth="425" />
|
||||||
|
<Button Name="HistoryButton"
|
||||||
|
Text="{Loc 'criminal-records-console-crime-history'}"
|
||||||
|
Margin="0 5" />
|
||||||
|
</BoxContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
<BoxContainer Orientation="Horizontal"
|
||||||
|
Margin="0 0 0 5">
|
||||||
|
<OptionButton
|
||||||
|
Name="CrewListFilter"
|
||||||
|
MinWidth="250"
|
||||||
|
Margin="10 0 10 0" />
|
||||||
|
</BoxContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
<!-- Footer -->
|
||||||
|
<BoxContainer Orientation="Vertical">
|
||||||
|
<PanelContainer StyleClasses="LowDivider" />
|
||||||
|
<BoxContainer Orientation="Horizontal"
|
||||||
|
Margin="10 2 5 0"
|
||||||
|
VerticalAlignment="Bottom">
|
||||||
|
<Label Text="{Loc 'criminal-records-console-flavor-left'}"
|
||||||
|
StyleClasses="WindowFooterText" />
|
||||||
|
<Label Text="{Loc 'criminal-records-console-flavor-right'}"
|
||||||
|
StyleClasses="WindowFooterText"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
Margin="0 0 5 0" />
|
||||||
|
<TextureRect StyleClasses="NTLogoDark"
|
||||||
|
Stretch="KeepAspectCentered"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
SetSize="19 19" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ using Robust.Shared.Prototypes;
|
|||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using Content.Shared.StatusIcon;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
|
||||||
namespace Content.Client.CriminalRecords;
|
namespace Content.Client.CriminalRecords;
|
||||||
|
|
||||||
@@ -24,6 +27,8 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
|
|||||||
private readonly IPrototypeManager _proto;
|
private readonly IPrototypeManager _proto;
|
||||||
private readonly IRobustRandom _random;
|
private readonly IRobustRandom _random;
|
||||||
private readonly AccessReaderSystem _accessReader;
|
private readonly AccessReaderSystem _accessReader;
|
||||||
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||||
|
private readonly SpriteSystem _spriteSystem;
|
||||||
|
|
||||||
public readonly EntityUid Console;
|
public readonly EntityUid Console;
|
||||||
|
|
||||||
@@ -33,10 +38,12 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
|
|||||||
public Action<uint?>? OnKeySelected;
|
public Action<uint?>? OnKeySelected;
|
||||||
public Action<StationRecordFilterType, string>? OnFiltersChanged;
|
public Action<StationRecordFilterType, string>? OnFiltersChanged;
|
||||||
public Action<SecurityStatus>? OnStatusSelected;
|
public Action<SecurityStatus>? OnStatusSelected;
|
||||||
|
public Action<uint>? OnCheckStatus;
|
||||||
public Action<CriminalRecord, bool, bool>? OnHistoryUpdated;
|
public Action<CriminalRecord, bool, bool>? OnHistoryUpdated;
|
||||||
public Action? OnHistoryClosed;
|
public Action? OnHistoryClosed;
|
||||||
public Action<SecurityStatus, string>? OnDialogConfirmed;
|
public Action<SecurityStatus, string>? OnDialogConfirmed;
|
||||||
|
|
||||||
|
public Action<SecurityStatus>? OnStatusFilterPressed;
|
||||||
private uint _maxLength;
|
private uint _maxLength;
|
||||||
private bool _access;
|
private bool _access;
|
||||||
private uint? _selectedKey;
|
private uint? _selectedKey;
|
||||||
@@ -46,6 +53,8 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
|
|||||||
|
|
||||||
private StationRecordFilterType _currentFilterType;
|
private StationRecordFilterType _currentFilterType;
|
||||||
|
|
||||||
|
private SecurityStatus _currentCrewListFilter;
|
||||||
|
|
||||||
public CriminalRecordsConsoleWindow(EntityUid console, uint maxLength, IPlayerManager playerManager, IPrototypeManager prototypeManager, IRobustRandom robustRandom, AccessReaderSystem accessReader)
|
public CriminalRecordsConsoleWindow(EntityUid console, uint maxLength, IPlayerManager playerManager, IPrototypeManager prototypeManager, IRobustRandom robustRandom, AccessReaderSystem accessReader)
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
@@ -55,10 +64,14 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
|
|||||||
_proto = prototypeManager;
|
_proto = prototypeManager;
|
||||||
_random = robustRandom;
|
_random = robustRandom;
|
||||||
_accessReader = accessReader;
|
_accessReader = accessReader;
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
_spriteSystem = _entManager.System<SpriteSystem>();
|
||||||
|
|
||||||
_maxLength = maxLength;
|
_maxLength = maxLength;
|
||||||
_currentFilterType = StationRecordFilterType.Name;
|
_currentFilterType = StationRecordFilterType.Name;
|
||||||
|
|
||||||
|
_currentCrewListFilter = SecurityStatus.None;
|
||||||
|
|
||||||
OpenCentered();
|
OpenCentered();
|
||||||
|
|
||||||
foreach (var item in Enum.GetValues<StationRecordFilterType>())
|
foreach (var item in Enum.GetValues<StationRecordFilterType>())
|
||||||
@@ -71,6 +84,12 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
|
|||||||
AddStatusSelect(status);
|
AddStatusSelect(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Populate status to filter crew list
|
||||||
|
foreach (var item in Enum.GetValues<SecurityStatus>())
|
||||||
|
{
|
||||||
|
CrewListFilter.AddItem(GetCrewListFilterLocals(item), (int)item);
|
||||||
|
}
|
||||||
|
|
||||||
OnClose += () => _reasonDialog?.Close();
|
OnClose += () => _reasonDialog?.Close();
|
||||||
|
|
||||||
RecordListing.OnItemSelected += args =>
|
RecordListing.OnItemSelected += args =>
|
||||||
@@ -97,6 +116,20 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Select Status to filter crew
|
||||||
|
CrewListFilter.OnItemSelected += eventArgs =>
|
||||||
|
{
|
||||||
|
var type = (SecurityStatus)eventArgs.Id;
|
||||||
|
|
||||||
|
if (_currentCrewListFilter != type)
|
||||||
|
{
|
||||||
|
_currentCrewListFilter = type;
|
||||||
|
|
||||||
|
StatusFilterPressed(type);
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
FilterText.OnTextEntered += args =>
|
FilterText.OnTextEntered += args =>
|
||||||
{
|
{
|
||||||
FilterListingOfRecords(args.Text);
|
FilterListingOfRecords(args.Text);
|
||||||
@@ -114,6 +147,11 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void StatusFilterPressed(SecurityStatus statusSelected)
|
||||||
|
{
|
||||||
|
OnStatusFilterPressed?.Invoke(statusSelected);
|
||||||
|
}
|
||||||
|
|
||||||
public void UpdateState(CriminalRecordsConsoleState state)
|
public void UpdateState(CriminalRecordsConsoleState state)
|
||||||
{
|
{
|
||||||
if (state.Filter != null)
|
if (state.Filter != null)
|
||||||
@@ -129,10 +167,14 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.FilterStatus != _currentCrewListFilter)
|
||||||
|
{
|
||||||
|
_currentCrewListFilter = state.FilterStatus;
|
||||||
|
}
|
||||||
|
|
||||||
_selectedKey = state.SelectedKey;
|
_selectedKey = state.SelectedKey;
|
||||||
|
|
||||||
FilterType.SelectId((int)_currentFilterType);
|
FilterType.SelectId((int)_currentFilterType);
|
||||||
|
CrewListFilter.SelectId((int)_currentCrewListFilter);
|
||||||
NoRecords.Visible = state.RecordListing == null || state.RecordListing.Count == 0;
|
NoRecords.Visible = state.RecordListing == null || state.RecordListing.Count == 0;
|
||||||
PopulateRecordListing(state.RecordListing);
|
PopulateRecordListing(state.RecordListing);
|
||||||
|
|
||||||
@@ -216,19 +258,40 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
|
|||||||
j--;
|
j--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PopulateRecordContainer(GeneralStationRecord stationRecord, CriminalRecord criminalRecord)
|
private void PopulateRecordContainer(GeneralStationRecord stationRecord, CriminalRecord criminalRecord)
|
||||||
{
|
{
|
||||||
|
var specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Misc/job_icons.rsi"), "Unknown");
|
||||||
var na = Loc.GetString("generic-not-available-shorthand");
|
var na = Loc.GetString("generic-not-available-shorthand");
|
||||||
PersonName.Text = stationRecord.Name;
|
PersonName.Text = stationRecord.Name;
|
||||||
PersonPrints.Text = Loc.GetString("general-station-record-console-record-fingerprint", ("fingerprint", stationRecord.Fingerprint ?? na));
|
PersonJob.Text = stationRecord.JobTitle ?? na;
|
||||||
PersonDna.Text = Loc.GetString("general-station-record-console-record-dna", ("dna", stationRecord.DNA ?? na));
|
|
||||||
|
// Job icon
|
||||||
|
if (_proto.TryIndex<JobIconPrototype>(stationRecord.JobIcon, out var proto))
|
||||||
|
{
|
||||||
|
PersonJobIcon.Texture = _spriteSystem.Frame0(proto.Icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
PersonPrints.Text = stationRecord.Fingerprint ?? Loc.GetString("generic-not-available-shorthand");
|
||||||
|
PersonDna.Text = stationRecord.DNA ?? Loc.GetString("generic-not-available-shorthand");
|
||||||
|
|
||||||
|
if (criminalRecord.Status != SecurityStatus.None)
|
||||||
|
{
|
||||||
|
specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Misc/security_icons.rsi"), GetStatusIcon(criminalRecord.Status));
|
||||||
|
}
|
||||||
|
PersonStatusTX.SetFromSpriteSpecifier(specifier);
|
||||||
|
PersonStatusTX.DisplayRect.TextureScale = new Vector2(3f, 3f);
|
||||||
|
|
||||||
StatusOptionButton.SelectId((int)criminalRecord.Status);
|
StatusOptionButton.SelectId((int)criminalRecord.Status);
|
||||||
if (criminalRecord.Reason is { } reason)
|
if (criminalRecord.Reason is { } reason)
|
||||||
{
|
{
|
||||||
var message = FormattedMessage.FromMarkupOrThrow(Loc.GetString("criminal-records-console-wanted-reason"));
|
var message = FormattedMessage.FromMarkupOrThrow(Loc.GetString("criminal-records-console-wanted-reason"));
|
||||||
|
|
||||||
|
if (criminalRecord.Status == SecurityStatus.Suspected)
|
||||||
|
{
|
||||||
|
message = FormattedMessage.FromMarkupOrThrow(Loc.GetString("criminal-records-console-suspected-reason"));
|
||||||
|
}
|
||||||
message.AddText($": {reason}");
|
message.AddText($": {reason}");
|
||||||
|
|
||||||
WantedReason.SetMessage(message);
|
WantedReason.SetMessage(message);
|
||||||
WantedReason.Visible = true;
|
WantedReason.Visible = true;
|
||||||
}
|
}
|
||||||
@@ -288,9 +351,37 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
|
|||||||
|
|
||||||
_reasonDialog.OnClose += () => { _reasonDialog = null; };
|
_reasonDialog.OnClose += () => { _reasonDialog = null; };
|
||||||
}
|
}
|
||||||
|
private string GetStatusIcon(SecurityStatus status)
|
||||||
|
{
|
||||||
|
return status switch
|
||||||
|
{
|
||||||
|
SecurityStatus.Paroled => "hud_paroled",
|
||||||
|
SecurityStatus.Wanted => "hud_wanted",
|
||||||
|
SecurityStatus.Detained => "hud_incarcerated",
|
||||||
|
SecurityStatus.Discharged => "hud_discharged",
|
||||||
|
SecurityStatus.Suspected => "hud_suspected",
|
||||||
|
_ => "SecurityIconNone"
|
||||||
|
};
|
||||||
|
}
|
||||||
private string GetTypeFilterLocals(StationRecordFilterType type)
|
private string GetTypeFilterLocals(StationRecordFilterType type)
|
||||||
{
|
{
|
||||||
return Loc.GetString($"criminal-records-{type.ToString().ToLower()}-filter");
|
return Loc.GetString($"criminal-records-{type.ToString().ToLower()}-filter");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetCrewListFilterLocals(SecurityStatus type)
|
||||||
|
{
|
||||||
|
string result;
|
||||||
|
|
||||||
|
// If "NONE" override to "show all"
|
||||||
|
if (type == SecurityStatus.None)
|
||||||
|
{
|
||||||
|
result = Loc.GetString("criminal-records-console-show-all");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = Loc.GetString($"criminal-records-status-{type.ToString().ToLower()}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,17 +5,24 @@ namespace Content.Client.Dice;
|
|||||||
|
|
||||||
public sealed class DiceSystem : SharedDiceSystem
|
public sealed class DiceSystem : SharedDiceSystem
|
||||||
{
|
{
|
||||||
protected override void UpdateVisuals(EntityUid uid, DiceComponent? die = null)
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref die) || !TryComp(uid, out SpriteComponent? sprite))
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<DiceComponent, AfterAutoHandleStateEvent>(OnDiceAfterHandleState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDiceAfterHandleState(Entity<DiceComponent> entity, ref AfterAutoHandleStateEvent args)
|
||||||
|
{
|
||||||
|
if (!TryComp<SpriteComponent>(entity, out var sprite))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// TODO maybe just move each diue to its own RSI?
|
// TODO maybe just move each die to its own RSI?
|
||||||
var state = sprite.LayerGetState(0).Name;
|
var state = sprite.LayerGetState(0).Name;
|
||||||
if (state == null)
|
if (state == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var prefix = state.Substring(0, state.IndexOf('_'));
|
var prefix = state.Substring(0, state.IndexOf('_'));
|
||||||
sprite.LayerSetState(0, $"{prefix}_{die.CurrentValue}");
|
sprite.LayerSetState(0, $"{prefix}_{entity.Comp.CurrentValue}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ public sealed class DoAfterSystem : SharedDoAfterSystem
|
|||||||
_overlay.RemoveOverlay<DoAfterOverlay>();
|
_overlay.RemoveOverlay<DoAfterOverlay>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma warning disable RA0028 // No base call in overriden function
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
|
#pragma warning restore RA0028 // No base call in overriden function
|
||||||
{
|
{
|
||||||
// Currently this only predicts do afters initiated by the player.
|
// Currently this only predicts do afters initiated by the player.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Content.Client.Wires.Visualizers;
|
using Content.Client.Wires.Visualizers;
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
using Content.Shared.Doors.Systems;
|
using Content.Shared.Doors.Systems;
|
||||||
|
using Content.Shared.Power;
|
||||||
using Robust.Client.Animations;
|
using Robust.Client.Animations;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
|
|
||||||
@@ -84,7 +85,8 @@ public sealed class AirlockSystem : SharedAirlockSystem
|
|||||||
if (!_appearanceSystem.TryGetData<DoorState>(uid, DoorVisuals.State, out var state, args.Component))
|
if (!_appearanceSystem.TryGetData<DoorState>(uid, DoorVisuals.State, out var state, args.Component))
|
||||||
state = DoorState.Closed;
|
state = DoorState.Closed;
|
||||||
|
|
||||||
if (_appearanceSystem.TryGetData<bool>(uid, DoorVisuals.Powered, out var powered, args.Component) && powered)
|
if (_appearanceSystem.TryGetData<bool>(uid, PowerDeviceVisuals.Powered, out var powered, args.Component)
|
||||||
|
&& powered)
|
||||||
{
|
{
|
||||||
boltedVisible = _appearanceSystem.TryGetData<bool>(uid, DoorVisuals.BoltLights, out var lights, args.Component)
|
boltedVisible = _appearanceSystem.TryGetData<bool>(uid, DoorVisuals.BoltLights, out var lights, args.Component)
|
||||||
&& lights && (state == DoorState.Closed || state == DoorState.Welded);
|
&& lights && (state == DoorState.Closed || state == DoorState.Welded);
|
||||||
|
|||||||
@@ -21,112 +21,131 @@ public sealed class DoorSystem : SharedDoorSystem
|
|||||||
protected override void OnComponentInit(Entity<DoorComponent> ent, ref ComponentInit args)
|
protected override void OnComponentInit(Entity<DoorComponent> ent, ref ComponentInit args)
|
||||||
{
|
{
|
||||||
var comp = ent.Comp;
|
var comp = ent.Comp;
|
||||||
comp.OpenSpriteStates = new(2);
|
comp.OpenSpriteStates = new List<(DoorVisualLayers, string)>(2);
|
||||||
comp.ClosedSpriteStates = new(2);
|
comp.ClosedSpriteStates = new List<(DoorVisualLayers, string)>(2);
|
||||||
|
|
||||||
comp.OpenSpriteStates.Add((DoorVisualLayers.Base, comp.OpenSpriteState));
|
comp.OpenSpriteStates.Add((DoorVisualLayers.Base, comp.OpenSpriteState));
|
||||||
comp.ClosedSpriteStates.Add((DoorVisualLayers.Base, comp.ClosedSpriteState));
|
comp.ClosedSpriteStates.Add((DoorVisualLayers.Base, comp.ClosedSpriteState));
|
||||||
|
|
||||||
comp.OpeningAnimation = new Animation()
|
comp.OpeningAnimation = new Animation
|
||||||
{
|
{
|
||||||
Length = TimeSpan.FromSeconds(comp.OpeningAnimationTime),
|
Length = TimeSpan.FromSeconds(comp.OpeningAnimationTime),
|
||||||
AnimationTracks =
|
AnimationTracks =
|
||||||
{
|
{
|
||||||
new AnimationTrackSpriteFlick()
|
new AnimationTrackSpriteFlick
|
||||||
{
|
{
|
||||||
LayerKey = DoorVisualLayers.Base,
|
LayerKey = DoorVisualLayers.Base,
|
||||||
KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(comp.OpeningSpriteState, 0f) }
|
KeyFrames =
|
||||||
}
|
{
|
||||||
|
new AnimationTrackSpriteFlick.KeyFrame(comp.OpeningSpriteState, 0f),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
comp.ClosingAnimation = new Animation()
|
comp.ClosingAnimation = new Animation
|
||||||
{
|
{
|
||||||
Length = TimeSpan.FromSeconds(comp.ClosingAnimationTime),
|
Length = TimeSpan.FromSeconds(comp.ClosingAnimationTime),
|
||||||
AnimationTracks =
|
AnimationTracks =
|
||||||
{
|
{
|
||||||
new AnimationTrackSpriteFlick()
|
new AnimationTrackSpriteFlick
|
||||||
{
|
{
|
||||||
LayerKey = DoorVisualLayers.Base,
|
LayerKey = DoorVisualLayers.Base,
|
||||||
KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(comp.ClosingSpriteState, 0f) }
|
KeyFrames =
|
||||||
}
|
{
|
||||||
|
new AnimationTrackSpriteFlick.KeyFrame(comp.ClosingSpriteState, 0f),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
comp.EmaggingAnimation = new Animation ()
|
comp.EmaggingAnimation = new Animation
|
||||||
{
|
{
|
||||||
Length = TimeSpan.FromSeconds(comp.EmaggingAnimationTime),
|
Length = TimeSpan.FromSeconds(comp.EmaggingAnimationTime),
|
||||||
AnimationTracks =
|
AnimationTracks =
|
||||||
{
|
{
|
||||||
new AnimationTrackSpriteFlick()
|
new AnimationTrackSpriteFlick
|
||||||
{
|
{
|
||||||
LayerKey = DoorVisualLayers.BaseUnlit,
|
LayerKey = DoorVisualLayers.BaseUnlit,
|
||||||
KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(comp.EmaggingSpriteState, 0f) }
|
KeyFrames =
|
||||||
}
|
{
|
||||||
|
new AnimationTrackSpriteFlick.KeyFrame(comp.EmaggingSpriteState, 0f),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAppearanceChange(EntityUid uid, DoorComponent comp, ref AppearanceChangeEvent args)
|
private void OnAppearanceChange(Entity<DoorComponent> entity, ref AppearanceChangeEvent args)
|
||||||
{
|
{
|
||||||
if (args.Sprite == null)
|
if (args.Sprite == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(!AppearanceSystem.TryGetData<DoorState>(uid, DoorVisuals.State, out var state, args.Component))
|
if (!AppearanceSystem.TryGetData<DoorState>(entity, DoorVisuals.State, out var state, args.Component))
|
||||||
state = DoorState.Closed;
|
state = DoorState.Closed;
|
||||||
|
|
||||||
if (AppearanceSystem.TryGetData<string>(uid, DoorVisuals.BaseRSI, out var baseRsi, args.Component))
|
if (AppearanceSystem.TryGetData<string>(entity, DoorVisuals.BaseRSI, out var baseRsi, args.Component))
|
||||||
|
UpdateSpriteLayers(args.Sprite, baseRsi);
|
||||||
|
|
||||||
|
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.AnimationKey))
|
||||||
|
_animationSystem.Stop(entity.Owner, DoorComponent.AnimationKey);
|
||||||
|
|
||||||
|
UpdateAppearanceForDoorState(entity, args.Sprite, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAppearanceForDoorState(Entity<DoorComponent> entity, SpriteComponent sprite, DoorState state)
|
||||||
|
{
|
||||||
|
sprite.DrawDepth = state is DoorState.Open ? entity.Comp.OpenDrawDepth : entity.Comp.ClosedDrawDepth;
|
||||||
|
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case DoorState.Open:
|
||||||
|
foreach (var (layer, layerState) in entity.Comp.OpenSpriteStates)
|
||||||
|
{
|
||||||
|
sprite.LayerSetState(layer, layerState);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
case DoorState.Closed:
|
||||||
|
foreach (var (layer, layerState) in entity.Comp.ClosedSpriteStates)
|
||||||
|
{
|
||||||
|
sprite.LayerSetState(layer, layerState);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
case DoorState.Opening:
|
||||||
|
if (entity.Comp.OpeningAnimationTime == 0.0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_animationSystem.Play(entity, (Animation)entity.Comp.OpeningAnimation, DoorComponent.AnimationKey);
|
||||||
|
|
||||||
|
return;
|
||||||
|
case DoorState.Closing:
|
||||||
|
if (entity.Comp.ClosingAnimationTime == 0.0 || entity.Comp.CurrentlyCrushing.Count != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_animationSystem.Play(entity, (Animation)entity.Comp.ClosingAnimation, DoorComponent.AnimationKey);
|
||||||
|
|
||||||
|
return;
|
||||||
|
case DoorState.Denying:
|
||||||
|
_animationSystem.Play(entity, (Animation)entity.Comp.DenyingAnimation, DoorComponent.AnimationKey);
|
||||||
|
|
||||||
|
return;
|
||||||
|
case DoorState.Emagging:
|
||||||
|
_animationSystem.Play(entity, (Animation)entity.Comp.EmaggingAnimation, DoorComponent.AnimationKey);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSpriteLayers(SpriteComponent sprite, string baseRsi)
|
||||||
{
|
{
|
||||||
if (!_resourceCache.TryGetResource<RSIResource>(SpriteSpecifierSerializer.TextureRoot / baseRsi, out var res))
|
if (!_resourceCache.TryGetResource<RSIResource>(SpriteSpecifierSerializer.TextureRoot / baseRsi, out var res))
|
||||||
{
|
{
|
||||||
Log.Error("Unable to load RSI '{0}'. Trace:\n{1}", baseRsi, Environment.StackTrace);
|
Log.Error("Unable to load RSI '{0}'. Trace:\n{1}", baseRsi, Environment.StackTrace);
|
||||||
}
|
return;
|
||||||
foreach (var layer in args.Sprite.AllLayers)
|
|
||||||
{
|
|
||||||
layer.Rsi = res?.RSI;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TryComp<AnimationPlayerComponent>(uid, out var animPlayer);
|
sprite.BaseRSI = res.RSI;
|
||||||
if (_animationSystem.HasRunningAnimation(uid, animPlayer, DoorComponent.AnimationKey))
|
|
||||||
_animationSystem.Stop(uid, animPlayer, DoorComponent.AnimationKey); // Halt all running anomations.
|
|
||||||
|
|
||||||
args.Sprite.DrawDepth = comp.ClosedDrawDepth;
|
|
||||||
switch(state)
|
|
||||||
{
|
|
||||||
case DoorState.Open:
|
|
||||||
args.Sprite.DrawDepth = comp.OpenDrawDepth;
|
|
||||||
foreach(var (layer, layerState) in comp.OpenSpriteStates)
|
|
||||||
{
|
|
||||||
args.Sprite.LayerSetState(layer, layerState);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DoorState.Closed:
|
|
||||||
foreach(var (layer, layerState) in comp.ClosedSpriteStates)
|
|
||||||
{
|
|
||||||
args.Sprite.LayerSetState(layer, layerState);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DoorState.Opening:
|
|
||||||
if (animPlayer != null && comp.OpeningAnimationTime != 0.0)
|
|
||||||
_animationSystem.Play((uid, animPlayer), (Animation)comp.OpeningAnimation, DoorComponent.AnimationKey);
|
|
||||||
break;
|
|
||||||
case DoorState.Closing:
|
|
||||||
if (animPlayer != null && comp.ClosingAnimationTime != 0.0 && comp.CurrentlyCrushing.Count == 0)
|
|
||||||
_animationSystem.Play((uid, animPlayer), (Animation)comp.ClosingAnimation, DoorComponent.AnimationKey);
|
|
||||||
break;
|
|
||||||
case DoorState.Denying:
|
|
||||||
if (animPlayer != null)
|
|
||||||
_animationSystem.Play((uid, animPlayer), (Animation)comp.DenyingAnimation, DoorComponent.AnimationKey);
|
|
||||||
break;
|
|
||||||
case DoorState.Welded:
|
|
||||||
break;
|
|
||||||
case DoorState.Emagging:
|
|
||||||
if (animPlayer != null)
|
|
||||||
_animationSystem.Play((uid, animPlayer), (Animation)comp.EmaggingAnimation, DoorComponent.AnimationKey);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException($"Invalid door visual state {state}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ namespace Content.Client.GameTicking.Managers
|
|||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeNetworkEvent<TickerJoinLobbyEvent>(JoinLobby);
|
SubscribeNetworkEvent<TickerJoinLobbyEvent>(JoinLobby);
|
||||||
SubscribeNetworkEvent<TickerJoinGameEvent>(JoinGame);
|
SubscribeNetworkEvent<TickerJoinGameEvent>(JoinGame);
|
||||||
SubscribeNetworkEvent<TickerConnectionStatusEvent>(ConnectionStatus);
|
SubscribeNetworkEvent<TickerConnectionStatusEvent>(ConnectionStatus);
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ public sealed partial class GhostRoleRadioMenu : RadialMenu
|
|||||||
|
|
||||||
var button = new GhostRoleRadioMenuButton()
|
var button = new GhostRoleRadioMenuButton()
|
||||||
{
|
{
|
||||||
StyleClasses = { "RadialMenuButton" },
|
|
||||||
SetSize = new Vector2(64, 64),
|
SetSize = new Vector2(64, 64),
|
||||||
ToolTip = Loc.GetString(ghostRoleProto.Name),
|
ToolTip = Loc.GetString(ghostRoleProto.Name),
|
||||||
ProtoId = ghostRoleProto.ID,
|
ProtoId = ghostRoleProto.ID,
|
||||||
@@ -100,7 +99,7 @@ public sealed partial class GhostRoleRadioMenu : RadialMenu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class GhostRoleRadioMenuButton : RadialMenuTextureButton
|
public sealed class GhostRoleRadioMenuButton : RadialMenuTextureButtonWithSector
|
||||||
{
|
{
|
||||||
public ProtoId<GhostRolePrototype> ProtoId { get; set; }
|
public ProtoId<GhostRolePrototype> ProtoId { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ namespace Content.Client.Ghost
|
|||||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
[Dependency] private readonly IClientConsoleHost _console = default!;
|
||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||||
|
[Dependency] private readonly PointLightSystem _pointLightSystem = default!;
|
||||||
[Dependency] private readonly ContentEyeSystem _contentEye = default!;
|
[Dependency] private readonly ContentEyeSystem _contentEye = default!;
|
||||||
|
|
||||||
public int AvailableGhostRoleCount { get; private set; }
|
public int AvailableGhostRoleCount { get; private set; }
|
||||||
@@ -79,8 +80,27 @@ namespace Content.Client.Ghost
|
|||||||
if (args.Handled)
|
if (args.Handled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Popup.PopupEntity(Loc.GetString("ghost-gui-toggle-lighting-manager-popup"), args.Performer);
|
TryComp<PointLightComponent>(uid, out var light);
|
||||||
_contentEye.RequestToggleLight(uid, component);
|
|
||||||
|
if (!component.DrawLight)
|
||||||
|
{
|
||||||
|
// normal lighting
|
||||||
|
Popup.PopupEntity(Loc.GetString("ghost-gui-toggle-lighting-manager-popup-normal"), args.Performer);
|
||||||
|
_contentEye.RequestEye(component.DrawFov, true);
|
||||||
|
}
|
||||||
|
else if (!light?.Enabled ?? false) // skip this option if we have no PointLightComponent
|
||||||
|
{
|
||||||
|
// enable personal light
|
||||||
|
Popup.PopupEntity(Loc.GetString("ghost-gui-toggle-lighting-manager-popup-personal-light"), args.Performer);
|
||||||
|
_pointLightSystem.SetEnabled(uid, true, light);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// fullbright mode
|
||||||
|
Popup.PopupEntity(Loc.GetString("ghost-gui-toggle-lighting-manager-popup-fullbright"), args.Performer);
|
||||||
|
_contentEye.RequestEye(component.DrawFov, false);
|
||||||
|
_pointLightSystem.SetEnabled(uid, false, light);
|
||||||
|
}
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,45 +2,38 @@ using Content.Shared.Chat.TypingIndicator;
|
|||||||
using Content.Shared.Holopad;
|
using Content.Shared.Holopad;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.Player;
|
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using DrawDepth = Content.Shared.DrawDepth.DrawDepth;
|
||||||
|
|
||||||
namespace Content.Client.Holopad;
|
namespace Content.Client.Holopad;
|
||||||
|
|
||||||
public sealed class HolopadSystem : SharedHolopadSystem
|
public sealed class HolopadSystem : SharedHolopadSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<HolopadHologramComponent, ComponentInit>(OnComponentInit);
|
SubscribeLocalEvent<HolopadHologramComponent, ComponentStartup>(OnComponentStartup);
|
||||||
SubscribeLocalEvent<HolopadHologramComponent, BeforePostShaderRenderEvent>(OnShaderRender);
|
SubscribeLocalEvent<HolopadHologramComponent, BeforePostShaderRenderEvent>(OnShaderRender);
|
||||||
SubscribeAllEvent<TypingChangedEvent>(OnTypingChanged);
|
SubscribeAllEvent<TypingChangedEvent>(OnTypingChanged);
|
||||||
|
|
||||||
SubscribeNetworkEvent<PlayerSpriteStateRequest>(OnPlayerSpriteStateRequest);
|
|
||||||
SubscribeNetworkEvent<PlayerSpriteStateMessage>(OnPlayerSpriteStateMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnComponentInit(EntityUid uid, HolopadHologramComponent component, ComponentInit ev)
|
private void OnComponentStartup(Entity<HolopadHologramComponent> entity, ref ComponentStartup ev)
|
||||||
{
|
{
|
||||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
UpdateHologramSprite(entity, entity.Comp.LinkedEntity);
|
||||||
return;
|
|
||||||
|
|
||||||
UpdateHologramSprite(uid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnShaderRender(EntityUid uid, HolopadHologramComponent component, BeforePostShaderRenderEvent ev)
|
private void OnShaderRender(Entity<HolopadHologramComponent> entity, ref BeforePostShaderRenderEvent ev)
|
||||||
{
|
{
|
||||||
if (ev.Sprite.PostShader == null)
|
if (ev.Sprite.PostShader == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ev.Sprite.PostShader.SetParameter("t", (float)_timing.CurTime.TotalSeconds * component.ScrollRate);
|
UpdateHologramSprite(entity, entity.Comp.LinkedEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTypingChanged(TypingChangedEvent ev, EntitySessionEventArgs args)
|
private void OnTypingChanged(TypingChangedEvent ev, EntitySessionEventArgs args)
|
||||||
@@ -57,100 +50,66 @@ public sealed class HolopadSystem : SharedHolopadSystem
|
|||||||
RaiseNetworkEvent(netEv);
|
RaiseNetworkEvent(netEv);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPlayerSpriteStateRequest(PlayerSpriteStateRequest ev)
|
private void UpdateHologramSprite(EntityUid hologram, EntityUid? target)
|
||||||
{
|
{
|
||||||
var targetPlayer = GetEntity(ev.TargetPlayer);
|
// Get required components
|
||||||
var player = _playerManager.LocalSession?.AttachedEntity;
|
if (!TryComp<SpriteComponent>(hologram, out var hologramSprite) ||
|
||||||
|
!TryComp<HolopadHologramComponent>(hologram, out var holopadhologram))
|
||||||
// Ignore the request if received by a player who isn't the target
|
|
||||||
if (targetPlayer != player)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!TryComp<SpriteComponent>(player, out var playerSprite))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var spriteLayerData = new List<PrototypeLayerData>();
|
|
||||||
|
|
||||||
if (playerSprite.Visible)
|
|
||||||
{
|
|
||||||
// Record the RSI paths, state names and shader paramaters of all visible layers
|
|
||||||
for (int i = 0; i < playerSprite.AllLayers.Count(); i++)
|
|
||||||
{
|
|
||||||
if (!playerSprite.TryGetLayer(i, out var layer))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!layer.Visible ||
|
|
||||||
string.IsNullOrEmpty(layer.ActualRsi?.Path.ToString()) ||
|
|
||||||
string.IsNullOrEmpty(layer.State.Name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var layerDatum = new PrototypeLayerData();
|
|
||||||
layerDatum.RsiPath = layer.ActualRsi.Path.ToString();
|
|
||||||
layerDatum.State = layer.State.Name;
|
|
||||||
|
|
||||||
if (layer.CopyToShaderParameters != null)
|
|
||||||
{
|
|
||||||
var key = (string)layer.CopyToShaderParameters.LayerKey;
|
|
||||||
|
|
||||||
if (playerSprite.LayerMapTryGet(key, out var otherLayerIdx) &&
|
|
||||||
playerSprite.TryGetLayer(otherLayerIdx, out var otherLayer) &&
|
|
||||||
otherLayer.Visible)
|
|
||||||
{
|
|
||||||
layerDatum.MapKeys = new() { key };
|
|
||||||
|
|
||||||
layerDatum.CopyToShaderParameters = new PrototypeCopyToShaderParameters()
|
|
||||||
{
|
|
||||||
LayerKey = key,
|
|
||||||
ParameterTexture = layer.CopyToShaderParameters.ParameterTexture,
|
|
||||||
ParameterUV = layer.CopyToShaderParameters.ParameterUV
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spriteLayerData.Add(layerDatum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the recorded data to the server
|
|
||||||
var evResponse = new PlayerSpriteStateMessage(ev.TargetPlayer, spriteLayerData.ToArray());
|
|
||||||
RaiseNetworkEvent(evResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPlayerSpriteStateMessage(PlayerSpriteStateMessage ev)
|
|
||||||
{
|
|
||||||
UpdateHologramSprite(GetEntity(ev.SpriteEntity), ev.SpriteLayerData);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateHologramSprite(EntityUid uid, PrototypeLayerData[]? layerData = null)
|
|
||||||
{
|
|
||||||
if (!TryComp<SpriteComponent>(uid, out var hologramSprite))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!TryComp<HolopadHologramComponent>(uid, out var holopadhologram))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Remove all sprite layers
|
||||||
for (int i = hologramSprite.AllLayers.Count() - 1; i >= 0; i--)
|
for (int i = hologramSprite.AllLayers.Count() - 1; i >= 0; i--)
|
||||||
hologramSprite.RemoveLayer(i);
|
hologramSprite.RemoveLayer(i);
|
||||||
|
|
||||||
if (layerData == null || layerData.Length == 0)
|
if (TryComp<SpriteComponent>(target, out var targetSprite))
|
||||||
{
|
{
|
||||||
layerData = new PrototypeLayerData[1];
|
// Use the target's holographic avatar (if available)
|
||||||
layerData[0] = new PrototypeLayerData()
|
if (TryComp<HolographicAvatarComponent>(target, out var targetAvatar) &&
|
||||||
|
targetAvatar.LayerData != null)
|
||||||
{
|
{
|
||||||
RsiPath = holopadhologram.RsiPath,
|
for (int i = 0; i < targetAvatar.LayerData.Length; i++)
|
||||||
State = holopadhologram.RsiState
|
{
|
||||||
};
|
var layer = targetAvatar.LayerData[i];
|
||||||
|
hologramSprite.AddLayer(targetAvatar.LayerData[i], i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < layerData.Length; i++)
|
// Otherwise copy the target's current physical appearance
|
||||||
|
else
|
||||||
{
|
{
|
||||||
var layer = layerData[i];
|
hologramSprite.CopyFrom(targetSprite);
|
||||||
layer.Shader = "unshaded";
|
}
|
||||||
|
|
||||||
hologramSprite.AddLayer(layerData[i], i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateHologramShader(uid, hologramSprite, holopadhologram);
|
// There is no target, display a default sprite instead (if available)
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(holopadhologram.RsiPath) || string.IsNullOrEmpty(holopadhologram.RsiState))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var layer = new PrototypeLayerData();
|
||||||
|
layer.RsiPath = holopadhologram.RsiPath;
|
||||||
|
layer.State = holopadhologram.RsiState;
|
||||||
|
|
||||||
|
hologramSprite.AddLayer(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override specific values
|
||||||
|
hologramSprite.Color = Color.White;
|
||||||
|
hologramSprite.Offset = holopadhologram.Offset;
|
||||||
|
hologramSprite.DrawDepth = (int)DrawDepth.Mobs;
|
||||||
|
hologramSprite.NoRotation = true;
|
||||||
|
hologramSprite.DirectionOverride = Direction.South;
|
||||||
|
hologramSprite.EnableDirectionOverride = true;
|
||||||
|
|
||||||
|
// Remove shading from all layers (except displacement maps)
|
||||||
|
for (int i = 0; i < hologramSprite.AllLayers.Count(); i++)
|
||||||
|
{
|
||||||
|
if (hologramSprite.TryGetLayer(i, out var layer) && layer.ShaderPrototype != "DisplacedStencilDraw")
|
||||||
|
hologramSprite.LayerSetShader(i, "unshaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateHologramShader(hologram, hologramSprite, holopadhologram);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateHologramShader(EntityUid uid, SpriteComponent sprite, HolopadHologramComponent holopadHologram)
|
private void UpdateHologramShader(EntityUid uid, SpriteComponent sprite, HolopadHologramComponent holopadHologram)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Content.Shared.ActionBlocker;
|
|||||||
using Content.Shared.Instruments.UI;
|
using Content.Shared.Instruments.UI;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Robust.Client.Audio.Midi;
|
using Robust.Client.Audio.Midi;
|
||||||
using Robust.Client.GameObjects;
|
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
|
|
||||||
@@ -13,7 +12,6 @@ namespace Content.Client.Instruments.UI
|
|||||||
public IEntityManager Entities => EntMan;
|
public IEntityManager Entities => EntMan;
|
||||||
[Dependency] public readonly IMidiManager MidiManager = default!;
|
[Dependency] public readonly IMidiManager MidiManager = default!;
|
||||||
[Dependency] public readonly IFileDialogManager FileDialogManager = default!;
|
[Dependency] public readonly IFileDialogManager FileDialogManager = default!;
|
||||||
[Dependency] public readonly IPlayerManager PlayerManager = default!;
|
|
||||||
[Dependency] public readonly ILocalizationManager Loc = default!;
|
[Dependency] public readonly ILocalizationManager Loc = default!;
|
||||||
|
|
||||||
public readonly InstrumentSystem Instruments;
|
public readonly InstrumentSystem Instruments;
|
||||||
@@ -41,6 +39,8 @@ namespace Content.Client.Instruments.UI
|
|||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
_instrumentMenu = this.CreateWindow<InstrumentMenu>();
|
_instrumentMenu = this.CreateWindow<InstrumentMenu>();
|
||||||
_instrumentMenu.Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName;
|
_instrumentMenu.Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName;
|
||||||
|
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ public sealed class DragDropSystem : SharedDragDropSystem
|
|||||||
dragSprite.DrawDepth = (int) DrawDepth.Overlays;
|
dragSprite.DrawDepth = (int) DrawDepth.Overlays;
|
||||||
if (!dragSprite.NoRotation)
|
if (!dragSprite.NoRotation)
|
||||||
{
|
{
|
||||||
Transform(_dragShadow.Value).WorldRotation = Transform(_draggedEntity.Value).WorldRotation;
|
_transformSystem.SetWorldRotationNoLerp(_dragShadow.Value, _transformSystem.GetWorldRotation(_draggedEntity.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
// drag initiated
|
// drag initiated
|
||||||
|
|||||||
11
Content.Client/ItemRecall/ItemRecallSystem.cs
Normal file
11
Content.Client/ItemRecall/ItemRecallSystem.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Content.Shared.ItemRecall;
|
||||||
|
|
||||||
|
namespace Content.Client.ItemRecall;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// System for handling the ItemRecall ability for wizards.
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class ItemRecallSystem : SharedItemRecallSystem
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -33,8 +33,13 @@ namespace Content.Client.Labels.UI
|
|||||||
_focused = false;
|
_focused = false;
|
||||||
LabelLineEdit.Text = _label;
|
LabelLineEdit.Text = _label;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Give the editor keybard focus, since that's the only
|
protected override void Opened()
|
||||||
|
{
|
||||||
|
base.Opened();
|
||||||
|
|
||||||
|
// Give the editor keyboard focus, since that's the only
|
||||||
// thing the user will want to be doing with this UI
|
// thing the user will want to be doing with this UI
|
||||||
LabelLineEdit.GrabKeyboardFocus();
|
LabelLineEdit.GrabKeyboardFocus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ namespace Content.Client.LateJoin
|
|||||||
|
|
||||||
foreach (var department in departments)
|
foreach (var department in departments)
|
||||||
{
|
{
|
||||||
var departmentName = Loc.GetString($"department-{department.ID}");
|
var departmentName = Loc.GetString(department.Name);
|
||||||
_jobCategories[id] = new Dictionary<string, BoxContainer>();
|
_jobCategories[id] = new Dictionary<string, BoxContainer>();
|
||||||
var stationAvailable = _gameTicker.JobsAvailable[id];
|
var stationAvailable = _gameTicker.JobsAvailable[id];
|
||||||
var jobsAvailable = new List<JobPrototype>();
|
var jobsAvailable = new List<JobPrototype>();
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public sealed partial class LatheMenu : DefaultWindow
|
|||||||
|
|
||||||
if (_entityManager.TryGetComponent<LatheComponent>(Entity, out var latheComponent))
|
if (_entityManager.TryGetComponent<LatheComponent>(Entity, out var latheComponent))
|
||||||
{
|
{
|
||||||
if (!latheComponent.DynamicRecipes.Any())
|
if (!latheComponent.DynamicPacks.Any())
|
||||||
{
|
{
|
||||||
ServerListButton.Visible = false;
|
ServerListButton.Visible = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
|
|||||||
[UISystemDependency] private readonly ClientInventorySystem _inventory = default!;
|
[UISystemDependency] private readonly ClientInventorySystem _inventory = default!;
|
||||||
[UISystemDependency] private readonly StationSpawningSystem _spawn = default!;
|
[UISystemDependency] private readonly StationSpawningSystem _spawn = default!;
|
||||||
[UISystemDependency] private readonly GuidebookSystem _guide = default!;
|
[UISystemDependency] private readonly GuidebookSystem _guide = default!;
|
||||||
[UISystemDependency] private readonly LoadoutSystem _loadouts = default!;
|
|
||||||
|
|
||||||
private CharacterSetupGui? _characterSetup;
|
private CharacterSetupGui? _characterSetup;
|
||||||
private HumanoidProfileEditor? _profileEditor;
|
private HumanoidProfileEditor? _profileEditor;
|
||||||
|
|||||||
@@ -839,7 +839,7 @@ namespace Content.Client.Lobby.UI
|
|||||||
|
|
||||||
foreach (var department in departments)
|
foreach (var department in departments)
|
||||||
{
|
{
|
||||||
var departmentName = Loc.GetString($"department-{department.ID}");
|
var departmentName = Loc.GetString(department.Name);
|
||||||
|
|
||||||
if (!_jobCategories.TryGetValue(department.ID, out var category))
|
if (!_jobCategories.TryGetValue(department.ID, out var category))
|
||||||
{
|
{
|
||||||
@@ -1015,6 +1015,13 @@ namespace Content.Client.Lobby.UI
|
|||||||
_loadoutWindow.RefreshLoadouts(roleLoadout, session, collection);
|
_loadoutWindow.RefreshLoadouts(roleLoadout, session, collection);
|
||||||
_loadoutWindow.OpenCenteredLeft();
|
_loadoutWindow.OpenCenteredLeft();
|
||||||
|
|
||||||
|
_loadoutWindow.OnNameChanged += name =>
|
||||||
|
{
|
||||||
|
roleLoadout.EntityName = name;
|
||||||
|
Profile = Profile.WithLoadout(roleLoadout);
|
||||||
|
SetDirty();
|
||||||
|
};
|
||||||
|
|
||||||
_loadoutWindow.OnLoadoutPressed += (loadoutGroup, loadoutProto) =>
|
_loadoutWindow.OnLoadoutPressed += (loadoutGroup, loadoutProto) =>
|
||||||
{
|
{
|
||||||
roleLoadout.AddLoadout(loadoutGroup, loadoutProto, _prototypeManager);
|
roleLoadout.AddLoadout(loadoutGroup, loadoutProto, _prototypeManager);
|
||||||
|
|||||||
@@ -5,17 +5,15 @@
|
|||||||
SetSize="800 800"
|
SetSize="800 800"
|
||||||
MinSize="800 128">
|
MinSize="800 128">
|
||||||
<BoxContainer Orientation="Vertical" VerticalExpand="True">
|
<BoxContainer Orientation="Vertical" VerticalExpand="True">
|
||||||
<!--
|
|
||||||
<BoxContainer Name="RoleNameBox" Orientation="Vertical" Margin="10">
|
<BoxContainer Name="RoleNameBox" Orientation="Vertical" Margin="10">
|
||||||
<Label Name="LoadoutNameLabel" Text="{Loc 'loadout-name-edit-label'}"/>
|
<Label Name="LoadoutNameLabel" Text="{Loc 'loadout-name-edit-label'}"/>
|
||||||
<PanelContainer HorizontalExpand="True" SetHeight="24">
|
<PanelContainer HorizontalExpand="True" SetHeight="24">
|
||||||
<PanelContainer.PanelOverride>
|
<PanelContainer.PanelOverride>
|
||||||
<graphics:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
<graphics:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
||||||
</PanelContainer.PanelOverride>
|
</PanelContainer.PanelOverride>
|
||||||
<LineEdit Name="RoleNameEdit" ToolTip="{Loc 'loadout-name-edit-tooltip'}" VerticalExpand="True" HorizontalExpand="True"/>
|
<LineEdit Name="RoleNameEdit" VerticalExpand="True" HorizontalExpand="True"/>
|
||||||
</PanelContainer>
|
</PanelContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
-->
|
|
||||||
<VerticalTabContainer Name="LoadoutGroupsContainer"
|
<VerticalTabContainer Name="LoadoutGroupsContainer"
|
||||||
VerticalExpand="True"
|
VerticalExpand="True"
|
||||||
HorizontalExpand="True">
|
HorizontalExpand="True">
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Content.Client.UserInterface.Controls;
|
using Content.Client.UserInterface.Controls;
|
||||||
|
using Content.Shared.Dataset;
|
||||||
using Content.Shared.Preferences;
|
using Content.Shared.Preferences;
|
||||||
using Content.Shared.Preferences.Loadouts;
|
using Content.Shared.Preferences.Loadouts;
|
||||||
|
using Content.Shared.Random.Helpers;
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
@@ -13,6 +15,7 @@ namespace Content.Client.Lobby.UI.Loadouts;
|
|||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class LoadoutWindow : FancyWindow
|
public sealed partial class LoadoutWindow : FancyWindow
|
||||||
{
|
{
|
||||||
|
public event Action<string>? OnNameChanged;
|
||||||
public event Action<ProtoId<LoadoutGroupPrototype>, ProtoId<LoadoutPrototype>>? OnLoadoutPressed;
|
public event Action<ProtoId<LoadoutGroupPrototype>, ProtoId<LoadoutPrototype>>? OnLoadoutPressed;
|
||||||
public event Action<ProtoId<LoadoutGroupPrototype>, ProtoId<LoadoutPrototype>>? OnLoadoutUnpressed;
|
public event Action<ProtoId<LoadoutGroupPrototype>, ProtoId<LoadoutPrototype>>? OnLoadoutUnpressed;
|
||||||
|
|
||||||
@@ -25,6 +28,23 @@ public sealed partial class LoadoutWindow : FancyWindow
|
|||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
Profile = profile;
|
Profile = profile;
|
||||||
var protoManager = collection.Resolve<IPrototypeManager>();
|
var protoManager = collection.Resolve<IPrototypeManager>();
|
||||||
|
RoleNameEdit.IsValid = text => text.Length <= HumanoidCharacterProfile.MaxLoadoutNameLength;
|
||||||
|
|
||||||
|
// Hide if we can't edit the name.
|
||||||
|
if (!proto.CanCustomizeName)
|
||||||
|
{
|
||||||
|
RoleNameBox.Visible = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var name = loadout.EntityName;
|
||||||
|
|
||||||
|
RoleNameEdit.ToolTip = Loc.GetString(
|
||||||
|
"loadout-name-edit-tooltip",
|
||||||
|
("max", HumanoidCharacterProfile.MaxLoadoutNameLength));
|
||||||
|
RoleNameEdit.Text = name ?? string.Empty;
|
||||||
|
RoleNameEdit.OnTextChanged += args => OnNameChanged?.Invoke(args.Text);
|
||||||
|
}
|
||||||
|
|
||||||
// Hide if no groups
|
// Hide if no groups
|
||||||
if (proto.Groups.Count == 0)
|
if (proto.Groups.Count == 0)
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
using Content.Shared.Magic;
|
using Content.Shared.Magic;
|
||||||
|
using Content.Shared.Magic.Events;
|
||||||
|
|
||||||
namespace Content.Client.Magic;
|
namespace Content.Client.Magic;
|
||||||
|
|
||||||
public sealed class MagicSystem : SharedMagicSystem;
|
public sealed class MagicSystem : SharedMagicSystem
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ public sealed class GridDraggingSystem : SharedGridDraggingSystem
|
|||||||
if (!_mapManager.TryFindGridAt(mousePos, out var gridUid, out var grid))
|
if (!_mapManager.TryFindGridAt(mousePos, out var gridUid, out var grid))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
StartDragging(gridUid, Vector2.Transform(mousePos.Position, Transform(gridUid).InvWorldMatrix));
|
StartDragging(gridUid, Vector2.Transform(mousePos.Position, _transformSystem.GetInvWorldMatrix(gridUid)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryComp(_dragging, out TransformComponent? xform))
|
if (!TryComp(_dragging, out TransformComponent? xform))
|
||||||
@@ -117,11 +117,11 @@ public sealed class GridDraggingSystem : SharedGridDraggingSystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var localToWorld = Vector2.Transform(_localPosition, xform.WorldMatrix);
|
var localToWorld = Vector2.Transform(_localPosition, _transformSystem.GetWorldMatrix(xform));
|
||||||
|
|
||||||
if (localToWorld.EqualsApprox(mousePos.Position, 0.01f)) return;
|
if (localToWorld.EqualsApprox(mousePos.Position, 0.01f)) return;
|
||||||
|
|
||||||
var requestedGridOrigin = mousePos.Position - xform.WorldRotation.RotateVec(_localPosition);
|
var requestedGridOrigin = mousePos.Position - _transformSystem.GetWorldRotation(xform).RotateVec(_localPosition);
|
||||||
_lastMousePosition = new MapCoordinates(requestedGridOrigin, mousePos.MapId);
|
_lastMousePosition = new MapCoordinates(requestedGridOrigin, mousePos.MapId);
|
||||||
|
|
||||||
RaiseNetworkEvent(new GridDragRequestPosition()
|
RaiseNetworkEvent(new GridDragRequestPosition()
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ public sealed class NewsWriterBoundUserInterface : BoundUserInterface
|
|||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
_menu = this.CreateWindow<NewsWriterMenu>();
|
_menu = this.CreateWindow<NewsWriterMenu>();
|
||||||
|
|
||||||
_menu.ArticleEditorPanel.PublishButtonPressed += OnPublishButtonPressed;
|
_menu.ArticleEditorPanel.PublishButtonPressed += OnPublishButtonPressed;
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ public sealed class CrewMonitoringBoundUserInterface : BoundUserInterface
|
|||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
EntityUid? gridUid = null;
|
EntityUid? gridUid = null;
|
||||||
var stationName = string.Empty;
|
var stationName = string.Empty;
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
private readonly SharedTransformSystem _transformSystem;
|
||||||
private readonly SpriteSystem _spriteSystem;
|
private readonly SpriteSystem _spriteSystem;
|
||||||
|
|
||||||
private NetEntity? _trackedEntity;
|
private NetEntity? _trackedEntity;
|
||||||
@@ -36,10 +37,10 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
|||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
IoCManager.InjectDependencies(this);
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
|
_transformSystem = _entManager.System<SharedTransformSystem>();
|
||||||
_spriteSystem = _entManager.System<SpriteSystem>();
|
_spriteSystem = _entManager.System<SpriteSystem>();
|
||||||
|
|
||||||
NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap;
|
NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Set(string stationName, EntityUid? mapUid)
|
public void Set(string stationName, EntityUid? mapUid)
|
||||||
@@ -290,7 +291,7 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
|||||||
{
|
{
|
||||||
NavMap.TrackedEntities.TryAdd(sensor.SuitSensorUid,
|
NavMap.TrackedEntities.TryAdd(sensor.SuitSensorUid,
|
||||||
new NavMapBlip
|
new NavMapBlip
|
||||||
(coordinates.Value,
|
(CoordinatesToLocal(coordinates.Value),
|
||||||
_blipTexture,
|
_blipTexture,
|
||||||
(_trackedEntity == null || sensor.SuitSensorUid == _trackedEntity) ? Color.LimeGreen : Color.LimeGreen * Color.DimGray,
|
(_trackedEntity == null || sensor.SuitSensorUid == _trackedEntity) ? Color.LimeGreen : Color.LimeGreen * Color.DimGray,
|
||||||
sensor.SuitSensorUid == _trackedEntity));
|
sensor.SuitSensorUid == _trackedEntity));
|
||||||
@@ -356,7 +357,7 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
|||||||
if (NavMap.TrackedEntities.TryGetValue(castSensor.SuitSensorUid, out var data))
|
if (NavMap.TrackedEntities.TryGetValue(castSensor.SuitSensorUid, out var data))
|
||||||
{
|
{
|
||||||
data = new NavMapBlip
|
data = new NavMapBlip
|
||||||
(data.Coordinates,
|
(CoordinatesToLocal(data.Coordinates),
|
||||||
data.Texture,
|
data.Texture,
|
||||||
(currTrackedEntity == null || castSensor.SuitSensorUid == currTrackedEntity) ? Color.LimeGreen : Color.LimeGreen * Color.DimGray,
|
(currTrackedEntity == null || castSensor.SuitSensorUid == currTrackedEntity) ? Color.LimeGreen : Color.LimeGreen * Color.DimGray,
|
||||||
castSensor.SuitSensorUid == currTrackedEntity);
|
castSensor.SuitSensorUid == currTrackedEntity);
|
||||||
@@ -421,6 +422,26 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the input coordinates to an EntityCoordinates which are in
|
||||||
|
/// reference to the grid that the map is displaying. This is a stylistic
|
||||||
|
/// choice; this window deliberately limits the rate that blips update,
|
||||||
|
/// but if the blip is attached to another grid which is moving, that
|
||||||
|
/// blip will move smoothly, unlike the others. By converting the
|
||||||
|
/// coordinates, we are back in control of the blip movement.
|
||||||
|
/// </summary>
|
||||||
|
private EntityCoordinates CoordinatesToLocal(EntityCoordinates refCoords)
|
||||||
|
{
|
||||||
|
if (NavMap.MapUid != null)
|
||||||
|
{
|
||||||
|
return _transformSystem.WithEntityId(refCoords, (EntityUid)NavMap.MapUid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return refCoords;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ClearOutDatedData()
|
private void ClearOutDatedData()
|
||||||
{
|
{
|
||||||
SensorsTable.RemoveAllChildren();
|
SensorsTable.RemoveAllChildren();
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Client.Movement.Systems;
|
||||||
|
using Content.Shared.Movement.Components;
|
||||||
|
|
||||||
|
namespace Content.Client.Movement.Components;
|
||||||
|
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed partial class EyeCursorOffsetComponent : SharedEyeCursorOffsetComponent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The location the offset will attempt to pan towards; based on the cursor's position in the game window.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 TargetPosition = Vector2.Zero;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current positional offset being applied. Used to enable gradual panning.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 CurrentPosition = Vector2.Zero;
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Content.Shared.Movement.Components;
|
using Content.Shared.Movement.Components;
|
||||||
using Content.Shared.Movement.Systems;
|
using Content.Shared.Movement.Systems;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
|
|
||||||
namespace Content.Client.Movement.Systems;
|
namespace Content.Client.Movement.Systems;
|
||||||
@@ -52,4 +53,14 @@ public sealed class ContentEyeSystem : SharedContentEyeSystem
|
|||||||
{
|
{
|
||||||
RaisePredictiveEvent(new RequestEyeEvent(drawFov, drawLight));
|
RaisePredictiveEvent(new RequestEyeEvent(drawFov, drawLight));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void FrameUpdate(float frameTime)
|
||||||
|
{
|
||||||
|
base.FrameUpdate(frameTime);
|
||||||
|
var eyeEntities = AllEntityQuery<ContentEyeComponent, EyeComponent>();
|
||||||
|
while (eyeEntities.MoveNext(out var entity, out ContentEyeComponent? contentComponent, out EyeComponent? eyeComponent))
|
||||||
|
{
|
||||||
|
UpdateEyeOffset((entity, eyeComponent));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
91
Content.Client/Movement/Systems/EyeCursorOffsetSystem.cs
Normal file
91
Content.Client/Movement/Systems/EyeCursorOffsetSystem.cs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Client.Movement.Components;
|
||||||
|
using Content.Shared.Camera;
|
||||||
|
using Content.Shared.Inventory;
|
||||||
|
using Content.Shared.Movement.Systems;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.Input;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Client.Player;
|
||||||
|
|
||||||
|
namespace Content.Client.Movement.Systems;
|
||||||
|
|
||||||
|
public partial class EyeCursorOffsetSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||||
|
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||||
|
[Dependency] private readonly IPlayerManager _player = default!;
|
||||||
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
|
[Dependency] private readonly SharedContentEyeSystem _contentEye = default!;
|
||||||
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
[Dependency] private readonly IClyde _clyde = default!;
|
||||||
|
|
||||||
|
// This value is here to make sure the user doesn't have to move their mouse
|
||||||
|
// all the way out to the edge of the screen to get the full offset.
|
||||||
|
static private float _edgeOffset = 0.9f;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<EyeCursorOffsetComponent, GetEyeOffsetEvent>(OnGetEyeOffsetEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetEyeOffsetEvent(EntityUid uid, EyeCursorOffsetComponent component, ref GetEyeOffsetEvent args)
|
||||||
|
{
|
||||||
|
var offset = OffsetAfterMouse(uid, component);
|
||||||
|
if (offset == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
args.Offset += offset.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2? OffsetAfterMouse(EntityUid uid, EyeCursorOffsetComponent? component)
|
||||||
|
{
|
||||||
|
var localPlayer = _player.LocalPlayer?.ControlledEntity;
|
||||||
|
var mousePos = _inputManager.MouseScreenPosition;
|
||||||
|
var screenSize = _clyde.MainWindow.Size;
|
||||||
|
var minValue = MathF.Min(screenSize.X / 2, screenSize.Y / 2) * _edgeOffset;
|
||||||
|
|
||||||
|
var mouseNormalizedPos = new Vector2(-(mousePos.X - screenSize.X / 2) / minValue, (mousePos.Y - screenSize.Y / 2) / minValue); // X needs to be inverted here for some reason, otherwise it ends up flipped.
|
||||||
|
|
||||||
|
if (localPlayer == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var playerPos = _transform.GetWorldPosition(localPlayer.Value);
|
||||||
|
|
||||||
|
if (component == null)
|
||||||
|
{
|
||||||
|
component = EnsureComp<EyeCursorOffsetComponent>(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doesn't move the offset if the mouse has left the game window!
|
||||||
|
if (mousePos.Window != WindowId.Invalid)
|
||||||
|
{
|
||||||
|
// The offset must account for the in-world rotation.
|
||||||
|
var eyeRotation = _eyeManager.CurrentEye.Rotation;
|
||||||
|
var mouseActualRelativePos = Vector2.Transform(mouseNormalizedPos, System.Numerics.Quaternion.CreateFromAxisAngle(-System.Numerics.Vector3.UnitZ, (float)(eyeRotation.Opposite().Theta))); // I don't know, it just works.
|
||||||
|
|
||||||
|
// Caps the offset into a circle around the player.
|
||||||
|
mouseActualRelativePos *= component.MaxOffset;
|
||||||
|
if (mouseActualRelativePos.Length() > component.MaxOffset)
|
||||||
|
{
|
||||||
|
mouseActualRelativePos = mouseActualRelativePos.Normalized() * component.MaxOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
component.TargetPosition = mouseActualRelativePos;
|
||||||
|
|
||||||
|
//Makes the view not jump immediately when moving the cursor fast.
|
||||||
|
if (component.CurrentPosition != component.TargetPosition)
|
||||||
|
{
|
||||||
|
Vector2 vectorOffset = component.TargetPosition - component.CurrentPosition;
|
||||||
|
if (vectorOffset.Length() > component.OffsetSpeed)
|
||||||
|
{
|
||||||
|
vectorOffset = vectorOffset.Normalized() * component.OffsetSpeed;
|
||||||
|
}
|
||||||
|
component.CurrentPosition += vectorOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return component.CurrentPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,8 @@ namespace Content.Client.Nuke
|
|||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
_menu = this.CreateWindow<NukeMenu>();
|
_menu = this.CreateWindow<NukeMenu>();
|
||||||
|
|
||||||
_menu.OnKeypadButtonPressed += i =>
|
_menu.OnKeypadButtonPressed += i =>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Robust.Client.Animations;
|
|||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Shared.Animations;
|
using Robust.Shared.Animations;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Client.Orbit;
|
namespace Content.Client.Orbit;
|
||||||
|
|
||||||
@@ -11,8 +12,8 @@ public sealed class OrbitVisualsSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||||
[Dependency] private readonly AnimationPlayerSystem _animations = default!;
|
[Dependency] private readonly AnimationPlayerSystem _animations = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
|
||||||
private readonly string _orbitAnimationKey = "orbiting";
|
|
||||||
private readonly string _orbitStopKey = "orbiting_stop";
|
private readonly string _orbitStopKey = "orbiting_stop";
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
@@ -21,11 +22,11 @@ public sealed class OrbitVisualsSystem : EntitySystem
|
|||||||
|
|
||||||
SubscribeLocalEvent<OrbitVisualsComponent, ComponentInit>(OnComponentInit);
|
SubscribeLocalEvent<OrbitVisualsComponent, ComponentInit>(OnComponentInit);
|
||||||
SubscribeLocalEvent<OrbitVisualsComponent, ComponentRemove>(OnComponentRemove);
|
SubscribeLocalEvent<OrbitVisualsComponent, ComponentRemove>(OnComponentRemove);
|
||||||
SubscribeLocalEvent<OrbitVisualsComponent, AnimationCompletedEvent>(OnAnimationCompleted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnComponentInit(EntityUid uid, OrbitVisualsComponent component, ComponentInit args)
|
private void OnComponentInit(EntityUid uid, OrbitVisualsComponent component, ComponentInit args)
|
||||||
{
|
{
|
||||||
|
_robustRandom.SetSeed((int)_timing.CurTime.TotalMilliseconds);
|
||||||
component.OrbitDistance =
|
component.OrbitDistance =
|
||||||
_robustRandom.NextFloat(0.75f * component.OrbitDistance, 1.25f * component.OrbitDistance);
|
_robustRandom.NextFloat(0.75f * component.OrbitDistance, 1.25f * component.OrbitDistance);
|
||||||
|
|
||||||
@@ -38,15 +39,10 @@ public sealed class OrbitVisualsSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
var animationPlayer = EnsureComp<AnimationPlayerComponent>(uid);
|
var animationPlayer = EnsureComp<AnimationPlayerComponent>(uid);
|
||||||
if (_animations.HasRunningAnimation(uid, animationPlayer, _orbitAnimationKey))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_animations.HasRunningAnimation(uid, animationPlayer, _orbitStopKey))
|
if (_animations.HasRunningAnimation(uid, animationPlayer, _orbitStopKey))
|
||||||
{
|
{
|
||||||
_animations.Stop(uid, animationPlayer, _orbitStopKey);
|
_animations.Stop((uid, animationPlayer), _orbitStopKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
_animations.Play(uid, animationPlayer, GetOrbitAnimation(component), _orbitAnimationKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnComponentRemove(EntityUid uid, OrbitVisualsComponent component, ComponentRemove args)
|
private void OnComponentRemove(EntityUid uid, OrbitVisualsComponent component, ComponentRemove args)
|
||||||
@@ -57,14 +53,9 @@ public sealed class OrbitVisualsSystem : EntitySystem
|
|||||||
sprite.EnableDirectionOverride = false;
|
sprite.EnableDirectionOverride = false;
|
||||||
|
|
||||||
var animationPlayer = EnsureComp<AnimationPlayerComponent>(uid);
|
var animationPlayer = EnsureComp<AnimationPlayerComponent>(uid);
|
||||||
if (_animations.HasRunningAnimation(uid, animationPlayer, _orbitAnimationKey))
|
|
||||||
{
|
|
||||||
_animations.Stop(uid, animationPlayer, _orbitAnimationKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_animations.HasRunningAnimation(uid, animationPlayer, _orbitStopKey))
|
if (!_animations.HasRunningAnimation(uid, animationPlayer, _orbitStopKey))
|
||||||
{
|
{
|
||||||
_animations.Play(uid, animationPlayer, GetStopAnimation(component, sprite), _orbitStopKey);
|
_animations.Play((uid, animationPlayer), GetStopAnimation(component, sprite), _orbitStopKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +65,8 @@ public sealed class OrbitVisualsSystem : EntitySystem
|
|||||||
|
|
||||||
foreach (var (orbit, sprite) in EntityManager.EntityQuery<OrbitVisualsComponent, SpriteComponent>())
|
foreach (var (orbit, sprite) in EntityManager.EntityQuery<OrbitVisualsComponent, SpriteComponent>())
|
||||||
{
|
{
|
||||||
var angle = new Angle(Math.PI * 2 * orbit.Orbit);
|
var progress = (float)(_timing.CurTime.TotalSeconds / orbit.OrbitLength) % 1;
|
||||||
|
var angle = new Angle(Math.PI * 2 * progress);
|
||||||
var vec = angle.RotateVec(new Vector2(orbit.OrbitDistance, 0));
|
var vec = angle.RotateVec(new Vector2(orbit.OrbitDistance, 0));
|
||||||
|
|
||||||
sprite.Rotation = angle;
|
sprite.Rotation = angle;
|
||||||
@@ -82,38 +74,6 @@ public sealed class OrbitVisualsSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAnimationCompleted(EntityUid uid, OrbitVisualsComponent component, AnimationCompletedEvent args)
|
|
||||||
{
|
|
||||||
if (args.Key == _orbitAnimationKey && TryComp(uid, out AnimationPlayerComponent? animationPlayer))
|
|
||||||
{
|
|
||||||
_animations.Play(uid, animationPlayer, GetOrbitAnimation(component), _orbitAnimationKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Animation GetOrbitAnimation(OrbitVisualsComponent component)
|
|
||||||
{
|
|
||||||
var length = component.OrbitLength;
|
|
||||||
|
|
||||||
return new Animation()
|
|
||||||
{
|
|
||||||
Length = TimeSpan.FromSeconds(length),
|
|
||||||
AnimationTracks =
|
|
||||||
{
|
|
||||||
new AnimationTrackComponentProperty()
|
|
||||||
{
|
|
||||||
ComponentType = typeof(OrbitVisualsComponent),
|
|
||||||
Property = nameof(OrbitVisualsComponent.Orbit),
|
|
||||||
KeyFrames =
|
|
||||||
{
|
|
||||||
new AnimationTrackProperty.KeyFrame(0.0f, 0f),
|
|
||||||
new AnimationTrackProperty.KeyFrame(1.0f, length),
|
|
||||||
},
|
|
||||||
InterpolationMode = AnimationInterpolationMode.Linear
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private Animation GetStopAnimation(OrbitVisualsComponent component, SpriteComponent sprite)
|
private Animation GetStopAnimation(OrbitVisualsComponent component, SpriteComponent sprite)
|
||||||
{
|
{
|
||||||
var length = component.OrbitStopLength;
|
var length = component.OrbitStopLength;
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ public sealed class TargetOutlineSystem : EntitySystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public EntityWhitelist? Whitelist = null;
|
public EntityWhitelist? Whitelist = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Blacklist that the target must satisfy.
|
||||||
|
/// </summary>
|
||||||
|
public EntityWhitelist? Blacklist = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Predicate the target must satisfy.
|
/// Predicate the target must satisfy.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -93,15 +98,16 @@ public sealed class TargetOutlineSystem : EntitySystem
|
|||||||
RemoveHighlights();
|
RemoveHighlights();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Enable(float range, bool checkObstructions, Func<EntityUid, bool>? predicate, EntityWhitelist? whitelist, CancellableEntityEventArgs? validationEvent)
|
public void Enable(float range, bool checkObstructions, Func<EntityUid, bool>? predicate, EntityWhitelist? whitelist, EntityWhitelist? blacklist, CancellableEntityEventArgs? validationEvent)
|
||||||
{
|
{
|
||||||
Range = range;
|
Range = range;
|
||||||
CheckObstruction = checkObstructions;
|
CheckObstruction = checkObstructions;
|
||||||
Predicate = predicate;
|
Predicate = predicate;
|
||||||
Whitelist = whitelist;
|
Whitelist = whitelist;
|
||||||
|
Blacklist = blacklist;
|
||||||
ValidationEvent = validationEvent;
|
ValidationEvent = validationEvent;
|
||||||
|
|
||||||
_enabled = Predicate != null || Whitelist != null || ValidationEvent != null;
|
_enabled = Predicate != null || Whitelist != null || Blacklist != null || ValidationEvent != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
|
|||||||
@@ -56,35 +56,35 @@ public abstract class EquipmentHudSystem<T> : EntitySystem where T : IComponent
|
|||||||
|
|
||||||
protected virtual void DeactivateInternal() { }
|
protected virtual void DeactivateInternal() { }
|
||||||
|
|
||||||
private void OnStartup(EntityUid uid, T component, ComponentStartup args)
|
private void OnStartup(Entity<T> ent, ref ComponentStartup args)
|
||||||
{
|
{
|
||||||
RefreshOverlay(uid);
|
RefreshOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRemove(EntityUid uid, T component, ComponentRemove args)
|
private void OnRemove(Entity<T> ent, ref ComponentRemove args)
|
||||||
{
|
{
|
||||||
RefreshOverlay(uid);
|
RefreshOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPlayerAttached(LocalPlayerAttachedEvent args)
|
private void OnPlayerAttached(LocalPlayerAttachedEvent args)
|
||||||
{
|
{
|
||||||
RefreshOverlay(args.Entity);
|
RefreshOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPlayerDetached(LocalPlayerDetachedEvent args)
|
private void OnPlayerDetached(LocalPlayerDetachedEvent args)
|
||||||
{
|
{
|
||||||
if (_player.LocalSession?.AttachedEntity == null)
|
if (_player.LocalSession?.AttachedEntity is null)
|
||||||
Deactivate();
|
Deactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCompEquip(EntityUid uid, T component, GotEquippedEvent args)
|
private void OnCompEquip(Entity<T> ent, ref GotEquippedEvent args)
|
||||||
{
|
{
|
||||||
RefreshOverlay(args.Equipee);
|
RefreshOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCompUnequip(EntityUid uid, T component, GotUnequippedEvent args)
|
private void OnCompUnequip(Entity<T> ent, ref GotUnequippedEvent args)
|
||||||
{
|
{
|
||||||
RefreshOverlay(args.Equipee);
|
RefreshOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRoundRestart(RoundRestartCleanupEvent args)
|
private void OnRoundRestart(RoundRestartCleanupEvent args)
|
||||||
@@ -92,24 +92,24 @@ public abstract class EquipmentHudSystem<T> : EntitySystem where T : IComponent
|
|||||||
Deactivate();
|
Deactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnRefreshEquipmentHud(EntityUid uid, T component, InventoryRelayedEvent<RefreshEquipmentHudEvent<T>> args)
|
protected virtual void OnRefreshEquipmentHud(Entity<T> ent, ref InventoryRelayedEvent<RefreshEquipmentHudEvent<T>> args)
|
||||||
{
|
{
|
||||||
OnRefreshComponentHud(uid, component, args.Args);
|
OnRefreshComponentHud(ent, ref args.Args);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnRefreshComponentHud(EntityUid uid, T component, RefreshEquipmentHudEvent<T> args)
|
protected virtual void OnRefreshComponentHud(Entity<T> ent, ref RefreshEquipmentHudEvent<T> args)
|
||||||
{
|
{
|
||||||
args.Active = true;
|
args.Active = true;
|
||||||
args.Components.Add(component);
|
args.Components.Add(ent.Comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void RefreshOverlay(EntityUid uid)
|
protected void RefreshOverlay()
|
||||||
{
|
{
|
||||||
if (uid != _player.LocalSession?.AttachedEntity)
|
if (_player.LocalSession?.AttachedEntity is not { } entity)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var ev = new RefreshEquipmentHudEvent<T>(TargetSlots);
|
var ev = new RefreshEquipmentHudEvent<T>(TargetSlots);
|
||||||
RaiseLocalEvent(uid, ev);
|
RaiseLocalEvent(entity, ref ev);
|
||||||
|
|
||||||
if (ev.Active)
|
if (ev.Active)
|
||||||
Update(ev);
|
Update(ev);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public sealed class ShowHealthBarsSystem : EquipmentHudSystem<ShowHealthBarsComp
|
|||||||
|
|
||||||
private void OnHandleState(Entity<ShowHealthBarsComponent> ent, ref AfterAutoHandleStateEvent args)
|
private void OnHandleState(Entity<ShowHealthBarsComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||||
{
|
{
|
||||||
RefreshOverlay(ent);
|
RefreshOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthBarsComponent> component)
|
protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthBarsComponent> component)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsCo
|
|||||||
|
|
||||||
private void OnHandleState(Entity<ShowHealthIconsComponent> ent, ref AfterAutoHandleStateEvent args)
|
private void OnHandleState(Entity<ShowHealthIconsComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||||
{
|
{
|
||||||
RefreshOverlay(ent);
|
RefreshOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGetStatusIconsEvent(Entity<DamageableComponent> entity, ref GetStatusIconsEvent args)
|
private void OnGetStatusIconsEvent(Entity<DamageableComponent> entity, ref GetStatusIconsEvent args)
|
||||||
|
|||||||
@@ -15,6 +15,16 @@ public sealed class ShowMindShieldIconsSystem : EquipmentHudSystem<ShowMindShiel
|
|||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<MindShieldComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
SubscribeLocalEvent<MindShieldComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||||
|
SubscribeLocalEvent<FakeMindShieldComponent, GetStatusIconsEvent>(OnGetStatusIconsEventFake);
|
||||||
|
}
|
||||||
|
// TODO: Probably need to get this OFF of client since this can be read by bad actors rather easily
|
||||||
|
// ...imagine cheating in a game about silly paper dolls
|
||||||
|
private void OnGetStatusIconsEventFake(EntityUid uid, FakeMindShieldComponent component, ref GetStatusIconsEvent ev)
|
||||||
|
{
|
||||||
|
if(!IsActive)
|
||||||
|
return;
|
||||||
|
if (component.IsEnabled && _prototype.TryIndex(component.MindShieldStatusIcon, out var fakeStatusIconPrototype))
|
||||||
|
ev.StatusIcons.Add(fakeStatusIconPrototype);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGetStatusIconsEvent(EntityUid uid, MindShieldComponent component, ref GetStatusIconsEvent ev)
|
private void OnGetStatusIconsEvent(EntityUid uid, MindShieldComponent component, ref GetStatusIconsEvent ev)
|
||||||
|
|||||||
@@ -2,49 +2,128 @@
|
|||||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
xmlns:ui="clr-namespace:Content.Client.ParticleAccelerator.UI"
|
xmlns:ui="clr-namespace:Content.Client.ParticleAccelerator.UI"
|
||||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
|
||||||
Title="{Loc 'particle-accelerator-control-menu-device-version-label'}"
|
Title="{Loc 'particle-accelerator-control-menu-device-version-label'}"
|
||||||
MinSize="420 320"
|
MinSize="320 120">
|
||||||
SetSize="420 320">
|
|
||||||
<BoxContainer Orientation="Vertical" VerticalExpand="True" Margin="0 10 0 0">
|
<!-- Main Container -->
|
||||||
<BoxContainer Orientation="Horizontal" VerticalExpand="True">
|
<BoxContainer Orientation="Vertical"
|
||||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" Margin="10 0 10 5">
|
VerticalExpand="True">
|
||||||
|
|
||||||
|
<!-- Sub-Main container -->
|
||||||
|
<BoxContainer Orientation="Horizontal"
|
||||||
|
VerticalExpand="True"
|
||||||
|
HorizontalExpand="True">
|
||||||
|
|
||||||
|
<!-- Info part -->
|
||||||
|
<BoxContainer Orientation="Vertical"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
Margin="8">
|
||||||
|
|
||||||
|
<!-- Info -->
|
||||||
|
<BoxContainer Orientation="Vertical"
|
||||||
|
SeparationOverride="4">
|
||||||
|
|
||||||
|
<!-- Status -->
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<RichTextLabel Name="StatusLabel" HorizontalExpand="True"/>
|
<RichTextLabel Name="StatusLabel" HorizontalExpand="True"/>
|
||||||
|
<Control MinWidth="8"/>
|
||||||
<RichTextLabel Name="StatusStateLabel"/>
|
<RichTextLabel Name="StatusStateLabel"/>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Control MinHeight="5"/>
|
|
||||||
|
<!-- Power -->
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<RichTextLabel Name="PowerLabel" Margin="0 0 20 0" HorizontalExpand="True" VerticalAlignment="Center"/>
|
<RichTextLabel Name="PowerLabel"
|
||||||
<Button Name="OffButton" ToggleMode="False" Text="{Loc 'particle-accelerator-control-menu-off-button'}" StyleClasses="OpenRight"/>
|
HorizontalExpand="True"
|
||||||
<Button Name="OnButton" ToggleMode="False" Text="{Loc 'particle-accelerator-control-menu-on-button'}" StyleClasses="OpenLeft"/>
|
VerticalAlignment="Center"/>
|
||||||
|
|
||||||
|
<Control MinWidth="8"/>
|
||||||
|
|
||||||
|
<Button Name="OffButton"
|
||||||
|
ToggleMode="False"
|
||||||
|
Text="{Loc 'particle-accelerator-control-menu-off-button'}"
|
||||||
|
StyleClasses="OpenRight"/>
|
||||||
|
|
||||||
|
<Button Name="OnButton"
|
||||||
|
ToggleMode="False"
|
||||||
|
Text="{Loc 'particle-accelerator-control-menu-on-button'}"
|
||||||
|
StyleClasses="OpenLeft"/>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Control MinHeight="5"/>
|
|
||||||
|
<!-- Strenght -->
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<RichTextLabel Name="StrengthLabel" Margin="0 0 20 0" HorizontalExpand="True" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
<RichTextLabel Name="StrengthLabel"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"/>
|
||||||
|
|
||||||
|
<Control MinWidth="8"/>
|
||||||
|
|
||||||
<SpinBox Name="StateSpinBox" Value="0"/>
|
<SpinBox Name="StateSpinBox" Value="0"/>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Control MinHeight="5"/>
|
|
||||||
|
<!-- Power -->
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<RichTextLabel Name="DrawLabel" HorizontalExpand="True"/>
|
<RichTextLabel Name="DrawLabel" HorizontalExpand="True"/>
|
||||||
|
<Control MinWidth="8"/>
|
||||||
<RichTextLabel Name="DrawValueLabel"/>
|
<RichTextLabel Name="DrawValueLabel"/>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Control MinHeight="10" VerticalExpand="True"/>
|
|
||||||
<BoxContainer Name="AlarmControl" Orientation="Vertical" VerticalAlignment="Center" Visible="False">
|
|
||||||
<RichTextLabel Name="BigAlarmLabel" HorizontalAlignment="Center"/>
|
|
||||||
<RichTextLabel Name="BigAlarmLabelTwo" HorizontalAlignment="Center"/>
|
|
||||||
<Label Text="{Loc 'particle-accelerator-control-menu-service-manual-reference'}" HorizontalAlignment="Center" StyleClasses="LabelSubText"/>
|
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Control MinHeight="10" VerticalExpand="True"/>
|
|
||||||
|
<Control MinHeight="8" VerticalExpand="True"/> <!-- Filler -->
|
||||||
|
|
||||||
|
<!-- Alarm -->
|
||||||
|
<BoxContainer Name="AlarmControl"
|
||||||
|
Orientation="Vertical"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Visible="False">
|
||||||
|
|
||||||
|
<controls:StripeBack Margin="-8 0">
|
||||||
|
<BoxContainer Orientation="Vertical">
|
||||||
|
<RichTextLabel Name="BigAlarmLabel"
|
||||||
|
HorizontalAlignment="Center"/>
|
||||||
|
|
||||||
|
<RichTextLabel Name="BigAlarmLabelTwo"
|
||||||
|
HorizontalAlignment="Center"/>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<customControls:VSeparator Margin="0 0 0 10"/>
|
</controls:StripeBack>
|
||||||
<BoxContainer Orientation="Vertical" Margin="20 0 20 0" VerticalAlignment="Center">
|
|
||||||
<PanelContainer Name="BackPanel" HorizontalAlignment="Center">
|
<Label Text="{Loc 'particle-accelerator-control-menu-service-manual-reference'}"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
StyleClasses="LabelSubText"/>
|
||||||
|
</BoxContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
|
||||||
|
<PanelContainer StyleClasses="LowDivider" Margin="0 -8" HorizontalAlignment="Right"/>
|
||||||
|
|
||||||
|
<!-- PA Visual part -->
|
||||||
|
<BoxContainer Orientation="Vertical"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="8">
|
||||||
|
|
||||||
|
<PanelContainer Name="BackPanel"
|
||||||
|
HorizontalAlignment="Center">
|
||||||
|
|
||||||
<PanelContainer.PanelOverride>
|
<PanelContainer.PanelOverride>
|
||||||
<gfx:StyleBoxTexture Modulate="#202023" PatchMarginBottom="10" PatchMarginLeft="10" PatchMarginRight="10" PatchMarginTop="10"/>
|
<gfx:StyleBoxTexture Modulate="#202023"
|
||||||
|
PatchMarginBottom="8"
|
||||||
|
PatchMarginLeft="8"
|
||||||
|
PatchMarginRight="8"
|
||||||
|
PatchMarginTop="8"/>
|
||||||
</PanelContainer.PanelOverride>
|
</PanelContainer.PanelOverride>
|
||||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" HorizontalAlignment="Center" VerticalExpand="True">
|
|
||||||
<GridContainer Columns="3" VSeparationOverride="0" HSeparationOverride="0" HorizontalAlignment="Center">
|
<BoxContainer Orientation="Vertical"
|
||||||
|
SeparationOverride="6"
|
||||||
|
VerticalExpand="True"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
HorizontalAlignment="Center">
|
||||||
|
|
||||||
|
<!-- PA Visualisation -->
|
||||||
|
<GridContainer Columns="3"
|
||||||
|
VSeparationOverride="0"
|
||||||
|
HSeparationOverride="0"
|
||||||
|
HorizontalAlignment="Center">
|
||||||
|
|
||||||
<Control/>
|
<Control/>
|
||||||
<ui:PASegmentControl Name="EndCapTexture" BaseState="end_cap"/>
|
<ui:PASegmentControl Name="EndCapTexture" BaseState="end_cap"/>
|
||||||
<Control/>
|
<Control/>
|
||||||
@@ -58,17 +137,47 @@
|
|||||||
<ui:PASegmentControl Name="EmitterForeTexture" BaseState="emitter_fore"/>
|
<ui:PASegmentControl Name="EmitterForeTexture" BaseState="emitter_fore"/>
|
||||||
<ui:PASegmentControl Name="EmitterPortTexture" BaseState="emitter_port"/>
|
<ui:PASegmentControl Name="EmitterPortTexture" BaseState="emitter_port"/>
|
||||||
</GridContainer>
|
</GridContainer>
|
||||||
<Control MinHeight="5"/>
|
|
||||||
<Button Name="ScanButton" Text="{Loc 'particle-accelerator-control-menu-scan-parts-button'}" HorizontalAlignment="Center"/>
|
<Button Name="ScanButton"
|
||||||
|
Text="{Loc 'particle-accelerator-control-menu-scan-parts-button'}"
|
||||||
|
HorizontalAlignment="Center"/>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</PanelContainer>
|
</PanelContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<BoxContainer Orientation="Vertical"
|
||||||
|
VerticalAlignment="Bottom">
|
||||||
|
|
||||||
<controls:StripeBack>
|
<controls:StripeBack>
|
||||||
<Label Text="{Loc 'particle-accelerator-control-menu-check-containment-field-warning'}" HorizontalAlignment="Center" StyleClasses="LabelSubText" Margin="4 4 0 4"/>
|
<Label Text="{Loc 'particle-accelerator-control-menu-check-containment-field-warning'}"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
StyleClasses="LabelSubText"
|
||||||
|
Margin="0 4"/>
|
||||||
</controls:StripeBack>
|
</controls:StripeBack>
|
||||||
<BoxContainer Orientation="Horizontal" Margin="12 0 0 0">
|
|
||||||
<Label Text="{Loc 'particle-accelerator-control-menu-foo-bar-baz'}" StyleClasses="LabelSubText"/>
|
<BoxContainer Orientation="Horizontal"
|
||||||
|
Margin="12 0 6 2"
|
||||||
|
VerticalAlignment="Bottom">
|
||||||
|
|
||||||
|
<!-- Footer title -->
|
||||||
|
<Label Text="{Loc 'particle-accelerator-control-menu-flavor-left'}"
|
||||||
|
StyleClasses="WindowFooterText" />
|
||||||
|
|
||||||
|
<!-- Version -->
|
||||||
|
<Label Text="{Loc 'particle-accelerator-control-menu-flavor-right'}"
|
||||||
|
StyleClasses="WindowFooterText"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
Margin="0 0 4 0" />
|
||||||
|
|
||||||
|
<TextureRect StyleClasses="NTLogoDark"
|
||||||
|
Stretch="KeepAspectCentered"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
SetSize="19 19"/>
|
||||||
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</controls:FancyWindow>
|
</controls:FancyWindow>
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ namespace Content.Client.Pinpointer.UI;
|
|||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class StationMapBeaconControl : Control, IComparable<StationMapBeaconControl>
|
public sealed partial class StationMapBeaconControl : Control, IComparable<StationMapBeaconControl>
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
|
||||||
|
|
||||||
public readonly EntityCoordinates BeaconPosition;
|
public readonly EntityCoordinates BeaconPosition;
|
||||||
public Action<EntityCoordinates>? OnPressed;
|
public Action<EntityCoordinates>? OnPressed;
|
||||||
public string? Label => BeaconNameLabel.Text;
|
public string? Label => BeaconNameLabel.Text;
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ public sealed class PowerMonitoringConsoleBoundUserInterface : BoundUserInterfac
|
|||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
_menu = this.CreateWindow<PowerMonitoringWindow>();
|
_menu = this.CreateWindow<PowerMonitoringWindow>();
|
||||||
_menu.SetEntity(Owner);
|
_menu.SetEntity(Owner);
|
||||||
_menu.SendPowerMonitoringConsoleMessageAction += SendPowerMonitoringConsoleMessage;
|
_menu.SendPowerMonitoringConsoleMessageAction += SendPowerMonitoringConsoleMessage;
|
||||||
|
|||||||
@@ -4,14 +4,18 @@ using Robust.Client.GameObjects;
|
|||||||
|
|
||||||
namespace Content.Client.Power.Visualizers;
|
namespace Content.Client.Power.Visualizers;
|
||||||
|
|
||||||
public sealed class CableVisualizerSystem : VisualizerSystem<CableVisualizerComponent>
|
public sealed class CableVisualizerSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<CableVisualizerComponent, AppearanceChangeEvent>(OnAppearanceChange, after: new[] { typeof(SubFloorHideSystem) });
|
SubscribeLocalEvent<CableVisualizerComponent, AppearanceChangeEvent>(OnAppearanceChange, after: new[] { typeof(SubFloorHideSystem) });
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnAppearanceChange(EntityUid uid, CableVisualizerComponent component, ref AppearanceChangeEvent args)
|
private void OnAppearanceChange(EntityUid uid, CableVisualizerComponent component, ref AppearanceChangeEvent args)
|
||||||
{
|
{
|
||||||
if (args.Sprite == null)
|
if (args.Sprite == null)
|
||||||
return;
|
return;
|
||||||
@@ -23,7 +27,7 @@ public sealed class CableVisualizerSystem : VisualizerSystem<CableVisualizerComp
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!AppearanceSystem.TryGetData<WireVisDirFlags>(uid, WireVisVisuals.ConnectedMask, out var mask, args.Component))
|
if (!_appearanceSystem.TryGetData<WireVisDirFlags>(uid, WireVisVisuals.ConnectedMask, out var mask, args.Component))
|
||||||
mask = WireVisDirFlags.None;
|
mask = WireVisDirFlags.None;
|
||||||
|
|
||||||
args.Sprite.LayerSetState(0, $"{component.StatePrefix}{(int) mask}");
|
args.Sprite.LayerSetState(0, $"{component.StatePrefix}{(int) mask}");
|
||||||
|
|||||||
@@ -11,37 +11,37 @@
|
|||||||
<!-- The radial menu will try to open so that its center is located where the player's cursor is currently -->
|
<!-- The radial menu will try to open so that its center is located where the player's cursor is currently -->
|
||||||
|
|
||||||
<!-- Entry layer (shows main categories) -->
|
<!-- Entry layer (shows main categories) -->
|
||||||
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" Radius="64" ReserveSpaceForHiddenChildren="False">
|
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100" ReserveSpaceForHiddenChildren="False">
|
||||||
<ui:RadialMenuTextureButton StyleClasses="RadialMenuButton" SetSize="64 64" ToolTip="{Loc 'rcd-component-walls-and-flooring'}" TargetLayer="WallsAndFlooring" Visible="False">
|
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-walls-and-flooring'}" TargetLayer="WallsAndFlooring" Visible="False">
|
||||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/walls_and_flooring.png"/>
|
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/walls_and_flooring.png"/>
|
||||||
</ui:RadialMenuTextureButton>
|
</ui:RadialMenuTextureButtonWithSector>
|
||||||
<ui:RadialMenuTextureButton StyleClasses="RadialMenuButton" SetSize="64 64" ToolTip="{Loc 'rcd-component-windows-and-grilles'}" TargetLayer="WindowsAndGrilles" Visible="False">
|
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-windows-and-grilles'}" TargetLayer="WindowsAndGrilles" Visible="False">
|
||||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/windows_and_grilles.png"/>
|
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/windows_and_grilles.png"/>
|
||||||
</ui:RadialMenuTextureButton>
|
</ui:RadialMenuTextureButtonWithSector>
|
||||||
<ui:RadialMenuTextureButton StyleClasses="RadialMenuButton" SetSize="64 64" ToolTip="{Loc 'rcd-component-airlocks'}" TargetLayer="Airlocks" Visible="False">
|
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-airlocks'}" TargetLayer="Airlocks" Visible="False">
|
||||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/airlocks.png"/>
|
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/airlocks.png"/>
|
||||||
</ui:RadialMenuTextureButton>
|
</ui:RadialMenuTextureButtonWithSector>
|
||||||
<ui:RadialMenuTextureButton StyleClasses="RadialMenuButton" SetSize="64 64" ToolTip="{Loc 'rcd-component-electrical'}" TargetLayer="Electrical" Visible="False">
|
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-electrical'}" TargetLayer="Electrical" Visible="False">
|
||||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/multicoil.png"/>
|
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/multicoil.png"/>
|
||||||
</ui:RadialMenuTextureButton>
|
</ui:RadialMenuTextureButtonWithSector>
|
||||||
<ui:RadialMenuTextureButton StyleClasses="RadialMenuButton" SetSize="64 64" ToolTip="{Loc 'rcd-component-lighting'}" TargetLayer="Lighting" Visible="False">
|
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-lighting'}" TargetLayer="Lighting" Visible="False">
|
||||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/lighting.png"/>
|
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/lighting.png"/>
|
||||||
</ui:RadialMenuTextureButton>
|
</ui:RadialMenuTextureButtonWithSector>
|
||||||
</ui:RadialContainer>
|
</ui:RadialContainer>
|
||||||
|
|
||||||
<!-- Walls and flooring -->
|
<!-- Walls and flooring -->
|
||||||
<ui:RadialContainer Name="WallsAndFlooring" VerticalExpand="True" HorizontalExpand="True" Radius="64"/>
|
<ui:RadialContainer Name="WallsAndFlooring" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
|
||||||
|
|
||||||
<!-- Windows and grilles -->
|
<!-- Windows and grilles -->
|
||||||
<ui:RadialContainer Name="WindowsAndGrilles" VerticalExpand="True" HorizontalExpand="True" Radius="64"/>
|
<ui:RadialContainer Name="WindowsAndGrilles" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
|
||||||
|
|
||||||
<!-- Airlocks -->
|
<!-- Airlocks -->
|
||||||
<ui:RadialContainer Name="Airlocks" VerticalExpand="True" HorizontalExpand="True" Radius="64"/>
|
<ui:RadialContainer Name="Airlocks" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
|
||||||
|
|
||||||
<!-- Computer and machine frames -->
|
<!-- Computer and machine frames -->
|
||||||
<ui:RadialContainer Name="Electrical" VerticalExpand="True" HorizontalExpand="True" Radius="64"/>
|
<ui:RadialContainer Name="Electrical" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
|
||||||
|
|
||||||
<!-- Lighting -->
|
<!-- Lighting -->
|
||||||
<ui:RadialContainer Name="Lighting" VerticalExpand="True" HorizontalExpand="True" Radius="64"/>
|
<ui:RadialContainer Name="Lighting" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
|
||||||
|
|
||||||
</ui:RadialMenu>
|
</ui:RadialMenu>
|
||||||
|
|||||||
@@ -74,7 +74,6 @@ public sealed partial class RCDMenu : RadialMenu
|
|||||||
|
|
||||||
var button = new RCDMenuButton()
|
var button = new RCDMenuButton()
|
||||||
{
|
{
|
||||||
StyleClasses = { "RadialMenuButton" },
|
|
||||||
SetSize = new Vector2(64f, 64f),
|
SetSize = new Vector2(64f, 64f),
|
||||||
ToolTip = tooltip,
|
ToolTip = tooltip,
|
||||||
ProtoId = protoId,
|
ProtoId = protoId,
|
||||||
@@ -99,9 +98,7 @@ public sealed partial class RCDMenu : RadialMenu
|
|||||||
// is visible in the main radial container (as these all start with Visible = false)
|
// is visible in the main radial container (as these all start with Visible = false)
|
||||||
foreach (var child in main.Children)
|
foreach (var child in main.Children)
|
||||||
{
|
{
|
||||||
var castChild = child as RadialMenuTextureButton;
|
if (child is not RadialMenuTextureButton castChild)
|
||||||
|
|
||||||
if (castChild is not RadialMenuTextureButton)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (castChild.TargetLayer == proto.Category)
|
if (castChild.TargetLayer == proto.Category)
|
||||||
@@ -169,12 +166,7 @@ public sealed partial class RCDMenu : RadialMenu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class RCDMenuButton : RadialMenuTextureButton
|
public sealed class RCDMenuButton : RadialMenuTextureButtonWithSector
|
||||||
{
|
{
|
||||||
public ProtoId<RCDPrototype> ProtoId { get; set; }
|
public ProtoId<RCDPrototype> ProtoId { get; set; }
|
||||||
|
|
||||||
public RCDMenuButton()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public sealed class RadiationSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
||||||
|
|
||||||
public List<RadiationRay>? Rays;
|
public List<DebugRadiationRay>? Rays;
|
||||||
public Dictionary<NetEntity, Dictionary<Vector2i, float>>? ResistanceGrids;
|
public Dictionary<NetEntity, Dictionary<Vector2i, float>>? ResistanceGrids;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem
|
|||||||
|
|
||||||
if (_enableShuttlePosition)
|
if (_enableShuttlePosition)
|
||||||
{
|
{
|
||||||
_overlay = new EmergencyShuttleOverlay(EntityManager);
|
_overlay = new EmergencyShuttleOverlay(EntityManager.TransformQuery, XformSystem);
|
||||||
overlayManager.AddOverlay(_overlay);
|
overlayManager.AddOverlay(_overlay);
|
||||||
RaiseNetworkEvent(new EmergencyShuttleRequestPositionMessage());
|
RaiseNetworkEvent(new EmergencyShuttleRequestPositionMessage());
|
||||||
}
|
}
|
||||||
@@ -57,23 +57,26 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class EmergencyShuttleOverlay : Overlay
|
public sealed class EmergencyShuttleOverlay : Overlay
|
||||||
{
|
{
|
||||||
private IEntityManager _entManager;
|
private readonly EntityQuery<TransformComponent> _transformQuery;
|
||||||
|
private readonly SharedTransformSystem _transformSystem;
|
||||||
|
|
||||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||||
|
|
||||||
public EntityUid? StationUid;
|
public EntityUid? StationUid;
|
||||||
public Box2? Position;
|
public Box2? Position;
|
||||||
|
|
||||||
public EmergencyShuttleOverlay(IEntityManager entManager)
|
public EmergencyShuttleOverlay(EntityQuery<TransformComponent> transformQuery, SharedTransformSystem transformSystem)
|
||||||
{
|
{
|
||||||
_entManager = entManager;
|
_transformQuery = transformQuery;
|
||||||
|
_transformSystem = transformSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Draw(in OverlayDrawArgs args)
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
{
|
{
|
||||||
if (Position == null || !_entManager.TryGetComponent<TransformComponent>(StationUid, out var xform)) return;
|
if (Position == null || !_transformQuery.TryGetComponent(StationUid, out var xform))
|
||||||
|
return;
|
||||||
|
|
||||||
args.WorldHandle.SetTransform(xform.WorldMatrix);
|
args.WorldHandle.SetTransform(_transformSystem.GetWorldMatrix(xform));
|
||||||
args.WorldHandle.DrawRect(Position.Value, Color.Red.WithAlpha(100));
|
args.WorldHandle.DrawRect(Position.Value, Color.Red.WithAlpha(100));
|
||||||
args.WorldHandle.SetTransform(Matrix3x2.Identity);
|
args.WorldHandle.SetTransform(Matrix3x2.Identity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,14 @@
|
|||||||
using System.Text;
|
|
||||||
using Content.Shared.Shuttles.BUIStates;
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
using Content.Shared.Shuttles.Systems;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Map;
|
|
||||||
|
|
||||||
namespace Content.Client.Shuttles.UI;
|
namespace Content.Client.Shuttles.UI;
|
||||||
|
|
||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class DockObject : PanelContainer
|
public sealed partial class DockObject : PanelContainer
|
||||||
{
|
{
|
||||||
[PublicAPI]
|
|
||||||
public event Action? UndockPressed;
|
|
||||||
|
|
||||||
[PublicAPI]
|
|
||||||
public event Action? ViewPressed;
|
|
||||||
|
|
||||||
public BoxContainer ContentsContainer => Contents;
|
public BoxContainer ContentsContainer => Contents;
|
||||||
|
|
||||||
public DockObject()
|
public DockObject()
|
||||||
|
|||||||
@@ -163,16 +163,6 @@ public sealed partial class DockingScreen : BoxContainer
|
|||||||
}
|
}
|
||||||
|
|
||||||
dockContainer.AddDock(dock, DockingControl);
|
dockContainer.AddDock(dock, DockingControl);
|
||||||
|
|
||||||
dockContainer.ViewPressed += () =>
|
|
||||||
{
|
|
||||||
OnDockPress(dock);
|
|
||||||
};
|
|
||||||
|
|
||||||
dockContainer.UndockPressed += () =>
|
|
||||||
{
|
|
||||||
UndockRequest?.Invoke(dock.Entity);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,13 +81,19 @@ public sealed partial class NavScreen : BoxContainer
|
|||||||
// Get the positive reduced angle.
|
// Get the positive reduced angle.
|
||||||
var displayRot = -worldRot.Reduced();
|
var displayRot = -worldRot.Reduced();
|
||||||
|
|
||||||
GridPosition.Text = $"{worldPos.X:0.0}, {worldPos.Y:0.0}";
|
GridPosition.Text = Loc.GetString("shuttle-console-position-value",
|
||||||
GridOrientation.Text = $"{displayRot.Degrees:0.0}";
|
("X", $"{worldPos.X:0.0}"),
|
||||||
|
("Y", $"{worldPos.Y:0.0}"));
|
||||||
|
GridOrientation.Text = Loc.GetString("shuttle-console-orientation-value",
|
||||||
|
("angle", $"{displayRot.Degrees:0.0}"));
|
||||||
|
|
||||||
var gridVelocity = gridBody.LinearVelocity;
|
var gridVelocity = gridBody.LinearVelocity;
|
||||||
gridVelocity = displayRot.RotateVec(gridVelocity);
|
gridVelocity = displayRot.RotateVec(gridVelocity);
|
||||||
// Get linear velocity relative to the console entity
|
// Get linear velocity relative to the console entity
|
||||||
GridLinearVelocity.Text = $"{gridVelocity.X + 10f * float.Epsilon:0.0}, {gridVelocity.Y + 10f * float.Epsilon:0.0}";
|
GridLinearVelocity.Text = Loc.GetString("shuttle-console-linear-velocity-value",
|
||||||
GridAngularVelocity.Text = $"{-gridBody.AngularVelocity + 10f * float.Epsilon:0.0}";
|
("X", $"{gridVelocity.X + 10f * float.Epsilon:0.0}"),
|
||||||
|
("Y", $"{gridVelocity.Y + 10f * float.Epsilon:0.0}"));
|
||||||
|
GridAngularVelocity.Text = Loc.GetString("shuttle-console-angular-velocity-value",
|
||||||
|
("angularVelocity", $"{-MathHelper.RadiansToDegrees(gridBody.AngularVelocity) + 10f * float.Epsilon:0.0}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<ui:RadialMenu xmlns="https://spacestation14.io"
|
<ui:RadialMenu xmlns="https://spacestation14.io"
|
||||||
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
|
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
BackButtonStyleClass="RadialMenuBackButton"
|
BackButtonStyleClass="RadialMenuBackButton"
|
||||||
CloseButtonStyleClass="RadialMenuCloseButton"
|
CloseButtonStyleClass="RadialMenuCloseButton"
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
MinSize="450 450">
|
MinSize="450 450">
|
||||||
|
|
||||||
<!-- Main -->
|
<!-- Main -->
|
||||||
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" Radius="64" ReserveSpaceForHiddenChildren="False">
|
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100" ReserveSpaceForHiddenChildren="False">
|
||||||
</ui:RadialContainer>
|
</ui:RadialContainer>
|
||||||
|
|
||||||
</ui:RadialMenu>
|
</ui:RadialMenu>
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ public sealed partial class StationAiMenu : RadialMenu
|
|||||||
// TODO: This radial boilerplate is quite annoying
|
// TODO: This radial boilerplate is quite annoying
|
||||||
var button = new StationAiMenuButton(action.Event)
|
var button = new StationAiMenuButton(action.Event)
|
||||||
{
|
{
|
||||||
StyleClasses = { "RadialMenuButton" },
|
|
||||||
SetSize = new Vector2(64f, 64f),
|
SetSize = new Vector2(64f, 64f),
|
||||||
ToolTip = action.Tooltip != null ? Loc.GetString(action.Tooltip) : null,
|
ToolTip = action.Tooltip != null ? Loc.GetString(action.Tooltip) : null,
|
||||||
};
|
};
|
||||||
@@ -121,7 +120,7 @@ public sealed partial class StationAiMenu : RadialMenu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class StationAiMenuButton(BaseStationAiAction action) : RadialMenuTextureButton
|
public sealed class StationAiMenuButton(BaseStationAiAction action) : RadialMenuTextureButtonWithSector
|
||||||
{
|
{
|
||||||
public BaseStationAiAction Action = action;
|
public BaseStationAiAction Action = action;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ using System.Numerics;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Client.Administration.Managers;
|
using Content.Client.Administration.Managers;
|
||||||
using Content.Shared.Database;
|
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Shared.ContentPack;
|
using Robust.Shared.ContentPack;
|
||||||
|
using Robust.Shared.Exceptions;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
@@ -24,6 +24,7 @@ public sealed class ContentSpriteSystem : EntitySystem
|
|||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
[Dependency] private readonly IResourceManager _resManager = default!;
|
[Dependency] private readonly IResourceManager _resManager = default!;
|
||||||
[Dependency] private readonly IUserInterfaceManager _ui = default!;
|
[Dependency] private readonly IUserInterfaceManager _ui = default!;
|
||||||
|
[Dependency] private readonly IRuntimeLog _runtimeLog = default!;
|
||||||
|
|
||||||
private ContentSpriteControl _control = new();
|
private ContentSpriteControl _control = new();
|
||||||
|
|
||||||
@@ -42,12 +43,12 @@ public sealed class ContentSpriteSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
base.Shutdown();
|
base.Shutdown();
|
||||||
|
|
||||||
foreach (var queued in _control._queuedTextures)
|
foreach (var queued in _control.QueuedTextures)
|
||||||
{
|
{
|
||||||
queued.Tcs.SetCanceled();
|
queued.Tcs.SetCanceled();
|
||||||
}
|
}
|
||||||
|
|
||||||
_control._queuedTextures.Clear();
|
_control.QueuedTextures.Clear();
|
||||||
|
|
||||||
_ui.RootControl.RemoveChild(_control);
|
_ui.RootControl.RemoveChild(_control);
|
||||||
}
|
}
|
||||||
@@ -103,7 +104,7 @@ public sealed class ContentSpriteSystem : EntitySystem
|
|||||||
var texture = _clyde.CreateRenderTarget(new Vector2i(size.X, size.Y), new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "export");
|
var texture = _clyde.CreateRenderTarget(new Vector2i(size.X, size.Y), new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "export");
|
||||||
var tcs = new TaskCompletionSource(cancelToken);
|
var tcs = new TaskCompletionSource(cancelToken);
|
||||||
|
|
||||||
_control._queuedTextures.Enqueue((texture, direction, entity, includeId, tcs));
|
_control.QueuedTextures.Enqueue((texture, direction, entity, includeId, tcs));
|
||||||
|
|
||||||
await tcs.Task;
|
await tcs.Task;
|
||||||
}
|
}
|
||||||
@@ -113,13 +114,21 @@ public sealed class ContentSpriteSystem : EntitySystem
|
|||||||
if (!_adminManager.IsAdmin())
|
if (!_adminManager.IsAdmin())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var target = ev.Target;
|
||||||
Verb verb = new()
|
Verb verb = new()
|
||||||
{
|
{
|
||||||
Text = Loc.GetString("export-entity-verb-get-data-text"),
|
Text = Loc.GetString("export-entity-verb-get-data-text"),
|
||||||
Category = VerbCategory.Debug,
|
Category = VerbCategory.Debug,
|
||||||
Act = () =>
|
Act = async () =>
|
||||||
{
|
{
|
||||||
Export(ev.Target);
|
try
|
||||||
|
{
|
||||||
|
await Export(target);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_runtimeLog.LogException(e, $"{nameof(ContentSpriteSystem)}.{nameof(Export)}");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -141,7 +150,7 @@ public sealed class ContentSpriteSystem : EntitySystem
|
|||||||
Direction Direction,
|
Direction Direction,
|
||||||
EntityUid Entity,
|
EntityUid Entity,
|
||||||
bool IncludeId,
|
bool IncludeId,
|
||||||
TaskCompletionSource Tcs)> _queuedTextures = new();
|
TaskCompletionSource Tcs)> QueuedTextures = new();
|
||||||
|
|
||||||
private ISawmill _sawmill;
|
private ISawmill _sawmill;
|
||||||
|
|
||||||
@@ -155,7 +164,7 @@ public sealed class ContentSpriteSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
base.Draw(handle);
|
base.Draw(handle);
|
||||||
|
|
||||||
while (_queuedTextures.TryDequeue(out var queued))
|
while (QueuedTextures.TryDequeue(out var queued))
|
||||||
{
|
{
|
||||||
if (queued.Tcs.Task.IsCanceled)
|
if (queued.Tcs.Task.IsCanceled)
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Content.Shared.StatusIcon;
|
|||||||
using Content.Shared.StatusIcon.Components;
|
using Content.Shared.StatusIcon.Components;
|
||||||
using Content.Shared.Stealth.Components;
|
using Content.Shared.Stealth.Components;
|
||||||
using Content.Shared.Whitelist;
|
using Content.Shared.Whitelist;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
@@ -85,6 +86,9 @@ public sealed class StatusIconSystem : SharedStatusIconSystem
|
|||||||
if (data.HideOnStealth && TryComp<StealthComponent>(ent, out var stealth) && stealth.Enabled)
|
if (data.HideOnStealth && TryComp<StealthComponent>(ent, out var stealth) && stealth.Enabled)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (TryComp<SpriteComponent>(ent, out var sprite) && !sprite.Visible)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (data.ShowTo != null && !_entityWhitelist.IsValid(data.ShowTo, viewer))
|
if (data.ShowTo != null && !_entityWhitelist.IsValid(data.ShowTo, viewer))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +1,80 @@
|
|||||||
using Content.Client.Storage.Systems;
|
using Content.Client.UserInterface.Systems.Storage;
|
||||||
|
using Content.Client.UserInterface.Systems.Storage.Controls;
|
||||||
using Content.Shared.Storage;
|
using Content.Shared.Storage;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
|
||||||
namespace Content.Client.Storage;
|
namespace Content.Client.Storage;
|
||||||
|
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public sealed class StorageBoundUserInterface : BoundUserInterface
|
public sealed class StorageBoundUserInterface : BoundUserInterface
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
private StorageWindow? _window;
|
||||||
|
|
||||||
private readonly StorageSystem _storage;
|
|
||||||
|
|
||||||
[Obsolete] public override bool DeferredClose => false;
|
|
||||||
|
|
||||||
public StorageBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
public StorageBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||||
{
|
{
|
||||||
IoCManager.InjectDependencies(this);
|
|
||||||
_storage = _entManager.System<StorageSystem>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
base.Open();
|
base.Open();
|
||||||
|
|
||||||
if (_entManager.TryGetComponent<StorageComponent>(Owner, out var comp))
|
_window = IoCManager.Resolve<IUserInterfaceManager>()
|
||||||
_storage.OpenStorageWindow((Owner, comp));
|
.GetUIController<StorageUIController>()
|
||||||
|
.CreateStorageWindow(Owner);
|
||||||
|
|
||||||
|
if (EntMan.TryGetComponent(Owner, out StorageComponent? storage))
|
||||||
|
{
|
||||||
|
_window.UpdateContainer((Owner, storage));
|
||||||
|
}
|
||||||
|
|
||||||
|
_window.OnClose += Close;
|
||||||
|
_window.FlagDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Refresh()
|
||||||
|
{
|
||||||
|
_window?.FlagDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reclaim()
|
||||||
|
{
|
||||||
|
if (_window == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_window.OnClose -= Close;
|
||||||
|
_window.Orphan();
|
||||||
|
_window = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
if (!disposing)
|
|
||||||
|
Reclaim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Hide()
|
||||||
|
{
|
||||||
|
if (_window == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_storage.CloseStorageWindow(Owner);
|
_window.Visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Show()
|
||||||
|
{
|
||||||
|
if (_window == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_window.Visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReOpen()
|
||||||
|
{
|
||||||
|
_window?.Orphan();
|
||||||
|
_window = null;
|
||||||
|
Open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ using Content.Client.Animations;
|
|||||||
using Content.Shared.Hands;
|
using Content.Shared.Hands;
|
||||||
using Content.Shared.Storage;
|
using Content.Shared.Storage;
|
||||||
using Content.Shared.Storage.EntitySystems;
|
using Content.Shared.Storage.EntitySystems;
|
||||||
using Robust.Shared.Collections;
|
using Robust.Client.Player;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
@@ -13,114 +14,91 @@ namespace Content.Client.Storage.Systems;
|
|||||||
public sealed class StorageSystem : SharedStorageSystem
|
public sealed class StorageSystem : SharedStorageSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly IPlayerManager _player = default!;
|
||||||
[Dependency] private readonly EntityPickupAnimationSystem _entityPickupAnimation = default!;
|
[Dependency] private readonly EntityPickupAnimationSystem _entityPickupAnimation = default!;
|
||||||
|
|
||||||
private readonly List<Entity<StorageComponent>> _openStorages = new();
|
private Dictionary<EntityUid, ItemStorageLocation> _oldStoredItems = new();
|
||||||
public int OpenStorageAmount => _openStorages.Count;
|
|
||||||
|
|
||||||
public event Action<Entity<StorageComponent>>? StorageUpdated;
|
|
||||||
public event Action<Entity<StorageComponent>?>? StorageOrderChanged;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<StorageComponent, ComponentShutdown>(OnShutdown);
|
SubscribeLocalEvent<StorageComponent, ComponentHandleState>(OnStorageHandleState);
|
||||||
SubscribeNetworkEvent<PickupAnimationEvent>(HandlePickupAnimation);
|
SubscribeNetworkEvent<PickupAnimationEvent>(HandlePickupAnimation);
|
||||||
SubscribeAllEvent<AnimateInsertingEntitiesEvent>(HandleAnimatingInsertingEntities);
|
SubscribeAllEvent<AnimateInsertingEntitiesEvent>(HandleAnimatingInsertingEntities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnStorageHandleState(EntityUid uid, StorageComponent component, ref ComponentHandleState args)
|
||||||
|
{
|
||||||
|
if (args.Current is not StorageComponentState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
component.Grid.Clear();
|
||||||
|
component.Grid.AddRange(state.Grid);
|
||||||
|
component.MaxItemSize = state.MaxItemSize;
|
||||||
|
component.Whitelist = state.Whitelist;
|
||||||
|
component.Blacklist = state.Blacklist;
|
||||||
|
|
||||||
|
_oldStoredItems.Clear();
|
||||||
|
|
||||||
|
foreach (var item in component.StoredItems)
|
||||||
|
{
|
||||||
|
_oldStoredItems.Add(item.Key, item.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
component.StoredItems.Clear();
|
||||||
|
|
||||||
|
foreach (var (nent, location) in state.StoredItems)
|
||||||
|
{
|
||||||
|
var ent = EnsureEntity<StorageComponent>(nent, uid);
|
||||||
|
component.StoredItems[ent] = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
component.SavedLocations.Clear();
|
||||||
|
|
||||||
|
foreach (var loc in state.SavedLocations)
|
||||||
|
{
|
||||||
|
component.SavedLocations[loc.Key] = new(loc.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var uiDirty = !component.StoredItems.SequenceEqual(_oldStoredItems);
|
||||||
|
|
||||||
|
if (uiDirty && UI.TryGetOpenUi<StorageBoundUserInterface>(uid, StorageComponent.StorageUiKey.Key, out var storageBui))
|
||||||
|
{
|
||||||
|
storageBui.Refresh();
|
||||||
|
// Make sure nesting still updated.
|
||||||
|
var player = _player.LocalEntity;
|
||||||
|
|
||||||
|
if (NestedStorage && player != null && ContainerSystem.TryGetContainingContainer((uid, null, null), out var container) &&
|
||||||
|
UI.TryGetOpenUi<StorageBoundUserInterface>(container.Owner, StorageComponent.StorageUiKey.Key, out var containerBui))
|
||||||
|
{
|
||||||
|
containerBui.Hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void UpdateUI(Entity<StorageComponent?> entity)
|
public override void UpdateUI(Entity<StorageComponent?> entity)
|
||||||
{
|
{
|
||||||
if (Resolve(entity.Owner, ref entity.Comp))
|
if (UI.TryGetOpenUi<StorageBoundUserInterface>(entity.Owner, StorageComponent.StorageUiKey.Key, out var sBui))
|
||||||
StorageUpdated?.Invoke((entity, entity.Comp));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OpenStorageWindow(Entity<StorageComponent> entity)
|
|
||||||
{
|
{
|
||||||
if (_openStorages.Contains(entity))
|
sBui.Refresh();
|
||||||
{
|
|
||||||
if (_openStorages.LastOrDefault() == entity)
|
|
||||||
{
|
|
||||||
CloseStorageWindow((entity, entity.Comp));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var storages = new ValueList<Entity<StorageComponent>>(_openStorages);
|
|
||||||
var reverseStorages = storages.Reverse();
|
|
||||||
|
|
||||||
foreach (var storageEnt in reverseStorages)
|
|
||||||
{
|
|
||||||
if (storageEnt == entity)
|
|
||||||
break;
|
|
||||||
|
|
||||||
CloseStorageBoundUserInterface(storageEnt.Owner);
|
|
||||||
_openStorages.Remove(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClearNonParentStorages(entity);
|
|
||||||
_openStorages.Add(entity);
|
|
||||||
Entity<StorageComponent>? last = _openStorages.LastOrDefault();
|
|
||||||
StorageOrderChanged?.Invoke(last);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CloseStorageWindow(Entity<StorageComponent?> entity)
|
|
||||||
{
|
|
||||||
if (!Resolve(entity, ref entity.Comp, false))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!_openStorages.Contains((entity, entity.Comp)))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var storages = new ValueList<Entity<StorageComponent>>(_openStorages);
|
|
||||||
var reverseStorages = storages.Reverse();
|
|
||||||
|
|
||||||
foreach (var storage in reverseStorages)
|
|
||||||
{
|
|
||||||
CloseStorageBoundUserInterface(storage.Owner);
|
|
||||||
_openStorages.Remove(storage);
|
|
||||||
if (storage.Owner == entity.Owner)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Entity<StorageComponent>? last = null;
|
|
||||||
if (_openStorages.Any())
|
|
||||||
last = _openStorages.LastOrDefault();
|
|
||||||
StorageOrderChanged?.Invoke(last);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearNonParentStorages(EntityUid uid)
|
|
||||||
{
|
|
||||||
var storages = new ValueList<Entity<StorageComponent>>(_openStorages);
|
|
||||||
var reverseStorages = storages.Reverse();
|
|
||||||
|
|
||||||
foreach (var storage in reverseStorages)
|
|
||||||
{
|
|
||||||
if (storage.Comp.Container.Contains(uid))
|
|
||||||
break;
|
|
||||||
|
|
||||||
CloseStorageBoundUserInterface(storage.Owner);
|
|
||||||
_openStorages.Remove(storage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CloseStorageBoundUserInterface(Entity<UserInterfaceComponent?> entity)
|
protected override void HideStorageWindow(EntityUid uid, EntityUid actor)
|
||||||
{
|
{
|
||||||
if (!Resolve(entity, ref entity.Comp, false))
|
if (UI.TryGetOpenUi<StorageBoundUserInterface>(uid, StorageComponent.StorageUiKey.Key, out var storageBui))
|
||||||
return;
|
{
|
||||||
|
storageBui.Hide();
|
||||||
if (entity.Comp.ClientOpenInterfaces.GetValueOrDefault(StorageComponent.StorageUiKey.Key) is not { } bui)
|
}
|
||||||
return;
|
|
||||||
|
|
||||||
bui.Close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnShutdown(Entity<StorageComponent> ent, ref ComponentShutdown args)
|
protected override void ShowStorageWindow(EntityUid uid, EntityUid actor)
|
||||||
{
|
{
|
||||||
CloseStorageWindow((ent, ent.Comp));
|
if (UI.TryGetOpenUi<StorageBoundUserInterface>(uid, StorageComponent.StorageUiKey.Key, out var storageBui))
|
||||||
|
{
|
||||||
|
storageBui.Show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ public sealed class StoreBoundUserInterface : BoundUserInterface
|
|||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
_menu = this.CreateWindow<StoreMenu>();
|
_menu = this.CreateWindow<StoreMenu>();
|
||||||
if (EntMan.TryGetComponent<StoreComponent>(Owner, out var store))
|
if (EntMan.TryGetComponent<StoreComponent>(Owner, out var store))
|
||||||
_menu.Title = Loc.GetString(store.Name);
|
_menu.Title = Loc.GetString(store.Name);
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ public sealed class SurveillanceCameraSetupBoundUi : BoundUserInterface
|
|||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
_window = new();
|
_window = new();
|
||||||
|
|
||||||
if (_type == SurveillanceCameraSetupUiKey.Router)
|
if (_type == SurveillanceCameraSetupUiKey.Router)
|
||||||
|
|||||||
@@ -87,9 +87,6 @@ public sealed partial class DialogWindow : FancyWindow
|
|||||||
Prompts.AddChild(box);
|
Prompts.AddChild(box);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grab keyboard focus for the first dialog entry
|
|
||||||
_promptLines[0].Item2.GrabKeyboardFocus();
|
|
||||||
|
|
||||||
OkButton.OnPressed += _ => Confirm();
|
OkButton.OnPressed += _ => Confirm();
|
||||||
|
|
||||||
CancelButton.OnPressed += _ =>
|
CancelButton.OnPressed += _ =>
|
||||||
@@ -110,6 +107,14 @@ public sealed partial class DialogWindow : FancyWindow
|
|||||||
OpenCentered();
|
OpenCentered();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Opened()
|
||||||
|
{
|
||||||
|
base.Opened();
|
||||||
|
|
||||||
|
// Grab keyboard focus for the first dialog entry
|
||||||
|
_promptLines[0].Item2.GrabKeyboardFocus();
|
||||||
|
}
|
||||||
|
|
||||||
private void Confirm()
|
private void Confirm()
|
||||||
{
|
{
|
||||||
var results = new Dictionary<string, string>();
|
var results = new Dictionary<string, string>();
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ using Robust.Client.ResourceManagement;
|
|||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Graphics;
|
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
@@ -96,7 +95,6 @@ public sealed partial class FancyTree : Control
|
|||||||
private void LoadIcons()
|
private void LoadIcons()
|
||||||
{
|
{
|
||||||
IconColor = TryGetStyleProperty(StylePropertyIconColor, out Color color) ? color : Color.White;
|
IconColor = TryGetStyleProperty(StylePropertyIconColor, out Color color) ? color : Color.White;
|
||||||
string? path;
|
|
||||||
|
|
||||||
if (!TryGetStyleProperty(StylePropertyIconExpanded, out IconExpanded))
|
if (!TryGetStyleProperty(StylePropertyIconExpanded, out IconExpanded))
|
||||||
IconExpanded = _resCache.GetTexture(DefaultIconExpanded);
|
IconExpanded = _resCache.GetTexture(DefaultIconExpanded);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using Robust.Client.Graphics;
|
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
@@ -8,6 +7,11 @@ namespace Content.Client.UserInterface.Controls;
|
|||||||
[Virtual]
|
[Virtual]
|
||||||
public class RadialContainer : LayoutContainer
|
public class RadialContainer : LayoutContainer
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Increment of radius per child element to be rendered.
|
||||||
|
/// </summary>
|
||||||
|
private const float RadiusIncrement = 5f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifies the anglular range, in radians, in which child elements will be placed.
|
/// Specifies the anglular range, in radians, in which child elements will be placed.
|
||||||
/// The first value denotes the angle at which the first element is to be placed, and
|
/// The first value denotes the angle at which the first element is to be placed, and
|
||||||
@@ -49,10 +53,30 @@ public class RadialContainer : LayoutContainer
|
|||||||
public RAlignment RadialAlignment { get; set; } = RAlignment.Clockwise;
|
public RAlignment RadialAlignment { get; set; } = RAlignment.Clockwise;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines how far from the radial container's center that its child elements will be placed
|
/// Radial menu radius determines how far from the radial container's center its child elements will be placed.
|
||||||
|
/// To correctly display dynamic amount of elements control actually resizes depending on amount of child buttons,
|
||||||
|
/// but uses this property as base value for final radius calculation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public float Radius { get; set; } = 100f;
|
public float InitialRadius { get; set; } = 100f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Radial menu radius determines how far from the radial container's center its child elements will be placed.
|
||||||
|
/// This is dynamically calculated (based on child button count) radius, result of <see cref="InitialRadius"/> and
|
||||||
|
/// <see cref="RadiusIncrement"/> multiplied by currently visible child button count.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public float CalculatedRadius { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines radial menu button sectors inner radius, is a multiplier of <see cref="InitialRadius"/>.
|
||||||
|
/// </summary>
|
||||||
|
public float InnerRadiusMultiplier { get; set; } = 0.5f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines radial menu button sectors outer radius, is a multiplier of <see cref="InitialRadius"/>.
|
||||||
|
/// </summary>
|
||||||
|
public float OuterRadiusMultiplier { get; set; } = 1.5f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets whether the container should reserve a space on the layout for child which are not currently visible
|
/// Sets whether the container should reserve a space on the layout for child which are not currently visible
|
||||||
@@ -68,38 +92,75 @@ public class RadialContainer : LayoutContainer
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleScreen handle)
|
/// <inheritdoc />
|
||||||
|
protected override Vector2 ArrangeOverride(Vector2 finalSize)
|
||||||
{
|
{
|
||||||
|
var children = ReserveSpaceForHiddenChildren
|
||||||
|
? Children
|
||||||
|
: Children.Where(x => x.Visible);
|
||||||
|
|
||||||
const float baseRadius = 100f;
|
|
||||||
const float radiusIncrement = 5f;
|
|
||||||
|
|
||||||
var children = ReserveSpaceForHiddenChildren ? Children : Children.Where(x => x.Visible);
|
|
||||||
var childCount = children.Count();
|
var childCount = children.Count();
|
||||||
|
|
||||||
// Add padding from the center at higher child counts so they don't overlap.
|
// Add padding from the center at higher child counts so they don't overlap.
|
||||||
Radius = baseRadius + (childCount * radiusIncrement);
|
CalculatedRadius = InitialRadius + (childCount * RadiusIncrement);
|
||||||
|
|
||||||
|
var isAntiClockwise = RadialAlignment == RAlignment.AntiClockwise;
|
||||||
|
|
||||||
// Determine the size of the arc, accounting for clockwise and anti-clockwise arrangements
|
// Determine the size of the arc, accounting for clockwise and anti-clockwise arrangements
|
||||||
var arc = AngularRange.Y - AngularRange.X;
|
var arc = AngularRange.Y - AngularRange.X;
|
||||||
arc = (arc < 0) ? MathF.Tau + arc : arc;
|
arc = arc < 0
|
||||||
arc = (RadialAlignment == RAlignment.AntiClockwise) ? MathF.Tau - arc : arc;
|
? MathF.Tau + arc
|
||||||
|
: arc;
|
||||||
|
arc = isAntiClockwise
|
||||||
|
? MathF.Tau - arc
|
||||||
|
: arc;
|
||||||
|
|
||||||
// Account for both circular arrangements and arc-based arrangements
|
// Account for both circular arrangements and arc-based arrangements
|
||||||
var childMod = MathHelper.CloseTo(arc, MathF.Tau, 0.01f) ? 0 : 1;
|
var childMod = MathHelper.CloseTo(arc, MathF.Tau, 0.01f)
|
||||||
|
? 0
|
||||||
|
: 1;
|
||||||
|
|
||||||
// Determine the separation between child elements
|
// Determine the separation between child elements
|
||||||
var sepAngle = arc / (childCount - childMod);
|
var sepAngle = arc / (childCount - childMod);
|
||||||
sepAngle *= (RadialAlignment == RAlignment.AntiClockwise) ? -1f : 1f;
|
sepAngle *= isAntiClockwise
|
||||||
|
? -1f
|
||||||
|
: 1f;
|
||||||
|
|
||||||
|
var controlCenter = finalSize * 0.5f;
|
||||||
|
|
||||||
// Adjust the positions of all the child elements
|
// Adjust the positions of all the child elements
|
||||||
foreach (var (i, child) in children.Select((x, i) => (i, x)))
|
var query = children.Select((x, index) => (index, x));
|
||||||
|
foreach (var (childIndex, child) in query)
|
||||||
{
|
{
|
||||||
var position = new Vector2(Radius * MathF.Sin(AngularRange.X + sepAngle * i) + Width / 2f - child.Width / 2f, -Radius * MathF.Cos(AngularRange.X + sepAngle * i) + Height / 2f - child.Height / 2f);
|
const float angleOffset = MathF.PI * 0.5f;
|
||||||
|
|
||||||
|
var targetAngleOfChild = AngularRange.X + sepAngle * (childIndex + 0.5f) + angleOffset;
|
||||||
|
|
||||||
|
// flooring values for snapping float values to physical grid -
|
||||||
|
// it prevents gaps and overlapping between different button segments
|
||||||
|
var position = new Vector2(
|
||||||
|
MathF.Floor(CalculatedRadius * MathF.Cos(targetAngleOfChild)),
|
||||||
|
MathF.Floor(-CalculatedRadius * MathF.Sin(targetAngleOfChild))
|
||||||
|
) + controlCenter - child.DesiredSize * 0.5f + Position;
|
||||||
|
|
||||||
SetPosition(child, position);
|
SetPosition(child, position);
|
||||||
|
|
||||||
|
// radial menu buttons with sector need to also know in which sector and around which point
|
||||||
|
// they should be rendered, how much space sector should should take etc.
|
||||||
|
if (child is IRadialMenuItemWithSector tb)
|
||||||
|
{
|
||||||
|
tb.AngleSectorFrom = sepAngle * childIndex;
|
||||||
|
tb.AngleSectorTo = sepAngle * (childIndex + 1);
|
||||||
|
tb.AngleOffset = angleOffset;
|
||||||
|
tb.InnerRadius = CalculatedRadius * InnerRadiusMultiplier;
|
||||||
|
tb.OuterRadius = CalculatedRadius * OuterRadiusMultiplier;
|
||||||
|
tb.ParentCenter = controlCenter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return base.ArrangeOverride(finalSize);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifies the different radial alignment modes
|
/// Specifies the different radial alignment modes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -109,4 +170,5 @@ public class RadialContainer : LayoutContainer
|
|||||||
Clockwise,
|
Clockwise,
|
||||||
AntiClockwise,
|
AntiClockwise,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ using Robust.Client.UserInterface.Controls;
|
|||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Content.Shared.Input;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Shared.Input;
|
||||||
|
|
||||||
namespace Content.Client.UserInterface.Controls;
|
namespace Content.Client.UserInterface.Controls;
|
||||||
|
|
||||||
@@ -12,7 +15,12 @@ public class RadialMenu : BaseWindow
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contextual button used to traverse through previous layers of the radial menu
|
/// Contextual button used to traverse through previous layers of the radial menu
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TextureButton? ContextualButton { get; set; }
|
public RadialMenuContextualCentralTextureButton ContextualButton { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Button that represents outer area of menu (closes menu on outside clicks).
|
||||||
|
/// </summary>
|
||||||
|
public RadialMenuOuterAreaButton MenuOuterAreaButton { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set a style class to be applied to the contextual button when it is set to move the user back through previous layers of the radial menu
|
/// Set a style class to be applied to the contextual button when it is set to move the user back through previous layers of the radial menu
|
||||||
@@ -52,7 +60,7 @@ public class RadialMenu : BaseWindow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Control> _path = new();
|
private readonly List<Control> _path = new();
|
||||||
private string? _backButtonStyleClass;
|
private string? _backButtonStyleClass;
|
||||||
private string? _closeButtonStyleClass;
|
private string? _closeButtonStyleClass;
|
||||||
|
|
||||||
@@ -78,23 +86,56 @@ public class RadialMenu : BaseWindow
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Auto generate a contextual button for moving back through visited layers
|
// Auto generate a contextual button for moving back through visited layers
|
||||||
ContextualButton = new TextureButton()
|
ContextualButton = new RadialMenuContextualCentralTextureButton
|
||||||
{
|
{
|
||||||
HorizontalAlignment = HAlignment.Center,
|
HorizontalAlignment = HAlignment.Center,
|
||||||
VerticalAlignment = VAlignment.Center,
|
VerticalAlignment = VAlignment.Center,
|
||||||
SetSize = new Vector2(64f, 64f),
|
SetSize = new Vector2(64f, 64f),
|
||||||
};
|
};
|
||||||
|
MenuOuterAreaButton = new RadialMenuOuterAreaButton();
|
||||||
|
|
||||||
ContextualButton.OnButtonUp += _ => ReturnToPreviousLayer();
|
ContextualButton.OnButtonUp += _ => ReturnToPreviousLayer();
|
||||||
|
MenuOuterAreaButton.OnButtonUp += _ => Close();
|
||||||
AddChild(ContextualButton);
|
AddChild(ContextualButton);
|
||||||
|
AddChild(MenuOuterAreaButton);
|
||||||
|
|
||||||
// Hide any further add children, unless its promoted to the active layer
|
// Hide any further add children, unless its promoted to the active layer
|
||||||
OnChildAdded += child => child.Visible = (GetCurrentActiveLayer() == child);
|
OnChildAdded += child =>
|
||||||
|
{
|
||||||
|
child.Visible = GetCurrentActiveLayer() == child;
|
||||||
|
SetupContextualButtonData(child);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupContextualButtonData(Control child)
|
||||||
|
{
|
||||||
|
if (child is RadialContainer { Visible: true } container)
|
||||||
|
{
|
||||||
|
var parentCenter = MinSize * 0.5f;
|
||||||
|
ContextualButton.ParentCenter = parentCenter;
|
||||||
|
MenuOuterAreaButton.ParentCenter = parentCenter;
|
||||||
|
ContextualButton.InnerRadius = container.CalculatedRadius * container.InnerRadiusMultiplier;
|
||||||
|
MenuOuterAreaButton.OuterRadius = container.CalculatedRadius * container.OuterRadiusMultiplier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override Vector2 ArrangeOverride(Vector2 finalSize)
|
||||||
|
{
|
||||||
|
var result = base.ArrangeOverride(finalSize);
|
||||||
|
|
||||||
|
var currentLayer = GetCurrentActiveLayer();
|
||||||
|
if (currentLayer != null)
|
||||||
|
{
|
||||||
|
SetupContextualButtonData(currentLayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Control? GetCurrentActiveLayer()
|
private Control? GetCurrentActiveLayer()
|
||||||
{
|
{
|
||||||
var children = Children.Where(x => x != ContextualButton);
|
var children = Children.Where(x => x != ContextualButton && x != MenuOuterAreaButton);
|
||||||
|
|
||||||
if (!children.Any())
|
if (!children.Any())
|
||||||
return null;
|
return null;
|
||||||
@@ -116,7 +157,7 @@ public class RadialMenu : BaseWindow
|
|||||||
|
|
||||||
foreach (var child in Children)
|
foreach (var child in Children)
|
||||||
{
|
{
|
||||||
if (child == ContextualButton)
|
if (child == ContextualButton || child == MenuOuterAreaButton)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Hide layers which are not of interest
|
// Hide layers which are not of interest
|
||||||
@@ -129,6 +170,7 @@ public class RadialMenu : BaseWindow
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
child.Visible = true;
|
child.Visible = true;
|
||||||
|
SetupContextualButtonData(child);
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,7 +200,7 @@ public class RadialMenu : BaseWindow
|
|||||||
// Hide all children except the contextual button
|
// Hide all children except the contextual button
|
||||||
foreach (var child in Children)
|
foreach (var child in Children)
|
||||||
{
|
{
|
||||||
if (child != ContextualButton)
|
if (child != ContextualButton && child != MenuOuterAreaButton)
|
||||||
child.Visible = false;
|
child.Visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,49 +214,86 @@ public class RadialMenu : BaseWindow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for radial menu buttons. Excludes all actions except clicks and alt-clicks
|
||||||
|
/// from interactions.
|
||||||
|
/// </summary>
|
||||||
[Virtual]
|
[Virtual]
|
||||||
public class RadialMenuButton : Button
|
public class RadialMenuTextureButtonBase : TextureButton
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Upon clicking this button the radial menu will transition to the named layer
|
protected RadialMenuTextureButtonBase()
|
||||||
/// </summary>
|
{
|
||||||
public string? TargetLayer { get; set; }
|
EnableAllKeybinds = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
|
||||||
|
{
|
||||||
|
if (args.Function == EngineKeyFunctions.UIClick
|
||||||
|
|| args.Function == ContentKeyFunctions.AltActivateItemInWorld)
|
||||||
|
base.KeyBindUp(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A simple button that can move the user to a different layer within a radial menu
|
/// Special button for closing radial menu or going back between radial menu levels.
|
||||||
|
/// Is looking like just <see cref="TextureButton "/> but considers whole space around
|
||||||
|
/// itself (til radial menu buttons) as itself in case of clicking. But this 'effect'
|
||||||
|
/// works only if control have parent, and ActiveContainer property is set.
|
||||||
|
/// Also considers all space outside of radial menu buttons as itself for clicking.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public RadialMenuButton()
|
public sealed class RadialMenuContextualCentralTextureButton : RadialMenuTextureButtonBase
|
||||||
{
|
{
|
||||||
OnButtonUp += OnClicked;
|
public float InnerRadius { get; set; }
|
||||||
|
|
||||||
|
public Vector2? ParentCenter { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override bool HasPoint(Vector2 point)
|
||||||
|
{
|
||||||
|
if (ParentCenter == null)
|
||||||
|
{
|
||||||
|
return base.HasPoint(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClicked(ButtonEventArgs args)
|
var distSquared = (point + Position - ParentCenter.Value).LengthSquared();
|
||||||
{
|
|
||||||
if (TargetLayer == null || TargetLayer == string.Empty)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var parent = FindParentMultiLayerContainer(this);
|
var innerRadiusSquared = InnerRadius * InnerRadius;
|
||||||
|
|
||||||
if (parent == null)
|
// comparing to squared values is faster then making sqrt
|
||||||
return;
|
return distSquared < innerRadiusSquared;
|
||||||
|
}
|
||||||
parent.TryToMoveToNewLayer(TargetLayer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private RadialMenu? FindParentMultiLayerContainer(Control control)
|
/// <summary>
|
||||||
|
/// Menu button for outer area of radial menu (covers everything 'outside').
|
||||||
|
/// </summary>
|
||||||
|
public sealed class RadialMenuOuterAreaButton : RadialMenuTextureButtonBase
|
||||||
{
|
{
|
||||||
foreach (var ancestor in control.GetSelfAndLogicalAncestors())
|
public float OuterRadius { get; set; }
|
||||||
|
|
||||||
|
public Vector2? ParentCenter { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override bool HasPoint(Vector2 point)
|
||||||
{
|
{
|
||||||
if (ancestor is RadialMenu)
|
if (ParentCenter == null)
|
||||||
return ancestor as RadialMenu;
|
{
|
||||||
|
return base.HasPoint(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
var distSquared = (point + Position - ParentCenter.Value).LengthSquared();
|
||||||
|
|
||||||
|
var outerRadiusSquared = OuterRadius * OuterRadius;
|
||||||
|
|
||||||
|
// comparing to squared values is faster, then making sqrt
|
||||||
|
return distSquared > outerRadiusSquared;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Virtual]
|
[Virtual]
|
||||||
public class RadialMenuTextureButton : TextureButton
|
public class RadialMenuTextureButton : RadialMenuTextureButtonBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Upon clicking this button the radial menu will be moved to the named layer
|
/// Upon clicking this button the radial menu will be moved to the named layer
|
||||||
@@ -226,6 +305,7 @@ public class RadialMenuTextureButton : TextureButton
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public RadialMenuTextureButton()
|
public RadialMenuTextureButton()
|
||||||
{
|
{
|
||||||
|
EnableAllKeybinds = true;
|
||||||
OnButtonUp += OnClicked;
|
OnButtonUp += OnClicked;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,10 +326,329 @@ public class RadialMenuTextureButton : TextureButton
|
|||||||
{
|
{
|
||||||
foreach (var ancestor in control.GetSelfAndLogicalAncestors())
|
foreach (var ancestor in control.GetSelfAndLogicalAncestors())
|
||||||
{
|
{
|
||||||
if (ancestor is RadialMenu)
|
if (ancestor is RadialMenu menu)
|
||||||
return ancestor as RadialMenu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface IRadialMenuItemWithSector
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Angle in radian where button sector should start.
|
||||||
|
/// </summary>
|
||||||
|
public float AngleSectorFrom { set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Angle in radian where button sector should end.
|
||||||
|
/// </summary>
|
||||||
|
public float AngleSectorTo { set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Outer radius for drawing segment and pointer detection.
|
||||||
|
/// </summary>
|
||||||
|
public float OuterRadius { set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Outer radius for drawing segment and pointer detection.
|
||||||
|
/// </summary>
|
||||||
|
public float InnerRadius { set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Offset in radian by which menu button should be rotated.
|
||||||
|
/// </summary>
|
||||||
|
public float AngleOffset { set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Coordinates of center in parent component - button container.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 ParentCenter { set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Virtual]
|
||||||
|
public class RadialMenuTextureButtonWithSector : RadialMenuTextureButton, IRadialMenuItemWithSector
|
||||||
|
{
|
||||||
|
private Vector2[]? _sectorPointsForDrawing;
|
||||||
|
|
||||||
|
private float _angleSectorFrom;
|
||||||
|
private float _angleSectorTo;
|
||||||
|
private float _outerRadius;
|
||||||
|
private float _innerRadius;
|
||||||
|
private float _angleOffset;
|
||||||
|
|
||||||
|
private bool _isWholeCircle;
|
||||||
|
private Vector2? _parentCenter;
|
||||||
|
|
||||||
|
private Color _backgroundColorSrgb = Color.ToSrgb(new Color(70, 73, 102, 128));
|
||||||
|
private Color _hoverBackgroundColorSrgb = Color.ToSrgb(new Color(87, 91, 127, 128));
|
||||||
|
private Color _borderColorSrgb = Color.ToSrgb(new Color(173, 216, 230, 70));
|
||||||
|
private Color _hoverBorderColorSrgb = Color.ToSrgb(new Color(87, 91, 127, 128));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Marker, that control should render border of segment. Is false by default.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// By default color of border is same as color of background. Use <see cref="BorderColor"/>
|
||||||
|
/// and <see cref="HoverBorderColor"/> to change it.
|
||||||
|
/// </remarks>
|
||||||
|
public bool DrawBorder { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Marker, that control should render background of all sector. Is true by default.
|
||||||
|
/// </summary>
|
||||||
|
public bool DrawBackground { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Marker, that control should render separator lines.
|
||||||
|
/// Separator lines are used to visually separate sector of radial menu items.
|
||||||
|
/// Is true by default
|
||||||
|
/// </summary>
|
||||||
|
public bool DrawSeparators { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Color of background in non-hovered state. Accepts RGB color, works with sRGB for DrawPrimitive internally.
|
||||||
|
/// </summary>
|
||||||
|
public Color BackgroundColor
|
||||||
|
{
|
||||||
|
get => Color.FromSrgb(_backgroundColorSrgb);
|
||||||
|
set => _backgroundColorSrgb = Color.ToSrgb(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Color of background in hovered state. Accepts RGB color, works with sRGB for DrawPrimitive internally.
|
||||||
|
/// </summary>
|
||||||
|
public Color HoverBackgroundColor
|
||||||
|
{
|
||||||
|
get => Color.FromSrgb(_hoverBackgroundColorSrgb);
|
||||||
|
set => _hoverBackgroundColorSrgb = Color.ToSrgb(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Color of button border. Accepts RGB color, works with sRGB for DrawPrimitive internally.
|
||||||
|
/// </summary>
|
||||||
|
public Color BorderColor
|
||||||
|
{
|
||||||
|
get => Color.FromSrgb(_borderColorSrgb);
|
||||||
|
set => _borderColorSrgb = Color.ToSrgb(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Color of button border when button is hovered. Accepts RGB color, works with sRGB for DrawPrimitive internally.
|
||||||
|
/// </summary>
|
||||||
|
public Color HoverBorderColor
|
||||||
|
{
|
||||||
|
get => Color.FromSrgb(_hoverBorderColorSrgb);
|
||||||
|
set => _hoverBorderColorSrgb = Color.ToSrgb(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Color of separator lines.
|
||||||
|
/// Separator lines are used to visually separate sector of radial menu items.
|
||||||
|
/// </summary>
|
||||||
|
public Color SeparatorColor { get; set; } = new Color(128, 128, 128, 128);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
float IRadialMenuItemWithSector.AngleSectorFrom
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_angleSectorFrom = value;
|
||||||
|
_isWholeCircle = IsWholeCircle(value, _angleSectorTo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
float IRadialMenuItemWithSector.AngleSectorTo
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_angleSectorTo = value;
|
||||||
|
_isWholeCircle = IsWholeCircle(_angleSectorFrom, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
float IRadialMenuItemWithSector.OuterRadius { set => _outerRadius = value; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
float IRadialMenuItemWithSector.InnerRadius { set => _innerRadius = value; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public float AngleOffset { set => _angleOffset = value; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
Vector2 IRadialMenuItemWithSector.ParentCenter { set => _parentCenter = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A simple texture button that can move the user to a different layer within a radial menu
|
||||||
|
/// </summary>
|
||||||
|
public RadialMenuTextureButtonWithSector()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Draw(DrawingHandleScreen handle)
|
||||||
|
{
|
||||||
|
base.Draw(handle);
|
||||||
|
|
||||||
|
if (_parentCenter == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw sector where space that button occupies actually is
|
||||||
|
var containerCenter = (_parentCenter.Value - Position) * UIScale;
|
||||||
|
|
||||||
|
var angleFrom = _angleSectorFrom + _angleOffset;
|
||||||
|
var angleTo = _angleSectorTo + _angleOffset;
|
||||||
|
if (DrawBackground)
|
||||||
|
{
|
||||||
|
var segmentColor = DrawMode == DrawModeEnum.Hover
|
||||||
|
? _hoverBackgroundColorSrgb
|
||||||
|
: _backgroundColorSrgb;
|
||||||
|
|
||||||
|
DrawAnnulusSector(handle, containerCenter, _innerRadius * UIScale, _outerRadius * UIScale, angleFrom, angleTo, segmentColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DrawBorder)
|
||||||
|
{
|
||||||
|
var borderColor = DrawMode == DrawModeEnum.Hover
|
||||||
|
? _hoverBorderColorSrgb
|
||||||
|
: _borderColorSrgb;
|
||||||
|
DrawAnnulusSector(handle, containerCenter, _innerRadius * UIScale, _outerRadius * UIScale, angleFrom, angleTo, borderColor, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_isWholeCircle && DrawSeparators)
|
||||||
|
{
|
||||||
|
DrawSeparatorLines(handle, containerCenter, _innerRadius * UIScale, _outerRadius * UIScale, angleFrom, angleTo, SeparatorColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override bool HasPoint(Vector2 point)
|
||||||
|
{
|
||||||
|
if (_parentCenter == null)
|
||||||
|
{
|
||||||
|
return base.HasPoint(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
var outerRadiusSquared = _outerRadius * _outerRadius;
|
||||||
|
var innerRadiusSquared = _innerRadius * _innerRadius;
|
||||||
|
|
||||||
|
var distSquared = (point + Position - _parentCenter.Value).LengthSquared();
|
||||||
|
var isInRadius = distSquared < outerRadiusSquared && distSquared > innerRadiusSquared;
|
||||||
|
if (!isInRadius)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// difference from the center of the parent to the `point`
|
||||||
|
var pointFromParent = point + Position - _parentCenter.Value;
|
||||||
|
|
||||||
|
// Flip Y to get from ui coordinates to natural coordinates
|
||||||
|
var angle = MathF.Atan2(-pointFromParent.Y, pointFromParent.X) - _angleOffset;
|
||||||
|
if (angle < 0)
|
||||||
|
{
|
||||||
|
// atan2 range is -pi->pi, while angle sectors are
|
||||||
|
// 0->2pi, so remap the result into that range
|
||||||
|
angle = MathF.PI * 2 + angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isInAngle = angle >= _angleSectorFrom && angle < _angleSectorTo;
|
||||||
|
return isInAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draw segment between two concentrated circles from and to certain angles.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="drawingHandleScreen">Drawing handle, to which rendering should be delegated.</param>
|
||||||
|
/// <param name="center">Point where circle center should be.</param>
|
||||||
|
/// <param name="radiusInner">Radius of internal circle.</param>
|
||||||
|
/// <param name="radiusOuter">Radius of external circle.</param>
|
||||||
|
/// <param name="angleSectorFrom">Angle in radian, from which sector should start.</param>
|
||||||
|
/// <param name="angleSectorTo">Angle in radian, from which sector should start.</param>
|
||||||
|
/// <param name="color">Color for drawing.</param>
|
||||||
|
/// <param name="filled">Should figure be filled, or have only border.</param>
|
||||||
|
private void DrawAnnulusSector(
|
||||||
|
DrawingHandleScreen drawingHandleScreen,
|
||||||
|
Vector2 center,
|
||||||
|
float radiusInner,
|
||||||
|
float radiusOuter,
|
||||||
|
float angleSectorFrom,
|
||||||
|
float angleSectorTo,
|
||||||
|
Color color,
|
||||||
|
bool filled = true
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const float minimalSegmentSize = MathF.Tau / 128f;
|
||||||
|
|
||||||
|
var requestedSegmentSize = angleSectorTo - angleSectorFrom;
|
||||||
|
var segmentCount = (int)(requestedSegmentSize / minimalSegmentSize) + 1;
|
||||||
|
var anglePerSegment = requestedSegmentSize / (segmentCount - 1);
|
||||||
|
|
||||||
|
var bufferSize = segmentCount * 2;
|
||||||
|
if (_sectorPointsForDrawing == null || _sectorPointsForDrawing.Length != bufferSize)
|
||||||
|
{
|
||||||
|
_sectorPointsForDrawing ??= new Vector2[bufferSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < segmentCount; i++)
|
||||||
|
{
|
||||||
|
var angle = angleSectorFrom + anglePerSegment * i;
|
||||||
|
|
||||||
|
// Flip Y to get from ui coordinates to natural coordinates
|
||||||
|
var unitPos = new Vector2(MathF.Cos(angle), -MathF.Sin(angle));
|
||||||
|
var outerPoint = center + unitPos * radiusOuter;
|
||||||
|
var innerPoint = center + unitPos * radiusInner;
|
||||||
|
if (filled)
|
||||||
|
{
|
||||||
|
// to make filled sector we need to create strip from triangles
|
||||||
|
_sectorPointsForDrawing[i * 2] = outerPoint;
|
||||||
|
_sectorPointsForDrawing[i * 2 + 1] = innerPoint;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// to make border of sector we need points ordered as sequences on radius
|
||||||
|
_sectorPointsForDrawing[i] = outerPoint;
|
||||||
|
_sectorPointsForDrawing[bufferSize - 1 - i] = innerPoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = filled
|
||||||
|
? DrawPrimitiveTopology.TriangleStrip
|
||||||
|
: DrawPrimitiveTopology.LineStrip;
|
||||||
|
drawingHandleScreen.DrawPrimitives(type, _sectorPointsForDrawing, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawSeparatorLines(
|
||||||
|
DrawingHandleScreen drawingHandleScreen,
|
||||||
|
Vector2 center,
|
||||||
|
float radiusInner,
|
||||||
|
float radiusOuter,
|
||||||
|
float angleSectorFrom,
|
||||||
|
float angleSectorTo,
|
||||||
|
Color color
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var fromPoint = new Angle(-angleSectorFrom).RotateVec(Vector2.UnitX);
|
||||||
|
drawingHandleScreen.DrawLine(
|
||||||
|
center + fromPoint * radiusOuter,
|
||||||
|
center + fromPoint * radiusInner,
|
||||||
|
color
|
||||||
|
);
|
||||||
|
|
||||||
|
var toPoint = new Angle(-angleSectorTo).RotateVec(Vector2.UnitX);
|
||||||
|
drawingHandleScreen.DrawLine(
|
||||||
|
center + toPoint * radiusOuter,
|
||||||
|
center + toPoint * radiusInner,
|
||||||
|
color
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsWholeCircle(float angleSectorFrom, float angleSectorTo)
|
||||||
|
{
|
||||||
|
return new Angle(angleSectorFrom).EqualsApprox(new Angle(angleSectorTo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -952,7 +952,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||||||
var range = entityAction.CheckCanAccess ? action.Range : -1;
|
var range = entityAction.CheckCanAccess ? action.Range : -1;
|
||||||
|
|
||||||
_interactionOutline?.SetEnabled(false);
|
_interactionOutline?.SetEnabled(false);
|
||||||
_targetOutline?.Enable(range, entityAction.CheckCanAccess, predicate, entityAction.Whitelist, null);
|
_targetOutline?.Enable(range, entityAction.CheckCanAccess, predicate, entityAction.Whitelist, entityAction.Blacklist, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ public sealed class CharacterUIController : UIController, IOnStateEntered<Gamepl
|
|||||||
_window = UIManager.CreateWindow<CharacterWindow>();
|
_window = UIManager.CreateWindow<CharacterWindow>();
|
||||||
LayoutContainer.SetAnchorPreset(_window, LayoutContainer.LayoutPreset.CenterTop);
|
LayoutContainer.SetAnchorPreset(_window, LayoutContainer.LayoutPreset.CenterTop);
|
||||||
|
|
||||||
|
_window.OnClose += DeactivateButton;
|
||||||
|
_window.OnOpen += ActivateButton;
|
||||||
|
|
||||||
CommandBinds.Builder
|
CommandBinds.Builder
|
||||||
.Bind(ContentKeyFunctions.OpenCharacterMenu,
|
.Bind(ContentKeyFunctions.OpenCharacterMenu,
|
||||||
@@ -68,7 +69,7 @@ public sealed class CharacterUIController : UIController, IOnStateEntered<Gamepl
|
|||||||
{
|
{
|
||||||
if (_window != null)
|
if (_window != null)
|
||||||
{
|
{
|
||||||
_window.Dispose();
|
_window.Close();
|
||||||
_window = null;
|
_window = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,18 +106,27 @@ public sealed class CharacterUIController : UIController, IOnStateEntered<Gamepl
|
|||||||
}
|
}
|
||||||
|
|
||||||
CharacterButton.OnPressed += CharacterButtonPressed;
|
CharacterButton.OnPressed += CharacterButtonPressed;
|
||||||
|
}
|
||||||
|
|
||||||
if (_window == null)
|
private void DeactivateButton()
|
||||||
|
{
|
||||||
|
if (CharacterButton == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_window.OnClose += DeactivateButton;
|
CharacterButton.Pressed = false;
|
||||||
_window.OnOpen += ActivateButton;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeactivateButton() => CharacterButton!.Pressed = false;
|
private void ActivateButton()
|
||||||
private void ActivateButton() => CharacterButton!.Pressed = true;
|
{
|
||||||
|
if (CharacterButton == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterButton.Pressed = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void CharacterUpdated(CharacterData data)
|
private void CharacterUpdated(CharacterData data)
|
||||||
{
|
{
|
||||||
@@ -245,10 +255,7 @@ public sealed class CharacterUIController : UIController, IOnStateEntered<Gamepl
|
|||||||
if (_window == null)
|
if (_window == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (CharacterButton != null)
|
CharacterButton?.SetClickPressed(!_window.IsOpen);
|
||||||
{
|
|
||||||
CharacterButton.SetClickPressed(!_window.IsOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_window.IsOpen)
|
if (_window.IsOpen)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Content.Shared.FixedPoint;
|
|||||||
using Content.Shared.Mobs;
|
using Content.Shared.Mobs;
|
||||||
using Content.Shared.Mobs.Components;
|
using Content.Shared.Mobs.Components;
|
||||||
using Content.Shared.Mobs.Systems;
|
using Content.Shared.Mobs.Systems;
|
||||||
|
using Content.Shared.Traits.Assorted;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
@@ -94,7 +95,11 @@ public sealed class DamageOverlayUiController : UIController
|
|||||||
{
|
{
|
||||||
case MobState.Alive:
|
case MobState.Alive:
|
||||||
{
|
{
|
||||||
if (damageable.DamagePerGroup.TryGetValue("Brute", out var bruteDamage))
|
if (EntityManager.HasComponent<PainNumbnessComponent>(entity))
|
||||||
|
{
|
||||||
|
_overlay.BruteLevel = 0;
|
||||||
|
}
|
||||||
|
else if (damageable.DamagePerGroup.TryGetValue("Brute", out var bruteDamage))
|
||||||
{
|
{
|
||||||
_overlay.BruteLevel = FixedPoint2.Min(1f, bruteDamage / critThreshold).Float();
|
_overlay.BruteLevel = FixedPoint2.Min(1f, bruteDamage / critThreshold).Float();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,13 +31,12 @@ public sealed class HotbarUIController : UIController
|
|||||||
ReloadHotbar();
|
ReloadHotbar();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Setup(HandsContainer handsContainer, StorageContainer storageContainer)
|
public void Setup(HandsContainer handsContainer)
|
||||||
{
|
{
|
||||||
_inventory = UIManager.GetUIController<InventoryUIController>();
|
_inventory = UIManager.GetUIController<InventoryUIController>();
|
||||||
_hands = UIManager.GetUIController<HandsUIController>();
|
_hands = UIManager.GetUIController<HandsUIController>();
|
||||||
_storage = UIManager.GetUIController<StorageUIController>();
|
_storage = UIManager.GetUIController<StorageUIController>();
|
||||||
_hands.RegisterHandContainer(handsContainer);
|
_hands.RegisterHandContainer(handsContainer);
|
||||||
_storage.RegisterStorageContainer(storageContainer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReloadHotbar()
|
public void ReloadHotbar()
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<widgets:HotbarGui
|
<widgets:HotbarGui
|
||||||
xmlns="https://spacestation14.io"
|
xmlns="https://spacestation14.io"
|
||||||
xmlns:inventory="clr-namespace:Content.Client.UserInterface.Systems.Inventory.Controls"
|
xmlns:inventory="clr-namespace:Content.Client.UserInterface.Systems.Inventory.Controls"
|
||||||
xmlns:storage="clr-namespace:Content.Client.UserInterface.Systems.Storage.Controls"
|
|
||||||
xmlns:hands="clr-namespace:Content.Client.UserInterface.Systems.Hands.Controls"
|
xmlns:hands="clr-namespace:Content.Client.UserInterface.Systems.Hands.Controls"
|
||||||
xmlns:widgets="clr-namespace:Content.Client.UserInterface.Systems.Hotbar.Widgets"
|
xmlns:widgets="clr-namespace:Content.Client.UserInterface.Systems.Hotbar.Widgets"
|
||||||
Name="HotbarInterface"
|
Name="HotbarInterface"
|
||||||
@@ -13,10 +12,8 @@
|
|||||||
<BoxContainer Name="StorageContainer"
|
<BoxContainer Name="StorageContainer"
|
||||||
Access="Public"
|
Access="Public"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
|
HorizontalExpand="True"
|
||||||
Margin="10">
|
Margin="10">
|
||||||
<storage:StorageContainer
|
|
||||||
Name="StoragePanel"
|
|
||||||
Visible="False"/>
|
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal" Name="Hotbar" HorizontalAlignment="Center">
|
<BoxContainer Orientation="Horizontal" Name="Hotbar" HorizontalAlignment="Center">
|
||||||
<inventory:ItemSlotButtonContainer
|
<inventory:ItemSlotButtonContainer
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public sealed partial class HotbarGui : UIWidget
|
|||||||
StatusPanelLeft.SetSide(HandUILocation.Left);
|
StatusPanelLeft.SetSide(HandUILocation.Left);
|
||||||
var hotbarController = UserInterfaceManager.GetUIController<HotbarUIController>();
|
var hotbarController = UserInterfaceManager.GetUIController<HotbarUIController>();
|
||||||
|
|
||||||
hotbarController.Setup(HandContainer, StoragePanel);
|
hotbarController.Setup(HandContainer);
|
||||||
LayoutContainer.SetGrowVertical(this, LayoutContainer.GrowDirection.Begin);
|
LayoutContainer.SetGrowVertical(this, LayoutContainer.GrowDirection.Begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public sealed class ItemGridPiece : Control, IEntityControl
|
|||||||
Location = location;
|
Location = location;
|
||||||
|
|
||||||
Visible = true;
|
Visible = true;
|
||||||
MouseFilter = MouseFilterMode.Pass;
|
MouseFilter = MouseFilterMode.Stop;
|
||||||
|
|
||||||
TooltipSupplier = SupplyTooltip;
|
TooltipSupplier = SupplyTooltip;
|
||||||
|
|
||||||
@@ -105,8 +105,11 @@ public sealed class ItemGridPiece : Control, IEntityControl
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_storageController.IsDragging && _storageController.DraggingGhost?.Entity == Entity && _storageController.DraggingGhost != this)
|
if (_storageController.IsDragging && _storageController.DraggingGhost?.Entity == Entity &&
|
||||||
|
_storageController.DraggingGhost != this)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var adjustedShape = _entityManager.System<ItemSystem>().GetAdjustedItemShape((Entity, itemComponent), Location.Rotation, Vector2i.Zero);
|
var adjustedShape = _entityManager.System<ItemSystem>().GetAdjustedItemShape((Entity, itemComponent), Location.Rotation, Vector2i.Zero);
|
||||||
var boundingGrid = adjustedShape.GetBoundingBox();
|
var boundingGrid = adjustedShape.GetBoundingBox();
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ using System.Linq;
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Content.Client.Hands.Systems;
|
using Content.Client.Hands.Systems;
|
||||||
using Content.Client.Items.Systems;
|
using Content.Client.Items.Systems;
|
||||||
|
using Content.Client.Storage;
|
||||||
using Content.Client.Storage.Systems;
|
using Content.Client.Storage.Systems;
|
||||||
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
using Content.Shared.Item;
|
using Content.Shared.Item;
|
||||||
using Content.Shared.Storage;
|
using Content.Shared.Storage;
|
||||||
@@ -11,12 +13,14 @@ using Robust.Client.Graphics;
|
|||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Shared.Collections;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Client.UserInterface.Systems.Storage.Controls;
|
namespace Content.Client.UserInterface.Systems.Storage.Controls;
|
||||||
|
|
||||||
public sealed class StorageContainer : BaseWindow
|
public sealed class StorageWindow : BaseWindow
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IEntityManager _entity = default!;
|
[Dependency] private readonly IEntityManager _entity = default!;
|
||||||
private readonly StorageUIController _storageController;
|
private readonly StorageUIController _storageController;
|
||||||
@@ -27,6 +31,20 @@ public sealed class StorageContainer : BaseWindow
|
|||||||
private readonly GridContainer _backgroundGrid;
|
private readonly GridContainer _backgroundGrid;
|
||||||
private readonly GridContainer _sidebar;
|
private readonly GridContainer _sidebar;
|
||||||
|
|
||||||
|
private Control _titleContainer;
|
||||||
|
private Label _titleLabel;
|
||||||
|
|
||||||
|
// Needs to be nullable in case a piece is in default spot.
|
||||||
|
private readonly Dictionary<EntityUid, (ItemStorageLocation? Loc, ItemGridPiece Control)> _pieces = new();
|
||||||
|
private readonly List<Control> _controlGrid = new();
|
||||||
|
|
||||||
|
private ValueList<EntityUid> _contained = new();
|
||||||
|
private ValueList<EntityUid> _toRemove = new();
|
||||||
|
|
||||||
|
private TextureButton? _backButton;
|
||||||
|
|
||||||
|
private bool _isDirty;
|
||||||
|
|
||||||
public event Action<GUIBoundKeyEventArgs, ItemGridPiece>? OnPiecePressed;
|
public event Action<GUIBoundKeyEventArgs, ItemGridPiece>? OnPiecePressed;
|
||||||
public event Action<GUIBoundKeyEventArgs, ItemGridPiece>? OnPieceUnpressed;
|
public event Action<GUIBoundKeyEventArgs, ItemGridPiece>? OnPieceUnpressed;
|
||||||
|
|
||||||
@@ -51,9 +69,10 @@ public sealed class StorageContainer : BaseWindow
|
|||||||
private readonly string _sidebarFatTexturePath = "Storage/sidebar_fat";
|
private readonly string _sidebarFatTexturePath = "Storage/sidebar_fat";
|
||||||
private Texture? _sidebarFatTexture;
|
private Texture? _sidebarFatTexture;
|
||||||
|
|
||||||
public StorageContainer()
|
public StorageWindow()
|
||||||
{
|
{
|
||||||
IoCManager.InjectDependencies(this);
|
IoCManager.InjectDependencies(this);
|
||||||
|
Resizable = false;
|
||||||
|
|
||||||
_storageController = UserInterfaceManager.GetUIController<StorageUIController>();
|
_storageController = UserInterfaceManager.GetUIController<StorageUIController>();
|
||||||
|
|
||||||
@@ -63,6 +82,7 @@ public sealed class StorageContainer : BaseWindow
|
|||||||
|
|
||||||
_sidebar = new GridContainer
|
_sidebar = new GridContainer
|
||||||
{
|
{
|
||||||
|
Name = "SideBar",
|
||||||
HSeparationOverride = 0,
|
HSeparationOverride = 0,
|
||||||
VSeparationOverride = 0,
|
VSeparationOverride = 0,
|
||||||
Columns = 1
|
Columns = 1
|
||||||
@@ -70,21 +90,48 @@ public sealed class StorageContainer : BaseWindow
|
|||||||
|
|
||||||
_pieceGrid = new GridContainer
|
_pieceGrid = new GridContainer
|
||||||
{
|
{
|
||||||
|
Name = "PieceGrid",
|
||||||
HSeparationOverride = 0,
|
HSeparationOverride = 0,
|
||||||
VSeparationOverride = 0
|
VSeparationOverride = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
_backgroundGrid = new GridContainer
|
_backgroundGrid = new GridContainer
|
||||||
{
|
{
|
||||||
|
Name = "BackgroundGrid",
|
||||||
HSeparationOverride = 0,
|
HSeparationOverride = 0,
|
||||||
VSeparationOverride = 0
|
VSeparationOverride = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_titleLabel = new Label()
|
||||||
|
{
|
||||||
|
HorizontalExpand = true,
|
||||||
|
Name = "StorageLabel",
|
||||||
|
ClipText = true,
|
||||||
|
Text = "Dummy",
|
||||||
|
StyleClasses =
|
||||||
|
{
|
||||||
|
"FancyWindowTitle",
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_titleContainer = new PanelContainer()
|
||||||
|
{
|
||||||
|
StyleClasses =
|
||||||
|
{
|
||||||
|
"WindowHeadingBackground"
|
||||||
|
},
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
_titleLabel
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var container = new BoxContainer
|
var container = new BoxContainer
|
||||||
{
|
{
|
||||||
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||||
Children =
|
Children =
|
||||||
{
|
{
|
||||||
|
_titleContainer,
|
||||||
new BoxContainer
|
new BoxContainer
|
||||||
{
|
{
|
||||||
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||||
@@ -130,12 +177,22 @@ public sealed class StorageContainer : BaseWindow
|
|||||||
if (entity == null)
|
if (entity == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (UserInterfaceManager.GetUIController<StorageUIController>().WindowTitle)
|
||||||
|
{
|
||||||
|
_titleLabel.Text = Identity.Name(entity.Value, _entity);
|
||||||
|
_titleContainer.Visible = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_titleContainer.Visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
BuildGridRepresentation();
|
BuildGridRepresentation();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BuildGridRepresentation()
|
private void BuildGridRepresentation()
|
||||||
{
|
{
|
||||||
if (!_entity.TryGetComponent<StorageComponent>(StorageEntity, out var comp) || !comp.Grid.Any())
|
if (!_entity.TryGetComponent<StorageComponent>(StorageEntity, out var comp) || comp.Grid.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var boundingGrid = comp.Grid.GetBoundingBox();
|
var boundingGrid = comp.Grid.GetBoundingBox();
|
||||||
@@ -144,12 +201,13 @@ public sealed class StorageContainer : BaseWindow
|
|||||||
|
|
||||||
#region Sidebar
|
#region Sidebar
|
||||||
_sidebar.Children.Clear();
|
_sidebar.Children.Clear();
|
||||||
_sidebar.Rows = boundingGrid.Height + 1;
|
var rows = boundingGrid.Height + 1;
|
||||||
|
_sidebar.Rows = rows;
|
||||||
|
|
||||||
var exitButton = new TextureButton
|
var exitButton = new TextureButton
|
||||||
{
|
{
|
||||||
TextureNormal = _entity.System<StorageSystem>().OpenStorageAmount == 1
|
Name = "ExitButton",
|
||||||
?_exitTexture
|
TextureNormal = _exitTexture,
|
||||||
: _backTexture,
|
|
||||||
Scale = new Vector2(2, 2),
|
Scale = new Vector2(2, 2),
|
||||||
};
|
};
|
||||||
exitButton.OnPressed += _ =>
|
exitButton.OnPressed += _ =>
|
||||||
@@ -165,8 +223,10 @@ public sealed class StorageContainer : BaseWindow
|
|||||||
args.Handle();
|
args.Handle();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var exitContainer = new BoxContainer
|
var exitContainer = new BoxContainer
|
||||||
{
|
{
|
||||||
|
Name = "ExitContainer",
|
||||||
Children =
|
Children =
|
||||||
{
|
{
|
||||||
new TextureRect
|
new TextureRect
|
||||||
@@ -182,28 +242,70 @@ public sealed class StorageContainer : BaseWindow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_sidebar.AddChild(exitContainer);
|
_sidebar.AddChild(exitContainer);
|
||||||
for (var i = 0; i < boundingGrid.Height - 1; i++)
|
var offset = 2;
|
||||||
|
|
||||||
|
if (_entity.System<StorageSystem>().NestedStorage && rows > 0)
|
||||||
{
|
{
|
||||||
_sidebar.AddChild(new TextureRect
|
_backButton = new TextureButton
|
||||||
{
|
{
|
||||||
Texture = _sidebarMiddleTexture,
|
TextureNormal = _backTexture,
|
||||||
|
Scale = new Vector2(2, 2),
|
||||||
|
};
|
||||||
|
_backButton.OnPressed += _ =>
|
||||||
|
{
|
||||||
|
var containerSystem = _entity.System<SharedContainerSystem>();
|
||||||
|
|
||||||
|
if (containerSystem.TryGetContainingContainer(StorageEntity.Value, out var container) &&
|
||||||
|
_entity.TryGetComponent(container.Owner, out StorageComponent? storage))
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
|
||||||
|
if (_entity.System<SharedUserInterfaceSystem>()
|
||||||
|
.TryGetOpenUi<StorageBoundUserInterface>(container.Owner,
|
||||||
|
StorageComponent.StorageUiKey.Key,
|
||||||
|
out var parentBui))
|
||||||
|
{
|
||||||
|
parentBui.Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var backContainer = new BoxContainer
|
||||||
|
{
|
||||||
|
Name = "ExitContainer",
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
new TextureRect
|
||||||
|
{
|
||||||
|
Texture = rows > 2 ? _sidebarMiddleTexture : _sidebarBottomTexture,
|
||||||
TextureScale = new Vector2(2, 2),
|
TextureScale = new Vector2(2, 2),
|
||||||
});
|
Children =
|
||||||
|
{
|
||||||
|
_backButton,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_sidebar.AddChild(backContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (boundingGrid.Height > 0)
|
var fillerRows = rows - offset;
|
||||||
|
|
||||||
|
for (var i = 0; i < fillerRows; i++)
|
||||||
{
|
{
|
||||||
_sidebar.AddChild(new TextureRect
|
_sidebar.AddChild(new TextureRect
|
||||||
{
|
{
|
||||||
Texture = _sidebarBottomTexture,
|
Texture = i != (fillerRows - 1) ? _sidebarMiddleTexture : _sidebarBottomTexture,
|
||||||
TextureScale = new Vector2(2, 2),
|
TextureScale = new Vector2(2, 2),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
BuildItemPieces();
|
FlagDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BuildBackground()
|
public void BuildBackground()
|
||||||
@@ -240,23 +342,54 @@ public sealed class StorageContainer : BaseWindow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Reclaim(ItemStorageLocation location, ItemGridPiece draggingGhost)
|
||||||
|
{
|
||||||
|
draggingGhost.OnPiecePressed += OnPiecePressed;
|
||||||
|
draggingGhost.OnPieceUnpressed += OnPieceUnpressed;
|
||||||
|
_pieces[draggingGhost.Entity] = (location, draggingGhost);
|
||||||
|
draggingGhost.Location = location;
|
||||||
|
var controlIndex = GetGridIndex(draggingGhost);
|
||||||
|
_controlGrid[controlIndex].AddChild(draggingGhost);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetGridIndex(ItemGridPiece piece)
|
||||||
|
{
|
||||||
|
return piece.Location.Position.X + piece.Location.Position.Y * _pieceGrid.Columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FlagDirty()
|
||||||
|
{
|
||||||
|
_isDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveGrid(ItemGridPiece control)
|
||||||
|
{
|
||||||
|
control.Orphan();
|
||||||
|
_pieces.Remove(control.Entity);
|
||||||
|
control.OnPiecePressed -= OnPiecePressed;
|
||||||
|
control.OnPieceUnpressed -= OnPieceUnpressed;
|
||||||
|
}
|
||||||
|
|
||||||
public void BuildItemPieces()
|
public void BuildItemPieces()
|
||||||
{
|
{
|
||||||
if (!_entity.TryGetComponent<StorageComponent>(StorageEntity, out var storageComp))
|
if (!_entity.TryGetComponent<StorageComponent>(StorageEntity, out var storageComp))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!storageComp.Grid.Any())
|
if (storageComp.Grid.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var boundingGrid = storageComp.Grid.GetBoundingBox();
|
var boundingGrid = storageComp.Grid.GetBoundingBox();
|
||||||
var size = _emptyTexture!.Size * 2;
|
var size = _emptyTexture!.Size * 2;
|
||||||
var containedEntities = storageComp.Container.ContainedEntities.Reverse().ToArray();
|
_contained.Clear();
|
||||||
|
_contained.AddRange(storageComp.Container.ContainedEntities.Reverse());
|
||||||
|
|
||||||
//todo. at some point, we may want to only rebuild the pieces that have actually received new data.
|
// Build the grid representation
|
||||||
|
if (_pieceGrid.Rows - 1 != boundingGrid.Height || _pieceGrid.Columns - 1 != boundingGrid.Width)
|
||||||
_pieceGrid.RemoveAllChildren();
|
{
|
||||||
_pieceGrid.Rows = boundingGrid.Height + 1;
|
_pieceGrid.Rows = boundingGrid.Height + 1;
|
||||||
_pieceGrid.Columns = boundingGrid.Width + 1;
|
_pieceGrid.Columns = boundingGrid.Width + 1;
|
||||||
|
_controlGrid.Clear();
|
||||||
|
|
||||||
for (var y = boundingGrid.Bottom; y <= boundingGrid.Top; y++)
|
for (var y = boundingGrid.Bottom; y <= boundingGrid.Top; y++)
|
||||||
{
|
{
|
||||||
for (var x = boundingGrid.Left; x <= boundingGrid.Right; x++)
|
for (var x = boundingGrid.Left; x <= boundingGrid.Right; x++)
|
||||||
@@ -266,29 +399,58 @@ public sealed class StorageContainer : BaseWindow
|
|||||||
MinSize = size
|
MinSize = size
|
||||||
};
|
};
|
||||||
|
|
||||||
var currentPosition = new Vector2i(x, y);
|
_controlGrid.Add(control);
|
||||||
|
_pieceGrid.AddChild(control);
|
||||||
foreach (var (itemEnt, itemPos) in storageComp.StoredItems)
|
|
||||||
{
|
|
||||||
if (itemPos.Position != currentPosition)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (_entity.TryGetComponent<ItemComponent>(itemEnt, out var itemEntComponent))
|
|
||||||
{
|
|
||||||
ItemGridPiece gridPiece;
|
|
||||||
|
|
||||||
if (_storageController.CurrentlyDragging?.Entity is { } dragging
|
|
||||||
&& dragging == itemEnt)
|
|
||||||
{
|
|
||||||
_storageController.CurrentlyDragging.Orphan();
|
|
||||||
gridPiece = _storageController.CurrentlyDragging;
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_toRemove.Clear();
|
||||||
|
|
||||||
|
// Remove entities no longer relevant / Update existing ones
|
||||||
|
foreach (var (ent, data) in _pieces)
|
||||||
{
|
{
|
||||||
gridPiece = new ItemGridPiece((itemEnt, itemEntComponent), itemPos, _entity)
|
if (storageComp.StoredItems.TryGetValue(ent, out var updated))
|
||||||
|
{
|
||||||
|
if (data.Loc.Equals(updated))
|
||||||
|
{
|
||||||
|
DebugTools.Assert(data.Control.Location == updated);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update
|
||||||
|
data.Control.Location = updated;
|
||||||
|
var index = GetGridIndex(data.Control);
|
||||||
|
data.Control.Orphan();
|
||||||
|
_controlGrid[index].AddChild(data.Control);
|
||||||
|
_pieces[ent] = (updated, data.Control);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_toRemove.Add(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var ent in _toRemove)
|
||||||
|
{
|
||||||
|
_pieces.Remove(ent, out var data);
|
||||||
|
data.Control.Orphan();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new ones
|
||||||
|
foreach (var (ent, loc) in storageComp.StoredItems)
|
||||||
|
{
|
||||||
|
if (_pieces.TryGetValue(ent, out var existing))
|
||||||
|
{
|
||||||
|
DebugTools.Assert(existing.Loc == loc);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_entity.TryGetComponent<ItemComponent>(ent, out var itemEntComponent))
|
||||||
|
{
|
||||||
|
var gridPiece = new ItemGridPiece((ent, itemEntComponent), loc, _entity)
|
||||||
{
|
{
|
||||||
MinSize = size,
|
MinSize = size,
|
||||||
Marked = Array.IndexOf(containedEntities, itemEnt) switch
|
Marked = _contained.IndexOf(ent) switch
|
||||||
{
|
{
|
||||||
0 => ItemGridPieceMarks.First,
|
0 => ItemGridPieceMarks.First,
|
||||||
1 => ItemGridPieceMarks.Second,
|
1 => ItemGridPieceMarks.Second,
|
||||||
@@ -297,13 +459,10 @@ public sealed class StorageContainer : BaseWindow
|
|||||||
};
|
};
|
||||||
gridPiece.OnPiecePressed += OnPiecePressed;
|
gridPiece.OnPiecePressed += OnPiecePressed;
|
||||||
gridPiece.OnPieceUnpressed += OnPieceUnpressed;
|
gridPiece.OnPieceUnpressed += OnPieceUnpressed;
|
||||||
}
|
var controlIndex = loc.Position.X + loc.Position.Y * (boundingGrid.Width + 1);
|
||||||
|
|
||||||
control.AddChild(gridPiece);
|
_controlGrid[controlIndex].AddChild(gridPiece);
|
||||||
}
|
_pieces[ent] = (loc, gridPiece);
|
||||||
}
|
|
||||||
|
|
||||||
_pieceGrid.AddChild(control);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -315,6 +474,35 @@ public sealed class StorageContainer : BaseWindow
|
|||||||
if (!IsOpen)
|
if (!IsOpen)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (_isDirty)
|
||||||
|
{
|
||||||
|
_isDirty = false;
|
||||||
|
BuildItemPieces();
|
||||||
|
}
|
||||||
|
|
||||||
|
var containerSystem = _entity.System<SharedContainerSystem>();
|
||||||
|
|
||||||
|
if (_backButton != null)
|
||||||
|
{
|
||||||
|
if (StorageEntity != null && _entity.System<StorageSystem>().NestedStorage)
|
||||||
|
{
|
||||||
|
if (containerSystem.TryGetContainingContainer(StorageEntity.Value, out var container) &&
|
||||||
|
_entity.HasComponent<StorageComponent>(container.Owner))
|
||||||
|
{
|
||||||
|
_backButton.Visible = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_backButton.Visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Hide the button.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_backButton.Visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var itemSystem = _entity.System<ItemSystem>();
|
var itemSystem = _entity.System<ItemSystem>();
|
||||||
var storageSystem = _entity.System<StorageSystem>();
|
var storageSystem = _entity.System<StorageSystem>();
|
||||||
var handsSystem = _entity.System<HandsSystem>();
|
var handsSystem = _entity.System<HandsSystem>();
|
||||||
@@ -324,7 +512,7 @@ public sealed class StorageContainer : BaseWindow
|
|||||||
child.ModulateSelfOverride = Color.FromHex("#222222");
|
child.ModulateSelfOverride = Color.FromHex("#222222");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UserInterfaceManager.CurrentlyHovered is StorageContainer con && con != this)
|
if (UserInterfaceManager.CurrentlyHovered is StorageWindow con && con != this)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!_entity.TryGetComponent<StorageComponent>(StorageEntity, out var storageComponent))
|
if (!_entity.TryGetComponent<StorageComponent>(StorageEntity, out var storageComponent))
|
||||||
@@ -373,7 +561,7 @@ public sealed class StorageContainer : BaseWindow
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
float spot = 0;
|
float spot = 0;
|
||||||
var marked = new List<Control>();
|
var marked = new ValueList<Control>();
|
||||||
|
|
||||||
foreach (var location in locations.Value)
|
foreach (var location in locations.Value)
|
||||||
{
|
{
|
||||||
@@ -500,14 +688,4 @@ public sealed class StorageContainer : BaseWindow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Close()
|
|
||||||
{
|
|
||||||
base.Close();
|
|
||||||
|
|
||||||
if (StorageEntity == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_entity.System<StorageSystem>().CloseStorageWindow(StorageEntity.Value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ using System.Numerics;
|
|||||||
using Content.Client.Examine;
|
using Content.Client.Examine;
|
||||||
using Content.Client.Hands.Systems;
|
using Content.Client.Hands.Systems;
|
||||||
using Content.Client.Interaction;
|
using Content.Client.Interaction;
|
||||||
|
using Content.Client.Storage;
|
||||||
using Content.Client.Storage.Systems;
|
using Content.Client.Storage.Systems;
|
||||||
using Content.Client.UserInterface.Systems.Hotbar.Widgets;
|
using Content.Client.UserInterface.Systems.Hotbar.Widgets;
|
||||||
using Content.Client.UserInterface.Systems.Storage.Controls;
|
using Content.Client.UserInterface.Systems.Storage.Controls;
|
||||||
@@ -9,9 +10,9 @@ using Content.Client.Verbs.UI;
|
|||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Item;
|
|
||||||
using Content.Shared.Storage;
|
using Content.Shared.Storage;
|
||||||
using Robust.Client.Input;
|
using Robust.Client.Input;
|
||||||
|
using Robust.Client.Player;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controllers;
|
using Robust.Client.UserInterface.Controllers;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
@@ -23,19 +24,23 @@ namespace Content.Client.UserInterface.Systems.Storage;
|
|||||||
|
|
||||||
public sealed class StorageUIController : UIController, IOnSystemChanged<StorageSystem>
|
public sealed class StorageUIController : UIController, IOnSystemChanged<StorageSystem>
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Things are a bit over the shop but essentially
|
||||||
|
* - Clicking into storagewindow is handled via storagewindow
|
||||||
|
* - Clicking out of it is via ItemGridPiece
|
||||||
|
* - Dragging around is handled here
|
||||||
|
* - Drawing is handled via ItemGridPiece
|
||||||
|
* - StorageSystem handles any sim stuff around open windows.
|
||||||
|
*/
|
||||||
|
|
||||||
[Dependency] private readonly IConfigurationManager _configuration = default!;
|
[Dependency] private readonly IConfigurationManager _configuration = default!;
|
||||||
[Dependency] private readonly IEntityManager _entity = default!;
|
|
||||||
[Dependency] private readonly IInputManager _input = default!;
|
[Dependency] private readonly IInputManager _input = default!;
|
||||||
[Dependency] private readonly IUserInterfaceManager _ui = default!;
|
[Dependency] private readonly IPlayerManager _player = default!;
|
||||||
|
[UISystemDependency] private readonly StorageSystem _storage = default!;
|
||||||
|
|
||||||
private readonly DragDropHelper<ItemGridPiece> _menuDragHelper;
|
private readonly DragDropHelper<ItemGridPiece> _menuDragHelper;
|
||||||
private StorageContainer? _container;
|
|
||||||
|
|
||||||
private Vector2? _lastContainerPosition;
|
public ItemGridPiece? DraggingGhost => _menuDragHelper.Dragged;
|
||||||
|
|
||||||
private HotbarGui? Hotbar => UIManager.GetActiveUIWidgetOrNull<HotbarGui>();
|
|
||||||
|
|
||||||
public ItemGridPiece? DraggingGhost;
|
|
||||||
public Angle DraggingRotation = Angle.Zero;
|
public Angle DraggingRotation = Angle.Zero;
|
||||||
public bool StaticStorageUIEnabled;
|
public bool StaticStorageUIEnabled;
|
||||||
public bool OpaqueStorageWindow;
|
public bool OpaqueStorageWindow;
|
||||||
@@ -43,6 +48,8 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
|
|||||||
public bool IsDragging => _menuDragHelper.IsDragging;
|
public bool IsDragging => _menuDragHelper.IsDragging;
|
||||||
public ItemGridPiece? CurrentlyDragging => _menuDragHelper.Dragged;
|
public ItemGridPiece? CurrentlyDragging => _menuDragHelper.Dragged;
|
||||||
|
|
||||||
|
public bool WindowTitle { get; private set; } = false;
|
||||||
|
|
||||||
public StorageUIController()
|
public StorageUIController()
|
||||||
{
|
{
|
||||||
_menuDragHelper = new DragDropHelper<ItemGridPiece>(OnMenuBeginDrag, OnMenuContinueDrag, OnMenuEndDrag);
|
_menuDragHelper = new DragDropHelper<ItemGridPiece>(OnMenuBeginDrag, OnMenuContinueDrag, OnMenuEndDrag);
|
||||||
@@ -52,106 +59,88 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
|
|||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
|
UIManager.OnScreenChanged += OnScreenChange;
|
||||||
|
|
||||||
_configuration.OnValueChanged(CCVars.StaticStorageUI, OnStaticStorageChanged, true);
|
_configuration.OnValueChanged(CCVars.StaticStorageUI, OnStaticStorageChanged, true);
|
||||||
_configuration.OnValueChanged(CCVars.OpaqueStorageWindow, OnOpaqueWindowChanged, true);
|
_configuration.OnValueChanged(CCVars.OpaqueStorageWindow, OnOpaqueWindowChanged, true);
|
||||||
|
_configuration.OnValueChanged(CCVars.StorageWindowTitle, OnStorageWindowTitle, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnScreenChange((UIScreen? Old, UIScreen? New) obj)
|
||||||
|
{
|
||||||
|
// Handle reconnects with hotbargui.
|
||||||
|
|
||||||
|
// Essentially HotbarGui / the screen gets loaded AFTER gamestates at the moment (because clientgameticker manually changes it via event)
|
||||||
|
// and changing this may be a massive change.
|
||||||
|
// So instead we'll just manually reload it for now.
|
||||||
|
if (!StaticStorageUIEnabled ||
|
||||||
|
obj.New == null ||
|
||||||
|
!EntityManager.TryGetComponent(_player.LocalEntity, out UserInterfaceUserComponent? userComp))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// UISystemDependency not injected at this point so do it the old fashion way, I love ordering issues.
|
||||||
|
var uiSystem = EntityManager.System<SharedUserInterfaceSystem>();
|
||||||
|
|
||||||
|
foreach (var bui in uiSystem.GetActorUis((_player.LocalEntity.Value, userComp)))
|
||||||
|
{
|
||||||
|
if (!uiSystem.TryGetOpenUi<StorageBoundUserInterface>(bui.Entity, StorageComponent.StorageUiKey.Key, out var storageBui))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
storageBui.ReOpen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStorageWindowTitle(bool obj)
|
||||||
|
{
|
||||||
|
WindowTitle = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnOpaqueWindowChanged(bool obj)
|
||||||
|
{
|
||||||
|
OpaqueStorageWindow = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStaticStorageChanged(bool obj)
|
||||||
|
{
|
||||||
|
StaticStorageUIEnabled = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StorageWindow CreateStorageWindow(EntityUid uid)
|
||||||
|
{
|
||||||
|
var window = new StorageWindow();
|
||||||
|
window.MouseFilter = Control.MouseFilterMode.Pass;
|
||||||
|
|
||||||
|
window.OnPiecePressed += (args, piece) =>
|
||||||
|
{
|
||||||
|
OnPiecePressed(args, window, piece);
|
||||||
|
};
|
||||||
|
window.OnPieceUnpressed += (args, piece) =>
|
||||||
|
{
|
||||||
|
OnPieceUnpressed(args, window, piece);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (StaticStorageUIEnabled)
|
||||||
|
{
|
||||||
|
UIManager.GetActiveUIWidgetOrNull<HotbarGui>()?.StorageContainer.AddChild(window);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
window.OpenCenteredLeft();
|
||||||
|
}
|
||||||
|
|
||||||
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnSystemLoaded(StorageSystem system)
|
public void OnSystemLoaded(StorageSystem system)
|
||||||
{
|
{
|
||||||
_input.FirstChanceOnKeyEvent += OnMiddleMouse;
|
_input.FirstChanceOnKeyEvent += OnMiddleMouse;
|
||||||
system.StorageUpdated += OnStorageUpdated;
|
|
||||||
system.StorageOrderChanged += OnStorageOrderChanged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnSystemUnloaded(StorageSystem system)
|
public void OnSystemUnloaded(StorageSystem system)
|
||||||
{
|
{
|
||||||
_input.FirstChanceOnKeyEvent -= OnMiddleMouse;
|
_input.FirstChanceOnKeyEvent -= OnMiddleMouse;
|
||||||
system.StorageUpdated -= OnStorageUpdated;
|
|
||||||
system.StorageOrderChanged -= OnStorageOrderChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStorageOrderChanged(Entity<StorageComponent>? nullEnt)
|
|
||||||
{
|
|
||||||
if (_container == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (IsDragging)
|
|
||||||
_menuDragHelper.EndDrag();
|
|
||||||
|
|
||||||
_container.UpdateContainer(nullEnt);
|
|
||||||
|
|
||||||
if (nullEnt is not null)
|
|
||||||
{
|
|
||||||
// center it if we knock it off screen somehow.
|
|
||||||
if (!StaticStorageUIEnabled &&
|
|
||||||
(_lastContainerPosition == null ||
|
|
||||||
_lastContainerPosition.Value.X < 0 ||
|
|
||||||
_lastContainerPosition.Value.Y < 0 ||
|
|
||||||
_lastContainerPosition.Value.X > _ui.WindowRoot.Width ||
|
|
||||||
_lastContainerPosition.Value.Y > _ui.WindowRoot.Height))
|
|
||||||
{
|
|
||||||
_container.OpenCenteredAt(new Vector2(0.5f, 0.75f));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_container.Open();
|
|
||||||
|
|
||||||
var pos = !StaticStorageUIEnabled && _lastContainerPosition != null
|
|
||||||
? _lastContainerPosition.Value
|
|
||||||
: Vector2.Zero;
|
|
||||||
|
|
||||||
LayoutContainer.SetPosition(_container, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StaticStorageUIEnabled)
|
|
||||||
{
|
|
||||||
// we have to orphan it here because Open() sets the parent.
|
|
||||||
_container.Orphan();
|
|
||||||
Hotbar?.StorageContainer.AddChild(_container);
|
|
||||||
}
|
|
||||||
_lastContainerPosition = _container.GlobalPosition;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_lastContainerPosition = _container.GlobalPosition;
|
|
||||||
_container.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStaticStorageChanged(bool obj)
|
|
||||||
{
|
|
||||||
if (StaticStorageUIEnabled == obj)
|
|
||||||
return;
|
|
||||||
|
|
||||||
StaticStorageUIEnabled = obj;
|
|
||||||
_lastContainerPosition = null;
|
|
||||||
|
|
||||||
if (_container == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!_container.IsOpen)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_container.Orphan();
|
|
||||||
if (StaticStorageUIEnabled)
|
|
||||||
{
|
|
||||||
Hotbar?.StorageContainer.AddChild(_container);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_ui.WindowRoot.AddChild(_container);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_entity.TryGetComponent<StorageComponent>(_container.StorageEntity, out var comp))
|
|
||||||
OnStorageOrderChanged((_container.StorageEntity.Value, comp));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnOpaqueWindowChanged(bool obj)
|
|
||||||
{
|
|
||||||
if (OpaqueStorageWindow == obj)
|
|
||||||
return;
|
|
||||||
OpaqueStorageWindow = obj;
|
|
||||||
_container?.BuildBackground();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One might ask, Hey Emo, why are you parsing raw keyboard input just to rotate a rectangle?
|
/// One might ask, Hey Emo, why are you parsing raw keyboard input just to rotate a rectangle?
|
||||||
@@ -190,7 +179,7 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
|
|||||||
binding.Mod3 == Keyboard.Key.Control))
|
binding.Mod3 == Keyboard.Key.Control))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!IsDragging && _entity.System<HandsSystem>().GetActiveHandEntity() == null)
|
if (!IsDragging && EntityManager.System<HandsSystem>().GetActiveHandEntity() == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//clamp it to a cardinal.
|
//clamp it to a cardinal.
|
||||||
@@ -198,43 +187,18 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
|
|||||||
if (DraggingGhost != null)
|
if (DraggingGhost != null)
|
||||||
DraggingGhost.Location.Rotation = DraggingRotation;
|
DraggingGhost.Location.Rotation = DraggingRotation;
|
||||||
|
|
||||||
if (IsDragging || (_container != null && UIManager.CurrentlyHovered == _container))
|
if (IsDragging || UIManager.CurrentlyHovered is StorageWindow)
|
||||||
keyEvent.Handle();
|
keyEvent.Handle();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStorageUpdated(Entity<StorageComponent> uid)
|
private void OnPiecePressed(GUIBoundKeyEventArgs args, StorageWindow window, ItemGridPiece control)
|
||||||
{
|
{
|
||||||
if (_container?.StorageEntity != uid)
|
if (IsDragging || !window.IsOpen)
|
||||||
return;
|
|
||||||
|
|
||||||
_container.BuildItemPieces();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RegisterStorageContainer(StorageContainer container)
|
|
||||||
{
|
|
||||||
if (_container != null)
|
|
||||||
{
|
|
||||||
container.OnPiecePressed -= OnPiecePressed;
|
|
||||||
container.OnPieceUnpressed -= OnPieceUnpressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
_container = container;
|
|
||||||
container.OnPiecePressed += OnPiecePressed;
|
|
||||||
container.OnPieceUnpressed += OnPieceUnpressed;
|
|
||||||
|
|
||||||
if (!StaticStorageUIEnabled)
|
|
||||||
_container.Orphan();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPiecePressed(GUIBoundKeyEventArgs args, ItemGridPiece control)
|
|
||||||
{
|
|
||||||
if (IsDragging || !_container?.IsOpen == true)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (args.Function == ContentKeyFunctions.MoveStoredItem)
|
if (args.Function == ContentKeyFunctions.MoveStoredItem)
|
||||||
{
|
{
|
||||||
DraggingRotation = control.Location.Rotation;
|
DraggingRotation = control.Location.Rotation;
|
||||||
|
|
||||||
_menuDragHelper.MouseDown(control);
|
_menuDragHelper.MouseDown(control);
|
||||||
_menuDragHelper.Update(0f);
|
_menuDragHelper.Update(0f);
|
||||||
|
|
||||||
@@ -242,17 +206,17 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
|
|||||||
}
|
}
|
||||||
else if (args.Function == ContentKeyFunctions.SaveItemLocation)
|
else if (args.Function == ContentKeyFunctions.SaveItemLocation)
|
||||||
{
|
{
|
||||||
if (_container?.StorageEntity is not {} storage)
|
if (window.StorageEntity is not {} storage)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_entity.RaisePredictiveEvent(new StorageSaveItemLocationEvent(
|
EntityManager.RaisePredictiveEvent(new StorageSaveItemLocationEvent(
|
||||||
_entity.GetNetEntity(control.Entity),
|
EntityManager.GetNetEntity(control.Entity),
|
||||||
_entity.GetNetEntity(storage)));
|
EntityManager.GetNetEntity(storage)));
|
||||||
args.Handle();
|
args.Handle();
|
||||||
}
|
}
|
||||||
else if (args.Function == ContentKeyFunctions.ExamineEntity)
|
else if (args.Function == ContentKeyFunctions.ExamineEntity)
|
||||||
{
|
{
|
||||||
_entity.System<ExamineSystem>().DoExamine(control.Entity);
|
EntityManager.System<ExamineSystem>().DoExamine(control.Entity);
|
||||||
args.Handle();
|
args.Handle();
|
||||||
}
|
}
|
||||||
else if (args.Function == EngineKeyFunctions.UseSecondary)
|
else if (args.Function == EngineKeyFunctions.UseSecondary)
|
||||||
@@ -262,62 +226,102 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
|
|||||||
}
|
}
|
||||||
else if (args.Function == ContentKeyFunctions.ActivateItemInWorld)
|
else if (args.Function == ContentKeyFunctions.ActivateItemInWorld)
|
||||||
{
|
{
|
||||||
_entity.RaisePredictiveEvent(
|
EntityManager.RaisePredictiveEvent(
|
||||||
new InteractInventorySlotEvent(_entity.GetNetEntity(control.Entity), altInteract: false));
|
new InteractInventorySlotEvent(EntityManager.GetNetEntity(control.Entity), altInteract: false));
|
||||||
args.Handle();
|
args.Handle();
|
||||||
}
|
}
|
||||||
else if (args.Function == ContentKeyFunctions.AltActivateItemInWorld)
|
else if (args.Function == ContentKeyFunctions.AltActivateItemInWorld)
|
||||||
{
|
{
|
||||||
_entity.RaisePredictiveEvent(new InteractInventorySlotEvent(_entity.GetNetEntity(control.Entity), altInteract: true));
|
EntityManager.RaisePredictiveEvent(new InteractInventorySlotEvent(EntityManager.GetNetEntity(control.Entity), altInteract: true));
|
||||||
args.Handle();
|
args.Handle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.FlagDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPieceUnpressed(GUIBoundKeyEventArgs args, ItemGridPiece control)
|
private void OnPieceUnpressed(GUIBoundKeyEventArgs args, StorageWindow window, ItemGridPiece control)
|
||||||
{
|
{
|
||||||
if (args.Function != ContentKeyFunctions.MoveStoredItem)
|
if (args.Function != ContentKeyFunctions.MoveStoredItem)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_container?.StorageEntity is not { } storageEnt|| !_entity.TryGetComponent<StorageComponent>(storageEnt, out var storageComp))
|
// Want to get the control under the dragged control.
|
||||||
return;
|
// This means we can drag the original control around (and not hide the original).
|
||||||
|
control.MouseFilter = Control.MouseFilterMode.Ignore;
|
||||||
|
var targetControl = UIManager.MouseGetControl(args.PointerLocation);
|
||||||
|
var targetStorage = targetControl as StorageWindow;
|
||||||
|
control.MouseFilter = Control.MouseFilterMode.Pass;
|
||||||
|
|
||||||
if (DraggingGhost is { } draggingGhost)
|
var localPlayer = _player.LocalEntity;
|
||||||
|
window.RemoveGrid(control);
|
||||||
|
window.FlagDirty();
|
||||||
|
|
||||||
|
// If we tried to drag it on top of another grid piece then cancel out.
|
||||||
|
if (targetControl is ItemGridPiece || window.StorageEntity is not { } sourceStorage || localPlayer == null)
|
||||||
|
{
|
||||||
|
window.Reclaim(control.Location, control);
|
||||||
|
args.Handle();
|
||||||
|
_menuDragHelper.EndDrag();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_menuDragHelper.IsDragging && DraggingGhost is { } draggingGhost)
|
||||||
{
|
{
|
||||||
var dragEnt = draggingGhost.Entity;
|
var dragEnt = draggingGhost.Entity;
|
||||||
var dragLoc = draggingGhost.Location;
|
var dragLoc = draggingGhost.Location;
|
||||||
var itemSys = _entity.System<SharedItemSystem>();
|
|
||||||
|
|
||||||
var position = _container.GetMouseGridPieceLocation(dragEnt, dragLoc);
|
// Dragging in the same storage
|
||||||
var itemBounding = itemSys.GetAdjustedItemShape(dragEnt, dragLoc).GetBoundingBox();
|
// The existing ItemGridPiece just stops rendering but still exists so check if it's hovered.
|
||||||
var gridBounding = storageComp.Grid.GetBoundingBox();
|
if (targetStorage == window)
|
||||||
|
|
||||||
// The extended bounding box for if this is out of the window is the grid bounding box dimensions combined
|
|
||||||
// with the item shape bounding box dimensions. Plus 1 on the left for the sidebar. This makes it so that.
|
|
||||||
// dropping an item on the floor requires dragging it all the way out of the window.
|
|
||||||
var left = gridBounding.Left - itemBounding.Width - 1;
|
|
||||||
var bottom = gridBounding.Bottom - itemBounding.Height;
|
|
||||||
var top = gridBounding.Top;
|
|
||||||
var right = gridBounding.Right;
|
|
||||||
var lenientBounding = new Box2i(left, bottom, right, top);
|
|
||||||
|
|
||||||
if (lenientBounding.Contains(position))
|
|
||||||
{
|
{
|
||||||
_entity.RaisePredictiveEvent(new StorageSetItemLocationEvent(
|
var position = targetStorage.GetMouseGridPieceLocation(dragEnt, dragLoc);
|
||||||
_entity.GetNetEntity(draggingGhost.Entity),
|
var newLocation = new ItemStorageLocation(DraggingRotation, position);
|
||||||
_entity.GetNetEntity(storageEnt),
|
|
||||||
new ItemStorageLocation(DraggingRotation, position)));
|
EntityManager.RaisePredictiveEvent(new StorageSetItemLocationEvent(
|
||||||
|
EntityManager.GetNetEntity(draggingGhost.Entity),
|
||||||
|
EntityManager.GetNetEntity(sourceStorage),
|
||||||
|
newLocation));
|
||||||
|
|
||||||
|
window.Reclaim(newLocation, control);
|
||||||
|
}
|
||||||
|
// Dragging to new storage
|
||||||
|
else if (targetStorage?.StorageEntity != null && targetStorage != window)
|
||||||
|
{
|
||||||
|
var position = targetStorage.GetMouseGridPieceLocation(dragEnt, dragLoc);
|
||||||
|
var newLocation = new ItemStorageLocation(DraggingRotation, position);
|
||||||
|
|
||||||
|
// Check it fits and we can move to hand (no free transfers).
|
||||||
|
if (_storage.ItemFitsInGridLocation(
|
||||||
|
(dragEnt, null),
|
||||||
|
(targetStorage.StorageEntity.Value, null),
|
||||||
|
newLocation))
|
||||||
|
{
|
||||||
|
// Can drop and move.
|
||||||
|
EntityManager.RaisePredictiveEvent(new StorageTransferItemEvent(
|
||||||
|
EntityManager.GetNetEntity(dragEnt),
|
||||||
|
EntityManager.GetNetEntity(targetStorage.StorageEntity.Value),
|
||||||
|
newLocation));
|
||||||
|
|
||||||
|
targetStorage.Reclaim(newLocation, control);
|
||||||
|
DraggingRotation = Angle.Zero;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Cancel it (rather than dropping).
|
||||||
|
window.Reclaim(dragLoc, control);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
targetStorage?.FlagDirty();
|
||||||
|
}
|
||||||
|
// If we just clicked, then take it out of the bag.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EntityManager.RaisePredictiveEvent(new StorageInteractWithItemEvent(
|
||||||
|
EntityManager.GetNetEntity(control.Entity),
|
||||||
|
EntityManager.GetNetEntity(sourceStorage)));
|
||||||
}
|
}
|
||||||
|
|
||||||
_menuDragHelper.EndDrag();
|
_menuDragHelper.EndDrag();
|
||||||
_container?.BuildItemPieces();
|
|
||||||
}
|
|
||||||
else //if we just clicked, then take it out of the bag.
|
|
||||||
{
|
|
||||||
_menuDragHelper.EndDrag();
|
|
||||||
_entity.RaisePredictiveEvent(new StorageInteractWithItemEvent(
|
|
||||||
_entity.GetNetEntity(control.Entity),
|
|
||||||
_entity.GetNetEntity(storageEnt)));
|
|
||||||
}
|
|
||||||
args.Handle();
|
args.Handle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,14 +330,8 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
|
|||||||
if (_menuDragHelper.Dragged is not { } dragged)
|
if (_menuDragHelper.Dragged is not { } dragged)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
DraggingGhost!.Orphan();
|
||||||
DraggingRotation = dragged.Location.Rotation;
|
DraggingRotation = dragged.Location.Rotation;
|
||||||
DraggingGhost = new ItemGridPiece(
|
|
||||||
(dragged.Entity, _entity.GetComponent<ItemComponent>(dragged.Entity)),
|
|
||||||
dragged.Location,
|
|
||||||
_entity);
|
|
||||||
DraggingGhost.MouseFilter = Control.MouseFilterMode.Ignore;
|
|
||||||
DraggingGhost.Visible = true;
|
|
||||||
DraggingGhost.Orphan();
|
|
||||||
|
|
||||||
UIManager.PopupRoot.AddChild(DraggingGhost);
|
UIManager.PopupRoot.AddChild(DraggingGhost);
|
||||||
SetDraggingRotation();
|
SetDraggingRotation();
|
||||||
@@ -344,6 +342,7 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
|
|||||||
{
|
{
|
||||||
if (DraggingGhost == null)
|
if (DraggingGhost == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
SetDraggingRotation();
|
SetDraggingRotation();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -356,7 +355,7 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
|
|||||||
var offset = ItemGridPiece.GetCenterOffset(
|
var offset = ItemGridPiece.GetCenterOffset(
|
||||||
(DraggingGhost.Entity, null),
|
(DraggingGhost.Entity, null),
|
||||||
new ItemStorageLocation(DraggingRotation, Vector2i.Zero),
|
new ItemStorageLocation(DraggingRotation, Vector2i.Zero),
|
||||||
_entity);
|
EntityManager);
|
||||||
|
|
||||||
// I don't know why it divides the position by 2. Hope this helps! -emo
|
// I don't know why it divides the position by 2. Hope this helps! -emo
|
||||||
LayoutContainer.SetPosition(DraggingGhost, UIManager.MousePositionScaled.Position / 2 - offset );
|
LayoutContainer.SetPosition(DraggingGhost, UIManager.MousePositionScaled.Position / 2 - offset );
|
||||||
@@ -366,18 +365,13 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
|
|||||||
{
|
{
|
||||||
if (DraggingGhost == null)
|
if (DraggingGhost == null)
|
||||||
return;
|
return;
|
||||||
DraggingGhost.Visible = false;
|
|
||||||
DraggingGhost = null;
|
|
||||||
DraggingRotation = Angle.Zero;
|
DraggingRotation = Angle.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void FrameUpdate(FrameEventArgs args)
|
public override void FrameUpdate(FrameEventArgs args)
|
||||||
{
|
{
|
||||||
base.FrameUpdate(args);
|
base.FrameUpdate(args);
|
||||||
|
|
||||||
_menuDragHelper.Update(args.DeltaSeconds);
|
_menuDragHelper.Update(args.DeltaSeconds);
|
||||||
|
|
||||||
if (!StaticStorageUIEnabled && _container?.Parent != null && _lastContainerPosition != null)
|
|
||||||
_lastContainerPosition = _container.GlobalPosition;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ using Content.Client.UserInterface.Systems.Gameplay;
|
|||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controllers;
|
using Robust.Client.UserInterface.Controllers;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Client.UserInterface.Systems.Viewport;
|
namespace Content.Client.UserInterface.Systems.Viewport;
|
||||||
@@ -15,6 +17,7 @@ public sealed class ViewportUIController : UIController
|
|||||||
[Dependency] private readonly IPlayerManager _playerMan = default!;
|
[Dependency] private readonly IPlayerManager _playerMan = default!;
|
||||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||||
|
[UISystemDependency] private readonly SharedTransformSystem? _transformSystem = default!;
|
||||||
public static readonly Vector2i ViewportSize = (EyeManager.PixelsPerMeter * 21, EyeManager.PixelsPerMeter * 15);
|
public static readonly Vector2i ViewportSize = (EyeManager.PixelsPerMeter * 21, EyeManager.PixelsPerMeter * 15);
|
||||||
public const int ViewportHeight = 15;
|
public const int ViewportHeight = 15;
|
||||||
private MainViewport? Viewport => UIManager.ActiveScreen?.GetWidget<MainViewport>();
|
private MainViewport? Viewport => UIManager.ActiveScreen?.GetWidget<MainViewport>();
|
||||||
@@ -93,8 +96,11 @@ public sealed class ViewportUIController : UIController
|
|||||||
_entMan.TryGetComponent(ent, out EyeComponent? eye);
|
_entMan.TryGetComponent(ent, out EyeComponent? eye);
|
||||||
|
|
||||||
if (eye?.Eye == _eyeManager.CurrentEye
|
if (eye?.Eye == _eyeManager.CurrentEye
|
||||||
&& _entMan.GetComponent<TransformComponent>(ent.Value).WorldPosition == default)
|
&& _entMan.GetComponent<TransformComponent>(ent.Value).MapID == MapId.Nullspace)
|
||||||
return; // nothing to worry about, the player is just in null space... actually that is probably a problem?
|
{
|
||||||
|
// nothing to worry about, the player is just in null space... actually that is probably a problem?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Currently, this shouldn't happen. This likely happened because the main eye was set to null. When this
|
// Currently, this shouldn't happen. This likely happened because the main eye was set to null. When this
|
||||||
// does happen it can create hard to troubleshoot bugs, so lets print some helpful warnings:
|
// does happen it can create hard to troubleshoot bugs, so lets print some helpful warnings:
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ namespace Content.Client.Viewport
|
|||||||
_inputManager.ViewportKeyEvent(this, args);
|
_inputManager.ViewportKeyEvent(this, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleScreen handle)
|
protected override void Draw(IRenderHandle handle)
|
||||||
{
|
{
|
||||||
EnsureViewportCreated();
|
EnsureViewportCreated();
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ namespace Content.Client.Viewport
|
|||||||
var drawBox = GetDrawBox();
|
var drawBox = GetDrawBox();
|
||||||
var drawBoxGlobal = drawBox.Translated(GlobalPixelPosition);
|
var drawBoxGlobal = drawBox.Translated(GlobalPixelPosition);
|
||||||
_viewport.RenderScreenOverlaysBelow(handle, this, drawBoxGlobal);
|
_viewport.RenderScreenOverlaysBelow(handle, this, drawBoxGlobal);
|
||||||
handle.DrawTextureRect(_viewport.RenderTarget.Texture, drawBox);
|
handle.DrawingHandleScreen.DrawTextureRect(_viewport.RenderTarget.Texture, drawBox);
|
||||||
_viewport.RenderScreenOverlaysAbove(handle, this, drawBoxGlobal);
|
_viewport.RenderScreenOverlaysAbove(handle, this, drawBoxGlobal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
using Content.Client.Ghost;
|
|
||||||
using Content.Shared.Voting;
|
using Content.Shared.Voting;
|
||||||
|
|
||||||
namespace Content.Client.Voting;
|
namespace Content.Client.Voting;
|
||||||
|
|
||||||
public sealed class VotingSystem : EntitySystem
|
public sealed class VotingSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
|
||||||
public event Action<VotePlayerListResponseEvent>? VotePlayerListResponse; //Provides a list of players elligble for vote actions
|
public event Action<VotePlayerListResponseEvent>? VotePlayerListResponse; //Provides a list of players elligble for vote actions
|
||||||
|
|
||||||
[Dependency] private readonly GhostSystem _ghostSystem = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ public sealed partial class MeleeWeaponSystem
|
|||||||
const float length = 0.15f;
|
const float length = 0.15f;
|
||||||
var startOffset = sprite.Rotation.RotateVec(new Vector2(0f, -distance / 5f));
|
var startOffset = sprite.Rotation.RotateVec(new Vector2(0f, -distance / 5f));
|
||||||
var endOffset = sprite.Rotation.RotateVec(new Vector2(0f, -distance));
|
var endOffset = sprite.Rotation.RotateVec(new Vector2(0f, -distance));
|
||||||
|
sprite.Rotation += spriteRotation;
|
||||||
|
|
||||||
return new Animation()
|
return new Animation()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
|||||||
var targetCoordinates = xform.Coordinates;
|
var targetCoordinates = xform.Coordinates;
|
||||||
var targetLocalAngle = xform.LocalRotation;
|
var targetLocalAngle = xform.LocalRotation;
|
||||||
|
|
||||||
return Interaction.InRangeUnobstructed(user, target, targetCoordinates, targetLocalAngle, range);
|
return Interaction.InRangeUnobstructed(user, target, targetCoordinates, targetLocalAngle, range, overlapCheck: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void DoDamageEffect(List<EntityUid> targets, EntityUid? user, TransformComponent targetXform)
|
protected override void DoDamageEffect(List<EntityUid> targets, EntityUid? user, TransformComponent targetXform)
|
||||||
|
|||||||
49
Content.Client/Wieldable/WieldableSystem.cs
Normal file
49
Content.Client/Wieldable/WieldableSystem.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Client.Movement.Components;
|
||||||
|
using Content.Client.Movement.Systems;
|
||||||
|
using Content.Shared.Camera;
|
||||||
|
using Content.Shared.Hands;
|
||||||
|
using Content.Shared.Movement.Components;
|
||||||
|
using Content.Shared.Wieldable;
|
||||||
|
using Content.Shared.Wieldable.Components;
|
||||||
|
using Robust.Client.Timing;
|
||||||
|
|
||||||
|
namespace Content.Client.Wieldable;
|
||||||
|
|
||||||
|
public sealed class WieldableSystem : SharedWieldableSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly EyeCursorOffsetSystem _eyeOffset = default!;
|
||||||
|
[Dependency] private readonly IClientGameTiming _gameTiming = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<CursorOffsetRequiresWieldComponent, ItemUnwieldedEvent>(OnEyeOffsetUnwielded);
|
||||||
|
SubscribeLocalEvent<CursorOffsetRequiresWieldComponent, HeldRelayedEvent<GetEyeOffsetRelayedEvent>>(OnGetEyeOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnEyeOffsetUnwielded(Entity<CursorOffsetRequiresWieldComponent> entity, ref ItemUnwieldedEvent args)
|
||||||
|
{
|
||||||
|
if (!TryComp(entity.Owner, out EyeCursorOffsetComponent? cursorOffsetComp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_gameTiming.IsFirstTimePredicted)
|
||||||
|
cursorOffsetComp.CurrentPosition = Vector2.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnGetEyeOffset(Entity<CursorOffsetRequiresWieldComponent> entity, ref HeldRelayedEvent<GetEyeOffsetRelayedEvent> args)
|
||||||
|
{
|
||||||
|
if (!TryComp(entity.Owner, out WieldableComponent? wieldableComp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!wieldableComp.Wielded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var offset = _eyeOffset.OffsetAfterMouse(entity.Owner, null);
|
||||||
|
if (offset == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
args.Args.Offset += offset.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ using Content.Server.Nutrition.Components;
|
|||||||
using Content.Server.Nutrition.EntitySystems;
|
using Content.Server.Nutrition.EntitySystems;
|
||||||
using Content.Shared.Cargo.Prototypes;
|
using Content.Shared.Cargo.Prototypes;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
|
using Content.Shared.Prototypes;
|
||||||
using Content.Shared.Stacks;
|
using Content.Shared.Stacks;
|
||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
using Content.Shared.Whitelist;
|
using Content.Shared.Whitelist;
|
||||||
@@ -104,51 +105,34 @@ public sealed class CargoTest
|
|||||||
await using var pair = await PoolManager.GetServerClient();
|
await using var pair = await PoolManager.GetServerClient();
|
||||||
var server = pair.Server;
|
var server = pair.Server;
|
||||||
|
|
||||||
var testMap = await pair.CreateTestMap();
|
var protoManager = server.ProtoMan;
|
||||||
|
var compFact = server.ResolveDependency<IComponentFactory>();
|
||||||
var entManager = server.ResolveDependency<IEntityManager>();
|
|
||||||
var mapManager = server.ResolveDependency<IMapManager>();
|
|
||||||
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
|
||||||
|
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
var mapId = testMap.MapId;
|
|
||||||
var grid = mapManager.CreateGridEntity(mapId);
|
|
||||||
var coord = new EntityCoordinates(grid.Owner, 0, 0);
|
|
||||||
|
|
||||||
var protoIds = protoManager.EnumeratePrototypes<EntityPrototype>()
|
var protoIds = protoManager.EnumeratePrototypes<EntityPrototype>()
|
||||||
.Where(p => !p.Abstract)
|
.Where(p => !p.Abstract)
|
||||||
.Where(p => !pair.IsTestPrototype(p))
|
.Where(p => !pair.IsTestPrototype(p))
|
||||||
.Where(p => !p.Components.ContainsKey("MapGrid")) // Grids are not for sale.
|
.Where(p => p.Components.ContainsKey("StaticPrice"))
|
||||||
.Select(p => p.ID)
|
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
foreach (var proto in protoIds)
|
foreach (var proto in protoIds)
|
||||||
{
|
{
|
||||||
var ent = entManager.SpawnEntity(proto, coord);
|
// Sanity check
|
||||||
|
Assert.That(proto.TryGetComponent<StaticPriceComponent>(out var staticPriceComp, compFact), Is.True);
|
||||||
|
|
||||||
if (entManager.TryGetComponent<StackPriceComponent>(ent, out var stackpricecomp)
|
if (proto.TryGetComponent<StackPriceComponent>(out var stackPriceComp, compFact) && stackPriceComp.Price > 0)
|
||||||
&& stackpricecomp.Price > 0)
|
|
||||||
{
|
{
|
||||||
if (entManager.TryGetComponent<StaticPriceComponent>(ent, out var staticpricecomp))
|
Assert.That(staticPriceComp.Price, Is.EqualTo(0),
|
||||||
{
|
|
||||||
Assert.That(staticpricecomp.Price, Is.EqualTo(0),
|
|
||||||
$"The prototype {proto} has a StackPriceComponent and StaticPriceComponent whose values are not compatible with each other.");
|
$"The prototype {proto} has a StackPriceComponent and StaticPriceComponent whose values are not compatible with each other.");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (entManager.HasComponent<StackComponent>(ent))
|
if (proto.HasComponent<StackComponent>(compFact))
|
||||||
{
|
{
|
||||||
if (entManager.TryGetComponent<StaticPriceComponent>(ent, out var staticpricecomp))
|
Assert.That(staticPriceComp.Price, Is.EqualTo(0),
|
||||||
{
|
|
||||||
Assert.That(staticpricecomp.Price, Is.EqualTo(0),
|
|
||||||
$"The prototype {proto} has a StackComponent and StaticPriceComponent whose values are not compatible with each other.");
|
$"The prototype {proto} has a StackComponent and StaticPriceComponent whose values are not compatible with each other.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entManager.DeleteEntity(ent);
|
|
||||||
}
|
|
||||||
mapManager.DeleteMap(mapId);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await pair.CleanReturnAsync();
|
await pair.CleanReturnAsync();
|
||||||
|
|||||||
41
Content.IntegrationTests/Tests/ContrabandTest.cs
Normal file
41
Content.IntegrationTests/Tests/ContrabandTest.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
using Content.Shared.Contraband;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.IntegrationTests.Tests;
|
||||||
|
|
||||||
|
[TestFixture]
|
||||||
|
public sealed class ContrabandTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public async Task EntityShowDepartmentsAndJobs()
|
||||||
|
{
|
||||||
|
await using var pair = await PoolManager.GetServerClient();
|
||||||
|
var client = pair.Client;
|
||||||
|
var protoMan = client.ResolveDependency<IPrototypeManager>();
|
||||||
|
var componentFactory = client.ResolveDependency<IComponentFactory>();
|
||||||
|
|
||||||
|
await client.WaitAssertion(() =>
|
||||||
|
{
|
||||||
|
foreach (var proto in protoMan.EnumeratePrototypes<EntityPrototype>())
|
||||||
|
{
|
||||||
|
if (proto.Abstract || pair.IsTestPrototype(proto))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!proto.TryGetComponent<ContrabandComponent>(out var contraband, componentFactory))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Assert.That(protoMan.TryIndex(contraband.Severity, out var severity, false),
|
||||||
|
@$"{proto.ID} has a ContrabandComponent with a unknown severity.");
|
||||||
|
|
||||||
|
if (!severity.ShowDepartmentsAndJobs)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Assert.That(contraband.AllowedDepartments.Count + contraband.AllowedJobs.Count, Is.Not.EqualTo(0),
|
||||||
|
@$"{proto.ID} has a ContrabandComponent with ShowDepartmentsAndJobs but no allowed departments or jobs.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await pair.CleanReturnAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
.Where(p => !p.Abstract)
|
.Where(p => !p.Abstract)
|
||||||
.Where(p => !pair.IsTestPrototype(p))
|
.Where(p => !pair.IsTestPrototype(p))
|
||||||
.Where(p => !p.Components.ContainsKey("MapGrid")) // This will smash stuff otherwise.
|
.Where(p => !p.Components.ContainsKey("MapGrid")) // This will smash stuff otherwise.
|
||||||
|
.Where(p => !p.Components.ContainsKey("RoomFill")) // This comp can delete all entities, and spawn others
|
||||||
.Select(p => p.ID)
|
.Select(p => p.ID)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
@@ -101,6 +102,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
.Where(p => !p.Abstract)
|
.Where(p => !p.Abstract)
|
||||||
.Where(p => !pair.IsTestPrototype(p))
|
.Where(p => !pair.IsTestPrototype(p))
|
||||||
.Where(p => !p.Components.ContainsKey("MapGrid")) // This will smash stuff otherwise.
|
.Where(p => !p.Components.ContainsKey("MapGrid")) // This will smash stuff otherwise.
|
||||||
|
.Where(p => !p.Components.ContainsKey("RoomFill")) // This comp can delete all entities, and spawn others
|
||||||
.Select(p => p.ID)
|
.Select(p => p.ID)
|
||||||
.ToList();
|
.ToList();
|
||||||
foreach (var protoId in protoIds)
|
foreach (var protoId in protoIds)
|
||||||
@@ -341,6 +343,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
"DebugExceptionInitialize",
|
"DebugExceptionInitialize",
|
||||||
"DebugExceptionStartup",
|
"DebugExceptionStartup",
|
||||||
"GridFill",
|
"GridFill",
|
||||||
|
"RoomFill",
|
||||||
"Map", // We aren't testing a map entity in this test
|
"Map", // We aren't testing a map entity in this test
|
||||||
"MapGrid",
|
"MapGrid",
|
||||||
"Broadphase",
|
"Broadphase",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public sealed class LatheTest
|
|||||||
var compFactory = server.ResolveDependency<IComponentFactory>();
|
var compFactory = server.ResolveDependency<IComponentFactory>();
|
||||||
var materialStorageSystem = server.System<SharedMaterialStorageSystem>();
|
var materialStorageSystem = server.System<SharedMaterialStorageSystem>();
|
||||||
var whitelistSystem = server.System<EntityWhitelistSystem>();
|
var whitelistSystem = server.System<EntityWhitelistSystem>();
|
||||||
|
var latheSystem = server.System<SharedLatheSystem>();
|
||||||
|
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
@@ -74,14 +75,14 @@ public sealed class LatheTest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect all the recipes assigned to this lathe
|
// Collect all possible recipes assigned to this lathe
|
||||||
var recipes = new List<ProtoId<LatheRecipePrototype>>();
|
var recipes = new HashSet<ProtoId<LatheRecipePrototype>>();
|
||||||
recipes.AddRange(latheComp.StaticRecipes);
|
latheSystem.AddRecipesFromPacks(recipes, latheComp.StaticPacks);
|
||||||
recipes.AddRange(latheComp.DynamicRecipes);
|
latheSystem.AddRecipesFromPacks(recipes, latheComp.DynamicPacks);
|
||||||
if (latheProto.TryGetComponent<EmagLatheRecipesComponent>(out var emagRecipesComp, compFactory))
|
if (latheProto.TryGetComponent<EmagLatheRecipesComponent>(out var emagRecipesComp, compFactory))
|
||||||
{
|
{
|
||||||
recipes.AddRange(emagRecipesComp.EmagStaticRecipes);
|
latheSystem.AddRecipesFromPacks(recipes, emagRecipesComp.EmagStaticPacks);
|
||||||
recipes.AddRange(emagRecipesComp.EmagDynamicRecipes);
|
latheSystem.AddRecipesFromPacks(recipes, emagRecipesComp.EmagDynamicPacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check each recipe assigned to this lathe
|
// Check each recipe assigned to this lathe
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Reflection;
|
using Robust.Shared.Reflection;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
@@ -29,7 +30,9 @@ public sealed class StaticFieldValidationTest
|
|||||||
Assert.That(protoMan.ValidateStaticFields(typeof(StringValid), protos), Is.Empty);
|
Assert.That(protoMan.ValidateStaticFields(typeof(StringValid), protos), Is.Empty);
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(StringArrayValid), protos), Is.Empty);
|
Assert.That(protoMan.ValidateStaticFields(typeof(StringArrayValid), protos), Is.Empty);
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdValid), protos), Is.Empty);
|
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdValid), protos), Is.Empty);
|
||||||
|
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdTValid), protos), Is.Empty);
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdArrayValid), protos), Is.Empty);
|
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdArrayValid), protos), Is.Empty);
|
||||||
|
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdTArrayValid), protos), Is.Empty);
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdTestValid), protos), Is.Empty);
|
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdTestValid), protos), Is.Empty);
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdArrayValid), protos), Is.Empty);
|
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdArrayValid), protos), Is.Empty);
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdListValid), protos), Is.Empty);
|
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdListValid), protos), Is.Empty);
|
||||||
@@ -39,7 +42,9 @@ public sealed class StaticFieldValidationTest
|
|||||||
Assert.That(protoMan.ValidateStaticFields(typeof(StringInvalid), protos), Has.Count.EqualTo(1));
|
Assert.That(protoMan.ValidateStaticFields(typeof(StringInvalid), protos), Has.Count.EqualTo(1));
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(StringArrayInvalid), protos), Has.Count.EqualTo(2));
|
Assert.That(protoMan.ValidateStaticFields(typeof(StringArrayInvalid), protos), Has.Count.EqualTo(2));
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdInvalid), protos), Has.Count.EqualTo(1));
|
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdInvalid), protos), Has.Count.EqualTo(1));
|
||||||
|
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdTInvalid), protos), Has.Count.EqualTo(1));
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdArrayInvalid), protos), Has.Count.EqualTo(2));
|
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdArrayInvalid), protos), Has.Count.EqualTo(2));
|
||||||
|
Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdTArrayInvalid), protos), Has.Count.EqualTo(2));
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdTestInvalid), protos), Has.Count.EqualTo(1));
|
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdTestInvalid), protos), Has.Count.EqualTo(1));
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdArrayInvalid), protos), Has.Count.EqualTo(2));
|
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdArrayInvalid), protos), Has.Count.EqualTo(2));
|
||||||
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdListInvalid), protos), Has.Count.EqualTo(2));
|
Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdListInvalid), protos), Has.Count.EqualTo(2));
|
||||||
@@ -88,24 +93,48 @@ public sealed class StaticFieldValidationTest
|
|||||||
public static EntProtoId Tag = "StaticFieldTestEnt";
|
public static EntProtoId Tag = "StaticFieldTestEnt";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Reflect(false)]
|
||||||
|
private sealed class EntProtoIdTValid
|
||||||
|
{
|
||||||
|
public static EntProtoId<TransformComponent> Tag = "StaticFieldTestEnt";
|
||||||
|
}
|
||||||
|
|
||||||
[Reflect(false)]
|
[Reflect(false)]
|
||||||
private sealed class EntProtoIdInvalid
|
private sealed class EntProtoIdInvalid
|
||||||
{
|
{
|
||||||
public static EntProtoId Tag = string.Empty;
|
public static EntProtoId Tag = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Reflect(false)]
|
||||||
|
private sealed class EntProtoIdTInvalid
|
||||||
|
{
|
||||||
|
public static EntProtoId<TransformComponent> Tag = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
[Reflect(false)]
|
[Reflect(false)]
|
||||||
private sealed class EntProtoIdArrayValid
|
private sealed class EntProtoIdArrayValid
|
||||||
{
|
{
|
||||||
public static EntProtoId[] Tag = ["StaticFieldTestEnt", "StaticFieldTestEnt"];
|
public static EntProtoId[] Tag = ["StaticFieldTestEnt", "StaticFieldTestEnt"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Reflect(false)]
|
||||||
|
private sealed class EntProtoIdTArrayValid
|
||||||
|
{
|
||||||
|
public static EntProtoId<TransformComponent>[] Tag = ["StaticFieldTestEnt", "StaticFieldTestEnt"];
|
||||||
|
}
|
||||||
|
|
||||||
[Reflect(false)]
|
[Reflect(false)]
|
||||||
private sealed class EntProtoIdArrayInvalid
|
private sealed class EntProtoIdArrayInvalid
|
||||||
{
|
{
|
||||||
public static EntProtoId[] Tag = [string.Empty, "StaticFieldTestEnt", string.Empty];
|
public static EntProtoId[] Tag = [string.Empty, "StaticFieldTestEnt", string.Empty];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Reflect(false)]
|
||||||
|
private sealed class EntProtoIdTArrayInvalid
|
||||||
|
{
|
||||||
|
public static EntProtoId<TransformComponent>[] Tag = [string.Empty, "StaticFieldTestEnt", string.Empty];
|
||||||
|
}
|
||||||
|
|
||||||
[Reflect(false)]
|
[Reflect(false)]
|
||||||
private sealed class ProtoIdTestValid
|
private sealed class ProtoIdTestValid
|
||||||
{
|
{
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user