From de07c291de827bd8a2e843974d4362a13be3bb89 Mon Sep 17 00:00:00 2001 From: Julian Giebel Date: Sun, 9 Jun 2024 21:18:54 +0200 Subject: [PATCH] Implement map text (#28705) --- Content.Client/MapText/MapTextComponent.cs | 20 +++++ Content.Client/MapText/MapTextOverlay.cs | 85 +++++++++++++++++++ Content.Client/MapText/MapTextSystem.cs | 81 ++++++++++++++++++ Content.Server/MapText/MapTextComponent.cs | 6 ++ Content.Server/MapText/MapTextSystem.cs | 28 ++++++ .../MapText/SharedMapTextComponent.cs | 51 +++++++++++ Content.Shared/MapText/SharedMapTextSystem.cs | 6 ++ .../en-US/mapping/map-text-component.ftl | 2 + .../Prototypes/Entities/Markers/map_text.yml | 11 +++ 9 files changed, 290 insertions(+) create mode 100644 Content.Client/MapText/MapTextComponent.cs create mode 100644 Content.Client/MapText/MapTextOverlay.cs create mode 100644 Content.Client/MapText/MapTextSystem.cs create mode 100644 Content.Server/MapText/MapTextComponent.cs create mode 100644 Content.Server/MapText/MapTextSystem.cs create mode 100644 Content.Shared/MapText/SharedMapTextComponent.cs create mode 100644 Content.Shared/MapText/SharedMapTextSystem.cs create mode 100644 Resources/Locale/en-US/mapping/map-text-component.ftl create mode 100644 Resources/Prototypes/Entities/Markers/map_text.yml diff --git a/Content.Client/MapText/MapTextComponent.cs b/Content.Client/MapText/MapTextComponent.cs new file mode 100644 index 0000000000..f70b3c8dd4 --- /dev/null +++ b/Content.Client/MapText/MapTextComponent.cs @@ -0,0 +1,20 @@ +using Content.Shared.MapText; +using Robust.Client.Graphics; + +namespace Content.Client.MapText; + +[RegisterComponent] +public sealed partial class MapTextComponent : SharedMapTextComponent +{ + /// + /// The font that gets cached on component init or state changes + /// + [ViewVariables] + public VectorFont? CachedFont; + + /// + /// The text currently being displayed. This is either or the + /// localized text or + /// + public string CachedText = string.Empty; +} diff --git a/Content.Client/MapText/MapTextOverlay.cs b/Content.Client/MapText/MapTextOverlay.cs new file mode 100644 index 0000000000..70e708bba8 --- /dev/null +++ b/Content.Client/MapText/MapTextOverlay.cs @@ -0,0 +1,85 @@ +using System.Numerics; +using Content.Shared.MapText; +using Robust.Client.Graphics; +using Robust.Client.ResourceManagement; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.RichText; +using Robust.Shared; +using Robust.Shared.Configuration; +using Robust.Shared.Enums; +using Robust.Shared.Prototypes; + +namespace Content.Client.MapText; + +/// +/// Draws map text as an overlay +/// +public sealed class MapTextOverlay : Overlay +{ + private readonly IConfigurationManager _configManager; + private readonly IEntityManager _entManager; + private readonly IUserInterfaceManager _uiManager; + private readonly SharedTransformSystem _transform; + public override OverlaySpace Space => OverlaySpace.ScreenSpace; + + public MapTextOverlay( + IConfigurationManager configManager, + IEntityManager entManager, + IUserInterfaceManager uiManager, + SharedTransformSystem transform, + IResourceCache resourceCache, + IPrototypeManager prototypeManager) + { + _configManager = configManager; + _entManager = entManager; + _uiManager = uiManager; + _transform = transform; + } + + protected override void Draw(in OverlayDrawArgs args) + { + if (args.ViewportControl == null) + return; + + args.DrawingHandle.SetTransform(Matrix3x2.Identity); + + var scale = _configManager.GetCVar(CVars.DisplayUIScale); + + if (scale == 0f) + scale = _uiManager.DefaultUIScale; + + DrawWorld(args.ScreenHandle, args, scale); + + args.DrawingHandle.UseShader(null); + } + + private void DrawWorld(DrawingHandleScreen handle, OverlayDrawArgs args, float scale) + { + if ( args.ViewportControl == null) + return; + + var matrix = args.ViewportControl.GetWorldToScreenMatrix(); + var query = _entManager.AllEntityQueryEnumerator(); + + // Enlarge bounds to try prevent pop-in due to large text. + var bounds = args.WorldBounds.Enlarged(2); + + while(query.MoveNext(out var uid, out var mapText)) + { + var mapPos = _transform.GetMapCoordinates(uid); + + if (mapPos.MapId != args.MapId) + continue; + + if (!bounds.Contains(mapPos.Position)) + continue; + + if (mapText.CachedFont == null) + continue; + + var pos = Vector2.Transform(mapPos.Position, matrix) + mapText.Offset; + var dimensions = handle.GetDimensions(mapText.CachedFont, mapText.CachedText, scale); + handle.DrawString(mapText.CachedFont, pos - dimensions / 2f, mapText.CachedText, scale, mapText.Color); + } + } +} diff --git a/Content.Client/MapText/MapTextSystem.cs b/Content.Client/MapText/MapTextSystem.cs new file mode 100644 index 0000000000..96ce8f93c2 --- /dev/null +++ b/Content.Client/MapText/MapTextSystem.cs @@ -0,0 +1,81 @@ +using Content.Shared.MapText; +using Robust.Client.Graphics; +using Robust.Client.ResourceManagement; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.RichText; +using Robust.Shared.Configuration; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + +namespace Content.Client.MapText; + +/// +public sealed class MapTextSystem : SharedMapTextSystem +{ + [Dependency] private readonly IConfigurationManager _configManager = default!; + [Dependency] private readonly IUserInterfaceManager _uiManager = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly IResourceCache _resourceCache = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IOverlayManager _overlayManager = default!; + + private MapTextOverlay _overlay = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnComponentStartup); + SubscribeLocalEvent(HandleCompState); + + _overlay = new MapTextOverlay(_configManager, EntityManager, _uiManager, _transform, _resourceCache, _prototypeManager); + _overlayManager.AddOverlay(_overlay); + + // TODO move font prototype to robust.shared, then use ProtoId + DebugTools.Assert(_prototypeManager.HasIndex(SharedMapTextComponent.DefaultFont)); + } + + private void OnComponentStartup(Entity ent, ref ComponentStartup args) + { + CacheText(ent.Comp); + // TODO move font prototype to robust.shared, then use ProtoId + DebugTools.Assert(_prototypeManager.HasIndex(ent.Comp.FontId)); + } + + private void HandleCompState(Entity ent, ref ComponentHandleState args) + { + if (args.Current is not MapTextComponentState state) + return; + + ent.Comp.Text = state.Text; + ent.Comp.LocText = state.LocText; + ent.Comp.Color = state.Color; + ent.Comp.FontId = state.FontId; + ent.Comp.FontSize = state.FontSize; + ent.Comp.Offset = state.Offset; + + CacheText(ent.Comp); + } + + private void CacheText(MapTextComponent component) + { + component.CachedFont = null; + + component.CachedText = string.IsNullOrWhiteSpace(component.Text) + ? Loc.GetString(component.LocText) + : component.Text; + + if (!_prototypeManager.TryIndex(component.FontId, out var fontPrototype)) + { + component.CachedText = Loc.GetString("map-text-font-error"); + component.Color = Color.Red; + + if(_prototypeManager.TryIndex(SharedMapTextComponent.DefaultFont, out var @default)) + component.CachedFont = new VectorFont(_resourceCache.GetResource(@default.Path), 14); + return; + } + + var fontResource = _resourceCache.GetResource(fontPrototype.Path); + component.CachedFont = new VectorFont(fontResource, component.FontSize); + } +} diff --git a/Content.Server/MapText/MapTextComponent.cs b/Content.Server/MapText/MapTextComponent.cs new file mode 100644 index 0000000000..2af9583484 --- /dev/null +++ b/Content.Server/MapText/MapTextComponent.cs @@ -0,0 +1,6 @@ +using Content.Shared.MapText; + +namespace Content.Server.MapText; + +[RegisterComponent] +public sealed partial class MapTextComponent : SharedMapTextComponent; diff --git a/Content.Server/MapText/MapTextSystem.cs b/Content.Server/MapText/MapTextSystem.cs new file mode 100644 index 0000000000..5632c06e1a --- /dev/null +++ b/Content.Server/MapText/MapTextSystem.cs @@ -0,0 +1,28 @@ +using Content.Shared.MapText; +using Robust.Shared.GameStates; + +namespace Content.Server.MapText; + +/// +public sealed class MapTextSystem : SharedMapTextSystem +{ + /// + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(GetCompState); + } + + private void GetCompState(Entity ent, ref ComponentGetState args) + { + args.State = new MapTextComponentState + { + Text = ent.Comp.Text, + LocText = ent.Comp.LocText, + Color = ent.Comp.Color, + FontId = ent.Comp.FontId, + FontSize = ent.Comp.FontSize, + Offset = ent.Comp.Offset + }; + } +} diff --git a/Content.Shared/MapText/SharedMapTextComponent.cs b/Content.Shared/MapText/SharedMapTextComponent.cs new file mode 100644 index 0000000000..7b8a02da6f --- /dev/null +++ b/Content.Shared/MapText/SharedMapTextComponent.cs @@ -0,0 +1,51 @@ +using System.Numerics; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared.MapText; + +/// +/// This is used for displaying text in world space +/// + +[NetworkedComponent, Access(typeof(SharedMapTextSystem))] +public abstract partial class SharedMapTextComponent : Component +{ + public const string DefaultFont = "Default"; + + /// + /// The text to display. This will override . + /// + [DataField] + public string? Text; + + /// + /// The localized-id of the text that should be displayed. + /// + [DataField] + public LocId LocText = "map-text-default"; + // TODO VV: LocId editing + + [DataField] + public Color Color = Color.White; + + [DataField] + public string FontId = DefaultFont; + + [DataField] + public int FontSize = 12; + + [DataField] + public Vector2 Offset = Vector2.Zero; +} + +[Serializable, NetSerializable] +public sealed class MapTextComponentState : ComponentState +{ + public string? Text { get; init;} + public LocId LocText { get; init;} + public Color Color { get; init;} + public string FontId { get; init; } = default!; + public int FontSize { get; init;} + public Vector2 Offset { get; init;} +} diff --git a/Content.Shared/MapText/SharedMapTextSystem.cs b/Content.Shared/MapText/SharedMapTextSystem.cs new file mode 100644 index 0000000000..da2adf16cf --- /dev/null +++ b/Content.Shared/MapText/SharedMapTextSystem.cs @@ -0,0 +1,6 @@ +namespace Content.Shared.MapText; + +/// +/// This handles registering the map text overlay, caching the text font and handling component state +/// +public abstract class SharedMapTextSystem : EntitySystem; diff --git a/Resources/Locale/en-US/mapping/map-text-component.ftl b/Resources/Locale/en-US/mapping/map-text-component.ftl new file mode 100644 index 0000000000..0a4d54b485 --- /dev/null +++ b/Resources/Locale/en-US/mapping/map-text-component.ftl @@ -0,0 +1,2 @@ +map-text-default = Use VV to change the displayed text +map-text-font-error = "Error - invalid font" diff --git a/Resources/Prototypes/Entities/Markers/map_text.yml b/Resources/Prototypes/Entities/Markers/map_text.yml new file mode 100644 index 0000000000..e8496138ac --- /dev/null +++ b/Resources/Prototypes/Entities/Markers/map_text.yml @@ -0,0 +1,11 @@ +- type: entity + id: MapText + parent: MarkerBase + name: map text + placement: + mode: PlaceFree + components: + - type: MapText + - type: Sprite + state: pink + -- 2.51.2