mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-16 05:41:14 +03:00
Signed-off-by: Prole <172158352+Prole0@users.noreply.github.com> Co-authored-by: PJBot <pieterjan.briers+bot@gmail.com> Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Co-authored-by: Samuka-C <47865393+Samuka-C@users.noreply.github.com> Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> Co-authored-by: Partmedia <kevinz5000@gmail.com> Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Co-authored-by: themias <89101928+themias@users.noreply.github.com> Co-authored-by: Victor Shen <71985089+Vexerot@users.noreply.github.com> Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com> Co-authored-by: Milon <milonpl.git@proton.me> Co-authored-by: Kirus59 <145689588+Kirus59@users.noreply.github.com> Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Co-authored-by: Stomf <5dorkydorks@gmail.com> Co-authored-by: drakewill-CRL <46307022+drakewill-CRL@users.noreply.github.com> Co-authored-by: PraxisMapper <praxismapper@gmail.com> Co-authored-by: EmoGarbage404 <retron404@gmail.com> Co-authored-by: lzk <124214523+lzk228@users.noreply.github.com> Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> Co-authored-by: IProduceWidgets <107586145+IProduceWidgets@users.noreply.github.com> Co-authored-by: TytosB <54259736+TytosB@users.noreply.github.com> Co-authored-by: abadaba695 <spacestation13thingy@gmail.com> Co-authored-by: kosticia <kosticia46@gmail.com> Co-authored-by: Thinbug <101073555+Thinbug0@users.noreply.github.com> Co-authored-by: pathetic meowmeow <uhhadd@gmail.com> Co-authored-by: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> Co-authored-by: Boaz1111 <149967078+Boaz1111@users.noreply.github.com> Co-authored-by: ActiveMammmoth <140334666+ActiveMammmoth@users.noreply.github.com> Co-authored-by: Myra <vasilis@pikachu.systems> Co-authored-by: Whatstone <166147148+whatston3@users.noreply.github.com> Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com> Co-authored-by: K-Dynamic <20566341+K-Dynamic@users.noreply.github.com> Co-authored-by: Gentleman-Bird <dcgreen406@gmail.com> Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: BIGZi0348 <svalker0348@gmail.com> Co-authored-by: LaCumbiaDelCoronavirus <90893484+LaCumbiaDelCoronavirus@users.noreply.github.com> Co-authored-by: imatsoup <93290208+imatsoup@users.noreply.github.com> Co-authored-by: Matthew Herber <32679887+happyrobot33@users.noreply.github.com> Co-authored-by: Ertanic <36124833+Ertanic@users.noreply.github.com> Co-authored-by: MissKay1994 <15877268+MissKay1994@users.noreply.github.com> Co-authored-by: Errant <35878406+Errant-4@users.noreply.github.com> Co-authored-by: eoineoineoin <helloworld@eoinrul.es> Co-authored-by: Tiniest Shark <head.rebel@yahoo.com> Co-authored-by: nikitosych <boriszyn@gmail.com> Co-authored-by: Tayrtahn <tayrtahn@gmail.com> Co-authored-by: Perry Fraser <perryprog@users.noreply.github.com> Co-authored-by: YoungThug <ramialanbagy@gmail.com> Co-authored-by: beck-thompson <107373427+beck-thompson@users.noreply.github.com> Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com> Co-authored-by: Southbridge <7013162+southbridge-fur@users.noreply.github.com> Co-authored-by: Vladislav Suchkov <20380250+murolem@users.noreply.github.com> Co-authored-by: Prole <172158352+Prole0@users.noreply.github.com> Co-authored-by: Unkn0wn_Gh0st <shadowstalkermll@gmail.com> Co-authored-by: 3nderall <101940324+3nderall@users.noreply.github.com> Co-authored-by: Radezolid <snappednexus@gmail.com> Co-authored-by: J <billsmith116@gmail.com> Co-authored-by: Ghagliiarghii <68826635+Ghagliiarghii@users.noreply.github.com> Co-authored-by: chromiumboy <50505512+chromiumboy@users.noreply.github.com> Co-authored-by: youtissoum <51883137+youtissoum@users.noreply.github.com> Co-authored-by: Minemoder5000 <minemoder50000@gmail.com> Co-authored-by: Spanky <scott@wearejacob.com> Co-authored-by: Spessmann <156740760+Spessmann@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: brainfood1183 <113240905+brainfood1183@users.noreply.github.com> Co-authored-by: Deerstop <edainturner@gmail.com> Co-authored-by: B_Kirill <153602297+B-Kirill@users.noreply.github.com> Co-authored-by: archee1 <archee3@hotmail.co.uk> Co-authored-by: Cojoke <83733158+Cojoke-dot@users.noreply.github.com> Co-authored-by: Quantum-cross <7065792+Quantum-cross@users.noreply.github.com> Co-authored-by: poklj <compgeek223@gmail.com> Co-authored-by: Krunklehorn <42424291+Krunklehorn@users.noreply.github.com> Co-authored-by: OnyxTheBrave <131422822+OnyxTheBrave@users.noreply.github.com> Co-authored-by: UpAndLeaves <92269094+Alpha-Two@users.noreply.github.com> Co-authored-by: Flareguy <78941145+Flareguy@users.noreply.github.com> Co-authored-by: Zalycon <84675130+Zalycon@users.noreply.github.com> Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com> Co-authored-by: Verm <32827189+Vermidia@users.noreply.github.com> Co-authored-by: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com> Co-authored-by: ScarKy0 <scarky0@onet.eu> Co-authored-by: Dmitry <57028746+dimm00n@users.noreply.github.com>
344 lines
13 KiB
C#
344 lines
13 KiB
C#
using System.Numerics;
|
|
using Content.Shared.Shuttles.BUIStates;
|
|
using Content.Shared.Shuttles.Components;
|
|
using Content.Shared.Shuttles.Systems;
|
|
using JetBrains.Annotations;
|
|
using Robust.Client.AutoGenerated;
|
|
using Robust.Client.Graphics;
|
|
using Robust.Client.UserInterface;
|
|
using Robust.Client.UserInterface.XAML;
|
|
using Robust.Shared.Collections;
|
|
using Robust.Shared.Input;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Map.Components;
|
|
using Robust.Shared.Physics;
|
|
using Robust.Shared.Physics.Components;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Content.Client.Shuttles.UI;
|
|
|
|
[GenerateTypedNameReferences]
|
|
public sealed partial class ShuttleNavControl : BaseShuttleControl
|
|
{
|
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
|
private readonly SharedShuttleSystem _shuttles;
|
|
private readonly SharedTransformSystem _transform;
|
|
|
|
/// <summary>
|
|
/// Used to transform all of the radar objects. Typically is a shuttle console parented to a grid.
|
|
/// </summary>
|
|
private EntityCoordinates? _coordinates;
|
|
|
|
/// <summary>
|
|
/// Entity of controlling console
|
|
/// </summary>
|
|
private EntityUid? _consoleEntity;
|
|
|
|
private Angle? _rotation;
|
|
|
|
private Dictionary<NetEntity, List<DockingPortState>> _docks = new();
|
|
|
|
public bool ShowIFF { get; set; } = true;
|
|
public bool ShowDocks { get; set; } = true;
|
|
public bool RotateWithEntity { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Raised if the user left-clicks on the radar control with the relevant entitycoordinates.
|
|
/// </summary>
|
|
public Action<EntityCoordinates>? OnRadarClick;
|
|
|
|
private List<Entity<MapGridComponent>> _grids = new();
|
|
|
|
public ShuttleNavControl() : base(64f, 256f, 256f)
|
|
{
|
|
RobustXamlLoader.Load(this);
|
|
_shuttles = EntManager.System<SharedShuttleSystem>();
|
|
_transform = EntManager.System<SharedTransformSystem>();
|
|
}
|
|
|
|
public void SetMatrix(EntityCoordinates? coordinates, Angle? angle)
|
|
{
|
|
_coordinates = coordinates;
|
|
_rotation = angle;
|
|
}
|
|
|
|
public void SetConsole(EntityUid? consoleEntity)
|
|
{
|
|
_consoleEntity = consoleEntity;
|
|
}
|
|
|
|
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
|
|
{
|
|
base.KeyBindUp(args);
|
|
|
|
if (_coordinates == null || _rotation == null || args.Function != EngineKeyFunctions.UIClick ||
|
|
OnRadarClick == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var a = InverseScalePosition(args.RelativePosition);
|
|
var relativeWorldPos = new Vector2(a.X, -a.Y);
|
|
relativeWorldPos = _rotation.Value.RotateVec(relativeWorldPos);
|
|
var coords = _coordinates.Value.Offset(relativeWorldPos);
|
|
OnRadarClick?.Invoke(coords);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the entitycoordinates of where the mouseposition is, relative to the control.
|
|
/// </summary>
|
|
[PublicAPI]
|
|
public EntityCoordinates GetMouseCoordinates(ScreenCoordinates screen)
|
|
{
|
|
if (_coordinates == null || _rotation == null)
|
|
{
|
|
return EntityCoordinates.Invalid;
|
|
}
|
|
|
|
var pos = screen.Position / UIScale - GlobalPosition;
|
|
|
|
var a = InverseScalePosition(pos);
|
|
var relativeWorldPos = new Vector2(a.X, -a.Y);
|
|
relativeWorldPos = _rotation.Value.RotateVec(relativeWorldPos);
|
|
var coords = _coordinates.Value.Offset(relativeWorldPos);
|
|
return coords;
|
|
}
|
|
|
|
public void UpdateState(NavInterfaceState state)
|
|
{
|
|
SetMatrix(EntManager.GetCoordinates(state.Coordinates), state.Angle);
|
|
|
|
WorldMaxRange = state.MaxRange;
|
|
|
|
if (WorldMaxRange < WorldRange)
|
|
{
|
|
ActualRadarRange = WorldMaxRange;
|
|
}
|
|
|
|
if (WorldMaxRange < WorldMinRange)
|
|
WorldMinRange = WorldMaxRange;
|
|
|
|
ActualRadarRange = Math.Clamp(ActualRadarRange, WorldMinRange, WorldMaxRange);
|
|
|
|
RotateWithEntity = state.RotateWithEntity;
|
|
|
|
_docks = state.Docks;
|
|
}
|
|
|
|
protected override void Draw(DrawingHandleScreen handle)
|
|
{
|
|
base.Draw(handle);
|
|
|
|
DrawBacking(handle);
|
|
DrawCircles(handle);
|
|
|
|
// No data
|
|
if (_coordinates == null || _rotation == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var xformQuery = EntManager.GetEntityQuery<TransformComponent>();
|
|
var fixturesQuery = EntManager.GetEntityQuery<FixturesComponent>();
|
|
var bodyQuery = EntManager.GetEntityQuery<PhysicsComponent>();
|
|
|
|
if (!xformQuery.TryGetComponent(_coordinates.Value.EntityId, out var xform)
|
|
|| xform.MapID == MapId.Nullspace)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var mapPos = _transform.ToMapCoordinates(_coordinates.Value);
|
|
var posMatrix = Matrix3Helpers.CreateTransform(_coordinates.Value.Position, _rotation.Value);
|
|
var ourEntRot = RotateWithEntity ? _transform.GetWorldRotation(xform) : _rotation.Value;
|
|
var ourEntMatrix = Matrix3Helpers.CreateTransform(_transform.GetWorldPosition(xform), ourEntRot);
|
|
var shuttleToWorld = Matrix3x2.Multiply(posMatrix, ourEntMatrix);
|
|
Matrix3x2.Invert(shuttleToWorld, out var worldToShuttle);
|
|
var shuttleToView = Matrix3x2.CreateScale(new Vector2(MinimapScale, -MinimapScale)) * Matrix3x2.CreateTranslation(MidPointVector);
|
|
|
|
// Draw our grid in detail
|
|
var ourGridId = xform.GridUid;
|
|
if (EntManager.TryGetComponent<MapGridComponent>(ourGridId, out var ourGrid) &&
|
|
fixturesQuery.HasComponent(ourGridId.Value))
|
|
{
|
|
var ourGridToWorld = _transform.GetWorldMatrix(ourGridId.Value);
|
|
var ourGridToShuttle = Matrix3x2.Multiply(ourGridToWorld, worldToShuttle);
|
|
var ourGridToView = ourGridToShuttle * shuttleToView;
|
|
var color = _shuttles.GetIFFColor(ourGridId.Value, self: true);
|
|
|
|
DrawGrid(handle, ourGridToView, (ourGridId.Value, ourGrid), color);
|
|
DrawDocks(handle, ourGridId.Value, ourGridToView);
|
|
}
|
|
|
|
// Draw radar position on the station
|
|
const float radarVertRadius = 2f;
|
|
var radarPosVerts = new Vector2[]
|
|
{
|
|
ScalePosition(new Vector2(0f, -radarVertRadius)),
|
|
ScalePosition(new Vector2(radarVertRadius / 2f, 0f)),
|
|
ScalePosition(new Vector2(0f, radarVertRadius)),
|
|
ScalePosition(new Vector2(radarVertRadius / -2f, 0f)),
|
|
};
|
|
|
|
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, radarPosVerts, Color.Lime);
|
|
|
|
var rot = ourEntRot + _rotation.Value;
|
|
var viewBounds = new Box2Rotated(new Box2(-WorldRange, -WorldRange, WorldRange, WorldRange).Translated(mapPos.Position), rot, mapPos.Position);
|
|
var viewAABB = viewBounds.CalcBoundingBox();
|
|
|
|
_grids.Clear();
|
|
_mapManager.FindGridsIntersecting(xform.MapID, new Box2(mapPos.Position - MaxRadarRangeVector, mapPos.Position + MaxRadarRangeVector), ref _grids, approx: true, includeMap: false);
|
|
|
|
// Draw other grids... differently
|
|
foreach (var grid in _grids)
|
|
{
|
|
var gUid = grid.Owner;
|
|
if (gUid == ourGridId || !fixturesQuery.HasComponent(gUid))
|
|
continue;
|
|
|
|
var gridBody = bodyQuery.GetComponent(gUid);
|
|
EntManager.TryGetComponent<IFFComponent>(gUid, out var iff);
|
|
|
|
if (!_shuttles.CanDraw(gUid, gridBody, iff))
|
|
continue;
|
|
|
|
var curGridToWorld = _transform.GetWorldMatrix(gUid);
|
|
var curGridToView = curGridToWorld * worldToShuttle * shuttleToView;
|
|
|
|
var labelColor = _shuttles.GetIFFColor(grid, self: false, iff);
|
|
var coordColor = new Color(labelColor.R * 0.8f, labelColor.G * 0.8f, labelColor.B * 0.8f, 0.5f);
|
|
|
|
// Others default:
|
|
// Color.FromHex("#FFC000FF")
|
|
// Hostile default: Color.Firebrick
|
|
var labelName = _shuttles.GetIFFLabel(grid, self: false, iff);
|
|
|
|
if (ShowIFF &&
|
|
labelName != null)
|
|
{
|
|
var gridBounds = grid.Comp.LocalAABB;
|
|
|
|
var gridCentre = Vector2.Transform(gridBody.LocalCenter, curGridToView);
|
|
|
|
var distance = gridCentre.Length();
|
|
var labelText = Loc.GetString("shuttle-console-iff-label", ("name", labelName),
|
|
("distance", $"{distance:0.0}"));
|
|
|
|
var mapCoords = _transform.GetWorldPosition(gUid);
|
|
var coordsText = $"({mapCoords.X:0.0}, {mapCoords.Y:0.0})";
|
|
|
|
// yes 1.0 scale is intended here.
|
|
var labelDimensions = handle.GetDimensions(Font, labelText, 1f);
|
|
var coordsDimensions = handle.GetDimensions(Font, coordsText, 0.7f);
|
|
|
|
// y-offset the control to always render below the grid (vertically)
|
|
var yOffset = Math.Max(gridBounds.Height, gridBounds.Width) * MinimapScale / 1.8f;
|
|
|
|
// The actual position in the UI.
|
|
var gridScaledPosition = gridCentre - new Vector2(0, -yOffset);
|
|
|
|
// Normalize the grid position if it exceeds the viewport bounds
|
|
// normalizing it instead of clamping it preserves the direction of the vector and prevents corner-hugging
|
|
var gridOffset = gridScaledPosition / PixelSize - new Vector2(0.5f, 0.5f);
|
|
var offsetMax = Math.Max(Math.Abs(gridOffset.X), Math.Abs(gridOffset.Y)) * 2f;
|
|
if (offsetMax > 1)
|
|
{
|
|
gridOffset = new Vector2(gridOffset.X / offsetMax, gridOffset.Y / offsetMax);
|
|
|
|
gridScaledPosition = (gridOffset + new Vector2(0.5f, 0.5f)) * PixelSize;
|
|
}
|
|
|
|
var labelUiPosition = gridScaledPosition - new Vector2(labelDimensions.X / 2f, 0);
|
|
var coordUiPosition = gridScaledPosition - new Vector2(coordsDimensions.X / 2f, -labelDimensions.Y);
|
|
|
|
// clamp the IFF label's UI position to within the viewport extents so it hugs the edges of the viewport
|
|
// coord label intentionally isn't clamped so we don't get ugly clutter at the edges
|
|
var controlExtents = PixelSize - new Vector2(labelDimensions.X, labelDimensions.Y); //new Vector2(labelDimensions.X * 2f, labelDimensions.Y);
|
|
labelUiPosition = Vector2.Clamp(labelUiPosition, Vector2.Zero, controlExtents);
|
|
|
|
// draw IFF label
|
|
handle.DrawString(Font, labelUiPosition, labelText, labelColor);
|
|
|
|
// only draw coords label if close enough
|
|
if (offsetMax < 1)
|
|
{
|
|
handle.DrawString(Font, coordUiPosition, coordsText, 0.7f, coordColor);
|
|
}
|
|
}
|
|
|
|
// Detailed view
|
|
var gridAABB = curGridToWorld.TransformBox(grid.Comp.LocalAABB);
|
|
|
|
// Skip drawing if it's out of range.
|
|
if (!gridAABB.Intersects(viewAABB))
|
|
continue;
|
|
|
|
DrawGrid(handle, curGridToView, grid, labelColor);
|
|
DrawDocks(handle, gUid, curGridToView);
|
|
}
|
|
|
|
// If we've set the controlling console, and it's on a different grid
|
|
// to the shuttle itself, then draw an additional marker to help the
|
|
// player determine where they are relative to the shuttle.
|
|
if (_consoleEntity != null && xformQuery.TryGetComponent(_consoleEntity, out var consoleXform))
|
|
{
|
|
if (consoleXform.ParentUid != _coordinates.Value.EntityId)
|
|
{
|
|
var consolePositionWorld = _transform.GetWorldPosition((EntityUid)_consoleEntity);
|
|
var p = Vector2.Transform(consolePositionWorld, worldToShuttle * shuttleToView);
|
|
handle.DrawCircle(p, 5, Color.ToSrgb(Color.Cyan), true);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private void DrawDocks(DrawingHandleScreen handle, EntityUid uid, Matrix3x2 gridToView)
|
|
{
|
|
if (!ShowDocks)
|
|
return;
|
|
|
|
const float DockScale = 0.6f;
|
|
var nent = EntManager.GetNetEntity(uid);
|
|
|
|
const float sqrt2 = 1.41421356f;
|
|
const float dockRadius = DockScale * sqrt2;
|
|
// Worst-case bounds used to cull a dock:
|
|
Box2 viewBounds = new Box2(
|
|
-dockRadius * UIScale,
|
|
-dockRadius * UIScale,
|
|
(Size.X + dockRadius) * UIScale,
|
|
(Size.Y + dockRadius) * UIScale);
|
|
|
|
if (_docks.TryGetValue(nent, out var docks))
|
|
{
|
|
foreach (var state in docks)
|
|
{
|
|
var position = state.Coordinates.Position;
|
|
|
|
var positionInView = Vector2.Transform(position, gridToView);
|
|
if (!viewBounds.Contains(positionInView))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var color = Color.ToSrgb(Color.Magenta);
|
|
|
|
var verts = new[]
|
|
{
|
|
Vector2.Transform(position + new Vector2(-DockScale, -DockScale), gridToView),
|
|
Vector2.Transform(position + new Vector2(DockScale, -DockScale), gridToView),
|
|
Vector2.Transform(position + new Vector2(DockScale, DockScale), gridToView),
|
|
Vector2.Transform(position + new Vector2(-DockScale, DockScale), gridToView),
|
|
};
|
|
|
|
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts, color.WithAlpha(0.8f));
|
|
handle.DrawPrimitives(DrawPrimitiveTopology.LineStrip, verts, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
private Vector2 InverseScalePosition(Vector2 value)
|
|
{
|
|
return (value - MidPointVector) / MinimapScale;
|
|
}
|
|
}
|