Add guidebook protodata tag: embed prototype values in guidebook text (#27570)
* First clumsy implementation of guidebook protodata * Support for Properties, format strings * Better null * Rename file * Documentation and some cleanup * Handle errors better * Added note about client-side components * A couple of examples * DataFields * Use attributes like a sane person * Outdated doc * string.Empty * No IComponent? * No casting * Use EntityManager.ComponentFactory * Use FrozenDictionary * Cache tagged component fields * Iterate components and check if they're tagged
This commit is contained in:
45
Content.Client/Guidebook/GuidebookDataSystem.cs
Normal file
45
Content.Client/Guidebook/GuidebookDataSystem.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using Content.Shared.Guidebook;
|
||||
|
||||
namespace Content.Client.Guidebook;
|
||||
|
||||
/// <summary>
|
||||
/// Client system for storing and retrieving values extracted from entity prototypes
|
||||
/// for display in the guidebook (<see cref="RichText.ProtodataTag"/>).
|
||||
/// Requests data from the server on <see cref="Initialize"/>.
|
||||
/// Can also be pushed new data when the server reloads prototypes.
|
||||
/// </summary>
|
||||
public sealed class GuidebookDataSystem : EntitySystem
|
||||
{
|
||||
private GuidebookData? _data;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeNetworkEvent<UpdateGuidebookDataEvent>(OnServerUpdated);
|
||||
|
||||
// Request data from the server
|
||||
RaiseNetworkEvent(new RequestGuidebookDataEvent());
|
||||
}
|
||||
|
||||
private void OnServerUpdated(UpdateGuidebookDataEvent args)
|
||||
{
|
||||
// Got new data from the server, either in response to our request, or because prototypes reloaded on the server
|
||||
_data = args.Data;
|
||||
_data.Freeze();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to retrieve a value using the given identifiers.
|
||||
/// See <see cref="GuidebookData.TryGetValue"/> for more information.
|
||||
/// </summary>
|
||||
public bool TryGetValue(string prototype, string component, string field, out object? value)
|
||||
{
|
||||
if (_data == null)
|
||||
{
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
return _data.TryGetValue(prototype, component, field, out value);
|
||||
}
|
||||
}
|
||||
49
Content.Client/Guidebook/Richtext/ProtodataTag.cs
Normal file
49
Content.Client/Guidebook/Richtext/ProtodataTag.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System.Globalization;
|
||||
using Robust.Client.UserInterface.RichText;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Guidebook.RichText;
|
||||
|
||||
/// <summary>
|
||||
/// RichText tag that can display values extracted from entity prototypes.
|
||||
/// In order to be accessed by this tag, the desired field/property must
|
||||
/// be tagged with <see cref="Shared.Guidebook.GuidebookDataAttribute"/>.
|
||||
/// </summary>
|
||||
public sealed class ProtodataTag : IMarkupTag
|
||||
{
|
||||
[Dependency] private readonly ILogManager _logMan = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
|
||||
public string Name => "protodata";
|
||||
private ISawmill Log => _log ??= _logMan.GetSawmill("protodata_tag");
|
||||
private ISawmill? _log;
|
||||
|
||||
public string TextBefore(MarkupNode node)
|
||||
{
|
||||
// Do nothing with an empty tag
|
||||
if (!node.Value.TryGetString(out var prototype))
|
||||
return string.Empty;
|
||||
|
||||
if (!node.Attributes.TryGetValue("comp", out var component))
|
||||
return string.Empty;
|
||||
if (!node.Attributes.TryGetValue("member", out var member))
|
||||
return string.Empty;
|
||||
node.Attributes.TryGetValue("format", out var format);
|
||||
|
||||
var guidebookData = _entMan.System<GuidebookDataSystem>();
|
||||
|
||||
// Try to get the value
|
||||
if (!guidebookData.TryGetValue(prototype, component.StringValue!, member.StringValue!, out var value))
|
||||
{
|
||||
Log.Error($"Failed to find protodata for {component}.{member} in {prototype}");
|
||||
return "???";
|
||||
}
|
||||
|
||||
// If we have a format string and a formattable value, format it as requested
|
||||
if (!string.IsNullOrEmpty(format.StringValue) && value is IFormattable formattable)
|
||||
return formattable.ToString(format.StringValue, CultureInfo.CurrentCulture);
|
||||
|
||||
// No format string given, so just use default ToString
|
||||
return value?.ToString() ?? "NULL";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user