Glamourer-related changes.

This commit is contained in:
Ottermandias 2023-07-12 02:45:40 +02:00
parent a7ace8a8c8
commit 9e0c38169f
15 changed files with 184 additions and 90 deletions

@ -1 +1 @@
Subproject commit e47c15c0496d1afad41cc7b2854dfc43d9b82eee
Subproject commit e43a0f00661f319be27f606b1aa841d3234dfb0f

View file

@ -17,8 +17,8 @@ public readonly struct ActorIdentifier : IEquatable<ActorIdentifier>
public enum RetainerType : ushort
{
Both = 0,
Bell = 1,
Both = 0,
Bell = 1,
Mannequin = 2,
}
@ -95,6 +95,9 @@ public readonly struct ActorIdentifier : IEquatable<ActorIdentifier>
_ => "Invalid",
};
public string ToName()
=> Manager?.ToName(this) ?? "Unknown Object";
public override int GetHashCode()
=> Type switch
{
@ -217,6 +220,6 @@ public static class ActorManagerExtensions
ScreenActor.FittingRoom => "Fitting Room Actor",
ScreenActor.DyePreview => "Dye Preview Actor",
ScreenActor.Portrait => "Portrait Actor",
_ => "Invalid",
_ => "Invalid",
};
}

View file

@ -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(
FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* main)
{
@ -382,7 +399,7 @@ public partial class ActorManager
public unsafe ActorIdentifier FromObject(GameObject? actor, out FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* owner,
bool allowPlayerNpc, bool check, bool withoutIndex)
=> FromObject((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)(actor?.Address ?? IntPtr.Zero), out owner, allowPlayerNpc,
check, withoutIndex);
check, withoutIndex);
public unsafe ActorIdentifier FromObject(GameObject? actor, bool allowPlayerNpc, bool check, bool withoutIndex)
=> FromObject(actor, out _, allowPlayerNpc, check, withoutIndex);

View file

@ -6,7 +6,7 @@ using Dalamud.Plugin;
using Lumina.Excel.GeneratedSheets;
using Penumbra.GameData.Enums;
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;
@ -51,6 +51,14 @@ internal sealed class EquipmentIdentificationList : KeyList<PseudoEquipItem>
private static IEnumerable<PseudoEquipItem> CreateEquipmentList(DataManager gameData, ClientLanguage 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"),
};
}

View file

@ -10,7 +10,7 @@ namespace Penumbra.GameData.Data;
public sealed class HumanModelList : DataSharer
{
public const string Tag = "HumanModels";
public const string Tag = "HumanModels";
public const int CurrentVersion = 1;
private readonly BitArray _humanModels;
@ -24,6 +24,9 @@ public sealed class HumanModelList : DataSharer
public bool IsHuman(uint modelId)
=> modelId < _humanModels.Count && _humanModels[(int)modelId];
public int Count
=> _humanModels.Count;
protected override void DisposeInternal()
{
DisposeTag(Tag);

View file

@ -8,7 +8,7 @@ using Dalamud.Plugin;
using Lumina.Excel.GeneratedSheets;
using Penumbra.GameData.Enums;
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;
@ -54,7 +54,7 @@ public sealed class ItemData : DataSharer, IReadOnlyDictionary<FullEquipType, IR
{
var list = items[(int)type];
foreach (var item in list)
dict.TryAdd(item.Item2, item);
dict.TryAdd((uint) item.Item2, item);
}
dict.TrimExcess();
@ -68,7 +68,7 @@ public sealed class ItemData : DataSharer, IReadOnlyDictionary<FullEquipType, IR
{
var list = items[(int)type];
foreach (var item in list)
dict.TryAdd(item.Item2, item);
dict.TryAdd((uint) item.Item2, item);
}
dict.TrimExcess();

View file

@ -6,7 +6,6 @@ using Lumina.Excel.GeneratedSheets;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Dalamud.Game.ClientState.Objects.Enums;
@ -23,11 +22,11 @@ internal sealed class ObjectIdentification : DataSharer, IObjectIdentifier
{
public const int IdentificationVersion = 1;
public IGamePathParser GamePathParser { get; } = new GamePathParser();
public readonly IReadOnlyList<IReadOnlyList<uint>> BnpcNames;
public readonly IReadOnlyList<IReadOnlyList<(string Name, ObjectKind Kind)>> ModelCharaToObjects;
public readonly IReadOnlyDictionary<string, IReadOnlyList<Action>> Actions;
private readonly ActorManager.ActorManagerData _actorData;
public IGamePathParser GamePathParser { get; } = new GamePathParser();
public readonly IReadOnlyList<IReadOnlyList<uint>> BnpcNames;
public readonly IReadOnlyList<IReadOnlyList<(string Name, ObjectKind Kind, uint Id)>> ModelCharaToObjects;
public readonly IReadOnlyDictionary<string, IReadOnlyList<Action>> Actions;
private readonly ActorManager.ActorManagerData _actorData;
private readonly EquipmentIdentificationList _equipment;
private readonly WeaponIdentificationList _weapons;
@ -49,10 +48,8 @@ internal sealed class ObjectIdentification : DataSharer, IObjectIdentifier
public void Identify(IDictionary<string, object?> set, string path)
{
if (path.EndsWith(".pap", StringComparison.OrdinalIgnoreCase) || path.EndsWith(".tmb", StringComparison.OrdinalIgnoreCase))
{
if (IdentifyVfx(set, path))
return;
}
var info = GamePathParser.GetFileInfo(path);
IdentifyParsed(set, info);
@ -73,6 +70,15 @@ internal sealed class ObjectIdentification : DataSharer, IObjectIdentifier
_ => _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()
{
_actorData.Dispose();
@ -125,14 +131,14 @@ internal sealed class ObjectIdentification : DataSharer, IObjectIdentifier
{
var items = _equipment.Between(info.PrimaryId, info.EquipSlot, info.Variant);
foreach (var item in items)
set[item.Name.ToString()] = item;
set[item.Name] = item;
}
private void FindWeapon(IDictionary<string, object?> set, GameObjectInfo info)
{
var items = _weapons.Between(info.PrimaryId, info.SecondaryId, info.Variant);
foreach (var item in items)
set[item.Name.ToString()] = item;
set[item.Name] = item;
}
private void FindModel(IDictionary<string, object?> set, GameObjectInfo info)
@ -142,10 +148,10 @@ internal sealed class ObjectIdentification : DataSharer, IObjectIdentifier
return;
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];
foreach (var (name, kind) in objectList)
foreach (var (name, kind, _) in objectList)
set[$"{name} ({kind.ToName()})"] = model;
}
}
@ -246,46 +252,46 @@ internal sealed class ObjectIdentification : DataSharer, IObjectIdentifier
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)
{
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)
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;
if (actors.TryGetName(kind, dataId, out var name))
ret[modelChara].Add((name, kind));
ret[modelChara].Add((name, kind, displayId));
}
var oTask = Task.Run(() =>
{
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(() =>
{
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(() =>
{
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(() =>
{
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()
@ -296,14 +302,14 @@ internal sealed class ObjectIdentification : DataSharer, IObjectIdentifier
Parallel.ForEach(gameData.GetExcelSheet<BNpcBase>(language)!.Where(b => b.RowId < BnpcNames.Count), options, bNpc =>
{
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);
return ret.Select(s => s.Count > 0
return ret.Select(s => !s.IsEmpty
? 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)

View file

@ -6,7 +6,7 @@ using Dalamud.Plugin;
using Lumina.Excel.GeneratedSheets;
using Penumbra.GameData.Enums;
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;

View file

@ -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[] AccessorySlots = Enum.GetValues<EquipSlot>().Where(e => e.IsAccessory()).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

View file

@ -2,8 +2,8 @@ using System;
using System.Collections.Generic;
using Dalamud;
using Dalamud.Data;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Plugin;
using Lumina.Excel.GeneratedSheets;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@ -63,6 +63,14 @@ public interface IObjectIdentifier : IDisposable
/// <inheritdoc cref="Identify(SetId, WeaponType, ushort, EquipSlot)"/>
public IEnumerable<EquipItem> Identify(SetId setId, ushort variant, EquipSlot 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

View file

@ -1,4 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using Penumbra.String.Functions;
@ -6,45 +8,61 @@ using Penumbra.String.Functions;
namespace Penumbra.GameData.Structs;
[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 fixed byte Data[Size];
public void Read( void* source )
public int Count
=> Size;
public IEnumerator<byte> GetEnumerator()
{
fixed( byte* ptr = Data )
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)
{
fixed (byte* ptr = Data)
{
MemoryUtility.MemCpyUnchecked( ptr, source, Size );
MemoryUtility.MemCpyUnchecked(ptr, source, Size);
}
}
public readonly void Write( void* target )
public readonly void Write(void* target)
{
fixed( byte* ptr = Data )
fixed (byte* ptr = Data)
{
MemoryUtility.MemCpyUnchecked( target, ptr, Size );
MemoryUtility.MemCpyUnchecked(target, ptr, Size);
}
}
public readonly CustomizeData Clone()
{
var ret = new CustomizeData();
Write( ret.Data );
Write(ret.Data);
return ret;
}
public readonly bool Equals( CustomizeData other )
public readonly bool Equals(CustomizeData other)
{
fixed( byte* ptr = Data )
fixed (byte* ptr = Data)
{
return MemoryUtility.MemCmpUnchecked( ptr, other.Data, Size ) == 0;
return MemoryUtility.MemCmpUnchecked(ptr, other.Data, Size) == 0;
}
}
public override bool Equals( object? obj )
=> obj is CustomizeData other && Equals( other );
public override bool Equals(object? obj)
=> obj is CustomizeData other && Equals(other);
public static bool Equals(CustomizeData* lhs, CustomizeData* rhs)
=> MemoryUtility.MemCmpUnchecked(lhs, rhs, Size) == 0;
@ -57,20 +75,20 @@ public unsafe struct CustomizeData : IEquatable< CustomizeData >
public override int GetHashCode()
{
fixed( byte* ptr = Data )
fixed (byte* ptr = Data)
{
var p = ( int* )ptr;
var u = *( ushort* )( p + 6 );
return HashCode.Combine( *p, p[ 1 ], p[ 2 ], p[ 3 ], p[ 4 ], p[ 5 ], u );
var p = (int*)ptr;
var u = *(ushort*)(p + 6);
return HashCode.Combine(*p, p[1], p[2], p[3], p[4], p[5], u);
}
}
public readonly string WriteBase64()
{
fixed( byte* ptr = Data )
fixed (byte* ptr = Data)
{
var data = new ReadOnlySpan< byte >( ptr, Size );
return Convert.ToBase64String( data );
var data = new ReadOnlySpan<byte>(ptr, Size);
return Convert.ToBase64String(data);
}
}
@ -78,23 +96,20 @@ public unsafe struct CustomizeData : IEquatable< CustomizeData >
{
var sb = new StringBuilder(Size * 3);
for (var i = 0; i < Size - 1; ++i)
{
sb.Append($"{Data[i]:X2} ");
}
sb.Append($"{Data[Size - 1]:X2}");
return sb.ToString();
}
public bool LoadBase64( string base64 )
public bool LoadBase64(string base64)
{
var buffer = stackalloc byte[Size];
var span = new Span< byte >( buffer, Size );
if( !Convert.TryFromBase64String( base64, span, out var written ) || written != Size )
{
var span = new Span<byte>(buffer, Size);
if (!Convert.TryFromBase64String(base64, span, out var written) || written != Size)
return false;
}
Read( buffer );
Read(buffer);
return true;
}
}

View file

@ -2,7 +2,7 @@
using Dalamud.Utility;
using Lumina.Excel.GeneratedSheets;
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;
@ -10,13 +10,16 @@ namespace Penumbra.GameData.Structs;
public readonly struct EquipItem
{
public readonly string Name;
public readonly uint Id;
public readonly ulong Id;
public readonly ushort IconId;
public readonly SetId ModelId;
public readonly WeaponType WeaponType;
public readonly byte Variant;
public readonly FullEquipType Type;
public uint ItemId
=> (uint)Id;
public bool Valid
=> Type != FullEquipType.Unknown;
@ -35,7 +38,7 @@ public readonly struct EquipItem
public EquipItem()
=> 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);
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);
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)
{
@ -90,4 +93,28 @@ public readonly struct EquipItem
var variant = (byte)(item.ModelSub >> 32);
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;
}

View file

@ -96,7 +96,7 @@ public sealed partial class IndividualCollections
identifiers = new[]
{
manager.CreateRetainer(retainerName, 0),
manager.CreateRetainer(retainerName, ActorIdentifier.RetainerType.Both),
};
break;
case IdentifierType.Owned:
@ -131,7 +131,7 @@ public sealed partial class IndividualCollections
ObjectKind.EventNpc => manager.Data.ENpcs,
ObjectKind.Companion => manager.Data.Companions,
ObjectKind.MountType => manager.Data.Mounts,
(ObjectKind)15 => manager.Data.Ornaments,
ObjectKind.Ornament => manager.Data.Ornaments,
_ => throw new NotImplementedException(),
};
return table.Where(kvp => kvp.Value == name)

View file

@ -122,14 +122,14 @@ public unsafe class MetaState : IDisposable
_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);
if (_lastCreatedCollection.Valid && _lastCreatedCollection.AssociatedGameObject != nint.Zero)
_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);
_characterBaseCreateMetaChanges.Dispose(); // Should always be empty.
_characterBaseCreateMetaChanges = new DisposableContainer(decal, cmp);

View file

@ -2,7 +2,6 @@ using Dalamud.Hooking;
using Dalamud.Utility.Signatures;
using Penumbra.GameData;
using System;
using System.Diagnostics;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Penumbra.Interop.Structs;
@ -154,7 +153,7 @@ public unsafe class GameEventManager : IDisposable
{
try
{
((CreatingCharacterBaseEvent)subscriber).Invoke(a, b, c);
((CreatingCharacterBaseEvent)subscriber).Invoke((nint) (&a), b, c);
}
catch (Exception ex)
{
@ -181,7 +180,7 @@ public unsafe class GameEventManager : IDisposable
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);
#endregion