Viewport improvements (#3765)
This commit is contained in:
committed by
GitHub
parent
8e2fc49357
commit
147a54c642
@@ -28,9 +28,9 @@ namespace Content.Client.Atmos
|
|||||||
_atmosDebugOverlaySystem = EntitySystem.Get<AtmosDebugOverlaySystem>();
|
_atmosDebugOverlaySystem = EntitySystem.Get<AtmosDebugOverlaySystem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleBase handle, OverlaySpace overlay)
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
{
|
{
|
||||||
var drawHandle = (DrawingHandleWorld) handle;
|
var drawHandle = args.WorldHandle;
|
||||||
|
|
||||||
var mapId = _eyeManager.CurrentMap;
|
var mapId = _eyeManager.CurrentMap;
|
||||||
var eye = _eyeManager.CurrentEye;
|
var eye = _eyeManager.CurrentEye;
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ namespace Content.Client.Atmos
|
|||||||
_gasTileOverlaySystem = EntitySystem.Get<GasTileOverlaySystem>();
|
_gasTileOverlaySystem = EntitySystem.Get<GasTileOverlaySystem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleBase handle, OverlaySpace overlay)
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
{
|
{
|
||||||
var drawHandle = (DrawingHandleWorld) handle;
|
var drawHandle = args.WorldHandle;
|
||||||
|
|
||||||
var mapId = _eyeManager.CurrentMap;
|
var mapId = _eyeManager.CurrentMap;
|
||||||
var eye = _eyeManager.CurrentEye;
|
var eye = _eyeManager.CurrentEye;
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ namespace Content.Client
|
|||||||
IoCManager.Register<EuiManager, EuiManager>();
|
IoCManager.Register<EuiManager, EuiManager>();
|
||||||
IoCManager.Register<IVoteManager, VoteManager>();
|
IoCManager.Register<IVoteManager, VoteManager>();
|
||||||
IoCManager.Register<ChangelogManager, ChangelogManager>();
|
IoCManager.Register<ChangelogManager, ChangelogManager>();
|
||||||
|
IoCManager.Register<ViewportManager, ViewportManager>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ using Robust.Client.Graphics;
|
|||||||
using Robust.Client.Input;
|
using Robust.Client.Input;
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
using Robust.Client.State;
|
using Robust.Client.State;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Shared.ContentPack;
|
using Robust.Shared.ContentPack;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
@@ -100,6 +101,7 @@ namespace Content.Client
|
|||||||
IoCManager.Resolve<IStylesheetManager>().Initialize();
|
IoCManager.Resolve<IStylesheetManager>().Initialize();
|
||||||
IoCManager.Resolve<IScreenshotHook>().Initialize();
|
IoCManager.Resolve<IScreenshotHook>().Initialize();
|
||||||
IoCManager.Resolve<ChangelogManager>().Initialize();
|
IoCManager.Resolve<ChangelogManager>().Initialize();
|
||||||
|
IoCManager.Resolve<ViewportManager>().Initialize();
|
||||||
|
|
||||||
IoCManager.InjectDependencies(this);
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
@@ -183,6 +185,9 @@ namespace Content.Client
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Disable engine-default viewport since we use our own custom viewport control.
|
||||||
|
IoCManager.Resolve<IUserInterfaceManager>().MainViewport.Visible = false;
|
||||||
|
|
||||||
SwitchToDefaultState();
|
SwitchToDefaultState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,28 +11,23 @@ namespace Content.Client.GameObjects.Components
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
|
||||||
|
private const float DefaultWidth = 1;
|
||||||
private const string ShaderInRange = "SelectionOutlineInrange";
|
private const string ShaderInRange = "SelectionOutlineInrange";
|
||||||
private const string ShaderOutOfRange = "SelectionOutline";
|
private const string ShaderOutOfRange = "SelectionOutline";
|
||||||
|
|
||||||
public override string Name => "InteractionOutline";
|
public override string Name => "InteractionOutline";
|
||||||
|
|
||||||
private ShaderInstance? _selectionShaderInstance;
|
private bool _inRange;
|
||||||
private ShaderInstance? _selectionShaderInRangeInstance;
|
private ShaderInstance? _shader;
|
||||||
|
private int _lastRenderScale;
|
||||||
|
|
||||||
/// <inheritdoc />
|
public void OnMouseEnter(bool inInteractionRange, int renderScale)
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
_selectionShaderInRangeInstance = _prototypeManager.Index<ShaderPrototype>(ShaderInRange).Instance();
|
|
||||||
_selectionShaderInstance = _prototypeManager.Index<ShaderPrototype>(ShaderOutOfRange).Instance();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnMouseEnter(bool inInteractionRange)
|
|
||||||
{
|
{
|
||||||
|
_lastRenderScale = renderScale;
|
||||||
|
_inRange = inInteractionRange;
|
||||||
if (Owner.TryGetComponent(out ISpriteComponent? sprite))
|
if (Owner.TryGetComponent(out ISpriteComponent? sprite))
|
||||||
{
|
{
|
||||||
sprite.PostShader = inInteractionRange ? _selectionShaderInRangeInstance : _selectionShaderInstance;
|
sprite.PostShader = MakeNewShader(inInteractionRange, renderScale);
|
||||||
sprite.RenderOrder = Owner.EntityManager.CurrentTick.Value;
|
sprite.RenderOrder = Owner.EntityManager.CurrentTick.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,14 +39,30 @@ namespace Content.Client.GameObjects.Components
|
|||||||
sprite.PostShader = null;
|
sprite.PostShader = null;
|
||||||
sprite.RenderOrder = 0;
|
sprite.RenderOrder = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_shader?.Dispose();
|
||||||
|
_shader = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateInRange(bool inInteractionRange)
|
public void UpdateInRange(bool inInteractionRange, int renderScale)
|
||||||
{
|
{
|
||||||
if (Owner.TryGetComponent(out ISpriteComponent? sprite))
|
if (Owner.TryGetComponent(out ISpriteComponent? sprite)
|
||||||
|
&& (inInteractionRange != _inRange || _lastRenderScale != renderScale))
|
||||||
{
|
{
|
||||||
sprite.PostShader = inInteractionRange ? _selectionShaderInRangeInstance : _selectionShaderInstance;
|
_inRange = inInteractionRange;
|
||||||
}
|
_lastRenderScale = renderScale;
|
||||||
|
_shader = MakeNewShader(_inRange, _lastRenderScale);
|
||||||
|
sprite.PostShader = _shader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ShaderInstance MakeNewShader(bool inRange, int renderScale)
|
||||||
|
{
|
||||||
|
var shaderName = inRange ? ShaderInRange : ShaderOutOfRange;
|
||||||
|
|
||||||
|
var instance = _prototypeManager.Index<ShaderPrototype>(shaderName).InstanceUnique();
|
||||||
|
instance.SetParameter("outline_width", DefaultWidth * renderScale);
|
||||||
|
return instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,17 +35,7 @@ namespace Content.Client.GameObjects.Components.Suspicion
|
|||||||
_font = new VectorFont(resourceCache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
|
_font = new VectorFont(resourceCache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleBase handle, OverlaySpace currentSpace)
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
{
|
|
||||||
switch (currentSpace)
|
|
||||||
{
|
|
||||||
case OverlaySpace.ScreenSpace:
|
|
||||||
DrawScreen((DrawingHandleScreen) handle);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawScreen(DrawingHandleScreen screen)
|
|
||||||
{
|
{
|
||||||
var viewport = _eyeManager.GetWorldViewport();
|
var viewport = _eyeManager.GetWorldViewport();
|
||||||
|
|
||||||
@@ -91,8 +81,8 @@ namespace Content.Client.GameObjects.Components.Suspicion
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var screenCoordinates = _eyeManager.WorldToScreen(physics.GetWorldAABB().TopLeft + (0, 0.5f));
|
var screenCoordinates = args.ViewportControl!.WorldToScreen(physics.GetWorldAABB().TopLeft + (0, 0.5f));
|
||||||
DrawString(screen, _font, screenCoordinates, _traitorText, Color.OrangeRed);
|
DrawString(args.ScreenHandle, _font, screenCoordinates, _traitorText, Color.OrangeRed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -464,15 +464,15 @@ namespace Content.Client.GameObjects.EntitySystems.AI
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleBase handle, OverlaySpace currentSpace)
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
{
|
{
|
||||||
if (Modes == 0)
|
if (Modes == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle.UseShader(_shader);
|
var screenHandle = args.ScreenHandle;
|
||||||
var screenHandle = (DrawingHandleScreen) handle;
|
screenHandle.UseShader(_shader);
|
||||||
var viewport = _eyeManager.GetWorldViewport();
|
var viewport = _eyeManager.GetWorldViewport();
|
||||||
|
|
||||||
if ((Modes & PathfindingDebugMode.Route) != 0)
|
if ((Modes & PathfindingDebugMode.Route) != 0)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using Content.Shared;
|
|||||||
using Content.Shared.GameObjects.Verbs;
|
using Content.Shared.GameObjects.Verbs;
|
||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.Input;
|
using Robust.Client.Input;
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
using Robust.Client.State;
|
using Robust.Client.State;
|
||||||
@@ -35,6 +36,7 @@ namespace Content.Client.GameObjects.EntitySystems
|
|||||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
|
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||||
|
|
||||||
private readonly IContextMenuView _contextMenuView;
|
private readonly IContextMenuView _contextMenuView;
|
||||||
private readonly VerbSystem _verbSystem;
|
private readonly VerbSystem _verbSystem;
|
||||||
@@ -152,7 +154,14 @@ namespace Content.Client.GameObjects.EntitySystems
|
|||||||
{
|
{
|
||||||
var inRange =
|
var inRange =
|
||||||
localPlayer.InRangeUnobstructed(e.ContextEntity, ignoreInsideBlocker: true);
|
localPlayer.InRangeUnobstructed(e.ContextEntity, ignoreInsideBlocker: true);
|
||||||
e.OutlineComponent?.UpdateInRange(inRange);
|
|
||||||
|
// BUG: This assumes that the main viewport is the viewport that the context menu is active on.
|
||||||
|
// This is not necessarily true but we currently have no way to find the viewport (reliably)
|
||||||
|
// from the input event.
|
||||||
|
//
|
||||||
|
// This might be particularly important in the future with a more advanced mapping mode.
|
||||||
|
var renderScale = _eyeManager.MainViewport.GetRenderScale();
|
||||||
|
e.OutlineComponent?.UpdateInRange(inRange, renderScale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +179,8 @@ namespace Content.Client.GameObjects.EntitySystems
|
|||||||
var localPlayer = _playerManager.LocalPlayer;
|
var localPlayer = _playerManager.LocalPlayer;
|
||||||
if (localPlayer?.ControlledEntity == null) return;
|
if (localPlayer?.ControlledEntity == null) return;
|
||||||
|
|
||||||
e.OutlineComponent?.OnMouseEnter(localPlayer.InRangeUnobstructed(entity, ignoreInsideBlocker: true));
|
var renderScale = _eyeManager.MainViewport.GetRenderScale();
|
||||||
|
e.OutlineComponent?.OnMouseEnter(localPlayer.InRangeUnobstructed(entity, ignoreInsideBlocker: true), renderScale);
|
||||||
if (e.SpriteComp != null)
|
if (e.SpriteComp != null)
|
||||||
{
|
{
|
||||||
e.SpriteComp.DrawDepth = (int) Shared.GameObjects.DrawDepth.HighlightedItems;
|
e.SpriteComp.DrawDepth = (int) Shared.GameObjects.DrawDepth.HighlightedItems;
|
||||||
|
|||||||
@@ -22,12 +22,14 @@ namespace Content.Client.Graphics.Overlays
|
|||||||
_shader = _prototypeManager.Index<ShaderPrototype>("CircleMask").Instance();
|
_shader = _prototypeManager.Index<ShaderPrototype>("CircleMask").Instance();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleBase handle, OverlaySpace currentSpace)
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
{
|
{
|
||||||
if (!CritOverlay.LocalPlayerHasState(_playerManager, false, true))
|
if (!CritOverlay.LocalPlayerHasState(_playerManager, false, true))
|
||||||
return;
|
return;
|
||||||
handle.UseShader(_shader);
|
|
||||||
var worldHandle = (DrawingHandleWorld)handle;
|
|
||||||
|
var worldHandle = args.WorldHandle;
|
||||||
|
worldHandle.UseShader(_shader);
|
||||||
var viewport = _eyeManager.GetWorldViewport();
|
var viewport = _eyeManager.GetWorldViewport();
|
||||||
worldHandle.DrawRect(viewport, Color.White);
|
worldHandle.DrawRect(viewport, Color.White);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ namespace Content.Client.Graphics.Overlays
|
|||||||
_shader = _prototypeManager.Index<ShaderPrototype>("ColoredScreenBorder").Instance();
|
_shader = _prototypeManager.Index<ShaderPrototype>("ColoredScreenBorder").Instance();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleBase handle, OverlaySpace currentSpace)
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
{
|
{
|
||||||
handle.UseShader(_shader);
|
var worldHandle = args.WorldHandle;
|
||||||
var worldHandle = (DrawingHandleWorld)handle;
|
worldHandle.UseShader(_shader);
|
||||||
var viewport = _eyeManager.GetWorldViewport();
|
var viewport = _eyeManager.GetWorldViewport();
|
||||||
worldHandle.DrawRect(viewport, Color.White);
|
worldHandle.DrawRect(viewport, Color.White);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,14 +45,14 @@ namespace Content.Client.Graphics.Overlays
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleBase handle, OverlaySpace currentSpace)
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
{
|
{
|
||||||
if (!LocalPlayerHasState(_playerManager, true, false))
|
if (!LocalPlayerHasState(_playerManager, true, false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var worldHandle = (DrawingHandleWorld) handle;
|
var worldHandle = args.WorldHandle;
|
||||||
var viewport = _eyeManager.GetWorldViewport();
|
var viewport = _eyeManager.GetWorldViewport();
|
||||||
handle.UseShader(_gradientCircleShader);
|
worldHandle.UseShader(_gradientCircleShader);
|
||||||
worldHandle.DrawRect(viewport, Color.White);
|
worldHandle.DrawRect(viewport, Color.White);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
using Content.Client.State;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.State;
|
||||||
using Robust.Shared.Enums;
|
using Robust.Shared.Enums;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
@@ -14,6 +16,7 @@ namespace Content.Client.Graphics.Overlays
|
|||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly IClyde _displayManager = default!;
|
[Dependency] private readonly IClyde _displayManager = default!;
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
|
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||||
|
|
||||||
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||||
private readonly ShaderInstance _shader;
|
private readonly ShaderInstance _shader;
|
||||||
@@ -29,29 +32,34 @@ namespace Content.Client.Graphics.Overlays
|
|||||||
|
|
||||||
public void ReceiveFlash(double duration)
|
public void ReceiveFlash(double duration)
|
||||||
{
|
{
|
||||||
_displayManager.Screenshot(ScreenshotType.BeforeUI, image =>
|
if (_stateManager.CurrentState is IMainViewportState state)
|
||||||
|
{
|
||||||
|
state.Viewport.Viewport.Screenshot(image =>
|
||||||
{
|
{
|
||||||
var rgba32Image = image.CloneAs<Rgba32>(Configuration.Default);
|
var rgba32Image = image.CloneAs<Rgba32>(Configuration.Default);
|
||||||
_screenshotTexture = _displayManager.LoadTextureFromImage(rgba32Image);
|
_screenshotTexture = _displayManager.LoadTextureFromImage(rgba32Image);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_startTime = _gameTiming.CurTime.TotalSeconds;
|
_startTime = _gameTiming.CurTime.TotalSeconds;
|
||||||
_lastsFor = duration;
|
_lastsFor = duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleBase handle, OverlaySpace currentSpace)
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
{
|
{
|
||||||
var percentComplete = (float) ((_gameTiming.CurTime.TotalSeconds - _startTime) / _lastsFor);
|
var percentComplete = (float) ((_gameTiming.CurTime.TotalSeconds - _startTime) / _lastsFor);
|
||||||
if (percentComplete >= 1.0f)
|
if (percentComplete >= 1.0f)
|
||||||
return;
|
return;
|
||||||
handle.UseShader(_shader);
|
|
||||||
_shader?.SetParameter("percentComplete", percentComplete);
|
|
||||||
|
|
||||||
var screenSpaceHandle = handle as DrawingHandleScreen;
|
var screenSpaceHandle = args.ScreenHandle;
|
||||||
|
screenSpaceHandle.UseShader(_shader);
|
||||||
|
_shader.SetParameter("percentComplete", percentComplete);
|
||||||
|
|
||||||
var screenSize = UIBox2.FromDimensions((0, 0), _displayManager.ScreenSize);
|
var screenSize = UIBox2.FromDimensions((0, 0), _displayManager.ScreenSize);
|
||||||
|
|
||||||
if (_screenshotTexture != null)
|
if (_screenshotTexture != null)
|
||||||
{
|
{
|
||||||
screenSpaceHandle?.DrawTextureRect(_screenshotTexture, screenSize);
|
screenSpaceHandle.DrawTextureRect(_screenshotTexture, screenSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace Content.Client.Graphics.Overlays
|
|||||||
return _singularities.Count() > 0;
|
return _singularities.Count() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleBase handle, OverlaySpace currentSpace)
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
{
|
{
|
||||||
SingularityQuery();
|
SingularityQuery();
|
||||||
|
|
||||||
@@ -52,8 +52,8 @@ namespace Content.Client.Graphics.Overlays
|
|||||||
_shader?.SetParameter("intensity", LevelToIntensity(instance.Level));
|
_shader?.SetParameter("intensity", LevelToIntensity(instance.Level));
|
||||||
_shader?.SetParameter("falloff", LevelToFalloff(instance.Level));
|
_shader?.SetParameter("falloff", LevelToFalloff(instance.Level));
|
||||||
|
|
||||||
handle.UseShader(_shader);
|
var worldHandle = args.WorldHandle;
|
||||||
var worldHandle = (DrawingHandleWorld) handle;
|
worldHandle.UseShader(_shader);
|
||||||
var viewport = _eyeManager.GetWorldViewport();
|
var viewport = _eyeManager.GetWorldViewport();
|
||||||
worldHandle.DrawRect(viewport, Color.White);
|
worldHandle.DrawRect(viewport, Color.White);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using Content.Client.Interfaces.Parallax;
|
using Content.Client.Interfaces.Parallax;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Shared.Enums;
|
using Robust.Shared.Enums;
|
||||||
@@ -18,7 +19,7 @@ namespace Content.Client.Parallax
|
|||||||
|
|
||||||
private Texture? _parallaxTexture;
|
private Texture? _parallaxTexture;
|
||||||
|
|
||||||
public override OverlaySpace Space => OverlaySpace.ScreenSpaceBelowWorld;
|
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowWorld;
|
||||||
private readonly ShaderInstance _shader;
|
private readonly ShaderInstance _shader;
|
||||||
|
|
||||||
public ParallaxOverlay()
|
public ParallaxOverlay()
|
||||||
@@ -36,26 +37,36 @@ namespace Content.Client.Parallax
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleBase handle, OverlaySpace currentSpace)
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
{
|
{
|
||||||
if (_parallaxTexture == null)
|
if (_parallaxTexture == null || args.Viewport.Eye == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle.UseShader(_shader);
|
var screenHandle = args.WorldHandle;
|
||||||
var screenHandle = (DrawingHandleScreen) handle;
|
screenHandle.UseShader(_shader);
|
||||||
|
|
||||||
var (sizeX, sizeY) = _parallaxTexture.Size;
|
var (sizeX, sizeY) = _parallaxTexture.Size / (float) EyeManager.PixelsPerMeter;
|
||||||
var (posX, posY) = _eyeManager.ScreenToMap(Vector2.Zero).Position;
|
var (posX, posY) = args.Viewport.Eye.Position;
|
||||||
var (ox, oy) = (Vector2i) new Vector2(-posX / Slowness, posY / Slowness);
|
var o = new Vector2(posX * Slowness, posY * Slowness);
|
||||||
ox = MathHelper.Mod(ox, sizeX);
|
|
||||||
oy = MathHelper.Mod(oy, sizeY);
|
|
||||||
|
|
||||||
var (screenSizeX, screenSizeY) = _displayManager.ScreenSize;
|
// Remove offset so we can floor.
|
||||||
for (var x = -sizeX; x < screenSizeX; x += sizeX) {
|
var (l, b) = args.WorldBounds.BottomLeft - o;
|
||||||
for (var y = -sizeY; y < screenSizeY; y += sizeY) {
|
|
||||||
screenHandle.DrawTexture(_parallaxTexture, new Vector2(ox + x, oy + y));
|
// Floor to background size.
|
||||||
|
l = sizeX * MathF.Floor(l / sizeX);
|
||||||
|
b = sizeY * MathF.Floor(b / sizeY);
|
||||||
|
|
||||||
|
// Re-offset.
|
||||||
|
l += o.X;
|
||||||
|
b += o.Y;
|
||||||
|
|
||||||
|
for (var x = l; x < args.WorldBounds.Right; x += sizeX)
|
||||||
|
{
|
||||||
|
for (var y = b; y < args.WorldBounds.Top; y += sizeY)
|
||||||
|
{
|
||||||
|
screenHandle.DrawTexture(_parallaxTexture, (x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Content.Client.State;
|
||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.Input;
|
using Robust.Client.Input;
|
||||||
|
using Robust.Client.State;
|
||||||
using Robust.Shared.ContentPack;
|
using Robust.Shared.ContentPack;
|
||||||
using Robust.Shared.Input.Binding;
|
using Robust.Shared.Input.Binding;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Log;
|
using Robust.Shared.Log;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
|
||||||
namespace Content.Client
|
namespace Content.Client
|
||||||
{
|
{
|
||||||
@@ -20,24 +23,30 @@ namespace Content.Client
|
|||||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||||
[Dependency] private readonly IClyde _clyde = default!;
|
[Dependency] private readonly IClyde _clyde = default!;
|
||||||
[Dependency] private readonly IResourceManager _resourceManager = default!;
|
[Dependency] private readonly IResourceManager _resourceManager = default!;
|
||||||
|
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
_inputManager.SetInputCommand(ContentKeyFunctions.TakeScreenshot, InputCmdHandler.FromDelegate(_ =>
|
_inputManager.SetInputCommand(ContentKeyFunctions.TakeScreenshot, InputCmdHandler.FromDelegate(_ =>
|
||||||
{
|
{
|
||||||
Take(ScreenshotType.AfterUI);
|
_clyde.Screenshot(ScreenshotType.Final, Take);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
_inputManager.SetInputCommand(ContentKeyFunctions.TakeScreenshotNoUI, InputCmdHandler.FromDelegate(_ =>
|
_inputManager.SetInputCommand(ContentKeyFunctions.TakeScreenshotNoUI, InputCmdHandler.FromDelegate(_ =>
|
||||||
{
|
{
|
||||||
Take(ScreenshotType.BeforeUI);
|
if (_stateManager.CurrentState is IMainViewportState state)
|
||||||
|
{
|
||||||
|
state.Viewport.Viewport.Screenshot(Take);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.InfoS("screenshot", "Can't take no-UI screenshot: current state is not GameScreen");
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Take(ScreenshotType type)
|
private async void Take<T>(Image<T> screenshot) where T : unmanaged, IPixel<T>
|
||||||
{
|
{
|
||||||
var screenshot = await _clyde.ScreenshotAsync(type);
|
|
||||||
|
|
||||||
var time = DateTime.Now.ToString("yyyy-M-dd_HH.mm.ss");
|
var time = DateTime.Now.ToString("yyyy-M-dd_HH.mm.ss");
|
||||||
|
|
||||||
if (!_resourceManager.UserData.IsDir(BaseScreenshotPath))
|
if (!_resourceManager.UserData.IsDir(BaseScreenshotPath))
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Content.Client.UserInterface;
|
|||||||
using Content.Client.Voting;
|
using Content.Client.Voting;
|
||||||
using Content.Shared;
|
using Content.Shared;
|
||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.Input;
|
using Robust.Client.Input;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
@@ -13,12 +14,16 @@ using Robust.Shared.Configuration;
|
|||||||
using Robust.Shared.Input.Binding;
|
using Robust.Shared.Input.Binding;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
namespace Content.Client.State
|
namespace Content.Client.State
|
||||||
{
|
{
|
||||||
public class GameScreen : GameScreenBase
|
public class GameScreen : GameScreenBase, IMainViewportState
|
||||||
{
|
{
|
||||||
|
public static readonly Vector2i ViewportSize = (EyeManager.PixelsPerMeter * 21, EyeManager.PixelsPerMeter * 15);
|
||||||
|
|
||||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||||
[Dependency] private readonly IGameHud _gameHud = default!;
|
[Dependency] private readonly IGameHud _gameHud = default!;
|
||||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||||
@@ -26,6 +31,8 @@ namespace Content.Client.State
|
|||||||
[Dependency] private readonly IVoteManager _voteManager = default!;
|
[Dependency] private readonly IVoteManager _voteManager = default!;
|
||||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||||
[Dependency] private readonly IClientAdminManager _adminManager = default!;
|
[Dependency] private readonly IClientAdminManager _adminManager = default!;
|
||||||
|
[Dependency] private readonly IClyde _clyde = default!;
|
||||||
|
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||||
|
|
||||||
[ViewVariables] private ChatBox? _gameChat;
|
[ViewVariables] private ChatBox? _gameChat;
|
||||||
private ConstructionMenuPresenter? _constructionMenu;
|
private ConstructionMenuPresenter? _constructionMenu;
|
||||||
@@ -33,15 +40,27 @@ namespace Content.Client.State
|
|||||||
private bool _oocEnabled;
|
private bool _oocEnabled;
|
||||||
private bool _adminOocEnabled;
|
private bool _adminOocEnabled;
|
||||||
|
|
||||||
|
public MainViewport Viewport { get; private set; } = default!;
|
||||||
|
|
||||||
public override void Startup()
|
public override void Startup()
|
||||||
{
|
{
|
||||||
base.Startup();
|
base.Startup();
|
||||||
|
|
||||||
_gameChat = new ChatBox();
|
_gameChat = new ChatBox();
|
||||||
|
Viewport = new MainViewport
|
||||||
|
{
|
||||||
|
Viewport =
|
||||||
|
{
|
||||||
|
ViewportSize = ViewportSize
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_userInterfaceManager.StateRoot.AddChild(Viewport);
|
||||||
|
LayoutContainer.SetAnchorPreset(Viewport, LayoutContainer.LayoutPreset.Wide);
|
||||||
|
Viewport.SetPositionFirst();
|
||||||
|
|
||||||
_userInterfaceManager.StateRoot.AddChild(_gameChat);
|
_userInterfaceManager.StateRoot.AddChild(_gameChat);
|
||||||
LayoutContainer.SetAnchorAndMarginPreset(_gameChat, LayoutContainer.LayoutPreset.TopRight, margin: 10);
|
LayoutContainer.SetAnchorAndMarginPreset(_gameChat, LayoutContainer.LayoutPreset.TopRight, margin: 10);
|
||||||
LayoutContainer.SetAnchorAndMarginPreset(_gameChat, LayoutContainer.LayoutPreset.TopRight, margin: 10);
|
|
||||||
LayoutContainer.SetMarginLeft(_gameChat, -475);
|
LayoutContainer.SetMarginLeft(_gameChat, -475);
|
||||||
LayoutContainer.SetMarginBottom(_gameChat, 235);
|
LayoutContainer.SetMarginBottom(_gameChat, 235);
|
||||||
|
|
||||||
@@ -65,6 +84,8 @@ namespace Content.Client.State
|
|||||||
_adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
|
_adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
|
||||||
|
|
||||||
SetupPresenters();
|
SetupPresenters();
|
||||||
|
|
||||||
|
_eyeManager.MainViewport = Viewport.Viewport;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Shutdown()
|
public override void Shutdown()
|
||||||
@@ -74,7 +95,10 @@ namespace Content.Client.State
|
|||||||
base.Shutdown();
|
base.Shutdown();
|
||||||
|
|
||||||
_gameChat?.Dispose();
|
_gameChat?.Dispose();
|
||||||
|
Viewport.Dispose();
|
||||||
_gameHud.RootControl.Orphan();
|
_gameHud.RootControl.Orphan();
|
||||||
|
// Clear viewport to some fallback, whatever.
|
||||||
|
_eyeManager.MainViewport = _userInterfaceManager.MainViewport;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,5 +192,12 @@ namespace Content.Client.State
|
|||||||
chat.Input.GrabKeyboardFocus();
|
chat.Input.GrabKeyboardFocus();
|
||||||
chat.Input.InsertAtCursor("]");
|
chat.Input.InsertAtCursor("]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void FrameUpdate(FrameEventArgs e)
|
||||||
|
{
|
||||||
|
base.FrameUpdate(e);
|
||||||
|
|
||||||
|
Viewport.Viewport.Eye = _eyeManager.CurrentEye;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,15 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Client.GameObjects.Components;
|
using Content.Client.GameObjects.Components;
|
||||||
|
using Content.Client.UserInterface;
|
||||||
using Content.Client.Utility;
|
using Content.Client.Utility;
|
||||||
using Content.Shared;
|
using Content.Shared;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Graphics;
|
|
||||||
using Robust.Client.Input;
|
using Robust.Client.Input;
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
using Robust.Client.State;
|
using Robust.Client.State;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Input;
|
using Robust.Shared.Input;
|
||||||
@@ -28,7 +29,6 @@ namespace Content.Client.State
|
|||||||
[Dependency] protected readonly IClientEntityManager EntityManager = default!;
|
[Dependency] protected readonly IClientEntityManager EntityManager = default!;
|
||||||
[Dependency] protected readonly IInputManager InputManager = default!;
|
[Dependency] protected readonly IInputManager InputManager = default!;
|
||||||
[Dependency] protected readonly IPlayerManager PlayerManager = default!;
|
[Dependency] protected readonly IPlayerManager PlayerManager = default!;
|
||||||
[Dependency] protected readonly IEyeManager EyeManager = default!;
|
|
||||||
[Dependency] protected readonly IEntitySystemManager EntitySystemManager = default!;
|
[Dependency] protected readonly IEntitySystemManager EntitySystemManager = default!;
|
||||||
[Dependency] protected readonly IGameTiming Timing = default!;
|
[Dependency] protected readonly IGameTiming Timing = default!;
|
||||||
[Dependency] protected readonly IMapManager MapManager = default!;
|
[Dependency] protected readonly IMapManager MapManager = default!;
|
||||||
@@ -68,10 +68,18 @@ namespace Content.Client.State
|
|||||||
if (localPlayer == null)
|
if (localPlayer == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var mousePosWorld = EyeManager.ScreenToMap(InputManager.MouseScreenPosition);
|
IEntity? entityToClick = null;
|
||||||
var entityToClick = UserInterfaceManager.CurrentlyHovered != null
|
var renderScale = 1;
|
||||||
? null
|
if (UserInterfaceManager.CurrentlyHovered is IViewportControl vp)
|
||||||
: GetEntityUnderPosition(mousePosWorld);
|
{
|
||||||
|
var mousePosWorld = vp.ScreenToMap(InputManager.MouseScreenPosition);
|
||||||
|
entityToClick = GetEntityUnderPosition(mousePosWorld);
|
||||||
|
|
||||||
|
if (vp is ScalingViewport svp)
|
||||||
|
{
|
||||||
|
renderScale = svp.CurrentRenderScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var inRange = false;
|
var inRange = false;
|
||||||
if (localPlayer.ControlledEntity != null && entityToClick != null)
|
if (localPlayer.ControlledEntity != null && entityToClick != null)
|
||||||
@@ -93,7 +101,7 @@ namespace Content.Client.State
|
|||||||
{
|
{
|
||||||
if (entityToClick != null && entityToClick.TryGetComponent(out outline))
|
if (entityToClick != null && entityToClick.TryGetComponent(out outline))
|
||||||
{
|
{
|
||||||
outline.UpdateInRange(inRange);
|
outline.UpdateInRange(inRange, renderScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -109,7 +117,7 @@ namespace Content.Client.State
|
|||||||
|
|
||||||
if (_lastHoveredEntity != null && _lastHoveredEntity.TryGetComponent(out outline))
|
if (_lastHoveredEntity != null && _lastHoveredEntity.TryGetComponent(out outline))
|
||||||
{
|
{
|
||||||
outline.OnMouseEnter(inRange);
|
outline.OnMouseEnter(inRange, renderScale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,30 +214,36 @@ namespace Content.Client.State
|
|||||||
/// Converts a state change event from outside the simulation to inside the simulation.
|
/// Converts a state change event from outside the simulation to inside the simulation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="args">Event data values for a bound key state change.</param>
|
/// <param name="args">Event data values for a bound key state change.</param>
|
||||||
private void OnKeyBindStateChanged(BoundKeyEventArgs args)
|
private void OnKeyBindStateChanged(ViewportBoundKeyEventArgs args)
|
||||||
{
|
{
|
||||||
// If there is no InputSystem, then there is nothing to forward to, and nothing to do here.
|
// If there is no InputSystem, then there is nothing to forward to, and nothing to do here.
|
||||||
if(!EntitySystemManager.TryGetEntitySystem(out InputSystem inputSys))
|
if(!EntitySystemManager.TryGetEntitySystem(out InputSystem inputSys))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var func = args.Function;
|
var kArgs = args.KeyEventArgs;
|
||||||
|
var func = kArgs.Function;
|
||||||
var funcId = InputManager.NetworkBindMap.KeyFunctionID(func);
|
var funcId = InputManager.NetworkBindMap.KeyFunctionID(func);
|
||||||
|
|
||||||
var mousePosWorld = EyeManager.ScreenToMap(args.PointerLocation);
|
EntityCoordinates coordinates = default;
|
||||||
var entityToClick = GetEntityUnderPosition(mousePosWorld);
|
EntityUid entityToClick = default;
|
||||||
|
if (args.Viewport is IViewportControl vp)
|
||||||
|
{
|
||||||
|
var mousePosWorld = vp.ScreenToMap(kArgs.PointerLocation.Position);
|
||||||
|
entityToClick = GetEntityUnderPosition(mousePosWorld)?.Uid ?? EntityUid.Invalid;
|
||||||
|
|
||||||
var coordinates = MapManager.TryFindGridAt(mousePosWorld, out var grid) ? grid.MapToGrid(mousePosWorld) :
|
coordinates = MapManager.TryFindGridAt(mousePosWorld, out var grid) ? grid.MapToGrid(mousePosWorld) :
|
||||||
EntityCoordinates.FromMap(EntityManager, MapManager, mousePosWorld);
|
EntityCoordinates.FromMap(EntityManager, MapManager, mousePosWorld);
|
||||||
|
}
|
||||||
|
|
||||||
var message = new FullInputCmdMessage(Timing.CurTick, Timing.TickFraction, funcId, args.State,
|
var message = new FullInputCmdMessage(Timing.CurTick, Timing.TickFraction, funcId, kArgs.State,
|
||||||
coordinates , args.PointerLocation,
|
coordinates , kArgs.PointerLocation,
|
||||||
entityToClick?.Uid ?? EntityUid.Invalid);
|
entityToClick);
|
||||||
|
|
||||||
// client side command handlers will always be sent the local player session.
|
// client side command handlers will always be sent the local player session.
|
||||||
var session = PlayerManager.LocalPlayer?.Session;
|
var session = PlayerManager.LocalPlayer?.Session;
|
||||||
if (inputSys.HandleInputCommand(session, func, message))
|
if (inputSys.HandleInputCommand(session, func, message))
|
||||||
{
|
{
|
||||||
args.Handle();
|
kArgs.Handle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
Content.Client/State/IMainViewport.cs
Normal file
15
Content.Client/State/IMainViewport.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using Content.Client.UserInterface;
|
||||||
|
|
||||||
|
namespace Content.Client.State
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Client state that has a main viewport.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Used for taking no-UI screenshots (including things like flash overlay).
|
||||||
|
/// </remarks>
|
||||||
|
public interface IMainViewportState
|
||||||
|
{
|
||||||
|
public MainViewport Viewport { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,251 +1,81 @@
|
|||||||
using Content.Client.UserInterface.Controls;
|
using System;
|
||||||
using Content.Client.UserInterface.Stylesheets;
|
using Content.Client.UserInterface;
|
||||||
using Content.Client.Utility;
|
|
||||||
using Robust.Client;
|
using Robust.Client;
|
||||||
using Robust.Client.Graphics;
|
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Localization;
|
|
||||||
using Robust.Shared.Maths;
|
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
using static Content.Client.StaticIoC;
|
|
||||||
|
|
||||||
namespace Content.Client.State
|
namespace Content.Client.State
|
||||||
{
|
{
|
||||||
public class LauncherConnecting : Robust.Client.State.State
|
public class LauncherConnecting : Robust.Client.State.State
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||||
[Dependency] private readonly IStylesheetManager _stylesheetManager = default!;
|
|
||||||
[Dependency] private readonly IClientNetManager _clientNetManager = default!;
|
[Dependency] private readonly IClientNetManager _clientNetManager = default!;
|
||||||
[Dependency] private readonly IGameController _gameController = default!;
|
[Dependency] private readonly IGameController _gameController = default!;
|
||||||
[Dependency] private readonly IBaseClient _baseClient = default!;
|
[Dependency] private readonly IBaseClient _baseClient = default!;
|
||||||
|
|
||||||
private Control? _control;
|
private LauncherConnectingGui? _control;
|
||||||
private Label? _connectStatus;
|
|
||||||
|
|
||||||
private Control? _connectingStatus;
|
private Page _currentPage;
|
||||||
private Control? _connectFail;
|
private string? _connectFailReason;
|
||||||
private Label? _connectFailReason;
|
|
||||||
private Control? _disconnected;
|
public string? Address => _gameController.LaunchState.Ss14Address ?? _gameController.LaunchState.ConnectAddress;
|
||||||
|
|
||||||
|
public string? ConnectFailReason
|
||||||
|
{
|
||||||
|
get => _connectFailReason;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
_connectFailReason = value;
|
||||||
|
ConnectFailReasonChanged?.Invoke(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page CurrentPage
|
||||||
|
{
|
||||||
|
get => _currentPage;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
_currentPage = value;
|
||||||
|
PageChanged?.Invoke(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientConnectionState ConnectionState => _clientNetManager.ClientConnectState;
|
||||||
|
|
||||||
|
public event Action<Page>? PageChanged;
|
||||||
|
public event Action<string?>? ConnectFailReasonChanged;
|
||||||
|
public event Action<ClientConnectionState>? ConnectionStateChanged;
|
||||||
|
|
||||||
public override void Startup()
|
public override void Startup()
|
||||||
{
|
{
|
||||||
Button exitButton;
|
_control = new LauncherConnectingGui(this);
|
||||||
Button reconnectButton;
|
|
||||||
Button retryButton;
|
|
||||||
|
|
||||||
var address = _gameController.LaunchState.Ss14Address ?? _gameController.LaunchState.ConnectAddress;
|
|
||||||
|
|
||||||
_control = new Control
|
|
||||||
{
|
|
||||||
Stylesheet = _stylesheetManager.SheetSpace,
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
new PanelContainer {StyleClasses = {StyleBase.ClassAngleRect}},
|
|
||||||
new VBoxContainer
|
|
||||||
{
|
|
||||||
SeparationOverride = 0,
|
|
||||||
MinSize = (300, 200),
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
new HBoxContainer
|
|
||||||
{
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
new Label
|
|
||||||
{
|
|
||||||
Margin = new Thickness(8, 0, 0, 0),
|
|
||||||
Text = Loc.GetString("Space Station 14"),
|
|
||||||
StyleClasses = {StyleBase.StyleClassLabelHeading},
|
|
||||||
VAlign = Label.VAlignMode.Center
|
|
||||||
},
|
|
||||||
|
|
||||||
(exitButton = new Button
|
|
||||||
{
|
|
||||||
Text = Loc.GetString("Exit"),
|
|
||||||
HorizontalAlignment = Control.HAlignment.Right,
|
|
||||||
HorizontalExpand = true,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Line
|
|
||||||
new HighDivider(),
|
|
||||||
|
|
||||||
new VBoxContainer
|
|
||||||
{
|
|
||||||
VerticalExpand = true,
|
|
||||||
Margin = new Thickness(4, 4, 4, 0),
|
|
||||||
SeparationOverride = 0,
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
new Control
|
|
||||||
{
|
|
||||||
VerticalExpand = true,
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
(_connectingStatus = new VBoxContainer
|
|
||||||
{
|
|
||||||
SeparationOverride = 0,
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
new Label
|
|
||||||
{
|
|
||||||
Text = Loc.GetString("Connecting to server..."),
|
|
||||||
Align = Label.AlignMode.Center,
|
|
||||||
},
|
|
||||||
|
|
||||||
(_connectStatus = new Label
|
|
||||||
{
|
|
||||||
StyleClasses = {StyleBase.StyleClassLabelSubText},
|
|
||||||
Align = Label.AlignMode.Center,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
(_connectFail = new VBoxContainer
|
|
||||||
{
|
|
||||||
Visible = false,
|
|
||||||
SeparationOverride = 0,
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
(_connectFailReason = new Label
|
|
||||||
{
|
|
||||||
Align = Label.AlignMode.Center
|
|
||||||
}),
|
|
||||||
|
|
||||||
(retryButton = new Button
|
|
||||||
{
|
|
||||||
Text = "Retry",
|
|
||||||
HorizontalAlignment = Control.HAlignment.Center,
|
|
||||||
VerticalExpand = true,
|
|
||||||
VerticalAlignment = Control.VAlignment.Bottom,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
(_disconnected = new VBoxContainer
|
|
||||||
{
|
|
||||||
SeparationOverride = 0,
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
new Label
|
|
||||||
{
|
|
||||||
Text = "Disconnected from server:",
|
|
||||||
Align = Label.AlignMode.Center
|
|
||||||
},
|
|
||||||
new Label
|
|
||||||
{
|
|
||||||
Text = _baseClient.LastDisconnectReason,
|
|
||||||
Align = Label.AlignMode.Center
|
|
||||||
},
|
|
||||||
(reconnectButton = new Button
|
|
||||||
{
|
|
||||||
Text = "Reconnect",
|
|
||||||
HorizontalAlignment = Control.HAlignment.Center,
|
|
||||||
VerticalExpand = true,
|
|
||||||
VerticalAlignment = Control.VAlignment.Bottom,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Padding.
|
|
||||||
new Control {MinSize = (0, 8)},
|
|
||||||
|
|
||||||
new Label
|
|
||||||
{
|
|
||||||
Text = address,
|
|
||||||
StyleClasses = {StyleBase.StyleClassLabelSubText},
|
|
||||||
HorizontalAlignment = Control.HAlignment.Center
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Line
|
|
||||||
new PanelContainer
|
|
||||||
{
|
|
||||||
PanelOverride = new StyleBoxFlat
|
|
||||||
{
|
|
||||||
BackgroundColor = Color.FromHex("#444"),
|
|
||||||
ContentMarginTopOverride = 2
|
|
||||||
},
|
|
||||||
},
|
|
||||||
new HBoxContainer
|
|
||||||
{
|
|
||||||
Margin = new Thickness(12, 0, 4, 0),
|
|
||||||
VerticalAlignment = Control.VAlignment.Bottom,
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
new Label
|
|
||||||
{
|
|
||||||
Text = Loc.GetString("Don't die!"),
|
|
||||||
StyleClasses = {StyleBase.StyleClassLabelSubText}
|
|
||||||
},
|
|
||||||
new Label
|
|
||||||
{
|
|
||||||
Text = "ver 0.1",
|
|
||||||
HorizontalExpand = true,
|
|
||||||
HorizontalAlignment = Control.HAlignment.Right,
|
|
||||||
StyleClasses = {StyleBase.StyleClassLabelSubText}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
_userInterfaceManager.StateRoot.AddChild(_control);
|
_userInterfaceManager.StateRoot.AddChild(_control);
|
||||||
|
|
||||||
LayoutContainer.SetAnchorPreset(_control, LayoutContainer.LayoutPreset.Center);
|
_clientNetManager.ConnectFailed += (_, args) =>
|
||||||
LayoutContainer.SetGrowHorizontal(_control, LayoutContainer.GrowDirection.Both);
|
|
||||||
LayoutContainer.SetGrowVertical(_control, LayoutContainer.GrowDirection.Both);
|
|
||||||
|
|
||||||
exitButton.OnPressed += _ =>
|
|
||||||
{
|
{
|
||||||
_gameController.Shutdown("Exit button pressed");
|
ConnectFailReason = args.Reason;
|
||||||
|
CurrentPage = Page.ConnectFailed;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Retry(BaseButton.ButtonEventArgs args)
|
_clientNetManager.ClientConnectStateChanged += state => ConnectionStateChanged?.Invoke(state);
|
||||||
|
|
||||||
|
CurrentPage = Page.Connecting;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RetryConnect()
|
||||||
{
|
{
|
||||||
if (_gameController.LaunchState.ConnectEndpoint != null)
|
if (_gameController.LaunchState.ConnectEndpoint != null)
|
||||||
{
|
{
|
||||||
_baseClient.ConnectToServer(_gameController.LaunchState.ConnectEndpoint);
|
_baseClient.ConnectToServer(_gameController.LaunchState.ConnectEndpoint);
|
||||||
SetActivePage(Page.Connecting);
|
CurrentPage = Page.Connecting;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reconnectButton.OnPressed += Retry;
|
public void Exit()
|
||||||
retryButton.OnPressed += Retry;
|
|
||||||
|
|
||||||
_clientNetManager.ConnectFailed += (_, args) =>
|
|
||||||
{
|
{
|
||||||
_connectFailReason.Text = Loc.GetString("Failed to connect to server:\n{0}", args.Reason);
|
_gameController.Shutdown("Exit button pressed");
|
||||||
SetActivePage(Page.ConnectFailed);
|
|
||||||
};
|
|
||||||
|
|
||||||
_clientNetManager.ClientConnectStateChanged += ConnectStateChanged;
|
|
||||||
|
|
||||||
SetActivePage(Page.Connecting);
|
|
||||||
|
|
||||||
ConnectStateChanged(_clientNetManager.ClientConnectState);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConnectStateChanged(ClientConnectionState state)
|
|
||||||
{
|
|
||||||
if (_connectStatus == null) return;
|
|
||||||
|
|
||||||
_connectStatus.Text = Loc.GetString(state switch
|
|
||||||
{
|
|
||||||
ClientConnectionState.NotConnecting => "You should not be seeing this",
|
|
||||||
ClientConnectionState.ResolvingHost => "Resolving server address...",
|
|
||||||
ClientConnectionState.EstablishingConnection => "Establishing initial connection...",
|
|
||||||
ClientConnectionState.Handshake => "Doing handshake...",
|
|
||||||
ClientConnectionState.Connected => "Synchronizing game state...",
|
|
||||||
_ => state.ToString()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Shutdown()
|
public override void Shutdown()
|
||||||
@@ -255,17 +85,10 @@ namespace Content.Client.State
|
|||||||
|
|
||||||
public void SetDisconnected()
|
public void SetDisconnected()
|
||||||
{
|
{
|
||||||
SetActivePage(Page.Disconnected);
|
CurrentPage = Page.Disconnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetActivePage(Page page)
|
public enum Page : byte
|
||||||
{
|
|
||||||
if (_connectingStatus != null) _connectingStatus.Visible = page == Page.Connecting;
|
|
||||||
if (_connectFail != null) _connectFail.Visible = page == Page.ConnectFailed;
|
|
||||||
if (_disconnected != null) _disconnected.Visible = page == Page.Disconnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum Page : byte
|
|
||||||
{
|
{
|
||||||
Connecting,
|
Connecting,
|
||||||
ConnectFailed,
|
ConnectFailed,
|
||||||
|
|||||||
@@ -215,6 +215,8 @@ namespace Content.Client.State
|
|||||||
|
|
||||||
LayoutContainer.SetAnchorPreset(this, LayoutContainer.LayoutPreset.Wide);
|
LayoutContainer.SetAnchorPreset(this, LayoutContainer.LayoutPreset.Wide);
|
||||||
|
|
||||||
|
AddChild(new ParallaxControl());
|
||||||
|
|
||||||
var layout = new LayoutContainer();
|
var layout = new LayoutContainer();
|
||||||
AddChild(layout);
|
AddChild(layout);
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ namespace Content.Client.StationEvents
|
|||||||
_alphaRateOfChange[entity] = 1.0f / (float) (transitionTime - currentTime).TotalSeconds;
|
_alphaRateOfChange[entity] = 1.0f / (float) (transitionTime - currentTime).TotalSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleBase handle, OverlaySpace currentSpace)
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
{
|
{
|
||||||
// PVS should control the overlay pretty well so the overlay doesn't get instantiated unless we're near one...
|
// PVS should control the overlay pretty well so the overlay doesn't get instantiated unless we're near one...
|
||||||
var playerEntity = _playerManager.LocalPlayer?.ControlledEntity;
|
var playerEntity = _playerManager.LocalPlayer?.ControlledEntity;
|
||||||
@@ -124,7 +124,7 @@ namespace Content.Client.StationEvents
|
|||||||
.EntityQuery<RadiationPulseComponent>(true)
|
.EntityQuery<RadiationPulseComponent>(true)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var screenHandle = (DrawingHandleScreen) handle;
|
var screenHandle = args.ScreenHandle;
|
||||||
var viewport = _eyeManager.GetWorldViewport();
|
var viewport = _eyeManager.GetWorldViewport();
|
||||||
|
|
||||||
foreach (var grid in _mapManager.FindGridsIntersecting(playerEntity.Transform.MapID, viewport))
|
foreach (var grid in _mapManager.FindGridsIntersecting(playerEntity.Transform.MapID, viewport))
|
||||||
@@ -134,7 +134,7 @@ namespace Content.Client.StationEvents
|
|||||||
if (!pulse.Draw || grid.Index != pulse.Owner.Transform.GridID) continue;
|
if (!pulse.Draw || grid.Index != pulse.Owner.Transform.GridID) continue;
|
||||||
|
|
||||||
// TODO: Check if viewport intersects circle
|
// TODO: Check if viewport intersects circle
|
||||||
var circlePosition = _eyeManager.WorldToScreen(pulse.Owner.Transform.WorldPosition);
|
var circlePosition = args.ViewportControl!.WorldToScreen(pulse.Owner.Transform.WorldPosition);
|
||||||
|
|
||||||
// change to worldhandle when implemented
|
// change to worldhandle when implemented
|
||||||
screenHandle.DrawCircle(
|
screenHandle.DrawCircle(
|
||||||
|
|||||||
@@ -29,11 +29,14 @@ namespace Content.Client.UserInterface
|
|||||||
public readonly Button CloseButton;
|
public readonly Button CloseButton;
|
||||||
public readonly Button SaveButton;
|
public readonly Button SaveButton;
|
||||||
|
|
||||||
public CharacterSetupGui(IEntityManager entityManager,
|
public CharacterSetupGui(
|
||||||
|
IEntityManager entityManager,
|
||||||
IResourceCache resourceCache,
|
IResourceCache resourceCache,
|
||||||
IClientPreferencesManager preferencesManager,
|
IClientPreferencesManager preferencesManager,
|
||||||
IPrototypeManager prototypeManager)
|
IPrototypeManager prototypeManager)
|
||||||
{
|
{
|
||||||
|
AddChild(new ParallaxControl());
|
||||||
|
|
||||||
_entityManager = entityManager;
|
_entityManager = entityManager;
|
||||||
_preferencesManager = preferencesManager;
|
_preferencesManager = preferencesManager;
|
||||||
var margin = new Control
|
var margin = new Control
|
||||||
|
|||||||
53
Content.Client/UserInterface/LauncherConnectingGui.xaml
Normal file
53
Content.Client/UserInterface/LauncherConnectingGui.xaml
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<Control xmlns="https://spacestation14.io"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:cui="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
|
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||||
|
xmlns:cuic="clr-namespace:Content.Client.UserInterface">
|
||||||
|
<cuic:ParallaxControl />
|
||||||
|
<Control HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
|
<PanelContainer StyleClasses="AngleRect" />
|
||||||
|
<VBoxContainer MinSize="300 200">
|
||||||
|
<HBoxContainer>
|
||||||
|
<Label Margin="8 0 0 0" Text="{Loc 'connecting-title'}"
|
||||||
|
StyleClasses="LabelHeading" VAlign="Center" />
|
||||||
|
<Button Name="ExitButton" Text="{Loc 'connecting-exit'}"
|
||||||
|
HorizontalAlignment="Right" HorizontalExpand="True" />
|
||||||
|
</HBoxContainer>
|
||||||
|
<cui:HighDivider />
|
||||||
|
<VBoxContainer VerticalExpand="True" Margin="4 4 4 0">
|
||||||
|
<Control VerticalExpand="True" Margin="0 0 0 8">
|
||||||
|
<VBoxContainer Name="ConnectingStatus">
|
||||||
|
<Label Text="{Loc 'connecting-in-progress'}" Align="Center" />
|
||||||
|
<!-- Who the fuck named these cont- oh wait I did -->
|
||||||
|
<Label Name="ConnectStatus" StyleClasses="LabelSubText" Align="Center" />
|
||||||
|
</VBoxContainer>
|
||||||
|
<VBoxContainer Name="ConnectFail" Visible="False">
|
||||||
|
<Label Name="ConnectFailReason" Align="Center" />
|
||||||
|
<Button Name="RetryButton" Text="{Loc 'connecting-retry'}"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalExpand="True" VerticalAlignment="Bottom" />
|
||||||
|
</VBoxContainer>
|
||||||
|
<VBoxContainer Name="Disconnected">
|
||||||
|
<Label Text="{Loc 'connecting-disconnected'}" Align="Center" />
|
||||||
|
<Label Name="DisconnectReason" Align="Center" />
|
||||||
|
<Button Name="ReconnectButton" Text="{Loc 'connecting-reconnect'}"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalExpand="True" VerticalAlignment="Bottom" />
|
||||||
|
</VBoxContainer>
|
||||||
|
</Control>
|
||||||
|
<Label Name="ConnectingAddress" StyleClasses="LabelSubText" HorizontalAlignment="Center" />
|
||||||
|
</VBoxContainer>
|
||||||
|
<PanelContainer>
|
||||||
|
<PanelContainer.PanelOverride>
|
||||||
|
<gfx:StyleBoxFlat BackgroundColor="#444" ContentMarginTopOverride="2" />
|
||||||
|
</PanelContainer.PanelOverride>
|
||||||
|
</PanelContainer>
|
||||||
|
|
||||||
|
<HBoxContainer Margin="12 0 4 0" VerticalAlignment="Bottom">
|
||||||
|
<Label Text="{Loc 'connecting-tip'}" StyleClasses="LabelSubText" />
|
||||||
|
<Label Text="{Loc 'connecting-version'}" StyleClasses="LabelSubText"
|
||||||
|
HorizontalAlignment="Right" HorizontalExpand="True" />
|
||||||
|
</HBoxContainer>
|
||||||
|
</VBoxContainer>
|
||||||
|
</Control>
|
||||||
|
</Control>
|
||||||
62
Content.Client/UserInterface/LauncherConnectingGui.xaml.cs
Normal file
62
Content.Client/UserInterface/LauncherConnectingGui.xaml.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using Content.Client.State;
|
||||||
|
using Content.Client.UserInterface.Stylesheets;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Network;
|
||||||
|
|
||||||
|
namespace Content.Client.UserInterface
|
||||||
|
{
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class LauncherConnectingGui : Control
|
||||||
|
{
|
||||||
|
private readonly LauncherConnecting _state;
|
||||||
|
|
||||||
|
public LauncherConnectingGui(LauncherConnecting state)
|
||||||
|
{
|
||||||
|
_state = state;
|
||||||
|
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
|
||||||
|
LayoutContainer.SetAnchorPreset(this, LayoutContainer.LayoutPreset.Wide);
|
||||||
|
|
||||||
|
Stylesheet = IoCManager.Resolve<IStylesheetManager>().SheetSpace;
|
||||||
|
|
||||||
|
ReconnectButton.OnPressed += _ => _state.RetryConnect();
|
||||||
|
RetryButton.OnPressed += _ => _state.RetryConnect();
|
||||||
|
ExitButton.OnPressed += _ => _state.Exit();
|
||||||
|
|
||||||
|
var addr = state.Address;
|
||||||
|
if (addr != null)
|
||||||
|
ConnectingAddress.Text = addr;
|
||||||
|
|
||||||
|
state.PageChanged += OnPageChanged;
|
||||||
|
state.ConnectFailReasonChanged += ConnectFailReasonChanged;
|
||||||
|
state.ConnectionStateChanged += ConnectionStateChanged;
|
||||||
|
|
||||||
|
ConnectionStateChanged(state.ConnectionState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConnectFailReasonChanged(string? reason)
|
||||||
|
{
|
||||||
|
ConnectFailReason.Text = reason == null
|
||||||
|
? null
|
||||||
|
: Loc.GetString("connecting-fail-reason", ("reason", reason));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPageChanged(LauncherConnecting.Page page)
|
||||||
|
{
|
||||||
|
ConnectingStatus.Visible = page == LauncherConnecting.Page.Connecting;
|
||||||
|
ConnectFail.Visible = page == LauncherConnecting.Page.ConnectFailed;
|
||||||
|
Disconnected.Visible = page == LauncherConnecting.Page.Disconnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConnectionStateChanged(ClientConnectionState state)
|
||||||
|
{
|
||||||
|
ConnectStatus.Text = Loc.GetString($"connecting-state-{state}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,10 @@
|
|||||||
xmlns:maths="clr-namespace:Robust.Shared.Maths;assembly=Robust.Shared.Maths"
|
xmlns:maths="clr-namespace:Robust.Shared.Maths;assembly=Robust.Shared.Maths"
|
||||||
xmlns:voting="clr-namespace:Content.Client.Voting">
|
xmlns:voting="clr-namespace:Content.Client.Voting">
|
||||||
|
|
||||||
|
<Control>
|
||||||
|
<!-- Parallax background -->
|
||||||
|
<cui:ParallaxControl />
|
||||||
|
|
||||||
<!-- One day I'll code a Margin property for controls. -->
|
<!-- One day I'll code a Margin property for controls. -->
|
||||||
<MarginContainer MarginBottomOverride="20" MarginLeftOverride="20" MarginRightOverride="20"
|
<MarginContainer MarginBottomOverride="20" MarginLeftOverride="20" MarginRightOverride="20"
|
||||||
MarginTopOverride="20">
|
MarginTopOverride="20">
|
||||||
@@ -85,4 +89,6 @@
|
|||||||
</HBoxContainer>
|
</HBoxContainer>
|
||||||
</VBoxContainer>
|
</VBoxContainer>
|
||||||
</MarginContainer>
|
</MarginContainer>
|
||||||
|
|
||||||
|
</Control>
|
||||||
</Control>
|
</Control>
|
||||||
|
|||||||
153
Content.Client/UserInterface/MainViewport.cs
Normal file
153
Content.Client/UserInterface/MainViewport.cs
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
using System;
|
||||||
|
using Content.Shared;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
|
||||||
|
namespace Content.Client.UserInterface
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Wrapper for <see cref="ScalingViewport"/> that listens to configuration variables.
|
||||||
|
/// Also does NN-snapping within tolerances.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class MainViewport : Control
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||||
|
[Dependency] private readonly ViewportManager _vpManager = default!;
|
||||||
|
|
||||||
|
public ScalingViewport Viewport { get; }
|
||||||
|
|
||||||
|
public MainViewport()
|
||||||
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
|
Viewport = new ScalingViewport
|
||||||
|
{
|
||||||
|
AlwaysRender = true,
|
||||||
|
RenderScaleMode = ScalingViewportRenderScaleMode.CeilInt,
|
||||||
|
MouseFilter = MouseFilterMode.Stop
|
||||||
|
};
|
||||||
|
|
||||||
|
AddChild(Viewport);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void EnteredTree()
|
||||||
|
{
|
||||||
|
base.EnteredTree();
|
||||||
|
|
||||||
|
_vpManager.AddViewport(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ExitedTree()
|
||||||
|
{
|
||||||
|
base.ExitedTree();
|
||||||
|
|
||||||
|
_vpManager.RemoveViewport(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateCfg()
|
||||||
|
{
|
||||||
|
var stretch = _cfg.GetCVar(CCVars.ViewportStretch);
|
||||||
|
var renderScaleUp = _cfg.GetCVar(CCVars.ViewportScaleRender);
|
||||||
|
var fixedFactor = _cfg.GetCVar(CCVars.ViewportFixedScaleFactor);
|
||||||
|
|
||||||
|
if (stretch)
|
||||||
|
{
|
||||||
|
var snapFactor = CalcSnappingFactor();
|
||||||
|
if (snapFactor == null)
|
||||||
|
{
|
||||||
|
// Did not find a snap, enable stretching.
|
||||||
|
Viewport.FixedStretchSize = null;
|
||||||
|
Viewport.StretchMode = ScalingViewportStretchMode.Bilinear;
|
||||||
|
|
||||||
|
if (renderScaleUp)
|
||||||
|
{
|
||||||
|
Viewport.RenderScaleMode = ScalingViewportRenderScaleMode.CeilInt;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Viewport.RenderScaleMode = ScalingViewportRenderScaleMode.Fixed;
|
||||||
|
Viewport.FixedRenderScale = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Found snap, set fixed factor and run non-stretching code.
|
||||||
|
fixedFactor = snapFactor.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Viewport.FixedStretchSize = Viewport.ViewportSize * fixedFactor;
|
||||||
|
Viewport.StretchMode = ScalingViewportStretchMode.Nearest;
|
||||||
|
|
||||||
|
if (renderScaleUp)
|
||||||
|
{
|
||||||
|
Viewport.RenderScaleMode = ScalingViewportRenderScaleMode.Fixed;
|
||||||
|
Viewport.FixedRenderScale = fixedFactor;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Snapping but forced to render scale at scale 1 so...
|
||||||
|
// At least we can NN.
|
||||||
|
Viewport.RenderScaleMode = ScalingViewportRenderScaleMode.Fixed;
|
||||||
|
Viewport.FixedRenderScale = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int? CalcSnappingFactor()
|
||||||
|
{
|
||||||
|
// Margin tolerance is tolerance of "the window is too big"
|
||||||
|
// where we add a margin to the viewport to make it fit.
|
||||||
|
var cfgToleranceMargin = _cfg.GetCVar(CCVars.ViewportSnapToleranceMargin);
|
||||||
|
// Clip tolerance is tolerance of "the window is too small"
|
||||||
|
// where we are clipping the viewport to make it fit.
|
||||||
|
var cfgToleranceClip = _cfg.GetCVar(CCVars.ViewportSnapToleranceClip);
|
||||||
|
|
||||||
|
// Calculate if the viewport, when rendered at an integer scale,
|
||||||
|
// is close enough to the control size to enable "snapping" to NN,
|
||||||
|
// potentially cutting a tiny bit off/leaving a margin.
|
||||||
|
//
|
||||||
|
// Idea here is that if you maximize the window at 1080p or 1440p
|
||||||
|
// we are close enough to an integer scale (2x and 3x resp) that we should "snap" to it.
|
||||||
|
|
||||||
|
// Just do it iteratively.
|
||||||
|
// I'm sure there's a smarter approach that needs one try with math but I'm dumb.
|
||||||
|
for (var i = 1; i <= 10; i++)
|
||||||
|
{
|
||||||
|
var toleranceMargin = i * cfgToleranceMargin;
|
||||||
|
var toleranceClip = i * cfgToleranceClip;
|
||||||
|
var scaled = (Vector2) Viewport.ViewportSize * i;
|
||||||
|
var (dx, dy) = PixelSize - scaled;
|
||||||
|
|
||||||
|
// The rule for which snap fits is that at LEAST one axis needs to be in the tolerance size wise.
|
||||||
|
// One axis MAY be larger but not smaller than tolerance.
|
||||||
|
// Obviously if it's too small it's bad, and if it's too big on both axis we should stretch up.
|
||||||
|
if (Fits(dx) && Fits(dy) || Fits(dx) && Larger(dy) || Larger(dx) && Fits(dy))
|
||||||
|
{
|
||||||
|
// Found snap that fits.
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Larger(float a)
|
||||||
|
{
|
||||||
|
return a > toleranceMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Fits(float a)
|
||||||
|
{
|
||||||
|
return a <= toleranceMargin && a >= -toleranceClip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Resized()
|
||||||
|
{
|
||||||
|
base.Resized();
|
||||||
|
|
||||||
|
UpdateCfg();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,10 @@
|
|||||||
using System;
|
|
||||||
using Content.Client.GameObjects.Components.HUD.Inventory;
|
|
||||||
using Content.Shared;
|
using Content.Shared;
|
||||||
using Content.Shared.Prototypes.HUD;
|
using Content.Shared.Prototypes.HUD;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.ResourceManagement;
|
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Shared;
|
using Robust.Shared;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
@@ -39,6 +35,11 @@ namespace Content.Client.UserInterface
|
|||||||
private readonly OptionButton LightingPresetOption;
|
private readonly OptionButton LightingPresetOption;
|
||||||
private readonly OptionButton _uiScaleOption;
|
private readonly OptionButton _uiScaleOption;
|
||||||
private readonly OptionButton _hudThemeOption;
|
private readonly OptionButton _hudThemeOption;
|
||||||
|
private readonly CheckBox _viewportStretchCheckBox;
|
||||||
|
private readonly CheckBox _viewportLowResCheckBox;
|
||||||
|
private readonly Slider _viewportScaleSlider;
|
||||||
|
private readonly Control _viewportScaleBox;
|
||||||
|
private readonly Label _viewportScaleText;
|
||||||
|
|
||||||
public GraphicsControl(IConfigurationManager cfg, IPrototypeManager proMan)
|
public GraphicsControl(IConfigurationManager cfg, IPrototypeManager proMan)
|
||||||
{
|
{
|
||||||
@@ -121,12 +122,55 @@ namespace Content.Client.UserInterface
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
contents.AddChild(new Placeholder()
|
_viewportStretchCheckBox = new CheckBox
|
||||||
{
|
{
|
||||||
VerticalExpand = true,
|
Text = Loc.GetString("ui-options-vp-stretch")
|
||||||
PlaceholderText = Loc.GetString("ui-options-placeholder-viewport")
|
};
|
||||||
|
|
||||||
|
_viewportStretchCheckBox.OnToggled += _ =>
|
||||||
|
{
|
||||||
|
UpdateViewportScale();
|
||||||
|
UpdateApplyButton();
|
||||||
|
};
|
||||||
|
|
||||||
|
_viewportScaleSlider = new Slider
|
||||||
|
{
|
||||||
|
MinValue = 1,
|
||||||
|
MaxValue = 5,
|
||||||
|
Rounded = true,
|
||||||
|
MinWidth = 200
|
||||||
|
};
|
||||||
|
|
||||||
|
_viewportScaleSlider.OnValueChanged += _ =>
|
||||||
|
{
|
||||||
|
UpdateApplyButton();
|
||||||
|
UpdateViewportScale();
|
||||||
|
};
|
||||||
|
|
||||||
|
_viewportLowResCheckBox = new CheckBox { Text = Loc.GetString("ui-options-vp-low-res")};
|
||||||
|
_viewportLowResCheckBox.OnToggled += OnCheckBoxToggled;
|
||||||
|
|
||||||
|
contents.AddChild(new HBoxContainer
|
||||||
|
{
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
_viewportStretchCheckBox,
|
||||||
|
(_viewportScaleBox = new HBoxContainer
|
||||||
|
{
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
(_viewportScaleText = new Label
|
||||||
|
{
|
||||||
|
Margin = new Thickness(8, 0)
|
||||||
|
}),
|
||||||
|
_viewportScaleSlider,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
contents.AddChild(_viewportLowResCheckBox);
|
||||||
|
|
||||||
vBox.AddChild(contents);
|
vBox.AddChild(contents);
|
||||||
|
|
||||||
vBox.AddChild(new StripeBack
|
vBox.AddChild(new StripeBack
|
||||||
@@ -145,6 +189,12 @@ namespace Content.Client.UserInterface
|
|||||||
LightingPresetOption.SelectId(GetConfigLightingQuality());
|
LightingPresetOption.SelectId(GetConfigLightingQuality());
|
||||||
_uiScaleOption.SelectId(GetConfigUIScalePreset(ConfigUIScale));
|
_uiScaleOption.SelectId(GetConfigUIScalePreset(ConfigUIScale));
|
||||||
_hudThemeOption.SelectId(_cfg.GetCVar(CCVars.HudTheme));
|
_hudThemeOption.SelectId(_cfg.GetCVar(CCVars.HudTheme));
|
||||||
|
_viewportScaleSlider.Value = _cfg.GetCVar(CCVars.ViewportFixedScaleFactor);
|
||||||
|
_viewportStretchCheckBox.Pressed = _cfg.GetCVar(CCVars.ViewportStretch);
|
||||||
|
_viewportLowResCheckBox.Pressed = !_cfg.GetCVar(CCVars.ViewportScaleRender);
|
||||||
|
|
||||||
|
UpdateViewportScale();
|
||||||
|
UpdateApplyButton();
|
||||||
|
|
||||||
AddChild(vBox);
|
AddChild(vBox);
|
||||||
}
|
}
|
||||||
@@ -173,6 +223,9 @@ namespace Content.Client.UserInterface
|
|||||||
_cfg.SetCVar(CVars.DisplayWindowMode,
|
_cfg.SetCVar(CVars.DisplayWindowMode,
|
||||||
(int) (FullscreenCheckBox.Pressed ? WindowMode.Fullscreen : WindowMode.Windowed));
|
(int) (FullscreenCheckBox.Pressed ? WindowMode.Fullscreen : WindowMode.Windowed));
|
||||||
_cfg.SetCVar(CVars.DisplayUIScale, UIScaleOptions[_uiScaleOption.SelectedId]);
|
_cfg.SetCVar(CVars.DisplayUIScale, UIScaleOptions[_uiScaleOption.SelectedId]);
|
||||||
|
_cfg.SetCVar(CCVars.ViewportStretch, _viewportStretchCheckBox.Pressed);
|
||||||
|
_cfg.SetCVar(CCVars.ViewportFixedScaleFactor, (int) _viewportScaleSlider.Value);
|
||||||
|
_cfg.SetCVar(CCVars.ViewportScaleRender, !_viewportLowResCheckBox.Pressed);
|
||||||
_cfg.SaveToFile();
|
_cfg.SaveToFile();
|
||||||
UpdateApplyButton();
|
UpdateApplyButton();
|
||||||
}
|
}
|
||||||
@@ -195,8 +248,18 @@ namespace Content.Client.UserInterface
|
|||||||
var isLightingQualitySame = LightingPresetOption.SelectedId == GetConfigLightingQuality();
|
var isLightingQualitySame = LightingPresetOption.SelectedId == GetConfigLightingQuality();
|
||||||
var isHudThemeSame = _hudThemeOption.SelectedId == _cfg.GetCVar(CCVars.HudTheme);
|
var isHudThemeSame = _hudThemeOption.SelectedId == _cfg.GetCVar(CCVars.HudTheme);
|
||||||
var isUIScaleSame = MathHelper.CloseTo(UIScaleOptions[_uiScaleOption.SelectedId], ConfigUIScale);
|
var isUIScaleSame = MathHelper.CloseTo(UIScaleOptions[_uiScaleOption.SelectedId], ConfigUIScale);
|
||||||
ApplyButton.Disabled = isVSyncSame && isFullscreenSame && isLightingQualitySame && isHudThemeSame &&
|
var isVPStretchSame = _viewportStretchCheckBox.Pressed == _cfg.GetCVar(CCVars.ViewportStretch);
|
||||||
isUIScaleSame;
|
var isVPScaleSame = (int) _viewportScaleSlider.Value == _cfg.GetCVar(CCVars.ViewportFixedScaleFactor);
|
||||||
|
var isVPResSame = _viewportLowResCheckBox.Pressed == !_cfg.GetCVar(CCVars.ViewportScaleRender);
|
||||||
|
|
||||||
|
ApplyButton.Disabled = isVSyncSame &&
|
||||||
|
isFullscreenSame &&
|
||||||
|
isLightingQualitySame &&
|
||||||
|
isUIScaleSame &&
|
||||||
|
isVPStretchSame &&
|
||||||
|
isVPScaleSame &&
|
||||||
|
isVPResSame &&
|
||||||
|
isHudThemeSame;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ConfigIsFullscreen =>
|
private bool ConfigIsFullscreen =>
|
||||||
@@ -261,6 +324,12 @@ namespace Content.Client.UserInterface
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateViewportScale()
|
||||||
|
{
|
||||||
|
_viewportScaleBox.Visible = !_viewportStretchCheckBox.Pressed;
|
||||||
|
_viewportScaleText.Text = Loc.GetString("ui-options-vp-scale", ("scale", _viewportScaleSlider.Value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
47
Content.Client/UserInterface/ParallaxControl.cs
Normal file
47
Content.Client/UserInterface/ParallaxControl.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using Content.Client.Interfaces.Parallax;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
|
namespace Content.Client.UserInterface
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Renders the parallax background as a UI control.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ParallaxControl : Control
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IParallaxManager _parallaxManager = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)] public Vector2i Offset { get; set; }
|
||||||
|
|
||||||
|
public ParallaxControl()
|
||||||
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
|
Offset = (_random.Next(0, 1000), _random.Next(0, 1000));
|
||||||
|
RectClipContent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(DrawingHandleScreen handle)
|
||||||
|
{
|
||||||
|
var tex = _parallaxManager.ParallaxTexture;
|
||||||
|
if (tex == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var size = tex.Size;
|
||||||
|
var ourSize = PixelSize;
|
||||||
|
|
||||||
|
for (var x = -size.X + Offset.X; x < ourSize.X; x += size.X)
|
||||||
|
{
|
||||||
|
for (var y = -size.Y + Offset.Y; y < ourSize.Y; y += size.Y)
|
||||||
|
{
|
||||||
|
handle.DrawTexture(tex, (x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
340
Content.Client/UserInterface/ScalingViewport.cs
Normal file
340
Content.Client/UserInterface/ScalingViewport.cs
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.Input;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
|
||||||
|
namespace Content.Client.UserInterface
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Viewport control that has a fixed viewport size and scales it appropriately.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ScalingViewport : Control, IViewportControl
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IClyde _clyde = default!;
|
||||||
|
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||||
|
|
||||||
|
// Internal viewport creation is deferred.
|
||||||
|
private IClydeViewport? _viewport;
|
||||||
|
private IEye? _eye;
|
||||||
|
private Vector2i _viewportSize;
|
||||||
|
private int _curRenderScale;
|
||||||
|
private ScalingViewportStretchMode _stretchMode = ScalingViewportStretchMode.Bilinear;
|
||||||
|
private ScalingViewportRenderScaleMode _renderScaleMode = ScalingViewportRenderScaleMode.Fixed;
|
||||||
|
private int _fixedRenderScale = 1;
|
||||||
|
|
||||||
|
private readonly List<CopyPixelsDelegate<Rgba32>> _queuedScreenshots = new();
|
||||||
|
|
||||||
|
public int CurrentRenderScale => _curRenderScale;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The eye to render.
|
||||||
|
/// </summary>
|
||||||
|
public IEye? Eye
|
||||||
|
{
|
||||||
|
get => _eye;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_eye = value;
|
||||||
|
|
||||||
|
if (_viewport != null)
|
||||||
|
_viewport.Eye = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The size, in unscaled pixels, of the internal viewport.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The actual viewport may have render scaling applied based on parameters.
|
||||||
|
/// </remarks>
|
||||||
|
public Vector2i ViewportSize
|
||||||
|
{
|
||||||
|
get => _viewportSize;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_viewportSize = value;
|
||||||
|
InvalidateViewport();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not need to InvalidateViewport() since it doesn't affect viewport creation.
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)] public Vector2i? FixedStretchSize { get; set; }
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public ScalingViewportStretchMode StretchMode
|
||||||
|
{
|
||||||
|
get => _stretchMode;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_stretchMode = value;
|
||||||
|
InvalidateViewport();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public ScalingViewportRenderScaleMode RenderScaleMode
|
||||||
|
{
|
||||||
|
get => _renderScaleMode;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_renderScaleMode = value;
|
||||||
|
InvalidateViewport();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public int FixedRenderScale
|
||||||
|
{
|
||||||
|
get => _fixedRenderScale;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_fixedRenderScale = value;
|
||||||
|
InvalidateViewport();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScalingViewport()
|
||||||
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
RectClipContent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void KeyBindDown(GUIBoundKeyEventArgs args)
|
||||||
|
{
|
||||||
|
base.KeyBindDown(args);
|
||||||
|
|
||||||
|
if (args.Handled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_inputManager.ViewportKeyEvent(this, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
|
||||||
|
{
|
||||||
|
base.KeyBindUp(args);
|
||||||
|
|
||||||
|
if (args.Handled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_inputManager.ViewportKeyEvent(this, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected override void FrameUpdate(FrameEventArgs args)
|
||||||
|
{
|
||||||
|
EnsureViewportCreated();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(DrawingHandleScreen handle)
|
||||||
|
{
|
||||||
|
DebugTools.AssertNotNull(_viewport);
|
||||||
|
|
||||||
|
_viewport!.Render();
|
||||||
|
|
||||||
|
if (_queuedScreenshots.Count != 0)
|
||||||
|
{
|
||||||
|
var callbacks = _queuedScreenshots.ToArray();
|
||||||
|
|
||||||
|
_viewport.RenderTarget.CopyPixelsToMemory<Rgba32>(image =>
|
||||||
|
{
|
||||||
|
foreach (var callback in callbacks)
|
||||||
|
{
|
||||||
|
callback(image);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_queuedScreenshots.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
var drawBox = GetDrawBox();
|
||||||
|
var drawBoxGlobal = drawBox.Translated(GlobalPixelPosition);
|
||||||
|
_viewport.RenderScreenOverlaysBelow(handle, this, drawBoxGlobal);
|
||||||
|
handle.DrawTextureRect(_viewport.RenderTarget.Texture, drawBox);
|
||||||
|
_viewport.RenderScreenOverlaysAbove(handle, this, drawBoxGlobal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Screenshot(CopyPixelsDelegate<Rgba32> callback)
|
||||||
|
{
|
||||||
|
_queuedScreenshots.Add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw box in pixel coords to draw the viewport at.
|
||||||
|
private UIBox2i GetDrawBox()
|
||||||
|
{
|
||||||
|
DebugTools.AssertNotNull(_viewport);
|
||||||
|
|
||||||
|
var vpSize = _viewport!.Size;
|
||||||
|
var ourSize = (Vector2) PixelSize;
|
||||||
|
|
||||||
|
if (FixedStretchSize == null)
|
||||||
|
{
|
||||||
|
var (ratioX, ratioY) = ourSize / vpSize;
|
||||||
|
var ratio = Math.Min(ratioX, ratioY);
|
||||||
|
|
||||||
|
var size = vpSize * ratio;
|
||||||
|
// Size
|
||||||
|
var pos = (ourSize - size) / 2;
|
||||||
|
|
||||||
|
return (UIBox2i) UIBox2.FromDimensions(pos, size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Center only, no scaling.
|
||||||
|
var pos = (ourSize - FixedStretchSize.Value) / 2;
|
||||||
|
return (UIBox2i) UIBox2.FromDimensions(pos, FixedStretchSize.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegenerateViewport()
|
||||||
|
{
|
||||||
|
DebugTools.AssertNull(_viewport);
|
||||||
|
|
||||||
|
var vpSizeBase = ViewportSize;
|
||||||
|
var ourSize = PixelSize;
|
||||||
|
var (ratioX, ratioY) = ourSize / (Vector2) vpSizeBase;
|
||||||
|
var ratio = Math.Min(ratioX, ratioY);
|
||||||
|
var renderScale = 1;
|
||||||
|
switch (_renderScaleMode)
|
||||||
|
{
|
||||||
|
case ScalingViewportRenderScaleMode.CeilInt:
|
||||||
|
renderScale = (int) Math.Ceiling(ratio);
|
||||||
|
break;
|
||||||
|
case ScalingViewportRenderScaleMode.FloorInt:
|
||||||
|
renderScale = (int) Math.Floor(ratio);
|
||||||
|
break;
|
||||||
|
case ScalingViewportRenderScaleMode.Fixed:
|
||||||
|
renderScale = _fixedRenderScale;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always has to be at least one to avoid passing 0,0 to the viewport constructor
|
||||||
|
renderScale = Math.Max(1, renderScale);
|
||||||
|
|
||||||
|
_curRenderScale = renderScale;
|
||||||
|
|
||||||
|
_viewport = _clyde.CreateViewport(
|
||||||
|
ViewportSize * renderScale,
|
||||||
|
new TextureSampleParameters
|
||||||
|
{
|
||||||
|
Filter = StretchMode == ScalingViewportStretchMode.Bilinear,
|
||||||
|
});
|
||||||
|
|
||||||
|
_viewport.RenderScale = (renderScale, renderScale);
|
||||||
|
|
||||||
|
_viewport.Eye = _eye;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Resized()
|
||||||
|
{
|
||||||
|
base.Resized();
|
||||||
|
|
||||||
|
InvalidateViewport();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InvalidateViewport()
|
||||||
|
{
|
||||||
|
_viewport?.Dispose();
|
||||||
|
_viewport = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapCoordinates ScreenToMap(Vector2 coords)
|
||||||
|
{
|
||||||
|
if (_eye == null)
|
||||||
|
return default;
|
||||||
|
|
||||||
|
EnsureViewportCreated();
|
||||||
|
|
||||||
|
var matrix = Matrix3.Invert(LocalToScreenMatrix());
|
||||||
|
|
||||||
|
return _viewport!.LocalToWorld(matrix.Transform(coords));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2 WorldToScreen(Vector2 map)
|
||||||
|
{
|
||||||
|
if (_eye == null)
|
||||||
|
return default;
|
||||||
|
|
||||||
|
EnsureViewportCreated();
|
||||||
|
|
||||||
|
var vpLocal = _viewport!.WorldToLocal(map);
|
||||||
|
|
||||||
|
var matrix = LocalToScreenMatrix();
|
||||||
|
|
||||||
|
return matrix.Transform(vpLocal);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Matrix3 LocalToScreenMatrix()
|
||||||
|
{
|
||||||
|
DebugTools.AssertNotNull(_viewport);
|
||||||
|
|
||||||
|
var drawBox = GetDrawBox();
|
||||||
|
var scaleFactor = drawBox.Size / (Vector2) _viewport!.Size;
|
||||||
|
|
||||||
|
if (scaleFactor == (0, 0))
|
||||||
|
// Basically a nonsense scenario, at least make sure to return something that can be inverted.
|
||||||
|
return Matrix3.Identity;
|
||||||
|
|
||||||
|
var scale = Matrix3.CreateScale(scaleFactor);
|
||||||
|
var translate = Matrix3.CreateTranslation(GlobalPixelPosition + drawBox.TopLeft);
|
||||||
|
|
||||||
|
return scale * translate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureViewportCreated()
|
||||||
|
{
|
||||||
|
if (_viewport == null)
|
||||||
|
{
|
||||||
|
RegenerateViewport();
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugTools.AssertNotNull(_viewport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines how the viewport is stretched if it does not match the size of the control perfectly.
|
||||||
|
/// </summary>
|
||||||
|
public enum ScalingViewportStretchMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Bilinear sampling is used.
|
||||||
|
/// </summary>
|
||||||
|
Bilinear = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Nearest neighbor sampling is used.
|
||||||
|
/// </summary>
|
||||||
|
Nearest,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines how the base render scale of the viewport is selected.
|
||||||
|
/// </summary>
|
||||||
|
public enum ScalingViewportRenderScaleMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="ScalingViewport.FixedRenderScale"/> is used.
|
||||||
|
/// </summary>
|
||||||
|
Fixed = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Floor to the closest integer scale possible.
|
||||||
|
/// </summary>
|
||||||
|
FloorInt,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ceiling to the closest integer scale possible.
|
||||||
|
/// </summary>
|
||||||
|
CeilInt
|
||||||
|
}
|
||||||
|
}
|
||||||
15
Content.Client/UserInterface/ViewportExt.cs
Normal file
15
Content.Client/UserInterface/ViewportExt.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
|
||||||
|
namespace Content.Client.UserInterface
|
||||||
|
{
|
||||||
|
public static class ViewportExt
|
||||||
|
{
|
||||||
|
public static int GetRenderScale(this IViewportControl viewport)
|
||||||
|
{
|
||||||
|
if (viewport is ScalingViewport svp)
|
||||||
|
return svp.CurrentRenderScale;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
Content.Client/UserInterface/ViewportManager.cs
Normal file
42
Content.Client/UserInterface/ViewportManager.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Shared;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
|
||||||
|
namespace Content.Client.UserInterface
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event proxy for <see cref="MainViewport"/> to listen to config events.
|
||||||
|
/// </summary>
|
||||||
|
// ReSharper disable once ClassNeverInstantiated.Global
|
||||||
|
public sealed class ViewportManager
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||||
|
|
||||||
|
private readonly List<MainViewport> _viewports = new();
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
_cfg.OnValueChanged(CCVars.ViewportStretch, _ => UpdateCfg());
|
||||||
|
_cfg.OnValueChanged(CCVars.ViewportSnapToleranceClip, _ => UpdateCfg());
|
||||||
|
_cfg.OnValueChanged(CCVars.ViewportSnapToleranceMargin, _ => UpdateCfg());
|
||||||
|
_cfg.OnValueChanged(CCVars.ViewportScaleRender, _ => UpdateCfg());
|
||||||
|
_cfg.OnValueChanged(CCVars.ViewportFixedScaleFactor, _ => UpdateCfg());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateCfg()
|
||||||
|
{
|
||||||
|
_viewports.ForEach(v => v.UpdateCfg());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddViewport(MainViewport vp)
|
||||||
|
{
|
||||||
|
_viewports.Add(vp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveViewport(MainViewport vp)
|
||||||
|
{
|
||||||
|
_viewports.Remove(vp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -318,5 +318,24 @@ namespace Content.Shared
|
|||||||
|
|
||||||
public static readonly CVarDef<bool> BanHardwareIds =
|
public static readonly CVarDef<bool> BanHardwareIds =
|
||||||
CVarDef.Create("ban.hardware_ids", false, CVar.SERVERONLY);
|
CVarDef.Create("ban.hardware_ids", false, CVar.SERVERONLY);
|
||||||
|
/*
|
||||||
|
* VIEWPORT
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static readonly CVarDef<bool> ViewportStretch =
|
||||||
|
CVarDef.Create("viewport.stretch", true, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||||
|
|
||||||
|
public static readonly CVarDef<int> ViewportFixedScaleFactor =
|
||||||
|
CVarDef.Create("viewport.fixed_scale_factor", 2, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||||
|
|
||||||
|
// This default is basically specifically chosen so fullscreen/maximized 1080p hits a 2x snap and does NN.
|
||||||
|
public static readonly CVarDef<int> ViewportSnapToleranceMargin =
|
||||||
|
CVarDef.Create("viewport.snap_tolerance_margin", 64, CVar.CLIENTONLY);
|
||||||
|
|
||||||
|
public static readonly CVarDef<int> ViewportSnapToleranceClip =
|
||||||
|
CVarDef.Create("viewport.snap_tolerance_clip", 32, CVar.CLIENTONLY);
|
||||||
|
|
||||||
|
public static readonly CVarDef<bool> ViewportScaleRender =
|
||||||
|
CVarDef.Create("viewport.scale_render", true, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
Resources/Locale/en-US/ui/connecting.ftl
Normal file
18
Resources/Locale/en-US/ui/connecting.ftl
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
### Connecting dialog when you start up the game
|
||||||
|
|
||||||
|
connecting-title = Space Station 14
|
||||||
|
connecting-exit = Exit
|
||||||
|
connecting-retry = Retry
|
||||||
|
connecting-reconnect = Reconnect
|
||||||
|
connecting-in-progress = Connecting to server...
|
||||||
|
connecting-disconnected = Disconnected from server:
|
||||||
|
connecting-tip = Don't die!
|
||||||
|
connecting-version = ver 0.1
|
||||||
|
connecting-fail-reason = Failed to connect to server:
|
||||||
|
{ $reason }
|
||||||
|
|
||||||
|
connecting-state-NotConnecting = You should not be seeing this
|
||||||
|
connecting-state-ResolvingHost = Resolving server address...
|
||||||
|
connecting-state-EstablishingConnection = Establishing initial connection...
|
||||||
|
connecting-state-Handshake = Doing handshake...
|
||||||
|
connecting-state-Connected = Synchronizing game state...
|
||||||
@@ -36,7 +36,9 @@ ui-options-hud-theme = HUD Theme:
|
|||||||
ui-options-hud-theme-default = Default
|
ui-options-hud-theme-default = Default
|
||||||
ui-options-hud-theme-modernized = Modernized
|
ui-options-hud-theme-modernized = Modernized
|
||||||
ui-options-hud-theme-classic = Classic
|
ui-options-hud-theme-classic = Classic
|
||||||
ui-options-placeholder-viewport = Viewport settings
|
ui-options-vp-stretch = Stretch viewport to fit game window?
|
||||||
|
ui-options-vp-scale = Fixed viewport scale: x{ $scale }
|
||||||
|
ui-options-vp-low-res = Low-resolution viewport?
|
||||||
|
|
||||||
## Controls menu
|
## Controls menu
|
||||||
ui-options-binds-reset-all = Reset ALL keybinds
|
ui-options-binds-reset-all = Reset ALL keybinds
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
- Disarm
|
- Disarm
|
||||||
- HumanScream
|
- HumanScream
|
||||||
- type: Eye
|
- type: Eye
|
||||||
zoom: 0.5, 0.5
|
|
||||||
- type: CameraRecoil
|
- type: CameraRecoil
|
||||||
- type: Examiner
|
- type: Examiner
|
||||||
- type: HumanInventoryController
|
- type: HumanInventoryController
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
status: InAir
|
status: InAir
|
||||||
- type: PlayerInputMover
|
- type: PlayerInputMover
|
||||||
- type: Eye
|
- type: Eye
|
||||||
zoom: 0.5, 0.5
|
|
||||||
drawFov: false
|
drawFov: false
|
||||||
- type: Input
|
- type: Input
|
||||||
context: "ghost"
|
context: "ghost"
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
- HumanScream
|
- HumanScream
|
||||||
- Disarm
|
- Disarm
|
||||||
- type: Eye
|
- type: Eye
|
||||||
zoom: 0.5, 0.5
|
|
||||||
- type: CameraRecoil
|
- type: CameraRecoil
|
||||||
- type: Examiner
|
- type: Examiner
|
||||||
- type: HumanInventoryController
|
- type: HumanInventoryController
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
- VoxScream
|
- VoxScream
|
||||||
- Disarm
|
- Disarm
|
||||||
- type: Eye
|
- type: Eye
|
||||||
zoom: 0.5, 0.5
|
|
||||||
- type: CameraRecoil
|
- type: CameraRecoil
|
||||||
- type: Examiner
|
- type: Examiner
|
||||||
- type: HumanInventoryController
|
- type: HumanInventoryController
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
kind: source
|
kind: source
|
||||||
path: "/Textures/Shaders/outline.swsl"
|
path: "/Textures/Shaders/outline.swsl"
|
||||||
params:
|
params:
|
||||||
outline_width: 2
|
|
||||||
outline_color: "#FF000055"
|
outline_color: "#FF000055"
|
||||||
|
|
||||||
- type: shader
|
- type: shader
|
||||||
@@ -11,5 +10,4 @@
|
|||||||
kind: source
|
kind: source
|
||||||
path: "/Textures/Shaders/outline.swsl"
|
path: "/Textures/Shaders/outline.swsl"
|
||||||
params:
|
params:
|
||||||
outline_width: 2
|
|
||||||
outline_color: "#00FF0055"
|
outline_color: "#00FF0055"
|
||||||
|
|||||||
Submodule RobustToolbox updated: ffa908bf27...c4946b8466
Reference in New Issue
Block a user