mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-31 21:03:43 +01:00
StyleCop: everything else
This commit is contained in:
parent
f64c9b8321
commit
595fd3f1e4
134 changed files with 16346 additions and 6202 deletions
|
|
@ -1,120 +1,438 @@
|
|||
#pragma warning disable 1591
|
||||
namespace Dalamud.Game.Text.SeStringHandling
|
||||
{
|
||||
/// <summary>
|
||||
/// This class represents special icons that can appear in chat naturally or as IconPayloads.
|
||||
/// </summary>
|
||||
public enum BitmapFontIcon : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// No icon.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
namespace Dalamud.Game.Text.SeStringHandling {
|
||||
public enum BitmapFontIcon : uint {
|
||||
None,
|
||||
ControllerDPadUp,
|
||||
ControllerDPadDown,
|
||||
ControllerDPadLeft,
|
||||
ControllerDPadRight,
|
||||
ControllerDPadUpDown,
|
||||
ControllerDPadLeftRight,
|
||||
ControllerDPadAll,
|
||||
/// <summary>
|
||||
/// The controller D-pad up icon.
|
||||
/// </summary>
|
||||
ControllerDPadUp = 1,
|
||||
|
||||
ControllerButton0, // Xbox B / PS Circle
|
||||
ControllerButton1, // Xbox A / PS Cross
|
||||
ControllerButton2, // Xbox X / PS Square
|
||||
ControllerButton3, // Xbox Y / PS Triangle
|
||||
/// <summary>
|
||||
/// The controller D-pad down icon.
|
||||
/// </summary>
|
||||
ControllerDPadDown = 2,
|
||||
|
||||
ControllerShoulderLeft,
|
||||
ControllerShoulderRight,
|
||||
/// <summary>
|
||||
/// The controller D-pad left icon.
|
||||
/// </summary>
|
||||
ControllerDPadLeft = 3,
|
||||
|
||||
ControllerTriggerLeft,
|
||||
ControllerTriggerRight,
|
||||
/// <summary>
|
||||
/// The controller D-pad right icon.
|
||||
/// </summary>
|
||||
ControllerDPadRight = 4,
|
||||
|
||||
ControllerAnalogLeftStickIn,
|
||||
ControllerAnalogRightStickIn,
|
||||
/// <summary>
|
||||
/// The controller D-pad up/down icon.
|
||||
/// </summary>
|
||||
ControllerDPadUpDown = 5,
|
||||
|
||||
ControllerStart,
|
||||
ControllerBack,
|
||||
/// <summary>
|
||||
/// The controller D-pad left/right icon.
|
||||
/// </summary>
|
||||
ControllerDPadLeftRight = 6,
|
||||
|
||||
ControllerAnalogLeftStick,
|
||||
ControllerAnalogLeftStickUpDown,
|
||||
ControllerAnalogLeftStickLeftRight,
|
||||
/// <summary>
|
||||
/// The controller D-pad all directions icon.
|
||||
/// </summary>
|
||||
ControllerDPadAll = 7,
|
||||
|
||||
ControllerAnalogRightStick,
|
||||
ControllerAnalogRightStickUpDown,
|
||||
ControllerAnalogRightStickLeftRight,
|
||||
/// <summary>
|
||||
/// The controller button 0 icon (Xbox: B, PlayStation: Circle).
|
||||
/// </summary>
|
||||
ControllerButton0 = 8,
|
||||
|
||||
/// <summary>
|
||||
/// The controller button 1 icon (XBox: A, PlayStation: Cross).
|
||||
/// </summary>
|
||||
ControllerButton1 = 9,
|
||||
|
||||
/// <summary>
|
||||
/// The controller button 2 icon (XBox: X, PlayStation: Square).
|
||||
/// </summary>
|
||||
ControllerButton2 = 10,
|
||||
|
||||
/// <summary>
|
||||
/// The controller button 3 icon (BBox: Y, PlayStation: Triangle).
|
||||
/// </summary>
|
||||
ControllerButton3 = 11,
|
||||
|
||||
/// <summary>
|
||||
/// The controller left shoulder button icon.
|
||||
/// </summary>
|
||||
ControllerShoulderLeft = 12,
|
||||
|
||||
/// <summary>
|
||||
/// The controller right shoulder button icon.
|
||||
/// </summary>
|
||||
ControllerShoulderRight = 13,
|
||||
|
||||
/// <summary>
|
||||
/// The controller left trigger button icon.
|
||||
/// </summary>
|
||||
ControllerTriggerLeft = 14,
|
||||
|
||||
/// <summary>
|
||||
/// The controller right trigger button icon.
|
||||
/// </summary>
|
||||
ControllerTriggerRight = 15,
|
||||
|
||||
/// <summary>
|
||||
/// The controller left analog stick in icon.
|
||||
/// </summary>
|
||||
ControllerAnalogLeftStickIn = 16,
|
||||
|
||||
/// <summary>
|
||||
/// The controller right analog stick in icon.
|
||||
/// </summary>
|
||||
ControllerAnalogRightStickIn = 17,
|
||||
|
||||
/// <summary>
|
||||
/// The controller start button icon.
|
||||
/// </summary>
|
||||
ControllerStart = 18,
|
||||
|
||||
/// <summary>
|
||||
/// The controller back button icon.
|
||||
/// </summary>
|
||||
ControllerBack = 19,
|
||||
|
||||
/// <summary>
|
||||
/// The controller left analog stick icon.
|
||||
/// </summary>
|
||||
ControllerAnalogLeftStick = 20,
|
||||
|
||||
/// <summary>
|
||||
/// The controller left analog stick up/down icon.
|
||||
/// </summary>
|
||||
ControllerAnalogLeftStickUpDown = 21,
|
||||
|
||||
/// <summary>
|
||||
/// The controller left analog stick left/right icon.
|
||||
/// </summary>
|
||||
ControllerAnalogLeftStickLeftRight = 22,
|
||||
|
||||
/// <summary>
|
||||
/// The controller right analog stick icon.
|
||||
/// </summary>
|
||||
ControllerAnalogRightStick = 23,
|
||||
|
||||
/// <summary>
|
||||
/// The controller right analog stick up/down icon.
|
||||
/// </summary>
|
||||
ControllerAnalogRightStickUpDown = 24,
|
||||
|
||||
/// <summary>
|
||||
/// The controller right analog stick left/right icon.
|
||||
/// </summary>
|
||||
ControllerAnalogRightStickLeftRight = 25,
|
||||
|
||||
/// <summary>
|
||||
/// The La Noscea region icon.
|
||||
/// </summary>
|
||||
LaNoscea = 51,
|
||||
BlackShroud,
|
||||
Thanalan,
|
||||
AutoTranslateBegin,
|
||||
AutoTranslateEnd,
|
||||
ElementFire,
|
||||
ElementIce,
|
||||
ElementWind,
|
||||
ElementEarth,
|
||||
ElementLightning,
|
||||
ElementWater,
|
||||
LevelSync,
|
||||
Warning,
|
||||
Ishgard,
|
||||
Aetheryte,
|
||||
Aethernet,
|
||||
|
||||
GoldStar,
|
||||
SilverStar,
|
||||
/// <summary>
|
||||
/// The Black Shroud region icon.
|
||||
/// </summary>
|
||||
BlackShroud = 52,
|
||||
|
||||
/// <summary>
|
||||
/// The Thanalan region icon.
|
||||
/// </summary>
|
||||
Thanalan = 53,
|
||||
|
||||
/// <summary>
|
||||
/// The auto translate begin icon.
|
||||
/// </summary>
|
||||
AutoTranslateBegin = 54,
|
||||
|
||||
/// <summary>
|
||||
/// The auto translate end icon.
|
||||
/// </summary>
|
||||
AutoTranslateEnd = 55,
|
||||
|
||||
/// <summary>
|
||||
/// The fire element icon.
|
||||
/// </summary>
|
||||
ElementFire = 56,
|
||||
|
||||
/// <summary>
|
||||
/// The ice element icon.
|
||||
/// </summary>
|
||||
ElementIce = 57,
|
||||
|
||||
/// <summary>
|
||||
/// The wind element icon.
|
||||
/// </summary>
|
||||
ElementWind = 58,
|
||||
|
||||
/// <summary>
|
||||
/// The earth element icon.
|
||||
/// </summary>
|
||||
ElementEarth = 59,
|
||||
|
||||
/// <summary>
|
||||
/// The lightning element icon.
|
||||
/// </summary>
|
||||
ElementLightning = 60,
|
||||
|
||||
/// <summary>
|
||||
/// The water element icon.
|
||||
/// </summary>
|
||||
ElementWater = 61,
|
||||
|
||||
/// <summary>
|
||||
/// The level sync icon.
|
||||
/// </summary>
|
||||
LevelSync = 62,
|
||||
|
||||
/// <summary>
|
||||
/// The warning icon.
|
||||
/// </summary>
|
||||
Warning = 63,
|
||||
|
||||
/// <summary>
|
||||
/// The Ishgard region icon.
|
||||
/// </summary>
|
||||
Ishgard = 64,
|
||||
|
||||
/// <summary>
|
||||
/// The Aetheryte icon.
|
||||
/// </summary>
|
||||
Aetheryte = 65,
|
||||
|
||||
/// <summary>
|
||||
/// The Aethernet icon.
|
||||
/// </summary>
|
||||
Aethernet = 66,
|
||||
|
||||
/// <summary>
|
||||
/// The gold star icon.
|
||||
/// </summary>
|
||||
GoldStar = 67,
|
||||
|
||||
/// <summary>
|
||||
/// The silver star icon.
|
||||
/// </summary>
|
||||
SilverStar = 68,
|
||||
|
||||
/// <summary>
|
||||
/// The green dot icon.
|
||||
/// </summary>
|
||||
GreenDot = 70,
|
||||
SwordUnsheathed,
|
||||
SwordSheathed,
|
||||
|
||||
Dice,
|
||||
/// <summary>
|
||||
/// The unsheathed sword icon.
|
||||
/// </summary>
|
||||
SwordUnsheathed = 71,
|
||||
|
||||
FlyZone,
|
||||
FlyZoneLocked,
|
||||
/// <summary>
|
||||
/// The sheathed sword icon.
|
||||
/// </summary>
|
||||
SwordSheathed = 72,
|
||||
|
||||
NoCircle,
|
||||
/// <summary>
|
||||
/// The dice icon.
|
||||
/// </summary>
|
||||
Dice = 73,
|
||||
|
||||
NewAdventurer,
|
||||
Mentor,
|
||||
MentorPvE,
|
||||
MentorCrafting,
|
||||
MentorPvP,
|
||||
/// <summary>
|
||||
/// The flyable zone icon.
|
||||
/// </summary>
|
||||
FlyZone = 74,
|
||||
|
||||
Tank,
|
||||
Healer,
|
||||
DPS,
|
||||
Crafter,
|
||||
Gatherer,
|
||||
AnyClass,
|
||||
/// <summary>
|
||||
/// The no-flying zone icon.
|
||||
/// </summary>
|
||||
FlyZoneLocked = 75,
|
||||
|
||||
CrossWorld,
|
||||
/// <summary>
|
||||
/// The no-circle/prohibited icon.
|
||||
/// </summary>
|
||||
NoCircle = 76,
|
||||
|
||||
FateSlay,
|
||||
FateBoss,
|
||||
FateGather,
|
||||
FateDefend,
|
||||
FateEscort,
|
||||
FateSpecial1,
|
||||
/// <summary>
|
||||
/// The sprout icon.
|
||||
/// </summary>
|
||||
NewAdventurer = 77,
|
||||
|
||||
Returner,
|
||||
/// <summary>
|
||||
/// The mentor icon.
|
||||
/// </summary>
|
||||
Mentor = 78,
|
||||
|
||||
FarEast,
|
||||
GyrAbania,
|
||||
/// <summary>
|
||||
/// The PvE mentor icon.
|
||||
/// </summary>
|
||||
MentorPvE = 79,
|
||||
|
||||
FateSpecial2,
|
||||
/// <summary>
|
||||
/// The crafting mentor icon.
|
||||
/// </summary>
|
||||
MentorCrafting = 80,
|
||||
|
||||
PriorityWorld,
|
||||
/// <summary>
|
||||
/// The PvP mentor icon.
|
||||
/// </summary>
|
||||
MentorPvP = 81,
|
||||
|
||||
ElementalLevel,
|
||||
ExclamationRectangle,
|
||||
/// <summary>
|
||||
/// The tank role icon.
|
||||
/// </summary>
|
||||
Tank = 82,
|
||||
|
||||
NotoriousMonster,
|
||||
/// <summary>
|
||||
/// The healer role icon.
|
||||
/// </summary>
|
||||
Healer = 83,
|
||||
|
||||
Recording,
|
||||
Alarm,
|
||||
|
||||
ArrowUp,
|
||||
ArrowDown,
|
||||
Crystarium,
|
||||
|
||||
MentorProblem,
|
||||
/// <summary>
|
||||
/// The DPS role icon.
|
||||
/// </summary>
|
||||
DPS = 84,
|
||||
|
||||
FateUnknownGold,
|
||||
/// <summary>
|
||||
/// The crafter role icon.
|
||||
/// </summary>
|
||||
Crafter = 85,
|
||||
|
||||
OrangeDiamond,
|
||||
FateCrafting
|
||||
/// <summary>
|
||||
/// The gatherer role icon.
|
||||
/// </summary>
|
||||
Gatherer = 86,
|
||||
|
||||
/// <summary>
|
||||
/// The "any" role icon.
|
||||
/// </summary>
|
||||
AnyClass = 87,
|
||||
|
||||
/// <summary>
|
||||
/// The cross-world icon.
|
||||
/// </summary>
|
||||
CrossWorld = 88,
|
||||
|
||||
/// <summary>
|
||||
/// The slay type Fate icon.
|
||||
/// </summary>
|
||||
FateSlay = 89,
|
||||
|
||||
/// <summary>
|
||||
/// The boss type Fate icon.
|
||||
/// </summary>
|
||||
FateBoss = 90,
|
||||
|
||||
/// <summary>
|
||||
/// The gather type Fate icon.
|
||||
/// </summary>
|
||||
FateGather = 91,
|
||||
|
||||
/// <summary>
|
||||
/// The defend type Fate icon.
|
||||
/// </summary>
|
||||
FateDefend = 92,
|
||||
|
||||
/// <summary>
|
||||
/// The escort type Fate icon.
|
||||
/// </summary>
|
||||
FateEscort = 93,
|
||||
|
||||
/// <summary>
|
||||
/// The special type 1 Fate icon.
|
||||
/// </summary>
|
||||
FateSpecial1 = 94,
|
||||
|
||||
/// <summary>
|
||||
/// The returner icon.
|
||||
/// </summary>
|
||||
Returner = 95,
|
||||
|
||||
/// <summary>
|
||||
/// The Far-East region icon.
|
||||
/// </summary>
|
||||
FarEast = 96,
|
||||
|
||||
/// <summary>
|
||||
/// The Gyr Albania region icon.
|
||||
/// </summary>
|
||||
GyrAbania = 97,
|
||||
|
||||
/// <summary>
|
||||
/// The special type 2 Fate icon.
|
||||
/// </summary>
|
||||
FateSpecial2 = 98,
|
||||
|
||||
/// <summary>
|
||||
/// The priority world icon.
|
||||
/// </summary>
|
||||
PriorityWorld = 99,
|
||||
|
||||
/// <summary>
|
||||
/// The elemental level icon.
|
||||
/// </summary>
|
||||
ElementalLevel = 100,
|
||||
|
||||
/// <summary>
|
||||
/// The exclamation rectangle icon.
|
||||
/// </summary>
|
||||
ExclamationRectangle = 101,
|
||||
|
||||
/// <summary>
|
||||
/// The notorious monster icon.
|
||||
/// </summary>
|
||||
NotoriousMonster = 102,
|
||||
|
||||
/// <summary>
|
||||
/// The recording icon.
|
||||
/// </summary>
|
||||
Recording = 103,
|
||||
|
||||
/// <summary>
|
||||
/// The alarm icon.
|
||||
/// </summary>
|
||||
Alarm = 104,
|
||||
|
||||
/// <summary>
|
||||
/// The arrow up icon.
|
||||
/// </summary>
|
||||
ArrowUp = 105,
|
||||
|
||||
/// <summary>
|
||||
/// The arrow down icon.
|
||||
/// </summary>
|
||||
ArrowDown = 106,
|
||||
|
||||
/// <summary>
|
||||
/// The Crystarium region icon.
|
||||
/// </summary>
|
||||
Crystarium = 107,
|
||||
|
||||
/// <summary>
|
||||
/// The mentor problem icon.
|
||||
/// </summary>
|
||||
MentorProblem = 108,
|
||||
|
||||
/// <summary>
|
||||
/// The unknown gold type Fate icon.
|
||||
/// </summary>
|
||||
FateUnknownGold = 109,
|
||||
|
||||
/// <summary>
|
||||
/// The orange diamond icon.
|
||||
/// </summary>
|
||||
OrangeDiamond = 110,
|
||||
|
||||
/// <summary>
|
||||
/// The crafting type Fate icon.
|
||||
/// </summary>
|
||||
FateCrafting = 111,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace Dalamud.Game.Text.SeStringHandling
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface binding for a payload that can provide readable Text.
|
||||
/// </summary>
|
||||
public interface ITextProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the readable text.
|
||||
/// </summary>
|
||||
string Text { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Serilog;
|
||||
|
|
@ -19,35 +19,8 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
/// <summary>
|
||||
/// This class represents a parsed SeString payload.
|
||||
/// </summary>
|
||||
public abstract class Payload
|
||||
public abstract partial class Payload
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of this payload.
|
||||
/// </summary>
|
||||
public abstract PayloadType Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this payload has been modified since the last Encode().
|
||||
/// </summary>
|
||||
public bool Dirty { get; protected set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Encodes the internal state of this payload into a byte[] suitable for sending to in-game
|
||||
/// handlers such as the chat log.
|
||||
/// </summary>
|
||||
/// <returns>Encoded binary payload data suitable for use with in-game handlers.</returns>
|
||||
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
|
||||
/// <summary>
|
||||
/// Decodes a byte stream from the game into a payload object.
|
||||
/// </summary>
|
||||
/// <param name="reader">A BinaryReader containing at least all the data for this payload.</param>
|
||||
/// <param name="endOfStream">The location holding the end of the data for this payload.</param>
|
||||
protected abstract void DecodeImpl(BinaryReader reader, long endOfStream);
|
||||
|
||||
/// <summary>
|
||||
/// The Lumina instance to use for any necessary data lookups.
|
||||
/// </summary>
|
||||
|
|
@ -58,31 +31,26 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
private byte[] encodedData;
|
||||
|
||||
/// <summary>
|
||||
/// Encode this payload object into a byte[] useable in-game for things like the chat log.
|
||||
/// Gets the type of this payload.
|
||||
/// </summary>
|
||||
/// <param name="force">If true, ignores any cached value and forcibly reencodes the payload from its internal representation.</param>
|
||||
/// <returns>A byte[] suitable for use with in-game handlers such as the chat log.</returns>
|
||||
public byte[] Encode(bool force = false)
|
||||
{
|
||||
if (Dirty || force)
|
||||
{
|
||||
this.encodedData = EncodeImpl();
|
||||
Dirty = false;
|
||||
}
|
||||
public abstract PayloadType Type { get; }
|
||||
|
||||
return this.encodedData;
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether whether this payload has been modified since the last Encode().
|
||||
/// </summary>
|
||||
public bool Dirty { get; protected set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a binary representation of a payload into its corresponding nice object payload.
|
||||
/// </summary>
|
||||
/// <param name="reader">A reader positioned at the start of the payload, and containing at least one entire payload.</param>
|
||||
/// <param name="data">The DataManager instance.</param>
|
||||
/// <returns>The constructed Payload-derived object that was decoded from the binary data.</returns>
|
||||
public static Payload Decode(BinaryReader reader, DataManager data)
|
||||
{
|
||||
var payloadStartPos = reader.BaseStream.Position;
|
||||
|
||||
Payload payload = null;
|
||||
Payload payload;
|
||||
|
||||
var initialByte = reader.ReadByte();
|
||||
reader.BaseStream.Position--;
|
||||
|
|
@ -113,6 +81,39 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
return payload;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encode this payload object into a byte[] useable in-game for things like the chat log.
|
||||
/// </summary>
|
||||
/// <param name="force">If true, ignores any cached value and forcibly reencodes the payload from its internal representation.</param>
|
||||
/// <returns>A byte[] suitable for use with in-game handlers such as the chat log.</returns>
|
||||
public byte[] Encode(bool force = false)
|
||||
{
|
||||
if (this.Dirty || force)
|
||||
{
|
||||
this.encodedData = this.EncodeImpl();
|
||||
this.Dirty = false;
|
||||
}
|
||||
|
||||
return this.encodedData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encodes the internal state of this payload into a byte[] suitable for sending to in-game
|
||||
/// handlers such as the chat log.
|
||||
/// </summary>
|
||||
/// <returns>Encoded binary payload data suitable for use with in-game handlers.</returns>
|
||||
protected abstract byte[] EncodeImpl();
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a byte stream from the game into a payload object.
|
||||
/// </summary>
|
||||
/// <param name="reader">A BinaryReader containing at least all the data for this payload.</param>
|
||||
/// <param name="endOfStream">The location holding the end of the data for this payload.</param>
|
||||
// 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
|
||||
protected abstract void DecodeImpl(BinaryReader reader, long endOfStream);
|
||||
|
||||
private static Payload DecodeChunk(BinaryReader reader)
|
||||
{
|
||||
Payload payload = null;
|
||||
|
|
@ -164,18 +165,20 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
break;
|
||||
|
||||
case EmbeddedInfoType.LinkTerminator:
|
||||
// this has no custom handling and so needs to fallthrough to ensure it is captured
|
||||
// this has no custom handling and so needs to fallthrough to ensure it is captured
|
||||
default:
|
||||
// but I'm also tired of this log
|
||||
if (subType != EmbeddedInfoType.LinkTerminator)
|
||||
{
|
||||
Log.Verbose("Unhandled EmbeddedInfoType: {0}", subType);
|
||||
}
|
||||
|
||||
// rewind so we capture the Interactable byte in the raw data
|
||||
reader.BaseStream.Seek(-1, SeekOrigin.Current);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SeStringChunkType.AutoTranslateKey:
|
||||
|
|
@ -216,43 +219,126 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
|
||||
#region parse constants and helpers
|
||||
|
||||
/// <summary>
|
||||
/// Parsing helpers.
|
||||
/// </summary>
|
||||
public abstract partial class Payload
|
||||
{
|
||||
/// <summary>
|
||||
/// The start byte of a payload.
|
||||
/// </summary>
|
||||
protected const byte START_BYTE = 0x02;
|
||||
|
||||
/// <summary>
|
||||
/// The end byte of a payload.
|
||||
/// </summary>
|
||||
protected const byte END_BYTE = 0x03;
|
||||
|
||||
protected enum SeStringChunkType
|
||||
{
|
||||
Icon = 0x12,
|
||||
EmphasisItalic = 0x1A,
|
||||
SeHyphen = 0x1F,
|
||||
Interactable = 0x27,
|
||||
AutoTranslateKey = 0x2E,
|
||||
UIForeground = 0x48,
|
||||
UIGlow = 0x49
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This represents the type of embedded info in a payload.
|
||||
/// </summary>
|
||||
public enum EmbeddedInfoType
|
||||
{
|
||||
/// <summary>
|
||||
/// A player's name.
|
||||
/// </summary>
|
||||
PlayerName = 0x01,
|
||||
|
||||
/// <summary>
|
||||
/// The link to an iteme.
|
||||
/// </summary>
|
||||
ItemLink = 0x03,
|
||||
|
||||
/// <summary>
|
||||
/// The link to a map position.
|
||||
/// </summary>
|
||||
MapPositionLink = 0x04,
|
||||
|
||||
/// <summary>
|
||||
/// The link to a quest.
|
||||
/// </summary>
|
||||
QuestLink = 0x05,
|
||||
|
||||
/// <summary>
|
||||
/// A status effect.
|
||||
/// </summary>
|
||||
Status = 0x09,
|
||||
|
||||
DalamudLink = 0x0F, // Dalamud Custom
|
||||
/// <summary>
|
||||
/// A custom Dalamud link.
|
||||
/// </summary>
|
||||
DalamudLink = 0x0F,
|
||||
|
||||
LinkTerminator = 0xCF // not clear but seems to always follow a link
|
||||
/// <summary>
|
||||
/// A link terminator.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It is not exactly clear what this is, but seems to always follow a link.
|
||||
/// </remarks>
|
||||
LinkTerminator = 0xCF,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This represents the type of payload and how it should be encoded.
|
||||
/// </summary>
|
||||
protected enum SeStringChunkType
|
||||
{
|
||||
/// <summary>
|
||||
/// See the <see cref="IconPayload"/> class.
|
||||
/// </summary>
|
||||
Icon = 0x12,
|
||||
|
||||
/// <summary>
|
||||
/// See the <see cref="EmphasisItalicPayload"/> class.
|
||||
/// </summary>
|
||||
EmphasisItalic = 0x1A,
|
||||
|
||||
/// <summary>
|
||||
/// See the <see cref="SeHyphenPayload"/> class.
|
||||
/// </summary>
|
||||
SeHyphen = 0x1F,
|
||||
|
||||
/// <summary>
|
||||
/// See any of the link-type classes:
|
||||
/// <see cref="PlayerPayload"/>,
|
||||
/// <see cref="ItemPayload"/>,
|
||||
/// <see cref="MapLinkPayload"/>,
|
||||
/// <see cref="StatusPayload"/>,
|
||||
/// <see cref="QuestPayload"/>,
|
||||
/// <see cref="DalamudLinkPayload"/>.
|
||||
/// </summary>
|
||||
Interactable = 0x27,
|
||||
|
||||
/// <summary>
|
||||
/// See the <see cref="AutoTranslatePayload"/> class.
|
||||
/// </summary>
|
||||
AutoTranslateKey = 0x2E,
|
||||
|
||||
/// <summary>
|
||||
/// See the <see cref="UIForegroundPayload"/> class.
|
||||
/// </summary>
|
||||
UIForeground = 0x48,
|
||||
|
||||
/// <summary>
|
||||
/// See the <see cref="UIGlowPayload"/> class.
|
||||
/// </summary>
|
||||
UIGlow = 0x49,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the packed integer from SE's native data format.
|
||||
/// </summary>
|
||||
/// <param name="input">The BinaryReader instance.</param>
|
||||
/// <returns>An integer.</returns>
|
||||
// made protected, unless we actually want to use it externally
|
||||
// in which case it should probably go live somewhere else
|
||||
protected static uint GetInteger(BinaryReader input)
|
||||
{
|
||||
uint marker = input.ReadByte();
|
||||
if (marker < 0xD0) return marker - 1;
|
||||
if (marker < 0xD0)
|
||||
return marker - 1;
|
||||
|
||||
// the game adds 0xF0 marker for values >= 0xCF
|
||||
// uasge of 0xD0-0xEF is unknown, should we throw here?
|
||||
|
|
@ -269,6 +355,11 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
return BitConverter.ToUInt32(ret, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a packed integer in Se's native data format.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to pack.</param>
|
||||
/// <returns>A packed integer.</returns>
|
||||
protected static byte[] MakeInteger(uint value)
|
||||
{
|
||||
if (value < 0xCF)
|
||||
|
|
@ -287,22 +378,33 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
ret[0] |= (byte)(1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
ret[0] -= 1;
|
||||
|
||||
return ret.ToArray();
|
||||
}
|
||||
|
||||
protected static (uint, uint) GetPackedIntegers(BinaryReader input)
|
||||
/// <summary>
|
||||
/// From a binary packed integer, get the high and low bytes.
|
||||
/// </summary>
|
||||
/// <param name="input">The BinaryReader instance.</param>
|
||||
/// <returns>The high and low bytes.</returns>
|
||||
protected static (uint High, uint Low) GetPackedIntegers(BinaryReader input)
|
||||
{
|
||||
var value = GetInteger(input);
|
||||
return (value >> 16, value & 0xFFFF);
|
||||
}
|
||||
|
||||
protected static byte[] MakePackedInteger(uint val1, uint val2)
|
||||
/// <summary>
|
||||
/// Create a packed integer from the given high and low bytes.
|
||||
/// </summary>
|
||||
/// <param name="high">The high order bytes.</param>
|
||||
/// <param name="low">The low order bytes.</param>
|
||||
/// <returns>A packed integer.</returns>
|
||||
protected static byte[] MakePackedInteger(uint high, uint low)
|
||||
{
|
||||
var value = (val1 << 16) | (val2 & 0xFFFF);
|
||||
var value = (high << 16) | (low & 0xFFFF);
|
||||
return MakeInteger(value);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
namespace Dalamud.Game.Text.SeStringHandling
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -10,54 +9,70 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
/// An SeString payload representing a player link.
|
||||
/// </summary>
|
||||
Player,
|
||||
|
||||
/// <summary>
|
||||
/// An SeString payload representing an Item link.
|
||||
/// </summary>
|
||||
Item,
|
||||
|
||||
/// <summary>
|
||||
/// An SeString payload representing an Status Effect link.
|
||||
/// </summary>
|
||||
Status,
|
||||
|
||||
/// <summary>
|
||||
/// An SeString payload representing raw, typed text.
|
||||
/// </summary>
|
||||
RawText,
|
||||
|
||||
/// <summary>
|
||||
/// An SeString payload representing a text foreground color.
|
||||
/// </summary>
|
||||
UIForeground,
|
||||
|
||||
/// <summary>
|
||||
/// An SeString payload representing a text glow color.
|
||||
/// </summary>
|
||||
UIGlow,
|
||||
|
||||
/// <summary>
|
||||
/// An SeString payload representing a map position link, such as from <flag> or <pos>.
|
||||
/// </summary>
|
||||
MapLink,
|
||||
|
||||
/// <summary>
|
||||
/// An SeString payload representing an auto-translate dictionary entry.
|
||||
/// </summary>
|
||||
AutoTranslateText,
|
||||
|
||||
/// <summary>
|
||||
/// An SeString payload representing italic emphasis formatting on text.
|
||||
/// </summary>
|
||||
EmphasisItalic,
|
||||
|
||||
/// <summary>
|
||||
/// An SeString payload representing a bitmap icon.
|
||||
/// </summary>
|
||||
Icon,
|
||||
|
||||
/// <summary>
|
||||
/// A SeString payload representing a quest link.
|
||||
/// </summary>
|
||||
Quest,
|
||||
|
||||
/// <summary>
|
||||
/// A SeString payload representing a custom clickable link for dalamud plugins
|
||||
/// A SeString payload representing a custom clickable link for dalamud plugins.
|
||||
/// </summary>
|
||||
DalamudLink,
|
||||
|
||||
/// <summary>
|
||||
/// An SeString payload representing any data we don't handle.
|
||||
/// </summary>
|
||||
Unknown,
|
||||
|
||||
/// <summary>
|
||||
/// An SeString payload representing a doublewide SE hypen.
|
||||
/// </summary>
|
||||
SeHyphen,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
using Lumina.Excel.GeneratedSheets;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using Dalamud.Data;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
|
||||
namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
||||
{
|
||||
|
|
@ -14,25 +15,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
/// </summary>
|
||||
public class AutoTranslatePayload : Payload, ITextProvider
|
||||
{
|
||||
public override PayloadType Type => PayloadType.AutoTranslateText;
|
||||
|
||||
private string text;
|
||||
/// <summary>
|
||||
/// The actual text displayed in-game for this payload.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Value is evaluated lazily and cached.
|
||||
/// </remarks>
|
||||
public string Text
|
||||
{
|
||||
get
|
||||
{
|
||||
// wrap the text in the colored brackets that is uses in-game, since those
|
||||
// are not actually part of any of the payloads
|
||||
this.text ??= $"{(char)SeIconChar.AutoTranslateOpen} {Resolve()} {(char)SeIconChar.AutoTranslateClose}";
|
||||
return this.text;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty]
|
||||
private uint group;
|
||||
|
|
@ -40,9 +23,8 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
[JsonProperty]
|
||||
private uint key;
|
||||
|
||||
internal AutoTranslatePayload() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AutoTranslatePayload"/> class.
|
||||
/// Creates a new auto-translate payload.
|
||||
/// </summary>
|
||||
/// <param name="data">DataManager instance needed to resolve game data.</param>
|
||||
|
|
@ -52,19 +34,49 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
/// 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.
|
||||
/// </remarks>
|
||||
public AutoTranslatePayload(DataManager data, uint group, uint key) {
|
||||
public AutoTranslatePayload(DataManager data, uint group, uint key)
|
||||
{
|
||||
// TODO: friendlier ctor? not sure how to handle that given how weird the tables are
|
||||
this.DataResolver = data;
|
||||
this.group = group;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
// TODO: friendlier ctor? not sure how to handle that given how weird the tables are
|
||||
|
||||
public override string ToString()
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AutoTranslatePayload"/> class.
|
||||
/// </summary>
|
||||
internal AutoTranslatePayload()
|
||||
{
|
||||
return $"{Type} - Group: {group}, Key: {key}, Text: {Text}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override PayloadType Type => PayloadType.AutoTranslateText;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actual text displayed in-game for this payload.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Value is evaluated lazily and cached.
|
||||
/// </remarks>
|
||||
public string Text
|
||||
{
|
||||
get
|
||||
{
|
||||
// wrap the text in the colored brackets that is uses in-game, since those are not actually part of any of the payloads
|
||||
return this.text ??= $"{(char)SeIconChar.AutoTranslateOpen} {this.Resolve()} {(char)SeIconChar.AutoTranslateClose}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents the current object.
|
||||
/// </summary>
|
||||
/// <returns>A string that represents the current object.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{this.Type} - Group: {this.group}, Key: {this.key}, Text: {this.Text}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] EncodeImpl()
|
||||
{
|
||||
var keyBytes = MakeInteger(this.key);
|
||||
|
|
@ -74,7 +86,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
{
|
||||
START_BYTE,
|
||||
(byte)SeStringChunkType.AutoTranslateKey, (byte)chunkLen,
|
||||
(byte)this.group
|
||||
(byte)this.group,
|
||||
};
|
||||
bytes.AddRange(keyBytes);
|
||||
bytes.Add(END_BYTE);
|
||||
|
|
@ -82,6 +94,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void DecodeImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
// this seems to always be a bare byte, and not following normal integer encoding
|
||||
|
|
@ -105,7 +118,9 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
// (again, if it's meant for another table)
|
||||
row = sheet.GetRow(this.key);
|
||||
}
|
||||
catch { } // don't care, row will be null
|
||||
catch
|
||||
{
|
||||
} // don't care, row will be null
|
||||
|
||||
if (row?.Group == this.group)
|
||||
{
|
||||
|
|
@ -142,7 +157,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
"TextCommand" => this.DataResolver.GetExcelSheet<TextCommand>().GetRow(this.key).Command,
|
||||
"Tribe" => this.DataResolver.GetExcelSheet<Tribe>().GetRow(this.key).Masculine,
|
||||
"Weather" => this.DataResolver.GetExcelSheet<Weather>().GetRow(this.key).Name,
|
||||
_ => throw new Exception(actualTableName)
|
||||
_ => throw new Exception(actualTableName),
|
||||
};
|
||||
|
||||
value = name;
|
||||
|
|
|
|||
|
|
@ -1,48 +1,62 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Dalamud.Game.Text.SeStringHandling.Payloads {
|
||||
|
||||
namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// This class represents a custom Dalamud clickable chat link.
|
||||
/// </summary>
|
||||
public class DalamudLinkPayload : Payload {
|
||||
public class DalamudLinkPayload : Payload
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override PayloadType Type => PayloadType.DalamudLink;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin command ID to be linked.
|
||||
/// </summary>
|
||||
public uint CommandId { get; internal set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin name to be linked.
|
||||
/// </summary>
|
||||
[NotNull]
|
||||
public string Plugin { get; internal set; } = string.Empty;
|
||||
|
||||
protected override byte[] EncodeImpl() {
|
||||
var pluginBytes = Encoding.UTF8.GetBytes(Plugin);
|
||||
var commandBytes = MakeInteger(CommandId);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{this.Type} - Plugin: {this.Plugin}, Command: {this.CommandId}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] EncodeImpl()
|
||||
{
|
||||
var pluginBytes = Encoding.UTF8.GetBytes(this.Plugin);
|
||||
var commandBytes = MakeInteger(this.CommandId);
|
||||
var chunkLen = 3 + pluginBytes.Length + commandBytes.Length;
|
||||
|
||||
if (chunkLen > 255) {
|
||||
if (chunkLen > 255)
|
||||
{
|
||||
throw new Exception("Chunk is too long. Plugin name exceeds limits for DalamudLinkPayload");
|
||||
}
|
||||
|
||||
var bytes = new List<byte> {START_BYTE, (byte) SeStringChunkType.Interactable, (byte) chunkLen, (byte) EmbeddedInfoType.DalamudLink};
|
||||
bytes.Add((byte) pluginBytes.Length);
|
||||
var bytes = new List<byte> { START_BYTE, (byte)SeStringChunkType.Interactable, (byte)chunkLen, (byte)EmbeddedInfoType.DalamudLink };
|
||||
bytes.Add((byte)pluginBytes.Length);
|
||||
bytes.AddRange(pluginBytes);
|
||||
bytes.AddRange(commandBytes);
|
||||
bytes.Add(END_BYTE);
|
||||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
protected override void DecodeImpl(BinaryReader reader, long endOfStream) {
|
||||
Plugin = Encoding.UTF8.GetString(reader.ReadBytes(reader.ReadByte()));
|
||||
CommandId = GetInteger(reader);
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return $"{Type} - Plugin: {Plugin}, Command: {CommandId}";
|
||||
/// <inheritdoc/>
|
||||
protected override void DecodeImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
this.Plugin = Encoding.UTF8.GetString(reader.ReadBytes(reader.ReadByte()));
|
||||
this.CommandId = GetInteger(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,47 +14,58 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
public class EmphasisItalicPayload : Payload
|
||||
{
|
||||
/// <summary>
|
||||
/// Payload representing enabling italics on following text.
|
||||
/// </summary>
|
||||
public static EmphasisItalicPayload ItalicsOn => new EmphasisItalicPayload(true);
|
||||
/// <summary>
|
||||
/// Payload representing disabling italics on following text.
|
||||
/// </summary>
|
||||
public static EmphasisItalicPayload ItalicsOff => new EmphasisItalicPayload(false);
|
||||
|
||||
public override PayloadType Type => PayloadType.EmphasisItalic;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this payload enables italics formatting for following text.
|
||||
/// </summary>
|
||||
public bool IsEnabled { get; private set; }
|
||||
|
||||
internal EmphasisItalicPayload() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EmphasisItalicPayload"/> class.
|
||||
/// Creates an EmphasisItalicPayload.
|
||||
/// </summary>
|
||||
/// <param name="enabled">Whether italics formatting should be enabled or disabled for following text.</param>
|
||||
public EmphasisItalicPayload(bool enabled)
|
||||
{
|
||||
IsEnabled = enabled;
|
||||
this.IsEnabled = enabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EmphasisItalicPayload"/> class.
|
||||
/// Creates an EmphasisItalicPayload.
|
||||
/// </summary>
|
||||
internal EmphasisItalicPayload()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a payload representing enabling italics on following text.
|
||||
/// </summary>
|
||||
public static EmphasisItalicPayload ItalicsOn => new(true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a payload representing disabling italics on following text.
|
||||
/// </summary>
|
||||
public static EmphasisItalicPayload ItalicsOff => new(false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this payload enables italics formatting for following text.
|
||||
/// </summary>
|
||||
public bool IsEnabled { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override PayloadType Type => PayloadType.EmphasisItalic;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Type} - Enabled: {IsEnabled}";
|
||||
return $"{this.Type} - Enabled: {this.IsEnabled}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] EncodeImpl()
|
||||
{
|
||||
// realistically this will always be a single byte of value 1 or 2
|
||||
// but we'll treat it normally anyway
|
||||
var enabledBytes = MakeInteger(IsEnabled ? (uint)1 : 0);
|
||||
var enabledBytes = MakeInteger(this.IsEnabled ? 1u : 0);
|
||||
|
||||
var chunkLen = enabledBytes.Length + 1;
|
||||
var bytes = new List<byte>()
|
||||
{
|
||||
START_BYTE, (byte)SeStringChunkType.EmphasisItalic, (byte)chunkLen
|
||||
START_BYTE, (byte)SeStringChunkType.EmphasisItalic, (byte)chunkLen,
|
||||
};
|
||||
bytes.AddRange(enabledBytes);
|
||||
bytes.Add(END_BYTE);
|
||||
|
|
@ -62,9 +73,10 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void DecodeImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
IsEnabled = (GetInteger(reader) == 1);
|
||||
this.IsEnabled = GetInteger(reader) == 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +1,71 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System;
|
||||
|
||||
namespace Dalamud.Game.Text.SeStringHandling.Payloads {
|
||||
|
||||
namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
||||
{
|
||||
/// <summary>
|
||||
/// SeString payload representing a bitmap icon from fontIcon
|
||||
/// SeString payload representing a bitmap icon from fontIcon.
|
||||
/// </summary>
|
||||
public class IconPayload : Payload {
|
||||
public class IconPayload : Payload
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IconPayload"/> class.
|
||||
/// Create a Icon payload for the specified icon.
|
||||
/// </summary>
|
||||
/// <param name="icon">The Icon.</param>
|
||||
public IconPayload(BitmapFontIcon icon)
|
||||
{
|
||||
this.Icon = icon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Index of the icon
|
||||
/// Initializes a new instance of the <see cref="IconPayload"/> class.
|
||||
/// Create a Icon payload for the specified icon.
|
||||
/// </summary>
|
||||
/// <param name="iconIndex">Index of the icon.</param>
|
||||
[Obsolete("IconPayload(uint) is deprecated, please use IconPayload(BitmapFontIcon).")]
|
||||
public IconPayload(uint iconIndex)
|
||||
: this((BitmapFontIcon)iconIndex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IconPayload"/> class.
|
||||
/// Create a Icon payload for the specified icon.
|
||||
/// </summary>
|
||||
internal IconPayload()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override PayloadType Type => PayloadType.Icon;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the icon.
|
||||
/// </summary>
|
||||
[Obsolete("Use IconPayload.Icon")]
|
||||
public uint IconIndex => (uint) Icon;
|
||||
public uint IconIndex => (uint)this.Icon;
|
||||
|
||||
/// <summary>
|
||||
/// Icon the payload represents.
|
||||
/// Gets or sets the icon the payload represents.
|
||||
/// </summary>
|
||||
public BitmapFontIcon Icon { get; set; } = BitmapFontIcon.None;
|
||||
|
||||
internal IconPayload() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a Icon payload for the specified icon.
|
||||
/// </summary>
|
||||
/// <param name="iconIndex">Index of the icon</param>
|
||||
[Obsolete("IconPayload(uint) is deprecated, please use IconPayload(BitmapFontIcon).")]
|
||||
public IconPayload(uint iconIndex) : this((BitmapFontIcon) iconIndex) { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a Icon payload for the specified icon.
|
||||
/// </summary>
|
||||
/// <param name="icon">The Icon</param>
|
||||
public IconPayload(BitmapFontIcon icon) {
|
||||
Icon = icon;
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{this.Type} - {this.Icon}";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override PayloadType Type => PayloadType.Icon;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override byte[] EncodeImpl() {
|
||||
var indexBytes = MakeInteger((uint) this.Icon);
|
||||
protected override byte[] EncodeImpl()
|
||||
{
|
||||
var indexBytes = MakeInteger((uint)this.Icon);
|
||||
var chunkLen = indexBytes.Length + 1;
|
||||
var bytes = new List<byte>(new byte[] {
|
||||
START_BYTE, (byte)SeStringChunkType.Icon, (byte)chunkLen
|
||||
var bytes = new List<byte>(new byte[]
|
||||
{
|
||||
START_BYTE, (byte)SeStringChunkType.Icon, (byte)chunkLen,
|
||||
});
|
||||
bytes.AddRange(indexBytes);
|
||||
bytes.Add(END_BYTE);
|
||||
|
|
@ -53,14 +73,9 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads {
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void DecodeImpl(BinaryReader reader, long endOfStream) {
|
||||
Icon = (BitmapFontIcon) GetInteger(reader);
|
||||
protected override void DecodeImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
this.Icon = (BitmapFontIcon)GetInteger(reader);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() {
|
||||
return $"{Type} - {Icon}";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Dalamud.Data;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Newtonsoft.Json;
|
||||
|
|
@ -14,32 +14,48 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
/// </summary>
|
||||
public class ItemPayload : Payload
|
||||
{
|
||||
public override PayloadType Type => PayloadType.Item;
|
||||
|
||||
private Item item;
|
||||
/// <summary>
|
||||
/// The underlying Lumina Item represented by this payload.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Value is evaluated lazily and cached.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public Item Item
|
||||
{
|
||||
get
|
||||
{
|
||||
this.item ??= this.DataResolver.GetExcelSheet<Item>().GetRow(this.itemId);
|
||||
return this.item;
|
||||
}
|
||||
}
|
||||
|
||||
// mainly to allow overriding the name (for things like owo)
|
||||
// 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;
|
||||
|
||||
[JsonProperty]
|
||||
private uint itemId;
|
||||
|
||||
/// <summary>
|
||||
/// The displayed name for this item link. Note that incoming links only sometimes have names embedded,
|
||||
/// Initializes a new instance of the <see cref="ItemPayload"/> class.
|
||||
/// Creates a payload representing an interactable item link for the specified item.
|
||||
/// </summary>
|
||||
/// <param name="data">DataManager instance needed to resolve game data.</param>
|
||||
/// <param name="itemId">The id of the item.</param>
|
||||
/// <param name="isHQ">Whether or not the link should be for the high-quality variant of the item.</param>
|
||||
/// <param name="displayNameOverride">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.</param>
|
||||
public ItemPayload(DataManager data, uint itemId, bool isHQ, string displayNameOverride = null)
|
||||
{
|
||||
this.DataResolver = data;
|
||||
this.itemId = itemId;
|
||||
this.IsHQ = isHQ;
|
||||
this.displayName = displayNameOverride;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ItemPayload"/> class.
|
||||
/// Creates a payload representing an interactable item link for the specified item.
|
||||
/// </summary>
|
||||
internal ItemPayload()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override PayloadType Type => PayloadType.Item;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets 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.
|
||||
/// </summary>
|
||||
public string DisplayName
|
||||
|
|
@ -52,54 +68,44 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
set
|
||||
{
|
||||
this.displayName = value;
|
||||
Dirty = true;
|
||||
this.Dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this item link is for a high-quality version of the item.
|
||||
/// Gets the underlying Lumina Item represented by this payload.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The value is evaluated lazily and cached.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public Item Item => this.item ??= this.DataResolver.GetExcelSheet<Item>().GetRow(this.itemId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether or not this item link is for a high-quality version of the item.
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public bool IsHQ { get; private set; } = false;
|
||||
|
||||
[JsonProperty]
|
||||
private uint itemId;
|
||||
|
||||
internal ItemPayload() { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a payload representing an interactable item link for the specified item.
|
||||
/// </summary>
|
||||
/// <param name="data">DataManager instance needed to resolve game data.</param>
|
||||
/// <param name="itemId">The id of the item.</param>
|
||||
/// <param name="isHQ">Whether or not the link should be for the high-quality variant of the item.</param>
|
||||
/// <param name="displayNameOverride">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.</param>
|
||||
public ItemPayload(DataManager data, uint itemId, bool isHQ, string displayNameOverride = null) {
|
||||
this.DataResolver = data;
|
||||
this.itemId = itemId;
|
||||
this.IsHQ = isHQ;
|
||||
this.displayName = displayNameOverride;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Type} - ItemId: {itemId}, IsHQ: {IsHQ}, Name: {this.displayName ?? Item.Name}";
|
||||
return $"{this.Type} - ItemId: {this.itemId}, IsHQ: {this.IsHQ}, Name: {this.displayName ?? this.Item.Name}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] EncodeImpl()
|
||||
{
|
||||
var actualItemId = IsHQ ? this.itemId + 1000000 : this.itemId;
|
||||
var actualItemId = this.IsHQ ? this.itemId + 1000000 : this.itemId;
|
||||
var idBytes = MakeInteger(actualItemId);
|
||||
bool hasName = !string.IsNullOrEmpty(this.displayName);
|
||||
var hasName = !string.IsNullOrEmpty(this.displayName);
|
||||
|
||||
var chunkLen = idBytes.Length + 4;
|
||||
if (hasName)
|
||||
{
|
||||
// 1 additional unknown byte compared to the nameless version, 1 byte for the name length, and then the name itself
|
||||
chunkLen += (1 + 1 + this.displayName.Length);
|
||||
if (IsHQ)
|
||||
chunkLen += 1 + 1 + this.displayName.Length;
|
||||
if (this.IsHQ)
|
||||
{
|
||||
chunkLen += 4; // unicode representation of the HQ symbol is 3 bytes, preceded by a space
|
||||
}
|
||||
|
|
@ -108,7 +114,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
var bytes = new List<byte>()
|
||||
{
|
||||
START_BYTE,
|
||||
(byte)SeStringChunkType.Interactable, (byte)chunkLen, (byte)EmbeddedInfoType.ItemLink
|
||||
(byte)SeStringChunkType.Interactable, (byte)chunkLen, (byte)EmbeddedInfoType.ItemLink,
|
||||
};
|
||||
bytes.AddRange(idBytes);
|
||||
// unk
|
||||
|
|
@ -118,7 +124,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
if (hasName)
|
||||
{
|
||||
var nameLen = this.displayName.Length + 1;
|
||||
if (IsHQ)
|
||||
if (this.IsHQ)
|
||||
{
|
||||
nameLen += 4; // space plus 3 bytes for HQ symbol
|
||||
}
|
||||
|
|
@ -126,11 +132,11 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
bytes.AddRange(new byte[]
|
||||
{
|
||||
0xFF, // unk
|
||||
(byte)nameLen
|
||||
(byte)nameLen,
|
||||
});
|
||||
bytes.AddRange(Encoding.UTF8.GetBytes(this.displayName));
|
||||
|
||||
if (IsHQ)
|
||||
if (this.IsHQ)
|
||||
{
|
||||
// space and HQ symbol
|
||||
bytes.AddRange(new byte[] { 0x20, 0xEE, 0x80, 0xBC });
|
||||
|
|
@ -142,6 +148,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void DecodeImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
this.itemId = GetInteger(reader);
|
||||
|
|
@ -149,7 +156,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
if (this.itemId > 1000000)
|
||||
{
|
||||
this.itemId -= 1000000;
|
||||
IsHQ = true;
|
||||
this.IsHQ = true;
|
||||
}
|
||||
|
||||
if (reader.BaseStream.Position + 3 < endOfStream)
|
||||
|
|
@ -167,7 +174,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
|
||||
// HQ items have the HQ symbol as part of the name, but since we already recorded
|
||||
// the HQ flag, we want just the bare name
|
||||
if (IsHQ)
|
||||
if (this.IsHQ)
|
||||
{
|
||||
itemNameBytes = itemNameBytes.Take(itemNameLen - 4).ToArray();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
using Lumina.Excel.GeneratedSheets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
using Dalamud.Data;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
||||
|
|
@ -12,140 +13,19 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
/// </summary>
|
||||
public class MapLinkPayload : Payload
|
||||
{
|
||||
public override PayloadType Type => PayloadType.MapLink;
|
||||
|
||||
private Map map;
|
||||
/// <summary>
|
||||
/// The Map specified for this map link.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Value is evaluated lazily and cached.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public Map Map
|
||||
{
|
||||
get
|
||||
{
|
||||
this.map ??= this.DataResolver.GetExcelSheet<Map>().GetRow(this.mapId);
|
||||
return this.map;
|
||||
}
|
||||
}
|
||||
|
||||
private TerritoryType territoryType;
|
||||
/// <summary>
|
||||
/// The TerritoryType specified for this map link.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Value is evaluated lazily and cached.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public TerritoryType TerritoryType
|
||||
{
|
||||
get
|
||||
{
|
||||
this.territoryType ??= this.DataResolver.GetExcelSheet<TerritoryType>().GetRow(this.territoryTypeId);
|
||||
return this.territoryType;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The internal x-coordinate for this map position.
|
||||
/// </summary>
|
||||
public int RawX { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The internal y-coordinate for this map position.
|
||||
/// </summary>
|
||||
public int RawY { get; private set; }
|
||||
|
||||
// these could be cached, but this isn't really too egregious
|
||||
/// <summary>
|
||||
/// The readable x-coordinate position for this map link. This value is approximate and unrounded.
|
||||
/// </summary>
|
||||
public float XCoord
|
||||
{
|
||||
get
|
||||
{
|
||||
return ConvertRawPositionToMapCoordinate(RawX, Map.SizeFactor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The readable y-coordinate position for this map link. This value is approximate and unrounded.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public float YCoord
|
||||
{
|
||||
get
|
||||
{
|
||||
return ConvertRawPositionToMapCoordinate(RawY, Map.SizeFactor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string CoordinateString
|
||||
{
|
||||
get
|
||||
{
|
||||
// this truncates the values to one decimal without rounding, which is what the game does
|
||||
// the fudge also just attempts to correct the truncated/displayed value for rounding/fp issues
|
||||
// TODO: should this fudge factor be the same as in the ctor? currently not since that is customizable
|
||||
const float fudge = 0.02f;
|
||||
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;
|
||||
/// <summary>
|
||||
/// 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"
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string PlaceNameRegion
|
||||
{
|
||||
get
|
||||
{
|
||||
this.placeNameRegion ??= TerritoryType.PlaceNameRegion.Value?.Name;
|
||||
return this.placeNameRegion;
|
||||
}
|
||||
}
|
||||
|
||||
private string placeName;
|
||||
/// <summary>
|
||||
/// 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"
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string PlaceName
|
||||
{
|
||||
get
|
||||
{
|
||||
this.placeName ??= TerritoryType.PlaceName.Value?.Name;
|
||||
return this.placeName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The data string for this map link, for use by internal game functions that take a string variant and not a binary payload.
|
||||
/// </summary>
|
||||
public string DataString => $"m:{TerritoryType.RowId},{Map.RowId},{RawX},{RawY}";
|
||||
|
||||
[JsonProperty]
|
||||
private uint territoryTypeId;
|
||||
|
||||
[JsonProperty]
|
||||
private uint mapId;
|
||||
// there is no Z; it's purely in the text payload where applicable
|
||||
|
||||
internal MapLinkPayload() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MapLinkPayload"/> class.
|
||||
/// Creates an interactable MapLinkPayload from a human-readable position.
|
||||
/// </summary>
|
||||
/// <param name="data">DataManager instance needed to resolve game data.</param>
|
||||
|
|
@ -154,18 +34,20 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
/// <param name="niceXCoord">The human-readable x-coordinate for this link.</param>
|
||||
/// <param name="niceYCoord">The human-readable y-coordinate for this link.</param>
|
||||
/// <param name="fudgeFactor">An optional offset to account for rounding and truncation errors; it is best to leave this untouched in most cases.</param>
|
||||
public MapLinkPayload(DataManager data, uint territoryTypeId, uint mapId, float niceXCoord, float niceYCoord, float fudgeFactor = 0.05f) {
|
||||
public MapLinkPayload(DataManager data, uint territoryTypeId, uint mapId, float niceXCoord, float niceYCoord, float fudgeFactor = 0.05f)
|
||||
{
|
||||
this.DataResolver = data;
|
||||
this.territoryTypeId = territoryTypeId;
|
||||
this.mapId = mapId;
|
||||
// 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
|
||||
RawX = this.ConvertMapCoordinateToRawPosition(niceXCoord + fudgeFactor, Map.SizeFactor);
|
||||
RawY = this.ConvertMapCoordinateToRawPosition(niceYCoord + fudgeFactor, Map.SizeFactor);
|
||||
this.RawX = this.ConvertMapCoordinateToRawPosition(niceXCoord + fudgeFactor, this.Map.SizeFactor);
|
||||
this.RawY = this.ConvertMapCoordinateToRawPosition(niceYCoord + fudgeFactor, this.Map.SizeFactor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MapLinkPayload"/> class.
|
||||
/// Creates an interactable MapLinkPayload from a raw position.
|
||||
/// </summary>
|
||||
/// <param name="data">DataManager instance needed to resolve game data.</param>
|
||||
|
|
@ -178,27 +60,121 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
this.DataResolver = data;
|
||||
this.territoryTypeId = territoryTypeId;
|
||||
this.mapId = mapId;
|
||||
RawX = rawX;
|
||||
RawY = rawY;
|
||||
this.RawX = rawX;
|
||||
this.RawY = rawY;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MapLinkPayload"/> class.
|
||||
/// Creates an interactable MapLinkPayload from a human-readable position.
|
||||
/// </summary>
|
||||
internal MapLinkPayload()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override PayloadType Type => PayloadType.MapLink;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Map specified for this map link.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The value is evaluated lazily and cached.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public Map Map => this.map ??= this.DataResolver.GetExcelSheet<Map>().GetRow(this.mapId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the TerritoryType specified for this map link.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The value is evaluated lazily and cached.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public TerritoryType TerritoryType => this.territoryType ??= this.DataResolver.GetExcelSheet<TerritoryType>().GetRow(this.territoryTypeId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal x-coordinate for this map position.
|
||||
/// </summary>
|
||||
public int RawX { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal y-coordinate for this map position.
|
||||
/// </summary>
|
||||
public int RawY { get; private set; }
|
||||
|
||||
// these could be cached, but this isn't really too egregious
|
||||
|
||||
/// <summary>
|
||||
/// Gets the readable x-coordinate position for this map link. This value is approximate and unrounded.
|
||||
/// </summary>
|
||||
public float XCoord => this.ConvertRawPositionToMapCoordinate(this.RawX, this.Map.SizeFactor);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the readable y-coordinate position for this map link. This value is approximate and unrounded.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public float YCoord => this.ConvertRawPositionToMapCoordinate(this.RawY, this.Map.SizeFactor);
|
||||
|
||||
// there is no Z; it's purely in the text payload where applicable
|
||||
|
||||
/// <summary>
|
||||
/// Gets 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.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string CoordinateString
|
||||
{
|
||||
get
|
||||
{
|
||||
// this truncates the values to one decimal without rounding, which is what the game does
|
||||
// the fudge also just attempts to correct the truncated/displayed value for rounding/fp issues
|
||||
// TODO: should this fudge factor be the same as in the ctor? currently not since that is customizable
|
||||
const float fudge = 0.02f;
|
||||
var x = Math.Truncate((this.XCoord + fudge) * 10.0f) / 10.0f;
|
||||
var y = Math.Truncate((this.YCoord + fudge) * 10.0f) / 10.0f;
|
||||
|
||||
// the formatting and spacing the game uses
|
||||
return $"( {x:0.0} , {y:0.0} )";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets 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".
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string PlaceNameRegion => this.placeNameRegion ??= this.TerritoryType.PlaceNameRegion.Value?.Name;
|
||||
|
||||
/// <summary>
|
||||
/// Gets 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".
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string PlaceName => this.placeName ??= this.TerritoryType.PlaceName.Value?.Name;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data string for this map link, for use by internal game functions that take a string variant and not a binary payload.
|
||||
/// </summary>
|
||||
public string DataString => $"m:{this.TerritoryType.RowId},{this.Map.RowId},{this.RawX},{this.RawY}";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Type} - TerritoryTypeId: {territoryTypeId}, MapId: {mapId}, RawX: {RawX}, RawY: {RawY}, display: {PlaceName} {CoordinateString}";
|
||||
return $"{this.Type} - TerritoryTypeId: {this.territoryTypeId}, MapId: {this.mapId}, RawX: {this.RawX}, RawY: {this.RawY}, display: {this.PlaceName} {this.CoordinateString}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] EncodeImpl()
|
||||
{
|
||||
var packedTerritoryAndMapBytes = MakePackedInteger(this.territoryTypeId, this.mapId);
|
||||
var xBytes = MakeInteger(unchecked((uint)RawX));
|
||||
var yBytes = MakeInteger(unchecked((uint)RawY));
|
||||
var xBytes = MakeInteger(unchecked((uint)this.RawX));
|
||||
var yBytes = MakeInteger(unchecked((uint)this.RawY));
|
||||
|
||||
var chunkLen = 4 + packedTerritoryAndMapBytes.Length + xBytes.Length + yBytes.Length;
|
||||
|
||||
var bytes = new List<byte>()
|
||||
{
|
||||
START_BYTE,
|
||||
(byte)SeStringChunkType.Interactable, (byte)chunkLen, (byte)EmbeddedInfoType.MapPositionLink
|
||||
(byte)SeStringChunkType.Interactable, (byte)chunkLen, (byte)EmbeddedInfoType.MapPositionLink,
|
||||
};
|
||||
|
||||
bytes.AddRange(packedTerritoryAndMapBytes);
|
||||
|
|
@ -211,6 +187,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void DecodeImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
// for debugging for now
|
||||
|
|
@ -221,8 +198,8 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
try
|
||||
{
|
||||
(this.territoryTypeId, this.mapId) = GetPackedIntegers(reader);
|
||||
RawX = unchecked((int)GetInteger(reader));
|
||||
RawY = unchecked((int)GetInteger(reader));
|
||||
this.RawX = unchecked((int)GetInteger(reader));
|
||||
this.RawY = unchecked((int)GetInteger(reader));
|
||||
// the Z coordinate is never in this chunk, just the text (if applicable)
|
||||
|
||||
// seems to always be FF 01
|
||||
|
|
@ -237,6 +214,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
}
|
||||
|
||||
#region ugliness
|
||||
|
||||
// from https://github.com/xivapi/ffxiv-datamining/blob/master/docs/MapCoordinates.md
|
||||
// extra 1/1000 because that is how the network ints are done
|
||||
private float ConvertRawPositionToMapCoordinate(int pos, float scale)
|
||||
|
|
@ -257,6 +235,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
|
||||
return (int)scaledPos;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
using Lumina.Excel.GeneratedSheets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using Dalamud.Data;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
||||
|
|
@ -13,70 +13,80 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
/// </summary>
|
||||
public class PlayerPayload : Payload
|
||||
{
|
||||
public override PayloadType Type => PayloadType.Player;
|
||||
|
||||
[JsonProperty]
|
||||
private string playerName;
|
||||
/// <summary>
|
||||
/// The player's displayed name. This does not contain the server name.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string PlayerName
|
||||
{
|
||||
get { return this.playerName; }
|
||||
set
|
||||
{
|
||||
this.playerName = value;
|
||||
Dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
private World world;
|
||||
/// <summary>
|
||||
/// The Lumina object representing the player's home server.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Value is evaluated lazily and cached.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public World World
|
||||
{
|
||||
get
|
||||
{
|
||||
this.world ??= this.DataResolver.GetExcelSheet<World>().GetRow(this.serverId);
|
||||
return this.world;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A text representation of this player link matching how it might appear in-game.
|
||||
/// The world name will always be present.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string DisplayedName => $"{PlayerName}{(char)SeIconChar.CrossWorld}{World.Name}";
|
||||
|
||||
[JsonProperty]
|
||||
private uint serverId;
|
||||
|
||||
internal PlayerPayload() { }
|
||||
[JsonProperty]
|
||||
private string playerName;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PlayerPayload"/> class.
|
||||
/// Create a PlayerPayload link for the specified player.
|
||||
/// </summary>
|
||||
/// <param name="data">DataManager instance needed to resolve game data.</param>
|
||||
/// <param name="playerName">The player's displayed name.</param>
|
||||
/// <param name="serverId">The player's home server id.</param>
|
||||
public PlayerPayload(DataManager data, string playerName, uint serverId) {
|
||||
public PlayerPayload(DataManager data, string playerName, uint serverId)
|
||||
{
|
||||
this.DataResolver = data;
|
||||
this.playerName = playerName;
|
||||
this.serverId = serverId;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PlayerPayload"/> class.
|
||||
/// Create a PlayerPayload link for the specified player.
|
||||
/// </summary>
|
||||
internal PlayerPayload()
|
||||
{
|
||||
return $"{Type} - PlayerName: {PlayerName}, ServerId: {serverId}, ServerName: {World.Name}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Lumina object representing the player's home server.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Value is evaluated lazily and cached.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public World World => this.world ??= this.DataResolver.GetExcelSheet<World>().GetRow(this.serverId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the player's displayed name. This does not contain the server name.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string PlayerName
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.playerName;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.playerName = value;
|
||||
this.Dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text representation of this player link matching how it might appear in-game.
|
||||
/// The world name will always be present.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string DisplayedName => $"{this.PlayerName}{(char)SeIconChar.CrossWorld}{this.World.Name}";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override PayloadType Type => PayloadType.Player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{this.Type} - PlayerName: {this.PlayerName}, ServerId: {this.serverId}, ServerName: {this.World.Name}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] EncodeImpl()
|
||||
{
|
||||
var chunkLen = this.playerName.Length + 7;
|
||||
|
|
@ -85,9 +95,10 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
START_BYTE,
|
||||
(byte)SeStringChunkType.Interactable, (byte)chunkLen, (byte)EmbeddedInfoType.PlayerName,
|
||||
/* unk */ 0x01,
|
||||
(byte)(this.serverId+1), // I didn't want to deal with single-byte values in MakeInteger, so we have to do the +1 manually
|
||||
/* unk */0x01, /* unk */0xFF, // these sometimes vary but are frequently this
|
||||
(byte)(this.playerName.Length+1)
|
||||
(byte)(this.serverId + 1), // I didn't want to deal with single-byte values in MakeInteger, so we have to do the +1 manually
|
||||
/* unk */ 0x01,
|
||||
/* unk */ 0xFF, // these sometimes vary but are frequently this
|
||||
(byte)(this.playerName.Length + 1),
|
||||
};
|
||||
|
||||
bytes.AddRange(Encoding.UTF8.GetBytes(this.playerName));
|
||||
|
|
@ -97,19 +108,20 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
|
||||
// encoded names are followed by the name in plain text again
|
||||
// use the payload parsing for consistency, as this is technically a new chunk
|
||||
bytes.AddRange(new TextPayload(playerName).Encode());
|
||||
bytes.AddRange(new TextPayload(this.playerName).Encode());
|
||||
|
||||
// unsure about this entire packet, but it seems to always follow a name
|
||||
bytes.AddRange(new byte[]
|
||||
{
|
||||
START_BYTE, (byte)SeStringChunkType.Interactable, 0x07, (byte)EmbeddedInfoType.LinkTerminator,
|
||||
0x01, 0x01, 0x01, 0xFF, 0x01,
|
||||
END_BYTE
|
||||
END_BYTE,
|
||||
});
|
||||
|
||||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void DecodeImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
// unk
|
||||
|
|
|
|||
|
|
@ -1,68 +1,79 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
using Dalamud.Data;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Dalamud.Game.Text.SeStringHandling.Payloads {
|
||||
namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
||||
{
|
||||
/// <summary>
|
||||
/// An SeString Payload representing an interactable quest link.
|
||||
/// </summary>
|
||||
public class QuestPayload : Payload {
|
||||
public override PayloadType Type => PayloadType.Quest;
|
||||
|
||||
public class QuestPayload : Payload
|
||||
{
|
||||
private Quest quest;
|
||||
/// <summary>
|
||||
/// The underlying Lumina Quest represented by this payload.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Value is evaluated lazily and cached.
|
||||
/// </remarks>
|
||||
|
||||
[JsonIgnore]
|
||||
public Quest Quest {
|
||||
get {
|
||||
this.quest ??= this.DataResolver.GetExcelSheet<Quest>().GetRow(this.questId);
|
||||
return this.quest;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty]
|
||||
private uint questId;
|
||||
|
||||
internal QuestPayload() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QuestPayload"/> class.
|
||||
/// Creates a payload representing an interactable quest link for the specified quest.
|
||||
/// </summary>
|
||||
/// <param name="data">DataManager instance needed to resolve game data.</param>
|
||||
/// <param name="questId">The id of the quest.</param>
|
||||
public QuestPayload(DataManager data, uint questId) {
|
||||
public QuestPayload(DataManager data, uint questId)
|
||||
{
|
||||
this.DataResolver = data;
|
||||
this.questId = questId;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() {
|
||||
return $"{Type} - QuestId: {this.questId}, Name: {Quest?.Name ?? "QUEST NOT FOUND"}";
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QuestPayload"/> class.
|
||||
/// Creates a payload representing an interactable quest link for the specified quest.
|
||||
/// </summary>
|
||||
internal QuestPayload()
|
||||
{
|
||||
}
|
||||
|
||||
protected override byte[] EncodeImpl() {
|
||||
var idBytes = MakeInteger((ushort) this.questId);
|
||||
/// <inheritdoc/>
|
||||
public override PayloadType Type => PayloadType.Quest;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying Lumina Quest represented by this payload.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The value is evaluated lazily and cached.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public Quest Quest => this.quest ??= this.DataResolver.GetExcelSheet<Quest>().GetRow(this.questId);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{this.Type} - QuestId: {this.questId}, Name: {this.Quest?.Name ?? "QUEST NOT FOUND"}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] EncodeImpl()
|
||||
{
|
||||
var idBytes = MakeInteger((ushort)this.questId);
|
||||
var chunkLen = idBytes.Length + 4;
|
||||
|
||||
var bytes = new List<byte>() {
|
||||
START_BYTE, (byte) SeStringChunkType.Interactable, (byte) chunkLen, (byte) EmbeddedInfoType.QuestLink,
|
||||
var bytes = new List<byte>()
|
||||
{
|
||||
START_BYTE, (byte)SeStringChunkType.Interactable, (byte)chunkLen, (byte)EmbeddedInfoType.QuestLink,
|
||||
};
|
||||
|
||||
bytes.AddRange(idBytes);
|
||||
bytes.AddRange(new byte[] {0x01, 0x01, END_BYTE});
|
||||
bytes.AddRange(new byte[] { 0x01, 0x01, END_BYTE });
|
||||
return bytes.ToArray();
|
||||
|
||||
}
|
||||
|
||||
protected override void DecodeImpl(BinaryReader reader, long endOfStream) {
|
||||
/// <inheritdoc/>
|
||||
protected override void DecodeImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
// Game uses int16, Luimina uses int32
|
||||
this.questId = GetInteger(reader) + 65536;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -13,65 +14,91 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
/// </summary>
|
||||
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
|
||||
/// <summary>
|
||||
/// A fixed Payload representing a common link-termination sequence, found in many payload chains.
|
||||
/// </summary>
|
||||
public static RawPayload LinkTerminator => new RawPayload(new byte[] { 0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03 });
|
||||
|
||||
public override PayloadType Type => PayloadType.Unknown;
|
||||
|
||||
[JsonProperty]
|
||||
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
|
||||
/// <summary>
|
||||
/// The entire payload byte sequence for this payload.
|
||||
/// The returned data is a clone and modifications will not be persisted.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
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[])Encode().Clone();
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty]
|
||||
private byte chunkType;
|
||||
|
||||
[JsonConstructor]
|
||||
internal RawPayload(byte chunkType)
|
||||
{
|
||||
this.chunkType = chunkType;
|
||||
}
|
||||
[JsonProperty]
|
||||
private byte[] data;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RawPayload"/> class.
|
||||
/// </summary>
|
||||
/// <param name="data">The payload data.</param>
|
||||
public RawPayload(byte[] data)
|
||||
{
|
||||
// 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();
|
||||
this.data = data.Skip(3).Take(data.Length - 4).ToArray();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RawPayload"/> class.
|
||||
/// </summary>
|
||||
/// <param name="chunkType">The chunk type.</param>
|
||||
[JsonConstructor]
|
||||
internal RawPayload(byte chunkType)
|
||||
{
|
||||
return $"{Type} - Data: {BitConverter.ToString(Data).Replace("-", " ")}";
|
||||
this.chunkType = chunkType;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
if (obj is RawPayload rp) {
|
||||
if (rp.Data.Length != this.Data.Length) return false;
|
||||
return !Data.Where((t, i) => rp.Data[i] != t).Any();
|
||||
/// <summary>
|
||||
/// Gets a fixed Payload representing a common link-termination sequence, found in many payload chains.
|
||||
/// </summary>
|
||||
public static RawPayload LinkTerminator => new(new byte[] { 0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03 });
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override PayloadType Type => PayloadType.Unknown;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the entire payload byte sequence for this payload.
|
||||
/// The returned data is a clone and modifications will not be persisted.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public 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
|
||||
get
|
||||
{
|
||||
// for now don't allow modifying the contents
|
||||
// because we don't really have a way to track Dirty
|
||||
return (byte[])this.Encode().Clone();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is RawPayload rp)
|
||||
{
|
||||
if (rp.Data.Length != this.Data.Length) return false;
|
||||
return !this.Data.Where((t, i) => rp.Data[i] != t).Any();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
// Generated random values.
|
||||
var hashCode = 1216194372;
|
||||
hashCode = (hashCode * -1521134295) + this.Type.GetHashCode();
|
||||
hashCode = (hashCode * -1521134295) + this.chunkType.GetHashCode();
|
||||
hashCode = (hashCode * -1521134295) + EqualityComparer<byte[]>.Default.GetHashCode(this.data);
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{this.Type} - Data: {BitConverter.ToString(this.Data).Replace("-", " ")}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] EncodeImpl()
|
||||
{
|
||||
var chunkLen = this.data.Length + 1;
|
||||
|
|
@ -80,7 +107,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
{
|
||||
START_BYTE,
|
||||
this.chunkType,
|
||||
(byte)chunkLen
|
||||
(byte)chunkLen,
|
||||
};
|
||||
bytes.AddRange(this.data);
|
||||
|
||||
|
|
@ -89,6 +116,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void DecodeImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
this.data = reader.ReadBytes((int)(endOfStream - reader.BaseStream.Position + 1));
|
||||
|
|
|
|||
|
|
@ -1,30 +1,33 @@
|
|||
using System.IO;
|
||||
|
||||
namespace Dalamud.Game.Text.SeStringHandling.Payloads {
|
||||
namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
||||
{
|
||||
/// <summary>
|
||||
/// A wrapped '–'
|
||||
/// A wrapped '–'.
|
||||
/// </summary>
|
||||
public class SeHyphenPayload : Payload, ITextProvider {
|
||||
|
||||
public class SeHyphenPayload : Payload, ITextProvider
|
||||
{
|
||||
private readonly byte[] bytes = { START_BYTE, (byte)SeStringChunkType.SeHyphen, 0x01, END_BYTE };
|
||||
|
||||
/// <summary>
|
||||
/// Instance of SeHyphenPayload
|
||||
/// Gets an instance of SeHyphenPayload.
|
||||
/// </summary>
|
||||
public static SeHyphenPayload Payload => new SeHyphenPayload();
|
||||
public static SeHyphenPayload Payload => new();
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Gets the text, just a '–'.
|
||||
/// </summary>
|
||||
public string Text => "–";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override PayloadType Type => PayloadType.SeHyphen;
|
||||
|
||||
private readonly byte[] bytes = {START_BYTE, (byte) SeStringChunkType.SeHyphen, 0x01, END_BYTE};
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override byte[] EncodeImpl() => this.bytes;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void DecodeImpl(BinaryReader reader, long endOfStream) { }
|
||||
|
||||
/// <summary>
|
||||
/// Just a '–'
|
||||
/// </summary>
|
||||
public string Text => "–";
|
||||
protected override void DecodeImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using Lumina.Excel.GeneratedSheets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
using Dalamud.Data;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
||||
|
|
@ -12,45 +12,50 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
/// </summary>
|
||||
public class StatusPayload : Payload
|
||||
{
|
||||
public override PayloadType Type => PayloadType.Status;
|
||||
|
||||
private Status status;
|
||||
/// <summary>
|
||||
/// The Lumina Status object represented by this payload.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Value is evaluated lazily and cached.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public Status Status
|
||||
{
|
||||
get
|
||||
{
|
||||
status ??= this.DataResolver.GetExcelSheet<Status>().GetRow(this.statusId);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty]
|
||||
private uint statusId;
|
||||
|
||||
internal StatusPayload() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StatusPayload"/> class.
|
||||
/// Creates a new StatusPayload for the given status id.
|
||||
/// </summary>
|
||||
/// <param name="data">DataManager instance needed to resolve game data.</param>
|
||||
/// <param name="statusId">The id of the Status for this link.</param>
|
||||
public StatusPayload(DataManager data, uint statusId) {
|
||||
public StatusPayload(DataManager data, uint statusId)
|
||||
{
|
||||
this.DataResolver = data;
|
||||
this.statusId = statusId;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StatusPayload"/> class.
|
||||
/// Creates a new StatusPayload for the given status id.
|
||||
/// </summary>
|
||||
internal StatusPayload()
|
||||
{
|
||||
return $"{Type} - StatusId: {statusId}, Name: {Status.Name}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override PayloadType Type => PayloadType.Status;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Lumina Status object represented by this payload.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The value is evaluated lazily and cached.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public Status Status => this.status ??= this.DataResolver.GetExcelSheet<Status>().GetRow(this.statusId);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{this.Type} - StatusId: {this.statusId}, Name: {this.Status.Name}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] EncodeImpl()
|
||||
{
|
||||
var idBytes = MakeInteger(this.statusId);
|
||||
|
|
@ -58,7 +63,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
var chunkLen = idBytes.Length + 7;
|
||||
var bytes = new List<byte>()
|
||||
{
|
||||
START_BYTE, (byte)SeStringChunkType.Interactable, (byte)chunkLen, (byte)EmbeddedInfoType.Status
|
||||
START_BYTE, (byte)SeStringChunkType.Interactable, (byte)chunkLen, (byte)EmbeddedInfoType.Status,
|
||||
};
|
||||
|
||||
bytes.AddRange(idBytes);
|
||||
|
|
@ -68,6 +73,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void DecodeImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
this.statusId = GetInteger(reader);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -11,34 +11,11 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
/// </summary>
|
||||
public class TextPayload : Payload, ITextProvider
|
||||
{
|
||||
public override PayloadType Type => PayloadType.RawText;
|
||||
|
||||
// allow modifying the text of existing payloads on the fly
|
||||
[JsonProperty]
|
||||
private string text;
|
||||
/// <summary>
|
||||
/// The text contained in this payload.
|
||||
/// This may contain SE's special unicode characters.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string Text
|
||||
{
|
||||
get { return this.text; }
|
||||
set
|
||||
{
|
||||
this.text = value;
|
||||
Dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Type} - Text: {Text}";
|
||||
}
|
||||
|
||||
internal TextPayload() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TextPayload"/> class.
|
||||
/// Creates a new TextPayload for the given text.
|
||||
/// </summary>
|
||||
/// <param name="text">The text to include for this payload.</param>
|
||||
|
|
@ -47,6 +24,43 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
this.text = text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TextPayload"/> class.
|
||||
/// Creates a new TextPayload for the given text.
|
||||
/// </summary>
|
||||
internal TextPayload()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override PayloadType Type => PayloadType.RawText;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text contained in this payload.
|
||||
/// This may contain SE's special unicode characters.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string Text
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.text;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.text = value;
|
||||
this.Dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{this.Type} - Text: {this.Text}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] EncodeImpl()
|
||||
{
|
||||
// special case to allow for empty text payloads, so users don't have to check
|
||||
|
|
@ -59,6 +73,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
return Encoding.UTF8.GetBytes(this.text);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void DecodeImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
var textBytes = new List<byte>();
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using Lumina.Excel.GeneratedSheets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
using Dalamud.Data;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
||||
|
|
@ -12,83 +12,86 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
/// </summary>
|
||||
public class UIForegroundPayload : Payload
|
||||
{
|
||||
/// <summary>
|
||||
/// Payload representing disabling foreground color on following text.
|
||||
/// </summary>
|
||||
// TODO Make this work with DI
|
||||
public static UIForegroundPayload UIForegroundOff => new UIForegroundPayload(null, 0);
|
||||
|
||||
public override PayloadType Type => PayloadType.UIForeground;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this payload represents applying a foreground color, or disabling one.
|
||||
/// </summary>
|
||||
public bool IsEnabled => ColorKey != 0;
|
||||
|
||||
private UIColor color;
|
||||
/// <summary>
|
||||
/// A Lumina UIColor object representing this payload. The actual color data is at UIColor.UIForeground
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Value is evaluated lazily and cached.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public UIColor UIColor
|
||||
{
|
||||
get
|
||||
{
|
||||
this.color ??= this.DataResolver.GetExcelSheet<UIColor>().GetRow(this.colorKey);
|
||||
return this.color;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The color key used as a lookup in the UIColor table for this foreground color.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public ushort ColorKey
|
||||
{
|
||||
get { return this.colorKey; }
|
||||
set
|
||||
{
|
||||
this.colorKey = value;
|
||||
this.color = null;
|
||||
Dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Red/Green/Blue values for this foreground color, encoded as a typical hex color.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public uint RGB
|
||||
{
|
||||
get
|
||||
{
|
||||
return (UIColor.UIForeground & 0xFFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty]
|
||||
private ushort colorKey;
|
||||
|
||||
internal UIForegroundPayload() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UIForegroundPayload"/> class.
|
||||
/// Creates a new UIForegroundPayload for the given UIColor key.
|
||||
/// </summary>
|
||||
/// <param name="data">DataManager instance needed to resolve game data.</param>
|
||||
/// <param name="colorKey"></param>
|
||||
public UIForegroundPayload(DataManager data, ushort colorKey) {
|
||||
/// <param name="colorKey">A UIColor key.</param>
|
||||
public UIForegroundPayload(DataManager data, ushort colorKey)
|
||||
{
|
||||
this.DataResolver = data;
|
||||
this.colorKey = colorKey;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UIForegroundPayload"/> class.
|
||||
/// Creates a new UIForegroundPayload for the given UIColor key.
|
||||
/// </summary>
|
||||
internal UIForegroundPayload()
|
||||
{
|
||||
return $"{Type} - UIColor: {colorKey} color: {(IsEnabled ? RGB : 0)}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a payload representing disabling foreground color on following text.
|
||||
/// </summary>
|
||||
// TODO Make this work with DI
|
||||
public static UIForegroundPayload UIForegroundOff => new(null, 0);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override PayloadType Type => PayloadType.UIForeground;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether or not this payload represents applying a foreground color, or disabling one.
|
||||
/// </summary>
|
||||
public bool IsEnabled => this.ColorKey != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a Lumina UIColor object representing this payload. The actual color data is at UIColor.UIForeground.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The value is evaluated lazily and cached.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public UIColor UIColor => this.color ??= this.DataResolver.GetExcelSheet<UIColor>().GetRow(this.colorKey);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color key used as a lookup in the UIColor table for this foreground color.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public ushort ColorKey
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.colorKey;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.colorKey = value;
|
||||
this.color = null;
|
||||
this.Dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Red/Green/Blue values for this foreground color, encoded as a typical hex color.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public uint RGB => this.UIColor.UIForeground & 0xFFFFFF;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{this.Type} - UIColor: {this.colorKey} color: {(this.IsEnabled ? this.RGB : 0)}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] EncodeImpl()
|
||||
{
|
||||
var colorBytes = MakeInteger(this.colorKey);
|
||||
|
|
@ -96,7 +99,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
|
||||
var bytes = new List<byte>(new byte[]
|
||||
{
|
||||
START_BYTE, (byte)SeStringChunkType.UIForeground, (byte)chunkLen
|
||||
START_BYTE, (byte)SeStringChunkType.UIForeground, (byte)chunkLen,
|
||||
});
|
||||
|
||||
bytes.AddRange(colorBytes);
|
||||
|
|
@ -105,6 +108,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void DecodeImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
this.colorKey = (ushort)GetInteger(reader);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using Lumina.Excel.GeneratedSheets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
using Dalamud.Data;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
||||
|
|
@ -12,83 +12,86 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
/// </summary>
|
||||
public class UIGlowPayload : Payload
|
||||
{
|
||||
/// <summary>
|
||||
/// Payload representing disabling glow color on following text.
|
||||
/// </summary>
|
||||
// TODO Make this work with DI
|
||||
public static UIGlowPayload UIGlowOff => new UIGlowPayload(null, 0);
|
||||
|
||||
public override PayloadType Type => PayloadType.UIGlow;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this payload represents applying a glow color, or disabling one.
|
||||
/// </summary>
|
||||
public bool IsEnabled => ColorKey != 0;
|
||||
|
||||
private UIColor color;
|
||||
/// <summary>
|
||||
/// A Lumina UIColor object representing this payload. The actual color data is at UIColor.UIGlow
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Value is evaluated lazily and cached.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public UIColor UIColor
|
||||
{
|
||||
get
|
||||
{
|
||||
this.color ??= this.DataResolver.GetExcelSheet<UIColor>().GetRow(this.colorKey);
|
||||
return this.color;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The color key used as a lookup in the UIColor table for this glow color.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public ushort ColorKey
|
||||
{
|
||||
get { return this.colorKey; }
|
||||
set
|
||||
{
|
||||
this.colorKey = value;
|
||||
this.color = null;
|
||||
Dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Red/Green/Blue values for this glow color, encoded as a typical hex color.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public uint RGB
|
||||
{
|
||||
get
|
||||
{
|
||||
return (UIColor.UIGlow & 0xFFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty]
|
||||
private ushort colorKey;
|
||||
|
||||
internal UIGlowPayload() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UIGlowPayload"/> class.
|
||||
/// Creates a new UIForegroundPayload for the given UIColor key.
|
||||
/// </summary>
|
||||
/// <param name="data">DataManager instance needed to resolve game data.</param>
|
||||
/// <param name="colorKey"></param>
|
||||
public UIGlowPayload(DataManager data, ushort colorKey) {
|
||||
/// <param name="colorKey">A UIColor key.</param>
|
||||
public UIGlowPayload(DataManager data, ushort colorKey)
|
||||
{
|
||||
this.DataResolver = data;
|
||||
this.colorKey = colorKey;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UIGlowPayload"/> class.
|
||||
/// Creates a new UIForegroundPayload for the given UIColor key.
|
||||
/// </summary>
|
||||
internal UIGlowPayload()
|
||||
{
|
||||
return $"{Type} - UIColor: {colorKey} color: {(IsEnabled ? RGB : 0)}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a payload representing disabling glow color on following text.
|
||||
/// </summary>
|
||||
// TODO Make this work with DI
|
||||
public static UIGlowPayload UIGlowOff => new(null, 0);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override PayloadType Type => PayloadType.UIGlow;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color key used as a lookup in the UIColor table for this glow color.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public ushort ColorKey
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.colorKey;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.colorKey = value;
|
||||
this.color = null;
|
||||
this.Dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether or not this payload represents applying a glow color, or disabling one.
|
||||
/// </summary>
|
||||
public bool IsEnabled => this.ColorKey != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Red/Green/Blue values for this glow color, encoded as a typical hex color.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public uint RGB => this.UIColor.UIGlow & 0xFFFFFF;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a Lumina UIColor object representing this payload. The actual color data is at UIColor.UIGlow.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The value is evaluated lazily and cached.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public UIColor UIColor => this.color ??= this.DataResolver.GetExcelSheet<UIColor>().GetRow(this.colorKey);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{this.Type} - UIColor: {this.colorKey} color: {(this.IsEnabled ? this.RGB : 0)}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] EncodeImpl()
|
||||
{
|
||||
var colorBytes = MakeInteger(this.colorKey);
|
||||
|
|
@ -96,7 +99,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
|
||||
var bytes = new List<byte>(new byte[]
|
||||
{
|
||||
START_BYTE, (byte)SeStringChunkType.UIGlow, (byte)chunkLen
|
||||
START_BYTE, (byte)SeStringChunkType.UIGlow, (byte)chunkLen,
|
||||
});
|
||||
|
||||
bytes.AddRange(colorBytes);
|
||||
|
|
@ -105,6 +108,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
|
|||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void DecodeImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
this.colorKey = (ushort)GetInteger(reader);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Newtonsoft.Json;
|
||||
|
|
@ -15,21 +14,42 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
public class SeString
|
||||
{
|
||||
/// <summary>
|
||||
/// The ordered list of payloads included in this SeString.
|
||||
/// Initializes a new instance of the <see cref="SeString"/> class.
|
||||
/// Creates a new SeString from an ordered list of payloads.
|
||||
/// </summary>
|
||||
/// <param name="payloads">The Payload objects to make up this string.</param>
|
||||
[JsonConstructor]
|
||||
public SeString(List<Payload> payloads)
|
||||
{
|
||||
this.Payloads = payloads;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SeString"/> class.
|
||||
/// Creates a new SeString from an ordered list of payloads.
|
||||
/// </summary>
|
||||
/// <param name="payloads">The Payload objects to make up this string.</param>
|
||||
public SeString(Payload[] payloads)
|
||||
{
|
||||
this.Payloads = new List<Payload>(payloads);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ordered list of payloads included in this SeString.
|
||||
/// </summary>
|
||||
public List<Payload> Payloads { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to get all raw text from a message as a single joined string
|
||||
/// Gets all of the raw text from a message as a single joined string.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// All the raw text from the contained payloads, joined into a single string
|
||||
/// All the raw text from the contained payloads, joined into a single string.
|
||||
/// </returns>
|
||||
public string TextValue
|
||||
{
|
||||
get
|
||||
{
|
||||
return Payloads
|
||||
return this.Payloads
|
||||
.Where(p => p is ITextProvider)
|
||||
.Cast<ITextProvider>()
|
||||
.Aggregate(new StringBuilder(), (sb, tp) => sb.Append(tp.Text), sb => sb.ToString());
|
||||
|
|
@ -39,27 +59,45 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
/// <summary>
|
||||
/// Implicitly convert a string into a SeString containing a <see cref="TextPayload"/>.
|
||||
/// </summary>
|
||||
/// <param name="str">string to convert</param>
|
||||
/// <returns>Equivalent SeString</returns>
|
||||
public static implicit operator SeString(string str) => new SeString(new Payload[] { new TextPayload(str) });
|
||||
/// <param name="str">string to convert.</param>
|
||||
/// <returns>Equivalent SeString.</returns>
|
||||
public static implicit operator SeString(string str) => new(new Payload[] { new TextPayload(str) });
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new SeString from an ordered list of payloads.
|
||||
/// Creates a SeString from a json. (For testing - not recommended for production use.)
|
||||
/// </summary>
|
||||
/// <param name="payloads">The Payload objects to make up this string.</param>
|
||||
[JsonConstructor]
|
||||
public SeString(List<Payload> payloads)
|
||||
/// <param name="json">A serialized SeString produced by ToJson() <see cref="ToJson"/>.</param>
|
||||
/// <param name="dataManager">An initialized instance of DataManager for Lumina queries.</param>
|
||||
/// <returns>A SeString initialized with values from the json.</returns>
|
||||
public static SeString FromJson(string json, DataManager dataManager)
|
||||
{
|
||||
Payloads = payloads;
|
||||
var s = JsonConvert.DeserializeObject<SeString>(json, new JsonSerializerSettings
|
||||
{
|
||||
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
|
||||
TypeNameHandling = TypeNameHandling.Auto,
|
||||
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
|
||||
});
|
||||
|
||||
foreach (var payload in s.Payloads)
|
||||
{
|
||||
payload.DataResolver = dataManager;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new SeString from an ordered list of payloads.
|
||||
/// Serializes the SeString to json.
|
||||
/// </summary>
|
||||
/// <param name="payloads">The Payload objects to make up this string.</param>
|
||||
public SeString(Payload[] payloads)
|
||||
/// <returns>An json representation of this object.</returns>
|
||||
public string ToJson()
|
||||
{
|
||||
Payloads = new List<Payload>(payloads);
|
||||
return JsonConvert.SerializeObject(this, Formatting.Indented, new JsonSerializerSettings()
|
||||
{
|
||||
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||||
TypeNameHandling = TypeNameHandling.Auto,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -69,7 +107,7 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
/// <returns>This object.</returns>
|
||||
public SeString Append(SeString other)
|
||||
{
|
||||
Payloads.AddRange(other.Payloads);
|
||||
this.Payloads.AddRange(other.Payloads);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -80,7 +118,7 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
/// <returns>This object.</returns>
|
||||
public SeString Append(List<Payload> payloads)
|
||||
{
|
||||
Payloads.AddRange(payloads);
|
||||
this.Payloads.AddRange(payloads);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +129,7 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
/// <returns>This object.</returns>
|
||||
public SeString Append(Payload payload)
|
||||
{
|
||||
Payloads.Add(payload);
|
||||
this.Payloads.Add(payload);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -103,7 +141,7 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
public byte[] Encode()
|
||||
{
|
||||
var messageBytes = new List<byte>();
|
||||
foreach (var p in Payloads)
|
||||
foreach (var p in this.Payloads)
|
||||
{
|
||||
messageBytes.AddRange(p.Encode());
|
||||
}
|
||||
|
|
@ -114,46 +152,10 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
/// <summary>
|
||||
/// Get the text value of this SeString.
|
||||
/// </summary>
|
||||
/// <returns>The TextValue property</returns>
|
||||
public override string ToString() {
|
||||
return TextValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the SeString to json
|
||||
/// </summary>
|
||||
/// <returns>An json representation of this object</returns>
|
||||
public string ToJson()
|
||||
/// <returns>The TextValue property.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return JsonConvert.SerializeObject(this, Formatting.Indented, new JsonSerializerSettings()
|
||||
{
|
||||
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||||
TypeNameHandling = TypeNameHandling.Auto
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a SeString from a json. (For testing - not recommended for production use.)
|
||||
/// </summary>
|
||||
/// <param name="json">A serialized SeString produced by ToJson() <see cref="ToJson"/></param>
|
||||
/// <param name="dataManager">An initialized instance of DataManager for Lumina queries.</param>
|
||||
/// <returns>A SeString initialized with values from the json</returns>
|
||||
public static SeString FromJson(string json, DataManager dataManager)
|
||||
{
|
||||
var s = JsonConvert.DeserializeObject<SeString>(json, new JsonSerializerSettings
|
||||
{
|
||||
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
|
||||
TypeNameHandling = TypeNameHandling.Auto,
|
||||
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
|
||||
});
|
||||
|
||||
foreach(var payload in s.Payloads)
|
||||
{
|
||||
payload.DataResolver = dataManager;
|
||||
}
|
||||
|
||||
return s;
|
||||
return this.TextValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,27 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
|
||||
namespace Dalamud.Game.Text.SeStringHandling
|
||||
{
|
||||
/// <summary>
|
||||
/// This class facilitates creating new SeStrings and breaking down existing ones into their individual payload components.
|
||||
/// </summary>
|
||||
public class SeStringManager
|
||||
{
|
||||
private readonly DataManager data;
|
||||
|
||||
public SeStringManager(DataManager Data) {
|
||||
this.data = Data;
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SeStringManager"/> class.
|
||||
/// </summary>
|
||||
/// <param name="data">The DataManager instance.</param>
|
||||
public SeStringManager(DataManager data)
|
||||
{
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -51,7 +56,7 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
/// <returns>An SeString containing all the payloads necessary to display an item link in the chat log.</returns>
|
||||
public SeString CreateItemLink(uint itemId, bool isHQ, string displayNameOverride = null)
|
||||
{
|
||||
string displayName = displayNameOverride ?? this.data.GetExcelSheet<Item>().GetRow(itemId).Name;
|
||||
var displayName = displayNameOverride ?? this.data.GetExcelSheet<Item>().GetRow(itemId).Name;
|
||||
if (isHQ)
|
||||
{
|
||||
displayName += $" {(char)SeIconChar.HighQuality}";
|
||||
|
|
@ -65,11 +70,11 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
new ItemPayload(this.data, itemId, isHQ),
|
||||
// arrow goes here
|
||||
new TextPayload(displayName),
|
||||
RawPayload.LinkTerminator
|
||||
RawPayload.LinkTerminator,
|
||||
// sometimes there is another set of uiglow/foreground off payloads here
|
||||
// might be necessary when including additional text after the item name
|
||||
});
|
||||
payloads.InsertRange(3, TextArrowPayloads());
|
||||
payloads.InsertRange(3, this.TextArrowPayloads());
|
||||
|
||||
return new SeString(payloads);
|
||||
}
|
||||
|
|
@ -83,9 +88,17 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
/// <returns>An SeString containing all the payloads necessary to display an item link in the chat log.</returns>
|
||||
public SeString CreateItemLink(Item item, bool isHQ, string displayNameOverride = null)
|
||||
{
|
||||
return CreateItemLink((uint)item.RowId, isHQ, displayNameOverride ?? item.Name);
|
||||
return this.CreateItemLink((uint)item.RowId, isHQ, displayNameOverride ?? item.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an SeString representing an entire Payload chain that can be used to link a map position in the chat log.
|
||||
/// </summary>
|
||||
/// <param name="territoryId">The id of the TerritoryType for this map link.</param>
|
||||
/// <param name="mapId">The id of the Map for this map link.</param>
|
||||
/// <param name="rawX">The raw x-coordinate for this link.</param>
|
||||
/// <param name="rawY">The raw y-coordinate for this link..</param>
|
||||
/// <returns>An SeString containing all of the payloads necessary to display a map link in the chat log.</returns>
|
||||
public SeString CreateMapLink(uint territoryId, uint mapId, int rawX, int rawY)
|
||||
{
|
||||
var mapPayload = new MapLinkPayload(this.data, territoryId, mapId, rawX, rawY);
|
||||
|
|
@ -96,9 +109,9 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
mapPayload,
|
||||
// arrow goes here
|
||||
new TextPayload(nameString),
|
||||
RawPayload.LinkTerminator
|
||||
RawPayload.LinkTerminator,
|
||||
});
|
||||
payloads.InsertRange(1, TextArrowPayloads());
|
||||
payloads.InsertRange(1, this.TextArrowPayloads());
|
||||
|
||||
return new SeString(payloads);
|
||||
}
|
||||
|
|
@ -122,9 +135,9 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
mapPayload,
|
||||
// arrow goes here
|
||||
new TextPayload(nameString),
|
||||
RawPayload.LinkTerminator
|
||||
RawPayload.LinkTerminator,
|
||||
});
|
||||
payloads.InsertRange(1, TextArrowPayloads());
|
||||
payloads.InsertRange(1, this.TextArrowPayloads());
|
||||
|
||||
return new SeString(payloads);
|
||||
}
|
||||
|
|
@ -147,10 +160,10 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
|
||||
foreach (var place in matches)
|
||||
{
|
||||
var map = mapSheet.GetRows().FirstOrDefault(row => row.PlaceName.Row == place.RowId);
|
||||
var map = mapSheet.FirstOrDefault(row => row.PlaceName.Row == place.RowId);
|
||||
if (map != null)
|
||||
{
|
||||
return CreateMapLink(map.TerritoryType.Row, (uint)map.RowId, xCoord, yCoord, fudgeFactor);
|
||||
return this.CreateMapLink(map.TerritoryType.Row, map.RowId, xCoord, yCoord, fudgeFactor);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -171,7 +184,7 @@ namespace Dalamud.Game.Text.SeStringHandling
|
|||
new UIGlowPayload(this.data, 0x01F5),
|
||||
new TextPayload($"{(char)SeIconChar.LinkMarker}"),
|
||||
UIGlowPayload.UIGlowOff,
|
||||
UIForegroundPayload.UIForegroundOff
|
||||
UIForegroundPayload.UIForegroundOff,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue