Make AutoDesignApplier use MergedDesign.

This commit is contained in:
Ottermandias 2024-01-21 00:51:42 +01:00
parent 2422295e67
commit 96c4ae762e
3 changed files with 101 additions and 65 deletions

View file

@ -35,20 +35,18 @@ public sealed class AutoDesignApplier : IDisposable
private readonly IClientState _clientState; private readonly IClientState _clientState;
private ActorState? _jobChangeState; private ActorState? _jobChangeState;
private readonly Dictionary<FullEquipType, (EquipItem, StateChanged.Source)> _jobChangeMainhand = []; private readonly Dictionary<FullEquipType, (EquipItem, StateChanged.Source)> _jobChange = [];
private readonly Dictionary<FullEquipType, (EquipItem, StateChanged.Source)> _jobChangeOffhand = [];
private void ResetJobChange() private void ResetJobChange()
{ {
_jobChangeState = null; _jobChangeState = null;
_jobChangeMainhand.Clear(); _jobChange.Clear();
_jobChangeOffhand.Clear();
} }
public AutoDesignApplier(Configuration config, AutoDesignManager manager, StateManager state, JobService jobs, public AutoDesignApplier(Configuration config, AutoDesignManager manager, StateManager state, JobService jobs,
CustomizeService customizations, ActorManager actors, ItemUnlockManager itemUnlocks, CustomizeUnlockManager customizeUnlocks, CustomizeService customizations, ActorManager actors, ItemUnlockManager itemUnlocks, CustomizeUnlockManager customizeUnlocks,
AutomationChanged @event, ObjectManager objects, WeaponLoading weapons, HumanModelList humans, IClientState clientState, AutomationChanged @event, ObjectManager objects, WeaponLoading weapons, HumanModelList humans, IClientState clientState,
EquippedGearset equippedGearset) EquippedGearset equippedGearset, DesignMerger designMerger)
{ {
_config = config; _config = config;
_manager = manager; _manager = manager;
@ -64,6 +62,7 @@ public sealed class AutoDesignApplier : IDisposable
_humans = humans; _humans = humans;
_clientState = clientState; _clientState = clientState;
_equippedGearset = equippedGearset; _equippedGearset = equippedGearset;
_designMerger = designMerger;
_jobs.JobChanged += OnJobChange; _jobs.JobChanged += OnJobChange;
_event.Subscribe(OnAutomationChange, AutomationChanged.Priority.AutoDesignApplier); _event.Subscribe(OnAutomationChange, AutomationChanged.Priority.AutoDesignApplier);
_weapons.Subscribe(OnWeaponLoading, WeaponLoading.Priority.AutoDesignApplier); _weapons.Subscribe(OnWeaponLoading, WeaponLoading.Priority.AutoDesignApplier);
@ -91,7 +90,7 @@ public sealed class AutoDesignApplier : IDisposable
{ {
case EquipSlot.MainHand: case EquipSlot.MainHand:
{ {
if (_jobChangeMainhand.TryGetValue(current.Type, out var data)) if (_jobChange.TryGetValue(current.Type, out var data))
{ {
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Changing Mainhand from {_jobChangeState.ModelData.Weapon(EquipSlot.MainHand)} | {_jobChangeState.BaseData.Weapon(EquipSlot.MainHand)} to {data.Item1} for 0x{actor.Address:X}."); $"Changing Mainhand from {_jobChangeState.ModelData.Weapon(EquipSlot.MainHand)} | {_jobChangeState.BaseData.Weapon(EquipSlot.MainHand)} to {data.Item1} for 0x{actor.Address:X}.");
@ -103,7 +102,7 @@ public sealed class AutoDesignApplier : IDisposable
} }
case EquipSlot.OffHand when current.Type == _jobChangeState.BaseData.MainhandType.Offhand(): case EquipSlot.OffHand when current.Type == _jobChangeState.BaseData.MainhandType.Offhand():
{ {
if (_jobChangeOffhand.TryGetValue(current.Type, out var data)) if (_jobChange.TryGetValue(current.Type, out var data))
{ {
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Changing Offhand from {_jobChangeState.ModelData.Weapon(EquipSlot.OffHand)} | {_jobChangeState.BaseData.Weapon(EquipSlot.OffHand)} to {data.Item1} for 0x{actor.Address:X}."); $"Changing Offhand from {_jobChangeState.ModelData.Weapon(EquipSlot.OffHand)} | {_jobChangeState.BaseData.Weapon(EquipSlot.OffHand)} to {data.Item1} for 0x{actor.Address:X}.");
@ -270,11 +269,6 @@ public sealed class AutoDesignApplier : IDisposable
private unsafe void Reduce(Actor actor, ActorState state, AutoDesignSet set, bool respectManual, bool fromJobChange) private unsafe void Reduce(Actor actor, ActorState state, AutoDesignSet set, bool respectManual, bool fromJobChange)
{ {
EquipFlag totalEquipFlags = 0;
CustomizeFlag totalCustomizeFlags = 0;
CrestFlag totalCrestFlags = 0;
CustomizeParameterFlag totalParameterFlags = 0;
byte totalMetaFlags = 0;
if (set.BaseState == AutoDesignSet.Base.Game) if (set.BaseState == AutoDesignSet.Base.Game)
_state.ResetStateFixed(state, respectManual); _state.ResetStateFixed(state, respectManual);
else if (!respectManual) else if (!respectManual)
@ -283,30 +277,8 @@ public sealed class AutoDesignApplier : IDisposable
if (!_humans.IsHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId)) if (!_humans.IsHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId))
return; return;
foreach (var design in set.Designs) var mergedDesign = _designMerger.Merge(set.Designs.Where(d => d.IsActive(actor)).Select(d => ((DesignBase?) d.Design, d.Type)), state.ModelData, true);
{ ApplyToState(state, mergedDesign, respectManual, fromJobChange, StateChanged.Source.Fixed);
if (!design.IsActive(actor))
continue;
if (design.Type is 0)
continue;
ref readonly var data = ref design.GetDesignData(state);
var source = design.Revert ? StateChanged.Source.Game : StateChanged.Source.Fixed;
if (!data.IsHuman)
continue;
var (equipFlags, customizeFlags, crestFlags, parameterFlags, applyHat, applyVisor, applyWeapon, applyWet) = design.ApplyWhat();
ReduceMeta(state, data, applyHat, applyVisor, applyWeapon, applyWet, ref totalMetaFlags, respectManual, source);
ReduceCustomize(state, data, customizeFlags, ref totalCustomizeFlags, respectManual, source);
ReduceEquip(state, data, equipFlags, ref totalEquipFlags, respectManual, source, fromJobChange);
ReduceCrests(state, data, crestFlags, ref totalCrestFlags, respectManual, source);
ReduceParameters(state, data, parameterFlags, ref totalParameterFlags, respectManual, source);
}
if (totalCustomizeFlags != 0)
state.ModelData.ModelId = 0;
} }
/// <summary> Get world-specific first and all-world afterward. </summary> /// <summary> Get world-specific first and all-world afterward. </summary>
@ -332,39 +304,57 @@ public sealed class AutoDesignApplier : IDisposable
} }
} }
private void ReduceCrests(ActorState state, in DesignData design, CrestFlag crestFlags, ref CrestFlag totalCrestFlags, bool respectManual, private void ApplyToState(ActorState state, MergedDesign mergedDesign, bool respectManual, bool fromJobChange, StateChanged.Source source)
StateChanged.Source source)
{ {
crestFlags &= ~totalCrestFlags; foreach (var slot in CrestExtensions.AllRelevantSet.Where(mergedDesign.Design.DoApplyCrest))
if (crestFlags == 0)
return;
foreach (var slot in CrestExtensions.AllRelevantSet)
{
if (!crestFlags.HasFlag(slot))
continue;
if (!respectManual || state.Source[slot] is not StateChanged.Source.Manual) if (!respectManual || state.Source[slot] is not StateChanged.Source.Manual)
_state.ChangeCrest(state, slot, design.Crest(slot), source); _state.ChangeCrest(state, slot, mergedDesign.Design.DesignData.Crest(slot), mergedDesign.GetSource(slot, source));
totalCrestFlags |= slot;
}
}
private void ReduceParameters(ActorState state, in DesignData design, CustomizeParameterFlag parameterFlags, foreach (var parameter in mergedDesign.Design.ApplyParameters.Iterate())
ref CustomizeParameterFlag totalParameterFlags, bool respectManual, StateChanged.Source source) if (!respectManual || state.Source[parameter] is not StateChanged.Source.Manual and not StateChanged.Source.Pending)
{ _state.ChangeCustomizeParameter(state, parameter, mergedDesign.Design.DesignData.Parameters[parameter], mergedDesign.GetSource(parameter, source));
parameterFlags &= ~totalParameterFlags;
if (parameterFlags == 0)
return;
foreach (var flag in CustomizeParameterExtensions.AllFlags) foreach (var slot in EquipSlotExtensions.EqdpSlots)
{ {
if (!parameterFlags.HasFlag(flag)) if (mergedDesign.Design.DoApplyEquip(slot))
{
if (!respectManual || state.Source[slot, false] is not StateChanged.Source.Manual)
_state.ChangeItem(state, slot, mergedDesign.Design.DesignData.Item(slot), mergedDesign.GetSource(slot, false, source));
}
if (mergedDesign.Design.DoApplyStain(slot))
{
if (!respectManual || state.Source[slot, true] is not StateChanged.Source.Manual)
_state.ChangeStain(state, slot, mergedDesign.Design.DesignData.Stain(slot), mergedDesign.GetSource(slot, true, source));
}
}
foreach (var weaponSlot in EquipSlotExtensions.WeaponSlots)
{
if (mergedDesign.Design.DoApplyStain(weaponSlot))
{
if (!respectManual || state.Source[weaponSlot, true] is not StateChanged.Source.Manual)
_state.ChangeStain(state, weaponSlot, mergedDesign.Design.DesignData.Stain(weaponSlot), mergedDesign.GetSource(weaponSlot, true, source));
}
if (!mergedDesign.Design.DoApplyEquip(weaponSlot))
continue; continue;
if (!respectManual || state.Source[flag] is not StateChanged.Source.Manual and not StateChanged.Source.Pending) if (respectManual && state.Source[weaponSlot, false] is StateChanged.Source.Manual)
_state.ChangeCustomizeParameter(state, flag, design.Parameters[flag], source); continue;
totalParameterFlags |= flag;
var currentType = state.ModelData.Item(weaponSlot).Type;
if (fromJobChange)
{
foreach (var (key, (weapon, weaponSource)) in mergedDesign.Weapons)
if (key.ToSlot() == weaponSlot)
_jobChange.TryAdd(key, (weapon, MergedDesign.GetSource(weaponSource, source)));
_jobChangeState = state;
}
else if (mergedDesign.Weapons.TryGetValue(currentType, out var weapon))
{
_state.ChangeItem(state, weaponSlot, weapon.Item1, MergedDesign.GetSource(weapon.Item2, source));
}
} }
} }
@ -407,7 +397,7 @@ public sealed class AutoDesignApplier : IDisposable
{ {
if (fromJobChange) if (fromJobChange)
{ {
_jobChangeMainhand.TryAdd(item.Type, (item, source)); _jobChange.TryAdd(item.Type, (item, source));
_jobChangeState = state; _jobChangeState = state;
} }
else if (state.ModelData.Item(EquipSlot.MainHand).Type == item.Type) else if (state.ModelData.Item(EquipSlot.MainHand).Type == item.Type)
@ -427,7 +417,7 @@ public sealed class AutoDesignApplier : IDisposable
{ {
if (fromJobChange) if (fromJobChange)
{ {
_jobChangeOffhand.TryAdd(item.Type, (item, source)); _jobChange.TryAdd(item.Type, (item, source));
_jobChangeState = state; _jobChangeState = state;
} }
else if (state.ModelData.Item(EquipSlot.OffHand).Type == item.Type) else if (state.ModelData.Item(EquipSlot.OffHand).Type == item.Type)

View file

@ -4,6 +4,7 @@ using Glamourer.GameData;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.State; using Glamourer.State;
using Glamourer.Unlocks; using Glamourer.Unlocks;
using OtterGui.Services;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
@ -54,7 +55,7 @@ public class DesignMerger(
CustomizeService _customize, CustomizeService _customize,
Configuration _config, Configuration _config,
ItemUnlockManager _itemUnlocks, ItemUnlockManager _itemUnlocks,
CustomizeUnlockManager _customizeUnlocks) CustomizeUnlockManager _customizeUnlocks) : IService
{ {
public MergedDesign Merge(IEnumerable<(DesignBase?, ApplicationType)> designs, in DesignData baseRef, bool respectOwnership) public MergedDesign Merge(IEnumerable<(DesignBase?, ApplicationType)> designs, in DesignData baseRef, bool respectOwnership)
{ {

View file

@ -0,0 +1,45 @@
using Glamourer.Events;
using Glamourer.GameData;
using Glamourer.State;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
namespace Glamourer.Designs.Links;
public sealed class MergedDesign
{
public MergedDesign(DesignManager designManager)
{
Design = designManager.CreateTemporary();
Design.ApplyEquip = 0;
Design.ApplyCustomize = 0;
Design.ApplyCrest = 0;
Design.ApplyParameters = 0;
Design.SetApplyWetness(false);
Design.SetApplyVisorToggle(false);
Design.SetApplyWeaponVisible(false);
Design.SetApplyHatVisible(false);
}
public readonly DesignBase Design;
public readonly Dictionary<FullEquipType, (EquipItem, StateChanged.Source)> Weapons = new(4);
public readonly StateSource Source = new();
public StateChanged.Source GetSource(EquipSlot slot, bool stain, StateChanged.Source actualSource)
=> GetSource(Source[slot, stain], actualSource);
public StateChanged.Source GetSource(CrestFlag slot, StateChanged.Source actualSource)
=> GetSource(Source[slot], actualSource);
public StateChanged.Source GetSource(CustomizeIndex type, StateChanged.Source actualSource)
=> GetSource(Source[type], actualSource);
public StateChanged.Source GetSource(MetaIndex index, StateChanged.Source actualSource)
=> GetSource(Source[index], actualSource);
public StateChanged.Source GetSource(CustomizeParameterFlag flag, StateChanged.Source actualSource)
=> GetSource(Source[flag], actualSource);
public static StateChanged.Source GetSource(StateChanged.Source given, StateChanged.Source actualSource)
=> given is StateChanged.Source.Game ? StateChanged.Source.Game : actualSource;
}