mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 10:17:23 +01:00
Progress made. May be successful!
This commit is contained in:
parent
8160f420db
commit
c605d19510
9 changed files with 201 additions and 63 deletions
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue