Progress made. May be successful!

This commit is contained in:
Cordelia Mist 2025-01-16 19:34:46 -08:00
parent 8160f420db
commit c605d19510
9 changed files with 201 additions and 63 deletions

View file

@ -333,6 +333,8 @@ 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.Error($"[OnStateChanged API CALL] Sending out OnStateChanged with type {type}.");
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);

View file

@ -1,4 +1,5 @@
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using FFXIVClientStructs.FFXIV.Client.UI.Misc; using FFXIVClientStructs.FFXIV.Client.UI.Misc;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Designs.Links; using Glamourer.Designs.Links;
@ -201,11 +202,22 @@ public sealed class AutoDesignApplier : IDisposable
} }
} }
/// <summary>
/// JOB CHANGE IS CALLED UPON HERE.
/// </summary>
private void OnJobChange(Actor actor, Job oldJob, Job newJob) private void OnJobChange(Actor actor, Job oldJob, Job newJob)
{ {
unsafe
{
var drawObject = actor.AsCharacter->DrawObject;
Glamourer.Log.Information($"[AutoDesignApplier][OnJobChange] 0x{(nint)drawObject:X} changed job from {oldJob} ({oldJob.Id}) to {newJob} ({newJob.Id}).");
}
if (!_config.EnableAutoDesigns || !actor.Identifier(_actors, out var id)) if (!_config.EnableAutoDesigns || !actor.Identifier(_actors, out var id))
return; return;
Glamourer.Log.Information($"[AutoDesignApplier][OnJobChange] We had EnableAutoDesigns active, and are a valid actor!");
if (!GetPlayerSet(id, out var set)) if (!GetPlayerSet(id, out var set))
{ {
if (_state.TryGetValue(id, out var s)) if (_state.TryGetValue(id, out var s))

View file

@ -64,12 +64,13 @@ public unsafe class ChangeCustomizeService : EventWrapperRef2<Model, CustomizeAr
private Hook<ChangeCustomizeDelegate> _changeCustomizeHook; private Hook<ChangeCustomizeDelegate> _changeCustomizeHook;
// manual invoke by calling the detours _original call to `execute to` instead of `listen to`.
public bool UpdateCustomize(Model model, CustomizeArray customize) public bool UpdateCustomize(Model model, CustomizeArray customize)
{ {
if (!model.IsHuman) if (!model.IsHuman)
return false; return false;
Glamourer.Log.Verbose($"[ChangeCustomize] Invoked on 0x{model.Address:X} with {customize}."); Glamourer.Log.Information($"[ChangeCustomize] Glamour-Invoked on 0x{model.Address:X} with {customize}.");
using var _ = InUpdate.EnterMethod(); using var _ = InUpdate.EnterMethod();
var ret = _original(model.AsHuman, customize.Data, true); var ret = _original(model.AsHuman, customize.Data, true);
return ret; return ret;
@ -78,16 +79,20 @@ public unsafe class ChangeCustomizeService : EventWrapperRef2<Model, CustomizeAr
public bool UpdateCustomize(Actor actor, CustomizeArray customize) public bool UpdateCustomize(Actor actor, CustomizeArray customize)
=> UpdateCustomize(actor.Model, customize); => UpdateCustomize(actor.Model, customize);
// detoured method.
private bool ChangeCustomizeDetour(Human* human, byte* data, byte skipEquipment) private bool ChangeCustomizeDetour(Human* human, byte* data, byte skipEquipment)
{ {
if (!InUpdate.InMethod) if (!InUpdate.InMethod)
Invoke(human, ref *(CustomizeArray*)data); Invoke(human, ref *(CustomizeArray*)data);
var ret = _changeCustomizeHook.Original(human, data, skipEquipment); var ret = _changeCustomizeHook.Original(human, data, skipEquipment);
Glamourer.Log.Information($"[ChangeCustomize] Called on with {*(CustomizeArray*)data} ({ret}).");
_postEvent.Invoke(human); _postEvent.Invoke(human);
return ret; return ret;
} }
public void Subscribe(Action<Model> action, Post.Priority priority) public void Subscribe(Action<Model> action, Post.Priority priority)
=> _postEvent.Subscribe(action, priority); => _postEvent.Subscribe(action, priority);

View file

@ -1,9 +1,11 @@
using Dalamud.Hooking; using Dalamud.Hooking;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.UI.Misc; using FFXIVClientStructs.FFXIV.Client.UI.Misc;
using Glamourer.Events; using Glamourer.Events;
using OtterGui.Services; using OtterGui.Services;
using Penumbra.GameData;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using Penumbra.String; using Penumbra.String;
@ -16,39 +18,48 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
private readonly EquippedGearset _gearsetEvent; private readonly EquippedGearset _gearsetEvent;
private readonly List<(EquipSlot, uint, StainIds)> _itemList = new(12); private readonly List<(EquipSlot, uint, StainIds)> _itemList = new(12);
// This can be moved into client structs or penumbra.gamedata when needed.
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 ChangeGearsetInternalDelegate(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId);
[Signature(EquipGearsetInternal, DetourName = nameof(EquipGearSetInternalDetour))]
private readonly Hook<ChangeGearsetInternalDelegate> _equipGearsetInternalHook = null!;
public InventoryService(MovedEquipment movedItemsEvent, IGameInteropProvider interop, EquippedGearset gearsetEvent) public InventoryService(MovedEquipment movedItemsEvent, IGameInteropProvider interop, EquippedGearset gearsetEvent)
{ {
_movedItemsEvent = movedItemsEvent; _movedItemsEvent = movedItemsEvent;
_gearsetEvent = gearsetEvent; _gearsetEvent = gearsetEvent;
_moveItemHook = interop.HookFromAddress<MoveItemDelegate>((nint)InventoryManager.MemberFunctionPointers.MoveItemSlot, MoveItemDetour); _moveItemHook = interop.HookFromAddress<MoveItemDelegate>((nint)InventoryManager.MemberFunctionPointers.MoveItemSlot, MoveItemDetour);
_equipGearsetHook = _equipGearsetHook = interop.HookFromAddress<EquipGearsetDelegate>((nint)RaptureGearsetModule.MemberFunctionPointers.EquipGearset, EquipGearSetDetour);
interop.HookFromAddress<EquipGearsetDelegate>((nint)RaptureGearsetModule.MemberFunctionPointers.EquipGearset, EquipGearSetDetour); interop.InitializeFromAttributes(this);
_moveItemHook.Enable(); _moveItemHook.Enable();
_equipGearsetHook.Enable(); _equipGearsetHook.Enable();
_equipGearsetInternalHook.Enable();
} }
public void Dispose() public void Dispose()
{ {
_moveItemHook.Dispose(); _moveItemHook.Dispose();
_equipGearsetHook.Dispose(); _equipGearsetHook.Dispose();
_equipGearsetInternalHook.Dispose();
} }
private delegate int EquipGearsetDelegate(RaptureGearsetModule* module, int gearsetId, byte glamourPlateId); private delegate int EquipGearsetDelegate(RaptureGearsetModule* module, int gearsetId, byte glamourPlateId);
private readonly Hook<EquipGearsetDelegate> _equipGearsetHook; private readonly Hook<EquipGearsetDelegate> _equipGearsetHook;
private int EquipGearSetDetour(RaptureGearsetModule* module, int gearsetId, byte glamourPlateId) private nint EquipGearSetInternalDetour(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId)
{ {
var prior = module->CurrentGearsetIndex; var prior = module->CurrentGearsetIndex;
var ret = _equipGearsetHook.Original(module, gearsetId, glamourPlateId); var ret = _equipGearsetInternalHook.Original(module, gearsetId, glamourPlateId);
var set = module->GetGearset(gearsetId); var set = module->GetGearset((int)gearsetId);
_gearsetEvent.Invoke(new ByteString(set->Name).ToString(), gearsetId, prior, glamourPlateId, set->ClassJob); _gearsetEvent.Invoke(new ByteString(set->Name).ToString(), (int)gearsetId, prior, glamourPlateId, set->ClassJob);
Glamourer.Log.Excessive($"[InventoryService] Applied gear set {gearsetId} with glamour plate {glamourPlateId} (Returned {ret})"); Glamourer.Log.Warning($"[InventoryService] [EquipInternal] Applied gear set {gearsetId} with glamour plate {glamourPlateId} (Returned {ret})");
if (ret == 0) if (ret == 0)
{ {
var entry = module->GetGearset(gearsetId); var entry = module->GetGearset((int)gearsetId);
if (entry == null) if (entry == null)
return ret; return ret;
@ -118,6 +129,13 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
return ret; return ret;
} }
private int EquipGearSetDetour(RaptureGearsetModule* module, int gearsetId, byte glamourPlateId)
{
var ret = _equipGearsetHook.Original(module, gearsetId, glamourPlateId);
Glamourer.Log.Verbose($"[InventoryService] Applied gear set {gearsetId} with glamour plate {glamourPlateId} (Returned {ret})");
return ret;
}
private static uint FixId(uint itemId) private static uint FixId(uint itemId)
=> itemId % 50000; => itemId % 50000;
@ -130,7 +148,7 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
InventoryType targetContainer, ushort targetSlot, byte unk) InventoryType targetContainer, ushort targetSlot, byte unk)
{ {
var ret = _moveItemHook.Original(manager, sourceContainer, sourceSlot, targetContainer, targetSlot, unk); var ret = _moveItemHook.Original(manager, sourceContainer, sourceSlot, targetContainer, targetSlot, unk);
Glamourer.Log.Excessive($"[InventoryService] Moved {sourceContainer} {sourceSlot} {targetContainer} {targetSlot} (Returned {ret})"); Glamourer.Log.Verbose($"[InventoryService] Moved {sourceContainer} {sourceSlot} {targetContainer} {targetSlot} (Returned {ret})");
if (ret == 0) if (ret == 0)
{ {
if (InvokeSource(sourceContainer, sourceSlot, out var source)) if (InvokeSource(sourceContainer, sourceSlot, out var source))

View file

@ -50,7 +50,7 @@ public class JobService : IDisposable
var newJob = Jobs.TryGetValue(newJobIndex, out var j) ? j : Jobs[0]; var newJob = Jobs.TryGetValue(newJobIndex, out var j) ? j : Jobs[0];
var oldJob = Jobs.TryGetValue(oldJobIndex, out var o) ? o : Jobs[0]; var oldJob = Jobs.TryGetValue(oldJobIndex, out var o) ? o : Jobs[0];
Glamourer.Log.Excessive($"{actor} changed job from {oldJob} to {newJob}."); Glamourer.Log.Error($"{actor} changed job from {oldJob} to {newJob}.");
JobChanged?.Invoke(actor, oldJob, newJob); JobChanged?.Invoke(actor, oldJob, newJob);
} }
} }

View file

@ -1,21 +1,107 @@
using Dalamud.Hooking; using Dalamud.Hooking;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures; using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using Glamourer.Events; using Glamourer.Events;
using Penumbra.GameData; using Penumbra.GameData;
using Penumbra.GameData.DataContainers; using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
namespace Glamourer.Interop; namespace Glamourer.Interop;
/// <summary>
/// This struct is the struct that loadallequipment passes in as its gearsetData container.
/// </summary>
[StructLayout(LayoutKind.Explicit)] // Size of 70 bytes maybe?
public readonly struct GearsetItemDataStruct
{
// 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 128 (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;
}
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;
private readonly DictBonusItems _bonusItems; private readonly DictBonusItems _bonusItems;
#region LoadAllEquipData
///////////////////////////////////////////////////
// This is a currently undocumented signature that loads all equipment after changing a gearset.
// :: Signature Maintainers Note:
// To obtain this signature, get the stacktrace from FlagSlotForUpdate for human, and find func `sub_140842F50`.
// This function is what calls the weapon/equipment/crest loads, which call FlagSlotForUpdate if different.
//
// By detouring this function, and executing the original, then logic after, we have a consistant point in time where we know all
// slots have been flagged, meaning a consistant point in time that glamourer has processed all of its updates.
public const string LoadAllEquipmentSig = "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 LoadAllEquipmentDelegate(DrawDataContainer* drawDataContainer, GearsetItemDataStruct* gearsetData);
private Int64 LoadAllEquipmentDetour(DrawDataContainer* drawDataContainer, GearsetItemDataStruct* gearsetData)
{
// return original first so we can log the changes after
var ret = _loadAllEquipmentHook.Original(drawDataContainer, gearsetData);
// perform logic stuff.
var owner = drawDataContainer->OwnerObject;
Glamourer.Log.Warning($"[LoadAllEquipmentDetour] Owner: 0x{(nint)owner->DrawObject:X} Finished Applying its GameState!");
Glamourer.Log.Warning($"[LoadAllEquipmentDetour] GearsetItemData: {FormatGearsetItemDataStruct(*gearsetData)}");
// return original.
return ret;
}
private string FormatWeaponModelId(WeaponModelId weaponModelId) => $"Id: {weaponModelId.Id}, Type: {weaponModelId.Type}, Variant: {weaponModelId.Variant}, Stain0: {weaponModelId.Stain0}, Stain1: {weaponModelId.Stain1}";
private string FormatGearsetItemDataStruct(GearsetItemDataStruct gearsetItemData)
{
string ret = $"\nMainhandWeaponData: {FormatWeaponModelId(gearsetItemData.MainhandWeaponData)}," +
$"\nOffhandWeaponData: {FormatWeaponModelId(gearsetItemData.OffhandWeaponData)}," +
$"\nCrestBitField: {gearsetItemData.CrestBitField} | JobId: {gearsetItemData.JobId} | UNK_18: {gearsetItemData.UNK_18} | UNK_19: {gearsetItemData.UNK_19}";
// Iterate through offsets from 20 to 60 and format the CharacterArmor data
for (int offset = 20; offset <= 56; offset += sizeof(LegacyCharacterArmor))
{
LegacyCharacterArmor* equipSlotPtr = (LegacyCharacterArmor*)((byte*)&gearsetItemData + offset);
int dyeOffset = (offset - 20) / sizeof(LegacyCharacterArmor) + 60; // Calculate the corresponding dye offset
byte* dyePtr = (byte*)&gearsetItemData + dyeOffset;
ret += $"\nEquipSlot {((EquipSlot)(dyeOffset-60)).ToString()}:: Id: {(*equipSlotPtr).Set}, Variant: {(*equipSlotPtr).Variant}, Stain0: {(*equipSlotPtr).Stain.Id}, Stain1: {*dyePtr}";
}
return ret;
}
#endregion LoadAllEquipData
public UpdateSlotService(EquipSlotUpdating equipSlotUpdating, BonusSlotUpdating bonusSlotUpdating, IGameInteropProvider interop, public UpdateSlotService(EquipSlotUpdating equipSlotUpdating, BonusSlotUpdating bonusSlotUpdating, IGameInteropProvider interop,
DictBonusItems bonusItems) DictBonusItems bonusItems)
{ {
@ -25,12 +111,14 @@ public unsafe class UpdateSlotService : IDisposable
interop.InitializeFromAttributes(this); interop.InitializeFromAttributes(this);
_flagSlotForUpdateHook.Enable(); _flagSlotForUpdateHook.Enable();
_flagBonusSlotForUpdateHook.Enable(); _flagBonusSlotForUpdateHook.Enable();
_loadAllEquipmentHook.Enable();
} }
public void Dispose() public void Dispose()
{ {
_flagSlotForUpdateHook.Dispose(); _flagSlotForUpdateHook.Dispose();
_flagBonusSlotForUpdateHook.Dispose(); _flagBonusSlotForUpdateHook.Dispose();
_loadAllEquipmentHook.Dispose();
} }
public void UpdateEquipSlot(Model drawObject, EquipSlot slot, CharacterArmor data) public void UpdateEquipSlot(Model drawObject, EquipSlot slot, CharacterArmor data)
@ -79,24 +167,36 @@ 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!;
[Signature(LoadAllEquipmentSig, DetourName = nameof(LoadAllEquipmentDetour))]
private readonly Hook<LoadAllEquipmentDelegate> _loadAllEquipmentHook = 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.Information($"[FlagSlotForUpdate] Called with 0x{drawObject:X} for slot {slot} with {*data} ({returnValue:X}).");
return returnValue == ulong.MaxValue ? _flagSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue; returnValue = returnValue == ulong.MaxValue ? _flagSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue;
return returnValue;
} }
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.Information($"[FlagBonusSlotForUpdate] Called with 0x{drawObject:X} for slot {slot} with {*data} ({returnValue:X}).");
return returnValue == ulong.MaxValue ? _flagBonusSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue; returnValue = returnValue == ulong.MaxValue ? _flagBonusSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue;
return returnValue;
} }
private ulong FlagSlotForUpdateInterop(Model drawObject, EquipSlot slot, CharacterArmor armor) private ulong FlagSlotForUpdateInterop(Model drawObject, EquipSlot slot, CharacterArmor armor)
=> _flagSlotForUpdateHook.Original(drawObject.Address, slot.ToIndex(), &armor); {
Glamourer.Log.Warning($"Glamour-Invoked Equip Slot update for 0x{drawObject.Address:X} with {slot} and {armor}.");
return _flagSlotForUpdateHook.Original(drawObject.Address, slot.ToIndex(), &armor);
}
} }

View file

@ -51,7 +51,7 @@ public class StateEditor(
return; return;
var actors = Applier.ChangeCustomize(state, settings.Source.RequiresChange()); var actors = Applier.ChangeCustomize(state, settings.Source.RequiresChange());
Glamourer.Log.Verbose( Glamourer.Log.Information(
$"Set {idx.ToDefaultName()} customizations in state {state.Identifier.Incognito(null)} from {old.Value} to {value.Value}. [Affecting {actors.ToLazyString("nothing")}.]"); $"Set {idx.ToDefaultName()} customizations in state {state.Identifier.Incognito(null)} from {old.Value} to {value.Value}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.Customize, settings.Source, state, actors, new CustomizeTransaction(idx, old, value)); StateChanged.Invoke(StateChangeType.Customize, settings.Source, state, actors, new CustomizeTransaction(idx, old, value));
} }
@ -64,7 +64,7 @@ public class StateEditor(
return; return;
var actors = Applier.ChangeCustomize(state, settings.Source.RequiresChange()); var actors = Applier.ChangeCustomize(state, settings.Source.RequiresChange());
Glamourer.Log.Verbose( Glamourer.Log.Information(
$"Set {applied} customizations in state {state.Identifier.Incognito(null)} from {old} to {customizeInput}. [Affecting {actors.ToLazyString("nothing")}.]"); $"Set {applied} customizations in state {state.Identifier.Incognito(null)} from {old} to {customizeInput}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.EntireCustomize, settings.Source, state, actors, StateChanged.Invoke(StateChangeType.EntireCustomize, settings.Source, state, actors,
new EntireCustomizeTransaction(applied, old, customizeInput)); new EntireCustomizeTransaction(applied, old, customizeInput));
@ -75,7 +75,10 @@ public class StateEditor(
{ {
var state = (ActorState)data; var state = (ActorState)data;
if (!Editor.ChangeItem(state, slot, item, settings.Source, out var old, settings.Key)) if (!Editor.ChangeItem(state, slot, item, settings.Source, out var old, settings.Key))
{
Glamourer.Log.Information("Not Setting State or invoking, Editor requested us not to change it!");
return; return;
}
var type = slot.ToIndex() < 10 ? StateChangeType.Equip : StateChangeType.Weapon; var type = slot.ToIndex() < 10 ? StateChangeType.Equip : StateChangeType.Weapon;
var actors = type is StateChangeType.Equip var actors = type is StateChangeType.Equip
@ -86,8 +89,8 @@ public class StateEditor(
if (slot is EquipSlot.MainHand) if (slot is EquipSlot.MainHand)
ApplyMainhandPeriphery(state, item, null, settings); ApplyMainhandPeriphery(state, item, null, settings);
Glamourer.Log.Verbose( Glamourer.Log.Debug(
$"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId}). [Affecting {actors.ToLazyString("nothing")}.]"); $"[ChangeItem] Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId}). [Affecting {actors.ToLazyString("nothing")}.]");
if (type is StateChangeType.Equip) if (type is StateChangeType.Equip)
{ {
@ -116,8 +119,8 @@ public class StateEditor(
return; return;
var actors = Applier.ChangeBonusItem(state, slot, settings.Source.RequiresChange()); var actors = Applier.ChangeBonusItem(state, slot, settings.Source.RequiresChange());
Glamourer.Log.Verbose( Glamourer.Log.Debug(
$"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.Id}) to {item.Name} ({item.Id}). [Affecting {actors.ToLazyString("nothing")}.]"); $"[ChangeBonus] Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.Id}) to {item.Name} ({item.Id}). [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.BonusItem, settings.Source, state, actors, new BonusItemTransaction(slot, old, item)); StateChanged.Invoke(StateChangeType.BonusItem, settings.Source, state, actors, new BonusItemTransaction(slot, old, item));
} }
@ -149,8 +152,8 @@ public class StateEditor(
if (slot is EquipSlot.MainHand) if (slot is EquipSlot.MainHand)
ApplyMainhandPeriphery(state, item, stains, settings); ApplyMainhandPeriphery(state, item, stains, settings);
Glamourer.Log.Verbose( Glamourer.Log.Debug(
$"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item!.Value.Name} ({item.Value.ItemId}) and its stain from {oldStains} to {stains!.Value}. [Affecting {actors.ToLazyString("nothing")}.]"); $"[ChangeEquip] Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item!.Value.Name} ({item.Value.ItemId}) and its stain from {oldStains} to {stains!.Value}. [Affecting {actors.ToLazyString("nothing")}.]");
if (type is StateChangeType.Equip) if (type is StateChangeType.Equip)
{ {
StateChanged.Invoke(type, settings.Source, state, actors, new EquipTransaction(slot, old, item!.Value)); StateChanged.Invoke(type, settings.Source, state, actors, new EquipTransaction(slot, old, item!.Value));
@ -181,7 +184,7 @@ public class StateEditor(
return; return;
var actors = Applier.ChangeStain(state, slot, settings.Source.RequiresChange()); var actors = Applier.ChangeStain(state, slot, settings.Source.RequiresChange());
Glamourer.Log.Verbose( Glamourer.Log.Debug(
$"Set {slot.ToName()} stain in state {state.Identifier.Incognito(null)} from {old} to {stains}. [Affecting {actors.ToLazyString("nothing")}.]"); $"Set {slot.ToName()} stain in state {state.Identifier.Incognito(null)} from {old} to {stains}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.Stains, settings.Source, state, actors, new StainTransaction(slot, old, stains)); StateChanged.Invoke(StateChangeType.Stains, settings.Source, state, actors, new StainTransaction(slot, old, stains));
} }
@ -250,7 +253,7 @@ public class StateEditor(
return; return;
var actors = Applier.ChangeMetaState(state, index, settings.Source.RequiresChange()); var actors = Applier.ChangeMetaState(state, index, settings.Source.RequiresChange());
Glamourer.Log.Verbose( Glamourer.Log.Debug(
$"Set {index.ToName()} in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]"); $"Set {index.ToName()} in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.Other, settings.Source, state, actors, new MetaTransaction(index, old, value)); StateChanged.Invoke(StateChangeType.Other, settings.Source, state, actors, new MetaTransaction(index, old, value));
} }
@ -414,8 +417,7 @@ public class StateEditor(
? Applier.ApplyAll(state, requiresRedraw, false) ? Applier.ApplyAll(state, requiresRedraw, false)
: ActorData.Invalid; : ActorData.Invalid;
Glamourer.Log.Verbose( Glamourer.Log.Debug($"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
return; return;

View file

@ -216,8 +216,7 @@ 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) if (actor.Identifier(_actors, out var identifier) && _manager.TryGetValue(identifier, out var state))
&& _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;
@ -383,7 +382,7 @@ public class StateListener : IDisposable
lastFistOffhand = new CharacterWeapon((PrimaryId)(weapon.Skeleton.Id + 50), weapon.Weapon, weapon.Variant, lastFistOffhand = new CharacterWeapon((PrimaryId)(weapon.Skeleton.Id + 50), weapon.Weapon, weapon.Variant,
weapon.Stains); weapon.Stains);
_fistOffhands[actor] = lastFistOffhand; _fistOffhands[actor] = lastFistOffhand;
Glamourer.Log.Excessive($"Storing fist weapon offhand {lastFistOffhand} for 0x{actor.Address:X}."); Glamourer.Log.Verbose($"Storing fist weapon offhand {lastFistOffhand} for 0x{actor.Address:X}.");
} }
_funModule.ApplyFunToWeapon(actor, ref weapon, slot); _funModule.ApplyFunToWeapon(actor, ref weapon, slot);

View file

@ -273,7 +273,7 @@ public sealed class StateManager(
if (source is not StateSource.Game) if (source is not StateSource.Game)
actors = Applier.ApplyAll(state, redraw, true); actors = Applier.ApplyAll(state, redraw, true);
Glamourer.Log.Verbose( Glamourer.Log.Debug(
$"Reset entire state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]"); $"Reset entire state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.Reset, source, state, actors, null); StateChanged.Invoke(StateChangeType.Reset, source, state, actors, null);
} }
@ -298,7 +298,7 @@ public sealed class StateManager(
state.Materials.Clear(); state.Materials.Clear();
Glamourer.Log.Verbose( Glamourer.Log.Debug(
$"Reset advanced customization and dye state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]"); $"Reset advanced customization and dye state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.Reset, source, state, actors, null); StateChanged.Invoke(StateChangeType.Reset, source, state, actors, null);
} }