diff --git a/Dalamud/Game/Chat/SeStringHandling/Payload.cs b/Dalamud/Game/Chat/SeStringHandling/Payload.cs index eb02e4fe3..b7a900b2c 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payload.cs @@ -151,6 +151,10 @@ namespace Dalamud.Game.Chat.SeStringHandling payload = new StatusPayload(); break; + case EmbeddedInfoType.QuestLink: + payload = new QuestPayload(); + break; + case EmbeddedInfoType.LinkTerminator: // this has no custom handling and so needs to fallthrough to ensure it is captured default: @@ -225,6 +229,7 @@ namespace Dalamud.Game.Chat.SeStringHandling PlayerName = 0x01, ItemLink = 0x03, MapPositionLink = 0x04, + QuestLink = 0x05, Status = 0x09, LinkTerminator = 0xCF // not clear but seems to always follow a link diff --git a/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs b/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs index b3fca980e..060d14093 100644 --- a/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs +++ b/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs @@ -47,6 +47,10 @@ namespace Dalamud.Game.Chat.SeStringHandling /// Icon, /// + /// A SeString payload representing a quest link. + /// + Quest, + /// /// An SeString payload representing any data we don't handle. /// Unknown diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/QuestPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/QuestPayload.cs new file mode 100644 index 000000000..ac802755e --- /dev/null +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/QuestPayload.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Dalamud.Data; +using Lumina.Excel.GeneratedSheets; +using Newtonsoft.Json; + +namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload representing an interactable quest link. + /// + public class QuestPayload : Payload { + public override PayloadType Type => PayloadType.Quest; + + private Quest quest; + /// + /// The underlying Lumina Quest represented by this payload. + /// + /// + /// Value is evaluated lazily and cached. + /// + + [JsonIgnore] + public Quest Quest { + get { + this.quest ??= this.DataResolver.GetExcelSheet().GetRow(this.questId); + return this.quest; + } + } + + [JsonProperty] + private uint questId; + + internal QuestPayload() { } + + /// + /// Creates a payload representing an interactable quest link for the specified quest. + /// + /// DataManager instance needed to resolve game data. + /// The id of the quest. + public QuestPayload(DataManager data, uint questId) { + this.DataResolver = data; + this.questId = questId; + } + + /// + public override string ToString() { + return $"{Type} - QuestId: {this.questId}, Name: {Quest?.Name ?? "QUEST NOT FOUND"}"; + } + + protected override byte[] EncodeImpl() { + var idBytes = MakeInteger((ushort) this.questId); + var chunkLen = idBytes.Length + 4; + + var bytes = new List() { + START_BYTE, (byte) SeStringChunkType.Interactable, (byte) chunkLen, (byte) EmbeddedInfoType.QuestLink, + }; + + bytes.AddRange(idBytes); + bytes.AddRange(new byte[] {0x01, 0x01, END_BYTE}); + return bytes.ToArray(); + + } + + protected override void DecodeImpl(BinaryReader reader, long endOfStream) { + // Game uses int16, Luimina uses int32 + this.questId = GetInteger(reader) + 65536; + } + } +}