Rework some stuff, add debug tab.

This commit is contained in:
Ottermandias 2023-12-01 21:44:23 +01:00
parent 668d4c033f
commit 358e33346f
6 changed files with 129 additions and 53 deletions

View file

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Penumbra.GameData.Enums;
namespace Glamourer.Structs;
@ -23,17 +22,22 @@ public enum CrestFlag : ushort
OffHand = 0x0800,
}
public enum CrestType : byte
{
None,
Human,
Mainhand,
Offhand,
};
public static class CrestExtensions
{
public const CrestFlag All = (CrestFlag)(((ulong)EquipFlag.Offhand << 1) - 1);
public const CrestFlag AllRelevant = CrestFlag.Head | CrestFlag.Body | CrestFlag.OffHand;
public static readonly IReadOnlyList<CrestFlag> AllRelevantSet = Enum.GetValues<CrestFlag>().Where(f => f.ToRelevantIndex() >= 0).ToArray();
public static readonly IReadOnlyList<CrestFlag> AllRelevantSet = Enum.GetValues<CrestFlag>().Where(f => AllRelevant.HasFlag(f)).ToArray();
public static int ToIndex(this CrestFlag flag)
=> BitOperations.TrailingZeroCount((uint)flag);
public static int ToRelevantIndex(this CrestFlag flag)
public static int ToInternalIndex(this CrestFlag flag)
=> flag switch
{
CrestFlag.Head => 0,
@ -42,6 +46,24 @@ public static class CrestExtensions
_ => -1,
};
public static (CrestType Type, byte Index) ToIndex(this CrestFlag flag)
=> flag switch
{
CrestFlag.Head => (CrestType.Human, 0),
CrestFlag.Body => (CrestType.Human, 1),
CrestFlag.Hands => (CrestType.Human, 2),
CrestFlag.Legs => (CrestType.Human, 3),
CrestFlag.Feet => (CrestType.Human, 4),
CrestFlag.Ears => (CrestType.None, 0),
CrestFlag.Neck => (CrestType.None, 0),
CrestFlag.Wrists => (CrestType.None, 0),
CrestFlag.RFinger => (CrestType.None, 0),
CrestFlag.LFinger => (CrestType.None, 0),
CrestFlag.MainHand => (CrestType.None, 0),
CrestFlag.OffHand => (CrestType.Offhand, 0),
_ => (CrestType.None, 0),
};
public static CrestFlag ToCrestFlag(this EquipSlot slot)
=> slot switch
{

View file

@ -21,6 +21,7 @@ using Glamourer.Interop.Penumbra;
using Glamourer.Interop.Structs;
using Glamourer.Services;
using Glamourer.State;
using Glamourer.Structs;
using Glamourer.Unlocks;
using Glamourer.Utility;
using ImGuiNET;
@ -43,6 +44,7 @@ public unsafe class DebugTab : ITab
private readonly VisorService _visorService;
private readonly ChangeCustomizeService _changeCustomizeService;
private readonly UpdateSlotService _updateSlotService;
private readonly CrestService _crestService;
private readonly WeaponService _weaponService;
private readonly MetaService _metaService;
private readonly InventoryService _inventoryService;
@ -50,7 +52,7 @@ public unsafe class DebugTab : ITab
private readonly ObjectManager _objectManager;
private readonly GlamourerIpc _ipc;
private readonly CodeService _code;
private readonly ImportService _importService;
private readonly ImportService _importService;
private readonly ItemManager _items;
private readonly ActorService _actors;
@ -82,7 +84,7 @@ public unsafe class DebugTab : ITab
PenumbraChangedItemTooltip penumbraTooltip, MetaService metaService, GlamourerIpc ipc, DalamudPluginInterface pluginInterface,
AutoDesignManager autoDesignManager, JobService jobs, CodeService code, CustomizeUnlockManager customizeUnlocks,
ItemUnlockManager itemUnlocks, DesignConverter designConverter, ImportService importService, InventoryService inventoryService,
HumanModelList humans, FunModule funModule)
HumanModelList humans, FunModule funModule, CrestService crestService)
{
_changeCustomizeService = changeCustomizeService;
_visorService = visorService;
@ -107,10 +109,11 @@ public unsafe class DebugTab : ITab
_customizeUnlocks = customizeUnlocks;
_itemUnlocks = itemUnlocks;
_designConverter = designConverter;
_importService = importService;
_importService = importService;
_inventoryService = inventoryService;
_humans = humans;
_funModule = funModule;
_crestService = crestService;
}
public ReadOnlySpan<byte> Label
@ -200,6 +203,7 @@ public unsafe class DebugTab : ITab
DrawWetness(actor, model);
DrawEquip(actor, model);
DrawCustomize(actor, model);
DrawCrests(actor, model);
}
private string _objectFilter = string.Empty;
@ -477,6 +481,25 @@ public unsafe class DebugTab : ITab
}
}
private void DrawCrests(Actor actor, Model model)
{
using var id = ImRaii.PushId("Crests");
foreach (var crestFlag in CrestExtensions.AllRelevantSet)
{
id.Push((int)crestFlag);
var modelCrest = CrestService.GetModelCrest(actor, crestFlag);
ImGuiUtil.DrawTableColumn($"{crestFlag.ToLabel()} Crest");
ImGuiUtil.DrawTableColumn(actor.IsCharacter ? actor.GetCrest(crestFlag).ToString() : "No Character");
ImGuiUtil.DrawTableColumn(modelCrest.ToString());
ImGui.TableNextColumn();
if (model.IsHuman && ImGui.SmallButton("Toggle"))
_crestService.UpdateCrest(actor, crestFlag, !modelCrest);
id.Pop();
}
}
#endregion
#region Penumbra

View file

@ -2,7 +2,10 @@
using Dalamud.Hooking;
using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Glamourer.Interop.Structs;
using Glamourer.Structs;
using OtterGui.Classes;
using Penumbra.GameData.Enums;
@ -27,6 +30,7 @@ public sealed unsafe class CrestService : EventWrapper<Action<Model, EquipSlot,
public CrestService(IGameInteropProvider interop)
: base(nameof(CrestService))
{
interop.InitializeFromAttributes(this);
_humanSetFreeCompanyCrestVisibleOnSlot =
interop.HookFromAddress<SetCrestDelegateIntern>(_humanVTable[96], HumanSetFreeCompanyCrestVisibleOnSlotDetour);
_weaponSetFreeCompanyCrestVisibleOnSlot =
@ -48,10 +52,68 @@ public sealed unsafe class CrestService : EventWrapper<Action<Model, EquipSlot,
visible = ret;
}
public void UpdateCrest(Model drawObject, EquipSlot slot, bool crest)
public static bool GetModelCrest(Actor gameObject, CrestFlag slot)
{
using var _ = _inUpdate.EnterMethod();
drawObject.SetFreeCompanyCrestVisibleOnSlot(slot, crest);
if (!gameObject.IsCharacter)
return false;
var (type, index) = slot.ToIndex();
switch (type)
{
case CrestType.Human:
{
var model = gameObject.Model;
if (!model.IsHuman)
return false;
var getter = (delegate* unmanaged<Human*, byte, byte>)((nint*)model.AsCharacterBase->VTable)[95];
return getter(model.AsHuman, index) != 0;
}
case CrestType.Offhand:
{
var model = (Model)gameObject.AsCharacter->DrawData.Weapon(DrawDataContainer.WeaponSlot.OffHand).DrawObject;
if (!model.IsWeapon)
return false;
var getter = (delegate* unmanaged<Weapon*, byte, byte>)((nint*)model.AsCharacterBase->VTable)[95];
return getter(model.AsWeapon, index) != 0;
}
}
return false;
}
public void UpdateCrest(Actor gameObject, CrestFlag slot, bool crest)
{
if (!gameObject.IsCharacter)
return;
var (type, index) = slot.ToIndex();
switch (type)
{
case CrestType.Human:
{
var model = gameObject.Model;
if (!model.IsHuman)
return;
using var _ = _inUpdate.EnterMethod();
var setter = (delegate* unmanaged<Human*, byte, byte, void>)((nint*)model.AsCharacterBase->VTable)[96];
setter(model.AsHuman, index, crest ? (byte)1 : (byte)0);
break;
}
case CrestType.Offhand:
{
var model = (Model)gameObject.AsCharacter->DrawData.Weapon(DrawDataContainer.WeaponSlot.OffHand).DrawObject;
if (!model.IsWeapon)
return;
using var _ = _inUpdate.EnterMethod();
var setter = (delegate* unmanaged<Weapon*, byte, byte, void>)((nint*)model.AsCharacterBase->VTable)[96];
setter(model.AsWeapon, index, crest ? (byte)1 : (byte)0);
break;
}
}
}
private readonly InMethodChecker _inUpdate = new();

View file

@ -3,6 +3,7 @@ using System;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Glamourer.Customization;
using Glamourer.Structs;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.String;
@ -106,7 +107,7 @@ public readonly unsafe struct Actor : IEquatable<Actor>
public CharacterArmor GetArmor(EquipSlot slot)
=> ((CharacterArmor*)&AsCharacter->DrawData.Head)[slot.ToIndex()];
public bool GetCrest(EquipSlot slot)
public bool GetCrest(CrestFlag slot)
=> (GetFreeCompanyCrestBitfield() & CrestMask(slot)) != 0;
public CharacterWeapon GetMainhand()
@ -122,15 +123,15 @@ public readonly unsafe struct Actor : IEquatable<Actor>
private byte GetFreeCompanyCrestBitfield()
=> ((byte*)Address)[0x1BBB];
private static byte CrestMask(EquipSlot slot)
private static byte CrestMask(CrestFlag slot)
=> slot switch
{
EquipSlot.OffHand => 0x01,
EquipSlot.Head => 0x02,
EquipSlot.Body => 0x04,
EquipSlot.Hands => 0x08,
EquipSlot.Legs => 0x10,
EquipSlot.Feet => 0x20,
CrestFlag.OffHand => 0x01,
CrestFlag.Head => 0x02,
CrestFlag.Body => 0x04,
CrestFlag.Hands => 0x08,
CrestFlag.Legs => 0x10,
CrestFlag.Feet => 0x20,
_ => 0x00,
};

View file

@ -91,9 +91,6 @@ public readonly unsafe struct Model : IEquatable<Model>
public CharacterArmor GetArmor(EquipSlot slot)
=> ((CharacterArmor*)&AsHuman->Head)[slot.ToIndex()];
public bool GetCrest(EquipSlot slot)
=> IsFreeCompanyCrestVisibleOnSlot(slot);
public Customize GetCustomize()
=> *(Customize*)&AsHuman->Customize;
@ -198,35 +195,6 @@ public readonly unsafe struct Model : IEquatable<Model>
return discriminator1 == 0 && discriminator2 != 0 ? (second, first) : (first, second);
}
// TODO remove these when available in ClientStructs
private bool IsFreeCompanyCrestVisibleOnSlot(EquipSlot slot)
{
if (!IsCharacterBase)
return false;
var index = (byte)slot.ToIndex();
if (index >= 12)
return false;
var characterBase = AsCharacterBase;
var getter = (delegate* unmanaged<CharacterBase*, byte, byte>)((nint*)characterBase->VTable)[95];
return getter(characterBase, index) != 0;
}
public void SetFreeCompanyCrestVisibleOnSlot(EquipSlot slot, bool visible)
{
if (!IsCharacterBase)
return;
var index = (byte)slot.ToIndex();
if (index >= 12)
return;
var characterBase = AsCharacterBase;
var setter = (delegate* unmanaged<CharacterBase*, byte, byte, void>)((nint*)characterBase->VTable)[96];
setter(characterBase, index, visible ? (byte)1 : (byte)0);
}
public override string ToString()
=> $"0x{Address:X}";
}

View file

@ -90,7 +90,7 @@ public class ActorState
=> ref _sources[slot.ToIndex() + (stain ? EquipFlagExtensions.NumEquipFlags / 2 : 0)];
public ref StateChanged.Source this[CrestFlag slot]
=> ref _sources[EquipFlagExtensions.NumEquipFlags + CustomizationExtensions.NumIndices + 5 + slot.ToRelevantIndex()];
=> ref _sources[EquipFlagExtensions.NumEquipFlags + CustomizationExtensions.NumIndices + 5 + slot.ToInternalIndex()];
public ref StateChanged.Source this[CustomizeIndex type]
=> ref _sources[EquipFlagExtensions.NumEquipFlags + (int)type];