mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Current state.
This commit is contained in:
parent
c54141be54
commit
e3a1ae6938
20 changed files with 104 additions and 43 deletions
2
OtterGui
2
OtterGui
|
|
@ -1 +1 @@
|
||||||
Subproject commit 3e6b085749741f35dd6732c33d0720c6a51ebb97
|
Subproject commit 8ba88eff15326bb28ed5e6157f5252c114d40b5f
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit e39a04c83b67246580492677414888357b5ebed8
|
Subproject commit fb81a0b55d3c68f2b26357fac3049c79fb0c22fb
|
||||||
|
|
@ -33,25 +33,27 @@ public sealed unsafe class ApricotListenerSoundPlayCaller : FastHook<ApricotList
|
||||||
private nint Detour(nint a1, nint unused, float timeOffset)
|
private nint Detour(nint a1, nint unused, float timeOffset)
|
||||||
{
|
{
|
||||||
// Short-circuiting and sanity checks done by game.
|
// Short-circuiting and sanity checks done by game.
|
||||||
var playTime = a1 == nint.Zero ? -1 : *(float*)(a1 + 0x250);
|
var playTime = a1 == nint.Zero ? -1 : *(float*)(a1 + VolatileOffsets.ApricotListenerSoundPlayCaller.PlayTimeOffset);
|
||||||
if (playTime < 0)
|
if (playTime < 0)
|
||||||
return Task.Result.Original(a1, unused, timeOffset);
|
return Task.Result.Original(a1, unused, timeOffset);
|
||||||
|
|
||||||
var someIntermediate = *(nint*)(a1 + 0x1F8);
|
var someIntermediate = *(nint*)(a1 + VolatileOffsets.ApricotListenerSoundPlayCaller.SomeIntermediate);
|
||||||
var flags = someIntermediate == nint.Zero ? (ushort)0 : *(ushort*)(someIntermediate + 0x49C);
|
var flags = someIntermediate == nint.Zero
|
||||||
if (((flags >> 13) & 1) == 0)
|
? (ushort)0
|
||||||
|
: *(ushort*)(someIntermediate + VolatileOffsets.ApricotListenerSoundPlayCaller.Flags);
|
||||||
|
if (((flags >> VolatileOffsets.ApricotListenerSoundPlayCaller.BitShift) & 1) == 0)
|
||||||
return Task.Result.Original(a1, unused, timeOffset);
|
return Task.Result.Original(a1, unused, timeOffset);
|
||||||
|
|
||||||
Penumbra.Log.Excessive(
|
Penumbra.Log.Excessive(
|
||||||
$"[Apricot Listener Sound Play Caller] Invoked on 0x{a1:X} with {unused}, {timeOffset}.");
|
$"[Apricot Listener Sound Play Caller] Invoked on 0x{a1:X} with {unused}, {timeOffset}.");
|
||||||
// Fetch the IInstanceListenner (sixth argument to inlined call of SoundPlay)
|
// Fetch the IInstanceListenner (sixth argument to inlined call of SoundPlay)
|
||||||
var apricotIInstanceListenner = *(nint*)(someIntermediate + 0x270);
|
var apricotIInstanceListenner = *(nint*)(someIntermediate + VolatileOffsets.ApricotListenerSoundPlayCaller.IInstanceListenner);
|
||||||
if (apricotIInstanceListenner == nint.Zero)
|
if (apricotIInstanceListenner == nint.Zero)
|
||||||
return Task.Result.Original(a1, unused, timeOffset);
|
return Task.Result.Original(a1, unused, timeOffset);
|
||||||
|
|
||||||
// In some cases we can obtain the associated caster via vfunc 1.
|
// In some cases we can obtain the associated caster via vfunc 1.
|
||||||
var newData = ResolveData.Invalid;
|
var newData = ResolveData.Invalid;
|
||||||
var gameObject = (*(delegate* unmanaged<nint, GameObject*>**)apricotIInstanceListenner)[1](apricotIInstanceListenner);
|
var gameObject = (*(delegate* unmanaged<nint, GameObject*>**)apricotIInstanceListenner)[VolatileOffsets.ApricotListenerSoundPlayCaller.CasterVFunc](apricotIInstanceListenner);
|
||||||
if (gameObject != null)
|
if (gameObject != null)
|
||||||
{
|
{
|
||||||
newData = _collectionResolver.IdentifyCollection(gameObject, true);
|
newData = _collectionResolver.IdentifyCollection(gameObject, true);
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ public sealed unsafe class SomePapLoad : FastHook<SomePapLoad.Delegate>
|
||||||
private void Detour(nint a1, int a2, nint a3, int a4)
|
private void Detour(nint a1, int a2, nint a3, int a4)
|
||||||
{
|
{
|
||||||
Penumbra.Log.Excessive($"[Some PAP Load] Invoked on 0x{a1:X} with {a2}, {a3}, {a4}.");
|
Penumbra.Log.Excessive($"[Some PAP Load] Invoked on 0x{a1:X} with {a2}, {a3}, {a4}.");
|
||||||
var timelinePtr = a1 + Offsets.TimeLinePtr;
|
var timelinePtr = a1 + VolatileOffsets.AnimationState.TimeLinePtr;
|
||||||
if (timelinePtr != nint.Zero)
|
if (timelinePtr != nint.Zero)
|
||||||
{
|
{
|
||||||
var actorIdx = (int)(*(*(ulong**)timelinePtr + 1) >> 3);
|
var actorIdx = (int)(*(*(ulong**)timelinePtr + 1) >> 3);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
using OtterGui.Services;
|
using OtterGui.Services;
|
||||||
using Penumbra.Interop.PathResolving;
|
using Penumbra.Interop.PathResolving;
|
||||||
using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character;
|
|
||||||
|
|
||||||
namespace Penumbra.Interop.Hooks.Meta;
|
namespace Penumbra.Interop.Hooks.Meta;
|
||||||
|
|
||||||
|
|
@ -14,18 +14,19 @@ public sealed unsafe class CalculateHeight : FastHook<CalculateHeight.Delegate>
|
||||||
{
|
{
|
||||||
_collectionResolver = collectionResolver;
|
_collectionResolver = collectionResolver;
|
||||||
_metaState = metaState;
|
_metaState = metaState;
|
||||||
Task = hooks.CreateHook<Delegate>("Calculate Height", (nint)Character.MemberFunctionPointers.CalculateHeight, Detour, !HookOverrides.Instance.Meta.CalculateHeight);
|
Task = hooks.CreateHook<Delegate>("Calculate Height", (nint)HeightContainer.MemberFunctionPointers.CalculateHeight, Detour,
|
||||||
|
!HookOverrides.Instance.Meta.CalculateHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public delegate ulong Delegate(Character* character);
|
public delegate ulong Delegate(HeightContainer* character);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
private ulong Detour(Character* character)
|
private ulong Detour(HeightContainer* container)
|
||||||
{
|
{
|
||||||
var collection = _collectionResolver.IdentifyCollection((GameObject*)character, true);
|
var collection = _collectionResolver.IdentifyCollection((GameObject*)container->OwnerObject, true);
|
||||||
_metaState.RspCollection.Push(collection);
|
_metaState.RspCollection.Push(collection);
|
||||||
var ret = Task.Result.Original.Invoke(character);
|
var ret = Task.Result.Original.Invoke(container);
|
||||||
Penumbra.Log.Excessive($"[Calculate Height] Invoked on {(nint)character:X} -> {ret}.");
|
Penumbra.Log.Excessive($"[Calculate Height] Invoked on {(nint)container:X} -> {ret}.");
|
||||||
_metaState.RspCollection.Pop();
|
_metaState.RspCollection.Pop();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ public sealed unsafe class UpdateModel : FastHook<UpdateModel.Delegate>
|
||||||
{
|
{
|
||||||
// Shortcut because this is called all the time.
|
// Shortcut because this is called all the time.
|
||||||
// Same thing is checked at the beginning of the original function.
|
// Same thing is checked at the beginning of the original function.
|
||||||
if (*(int*)((nint)drawObject + Offsets.UpdateModelSkip) == 0)
|
if (*(int*)((nint)drawObject + VolatileOffsets.UpdateModel.ShortCircuit) == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Penumbra.Log.Excessive($"[Update Model] Invoked on {(nint)drawObject:X}.");
|
Penumbra.Log.Excessive($"[Update Model] Invoked on {(nint)drawObject:X}.");
|
||||||
|
|
|
||||||
|
|
@ -401,7 +401,6 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
|
||||||
private void ModelRendererUnkFuncDetour(CSModelRenderer* modelRenderer, ModelRendererStructs.UnkPayload* unkPayload, uint unk2, uint unk3,
|
private void ModelRendererUnkFuncDetour(CSModelRenderer* modelRenderer, ModelRendererStructs.UnkPayload* unkPayload, uint unk2, uint unk3,
|
||||||
uint unk4, uint unk5)
|
uint unk4, uint unk5)
|
||||||
{
|
{
|
||||||
// If we don't have any on-screen instances of modded iris.shpk or others, we don't need the slow path at all.
|
|
||||||
if (!Enabled || GetTotalMaterialCountForModelRendererUnk() == 0)
|
if (!Enabled || GetTotalMaterialCountForModelRendererUnk() == 0)
|
||||||
{
|
{
|
||||||
_modelRendererUnkFuncHook.Original(modelRenderer, unkPayload, unk2, unk3, unk4, unk5);
|
_modelRendererUnkFuncHook.Original(modelRenderer, unkPayload, unk2, unk3, unk4, unk5);
|
||||||
|
|
|
||||||
|
|
@ -240,7 +240,7 @@ public sealed unsafe class CollectionResolver(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only handle human models.
|
// Only handle human models.
|
||||||
if (!IsModelHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId))
|
if (!IsModelHuman((uint)actor.AsCharacter->ModelCharaId))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (actor.Customize->Data[0] == 0)
|
if (actor.Customize->Data[0] == 0)
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ public class ResourceTree
|
||||||
Unsafe.AsPointer(ref character->DrawData.EquipmentModelIds[0]), 10),
|
Unsafe.AsPointer(ref character->DrawData.EquipmentModelIds[0]), 10),
|
||||||
_ => [],
|
_ => [],
|
||||||
};
|
};
|
||||||
ModelId = character->CharacterData.ModelCharaId;
|
ModelId = character->ModelCharaId;
|
||||||
CustomizeData = character->DrawData.CustomizeData;
|
CustomizeData = character->DrawData.CustomizeData;
|
||||||
RaceCode = human != null ? (GenderRace)human->RaceSexId : GenderRace.Unknown;
|
RaceCode = human != null ? (GenderRace)human->RaceSexId : GenderRace.Unknown;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ public unsafe class FontReloader : IService
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_atkModule = &atkModule->AtkModule;
|
_atkModule = &atkModule->AtkModule;
|
||||||
_reloadFontsFunc = ((delegate* unmanaged<AtkModule*, bool, bool, void>*)_atkModule->VirtualTable)[Offsets.ReloadFontsVfunc];
|
_reloadFontsFunc = ((delegate* unmanaged<AtkModule*, bool, bool, void>*)_atkModule->VirtualTable)[VolatileOffsets.FontReloader.ReloadFontsVFunc];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,10 @@ public unsafe partial class RedrawService : IService
|
||||||
|
|
||||||
// VFuncs that disable and enable draw, used only for GPose actors.
|
// VFuncs that disable and enable draw, used only for GPose actors.
|
||||||
private static void DisableDraw(IGameObject actor)
|
private static void DisableDraw(IGameObject actor)
|
||||||
=> ((delegate* unmanaged<nint, void >**)actor.Address)[0][Offsets.DisableDrawVfunc](actor.Address);
|
=> ((delegate* unmanaged<nint, void >**)actor.Address)[0][VolatileOffsets.RedrawService.DisableDrawVFunc](actor.Address);
|
||||||
|
|
||||||
private static void EnableDraw(IGameObject actor)
|
private static void EnableDraw(IGameObject actor)
|
||||||
=> ((delegate* unmanaged<nint, void >**)actor.Address)[0][Offsets.EnableDrawVfunc](actor.Address);
|
=> ((delegate* unmanaged<nint, void >**)actor.Address)[0][VolatileOffsets.RedrawService.EnableDrawVFunc](actor.Address);
|
||||||
|
|
||||||
// Check whether we currently are in GPose.
|
// Check whether we currently are in GPose.
|
||||||
// Also clear the name list.
|
// Also clear the name list.
|
||||||
|
|
|
||||||
35
Penumbra/Interop/VolatileOffsets.cs
Normal file
35
Penumbra/Interop/VolatileOffsets.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
namespace Penumbra.Interop;
|
||||||
|
|
||||||
|
public static class VolatileOffsets
|
||||||
|
{
|
||||||
|
public static class ApricotListenerSoundPlayCaller
|
||||||
|
{
|
||||||
|
public const int PlayTimeOffset = 0x254;
|
||||||
|
public const int SomeIntermediate = 0x1F8;
|
||||||
|
public const int Flags = 0x4A4;
|
||||||
|
public const int IInstanceListenner = 0x270;
|
||||||
|
public const int BitShift = 13;
|
||||||
|
public const int CasterVFunc = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AnimationState
|
||||||
|
{
|
||||||
|
public const int TimeLinePtr = 0x50;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UpdateModel
|
||||||
|
{
|
||||||
|
public const int ShortCircuit = 0xA2C;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FontReloader
|
||||||
|
{
|
||||||
|
public const int ReloadFontsVFunc = 43;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RedrawService
|
||||||
|
{
|
||||||
|
public const int EnableDrawVFunc = 12;
|
||||||
|
public const int DisableDrawVFunc = 13;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -138,7 +138,7 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ
|
||||||
|
|
||||||
protected override bool MoveOption(ImcModGroup group, int optionIdxFrom, int optionIdxTo)
|
protected override bool MoveOption(ImcModGroup group, int optionIdxFrom, int optionIdxTo)
|
||||||
{
|
{
|
||||||
if (!group.OptionData.Move(optionIdxFrom, optionIdxTo))
|
if (!group.OptionData.Move(ref optionIdxFrom, ref optionIdxTo))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
group.DefaultSettings = group.DefaultSettings.MoveBit(optionIdxFrom, optionIdxTo);
|
group.DefaultSettings = group.DefaultSettings.MoveBit(optionIdxFrom, optionIdxTo);
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ public class ModGroupEditor(
|
||||||
{
|
{
|
||||||
var mod = group.Mod;
|
var mod = group.Mod;
|
||||||
var idxFrom = group.GetIndex();
|
var idxFrom = group.GetIndex();
|
||||||
if (!mod.Groups.Move(idxFrom, groupIdxTo))
|
if (!mod.Groups.Move(ref idxFrom, ref groupIdxTo))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
saveService.SaveAllOptionGroups(mod, false, config.ReplaceNonAsciiOnImport);
|
saveService.SaveAllOptionGroups(mod, false, config.ReplaceNonAsciiOnImport);
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ public sealed class MultiModGroupEditor(CommunicatorService communicator, SaveSe
|
||||||
|
|
||||||
protected override bool MoveOption(MultiModGroup group, int optionIdxFrom, int optionIdxTo)
|
protected override bool MoveOption(MultiModGroup group, int optionIdxFrom, int optionIdxTo)
|
||||||
{
|
{
|
||||||
if (!group.OptionData.Move(optionIdxFrom, optionIdxTo))
|
if (!group.OptionData.Move(ref optionIdxFrom, ref optionIdxTo))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
group.DefaultSettings = group.DefaultSettings.MoveBit(optionIdxFrom, optionIdxTo);
|
group.DefaultSettings = group.DefaultSettings.MoveBit(optionIdxFrom, optionIdxTo);
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ public sealed class SingleModGroupEditor(CommunicatorService communicator, SaveS
|
||||||
|
|
||||||
protected override bool MoveOption(SingleModGroup group, int optionIdxFrom, int optionIdxTo)
|
protected override bool MoveOption(SingleModGroup group, int optionIdxFrom, int optionIdxTo)
|
||||||
{
|
{
|
||||||
if (!group.OptionData.Move(optionIdxFrom, optionIdxTo))
|
if (!group.OptionData.Move(ref optionIdxFrom, ref optionIdxTo))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
group.DefaultSettings = group.DefaultSettings.MoveSingle(optionIdxFrom, optionIdxTo);
|
group.DefaultSettings = group.DefaultSettings.MoveSingle(optionIdxFrom, optionIdxTo);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
using OtterGui.Log;
|
using OtterGui.Log;
|
||||||
using OtterGui.Services;
|
using OtterGui.Services;
|
||||||
|
|
@ -20,6 +19,7 @@ using OtterGui.Tasks;
|
||||||
using Penumbra.UI;
|
using Penumbra.UI;
|
||||||
using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManager;
|
using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManager;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
using Lumina.Excel.Sheets;
|
||||||
using Penumbra.GameData.Data;
|
using Penumbra.GameData.Data;
|
||||||
using Penumbra.Interop.Hooks;
|
using Penumbra.Interop.Hooks;
|
||||||
using Penumbra.Interop.Hooks.ResourceLoading;
|
using Penumbra.Interop.Hooks.ResourceLoading;
|
||||||
|
|
@ -111,7 +111,7 @@ public class Penumbra : IDalamudPlugin
|
||||||
private void SetupApi()
|
private void SetupApi()
|
||||||
{
|
{
|
||||||
_services.GetService<IpcProviders>();
|
_services.GetService<IpcProviders>();
|
||||||
var itemSheet = _services.GetService<IDataManager>().GetExcelSheet<Item>()!;
|
var itemSheet = _services.GetService<IDataManager>().GetExcelSheet<Item>();
|
||||||
_communicatorService.ChangedItemHover.Subscribe(it =>
|
_communicatorService.ChangedItemHover.Subscribe(it =>
|
||||||
{
|
{
|
||||||
if (it is IdentifiedItem { Item.Id.IsItem: true })
|
if (it is IdentifiedItem { Item.Id.IsItem: true })
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.ImGuiNotification;
|
using Dalamud.Interface.ImGuiNotification;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.Sheets;
|
||||||
using OtterGui.Log;
|
using OtterGui.Log;
|
||||||
using OtterGui.Services;
|
using OtterGui.Services;
|
||||||
using Penumbra.Mods.Manager;
|
using Penumbra.Mods.Manager;
|
||||||
|
|
@ -16,7 +16,7 @@ namespace Penumbra.Services;
|
||||||
public class MessageService(Logger log, IUiBuilder builder, IChatGui chat, INotificationManager notificationManager)
|
public class MessageService(Logger log, IUiBuilder builder, IChatGui chat, INotificationManager notificationManager)
|
||||||
: OtterGui.Classes.MessageService(log, builder, chat, notificationManager), IService
|
: OtterGui.Classes.MessageService(log, builder, chat, notificationManager), IService
|
||||||
{
|
{
|
||||||
public void LinkItem(Item item)
|
public void LinkItem(in Item item)
|
||||||
{
|
{
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
var payloadList = new List<Payload>
|
var payloadList = new List<Payload>
|
||||||
|
|
@ -29,7 +29,7 @@ public class MessageService(Logger log, IUiBuilder builder, IChatGui chat, INoti
|
||||||
new TextPayload($"{(char)SeIconChar.LinkMarker}"),
|
new TextPayload($"{(char)SeIconChar.LinkMarker}"),
|
||||||
new UIForegroundPayload(0),
|
new UIForegroundPayload(0),
|
||||||
new UIGlowPayload(0),
|
new UIGlowPayload(0),
|
||||||
new TextPayload(item.Name),
|
new TextPayload(item.Name.ExtractText()),
|
||||||
new RawPayload([0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03]),
|
new RawPayload([0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03]),
|
||||||
new RawPayload([0x02, 0x13, 0x02, 0xEC, 0x03]),
|
new RawPayload([0x02, 0x13, 0x02, 0xEC, 0x03]),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,9 @@ public class Diagnostics(ServiceManager provider) : IUiService
|
||||||
return;
|
return;
|
||||||
|
|
||||||
using var table = ImRaii.Table("##data", 4, ImGuiTableFlags.RowBg);
|
using var table = ImRaii.Table("##data", 4, ImGuiTableFlags.RowBg);
|
||||||
|
if (!table)
|
||||||
|
return;
|
||||||
|
|
||||||
foreach (var type in typeof(ActorManager).Assembly.GetTypes()
|
foreach (var type in typeof(ActorManager).Assembly.GetTypes()
|
||||||
.Where(t => t is { IsAbstract: false, IsInterface: false } && t.IsAssignableTo(typeof(IAsyncDataContainer))))
|
.Where(t => t is { IsAbstract: false, IsInterface: false } && t.IsAssignableTo(typeof(IAsyncDataContainer))))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -34,30 +34,51 @@ public class HookOverrideDrawer(IDalamudPluginInterface pluginInterface) : IUiSe
|
||||||
Penumbra.Log.Error($"Could not delete hook override file at {path}:\n{ex}");
|
Penumbra.Log.Error($"Could not delete hook override file at {path}:\n{ex}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool? allVisible = null;
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImUtf8.Button("Disable All Visible Hooks"u8))
|
||||||
|
allVisible = true;
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImUtf8.Button("Enable All VisibleHooks"u8))
|
||||||
|
allVisible = false;
|
||||||
|
|
||||||
bool? all = null;
|
bool? all = null;
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (ImUtf8.Button("Disable All Hooks"u8))
|
if (ImUtf8.Button("Disable All Hooks"))
|
||||||
all = true;
|
all = true;
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (ImUtf8.Button("Enable All Hooks"u8))
|
if (ImUtf8.Button("Enable All Hooks"))
|
||||||
all = false;
|
all = false;
|
||||||
|
|
||||||
foreach (var propertyField in typeof(HookOverrides).GetFields().Where(f => f is { IsStatic: false, FieldType.IsValueType: true }))
|
foreach (var propertyField in typeof(HookOverrides).GetFields().Where(f => f is { IsStatic: false, FieldType.IsValueType: true }))
|
||||||
{
|
{
|
||||||
using var tree = ImUtf8.TreeNode(propertyField.Name);
|
using var tree = ImUtf8.TreeNode(propertyField.Name);
|
||||||
if (!tree)
|
if (!tree)
|
||||||
continue;
|
{
|
||||||
|
if (all.HasValue)
|
||||||
|
{
|
||||||
|
var property = propertyField.GetValue(_overrides);
|
||||||
|
foreach (var valueField in propertyField.FieldType.GetFields())
|
||||||
|
{
|
||||||
|
valueField.SetValue(property, all.Value);
|
||||||
|
propertyField.SetValue(_overrides, property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
allVisible ??= all;
|
||||||
var property = propertyField.GetValue(_overrides);
|
var property = propertyField.GetValue(_overrides);
|
||||||
foreach (var valueField in propertyField.FieldType.GetFields())
|
foreach (var valueField in propertyField.FieldType.GetFields())
|
||||||
{
|
{
|
||||||
var value = valueField.GetValue(property) as bool? ?? false;
|
var value = valueField.GetValue(property) as bool? ?? false;
|
||||||
if (ImUtf8.Checkbox($"Disable {valueField.Name}", ref value) || all.HasValue)
|
if (ImUtf8.Checkbox($"Disable {valueField.Name}", ref value) || allVisible.HasValue)
|
||||||
{
|
{
|
||||||
valueField.SetValue(property, all ?? value);
|
valueField.SetValue(property, allVisible ?? value);
|
||||||
propertyField.SetValue(_overrides, property);
|
propertyField.SetValue(_overrides, property);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue