Extricate bonus slots somewhat.

This commit is contained in:
Ottermandias 2024-07-11 19:45:54 +02:00
parent 7caf6cc08a
commit 7a602d6ec5
8 changed files with 86 additions and 37 deletions

View file

@ -0,0 +1,25 @@
using OtterGui.Classes;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs;
namespace Glamourer.Events;
/// <summary>
/// Triggered when a model flags a bonus slot for an update.
/// <list type="number">
/// <item>Parameter is the model with a flagged slot. </item>
/// <item>Parameter is the bonus slot changed. </item>
/// <item>Parameter is the model values to change the bonus piece to. </item>
/// <item>Parameter is the return value the function should return, if it is ulong.MaxValue, the original will be called and returned. </item>
/// </list>
/// </summary>
public sealed class BonusSlotUpdating()
: EventWrapperRef34<Model, BonusEquipFlag, CharacterArmor, ulong, BonusSlotUpdating.Priority>(nameof(BonusSlotUpdating))
{
public enum Priority
{
/// <seealso cref="State.StateListener.OnBonusSlotUpdating"/>
StateListener = 0,
}
}

View file

@ -14,12 +14,12 @@ namespace Glamourer.Events;
/// <item>Parameter is the return value the function should return, if it is ulong.MaxValue, the original will be called and returned. </item>
/// </list>
/// </summary>
public sealed class SlotUpdating()
: EventWrapperRef34<Model, EquipSlot, CharacterArmor, ulong, SlotUpdating.Priority>(nameof(SlotUpdating))
public sealed class EquipSlotUpdating()
: EventWrapperRef34<Model, EquipSlot, CharacterArmor, ulong, EquipSlotUpdating.Priority>(nameof(EquipSlotUpdating))
{
public enum Priority
{
/// <seealso cref="State.StateListener.OnSlotUpdating"/>
/// <seealso cref="State.StateListener.OnEquipSlotUpdating"/>
StateListener = 0,
}
}
}

View file

@ -90,7 +90,7 @@ public unsafe class ModelEvaluationPanel(
: "No CharacterBase");
}
private void DrawParameters(Actor actor, Model model)
private static void DrawParameters(Actor actor, Model model)
{
if (!model.IsHuman)
return;
@ -140,13 +140,13 @@ public unsafe class ModelEvaluationPanel(
return;
if (ImGui.SmallButton("Hide"))
_updateSlotService.UpdateSlot(model, EquipSlot.Head, CharacterArmor.Empty);
_updateSlotService.UpdateEquipSlot(model, EquipSlot.Head, CharacterArmor.Empty);
ImGui.SameLine();
if (ImGui.SmallButton("Show"))
_updateSlotService.UpdateSlot(model, EquipSlot.Head, actor.GetArmor(EquipSlot.Head));
_updateSlotService.UpdateEquipSlot(model, EquipSlot.Head, actor.GetArmor(EquipSlot.Head));
ImGui.SameLine();
if (ImGui.SmallButton("Toggle"))
_updateSlotService.UpdateSlot(model, EquipSlot.Head,
_updateSlotService.UpdateEquipSlot(model, EquipSlot.Head,
model.AsHuman->Head.Value == 0 ? actor.GetArmor(EquipSlot.Head) : CharacterArmor.Empty);
}
@ -223,31 +223,32 @@ public unsafe class ModelEvaluationPanel(
_updateSlotService.UpdateStain(model, slot, new StainIds(5, 7));
ImGui.SameLine();
if (ImGui.SmallButton("Reset"))
_updateSlotService.UpdateSlot(model, slot, actor.GetArmor(slot));
_updateSlotService.UpdateEquipSlot(model, slot, actor.GetArmor(slot));
}
using (ImRaii.PushId((int)EquipSlot.FaceWear))
foreach (var slot in BonusSlotExtensions.AllFlags)
{
ImGuiUtil.DrawTableColumn(EquipSlot.FaceWear.ToName());
using var id2 = ImRaii.PushId((int)slot.ToModelIndex());
ImGuiUtil.DrawTableColumn(slot.ToName());
if (!actor.IsCharacter)
{
ImGuiUtil.DrawTableColumn("No Character");
}
else
{
var glassesId = actor.AsCharacter->DrawData.GlassesIds[(int)EquipSlot.FaceWear.ToBonusIndex()];
var glassesId = actor.GetBonusSlot(slot);
if (_glasses.TryGetValue(glassesId, out var glasses))
ImGuiUtil.DrawTableColumn($"{glasses.Id.Id},{glasses.Variant.Id} ({glassesId})");
else
ImGuiUtil.DrawTableColumn($"{glassesId}");
}
ImGuiUtil.DrawTableColumn(model.IsHuman ? model.GetArmor(EquipSlot.FaceWear).ToString() : "No Human");
ImGuiUtil.DrawTableColumn(model.IsHuman ? model.GetBonus(slot).ToString() : "No Human");
ImGui.TableNextColumn();
if (ImUtf8.SmallButton("Change Piece"u8))
{
var data = model.GetArmor(EquipSlot.FaceWear);
_updateSlotService.UpdateSlot(model, EquipSlot.FaceWear, data with { Variant = (Variant)((data.Variant.Id + 1) % 12) });
var data = model.GetBonus(slot);
_updateSlotService.UpdateBonusSlot(model, slot, data with { Variant = (Variant)((data.Variant.Id + 1) % 12) });
}
}
}

View file

@ -3,6 +3,7 @@ using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures;
using Glamourer.Events;
using Penumbra.GameData;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs;
@ -11,11 +12,16 @@ namespace Glamourer.Interop;
public unsafe class UpdateSlotService : IDisposable
{
public readonly SlotUpdating SlotUpdatingEvent;
public readonly EquipSlotUpdating EquipSlotUpdatingEvent;
public readonly BonusSlotUpdating BonusSlotUpdatingEvent;
private readonly DictGlasses _glasses;
public UpdateSlotService(SlotUpdating slotUpdating, IGameInteropProvider interop)
public UpdateSlotService(EquipSlotUpdating equipSlotUpdating, BonusSlotUpdating bonusSlotUpdating, IGameInteropProvider interop,
DictGlasses glasses)
{
SlotUpdatingEvent = slotUpdating;
EquipSlotUpdatingEvent = equipSlotUpdating;
BonusSlotUpdatingEvent = bonusSlotUpdating;
_glasses = glasses;
interop.InitializeFromAttributes(this);
_flagSlotForUpdateHook.Enable();
_flagBonusSlotForUpdateHook.Enable();
@ -27,20 +33,37 @@ public unsafe class UpdateSlotService : IDisposable
_flagBonusSlotForUpdateHook.Dispose();
}
public void UpdateSlot(Model drawObject, EquipSlot slot, CharacterArmor data)
public void UpdateEquipSlot(Model drawObject, EquipSlot slot, CharacterArmor data)
{
if (!drawObject.IsCharacterBase)
return;
var bonusSlot = slot.ToBonusIndex();
if (bonusSlot == uint.MaxValue)
FlagSlotForUpdateInterop(drawObject, slot, data);
else
_flagBonusSlotForUpdateHook.Original(drawObject.Address, bonusSlot, &data);
FlagSlotForUpdateInterop(drawObject, slot, data);
}
public void UpdateBonusSlot(Model drawObject, BonusEquipFlag slot, CharacterArmor data)
{
if (!drawObject.IsCharacterBase)
return;
var index = slot.ToIndex();
if (index == uint.MaxValue)
return;
_flagBonusSlotForUpdateHook.Original(drawObject.Address, index, &data);
}
public void UpdateGlasses(Model drawObject, GlassesId id)
{
if (!_glasses.TryGetValue(id, out var glasses))
return;
var armor = new CharacterArmor(glasses.Id, glasses.Variant, StainIds.None);
_flagBonusSlotForUpdateHook.Original(drawObject.Address, BonusEquipFlag.Glasses.ToIndex(), &armor);
}
public void UpdateArmor(Model drawObject, EquipSlot slot, CharacterArmor armor, StainIds stains)
=> UpdateSlot(drawObject, slot, armor.With(stains));
=> UpdateEquipSlot(drawObject, slot, armor.With(stains));
public void UpdateArmor(Model drawObject, EquipSlot slot, CharacterArmor armor)
=> UpdateArmor(drawObject, slot, armor, drawObject.GetArmor(slot).Stains);
@ -60,7 +83,7 @@ public unsafe class UpdateSlotService : IDisposable
{
var slot = slotIdx.ToEquipSlot();
var returnValue = ulong.MaxValue;
SlotUpdatingEvent.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}).");
return returnValue == ulong.MaxValue ? _flagSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue;
}
@ -69,7 +92,7 @@ public unsafe class UpdateSlotService : IDisposable
{
var slot = slotIdx.ToBonusSlot();
var returnValue = ulong.MaxValue;
SlotUpdatingEvent.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}).");
return returnValue == ulong.MaxValue ? _flagBonusSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue;
}

View file

@ -70,7 +70,7 @@ public static class StaticServiceManager
private static ServiceManager AddEvents(this ServiceManager services)
=> services.AddSingleton<VisorStateChanged>()
.AddSingleton<SlotUpdating>()
.AddSingleton<EquipSlotUpdating>()
.AddSingleton<DesignChanged>()
.AddSingleton<AutomationChanged>()
.AddSingleton<StateChanged>()

View file

@ -105,11 +105,11 @@ public class StateApplier(
{
var customize = mdl.GetCustomize();
var (_, resolvedItem) = _items.ResolveRestrictedGear(armor, slot, customize.Race, customize.Gender);
_updateSlot.UpdateSlot(actor.Model, slot, resolvedItem);
_updateSlot.UpdateEquipSlot(actor.Model, slot, resolvedItem);
}
else
{
_updateSlot.UpdateSlot(actor.Model, slot, armor);
_updateSlot.UpdateEquipSlot(actor.Model, slot, armor);
}
}
}

View file

@ -32,7 +32,7 @@ public class StateListener : IDisposable
private readonly ItemManager _items;
private readonly CustomizeService _customizations;
private readonly PenumbraService _penumbra;
private readonly SlotUpdating _slotUpdating;
private readonly EquipSlotUpdating _equipSlotUpdating;
private readonly WeaponLoading _weaponLoading;
private readonly HeadGearVisibilityChanged _headGearVisibility;
private readonly VisorStateChanged _visorState;
@ -52,7 +52,7 @@ public class StateListener : IDisposable
private CharacterWeapon _lastFistOffhand = CharacterWeapon.Empty;
public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorManager actors, Configuration config,
SlotUpdating slotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState, WeaponVisibilityChanged weaponVisibility,
EquipSlotUpdating equipSlotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState, WeaponVisibilityChanged weaponVisibility,
HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier, FunModule funModule, HumanModelList humans,
StateApplier applier, MovedEquipment movedEquipment, ObjectManager objects, GPoseService gPose,
ChangeCustomizeService changeCustomizeService, CustomizeService customizations, ICondition condition, CrestService crestService)
@ -62,7 +62,7 @@ public class StateListener : IDisposable
_penumbra = penumbra;
_actors = actors;
_config = config;
_slotUpdating = slotUpdating;
_equipSlotUpdating = equipSlotUpdating;
_weaponLoading = weaponLoading;
_visorState = visorState;
_weaponVisibility = weaponVisibility;
@ -202,7 +202,7 @@ public class StateListener : IDisposable
/// A draw model loads a new equipment piece.
/// Update base data, apply or update model data, and protect against restricted gear.
/// </summary>
private void OnSlotUpdating(Model model, EquipSlot slot, ref CharacterArmor armor, ref ulong returnValue)
private void OnEquipSlotUpdating(Model model, EquipSlot slot, ref CharacterArmor armor, ref ulong returnValue)
{
var actor = _penumbra.GameObjectFromDrawObject(model);
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
@ -699,7 +699,7 @@ public class StateListener : IDisposable
{
_penumbra.CreatingCharacterBase += OnCreatingCharacterBase;
_penumbra.CreatedCharacterBase += OnCreatedCharacterBase;
_slotUpdating.Subscribe(OnSlotUpdating, SlotUpdating.Priority.StateListener);
_equipSlotUpdating.Subscribe(OnEquipSlotUpdating, EquipSlotUpdating.Priority.StateListener);
_movedEquipment.Subscribe(OnMovedEquipment, MovedEquipment.Priority.StateListener);
_weaponLoading.Subscribe(OnWeaponLoading, WeaponLoading.Priority.StateListener);
_visorState.Subscribe(OnVisorChange, VisorStateChanged.Priority.StateListener);
@ -715,7 +715,7 @@ public class StateListener : IDisposable
{
_penumbra.CreatingCharacterBase -= OnCreatingCharacterBase;
_penumbra.CreatedCharacterBase -= OnCreatedCharacterBase;
_slotUpdating.Unsubscribe(OnSlotUpdating);
_equipSlotUpdating.Unsubscribe(OnEquipSlotUpdating);
_movedEquipment.Unsubscribe(OnMovedEquipment);
_weaponLoading.Unsubscribe(OnWeaponLoading);
_visorState.Unsubscribe(OnVisorChange);

@ -1 +1 @@
Subproject commit 8ec296d1f8113ae2ba509527749cd3e8f54debbf
Subproject commit d83303ccc3ec5d7237f5da621e9c2433ad28f9e1