mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Glamourer-related changes.
This commit is contained in:
parent
a7ace8a8c8
commit
9e0c38169f
15 changed files with 184 additions and 90 deletions
2
OtterGui
2
OtterGui
|
|
@ -1 +1 @@
|
||||||
Subproject commit e47c15c0496d1afad41cc7b2854dfc43d9b82eee
|
Subproject commit e43a0f00661f319be27f606b1aa841d3234dfb0f
|
||||||
|
|
@ -95,6 +95,9 @@ public readonly struct ActorIdentifier : IEquatable<ActorIdentifier>
|
||||||
_ => "Invalid",
|
_ => "Invalid",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public string ToName()
|
||||||
|
=> Manager?.ToName(this) ?? "Unknown Object";
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
=> Type switch
|
=> Type switch
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,23 @@ public partial class ActorManager
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use stored data to convert an ActorIdentifier to a name only.
|
||||||
|
/// </summary>
|
||||||
|
public string ToName(ActorIdentifier id)
|
||||||
|
{
|
||||||
|
return id.Type switch
|
||||||
|
{
|
||||||
|
IdentifierType.Player => id.PlayerName.ToString(),
|
||||||
|
IdentifierType.Retainer => id.PlayerName.ToString(),
|
||||||
|
IdentifierType.Owned => $"{id.PlayerName}s {Data.ToName(id.Kind, id.DataId)}",
|
||||||
|
IdentifierType.Special => id.Special.ToName(),
|
||||||
|
IdentifierType.Npc => Data.ToName(id.Kind, id.DataId),
|
||||||
|
IdentifierType.UnkObject => id.PlayerName.IsEmpty ? id.PlayerName.ToString() : "Unknown Object",
|
||||||
|
_ => "Invalid",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private unsafe FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* HandleCutscene(
|
private unsafe FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* HandleCutscene(
|
||||||
FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* main)
|
FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* main)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ using Dalamud.Plugin;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using PseudoEquipItem = System.ValueTuple<string, uint, ushort, ushort, ushort, byte, byte>;
|
using PseudoEquipItem = System.ValueTuple<string, ulong, ushort, ushort, ushort, byte, byte>;
|
||||||
|
|
||||||
namespace Penumbra.GameData.Data;
|
namespace Penumbra.GameData.Data;
|
||||||
|
|
||||||
|
|
@ -51,6 +51,14 @@ internal sealed class EquipmentIdentificationList : KeyList<PseudoEquipItem>
|
||||||
private static IEnumerable<PseudoEquipItem> CreateEquipmentList(DataManager gameData, ClientLanguage language)
|
private static IEnumerable<PseudoEquipItem> CreateEquipmentList(DataManager gameData, ClientLanguage language)
|
||||||
{
|
{
|
||||||
var items = gameData.GetExcelSheet<Item>(language)!;
|
var items = gameData.GetExcelSheet<Item>(language)!;
|
||||||
return items.Where(i => ((EquipSlot)i.EquipSlotCategory.Row).IsEquipmentPiece()).Select(i => (PseudoEquipItem)EquipItem.FromArmor(i));
|
return items.Where(i => ((EquipSlot)i.EquipSlotCategory.Row).IsEquipmentPiece())
|
||||||
|
.Select(i => (PseudoEquipItem)EquipItem.FromArmor(i))
|
||||||
|
.Concat(CustomList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<PseudoEquipItem> CustomList
|
||||||
|
=> new[]
|
||||||
|
{
|
||||||
|
(PseudoEquipItem)EquipItem.FromIds(0, 0, 8100, 0, 1, FullEquipType.Body, "Reaper Shroud"),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,9 @@ public sealed class HumanModelList : DataSharer
|
||||||
public bool IsHuman(uint modelId)
|
public bool IsHuman(uint modelId)
|
||||||
=> modelId < _humanModels.Count && _humanModels[(int)modelId];
|
=> modelId < _humanModels.Count && _humanModels[(int)modelId];
|
||||||
|
|
||||||
|
public int Count
|
||||||
|
=> _humanModels.Count;
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
protected override void DisposeInternal()
|
||||||
{
|
{
|
||||||
DisposeTag(Tag);
|
DisposeTag(Tag);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ using Dalamud.Plugin;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using PseudoEquipItem = System.ValueTuple<string, uint, ushort, ushort, ushort, byte, byte>;
|
using PseudoEquipItem = System.ValueTuple<string, ulong, ushort, ushort, ushort, byte, byte>;
|
||||||
|
|
||||||
namespace Penumbra.GameData.Data;
|
namespace Penumbra.GameData.Data;
|
||||||
|
|
||||||
|
|
@ -54,7 +54,7 @@ public sealed class ItemData : DataSharer, IReadOnlyDictionary<FullEquipType, IR
|
||||||
{
|
{
|
||||||
var list = items[(int)type];
|
var list = items[(int)type];
|
||||||
foreach (var item in list)
|
foreach (var item in list)
|
||||||
dict.TryAdd(item.Item2, item);
|
dict.TryAdd((uint) item.Item2, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
dict.TrimExcess();
|
dict.TrimExcess();
|
||||||
|
|
@ -68,7 +68,7 @@ public sealed class ItemData : DataSharer, IReadOnlyDictionary<FullEquipType, IR
|
||||||
{
|
{
|
||||||
var list = items[(int)type];
|
var list = items[(int)type];
|
||||||
foreach (var item in list)
|
foreach (var item in list)
|
||||||
dict.TryAdd(item.Item2, item);
|
dict.TryAdd((uint) item.Item2, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
dict.TrimExcess();
|
dict.TrimExcess();
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ using Lumina.Excel.GeneratedSheets;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Dalamud.Game.ClientState.Objects.Enums;
|
using Dalamud.Game.ClientState.Objects.Enums;
|
||||||
|
|
@ -25,7 +24,7 @@ internal sealed class ObjectIdentification : DataSharer, IObjectIdentifier
|
||||||
|
|
||||||
public IGamePathParser GamePathParser { get; } = new GamePathParser();
|
public IGamePathParser GamePathParser { get; } = new GamePathParser();
|
||||||
public readonly IReadOnlyList<IReadOnlyList<uint>> BnpcNames;
|
public readonly IReadOnlyList<IReadOnlyList<uint>> BnpcNames;
|
||||||
public readonly IReadOnlyList<IReadOnlyList<(string Name, ObjectKind Kind)>> ModelCharaToObjects;
|
public readonly IReadOnlyList<IReadOnlyList<(string Name, ObjectKind Kind, uint Id)>> ModelCharaToObjects;
|
||||||
public readonly IReadOnlyDictionary<string, IReadOnlyList<Action>> Actions;
|
public readonly IReadOnlyDictionary<string, IReadOnlyList<Action>> Actions;
|
||||||
private readonly ActorManager.ActorManagerData _actorData;
|
private readonly ActorManager.ActorManagerData _actorData;
|
||||||
|
|
||||||
|
|
@ -49,10 +48,8 @@ internal sealed class ObjectIdentification : DataSharer, IObjectIdentifier
|
||||||
public void Identify(IDictionary<string, object?> set, string path)
|
public void Identify(IDictionary<string, object?> set, string path)
|
||||||
{
|
{
|
||||||
if (path.EndsWith(".pap", StringComparison.OrdinalIgnoreCase) || path.EndsWith(".tmb", StringComparison.OrdinalIgnoreCase))
|
if (path.EndsWith(".pap", StringComparison.OrdinalIgnoreCase) || path.EndsWith(".tmb", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
|
||||||
if (IdentifyVfx(set, path))
|
if (IdentifyVfx(set, path))
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
var info = GamePathParser.GetFileInfo(path);
|
var info = GamePathParser.GetFileInfo(path);
|
||||||
IdentifyParsed(set, info);
|
IdentifyParsed(set, info);
|
||||||
|
|
@ -73,6 +70,15 @@ internal sealed class ObjectIdentification : DataSharer, IObjectIdentifier
|
||||||
_ => _equipment.Between(setId, slot, (byte)variant),
|
_ => _equipment.Between(setId, slot, (byte)variant),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public IReadOnlyList<uint> GetBnpcNames(uint bNpcId)
|
||||||
|
=> bNpcId >= BnpcNames.Count ? Array.Empty<uint>() : BnpcNames[(int)bNpcId];
|
||||||
|
|
||||||
|
public IReadOnlyList<(string Name, ObjectKind Kind, uint Id)> ModelCharaNames(uint modelId)
|
||||||
|
=> modelId >= ModelCharaToObjects.Count ? Array.Empty<(string Name, ObjectKind Kind, uint Id)>() : ModelCharaToObjects[(int)modelId];
|
||||||
|
|
||||||
|
public int NumModelChara
|
||||||
|
=> ModelCharaToObjects.Count;
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
protected override void DisposeInternal()
|
||||||
{
|
{
|
||||||
_actorData.Dispose();
|
_actorData.Dispose();
|
||||||
|
|
@ -125,14 +131,14 @@ internal sealed class ObjectIdentification : DataSharer, IObjectIdentifier
|
||||||
{
|
{
|
||||||
var items = _equipment.Between(info.PrimaryId, info.EquipSlot, info.Variant);
|
var items = _equipment.Between(info.PrimaryId, info.EquipSlot, info.Variant);
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
set[item.Name.ToString()] = item;
|
set[item.Name] = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FindWeapon(IDictionary<string, object?> set, GameObjectInfo info)
|
private void FindWeapon(IDictionary<string, object?> set, GameObjectInfo info)
|
||||||
{
|
{
|
||||||
var items = _weapons.Between(info.PrimaryId, info.SecondaryId, info.Variant);
|
var items = _weapons.Between(info.PrimaryId, info.SecondaryId, info.Variant);
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
set[item.Name.ToString()] = item;
|
set[item.Name] = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FindModel(IDictionary<string, object?> set, GameObjectInfo info)
|
private void FindModel(IDictionary<string, object?> set, GameObjectInfo info)
|
||||||
|
|
@ -142,10 +148,10 @@ internal sealed class ObjectIdentification : DataSharer, IObjectIdentifier
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var models = _modelIdentifierToModelChara.Between(type, info.PrimaryId, (byte)info.SecondaryId, info.Variant);
|
var models = _modelIdentifierToModelChara.Between(type, info.PrimaryId, (byte)info.SecondaryId, info.Variant);
|
||||||
foreach (var model in models.Where(m => m.RowId < ModelCharaToObjects.Count))
|
foreach (var model in models.Where(m => m.RowId != 0 && m.RowId < ModelCharaToObjects.Count))
|
||||||
{
|
{
|
||||||
var objectList = ModelCharaToObjects[(int)model.RowId];
|
var objectList = ModelCharaToObjects[(int)model.RowId];
|
||||||
foreach (var (name, kind) in objectList)
|
foreach (var (name, kind, _) in objectList)
|
||||||
set[$"{name} ({kind.ToName()})"] = model;
|
set[$"{name} ({kind.ToName()})"] = model;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -246,46 +252,46 @@ internal sealed class ObjectIdentification : DataSharer, IObjectIdentifier
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IReadOnlyList<IReadOnlyList<(string Name, ObjectKind Kind)>> CreateModelObjects(ActorManager.ActorManagerData actors,
|
private IReadOnlyList<IReadOnlyList<(string Name, ObjectKind Kind, uint Id)>> CreateModelObjects(ActorManager.ActorManagerData actors,
|
||||||
DataManager gameData, ClientLanguage language)
|
DataManager gameData, ClientLanguage language)
|
||||||
{
|
{
|
||||||
var modelSheet = gameData.GetExcelSheet<ModelChara>(language)!;
|
var modelSheet = gameData.GetExcelSheet<ModelChara>(language)!;
|
||||||
var ret = new List<ConcurrentBag<(string Name, ObjectKind Kind)>>((int)modelSheet.RowCount);
|
var ret = new List<ConcurrentBag<(string Name, ObjectKind Kind, uint Id)>>((int)modelSheet.RowCount);
|
||||||
|
|
||||||
for (var i = -1; i < modelSheet.Last().RowId; ++i)
|
for (var i = -1; i < modelSheet.Last().RowId; ++i)
|
||||||
ret.Add(new ConcurrentBag<(string Name, ObjectKind Kind)>());
|
ret.Add(new ConcurrentBag<(string Name, ObjectKind Kind, uint Id)>());
|
||||||
|
|
||||||
void AddChara(int modelChara, ObjectKind kind, uint dataId)
|
void AddChara(int modelChara, ObjectKind kind, uint dataId, uint displayId)
|
||||||
{
|
{
|
||||||
if (modelChara == 0 || modelChara >= ret.Count)
|
if (modelChara >= ret.Count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (actors.TryGetName(kind, dataId, out var name))
|
if (actors.TryGetName(kind, dataId, out var name))
|
||||||
ret[modelChara].Add((name, kind));
|
ret[modelChara].Add((name, kind, displayId));
|
||||||
}
|
}
|
||||||
|
|
||||||
var oTask = Task.Run(() =>
|
var oTask = Task.Run(() =>
|
||||||
{
|
{
|
||||||
foreach (var ornament in gameData.GetExcelSheet<Ornament>(language)!)
|
foreach (var ornament in gameData.GetExcelSheet<Ornament>(language)!)
|
||||||
AddChara(ornament.Model, (ObjectKind)15, ornament.RowId);
|
AddChara(ornament.Model, ObjectKind.Ornament, ornament.RowId, ornament.RowId);
|
||||||
});
|
});
|
||||||
|
|
||||||
var mTask = Task.Run(() =>
|
var mTask = Task.Run(() =>
|
||||||
{
|
{
|
||||||
foreach (var mount in gameData.GetExcelSheet<Mount>(language)!)
|
foreach (var mount in gameData.GetExcelSheet<Mount>(language)!)
|
||||||
AddChara((int)mount.ModelChara.Row, ObjectKind.MountType, mount.RowId);
|
AddChara((int)mount.ModelChara.Row, ObjectKind.MountType, mount.RowId, mount.RowId);
|
||||||
});
|
});
|
||||||
|
|
||||||
var cTask = Task.Run(() =>
|
var cTask = Task.Run(() =>
|
||||||
{
|
{
|
||||||
foreach (var companion in gameData.GetExcelSheet<Companion>(language)!)
|
foreach (var companion in gameData.GetExcelSheet<Companion>(language)!)
|
||||||
AddChara((int)companion.Model.Row, ObjectKind.Companion, companion.RowId);
|
AddChara((int)companion.Model.Row, ObjectKind.Companion, companion.RowId, companion.RowId);
|
||||||
});
|
});
|
||||||
|
|
||||||
var eTask = Task.Run(() =>
|
var eTask = Task.Run(() =>
|
||||||
{
|
{
|
||||||
foreach (var eNpc in gameData.GetExcelSheet<ENpcBase>(language)!)
|
foreach (var eNpc in gameData.GetExcelSheet<ENpcBase>(language)!)
|
||||||
AddChara((int)eNpc.ModelChara.Row, ObjectKind.EventNpc, eNpc.RowId);
|
AddChara((int)eNpc.ModelChara.Row, ObjectKind.EventNpc, eNpc.RowId, eNpc.RowId);
|
||||||
});
|
});
|
||||||
|
|
||||||
var options = new ParallelOptions()
|
var options = new ParallelOptions()
|
||||||
|
|
@ -296,14 +302,14 @@ internal sealed class ObjectIdentification : DataSharer, IObjectIdentifier
|
||||||
Parallel.ForEach(gameData.GetExcelSheet<BNpcBase>(language)!.Where(b => b.RowId < BnpcNames.Count), options, bNpc =>
|
Parallel.ForEach(gameData.GetExcelSheet<BNpcBase>(language)!.Where(b => b.RowId < BnpcNames.Count), options, bNpc =>
|
||||||
{
|
{
|
||||||
foreach (var name in BnpcNames[(int)bNpc.RowId])
|
foreach (var name in BnpcNames[(int)bNpc.RowId])
|
||||||
AddChara((int)bNpc.ModelChara.Row, ObjectKind.BattleNpc, name);
|
AddChara((int)bNpc.ModelChara.Row, ObjectKind.BattleNpc, name, bNpc.RowId);
|
||||||
});
|
});
|
||||||
|
|
||||||
Task.WaitAll(oTask, mTask, cTask, eTask);
|
Task.WaitAll(oTask, mTask, cTask, eTask);
|
||||||
|
|
||||||
return ret.Select(s => s.Count > 0
|
return ret.Select(s => !s.IsEmpty
|
||||||
? s.ToArray()
|
? s.ToArray()
|
||||||
: Array.Empty<(string Name, ObjectKind Kind)>()).ToArray();
|
: Array.Empty<(string Name, ObjectKind Kind, uint Id)>()).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe ulong KeyFromCharacterBase(CharacterBase* drawObject)
|
public static unsafe ulong KeyFromCharacterBase(CharacterBase* drawObject)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ using Dalamud.Plugin;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using PseudoEquipItem = System.ValueTuple<string, uint, ushort, ushort, ushort, byte, byte>;
|
using PseudoEquipItem = System.ValueTuple<string, ulong, ushort, ushort, ushort, byte, byte>;
|
||||||
|
|
||||||
namespace Penumbra.GameData.Data;
|
namespace Penumbra.GameData.Data;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,14 @@ public static class EquipSlotExtensions
|
||||||
public static readonly EquipSlot[] EquipmentSlots = Enum.GetValues<EquipSlot>().Where(e => e.IsEquipment()).ToArray();
|
public static readonly EquipSlot[] EquipmentSlots = Enum.GetValues<EquipSlot>().Where(e => e.IsEquipment()).ToArray();
|
||||||
public static readonly EquipSlot[] AccessorySlots = Enum.GetValues<EquipSlot>().Where(e => e.IsAccessory()).ToArray();
|
public static readonly EquipSlot[] AccessorySlots = Enum.GetValues<EquipSlot>().Where(e => e.IsAccessory()).ToArray();
|
||||||
public static readonly EquipSlot[] EqdpSlots = EquipmentSlots.Concat(AccessorySlots).ToArray();
|
public static readonly EquipSlot[] EqdpSlots = EquipmentSlots.Concat(AccessorySlots).ToArray();
|
||||||
|
|
||||||
|
public static readonly EquipSlot[] WeaponSlots =
|
||||||
|
{
|
||||||
|
EquipSlot.MainHand,
|
||||||
|
EquipSlot.OffHand,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static readonly EquipSlot[] FullSlots = WeaponSlots.Concat(EqdpSlots).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static partial class Names
|
public static partial class Names
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Dalamud;
|
using Dalamud;
|
||||||
using Dalamud.Data;
|
using Dalamud.Data;
|
||||||
|
using Dalamud.Game.ClientState.Objects.Enums;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
|
||||||
using Penumbra.GameData.Data;
|
using Penumbra.GameData.Data;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
|
|
@ -63,6 +63,14 @@ public interface IObjectIdentifier : IDisposable
|
||||||
/// <inheritdoc cref="Identify(SetId, WeaponType, ushort, EquipSlot)"/>
|
/// <inheritdoc cref="Identify(SetId, WeaponType, ushort, EquipSlot)"/>
|
||||||
public IEnumerable<EquipItem> Identify(SetId setId, ushort variant, EquipSlot slot)
|
public IEnumerable<EquipItem> Identify(SetId setId, ushort variant, EquipSlot slot)
|
||||||
=> Identify(setId, 0, variant, slot);
|
=> Identify(setId, 0, variant, slot);
|
||||||
|
|
||||||
|
/// <summary> Obtain a list of BNPC Name Ids associated with a BNPC Id. </summary>
|
||||||
|
public IReadOnlyList<uint> GetBnpcNames(uint bNpcId);
|
||||||
|
|
||||||
|
/// <summary> Obtain a list of Names and Object Kinds associated with a ModelChara ID. </summary>
|
||||||
|
public IReadOnlyList<(string Name, ObjectKind Kind, uint Id)> ModelCharaNames(uint modelId);
|
||||||
|
|
||||||
|
public int NumModelChara { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IGamePathParser
|
public interface IGamePathParser
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Penumbra.String.Functions;
|
using Penumbra.String.Functions;
|
||||||
|
|
@ -6,12 +8,28 @@ using Penumbra.String.Functions;
|
||||||
namespace Penumbra.GameData.Structs;
|
namespace Penumbra.GameData.Structs;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = Size)]
|
[StructLayout(LayoutKind.Sequential, Size = Size)]
|
||||||
public unsafe struct CustomizeData : IEquatable< CustomizeData >
|
public unsafe struct CustomizeData : IEquatable<CustomizeData>, IReadOnlyCollection<byte>
|
||||||
{
|
{
|
||||||
public const int Size = 26;
|
public const int Size = 26;
|
||||||
|
|
||||||
public fixed byte Data[Size];
|
public fixed byte Data[Size];
|
||||||
|
|
||||||
|
public int Count
|
||||||
|
=> Size;
|
||||||
|
|
||||||
|
public IEnumerator<byte> GetEnumerator()
|
||||||
|
{
|
||||||
|
for (var i = 0; i < Size; ++i)
|
||||||
|
yield return At(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
=> GetEnumerator();
|
||||||
|
|
||||||
|
|
||||||
|
private unsafe byte At(int index)
|
||||||
|
=> Data[index];
|
||||||
|
|
||||||
public void Read(void* source)
|
public void Read(void* source)
|
||||||
{
|
{
|
||||||
fixed (byte* ptr = Data)
|
fixed (byte* ptr = Data)
|
||||||
|
|
@ -78,21 +96,18 @@ public unsafe struct CustomizeData : IEquatable< CustomizeData >
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder(Size * 3);
|
var sb = new StringBuilder(Size * 3);
|
||||||
for (var i = 0; i < Size - 1; ++i)
|
for (var i = 0; i < Size - 1; ++i)
|
||||||
{
|
|
||||||
sb.Append($"{Data[i]:X2} ");
|
sb.Append($"{Data[i]:X2} ");
|
||||||
}
|
|
||||||
sb.Append($"{Data[Size - 1]:X2}");
|
sb.Append($"{Data[Size - 1]:X2}");
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public bool LoadBase64(string base64)
|
public bool LoadBase64(string base64)
|
||||||
{
|
{
|
||||||
var buffer = stackalloc byte[Size];
|
var buffer = stackalloc byte[Size];
|
||||||
var span = new Span<byte>(buffer, Size);
|
var span = new Span<byte>(buffer, Size);
|
||||||
if (!Convert.TryFromBase64String(base64, span, out var written) || written != Size)
|
if (!Convert.TryFromBase64String(base64, span, out var written) || written != Size)
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
Read(buffer);
|
Read(buffer);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using PseudoEquipItem = System.ValueTuple<string, uint, ushort, ushort, ushort, byte, byte>;
|
using PseudoEquipItem = System.ValueTuple<string, ulong, ushort, ushort, ushort, byte, byte>;
|
||||||
|
|
||||||
namespace Penumbra.GameData.Structs;
|
namespace Penumbra.GameData.Structs;
|
||||||
|
|
||||||
|
|
@ -10,13 +10,16 @@ namespace Penumbra.GameData.Structs;
|
||||||
public readonly struct EquipItem
|
public readonly struct EquipItem
|
||||||
{
|
{
|
||||||
public readonly string Name;
|
public readonly string Name;
|
||||||
public readonly uint Id;
|
public readonly ulong Id;
|
||||||
public readonly ushort IconId;
|
public readonly ushort IconId;
|
||||||
public readonly SetId ModelId;
|
public readonly SetId ModelId;
|
||||||
public readonly WeaponType WeaponType;
|
public readonly WeaponType WeaponType;
|
||||||
public readonly byte Variant;
|
public readonly byte Variant;
|
||||||
public readonly FullEquipType Type;
|
public readonly FullEquipType Type;
|
||||||
|
|
||||||
|
public uint ItemId
|
||||||
|
=> (uint)Id;
|
||||||
|
|
||||||
public bool Valid
|
public bool Valid
|
||||||
=> Type != FullEquipType.Unknown;
|
=> Type != FullEquipType.Unknown;
|
||||||
|
|
||||||
|
|
@ -35,7 +38,7 @@ public readonly struct EquipItem
|
||||||
public EquipItem()
|
public EquipItem()
|
||||||
=> Name = string.Empty;
|
=> Name = string.Empty;
|
||||||
|
|
||||||
public EquipItem(string name, uint id, ushort iconId, SetId modelId, WeaponType weaponType, byte variant, FullEquipType type)
|
public EquipItem(string name, ulong id, ushort iconId, SetId modelId, WeaponType weaponType, byte variant, FullEquipType type)
|
||||||
{
|
{
|
||||||
Name = string.Intern(name);
|
Name = string.Intern(name);
|
||||||
Id = id;
|
Id = id;
|
||||||
|
|
@ -53,7 +56,7 @@ public readonly struct EquipItem
|
||||||
=> new(it.Item1, it.Item2, it.Item3, it.Item4, it.Item5, it.Item6, (FullEquipType)it.Item7);
|
=> new(it.Item1, it.Item2, it.Item3, it.Item4, it.Item5, it.Item6, (FullEquipType)it.Item7);
|
||||||
|
|
||||||
public static explicit operator PseudoEquipItem(EquipItem it)
|
public static explicit operator PseudoEquipItem(EquipItem it)
|
||||||
=> (it.Name, it.Id, it.IconId, (ushort)it.ModelId, (ushort)it.WeaponType, it.Variant, (byte)it.Type);
|
=> (it.Name, it.ItemId, it.IconId, (ushort)it.ModelId, (ushort)it.WeaponType, it.Variant, (byte)it.Type);
|
||||||
|
|
||||||
public static EquipItem FromArmor(Item item)
|
public static EquipItem FromArmor(Item item)
|
||||||
{
|
{
|
||||||
|
|
@ -90,4 +93,28 @@ public readonly struct EquipItem
|
||||||
var variant = (byte)(item.ModelSub >> 32);
|
var variant = (byte)(item.ModelSub >> 32);
|
||||||
return new EquipItem(name, id, icon, model, weapon, variant, type);
|
return new EquipItem(name, id, icon, model, weapon, variant, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static EquipItem FromIds(uint itemId, ushort iconId, SetId modelId, WeaponType type, byte variant,
|
||||||
|
FullEquipType equipType = FullEquipType.Unknown, string? name = null)
|
||||||
|
{
|
||||||
|
name ??= $"Unknown ({modelId.Value}-{(type.Value != 0 ? $"{type.Value}-" : string.Empty)}{variant})";
|
||||||
|
var fullId = itemId == 0
|
||||||
|
? modelId.Value | ((ulong)type.Value << 16) | ((ulong)variant << 32) | ((ulong)equipType << 40) | (1ul << 48)
|
||||||
|
: itemId;
|
||||||
|
return new EquipItem(name, fullId, iconId, modelId, type, variant, equipType);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static EquipItem FromId(ulong id)
|
||||||
|
{
|
||||||
|
var setId = (SetId)id;
|
||||||
|
var type = (WeaponType)(id >> 16);
|
||||||
|
var variant = (byte)(id >> 32);
|
||||||
|
var equipType = (FullEquipType)(id >> 40);
|
||||||
|
return new EquipItem($"Unknown ({setId.Value}-{(type.Value != 0 ? $"{type.Value}-" : string.Empty)}{variant})", id, 0, setId, type,
|
||||||
|
variant, equipType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
=> Name;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ public sealed partial class IndividualCollections
|
||||||
|
|
||||||
identifiers = new[]
|
identifiers = new[]
|
||||||
{
|
{
|
||||||
manager.CreateRetainer(retainerName, 0),
|
manager.CreateRetainer(retainerName, ActorIdentifier.RetainerType.Both),
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case IdentifierType.Owned:
|
case IdentifierType.Owned:
|
||||||
|
|
@ -131,7 +131,7 @@ public sealed partial class IndividualCollections
|
||||||
ObjectKind.EventNpc => manager.Data.ENpcs,
|
ObjectKind.EventNpc => manager.Data.ENpcs,
|
||||||
ObjectKind.Companion => manager.Data.Companions,
|
ObjectKind.Companion => manager.Data.Companions,
|
||||||
ObjectKind.MountType => manager.Data.Mounts,
|
ObjectKind.MountType => manager.Data.Mounts,
|
||||||
(ObjectKind)15 => manager.Data.Ornaments,
|
ObjectKind.Ornament => manager.Data.Ornaments,
|
||||||
_ => throw new NotImplementedException(),
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
return table.Where(kvp => kvp.Value == name)
|
return table.Where(kvp => kvp.Value == name)
|
||||||
|
|
|
||||||
|
|
@ -122,14 +122,14 @@ public unsafe class MetaState : IDisposable
|
||||||
_gameEventManager.CharacterBaseCreated -= OnCharacterBaseCreated;
|
_gameEventManager.CharacterBaseCreated -= OnCharacterBaseCreated;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCreatingCharacterBase(uint modelCharaId, nint customize, nint equipData)
|
private void OnCreatingCharacterBase(nint modelCharaId, nint customize, nint equipData)
|
||||||
{
|
{
|
||||||
_lastCreatedCollection = _collectionResolver.IdentifyLastGameObjectCollection(true);
|
_lastCreatedCollection = _collectionResolver.IdentifyLastGameObjectCollection(true);
|
||||||
if (_lastCreatedCollection.Valid && _lastCreatedCollection.AssociatedGameObject != nint.Zero)
|
if (_lastCreatedCollection.Valid && _lastCreatedCollection.AssociatedGameObject != nint.Zero)
|
||||||
_communicator.CreatingCharacterBase.Invoke(_lastCreatedCollection.AssociatedGameObject,
|
_communicator.CreatingCharacterBase.Invoke(_lastCreatedCollection.AssociatedGameObject,
|
||||||
_lastCreatedCollection.ModCollection.Name, (nint)(&modelCharaId), customize, equipData);
|
_lastCreatedCollection.ModCollection.Name, modelCharaId, customize, equipData);
|
||||||
|
|
||||||
var decal = new DecalReverter(_config, _characterUtility, _resources, _lastCreatedCollection, UsesDecal(modelCharaId, customize));
|
var decal = new DecalReverter(_config, _characterUtility, _resources, _lastCreatedCollection, UsesDecal(*(uint*)modelCharaId, customize));
|
||||||
var cmp = _lastCreatedCollection.ModCollection.TemporarilySetCmpFile(_characterUtility);
|
var cmp = _lastCreatedCollection.ModCollection.TemporarilySetCmpFile(_characterUtility);
|
||||||
_characterBaseCreateMetaChanges.Dispose(); // Should always be empty.
|
_characterBaseCreateMetaChanges.Dispose(); // Should always be empty.
|
||||||
_characterBaseCreateMetaChanges = new DisposableContainer(decal, cmp);
|
_characterBaseCreateMetaChanges = new DisposableContainer(decal, cmp);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ using Dalamud.Hooking;
|
||||||
using Dalamud.Utility.Signatures;
|
using Dalamud.Utility.Signatures;
|
||||||
using Penumbra.GameData;
|
using Penumbra.GameData;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
using Penumbra.Interop.Structs;
|
using Penumbra.Interop.Structs;
|
||||||
|
|
@ -154,7 +153,7 @@ public unsafe class GameEventManager : IDisposable
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
((CreatingCharacterBaseEvent)subscriber).Invoke(a, b, c);
|
((CreatingCharacterBaseEvent)subscriber).Invoke((nint) (&a), b, c);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -181,7 +180,7 @@ public unsafe class GameEventManager : IDisposable
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public delegate void CreatingCharacterBaseEvent(uint modelCharaId, nint customize, nint equipment);
|
public delegate void CreatingCharacterBaseEvent(nint modelCharaId, nint customize, nint equipment);
|
||||||
public delegate void CharacterBaseCreatedEvent(uint modelCharaId, nint customize, nint equipment, nint drawObject);
|
public delegate void CharacterBaseCreatedEvent(uint modelCharaId, nint customize, nint equipment, nint drawObject);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue