Add Model Parsing and display them under Changed Items, also display variants there, and rework Data Sharing a bunch.

This commit is contained in:
Ottermandias 2022-11-24 18:25:51 +01:00
parent a64273bd73
commit eedd3e2dac
21 changed files with 17032 additions and 332 deletions

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using Dalamud;
@ -20,25 +21,135 @@ using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character;
namespace Penumbra.GameData.Actors;
public sealed partial class ActorManager : DataSharer
public sealed partial class ActorManager : IDisposable
{
/// <summary> Worlds available for players. </summary>
public IReadOnlyDictionary<ushort, string> Worlds { get; }
public sealed class ActorManagerData : DataSharer
{
/// <summary> Worlds available for players. </summary>
public IReadOnlyDictionary<ushort, string> Worlds { get; }
/// <summary> Valid Mount names in title case by mount id. </summary>
public IReadOnlyDictionary<uint, string> Mounts { get; }
/// <summary> Valid Mount names in title case by mount id. </summary>
public IReadOnlyDictionary<uint, string> Mounts { get; }
/// <summary> Valid Companion names in title case by companion id. </summary>
public IReadOnlyDictionary<uint, string> Companions { get; }
/// <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 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; }
/// <summary> Valid BNPC names in title case by BNPC Name id. </summary>
public IReadOnlyDictionary<uint, string> BNpcs { get; }
/// <summary> Valid ENPC names in title case by ENPC id. </summary>
public IReadOnlyDictionary<uint, string> ENpcs { get; }
/// <summary> Valid ENPC names in title case by ENPC id. </summary>
public IReadOnlyDictionary<uint, string> ENpcs { get; }
public ActorManagerData(DalamudPluginInterface pluginInterface, DataManager gameData, ClientLanguage language)
: base(pluginInterface, language, 1)
{
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));
}
/// <summary>
/// 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";
/// <summary>
/// Convert a given ID for a certain ObjectKind to a name.
/// </summary>
/// <returns>Invalid or a valid name.</returns>
public string ToName(ObjectKind kind, uint dataId)
=> TryGetName(kind, dataId, out var ret) ? ret : "Invalid";
/// <summary>
/// Convert a given ID for a certain ObjectKind to a name.
/// </summary>
public bool TryGetName(ObjectKind kind, uint dataId, [NotNullWhen(true)] out string? name)
{
name = null;
return kind switch
{
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,
};
}
protected override void DisposeInternal()
{
DisposeTag("Worlds");
DisposeTag("Mounts");
DisposeTag("Companions");
DisposeTag("Ornaments");
DisposeTag("BNpcs");
DisposeTag("ENpcs");
}
private IReadOnlyDictionary<ushort, string> CreateWorldData(DataManager gameData)
=> gameData.GetExcelSheet<World>(Language)!
.Where(w => w.IsPublic && !w.Name.RawData.IsEmpty)
.ToDictionary(w => (ushort)w.RowId, w => w.Name.ToString());
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 => 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 => 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 => 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 => 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();
}
}
public readonly ActorManagerData Data;
public ActorManager(DalamudPluginInterface pluginInterface, ObjectTable objects, ClientState state, DataManager gameData, GameGui gameGui,
Func<ushort, short> toParentIdx)
@ -47,19 +158,12 @@ public sealed partial class ActorManager : DataSharer
public ActorManager(DalamudPluginInterface pluginInterface, ObjectTable objects, ClientState state, DataManager gameData, GameGui gameGui,
ClientLanguage language, Func<ushort, short> toParentIdx)
: base(pluginInterface, language, 1)
{
_objects = objects;
_gameGui = gameGui;
_clientState = state;
_toParentIdx = toParentIdx;
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));
Data = new ActorManagerData(pluginInterface, gameData, language);
ActorIdentifier.Manager = this;
@ -100,14 +204,11 @@ public sealed partial class ActorManager : DataSharer
return addon == IntPtr.Zero ? ActorIdentifier.Invalid : GetCurrentPlayer();
}
protected override void DisposeInternal()
public void Dispose()
{
DisposeTag("Worlds");
DisposeTag("Mounts");
DisposeTag("Companions");
DisposeTag("Ornaments");
DisposeTag("BNpcs");
DisposeTag("ENpcs");
Data.Dispose();
if (ActorIdentifier.Manager == this)
ActorIdentifier.Manager = null;
}
~ActorManager()
@ -119,60 +220,6 @@ public sealed partial class ActorManager : DataSharer
private readonly Func<ushort, short> _toParentIdx;
private IReadOnlyDictionary<ushort, string> CreateWorldData(DataManager gameData)
=> gameData.GetExcelSheet<World>(Language)!
.Where(w => w.IsPublic && !w.Name.RawData.IsEmpty)
.ToDictionary(w => (ushort)w.RowId, w => w.Name.ToString());
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 => 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 => 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 => 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 => 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)]
private static unsafe ushort* _inspectTitleId = null!;