Add Ornaments, further work.

This commit is contained in:
Ottermandias 2022-11-17 13:49:15 +01:00
parent bda3c1f1ac
commit f8c0702432
9 changed files with 273 additions and 161 deletions

View file

@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Dalamud.Game.ClientState.Objects.Enums;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using Newtonsoft.Json.Linq;
using Penumbra.String;
@ -58,12 +60,12 @@ public readonly struct ActorIdentifier : IEquatable<ActorIdentifier>
?? Type switch
{
IdentifierType.Player => $"{PlayerName} ({HomeWorld})",
IdentifierType.Owned => $"{PlayerName}s {Kind} {DataId} ({HomeWorld})",
IdentifierType.Owned => $"{PlayerName}s {Kind.ToName()} {DataId} ({HomeWorld})",
IdentifierType.Special => Special.ToName(),
IdentifierType.Npc =>
Index == ushort.MaxValue
? $"{Kind} #{DataId}"
: $"{Kind} #{DataId} at {Index}",
? $"{Kind.ToName()} #{DataId}"
: $"{Kind.ToName()} #{DataId} at {Index}",
_ => "Invalid",
};
@ -87,7 +89,6 @@ public readonly struct ActorIdentifier : IEquatable<ActorIdentifier>
PlayerName = playerName;
}
public JObject ToJson()
{
var ret = new JObject { { nameof(Type), Type.ToString() } };
@ -130,22 +131,19 @@ public static class ActorManagerExtensions
if (manager == null)
return lhs.Kind == rhs.Kind && lhs.DataId == rhs.DataId || lhs.DataId == uint.MaxValue || rhs.DataId == uint.MaxValue;
return lhs.Kind switch
var dict = lhs.Kind switch
{
ObjectKind.MountType => manager.Mounts.TryGetValue(lhs.DataId, out var lhsName)
&& manager.Mounts.TryGetValue(rhs.DataId, out var rhsName)
&& lhsName.Equals(rhsName, StringComparison.OrdinalIgnoreCase),
ObjectKind.Companion => manager.Companions.TryGetValue(lhs.DataId, out var lhsName)
&& manager.Companions.TryGetValue(rhs.DataId, out var rhsName)
&& lhsName.Equals(rhsName, StringComparison.OrdinalIgnoreCase),
ObjectKind.BattleNpc => manager.BNpcs.TryGetValue(lhs.DataId, out var lhsName)
&& manager.BNpcs.TryGetValue(rhs.DataId, out var rhsName)
&& lhsName.Equals(rhsName, StringComparison.OrdinalIgnoreCase),
ObjectKind.EventNpc => manager.ENpcs.TryGetValue(lhs.DataId, out var lhsName)
&& manager.ENpcs.TryGetValue(rhs.DataId, out var rhsName)
&& lhsName.Equals(rhsName, StringComparison.OrdinalIgnoreCase),
_ => false,
ObjectKind.MountType => manager.Mounts,
ObjectKind.Companion => manager.Companions,
(ObjectKind)15 => manager.Ornaments, // TODO: CS Update
ObjectKind.BattleNpc => manager.BNpcs,
ObjectKind.EventNpc => manager.ENpcs,
_ => new Dictionary<uint, string>(),
};
return dict.TryGetValue(lhs.DataId, out var lhsName)
&& dict.TryGetValue(rhs.DataId, out var rhsName)
&& lhsName.Equals(rhsName, StringComparison.OrdinalIgnoreCase);
}
public static string ToName(this ObjectKind kind)
@ -156,6 +154,7 @@ public static class ActorManagerExtensions
ObjectKind.EventNpc => "Event NPC",
ObjectKind.MountType => "Mount",
ObjectKind.Companion => "Companion",
(ObjectKind)15 => "Accessory", // TODO: CS update
_ => kind.ToString(),
};

View file

@ -2,18 +2,19 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using Dalamud;
using Dalamud.Data;
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.Gui;
using Dalamud.Plugin;
using Dalamud.Utility;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Component.GUI;
using Lumina.Excel;
using Lumina.Excel.GeneratedSheets;
using Lumina.Text;
using Penumbra.GameData.Data;
using Penumbra.String;
using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character;
@ -31,6 +32,9 @@ public sealed partial class ActorManager : DataSharer
/// <summary> Valid Companion names in title case by companion id. </summary>
public IReadOnlyDictionary<uint, string> Companions { get; }
/// <summary> Valid ornament names by id. </summary>
public IReadOnlyDictionary<uint, string> Ornaments { get; }
/// <summary> Valid BNPC names in title case by BNPC Name id. </summary>
public IReadOnlyDictionary<uint, string> BNpcs { get; }
@ -54,6 +58,7 @@ public sealed partial class ActorManager : DataSharer
Worlds = TryCatchData("Worlds", () => CreateWorldData(gameData));
Mounts = TryCatchData("Mounts", () => CreateMountData(gameData));
Companions = TryCatchData("Companions", () => CreateCompanionData(gameData));
Ornaments = TryCatchData("Ornaments", () => CreateOrnamentData(gameData));
BNpcs = TryCatchData("BNpcs", () => CreateBNpcData(gameData));
ENpcs = TryCatchData("ENpcs", () => CreateENpcData(gameData));
@ -98,6 +103,7 @@ public sealed partial class ActorManager : DataSharer
DisposeTag("Worlds");
DisposeTag("Mounts");
DisposeTag("Companions");
DisposeTag("Ornaments");
DisposeTag("BNpcs");
DisposeTag("ENpcs");
}
@ -119,22 +125,50 @@ public sealed partial class ActorManager : DataSharer
private IReadOnlyDictionary<uint, string> CreateMountData(DataManager gameData)
=> gameData.GetExcelSheet<Mount>(Language)!
.Where(m => m.Singular.RawData.Length > 0 && m.Order >= 0)
.ToDictionary(m => m.RowId, m => CultureInfo.InvariantCulture.TextInfo.ToTitleCase(m.Singular.ToDalamudString().ToString()));
.ToDictionary(m => m.RowId, m => ToTitleCaseExtended(m.Singular, m.Article));
private IReadOnlyDictionary<uint, string> CreateCompanionData(DataManager gameData)
=> gameData.GetExcelSheet<Companion>(Language)!
.Where(c => c.Singular.RawData.Length > 0 && c.Order < ushort.MaxValue)
.ToDictionary(c => c.RowId, c => CultureInfo.InvariantCulture.TextInfo.ToTitleCase(c.Singular.ToDalamudString().ToString()));
.ToDictionary(c => c.RowId, c => ToTitleCaseExtended(c.Singular, c.Article));
private IReadOnlyDictionary<uint, string> CreateOrnamentData(DataManager gameData)
=> gameData.GetExcelSheet<Ornament>(Language)!
.Where(o => o.Singular.RawData.Length > 0)
.ToDictionary(o => o.RowId, o => ToTitleCaseExtended(o.Singular, o.Article));
private IReadOnlyDictionary<uint, string> CreateBNpcData(DataManager gameData)
=> gameData.GetExcelSheet<BNpcName>(Language)!
.Where(n => n.Singular.RawData.Length > 0)
.ToDictionary(n => n.RowId, n => CultureInfo.InvariantCulture.TextInfo.ToTitleCase(n.Singular.ToDalamudString().ToString()));
.ToDictionary(n => n.RowId, n => ToTitleCaseExtended(n.Singular, n.Article));
private IReadOnlyDictionary<uint, string> CreateENpcData(DataManager gameData)
=> gameData.GetExcelSheet<ENpcResident>(Language)!
.Where(e => e.Singular.RawData.Length > 0)
.ToDictionary(e => e.RowId, e => CultureInfo.InvariantCulture.TextInfo.ToTitleCase(e.Singular.ToDalamudString().ToString()));
.ToDictionary(e => e.RowId, e => ToTitleCaseExtended(e.Singular, e.Article));
private static string ToTitleCaseExtended(SeString s, sbyte article)
{
if (article == 1)
return s.ToDalamudString().ToString();
var sb = new StringBuilder(s.ToDalamudString().ToString());
var lastSpace = true;
for (var i = 0; i < sb.Length; ++i)
{
if (sb[i] == ' ')
{
lastSpace = true;
}
else if (lastSpace)
{
lastSpace = false;
sb[i] = char.ToUpperInvariant(sb[i]);
}
}
return sb.ToString();
}
[Signature("0F B7 0D ?? ?? ?? ?? C7 85", ScanType = ScanType.StaticAddress)]

View file

@ -52,7 +52,7 @@ public partial class ActorManager
}
/// <summary>
/// Return the world name including the All Worlds option.
/// Return the world name including the Any World option.
/// </summary>
public string ToWorldName(ushort worldId)
=> worldId == ushort.MaxValue ? "Any World" : Worlds.TryGetValue(worldId, out var name) ? name : "Invalid";
@ -98,6 +98,7 @@ public partial class ActorManager
{
ObjectKind.MountType => Mounts.TryGetValue(dataId, out name),
ObjectKind.Companion => Companions.TryGetValue(dataId, out name),
(ObjectKind)15 => Ornaments.TryGetValue(dataId, out name), // TODO: CS Update
ObjectKind.BattleNpc => BNpcs.TryGetValue(dataId, out name),
ObjectKind.EventNpc => ENpcs.TryGetValue(dataId, out name),
_ => false,
@ -154,6 +155,7 @@ public partial class ActorManager
case ObjectKind.EventNpc: return CreateNpc(ObjectKind.EventNpc, actor->DataID, actor->ObjectIndex);
case ObjectKind.MountType:
case ObjectKind.Companion:
case (ObjectKind)15: // TODO: CS Update
{
if (actor->ObjectIndex % 2 == 0)
return ActorIdentifier.Invalid;
@ -173,11 +175,12 @@ public partial class ActorManager
/// Obtain the current companion ID for an object by its actor and owner.
/// </summary>
private unsafe uint GetCompanionId(FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* actor,
FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* owner)
FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* owner) // TODO: CS Update
{
return (ObjectKind)actor->ObjectKind switch
{
ObjectKind.MountType => *(ushort*)((byte*)owner + 0x668),
ObjectKind.MountType => *(ushort*)((byte*)owner + 0x650 + 0x18),
(ObjectKind)15 => *(ushort*)((byte*)owner + 0x860 + 0x18),
ObjectKind.Companion => *(ushort*)((byte*)actor + 0x1AAC),
_ => actor->DataID,
};
@ -196,6 +199,12 @@ public partial class ActorManager
_ => ActorIdentifier.Invalid,
};
/// <summary>
/// Only use this if you are sure the input is valid.
/// </summary>
public ActorIdentifier CreateIndividualUnchecked(IdentifierType type, ByteString name, ushort homeWorld, ObjectKind kind, uint dataId)
=> new(type, kind, homeWorld, dataId, name);
public ActorIdentifier CreatePlayer(ByteString name, ushort homeWorld)
{
if (!VerifyWorld(homeWorld) || !VerifyPlayerName(name.Span))
@ -345,6 +354,7 @@ public partial class ActorManager
{
ObjectKind.MountType => Mounts.ContainsKey(dataId),
ObjectKind.Companion => Companions.ContainsKey(dataId),
(ObjectKind)15 => Ornaments.ContainsKey(dataId), // TODO: CS Update
ObjectKind.BattleNpc => BNpcs.ContainsKey(dataId),
_ => false,
};
@ -355,6 +365,7 @@ public partial class ActorManager
{
ObjectKind.MountType => Mounts.ContainsKey(dataId),
ObjectKind.Companion => Companions.ContainsKey(dataId),
(ObjectKind)15 => Ornaments.ContainsKey(dataId), // TODO: CS Update
ObjectKind.BattleNpc => BNpcs.ContainsKey(dataId),
ObjectKind.EventNpc => ENpcs.ContainsKey(dataId),
_ => false,