diff --git a/Dalamud/Game/Chat/SeStringHandling/Payload.cs b/Dalamud/Game/Chat/SeStringHandling/Payload.cs index 2320a8dfb..07baef08e 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payload.cs @@ -8,7 +8,6 @@ using Serilog; // TODOs: // - refactor integer handling now that we have multiple packed types -// - wrapper class(es) for handling of composite links in chat (item, map etc) and formatting operations // Maybes: // - convert parsing to custom structs for each payload? would make some code prettier and easier to work with // but also wouldn't work out as well for things that are dynamically-sized @@ -22,18 +21,41 @@ namespace Dalamud.Game.Chat.SeStringHandling /// public abstract class Payload { + /// + /// The type of this payload. + /// public abstract PayloadType Type { get; } + /// + /// Whether this payload has been modified since the last Encode(). + /// public bool Dirty { get; protected set; } = true; + /// + /// Encodes the internal state of this payload into a byte[] suitable for sending to in-game + /// handlers such as the chat log. + /// + /// Encoded binary payload data suitable for use with in-game handlers. protected abstract byte[] EncodeImpl(); + // TODO: endOfStream is somewhat legacy now that payload length is always handled correctly. + // This could be changed to just take a straight byte[], but that would complicate reading + // but we could probably at least remove the end param + /// + /// Decodes a byte stream from the game into a payload object. + /// + /// A BinaryReader containing at least all the data for this payload. + /// The location holding the end of the data for this payload. protected abstract void DecodeImpl(BinaryReader reader, long endOfStream); - // :( + /// + /// The Lumina instance to use for any necessary data lookups. + /// protected DataManager dataResolver; - protected byte[] encodedData; + // private for now, since subclasses shouldn't interact with this + // To force-invalidate it, Dirty can be set to true + private byte[] encodedData; protected Payload() { @@ -45,6 +67,11 @@ namespace Dalamud.Game.Chat.SeStringHandling this.dataResolver = SeString.Dalamud.Data; } + /// + /// Encode this payload object into a byte[] useable in-game for things like the chat log. + /// + /// If true, ignores any cached value and forcibly reencodes the payload from its internal representation. + /// A byte[] suitable for use with in-game handlers such as the chat log. public byte[] Encode(bool force = false) { if (Dirty || force) @@ -56,6 +83,11 @@ namespace Dalamud.Game.Chat.SeStringHandling return this.encodedData; } + /// + /// Decodes a binary representation of a payload into its corresponding nice object payload. + /// + /// A reader positioned at the start of the payload, and containing at least one entire payload. + /// The constructed Payload-derived object that was decoded from the binary data. public static Payload Decode(BinaryReader reader) { var payloadStartPos = reader.BaseStream.Position; @@ -96,6 +128,7 @@ namespace Dalamud.Game.Chat.SeStringHandling var packetStart = reader.BaseStream.Position; + // any unhandled payload types will be turned into a RawPayload with the exact same binary data switch (chunkType) { case SeStringChunkType.EmphasisItalic: @@ -197,6 +230,10 @@ namespace Dalamud.Game.Chat.SeStringHandling LinkTerminator = 0xCF // not clear but seems to always follow a link } + + // TODO - everything below needs to be completely refactored, now that we have run into + // a lot more cases than were originally handled. + protected enum IntegerType { // used as an internal marker; sometimes single bytes are bare with no marker at all @@ -205,7 +242,6 @@ namespace Dalamud.Game.Chat.SeStringHandling Byte = 0xF0, ByteTimes256 = 0xF1, Int16 = 0xF2, - // ByteTimes65536 = 0xF3, // from RE but never seen Int16Packed = 0xF4, // seen in map links, seemingly 2 8-bit values packed into 2 bytes with only one marker Int24Special = 0xF6, // unsure how different form Int24 - used for hq items that add 1 million, also used for normal 24-bit values in map links Int24 = 0xFA, diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs index 31f2a9243..3c022d8ec 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs @@ -8,11 +8,20 @@ using System.Linq; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload containing an auto-translation/completion chat message. + /// public class AutoTranslatePayload : Payload, ITextProvider { public override PayloadType Type => PayloadType.AutoTranslateText; private string text; + /// + /// The actual text displayed in-game for this payload. + /// + /// + /// Value is evaluated lazily and cached. + /// public string Text { get @@ -29,6 +38,15 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads internal AutoTranslatePayload() { } + /// + /// Creates a new auto-translate payload. + /// + /// The group id for this message. + /// The key/row id for this message. Which table this is in depends on the group id and details the Completion table. + /// + /// This table is somewhat complicated in structure, and so using this constructor may not be very nice. + /// There is probably little use to create one of these, however. + /// public AutoTranslatePayload(uint group, uint key) { this.group = group; @@ -39,7 +57,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override string ToString() { - return $"{Type} - Group: {group}, Key: {key}"; + return $"{Type} - Group: {group}, Key: {key}, Text: {Text}"; } protected override byte[] EncodeImpl() diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/EmphasisItalicPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/EmphasisItalicPayload.cs index 19909f516..341439edb 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/EmphasisItalicPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/EmphasisItalicPayload.cs @@ -4,17 +4,37 @@ using System.IO; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { - class EmphasisItalicPayload : Payload + /// + /// An SeString Payload containing information about enabling or disabling italics formatting on following text. + /// + /// + /// As with other formatting payloads, this is only useful in a payload block, where it affects any subsequent + /// text payloads. + /// + public class EmphasisItalicPayload : Payload { + /// + /// Payload representing enabling italics on following text. + /// public static EmphasisItalicPayload ItalicsOn => new EmphasisItalicPayload(true); + /// + /// Payload representing disabling italics on following text. + /// public static EmphasisItalicPayload ItalicsOff => new EmphasisItalicPayload(false); public override PayloadType Type => PayloadType.EmphasisItalic; + /// + /// Whether this payload enables italics formatting for following text. + /// public bool IsEnabled { get; private set; } internal EmphasisItalicPayload() { } + /// + /// Creates an EmphasisItalicPayload. + /// + /// Whether italics formatting should be enabled or disabled for following text. public EmphasisItalicPayload(bool enabled) { IsEnabled = enabled; diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs index da09fc5d0..87b3ab87d 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs @@ -7,11 +7,20 @@ using System.Text; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload representing an interactable item link. + /// public class ItemPayload : Payload { public override PayloadType Type => PayloadType.Item; private Item item; + /// + /// The underlying Lumina Item represented by this payload. + /// + /// + /// Value is evaluated lazily and cached. + /// public Item Item { get @@ -22,7 +31,14 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } // mainly to allow overriding the name (for things like owo) - private string displayName; + // TODO: even though this is present in some item links, it may not really have a use at all + // For things like owo, changing the text payload is probably correct, whereas changing the + // actual embedded name might not work properly. + private string displayName = null; + /// + /// The displayed name for this item link. Note that incoming links only sometimes have names embedded, + /// often the name is only present in a following text payload. + /// public string DisplayName { get @@ -37,12 +53,23 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + /// + /// Whether or not this item link is for a high-quality version of the item. + /// public bool IsHQ { get; private set; } = false; private uint itemId; internal ItemPayload() { } + /// + /// Creates a payload representing an interactable item link for the specified item. + /// + /// The id of the item. + /// Whether or not the link should be for the high-quality variant of the item. + /// An optional name to include in the item link. Typically this should + /// be left as null, or set to the normal item name. Actual overrides are better done with the subsequent + /// TextPayload that is a part of a full item link in chat. public ItemPayload(uint itemId, bool isHQ, string displayNameOverride = null) { this.itemId = itemId; @@ -52,7 +79,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override string ToString() { - return $"{Type} - ItemId: {itemId}, IsHQ: {IsHQ}"; + return $"{Type} - ItemId: {itemId}, IsHQ: {IsHQ}, Name: {this.displayName ?? Item.Name}"; } protected override byte[] EncodeImpl() diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs index 30d11e5fe..e73eb8393 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs @@ -5,11 +5,20 @@ using System.IO; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload representing an interactable map position link. + /// public class MapLinkPayload : Payload { public override PayloadType Type => PayloadType.MapLink; private Map map; + /// + /// The Map specified for this map link. + /// + /// + /// Value is evaluated lazily and cached. + /// public Map Map { get @@ -20,6 +29,12 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } private TerritoryType territoryType; + /// + /// The TerritoryType specified for this map link. + /// + /// + /// Value is evaluated lazily and cached. + /// public TerritoryType TerritoryType { get @@ -29,23 +44,43 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + /// + /// The internal x-coordinate for this map position. + /// + public int RawX { get; private set; } + + /// + /// The internal y-coordinate for this map position. + /// + public int RawY { get; private set; } + // these could be cached, but this isn't really too egregious + /// + /// The readable x-coordinate position for this map link. This value is approximate and unrounded. + /// public float XCoord { get { - return ConvertRawPositionToMapCoordinate(this.rawX, Map.SizeFactor); + return ConvertRawPositionToMapCoordinate(RawX, Map.SizeFactor); } } + /// + /// The readable y-coordinate position for this map link. This value is approximate and unrounded. + /// public float YCoord { get { - return ConvertRawPositionToMapCoordinate(this.rawY, Map.SizeFactor); + return ConvertRawPositionToMapCoordinate(RawY, Map.SizeFactor); } } + /// + /// The printable map coordinates for this link. This value tries to match the in-game printable text as closely as possible + /// but is an approximation and may be slightly off for some positions. + /// public string CoordinateString { get @@ -57,11 +92,15 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads var x = Math.Truncate((XCoord+fudge) * 10.0f) / 10.0f; var y = Math.Truncate((YCoord+fudge) * 10.0f) / 10.0f; + // the formatting and spacing the game uses return $"( {x.ToString("0.0")} , {y.ToString("0.0")} )"; } } private string placeNameRegion; + /// + /// The region name for this map link. This corresponds to the upper zone name found in the actual in-game map UI. eg, "La Noscea" + /// public string PlaceNameRegion { get @@ -72,6 +111,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } private string placeName; + /// + /// The place name for this map link. This corresponds to the lower zone name found in the actual in-game map UI. eg, "Limsa Lominsa Upper Decks" + /// public string PlaceName { get @@ -81,16 +123,25 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } - public string DataString => $"m:{TerritoryType.RowId},{Map.RowId},{rawX},{rawY}"; + /// + /// The data string for this map link, for use by internal game functions that take a string variant and not a binary payload. + /// + public string DataString => $"m:{TerritoryType.RowId},{Map.RowId},{RawX},{RawY}"; private uint territoryTypeId; private uint mapId; - private int rawX; - private int rawY; // there is no Z; it's purely in the text payload where applicable internal MapLinkPayload() { } + /// + /// Creates an interactable MapLinkPayload from a human-readable position. + /// + /// The id of the TerritoryType entry for this link. + /// The id of the Map entry for this link. + /// The human-readable x-coordinate for this link. + /// The human-readable y-coordinate for this link. + /// An optional offset to account for rounding and truncation errors; it is best to leave this untouched in most cases. public MapLinkPayload(uint territoryTypeId, uint mapId, float niceXCoord, float niceYCoord, float fudgeFactor = 0.05f) { this.territoryTypeId = territoryTypeId; @@ -98,28 +149,35 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads // this fudge is necessary basically to ensure we don't shift down a full tenth // because essentially values are truncated instead of rounded, so 3.09999f will become // 3.0f and not 3.1f - this.rawX = this.ConvertMapCoordinateToRawPosition(niceXCoord + fudgeFactor, Map.SizeFactor); - this.rawY = this.ConvertMapCoordinateToRawPosition(niceYCoord + fudgeFactor, Map.SizeFactor); + RawX = this.ConvertMapCoordinateToRawPosition(niceXCoord + fudgeFactor, Map.SizeFactor); + RawY = this.ConvertMapCoordinateToRawPosition(niceYCoord + fudgeFactor, Map.SizeFactor); } + /// + /// Creates an interactable MapLinkPayload from a raw position. + /// + /// The id of the TerritoryType entry for this link. + /// The id of the Map entry for this link. + /// The internal raw x-coordinate for this link. + /// The internal raw y-coordinate for this link. public MapLinkPayload(uint territoryTypeId, uint mapId, int rawX, int rawY) { this.territoryTypeId = territoryTypeId; this.mapId = mapId; - this.rawX = rawX; - this.rawY = rawY; + RawX = rawX; + RawY = rawY; } public override string ToString() { - return $"{Type} - TerritoryTypeId: {territoryTypeId}, MapId: {mapId}, RawX: {rawX}, RawY: {rawY}"; + return $"{Type} - TerritoryTypeId: {territoryTypeId}, MapId: {mapId}, RawX: {RawX}, RawY: {RawY}, display: {PlaceName} {CoordinateString}"; } protected override byte[] EncodeImpl() { var packedTerritoryAndMapBytes = MakePackedInteger(this.territoryTypeId, this.mapId); - var xBytes = MakeInteger(unchecked((uint)this.rawX)); - var yBytes = MakeInteger(unchecked((uint)this.rawY)); + var xBytes = MakeInteger(unchecked((uint)RawX)); + var yBytes = MakeInteger(unchecked((uint)RawY)); var chunkLen = 4 + packedTerritoryAndMapBytes.Length + xBytes.Length + yBytes.Length; @@ -149,8 +207,8 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads try { (this.territoryTypeId, this.mapId) = GetPackedIntegers(reader); - this.rawX = unchecked((int)GetInteger(reader)); - this.rawY = unchecked((int)GetInteger(reader)); + RawX = unchecked((int)GetInteger(reader)); + RawY = unchecked((int)GetInteger(reader)); // the Z coordinate is never in this chunk, just the text (if applicable) // seems to always be FF 01 diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs index 6a05f1a74..d2c0cbfb8 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs @@ -6,11 +6,17 @@ using System.Text; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload representing a player link. + /// public class PlayerPayload : Payload { public override PayloadType Type => PayloadType.Player; private string playerName; + /// + /// The player's displayed name. This does not contain the server name. + /// public string PlayerName { get { return this.playerName; } @@ -22,6 +28,12 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } private World world; + /// + /// The Lumina object representing the player's home server. + /// + /// + /// Value is evaluated lazily and cached. + /// public World World { get @@ -31,10 +43,21 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + /// + /// A text representation of this player link matching how it might appear in-game. + /// The world name will always be present. + /// + public string DisplayedName => $"{PlayerName}{(char)SeIconChar.CrossWorld}{World.Name}"; + private uint serverId; internal PlayerPayload() { } + /// + /// Create a PlayerPayload link for the specified player. + /// + /// The player's displayed name. + /// The player's home server id. public PlayerPayload(string playerName, uint serverId) { this.playerName = playerName; @@ -43,7 +66,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override string ToString() { - return $"{Type} - PlayerName: {PlayerName}, ServerId: {serverId}"; + return $"{Type} - PlayerName: {PlayerName}, ServerId: {serverId}, ServerName: {World.Name}"; } protected override byte[] EncodeImpl() diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs index 2a1d04b55..301bde07e 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs @@ -5,21 +5,36 @@ using System.Linq; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload representing unhandled raw payload data. + /// Mainly useful for constructing unhandled hardcoded payloads, or forwarding any unknown + /// payloads without modification. + /// public class RawPayload : Payload { // this and others could be an actual static member somewhere and avoid construction costs, but that probably isn't a real concern + /// + /// A fixed Payload representing a common link-termination sequence, found in many payload chains. + /// public static RawPayload LinkTerminator => new RawPayload(new byte[] { 0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03 }); public override PayloadType Type => PayloadType.Unknown; private byte[] data; + // this is a bit different from the underlying data + // We need to store just the chunk data for decode to behave nicely, but when reading data out + // it makes more sense to get the entire payload + /// + /// The entire payload byte sequence for this payload. + /// The returned data is a clone and modifications will not be persisted. + /// public byte[] Data { get { // for now don't allow modifying the contents // because we don't really have a way to track Dirty - return (byte[])data.Clone(); + return (byte[])Encode().Clone(); } } @@ -34,13 +49,14 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { // this payload is 'special' in that we require the entire chunk to be passed in // and not just the data after the header + // This sets data to hold the chunk data fter the header, excluding the END_BYTE this.chunkType = data[1]; this.data = data.Skip(3).Take(data.Length-4).ToArray(); } public override string ToString() { - return $"{Type} - Chunk type: {chunkType:X}, Data: {BitConverter.ToString(data).Replace("-", " ")}"; + return $"{Type} - Data: {BitConverter.ToString(Data).Replace("-", " ")}"; } protected override byte[] EncodeImpl() diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs index 3b822c239..d5753e40c 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs @@ -5,11 +5,20 @@ using System.IO; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload representing an interactable status link. + /// public class StatusPayload : Payload { public override PayloadType Type => PayloadType.Status; private Status status; + /// + /// The Lumina Status object represented by this payload. + /// + /// + /// Value is evaluated lazily and cached. + /// public Status Status { get @@ -23,6 +32,10 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads internal StatusPayload() { } + /// + /// Creates a new StatusPayload for the given status id. + /// + /// The id of the Status for this link. public StatusPayload(uint statusId) { this.statusId = statusId; @@ -30,7 +43,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override string ToString() { - return $"{Type} - StatusId: {statusId}"; + return $"{Type} - StatusId: {statusId}, Name: {Status.Name}"; } protected override byte[] EncodeImpl() diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs index 5b3fc9882..ae7bcb23b 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs @@ -5,12 +5,19 @@ using System.Text; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload representing a plain text string. + /// public class TextPayload : Payload, ITextProvider { public override PayloadType Type => PayloadType.RawText; // allow modifying the text of existing payloads on the fly private string text; + /// + /// The text contained in this payload. + /// This may contain SE's special unicode characters. + /// public string Text { get { return this.text; } @@ -28,6 +35,10 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads internal TextPayload() { } + /// + /// Creates a new TextPayload for the given text. + /// + /// The text to include for this payload. public TextPayload(string text) { this.text = text; diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs index 8d7290058..9a8e5780c 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs @@ -5,15 +5,30 @@ using System.IO; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload representing a UI foreground color applied to following text payloads. + /// public class UIForegroundPayload : Payload { + /// + /// Payload representing disabling foreground color on following text. + /// public static UIForegroundPayload UIForegroundOff => new UIForegroundPayload(0); public override PayloadType Type => PayloadType.UIForeground; + /// + /// Whether or not this payload represents applying a foreground color, or disabling one. + /// public bool IsEnabled => ColorKey != 0; private UIColor color; + /// + /// A Lumina UIColor object representing this payload. The actual color data is at UIColor.UIForeground + /// + /// + /// Value is evaluated lazily and cached. + /// public UIColor UIColor { get @@ -23,6 +38,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + /// + /// The color key used as a lookup in the UIColor table for this foreground color. + /// public ushort ColorKey { get { return this.colorKey; } @@ -34,6 +52,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + /// + /// The Red/Green/Blue values for this foreground color, encoded as a typical hex color. + /// public uint RGB { get @@ -46,6 +67,10 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads internal UIForegroundPayload() { } + /// + /// Creates a new UIForegroundPayload for the given UIColor key. + /// + /// public UIForegroundPayload(ushort colorKey) { this.colorKey = colorKey; @@ -53,7 +78,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override string ToString() { - return $"{Type} - UIColor: {colorKey}"; + return $"{Type} - UIColor: {colorKey} color: {(IsEnabled ? RGB : 0)}"; } protected override byte[] EncodeImpl() diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs index fcabfe742..7731b431a 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs @@ -5,15 +5,30 @@ using System.IO; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload representing a UI glow color applied to following text payloads. + /// public class UIGlowPayload : Payload { + /// + /// Payload representing disabling glow color on following text. + /// public static UIGlowPayload UIGlowOff => new UIGlowPayload(0); public override PayloadType Type => PayloadType.UIGlow; + /// + /// Whether or not this payload represents applying a glow color, or disabling one. + /// public bool IsEnabled => ColorKey != 0; private UIColor color; + /// + /// A Lumina UIColor object representing this payload. The actual color data is at UIColor.UIGlow + /// + /// + /// Value is evaluated lazily and cached. + /// public UIColor UIColor { get @@ -23,6 +38,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + /// + /// The color key used as a lookup in the UIColor table for this glow color. + /// public ushort ColorKey { get { return this.colorKey; } @@ -34,6 +52,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + /// + /// The Red/Green/Blue values for this glow color, encoded as a typical hex color. + /// public uint RGB { get @@ -46,6 +67,10 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads internal UIGlowPayload() { } + /// + /// Creates a new UIForegroundPayload for the given UIColor key. + /// + /// public UIGlowPayload(ushort colorKey) { this.colorKey = colorKey; @@ -53,7 +78,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override string ToString() { - return $"{Type} - UIColor: {colorKey}"; + return $"{Type} - UIColor: {colorKey} color: {(IsEnabled ? RGB : 0)}"; } protected override byte[] EncodeImpl() diff --git a/Dalamud/Game/Chat/SeStringHandling/SeString.cs b/Dalamud/Game/Chat/SeStringHandling/SeString.cs index 01f01d593..26e7e7342 100644 --- a/Dalamud/Game/Chat/SeStringHandling/SeString.cs +++ b/Dalamud/Game/Chat/SeStringHandling/SeString.cs @@ -14,6 +14,9 @@ namespace Dalamud.Game.Chat.SeStringHandling // TODO: probably change how this is done/where it comes from internal static Dalamud Dalamud { get; set; } + /// + /// The ordered list of payloads included in this SeString. + /// public List Payloads { get; } /// @@ -56,30 +59,54 @@ namespace Dalamud.Game.Chat.SeStringHandling return new SeString(payloads); } + /// + /// Creates a new SeString from an ordered list of payloads. + /// + /// The Payload objects to make up this string. public SeString(List payloads) { Payloads = payloads; } + /// + /// Appends the contents of one SeString to this one. + /// + /// The SeString to append to this one. + /// This object. public SeString Append(SeString other) { Payloads.AddRange(other.Payloads); return this; } + /// + /// Appends a list of payloads to this SeString. + /// + /// The Payloads to append. + /// This object. public SeString Append(List payloads) { Payloads.AddRange(payloads); return this; } + /// + /// Appends a single payload to this SeString. + /// + /// The payload to append. + /// This object. public SeString Append(Payload payload) { Payloads.Add(payload); return this; } - internal byte[] Encode() + /// + /// Encodes the Payloads in this SeString into a binary representation + /// suitable for use by in-game handlers, such as the chat log. + /// + /// The binary encoded payload data. + public byte[] Encode() { var messageBytes = new List(); foreach (var p in Payloads) diff --git a/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs b/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs index 4a5dbfc9f..e7b4cc43f 100644 --- a/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs +++ b/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs @@ -8,8 +8,18 @@ using DalamudItem = Dalamud.Data.TransientSheet.Item; namespace Dalamud.Game.Chat.SeStringHandling { - public class SeStringUtils + /// + /// A utility class for working with common SeString variants. + /// + public static class SeStringUtils { + /// + /// Creates an SeString representing an entire Payload chain that can be used to link an item in the chat log. + /// + /// The id of the item to link. + /// Whether to link the high-quality variant of the item. + /// An optional name override to display, instead of the actual item name. + /// An SeString containing all the payloads necessary to display an item link in the chat log. public static SeString CreateItemLink(uint itemId, bool isHQ, string displayNameOverride = null) { string displayName = displayNameOverride ?? SeString.Dalamud.Data.GetExcelSheet().GetRow((int)itemId).Name; @@ -35,11 +45,44 @@ namespace Dalamud.Game.Chat.SeStringHandling return new SeString(payloads); } + /// + /// Creates an SeString representing an entire Payload chain that can be used to link an item in the chat log. + /// + /// The Lumina Item to link. + /// Whether to link the high-quality variant of the item. + /// An optional name override to display, instead of the actual item name. + /// An SeString containing all the payloads necessary to display an item link in the chat log. public static SeString CreateItemLink(DalamudItem item, bool isHQ, string displayNameOverride = null) { return CreateItemLink((uint)item.RowId, isHQ, displayNameOverride ?? item.Name); } + public static SeString CreateMapLink(uint territoryId, uint mapId, int rawX, int rawY) + { + var mapPayload = new MapLinkPayload(territoryId, mapId, rawX, rawY); + var nameString = $"{mapPayload.PlaceName} {mapPayload.CoordinateString}"; + + var payloads = new List(new Payload[] + { + mapPayload, + // arrow goes here + new TextPayload(nameString), + RawPayload.LinkTerminator + }); + payloads.InsertRange(1, TextArrowPayloads()); + + return new SeString(payloads); + } + + /// + /// Creates an SeString representing an entire Payload chain that can be used to link a map position in the chat log. + /// + /// The id of the TerritoryType for this map link. + /// The id of the Map for this map link. + /// The human-readable x-coordinate for this link. + /// The human-readable y-coordinate for this link. + /// An optional offset to account for rounding and truncation errors; it is best to leave this untouched in most cases. + /// An SeString containing all of the payloads necessary to display a map link in the chat log. public static SeString CreateMapLink(uint territoryId, uint mapId, float xCoord, float yCoord, float fudgeFactor = 0.05f) { var mapPayload = new MapLinkPayload(territoryId, mapId, xCoord, yCoord, fudgeFactor); @@ -57,6 +100,14 @@ namespace Dalamud.Game.Chat.SeStringHandling return new SeString(payloads); } + /// + /// Creates an SeString representing an entire Payload chain that can be used to link a map position in the chat log, matching a specified zone name. + /// + /// The name of the location for this link. This should be exactly the name as seen in a displayed map link in-game for the same zone. + /// The human-readable x-coordinate for this link. + /// The human-readable y-coordinate for this link. + /// An optional offset to account for rounding and truncation errors; it is best to leave this untouched in most cases. + /// An SeString containing all of the payloads necessary to display a map link in the chat log. public static SeString CreateMapLink(string placeName, float xCoord, float yCoord, float fudgeFactor = 0.05f) { var mapSheet = SeString.Dalamud.Data.GetExcelSheet(); @@ -78,6 +129,11 @@ namespace Dalamud.Game.Chat.SeStringHandling return null; } + /// + /// Creates a list of Payloads necessary to display the arrow link marker icon in chat + /// with the appropriate glow and coloring. + /// + /// A list of all the payloads required to insert the link marker. public static List TextArrowPayloads() { return new List(new Payload[] diff --git a/Dalamud/Game/Chat/XivChatType.cs b/Dalamud/Game/Chat/XivChatType.cs index 7e8eb5326..b0f04a93b 100644 --- a/Dalamud/Game/Chat/XivChatType.cs +++ b/Dalamud/Game/Chat/XivChatType.cs @@ -6,7 +6,7 @@ namespace Dalamud.Game.Chat /// /// The FFXIV chat types as seen in the LogKind ex table. /// - public enum XivChatType : ushort + public enum XivChatType : ushort // FIXME: this is a single byte { None = 0, Debug = 1,