using System.Numerics; using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Component.GUI; using KamiToolKit.Classes; using KamiToolKit.Enums; using Lumina.Text.Payloads; using Lumina.Text.ReadOnly; namespace KamiToolKit.Nodes; /// /// A counter node for displaying numbers /// public unsafe class CounterNode : NodeBase { public readonly PartsList PartsList; public CounterNode() : base(NodeType.Counter) { PartsList = new PartsList(); PartsList.Add(new Part()); Node->PartsList = PartsList.InternalPartsList; NumberWidth = 10; CommaWidth = 8; SpaceWidth = 6; TextAlignment = AlignmentType.Right; CounterWidth = 32; Font = CounterFont.MoneyFont; } protected override void Dispose(bool disposing, bool isNativeDestructor) { if (disposing) { if (!isNativeDestructor) { PartsList.Dispose(); Node->PartsList = null; } base.Dispose(disposing, isNativeDestructor); } } protected string TexturePath { get => PartsList[0]->LoadedPath; set => PartsList[0]->LoadTexture(value); } protected Vector2 TextureCoordinates { get => new(PartsList[0]->U, PartsList[0]->V); set { PartsList[0]->U = (ushort) value.X; PartsList[0]->V = (ushort) value.X; } } protected Vector2 TextureSize { get => new(PartsList[0]->Width, PartsList[0]->Height); set { PartsList[0]->Width = (ushort) value.X; PartsList[0]->Height = (ushort) value.X; } } public uint NumberWidth { get => Node->NumberWidth; set => Node->NumberWidth = (byte)value; } public uint CommaWidth { get => Node->CommaWidth; set => Node->CommaWidth = (byte)value; } public uint SpaceWidth { get => Node->SpaceWidth; set => Node->SpaceWidth = (byte)value; } public AlignmentType TextAlignment { get => (AlignmentType) Node->TextAlign; set => Node->TextAlign = (ushort) value; } public float CounterWidth { get => Node->CounterWidth; set => Node->CounterWidth = value; } public int Number { get => int.Parse(Node->NodeText.ToString()); set => Node->SetText(ParseNumber(value)); } public ReadOnlySeString String { get => Node->NodeText.AsSpan(); set => Node->SetText(ParseString(value)); } public CounterFont Font { get; set { field = value; var fontPath = string.Empty; var partSize = Vector2.Zero; switch (value) { case CounterFont.MoneyFont: fontPath = "ui/uld/Money_Number.tex"; partSize = new Vector2(22.0f, 22.0f); break; case CounterFont.ChocoboRace: fontPath = "ui/uld/RaceChocoboNum.tex"; partSize = new Vector2(30.0f, 60.0f); break; } if (fontPath != string.Empty && partSize != Vector2.Zero) { PartsList[0]->Width = (ushort)partSize.X; PartsList[0]->Height = (ushort)partSize.Y; PartsList[0]->LoadTexture(fontPath); } } } private static ReadOnlySeString ParseString(ReadOnlySeString value) { using var builder = new RentedSeStringBuilder(); return builder.Builder.Append(value).GetViewAsSpan(); } private static ReadOnlySeString ParseNumber(int value) { using var rentedBuilder = new RentedSeStringBuilder(); // var evaluatedString = DalamudInterface.Instance.SeStringEvaluator.EvaluateFromAddon(18, [ value ]); foreach (var payload in evaluatedString) { switch (payload.Type) { // Fix for French thousands separators. // The game calls FormatAddonText2 that does this. case ReadOnlySePayloadType.Macro when payload.MacroCode is MacroCode.NonBreakingSpace: rentedBuilder.Builder.Append(' '); break; default: rentedBuilder.Builder.Append(payload); break; } } return rentedBuilder.Builder.GetViewAsSpan(); } }