Update OtterGui

This commit is contained in:
Ottermandias 2024-01-06 23:56:19 +01:00
parent 6130bae81d
commit 6ecf06a671
23 changed files with 142 additions and 254 deletions

View file

@ -11,7 +11,6 @@ using Glamourer.Interop.Structs;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.State; using Glamourer.State;
using Glamourer.Unlocks; using Glamourer.Unlocks;
using OtterGui.Classes;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.DataContainers; using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
@ -27,7 +26,7 @@ public class AutoDesignApplier : IDisposable
private readonly JobService _jobs; private readonly JobService _jobs;
private readonly EquippedGearset _equippedGearset; private readonly EquippedGearset _equippedGearset;
private readonly ActorManager _actors; private readonly ActorManager _actors;
private readonly CustomizeService _customizations; private readonly CustomizeService _customizations;
private readonly CustomizeUnlockManager _customizeUnlocks; private readonly CustomizeUnlockManager _customizeUnlocks;
private readonly ItemUnlockManager _itemUnlocks; private readonly ItemUnlockManager _itemUnlocks;
private readonly AutomationChanged _event; private readonly AutomationChanged _event;
@ -80,7 +79,7 @@ public class AutoDesignApplier : IDisposable
_jobs.JobChanged -= OnJobChange; _jobs.JobChanged -= OnJobChange;
} }
private void OnWeaponLoading(Actor actor, EquipSlot slot, Ref<CharacterWeapon> weapon) private void OnWeaponLoading(Actor actor, EquipSlot slot, ref CharacterWeapon weapon)
{ {
if (_jobChangeState == null || !_config.EnableAutoDesigns) if (_jobChangeState == null || !_config.EnableAutoDesigns)
return; return;
@ -89,27 +88,33 @@ public class AutoDesignApplier : IDisposable
if (id == _jobChangeState.Identifier) if (id == _jobChangeState.Identifier)
{ {
var current = _jobChangeState.BaseData.Item(slot); var current = _jobChangeState.BaseData.Item(slot);
if (slot is EquipSlot.MainHand) switch (slot)
{ {
if (_jobChangeMainhand.TryGetValue(current.Type, out var data)) case EquipSlot.MainHand:
{ {
Glamourer.Log.Verbose( if (_jobChangeMainhand.TryGetValue(current.Type, out var data))
$"Changing Mainhand from {_jobChangeState.ModelData.Weapon(EquipSlot.MainHand)} | {_jobChangeState.BaseData.Weapon(EquipSlot.MainHand)} to {data.Item1} for 0x{actor.Address:X}."); {
_state.ChangeItem(_jobChangeState, EquipSlot.MainHand, data.Item1, data.Item2); Glamourer.Log.Verbose(
weapon.Value = _jobChangeState.ModelData.Weapon(EquipSlot.MainHand); $"Changing Mainhand from {_jobChangeState.ModelData.Weapon(EquipSlot.MainHand)} | {_jobChangeState.BaseData.Weapon(EquipSlot.MainHand)} to {data.Item1} for 0x{actor.Address:X}.");
} _state.ChangeItem(_jobChangeState, EquipSlot.MainHand, data.Item1, data.Item2);
} weapon = _jobChangeState.ModelData.Weapon(EquipSlot.MainHand);
else if (slot is EquipSlot.OffHand && current.Type == _jobChangeState.BaseData.MainhandType.Offhand()) }
{
if (_jobChangeOffhand.TryGetValue(current.Type, out var data))
{
Glamourer.Log.Verbose(
$"Changing Offhand from {_jobChangeState.ModelData.Weapon(EquipSlot.OffHand)} | {_jobChangeState.BaseData.Weapon(EquipSlot.OffHand)} to {data.Item1} for 0x{actor.Address:X}.");
_state.ChangeItem(_jobChangeState, EquipSlot.OffHand, data.Item1, data.Item2);
weapon.Value = _jobChangeState.ModelData.Weapon(EquipSlot.OffHand);
}
ResetJobChange(); break;
}
case EquipSlot.OffHand when current.Type == _jobChangeState.BaseData.MainhandType.Offhand():
{
if (_jobChangeOffhand.TryGetValue(current.Type, out var data))
{
Glamourer.Log.Verbose(
$"Changing Offhand from {_jobChangeState.ModelData.Weapon(EquipSlot.OffHand)} | {_jobChangeState.BaseData.Weapon(EquipSlot.OffHand)} to {data.Item1} for 0x{actor.Address:X}.");
_state.ChangeItem(_jobChangeState, EquipSlot.OffHand, data.Item1, data.Item2);
weapon = _jobChangeState.ModelData.Weapon(EquipSlot.OffHand);
}
ResetJobChange();
break;
}
} }
} }
else else
@ -123,21 +128,32 @@ public class AutoDesignApplier : IDisposable
if (!_config.EnableAutoDesigns || set == null) if (!_config.EnableAutoDesigns || set == null)
return; return;
void RemoveOld(ActorIdentifier[]? identifiers) switch (type)
{ {
if (identifiers == null) case AutomationChanged.Type.ToggleSet when !set.Enabled:
return; case AutomationChanged.Type.DeletedDesign when set.Enabled:
// The automation set was disabled or deleted, no other for those identifiers can be enabled, remove existing Fixed Locks.
foreach (var id in identifiers) RemoveOld(set.Identifiers);
{ break;
if (id.Type is IdentifierType.Player && id.HomeWorld == WorldId.AnyWorld) case AutomationChanged.Type.ChangeIdentifier when set.Enabled:
foreach (var state in _state.Where(kvp => kvp.Key.PlayerName == id.PlayerName).Select(kvp => kvp.Value)) // Remove fixed state from the old identifiers assigned and the old enabled set, if any.
state.RemoveFixedDesignSources(); var (oldIds, _, oldSet) = ((ActorIdentifier[], ActorIdentifier, AutoDesignSet?))bonusData!;
else if (_state.TryGetValue(id, out var state)) RemoveOld(oldIds);
state.RemoveFixedDesignSources(); ApplyNew(set); // Does not need to disable oldSet because same identifiers.
} break;
case AutomationChanged.Type.ToggleSet: // Does not need to disable old states because same identifiers.
case AutomationChanged.Type.ChangedBase:
case AutomationChanged.Type.AddedDesign:
case AutomationChanged.Type.MovedDesign:
case AutomationChanged.Type.ChangedDesign:
case AutomationChanged.Type.ChangedConditions:
case AutomationChanged.Type.ChangedType:
ApplyNew(set);
break;
} }
return;
void ApplyNew(AutoDesignSet? newSet) void ApplyNew(AutoDesignSet? newSet)
{ {
if (newSet is not { Enabled: true }) if (newSet is not { Enabled: true })
@ -174,28 +190,19 @@ public class AutoDesignApplier : IDisposable
} }
} }
switch (type) void RemoveOld(ActorIdentifier[]? identifiers)
{ {
case AutomationChanged.Type.ToggleSet when !set.Enabled: if (identifiers == null)
case AutomationChanged.Type.DeletedDesign when set.Enabled: return;
// The automation set was disabled or deleted, no other for those identifiers can be enabled, remove existing Fixed Locks.
RemoveOld(set.Identifiers); foreach (var id in identifiers)
break; {
case AutomationChanged.Type.ChangeIdentifier when set.Enabled: if (id.Type is IdentifierType.Player && id.HomeWorld == WorldId.AnyWorld)
// Remove fixed state from the old identifiers assigned and the old enabled set, if any. foreach (var state in _state.Where(kvp => kvp.Key.PlayerName == id.PlayerName).Select(kvp => kvp.Value))
var (oldIds, _, oldSet) = ((ActorIdentifier[], ActorIdentifier, AutoDesignSet?))bonusData!; state.RemoveFixedDesignSources();
RemoveOld(oldIds); else if (_state.TryGetValue(id, out var state))
ApplyNew(set); // Does not need to disable oldSet because same identifiers. state.RemoveFixedDesignSources();
break; }
case AutomationChanged.Type.ToggleSet: // Does not need to disable old states because same identifiers.
case AutomationChanged.Type.ChangedBase:
case AutomationChanged.Type.AddedDesign:
case AutomationChanged.Type.MovedDesign:
case AutomationChanged.Type.ChangedDesign:
case AutomationChanged.Type.ChangedConditions:
case AutomationChanged.Type.ChangedType:
ApplyNew(set);
break;
} }
} }

View file

@ -99,7 +99,7 @@ public class DesignManager
Glamourer.Log.Information( Glamourer.Log.Information(
$"Loaded {_designs.Count} designs in {stopwatch.ElapsedMilliseconds} ms.{(skipped > 0 ? $" Skipped loading {skipped} designs due to errors." : string.Empty)}"); $"Loaded {_designs.Count} designs in {stopwatch.ElapsedMilliseconds} ms.{(skipped > 0 ? $" Skipped loading {skipped} designs due to errors." : string.Empty)}");
_event.Invoke(DesignChanged.Type.ReloadedAll, null!); _event.Invoke(DesignChanged.Type.ReloadedAll, null!, null);
} }
/// <summary> Whether an Undo for the given design is possible. </summary> /// <summary> Whether an Undo for the given design is possible. </summary>
@ -176,7 +176,7 @@ public class DesignManager
--d.Index; --d.Index;
_designs.RemoveAt(design.Index); _designs.RemoveAt(design.Index);
_saveService.ImmediateDelete(design); _saveService.ImmediateDelete(design);
_event.Invoke(DesignChanged.Type.Deleted, design); _event.Invoke(DesignChanged.Type.Deleted, design, null);
} }
/// <summary> Rename a design. </summary> /// <summary> Rename a design. </summary>
@ -723,7 +723,7 @@ public class DesignManager
if (!message.IsNullOrEmpty()) if (!message.IsNullOrEmpty())
Glamourer.Log.Debug(message); Glamourer.Log.Debug(message);
_saveService.ImmediateSave(design); _saveService.ImmediateSave(design);
_event.Invoke(DesignChanged.Type.Created, design); _event.Invoke(DesignChanged.Type.Created, design, null);
return true; return true;
} }

View file

@ -1,5 +1,4 @@
using System; using Glamourer.Automation;
using Glamourer.Automation;
using OtterGui.Classes; using OtterGui.Classes;
namespace Glamourer.Events; namespace Glamourer.Events;
@ -12,8 +11,8 @@ namespace Glamourer.Events;
/// <item>Parameter is additional data depending on the type of change. </item> /// <item>Parameter is additional data depending on the type of change. </item>
/// </list> /// </list>
/// </summary> /// </summary>
public sealed class AutomationChanged : EventWrapper<Action<AutomationChanged.Type, AutoDesignSet?, object?>, public sealed class AutomationChanged()
AutomationChanged.Priority> : EventWrapper<AutomationChanged.Type, AutoDesignSet?, object?, AutomationChanged.Priority>(nameof(AutomationChanged))
{ {
public enum Type public enum Type
{ {
@ -65,11 +64,4 @@ public sealed class AutomationChanged : EventWrapper<Action<AutomationChanged.Ty
/// <seealso cref="AutoDesignApplier.OnAutomationChange"/> /// <seealso cref="AutoDesignApplier.OnAutomationChange"/>
AutoDesignApplier, AutoDesignApplier,
} }
public AutomationChanged()
: base(nameof(AutomationChanged))
{ }
public void Invoke(Type type, AutoDesignSet? set, object? data)
=> Invoke(this, type, set, data);
} }

View file

@ -1,4 +1,3 @@
using System;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Gui; using Glamourer.Gui;
using OtterGui.Classes; using OtterGui.Classes;
@ -13,7 +12,8 @@ namespace Glamourer.Events;
/// <item>Parameter is any additional data depending on the type of change. </item> /// <item>Parameter is any additional data depending on the type of change. </item>
/// </list> /// </list>
/// </summary> /// </summary>
public sealed class DesignChanged : EventWrapper<Action<DesignChanged.Type, Design, object?>, DesignChanged.Priority> public sealed class DesignChanged()
: EventWrapper<DesignChanged.Type, Design, object?, DesignChanged.Priority>(nameof(DesignChanged))
{ {
public enum Type public enum Type
{ {
@ -98,11 +98,4 @@ public sealed class DesignChanged : EventWrapper<Action<DesignChanged.Type, Desi
/// <seealso cref="RevertDesignCombo.OnDesignChange"/> /// <seealso cref="RevertDesignCombo.OnDesignChange"/>
DesignCombo = -2, DesignCombo = -2,
} }
public DesignChanged()
: base(nameof(DesignChanged))
{ }
public void Invoke(Type type, Design design, object? data = null)
=> Invoke(this, type, design, data);
} }

View file

@ -1,4 +1,3 @@
using System;
using OtterGui.Classes; using OtterGui.Classes;
namespace Glamourer.Events; namespace Glamourer.Events;
@ -13,18 +12,12 @@ namespace Glamourer.Events;
/// <item>Parameter is the job id of the associated job. </item> /// <item>Parameter is the job id of the associated job. </item>
/// </list> /// </list>
/// </summary> /// </summary>
public sealed class EquippedGearset : EventWrapper<Action<string, int, int, byte, byte>, EquippedGearset.Priority> public sealed class EquippedGearset()
: EventWrapper<string, int, int, byte, byte, EquippedGearset.Priority>(nameof(EquippedGearset))
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="Automation.AutoDesignApplier.OnEquippedGearset"/> /// <seealso cref="Automation.AutoDesignApplier.OnEquippedGearset"/>
AutoDesignApplier = 0, AutoDesignApplier = 0,
} }
public EquippedGearset()
: base(nameof(EquippedGearset))
{ }
public void Invoke(string name, int id, int lastId, byte glamour, byte jobId)
=> Invoke(this, name, id, lastId, glamour, jobId);
} }

View file

@ -5,7 +5,7 @@ using OtterGui.Classes;
namespace Glamourer.Events; namespace Glamourer.Events;
public sealed class GPoseService : EventWrapper<Action<bool>, GPoseService.Priority> public sealed class GPoseService : EventWrapper<bool, GPoseService.Priority>
{ {
private readonly IFramework _framework; private readonly IFramework _framework;
private readonly IClientState _state; private readonly IClientState _state;
@ -56,7 +56,7 @@ public sealed class GPoseService : EventWrapper<Action<bool>, GPoseService.Prior
return; return;
InGPose = inGPose; InGPose = inGPose;
Invoke(this, InGPose); Invoke(InGPose);
var actions = InGPose ? _onEnter : _onLeave; var actions = InGPose ? _onEnter : _onLeave;
foreach (var action in actions) foreach (var action in actions)
{ {

View file

@ -1,4 +1,3 @@
using System;
using Glamourer.Interop.Structs; using Glamourer.Interop.Structs;
using OtterGui.Classes; using OtterGui.Classes;
@ -11,22 +10,12 @@ namespace Glamourer.Events;
/// <item>Parameter is the new state. </item> /// <item>Parameter is the new state. </item>
/// </list> /// </list>
/// </summary> /// </summary>
public sealed class HeadGearVisibilityChanged : EventWrapper<Action<Actor, Ref<bool>>, HeadGearVisibilityChanged.Priority> public sealed class HeadGearVisibilityChanged()
: EventWrapperRef2<Actor, bool, HeadGearVisibilityChanged.Priority>(nameof(HeadGearVisibilityChanged))
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="State.StateListener.OnHeadGearVisibilityChange"/> /// <seealso cref="State.StateListener.OnHeadGearVisibilityChange"/>
StateListener = 0, StateListener = 0,
} }
public HeadGearVisibilityChanged()
: base(nameof(HeadGearVisibilityChanged))
{ }
public void Invoke(Actor actor, ref bool state)
{
var value = new Ref<bool>(state);
Invoke(this, actor, value);
state = value;
}
} }

View file

@ -1,4 +1,3 @@
using System;
using OtterGui.Classes; using OtterGui.Classes;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
@ -11,18 +10,12 @@ namespace Glamourer.Events;
/// <item>Parameter is an array of slots updated and corresponding item ids and stains. </item> /// <item>Parameter is an array of slots updated and corresponding item ids and stains. </item>
/// </list> /// </list>
/// </summary> /// </summary>
public sealed class MovedEquipment : EventWrapper<Action<(EquipSlot, uint, StainId)[]>, MovedEquipment.Priority> public sealed class MovedEquipment()
: EventWrapper<(EquipSlot, uint, StainId)[], MovedEquipment.Priority>(nameof(MovedEquipment))
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="State.StateListener.OnMovedEquipment"/> /// <seealso cref="State.StateListener.OnMovedEquipment"/>
StateListener = 0, StateListener = 0,
} }
public MovedEquipment()
: base(nameof(MovedEquipment))
{ }
public void Invoke((EquipSlot, uint, StainId)[] items)
=> Invoke(this, items);
} }

View file

@ -11,7 +11,8 @@ namespace Glamourer.Events;
/// <item>Parameter is the timestamp of the unlock. </item> /// <item>Parameter is the timestamp of the unlock. </item>
/// </list> /// </list>
/// </summary> /// </summary>
public sealed class ObjectUnlocked : EventWrapper<Action<ObjectUnlocked.Type, uint, DateTimeOffset>, ObjectUnlocked.Priority> public sealed class ObjectUnlocked()
: EventWrapper<ObjectUnlocked.Type, uint, DateTimeOffset, ObjectUnlocked.Priority>(nameof(ObjectUnlocked))
{ {
public enum Type public enum Type
{ {
@ -25,11 +26,4 @@ public sealed class ObjectUnlocked : EventWrapper<Action<ObjectUnlocked.Type, ui
/// <remarks> Currently used as a hack to make the unlock table dirty in it. If anything else starts using this, rework. </remarks> /// <remarks> Currently used as a hack to make the unlock table dirty in it. If anything else starts using this, rework. </remarks>
UnlockTable = 0, UnlockTable = 0,
} }
public ObjectUnlocked()
: base(nameof(ObjectUnlocked))
{ }
public void Invoke(Type type, uint id, DateTimeOffset timestamp)
=> Invoke(this, type, id, timestamp);
} }

View file

@ -1,4 +1,3 @@
using System;
using OtterGui.Classes; using OtterGui.Classes;
namespace Glamourer.Events; namespace Glamourer.Events;
@ -6,18 +5,12 @@ namespace Glamourer.Events;
/// <summary> /// <summary>
/// Triggered when Penumbra is reloaded. /// Triggered when Penumbra is reloaded.
/// </summary> /// </summary>
public sealed class PenumbraReloaded : EventWrapper<Action, PenumbraReloaded.Priority> public sealed class PenumbraReloaded()
: EventWrapper<PenumbraReloaded.Priority>(nameof(PenumbraReloaded))
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="Interop.ChangeCustomizeService.Restore"/> /// <seealso cref="Interop.ChangeCustomizeService.Restore"/>
ChangeCustomizeService = 0, ChangeCustomizeService = 0,
} }
public PenumbraReloaded()
: base(nameof(PenumbraReloaded))
{ }
public void Invoke()
=> Invoke(this);
} }

View file

@ -15,24 +15,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> /// <item>Parameter is the return value the function should return, if it is ulong.MaxValue, the original will be called and returned. </item>
/// </list> /// </list>
/// </summary> /// </summary>
public sealed class SlotUpdating : EventWrapper<Action<Model, EquipSlot, Ref<CharacterArmor>, Ref<ulong>>, SlotUpdating.Priority> public sealed class SlotUpdating()
: EventWrapperRef34<Model, EquipSlot, CharacterArmor, ulong, SlotUpdating.Priority>(nameof(SlotUpdating))
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="State.StateListener.OnSlotUpdating"/> /// <seealso cref="State.StateListener.OnSlotUpdating"/>
StateListener = 0, StateListener = 0,
} }
public SlotUpdating()
: base(nameof(SlotUpdating))
{ }
public void Invoke(Model model, EquipSlot slot, ref CharacterArmor armor, ref ulong returnValue)
{
var value = new Ref<CharacterArmor>(armor);
var @return = new Ref<ulong>(returnValue);
Invoke(this, model, slot, value, @return);
armor = value;
returnValue = @return;
}
} }

View file

@ -1,8 +1,6 @@
using System;
using Glamourer.Interop.Structs; using Glamourer.Interop.Structs;
using Glamourer.State; using Glamourer.State;
using OtterGui.Classes; using OtterGui.Classes;
using Penumbra.GameData.Actors;
namespace Glamourer.Events; namespace Glamourer.Events;
@ -15,7 +13,8 @@ namespace Glamourer.Events;
/// <item>Parameter is any additional data depending on the type of change. </item> /// <item>Parameter is any additional data depending on the type of change. </item>
/// </list> /// </list>
/// </summary> /// </summary>
public sealed class StateChanged : EventWrapper<Action<StateChanged.Type, StateChanged.Source, ActorState, ActorData, object?>, StateChanged.Priority> public sealed class StateChanged()
: EventWrapper<StateChanged.Type, StateChanged.Source, ActorState, ActorData, object?, StateChanged.Priority>(nameof(StateChanged))
{ {
public enum Type public enum Type
{ {
@ -62,11 +61,4 @@ public sealed class StateChanged : EventWrapper<Action<StateChanged.Type, StateC
{ {
GlamourerIpc = int.MinValue, GlamourerIpc = int.MinValue,
} }
public StateChanged()
: base(nameof(StateChanged))
{ }
public void Invoke(Type type, Source source, ActorState state, ActorData actors, object? data = null)
=> Invoke(this, type, source, state, actors, data);
} }

View file

@ -1,5 +1,4 @@
using System; using Glamourer.Designs;
using Glamourer.Designs;
using Glamourer.Gui; using Glamourer.Gui;
using OtterGui.Classes; using OtterGui.Classes;
@ -12,8 +11,8 @@ namespace Glamourer.Events;
/// <item>Parameter is the design to select if the tab is the designs tab. </item> /// <item>Parameter is the design to select if the tab is the designs tab. </item>
/// </list> /// </list>
/// </summary> /// </summary>
public sealed class TabSelected : EventWrapper<Action<MainWindow.TabType, Design?>, public sealed class TabSelected()
TabSelected.Priority> : EventWrapper<MainWindow.TabType, Design?, TabSelected.Priority>(nameof(TabSelected))
{ {
public enum Priority public enum Priority
{ {
@ -23,11 +22,4 @@ public sealed class TabSelected : EventWrapper<Action<MainWindow.TabType, Design
/// <seealso cref="Gui.MainWindow.OnTabSelected"/> /// <seealso cref="Gui.MainWindow.OnTabSelected"/>
MainWindow = 1, MainWindow = 1,
} }
public TabSelected()
: base(nameof(TabSelected))
{ }
public void Invoke(MainWindow.TabType type, Design? design)
=> Invoke(this, type, design);
} }

View file

@ -1,4 +1,3 @@
using System;
using Glamourer.Interop.Structs; using Glamourer.Interop.Structs;
using OtterGui.Classes; using OtterGui.Classes;
@ -12,22 +11,12 @@ namespace Glamourer.Events;
/// <item>Parameter is whether to call the original function. </item> /// <item>Parameter is whether to call the original function. </item>
/// </list> /// </list>
/// </summary> /// </summary>
public sealed class VisorStateChanged : EventWrapper<Action<Model, Ref<bool>>, VisorStateChanged.Priority> public sealed class VisorStateChanged()
: EventWrapperRef2<Model, bool, VisorStateChanged.Priority>(nameof(VisorStateChanged))
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="State.StateListener.OnVisorChange"/> /// <seealso cref="State.StateListener.OnVisorChange"/>
StateListener = 0, StateListener = 0,
} }
public VisorStateChanged()
: base(nameof(VisorStateChanged))
{ }
public void Invoke(Model model, ref bool state)
{
var value = new Ref<bool>(state);
Invoke(this, model, value);
state = value;
}
} }

View file

@ -1,4 +1,3 @@
using System;
using Glamourer.Interop.Structs; using Glamourer.Interop.Structs;
using OtterGui.Classes; using OtterGui.Classes;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
@ -14,7 +13,8 @@ namespace Glamourer.Events;
/// <item>Parameter is the model values to change the weapon to. </item> /// <item>Parameter is the model values to change the weapon to. </item>
/// </list> /// </list>
/// </summary> /// </summary>
public sealed class WeaponLoading : EventWrapper<Action<Actor, EquipSlot, Ref<CharacterWeapon>>, WeaponLoading.Priority> public sealed class WeaponLoading()
: EventWrapperRef3<Actor, EquipSlot, CharacterWeapon, WeaponLoading.Priority>(nameof(WeaponLoading))
{ {
public enum Priority public enum Priority
{ {
@ -24,15 +24,4 @@ public sealed class WeaponLoading : EventWrapper<Action<Actor, EquipSlot, Ref<Ch
/// <seealso cref="Automation.AutoDesignApplier.OnWeaponLoading"/> /// <seealso cref="Automation.AutoDesignApplier.OnWeaponLoading"/>
AutoDesignApplier = -1, AutoDesignApplier = -1,
} }
public WeaponLoading()
: base(nameof(WeaponLoading))
{ }
public void Invoke(Actor actor, EquipSlot slot, ref CharacterWeapon weapon)
{
var value = new Ref<CharacterWeapon>(weapon);
Invoke(this, actor, slot, value);
weapon = value;
}
} }

View file

@ -1,4 +1,3 @@
using System;
using Glamourer.Interop.Structs; using Glamourer.Interop.Structs;
using OtterGui.Classes; using OtterGui.Classes;
@ -11,22 +10,11 @@ namespace Glamourer.Events;
/// <item>Parameter is the new state. </item> /// <item>Parameter is the new state. </item>
/// </list> /// </list>
/// </summary> /// </summary>
public sealed class WeaponVisibilityChanged : EventWrapper<Action<Actor, Ref<bool>>, WeaponVisibilityChanged.Priority> public sealed class WeaponVisibilityChanged() : EventWrapperRef2<Actor, bool, WeaponVisibilityChanged.Priority>(nameof(WeaponVisibilityChanged))
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="State.StateListener.OnWeaponVisibilityChange"/> /// <seealso cref="State.StateListener.OnWeaponVisibilityChange"/>
StateListener = 0, StateListener = 0,
} }
public WeaponVisibilityChanged()
: base(nameof(WeaponVisibilityChanged))
{ }
public void Invoke(Actor actor, ref bool state)
{
var value = new Ref<bool>(state);
Invoke(this, actor, value);
state = value;
}
} }

View file

@ -5,7 +5,6 @@ using Glamourer.Gui;
using Glamourer.Interop; using Glamourer.Interop;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.State; using Glamourer.State;
using Microsoft.Extensions.DependencyInjection;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Log; using OtterGui.Log;
using OtterGui.Services; using OtterGui.Services;
@ -34,6 +33,8 @@ public class Glamourer : IDalamudPlugin
{ {
_services = ServiceManagerA.CreateProvider(pluginInterface, Log); _services = ServiceManagerA.CreateProvider(pluginInterface, Log);
Messager = _services.GetService<MessageService>(); Messager = _services.GetService<MessageService>();
_services.EnsureRequiredServices();
_services.GetService<VisorService>(); _services.GetService<VisorService>();
_services.GetService<WeaponService>(); _services.GetService<WeaponService>();
_services.GetService<ScalingService>(); _services.GetService<ScalingService>();

View file

@ -14,7 +14,7 @@ namespace Glamourer.Interop;
/// Changes in Race, body type or Gender are probably ignored. /// Changes in Race, body type or Gender are probably ignored.
/// This operates on draw objects, not game objects. /// This operates on draw objects, not game objects.
/// </summary> /// </summary>
public unsafe class ChangeCustomizeService : EventWrapper<Action<Model, Ref<CustomizeArray>>, ChangeCustomizeService.Priority> public unsafe class ChangeCustomizeService : EventWrapperRef2<Model, CustomizeArray, ChangeCustomizeService.Priority>
{ {
private readonly PenumbraReloaded _penumbraReloaded; private readonly PenumbraReloaded _penumbraReloaded;
private readonly IGameInteropProvider _interop; private readonly IGameInteropProvider _interop;
@ -79,11 +79,7 @@ public unsafe class ChangeCustomizeService : EventWrapper<Action<Model, Ref<Cust
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);
var customize = new Ref<CustomizeArray>(*(CustomizeArray*)data);
Invoke(this, (Model)human, customize);
*(CustomizeArray*)data = customize.Value;
}
return _changeCustomizeHook.Original(human, data, skipEquipment); return _changeCustomizeHook.Original(human, data, skipEquipment);
} }

View file

@ -1,5 +1,4 @@
using System; 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 FFXIVClientStructs.FFXIV.Client.Game.Character;
@ -15,10 +14,10 @@ namespace Glamourer.Interop;
/// <list type="number"> /// <list type="number">
/// <item>Parameter is the model with an update. </item> /// <item>Parameter is the model with an update. </item>
/// <item>Parameter is the equipment slot changed. </item> /// <item>Parameter is the equipment slot changed. </item>
/// <item>Parameter is the whether the crest will be shown. </item> /// <item>Parameter is whether the crest will be shown. </item>
/// </list> /// </list>
/// </summary> /// </summary>
public sealed unsafe class CrestService : EventWrapper<Action<Actor, CrestFlag, Ref<bool>>, CrestService.Priority> public sealed unsafe class CrestService : EventWrapperRef3<Actor, CrestFlag, bool, CrestService.Priority>
{ {
public enum Priority public enum Priority
{ {
@ -72,9 +71,9 @@ public sealed unsafe class CrestService : EventWrapper<Action<Actor, CrestFlag,
var actor = (Actor)character; var actor = (Actor)character;
foreach (var slot in CrestExtensions.AllRelevantSet) foreach (var slot in CrestExtensions.AllRelevantSet)
{ {
var newValue = new Ref<bool>(((CrestFlag)crestFlags).HasFlag(slot)); var newValue = ((CrestFlag)crestFlags).HasFlag(slot);
Invoke(this, actor, slot, newValue); Invoke(actor, slot, ref newValue);
crestFlags = (byte)(newValue.Value ? crestFlags | (byte)slot : crestFlags & (byte)~slot); crestFlags = (byte)(newValue ? crestFlags | (byte)slot : crestFlags & (byte)~slot);
} }
Glamourer.Log.Verbose( Glamourer.Log.Verbose(

View file

@ -33,7 +33,7 @@ public static class ServiceManagerA
{ {
public static ServiceManager CreateProvider(DalamudPluginInterface pi, Logger log) public static ServiceManager CreateProvider(DalamudPluginInterface pi, Logger log)
{ {
EventWrapper.ChangeLogger(log); EventWrapperBase.ChangeLogger(log);
var services = new ServiceManager(log) var services = new ServiceManager(log)
.AddExistingService(log) .AddExistingService(log)
.AddMeta() .AddMeta()

View file

@ -138,7 +138,7 @@ public class StateListener : IDisposable
ProtectRestrictedGear(equipDataPtr, customize.Race, customize.Gender); ProtectRestrictedGear(equipDataPtr, customize.Race, customize.Gender);
} }
private unsafe void OnCustomizeChange(Model model, Ref<CustomizeArray> customize) private unsafe void OnCustomizeChange(Model model, ref CustomizeArray customize)
{ {
if (!model.IsHuman) if (!model.IsHuman)
return; return;
@ -151,7 +151,7 @@ public class StateListener : IDisposable
|| !_manager.TryGetValue(identifier, out var state)) || !_manager.TryGetValue(identifier, out var state))
return; return;
UpdateCustomize(actor, state, ref customize.Value, false); UpdateCustomize(actor, state, ref customize, false);
} }
private void UpdateCustomize(Actor actor, ActorState state, ref CustomizeArray customize, bool checkTransform) private void UpdateCustomize(Actor actor, ActorState state, ref CustomizeArray customize, bool checkTransform)
@ -199,7 +199,7 @@ public class StateListener : IDisposable
/// A draw model loads a new equipment piece. /// A draw model loads a new equipment piece.
/// Update base data, apply or update model data, and protect against restricted gear. /// Update base data, apply or update model data, and protect against restricted gear.
/// </summary> /// </summary>
private void OnSlotUpdating(Model model, EquipSlot slot, Ref<CharacterArmor> armor, Ref<ulong> returnValue) private void OnSlotUpdating(Model model, EquipSlot slot, ref CharacterArmor armor, ref ulong returnValue)
{ {
var actor = _penumbra.GameObjectFromDrawObject(model); var actor = _penumbra.GameObjectFromDrawObject(model);
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
@ -212,16 +212,16 @@ public class StateListener : IDisposable
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.Value); HandleEquipSlot(actor, state, slot, ref armor);
locked = state[slot, false] is StateChanged.Source.Ipc; locked = state[slot, false] is StateChanged.Source.Ipc;
} }
_funModule.ApplyFunToSlot(actor, ref armor.Value, slot); _funModule.ApplyFunToSlot(actor, ref armor, slot);
if (!_config.UseRestrictedGearProtection || locked) if (!_config.UseRestrictedGearProtection || locked)
return; return;
var customize = model.GetCustomize(); var customize = model.GetCustomize();
(_, armor.Value) = _items.RestrictedGear.ResolveRestricted(armor, slot, customize.Race, customize.Gender); (_, armor) = _items.RestrictedGear.ResolveRestricted(armor, slot, customize.Race, customize.Gender);
} }
private void OnMovedEquipment((EquipSlot, uint, StainId)[] items) private void OnMovedEquipment((EquipSlot, uint, StainId)[] items)
@ -264,20 +264,20 @@ public class StateListener : IDisposable
/// Update base data, apply or update model data. /// Update base data, apply or update model data.
/// Verify consistent weapon types. /// Verify consistent weapon types.
/// </summary> /// </summary>
private void OnWeaponLoading(Actor actor, EquipSlot slot, Ref<CharacterWeapon> weapon) private void OnWeaponLoading(Actor actor, EquipSlot slot, ref CharacterWeapon weapon)
{ {
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
return; return;
// Fist weapon gauntlet hack. // Fist weapon gauntlet hack.
if (slot is EquipSlot.OffHand && weapon.Value.Variant == 0 && weapon.Value.Weapon.Id != 0 && _lastFistOffhand.Weapon.Id != 0) if (slot is EquipSlot.OffHand && weapon.Variant == 0 && weapon.Weapon.Id != 0 && _lastFistOffhand.Weapon.Id != 0)
weapon.Value = _lastFistOffhand; weapon = _lastFistOffhand;
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))
return; return;
ref var actorWeapon = ref weapon.Value; ref var actorWeapon = ref weapon;
var baseType = state.BaseData.Item(slot).Type; var baseType = state.BaseData.Item(slot).Type;
var apply = false; var apply = false;
switch (UpdateBaseData(actor, state, slot, actorWeapon)) switch (UpdateBaseData(actor, state, slot, actorWeapon))
@ -311,25 +311,16 @@ public class StateListener : IDisposable
} }
// Fist Weapon Offhand hack. // Fist Weapon Offhand hack.
if (slot is EquipSlot.MainHand && weapon.Value.Skeleton.Id is > 1600 and < 1651) if (slot is EquipSlot.MainHand && weapon.Skeleton.Id is > 1600 and < 1651)
_lastFistOffhand = new CharacterWeapon((PrimaryId)(weapon.Value.Skeleton.Id + 50), weapon.Value.Weapon, weapon.Value.Variant, _lastFistOffhand = new CharacterWeapon((PrimaryId)(weapon.Skeleton.Id + 50), weapon.Weapon, weapon.Variant,
weapon.Value.Stain); weapon.Stain);
_funModule.ApplyFunToWeapon(actor, ref weapon.Value, slot); _funModule.ApplyFunToWeapon(actor, ref weapon, slot);
} }
/// <summary> Update base data for a single changed equipment slot. </summary> /// <summary> Update base data for a single changed equipment slot. </summary>
private UpdateState UpdateBaseData(Actor actor, ActorState state, EquipSlot slot, CharacterArmor armor) private UpdateState UpdateBaseData(Actor actor, ActorState state, EquipSlot slot, CharacterArmor armor)
{ {
bool FistWeaponGauntletHack()
{
if (slot is not EquipSlot.Hands)
return false;
var offhand = actor.GetOffhand();
return offhand.Variant == 0 && offhand.Weapon.Id != 0 && armor.Set.Id == offhand.Weapon.Id;
}
var actorArmor = actor.GetArmor(slot); var actorArmor = actor.GetArmor(slot);
var fistWeapon = FistWeaponGauntletHack(); var fistWeapon = FistWeaponGauntletHack();
@ -373,6 +364,15 @@ public class StateListener : IDisposable
} }
return change; return change;
bool FistWeaponGauntletHack()
{
if (slot is not EquipSlot.Hands)
return false;
var offhand = actor.GetOffhand();
return offhand.Variant == 0 && offhand.Weapon.Id != 0 && armor.Set.Id == offhand.Weapon.Id;
}
} }
/// <summary> Handle a full equip slot update for base data and model data. </summary> /// <summary> Handle a full equip slot update for base data and model data. </summary>
@ -406,7 +406,7 @@ public class StateListener : IDisposable
} }
} }
private void OnCrestChange(Actor actor, CrestFlag slot, Ref<bool> value) private void OnCrestChange(Actor actor, CrestFlag slot, ref bool value)
{ {
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
return; return;
@ -415,17 +415,17 @@ public class StateListener : IDisposable
|| !_manager.TryGetValue(identifier, out var state)) || !_manager.TryGetValue(identifier, out var state))
return; return;
switch (UpdateBaseCrest(actor, state, slot, value.Value)) switch (UpdateBaseCrest(actor, state, slot, value))
{ {
case UpdateState.Change: case UpdateState.Change:
if (state[slot] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc) if (state[slot] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
_manager.ChangeCrest(state, slot, state.BaseData.Crest(slot), StateChanged.Source.Game); _manager.ChangeCrest(state, slot, state.BaseData.Crest(slot), StateChanged.Source.Game);
else else
value.Value = state.ModelData.Crest(slot); value = state.ModelData.Crest(slot);
break; break;
case UpdateState.NoChange: case UpdateState.NoChange:
case UpdateState.HatHack: case UpdateState.HatHack:
value.Value = state.ModelData.Crest(slot); value = state.ModelData.Crest(slot);
break; break;
case UpdateState.Transformed: break; case UpdateState.Transformed: break;
} }
@ -540,7 +540,7 @@ public class StateListener : IDisposable
} }
/// <summary> Handle visor state changes made by the game. </summary> /// <summary> Handle visor state changes made by the game. </summary>
private void OnVisorChange(Model model, Ref<bool> value) private void OnVisorChange(Model model, ref bool value)
{ {
// Skip updates when in customize update. // Skip updates when in customize update.
if (ChangeCustomizeService.InUpdate.InMethod) if (ChangeCustomizeService.InUpdate.InMethod)
@ -565,19 +565,19 @@ public class StateListener : IDisposable
// if base state changed, either overwrite the actual value if we have fixed values, // if base state changed, either overwrite the actual value if we have fixed values,
// or overwrite the stored model state with the new one. // or overwrite the stored model state with the new one.
if (state[ActorState.MetaIndex.VisorState] is StateChanged.Source.Fixed or StateChanged.Source.Ipc) if (state[ActorState.MetaIndex.VisorState] is StateChanged.Source.Fixed or StateChanged.Source.Ipc)
value.Value = state.ModelData.IsVisorToggled(); value = state.ModelData.IsVisorToggled();
else else
_manager.ChangeVisorState(state, value, StateChanged.Source.Game); _manager.ChangeVisorState(state, value, StateChanged.Source.Game);
} }
else else
{ {
// if base state did not change, overwrite the value with the model state one. // if base state did not change, overwrite the value with the model state one.
value.Value = state.ModelData.IsVisorToggled(); value = state.ModelData.IsVisorToggled();
} }
} }
/// <summary> Handle Hat Visibility changes. These act on the game object. </summary> /// <summary> Handle Hat Visibility changes. These act on the game object. </summary>
private void OnHeadGearVisibilityChange(Actor actor, Ref<bool> value) private void OnHeadGearVisibilityChange(Actor actor, ref bool value)
{ {
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
return; return;
@ -598,19 +598,19 @@ public class StateListener : IDisposable
// if base state changed, either overwrite the actual value if we have fixed values, // if base state changed, either overwrite the actual value if we have fixed values,
// or overwrite the stored model state with the new one. // or overwrite the stored model state with the new one.
if (state[ActorState.MetaIndex.HatState] is StateChanged.Source.Fixed or StateChanged.Source.Ipc) if (state[ActorState.MetaIndex.HatState] is StateChanged.Source.Fixed or StateChanged.Source.Ipc)
value.Value = state.ModelData.IsHatVisible(); value = state.ModelData.IsHatVisible();
else else
_manager.ChangeHatState(state, value, StateChanged.Source.Game); _manager.ChangeHatState(state, value, StateChanged.Source.Game);
} }
else else
{ {
// if base state did not change, overwrite the value with the model state one. // if base state did not change, overwrite the value with the model state one.
value.Value = state.ModelData.IsHatVisible(); value = state.ModelData.IsHatVisible();
} }
} }
/// <summary> Handle Weapon Visibility changes. These act on the game object. </summary> /// <summary> Handle Weapon Visibility changes. These act on the game object. </summary>
private void OnWeaponVisibilityChange(Actor actor, Ref<bool> value) private void OnWeaponVisibilityChange(Actor actor, ref bool value)
{ {
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
return; return;
@ -631,14 +631,14 @@ public class StateListener : IDisposable
// if base state changed, either overwrite the actual value if we have fixed values, // if base state changed, either overwrite the actual value if we have fixed values,
// or overwrite the stored model state with the new one. // or overwrite the stored model state with the new one.
if (state[ActorState.MetaIndex.WeaponState] is StateChanged.Source.Fixed or StateChanged.Source.Ipc) if (state[ActorState.MetaIndex.WeaponState] is StateChanged.Source.Fixed or StateChanged.Source.Ipc)
value.Value = state.ModelData.IsWeaponVisible(); value = state.ModelData.IsWeaponVisible();
else else
_manager.ChangeWeaponState(state, value, StateChanged.Source.Game); _manager.ChangeWeaponState(state, value, StateChanged.Source.Game);
} }
else else
{ {
// if base state did not change, overwrite the value with the model state one. // if base state did not change, overwrite the value with the model state one.
value.Value = state.ModelData.IsWeaponVisible(); value = state.ModelData.IsWeaponVisible();
} }
} }

View file

@ -476,7 +476,7 @@ public class StateManager(
actors = ApplyAll(state, redraw, true); actors = ApplyAll(state, redraw, true);
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"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")}.]");
_event.Invoke(StateChanged.Type.Reset, StateChanged.Source.Manual, state, actors); _event.Invoke(StateChanged.Type.Reset, StateChanged.Source.Manual, state, actors, null);
} }
public void ResetStateFixed(ActorState state, uint key = 0) public void ResetStateFixed(ActorState state, uint key = 0)

@ -1 +1 @@
Subproject commit e58c3c1240cda9d2d2b54f5ab7b8c729c1251fd4 Subproject commit e4a82619332aade2748a381956a1ab8934de6211