mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 18:27:24 +01:00
Updated new functionality and internal gearset to use FFXIVClientStructs member functions and implemented structs, corrected remaining spacing issues.
This commit is contained in:
parent
2e11481276
commit
c43ce9d978
6 changed files with 33 additions and 94 deletions
|
|
@ -14,7 +14,6 @@ using StateChanged = Glamourer.Events.StateChanged;
|
||||||
using StateUpdated = Glamourer.Events.StateUpdated;
|
using StateUpdated = Glamourer.Events.StateUpdated;
|
||||||
|
|
||||||
namespace Glamourer.Api;
|
namespace Glamourer.Api;
|
||||||
|
|
||||||
public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
|
public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
|
||||||
{
|
{
|
||||||
private readonly ApiHelpers _helpers;
|
private readonly ApiHelpers _helpers;
|
||||||
|
|
@ -340,7 +339,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
|
||||||
|
|
||||||
private void OnStateChanged(StateChangeType type, StateSource _2, ActorState _3, ActorData actors, ITransaction? _5)
|
private void OnStateChanged(StateChangeType type, StateSource _2, ActorState _3, ActorData actors, ITransaction? _5)
|
||||||
{
|
{
|
||||||
// Glamourer.Log.Verbose($"[OnStateChanged] Sending out OnStateChanged with type {type}.");
|
Glamourer.Log.Excessive($"[OnStateChanged] State Changed with Type {type} [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
if (StateChanged != null)
|
if (StateChanged != null)
|
||||||
foreach (var actor in actors.Objects)
|
foreach (var actor in actors.Objects)
|
||||||
StateChanged.Invoke(actor.Address);
|
StateChanged.Invoke(actor.Address);
|
||||||
|
|
@ -352,7 +351,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
|
||||||
|
|
||||||
private void OnStateUpdated(StateUpdateType type, ActorData actors)
|
private void OnStateUpdated(StateUpdateType type, ActorData actors)
|
||||||
{
|
{
|
||||||
// Glamourer.Log.Verbose($"[OnStateUpdated] Sending out OnStateUpdated with type {type}.");
|
Glamourer.Log.Verbose($"[OnStateUpdated] State Updated with Type {type}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
if (StateUpdated != null)
|
if (StateUpdated != null)
|
||||||
foreach (var actor in actors.Objects)
|
foreach (var actor in actors.Objects)
|
||||||
StateUpdated.Invoke(actor.Address, type);
|
StateUpdated.Invoke(actor.Address, type);
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ using Penumbra.GameData.Interop;
|
||||||
namespace Glamourer.Events;
|
namespace Glamourer.Events;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Triggers when the equipped gearset finished running all of its LoadEquipment, LoadWeapon, and crest calls.
|
/// Triggers when the equipped gearset finished all LoadEquipment, LoadWeapon, and LoadCrest calls. (All Non-MetaData)
|
||||||
/// This defines a universal endpoint of base game state application to monitor.
|
/// This defines an endpoint for when the gameState is updated.
|
||||||
/// <list type="number">
|
/// <list type="number">
|
||||||
/// <item>The model drawobject associated with the finished load (Also fired by other players on render) </item>
|
/// <item>The model draw object associated with the finished load (Also fired by other players on render) </item>
|
||||||
/// </list>
|
/// </list>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class GearsetDataLoaded()
|
public sealed class GearsetDataLoaded()
|
||||||
|
|
@ -18,4 +18,4 @@ public sealed class GearsetDataLoaded()
|
||||||
/// <seealso cref="State.StateListener.OnGearsetDataLoaded"/>
|
/// <seealso cref="State.StateListener.OnGearsetDataLoaded"/>
|
||||||
StateListener = 0,
|
StateListener = 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,34 +23,26 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
|
||||||
_gearsetEvent = gearsetEvent;
|
_gearsetEvent = gearsetEvent;
|
||||||
|
|
||||||
_moveItemHook = interop.HookFromAddress<MoveItemDelegate>((nint)InventoryManager.MemberFunctionPointers.MoveItemSlot, MoveItemDetour);
|
_moveItemHook = interop.HookFromAddress<MoveItemDelegate>((nint)InventoryManager.MemberFunctionPointers.MoveItemSlot, MoveItemDetour);
|
||||||
// This can be uncommented after ClientStructs Updates with EquipGearsetInternal after merged PR. (See comment below)
|
_equipGearsetHook = interop.HookFromAddress<EquipGearsetInternalDelegate>((nint)RaptureGearsetModule.MemberFunctionPointers.EquipGearsetInternal, EquipGearSetDetour);
|
||||||
//_equipGearsetInternalHook = interop.HookFromAddress<EquipGearsetDelegate>((nint)RaptureGearsetModule.MemberFunctionPointers.EquipGearsetInternal, EquipGearSetInternalDetour);
|
|
||||||
|
|
||||||
// Can be removed after ClientStructs Update since this is only needed for current EquipGearsetInternal [Signature]
|
|
||||||
interop.InitializeFromAttributes(this);
|
|
||||||
|
|
||||||
_moveItemHook.Enable();
|
_moveItemHook.Enable();
|
||||||
_equipGearsetInternalHook.Enable();
|
_equipGearsetHook.Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_moveItemHook.Dispose();
|
_moveItemHook.Dispose();
|
||||||
_equipGearsetInternalHook.Dispose();
|
_equipGearsetHook.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the internal function processed by all sources of Equipping a gearset, such as hotbar gearset application and command gearset application.
|
|
||||||
// Currently is pending to ClientStructs for integration. See: https://github.com/aers/FFXIVClientStructs/pull/1277
|
|
||||||
public const string EquipGearsetInternal = "40 55 53 56 57 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 85 ?? ?? ?? ?? 4C 63 FA";
|
|
||||||
private delegate nint EquipGearsetInternalDelegate(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId);
|
private delegate nint EquipGearsetInternalDelegate(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId);
|
||||||
|
|
||||||
[Signature(EquipGearsetInternal, DetourName = nameof(EquipGearSetInternalDetour))]
|
private readonly Hook<EquipGearsetInternalDelegate> _equipGearsetHook = null!;
|
||||||
private readonly Hook<EquipGearsetInternalDelegate> _equipGearsetInternalHook = null!;
|
|
||||||
|
|
||||||
private nint EquipGearSetInternalDetour(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId)
|
private nint EquipGearSetDetour(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId)
|
||||||
{
|
{
|
||||||
var prior = module->CurrentGearsetIndex;
|
var prior = module->CurrentGearsetIndex;
|
||||||
var ret = _equipGearsetInternalHook.Original(module, gearsetId, glamourPlateId);
|
var ret = _equipGearsetHook.Original(module, gearsetId, glamourPlateId);
|
||||||
var set = module->GetGearset((int)gearsetId);
|
var set = module->GetGearset((int)gearsetId);
|
||||||
_gearsetEvent.Invoke(new ByteString(set->Name).ToString(), (int)gearsetId, prior, glamourPlateId, set->ClassJob);
|
_gearsetEvent.Invoke(new ByteString(set->Name).ToString(), (int)gearsetId, prior, glamourPlateId, set->ClassJob);
|
||||||
Glamourer.Log.Verbose($"[InventoryService] Applied gear set {gearsetId} with glamour plate {glamourPlateId} (Returned {ret})");
|
Glamourer.Log.Verbose($"[InventoryService] Applied gear set {gearsetId} with glamour plate {glamourPlateId} (Returned {ret})");
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Dalamud.Utility.Signatures;
|
using Dalamud.Utility.Signatures;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game.Network;
|
||||||
using Glamourer.Events;
|
using Glamourer.Events;
|
||||||
using Penumbra.GameData;
|
using Penumbra.GameData;
|
||||||
using Penumbra.GameData.DataContainers;
|
using Penumbra.GameData.DataContainers;
|
||||||
|
|
@ -13,22 +14,20 @@ namespace Glamourer.Interop;
|
||||||
|
|
||||||
public unsafe class UpdateSlotService : IDisposable
|
public unsafe class UpdateSlotService : IDisposable
|
||||||
{
|
{
|
||||||
public readonly EquipSlotUpdating EquipSlotUpdatingEvent;
|
public readonly EquipSlotUpdating EquipSlotUpdatingEvent;
|
||||||
public readonly BonusSlotUpdating BonusSlotUpdatingEvent;
|
public readonly BonusSlotUpdating BonusSlotUpdatingEvent;
|
||||||
public readonly GearsetDataLoaded GearsetDataLoadedEvent;
|
public readonly GearsetDataLoaded GearsetDataLoadedEvent;
|
||||||
private readonly DictBonusItems _bonusItems;
|
private readonly DictBonusItems _bonusItems;
|
||||||
public UpdateSlotService(EquipSlotUpdating equipSlotUpdating, BonusSlotUpdating bonusSlotUpdating, GearsetDataLoaded gearsetDataLoaded,
|
public UpdateSlotService(EquipSlotUpdating equipSlotUpdating, BonusSlotUpdating bonusSlotUpdating, GearsetDataLoaded gearsetDataLoaded,
|
||||||
IGameInteropProvider interop, DictBonusItems bonusItems)
|
IGameInteropProvider interop, DictBonusItems bonusItems)
|
||||||
{
|
{
|
||||||
EquipSlotUpdatingEvent = equipSlotUpdating;
|
EquipSlotUpdatingEvent = equipSlotUpdating;
|
||||||
BonusSlotUpdatingEvent = bonusSlotUpdating;
|
BonusSlotUpdatingEvent = bonusSlotUpdating;
|
||||||
GearsetDataLoadedEvent = gearsetDataLoaded;
|
GearsetDataLoadedEvent = gearsetDataLoaded;
|
||||||
_bonusItems = bonusItems;
|
_bonusItems = bonusItems;
|
||||||
|
|
||||||
// Usable after the merge with client structs.
|
_loadGearsetDataHook = interop.HookFromAddress<LoadGearsetDataDelegate>((nint)DrawDataContainer.MemberFunctionPointers.LoadGearsetData, LoadGearsetDataDetour);
|
||||||
//_loadGearsetDataHook = interop.HookFromAddress<LoadGearsetDataDelegate>((nint)DrawDataContainer.MemberFunctionPointers.LoadGearsetData, LoadGearsetDataDetour);
|
|
||||||
interop.InitializeFromAttributes(this);
|
interop.InitializeFromAttributes(this);
|
||||||
|
|
||||||
_flagSlotForUpdateHook.Enable();
|
_flagSlotForUpdateHook.Enable();
|
||||||
_flagBonusSlotForUpdateHook.Enable();
|
_flagBonusSlotForUpdateHook.Enable();
|
||||||
_loadGearsetDataHook.Enable();
|
_loadGearsetDataHook.Enable();
|
||||||
|
|
@ -87,25 +86,15 @@ public unsafe class UpdateSlotService : IDisposable
|
||||||
[Signature(Sigs.FlagBonusSlotForUpdate, DetourName = nameof(FlagBonusSlotForUpdateDetour))]
|
[Signature(Sigs.FlagBonusSlotForUpdate, DetourName = nameof(FlagBonusSlotForUpdateDetour))]
|
||||||
private readonly Hook<FlagSlotForUpdateDelegateIntern> _flagBonusSlotForUpdateHook = null!;
|
private readonly Hook<FlagSlotForUpdateDelegateIntern> _flagBonusSlotForUpdateHook = null!;
|
||||||
|
|
||||||
// This signature is what calls the weapon/equipment/crest load functions in the drawData container inherited from a human/characterBase.
|
/// <summary> Detours the func that makes all FlagSlotForUpdate calls on a gearset change or initial render of a given actor (Only Cases this is Called).
|
||||||
//
|
/// <para> Logic done after returning the original hook executes <b>After</b> all equipment/weapon/crest data is loaded into the Actors BaseData. </para>
|
||||||
// Contrary to assumption, this is not frequently fired when any slot changes, and is instead only called when another player
|
/// </summary>
|
||||||
// initially loads, or when the client player changes gearsets. (Does not fire when another player or self is redrawn)
|
private delegate Int64 LoadGearsetDataDelegate(DrawDataContainer* drawDataContainer, PacketPlayerGearsetData* gearsetData);
|
||||||
//
|
|
||||||
// This functions purpose is to iterate all Equipment/Weapon/Crest data on gearset change / initial player load, and determine which slots need to fire FlagSlotForUpdate.
|
|
||||||
//
|
|
||||||
// Because Glamourer processes GameState changes by detouring this method, this means by returning original after detour, any logic performed after will occur
|
|
||||||
// AFTER Glamourer finishes applying all changes to the game State, providing a gearset endpoint. (MetaData not included)
|
|
||||||
// Currently pending a merge to clientStructs, after which it can be removed, along with the explicit struct. See: https://github.com/aers/FFXIVClientStructs/pull/1277/files
|
|
||||||
public const string LoadGearsetDataSig = "48 89 5C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 44 0F B6 B9";
|
|
||||||
private delegate Int64 LoadGearsetDataDelegate(DrawDataContainer* drawDataContainer, GearsetDataStruct* gearsetData);
|
|
||||||
|
|
||||||
[Signature(LoadGearsetDataSig, DetourName = nameof(LoadGearsetDataDetour))]
|
|
||||||
private readonly Hook<LoadGearsetDataDelegate> _loadGearsetDataHook = null!;
|
private readonly Hook<LoadGearsetDataDelegate> _loadGearsetDataHook = null!;
|
||||||
|
|
||||||
private ulong FlagSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
|
private ulong FlagSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
|
||||||
{
|
{
|
||||||
var slot = slotIdx.ToEquipSlot();
|
var slot = slotIdx.ToEquipSlot();
|
||||||
var returnValue = ulong.MaxValue;
|
var returnValue = ulong.MaxValue;
|
||||||
EquipSlotUpdatingEvent.Invoke(drawObject, slot, ref *data, ref returnValue);
|
EquipSlotUpdatingEvent.Invoke(drawObject, slot, ref *data, ref returnValue);
|
||||||
Glamourer.Log.Excessive($"[FlagSlotForUpdate] Called with 0x{drawObject:X} for slot {slot} with {*data} ({returnValue:X}).");
|
Glamourer.Log.Excessive($"[FlagSlotForUpdate] Called with 0x{drawObject:X} for slot {slot} with {*data} ({returnValue:X}).");
|
||||||
|
|
@ -114,7 +103,7 @@ public unsafe class UpdateSlotService : IDisposable
|
||||||
|
|
||||||
private ulong FlagBonusSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
|
private ulong FlagBonusSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
|
||||||
{
|
{
|
||||||
var slot = slotIdx.ToBonusSlot();
|
var slot = slotIdx.ToBonusSlot();
|
||||||
var returnValue = ulong.MaxValue;
|
var returnValue = ulong.MaxValue;
|
||||||
BonusSlotUpdatingEvent.Invoke(drawObject, slot, ref *data, ref returnValue);
|
BonusSlotUpdatingEvent.Invoke(drawObject, slot, ref *data, ref returnValue);
|
||||||
Glamourer.Log.Excessive($"[FlagBonusSlotForUpdate] Called with 0x{drawObject:X} for slot {slot} with {*data} ({returnValue:X}).");
|
Glamourer.Log.Excessive($"[FlagBonusSlotForUpdate] Called with 0x{drawObject:X} for slot {slot} with {*data} ({returnValue:X}).");
|
||||||
|
|
@ -123,29 +112,27 @@ public unsafe class UpdateSlotService : IDisposable
|
||||||
|
|
||||||
private ulong FlagSlotForUpdateInterop(Model drawObject, EquipSlot slot, CharacterArmor armor)
|
private ulong FlagSlotForUpdateInterop(Model drawObject, EquipSlot slot, CharacterArmor armor)
|
||||||
{
|
{
|
||||||
Glamourer.Log.Excessive($"[FlagBonusSlotForUpdate] Invoked by Glamourer on 0x{drawObject.Address:X} on {slot} with itemdata {armor}.");
|
Glamourer.Log.Excessive($"[FlagBonusSlotForUpdate] Glamourer-Invoked on 0x{drawObject.Address:X} on {slot} with item data {armor}.");
|
||||||
return _flagSlotForUpdateHook.Original(drawObject.Address, slot.ToIndex(), &armor);
|
return _flagSlotForUpdateHook.Original(drawObject.Address, slot.ToIndex(), &armor);
|
||||||
}
|
}
|
||||||
private Int64 LoadGearsetDataDetour(DrawDataContainer* drawDataContainer, GearsetDataStruct* gearsetData)
|
private Int64 LoadGearsetDataDetour(DrawDataContainer* drawDataContainer, PacketPlayerGearsetData* gearsetData)
|
||||||
{
|
{
|
||||||
// Let the gearset data process all of its loads and slot flag update calls first.
|
|
||||||
var ret = _loadGearsetDataHook.Original(drawDataContainer, gearsetData);
|
var ret = _loadGearsetDataHook.Original(drawDataContainer, gearsetData);
|
||||||
Model drawObject = drawDataContainer->OwnerObject->DrawObject;
|
Model drawObject = drawDataContainer->OwnerObject->DrawObject;
|
||||||
Glamourer.Log.Verbose($"[LoadAllEquipmentDetour] Owner: 0x{drawObject.Address:X} Finished Applying its GameState!");
|
|
||||||
GearsetDataLoadedEvent.Invoke(drawObject);
|
GearsetDataLoadedEvent.Invoke(drawObject);
|
||||||
// Glamourer.Log.Verbose($"[LoadAllEquipmentDetour] GearsetItemData: {FormatGearsetItemDataStruct(*gearsetData)}");
|
// Glamourer.Log.Excessive($"[LoadAllEquipmentDetour] GearsetItemData: {FormatGearsetItemDataStruct(*gearsetData)}");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If you ever care to debug this, here is a formatted string output of this new gearsetData struct.
|
// If you ever care to debug this, here is a formatted string output of this new gearsetData struct.
|
||||||
private string FormatGearsetItemDataStruct(GearsetDataStruct gearsetData)
|
private string FormatGearsetItemDataStruct(PacketPlayerGearsetData gearsetData)
|
||||||
{
|
{
|
||||||
string ret =
|
string ret =
|
||||||
$"\nMainhandWeaponData: Id: {gearsetData.MainhandWeaponData.Id}, Type: {gearsetData.MainhandWeaponData.Type}, " +
|
$"\nMainhandWeaponData: Id: {gearsetData.MainhandWeaponData.Id}, Type: {gearsetData.MainhandWeaponData.Type}, " +
|
||||||
$"Variant: {gearsetData.MainhandWeaponData.Variant}, Stain0: {gearsetData.MainhandWeaponData.Stain0}, Stain1: {gearsetData.MainhandWeaponData.Stain1}" +
|
$"Variant: {gearsetData.MainhandWeaponData.Variant}, Stain0: {gearsetData.MainhandWeaponData.Stain0}, Stain1: {gearsetData.MainhandWeaponData.Stain1}" +
|
||||||
$"\nOffhandWeaponData: Id: {gearsetData.OffhandWeaponData.Id}, Type: {gearsetData.OffhandWeaponData.Type}, " +
|
$"\nOffhandWeaponData: Id: {gearsetData.OffhandWeaponData.Id}, Type: {gearsetData.OffhandWeaponData.Type}, " +
|
||||||
$"Variant: {gearsetData.OffhandWeaponData.Variant}, Stain0: {gearsetData.OffhandWeaponData.Stain0}, Stain1: {gearsetData.OffhandWeaponData.Stain1}" +
|
$"Variant: {gearsetData.OffhandWeaponData.Variant}, Stain0: {gearsetData.OffhandWeaponData.Stain0}, Stain1: {gearsetData.OffhandWeaponData.Stain1}" +
|
||||||
$"\nCrestBitField: {gearsetData.CrestBitField} | JobId: {gearsetData.JobId} | UNK_18: {gearsetData.UNK_18} | UNK_19: {gearsetData.UNK_19}";
|
$"\nCrestBitField: {gearsetData.CrestBitField} | JobId: {gearsetData.JobId}";
|
||||||
for (int offset = 20; offset <= 56; offset += sizeof(LegacyCharacterArmor))
|
for (int offset = 20; offset <= 56; offset += sizeof(LegacyCharacterArmor))
|
||||||
{
|
{
|
||||||
LegacyCharacterArmor* equipSlotPtr = (LegacyCharacterArmor*)((byte*)&gearsetData + offset);
|
LegacyCharacterArmor* equipSlotPtr = (LegacyCharacterArmor*)((byte*)&gearsetData + offset);
|
||||||
|
|
@ -156,43 +143,3 @@ public unsafe class UpdateSlotService : IDisposable
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can be removed once merged with client structs and referenced directly. See: https://github.com/aers/FFXIVClientStructs/pull/1277/files
|
|
||||||
[StructLayout(LayoutKind.Explicit)]
|
|
||||||
public readonly struct GearsetDataStruct
|
|
||||||
{
|
|
||||||
// Stores the weapon data. Includes both dyes in the data. </summary>
|
|
||||||
[FieldOffset(0)] public readonly WeaponModelId MainhandWeaponData;
|
|
||||||
[FieldOffset(8)] public readonly WeaponModelId OffhandWeaponData;
|
|
||||||
|
|
||||||
[FieldOffset(16)] public readonly byte CrestBitField; // A Bitfield:: ShieldCrest == 1, HeadCrest == 2, Chest Crest == 4
|
|
||||||
[FieldOffset(17)] public readonly byte JobId; // Job ID associated with the gearset change.
|
|
||||||
|
|
||||||
// Flicks from 0 to 127 (anywhere inbetween), have yet to associate what it is linked to. Remains the same when flicking between gearsets of the same job.
|
|
||||||
[FieldOffset(18)] public readonly byte UNK_18;
|
|
||||||
[FieldOffset(19)] public readonly byte UNK_19; // I have never seen this be anything other than 0.
|
|
||||||
|
|
||||||
// Legacy helmet equip slot armor for a character.
|
|
||||||
[FieldOffset(20)] public readonly LegacyCharacterArmor HeadSlotArmor;
|
|
||||||
[FieldOffset(24)] public readonly LegacyCharacterArmor TopSlotArmor;
|
|
||||||
[FieldOffset(28)] public readonly LegacyCharacterArmor ArmsSlotArmor;
|
|
||||||
[FieldOffset(32)] public readonly LegacyCharacterArmor LegsSlotArmor;
|
|
||||||
[FieldOffset(26)] public readonly LegacyCharacterArmor FeetSlotArmor;
|
|
||||||
[FieldOffset(40)] public readonly LegacyCharacterArmor EarSlotArmor;
|
|
||||||
[FieldOffset(44)] public readonly LegacyCharacterArmor NeckSlotArmor;
|
|
||||||
[FieldOffset(48)] public readonly LegacyCharacterArmor WristSlotArmor;
|
|
||||||
[FieldOffset(52)] public readonly LegacyCharacterArmor RFingerSlotArmor;
|
|
||||||
[FieldOffset(56)] public readonly LegacyCharacterArmor LFingerSlotArmor;
|
|
||||||
|
|
||||||
// Byte array of all slot's secondary dyes.
|
|
||||||
[FieldOffset(60)] public readonly byte HeadSlotSecondaryDye;
|
|
||||||
[FieldOffset(61)] public readonly byte TopSlotSecondaryDye;
|
|
||||||
[FieldOffset(62)] public readonly byte ArmsSlotSecondaryDye;
|
|
||||||
[FieldOffset(63)] public readonly byte LegsSlotSecondaryDye;
|
|
||||||
[FieldOffset(64)] public readonly byte FeetSlotSecondaryDye;
|
|
||||||
[FieldOffset(65)] public readonly byte EarSlotSecondaryDye;
|
|
||||||
[FieldOffset(66)] public readonly byte NeckSlotSecondaryDye;
|
|
||||||
[FieldOffset(67)] public readonly byte WristSlotSecondaryDye;
|
|
||||||
[FieldOffset(68)] public readonly byte RFingerSlotSecondaryDye;
|
|
||||||
[FieldOffset(69)] public readonly byte LFingerSlotSecondaryDye;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -416,7 +416,7 @@ public class StateEditor(
|
||||||
var actors = settings.Source.RequiresChange()
|
var actors = settings.Source.RequiresChange()
|
||||||
? Applier.ApplyAll(state, requiresRedraw, false)
|
? Applier.ApplyAll(state, requiresRedraw, false)
|
||||||
: ActorData.Invalid;
|
: ActorData.Invalid;
|
||||||
|
|
||||||
Glamourer.Log.Verbose(
|
Glamourer.Log.Verbose(
|
||||||
$"Applied design to {state.Identifier.Incognito(null)}. [Affecting {actors.ToLazyString("nothing")}.]");
|
$"Applied design to {state.Identifier.Incognito(null)}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
StateChanged.Invoke(StateChangeType.Design, state.Sources[MetaIndex.Wetness], state, actors, null); // FIXME: maybe later
|
StateChanged.Invoke(StateChangeType.Design, state.Sources[MetaIndex.Wetness], state, actors, null); // FIXME: maybe later
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,8 @@ public class StateListener : IDisposable
|
||||||
// then we do not want to use our restricted gear protection
|
// then we do not want to use our restricted gear protection
|
||||||
// since we assume the player has that gear modded to availability.
|
// since we assume the player has that gear modded to availability.
|
||||||
var locked = false;
|
var locked = false;
|
||||||
if (actor.Identifier(_actors, out var identifier) && _manager.TryGetValue(identifier, out var state))
|
if (actor.Identifier(_actors, out var identifier)
|
||||||
|
&& _manager.TryGetValue(identifier, out var state))
|
||||||
{
|
{
|
||||||
HandleEquipSlot(actor, state, slot, ref armor);
|
HandleEquipSlot(actor, state, slot, ref armor);
|
||||||
locked = state.Sources[slot, false] is StateSource.IpcFixed;
|
locked = state.Sources[slot, false] is StateSource.IpcFixed;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue