mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-14 20:54:17 +01:00
Make states work.
This commit is contained in:
parent
25ddbb1310
commit
a4de13f228
27 changed files with 787 additions and 857 deletions
|
|
@ -130,7 +130,7 @@ public partial class GlamourerIpc
|
||||||
|
|
||||||
if ((hasModelId || state.ModelData.ModelId == 0) && state.CanUnlock(lockCode))
|
if ((hasModelId || state.ModelData.ModelId == 0) && state.CanUnlock(lockCode))
|
||||||
{
|
{
|
||||||
_stateManager.ApplyDesign(design, state, StateSource.Ipc, lockCode);
|
_stateManager.ApplyDesign(state, design, new ApplySettings(Source:StateSource.Ipc, Key:lockCode));
|
||||||
state.Lock(lockCode);
|
state.Lock(lockCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using Dalamud.Game.ClientState.Objects.Types;
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
|
using Glamourer.Designs;
|
||||||
using Glamourer.Events;
|
using Glamourer.Events;
|
||||||
using Glamourer.Services;
|
using Glamourer.Services;
|
||||||
using Glamourer.State;
|
using Glamourer.State;
|
||||||
|
|
@ -56,7 +57,7 @@ public partial class GlamourerIpc
|
||||||
if (!state.ModelData.IsHuman)
|
if (!state.ModelData.IsHuman)
|
||||||
return GlamourerErrorCode.ActorNotHuman;
|
return GlamourerErrorCode.ActorNotHuman;
|
||||||
|
|
||||||
_stateManager.ChangeEquip(state, slot, item, stainId, StateSource.Ipc, key);
|
_stateManager.ChangeEquip(state, slot, item, stainId, new ApplySettings(Source: StateSource.Ipc, Key:key));
|
||||||
return GlamourerErrorCode.Success;
|
return GlamourerErrorCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,7 +84,7 @@ public partial class GlamourerIpc
|
||||||
if (!state.ModelData.IsHuman)
|
if (!state.ModelData.IsHuman)
|
||||||
return GlamourerErrorCode.ActorNotHuman;
|
return GlamourerErrorCode.ActorNotHuman;
|
||||||
|
|
||||||
_stateManager.ChangeEquip(state, slot, item, stainId, StateSource.Ipc, key);
|
_stateManager.ChangeEquip(state, slot, item, stainId, new ApplySettings(Source: StateSource.Ipc, Key: key));
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,9 @@ using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
||||||
using Glamourer.Designs;
|
using Glamourer.Designs;
|
||||||
using Glamourer.Designs.Links;
|
using Glamourer.Designs.Links;
|
||||||
using Glamourer.Events;
|
using Glamourer.Events;
|
||||||
using Glamourer.GameData;
|
|
||||||
using Glamourer.Interop;
|
using Glamourer.Interop;
|
||||||
using Glamourer.Interop.Structs;
|
using Glamourer.Interop.Structs;
|
||||||
using Glamourer.Services;
|
|
||||||
using Glamourer.State;
|
using Glamourer.State;
|
||||||
using Glamourer.Unlocks;
|
|
||||||
using Penumbra.GameData.Actors;
|
using Penumbra.GameData.Actors;
|
||||||
using Penumbra.GameData.DataContainers;
|
using Penumbra.GameData.DataContainers;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
|
|
@ -18,52 +15,39 @@ namespace Glamourer.Automation;
|
||||||
|
|
||||||
public sealed class AutoDesignApplier : IDisposable
|
public sealed class AutoDesignApplier : IDisposable
|
||||||
{
|
{
|
||||||
private readonly Configuration _config;
|
private readonly Configuration _config;
|
||||||
private readonly AutoDesignManager _manager;
|
private readonly AutoDesignManager _manager;
|
||||||
private readonly StateManager _state;
|
private readonly StateManager _state;
|
||||||
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 AutomationChanged _event;
|
||||||
private readonly CustomizeUnlockManager _customizeUnlocks;
|
private readonly ObjectManager _objects;
|
||||||
private readonly ItemUnlockManager _itemUnlocks;
|
private readonly WeaponLoading _weapons;
|
||||||
private readonly AutomationChanged _event;
|
private readonly HumanModelList _humans;
|
||||||
private readonly ObjectManager _objects;
|
private readonly DesignMerger _designMerger;
|
||||||
private readonly WeaponLoading _weapons;
|
private readonly IClientState _clientState;
|
||||||
private readonly HumanModelList _humans;
|
|
||||||
private readonly DesignMerger _designMerger;
|
|
||||||
private readonly IClientState _clientState;
|
|
||||||
|
|
||||||
private ActorState? _jobChangeState;
|
private readonly JobChangeState _jobChangeState;
|
||||||
private readonly Dictionary<FullEquipType, (EquipItem, StateSource)> _jobChange = [];
|
|
||||||
|
|
||||||
private void ResetJobChange()
|
public AutoDesignApplier(Configuration config, AutoDesignManager manager, StateManager state, JobService jobs, ActorManager actors,
|
||||||
{
|
|
||||||
_jobChangeState = null;
|
|
||||||
_jobChange.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public AutoDesignApplier(Configuration config, AutoDesignManager manager, StateManager state, JobService jobs,
|
|
||||||
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, DesignMerger designMerger)
|
EquippedGearset equippedGearset, DesignMerger designMerger, JobChangeState jobChangeState)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
_manager = manager;
|
_manager = manager;
|
||||||
_state = state;
|
_state = state;
|
||||||
_jobs = jobs;
|
_jobs = jobs;
|
||||||
_customizations = customizations;
|
_actors = actors;
|
||||||
_actors = actors;
|
_event = @event;
|
||||||
_itemUnlocks = itemUnlocks;
|
_objects = objects;
|
||||||
_customizeUnlocks = customizeUnlocks;
|
_weapons = weapons;
|
||||||
_event = @event;
|
_humans = humans;
|
||||||
_objects = objects;
|
_clientState = clientState;
|
||||||
_weapons = weapons;
|
_equippedGearset = equippedGearset;
|
||||||
_humans = humans;
|
_designMerger = designMerger;
|
||||||
_clientState = clientState;
|
_jobChangeState = jobChangeState;
|
||||||
_equippedGearset = equippedGearset;
|
_jobs.JobChanged += OnJobChange;
|
||||||
_designMerger = designMerger;
|
|
||||||
_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);
|
||||||
_equippedGearset.Subscribe(OnEquippedGearset, EquippedGearset.Priority.AutoDesignApplier);
|
_equippedGearset.Subscribe(OnEquippedGearset, EquippedGearset.Priority.AutoDesignApplier);
|
||||||
|
|
@ -79,45 +63,46 @@ public sealed class AutoDesignApplier : IDisposable
|
||||||
|
|
||||||
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.HasState || !_config.EnableAutoDesigns)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var id = actor.GetIdentifier(_actors);
|
var id = actor.GetIdentifier(_actors);
|
||||||
if (id == _jobChangeState.Identifier)
|
if (id == _jobChangeState.Identifier)
|
||||||
{
|
{
|
||||||
var current = _jobChangeState.BaseData.Item(slot);
|
var state = _jobChangeState.State!;
|
||||||
|
var current = state.BaseData.Item(slot);
|
||||||
switch (slot)
|
switch (slot)
|
||||||
{
|
{
|
||||||
case EquipSlot.MainHand:
|
case EquipSlot.MainHand:
|
||||||
{
|
{
|
||||||
if (_jobChange.TryGetValue(current.Type, out var data))
|
if (_jobChangeState.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 {state.ModelData.Weapon(EquipSlot.MainHand)} | {state.BaseData.Weapon(EquipSlot.MainHand)} to {data.Item1} for 0x{actor.Address:X}.");
|
||||||
_state.ChangeItem(_jobChangeState, EquipSlot.MainHand, data.Item1, data.Item2);
|
_state.ChangeItem(_jobChangeState, EquipSlot.MainHand, data.Item1, new ApplySettings(Source: data.Item2));
|
||||||
weapon = _jobChangeState.ModelData.Weapon(EquipSlot.MainHand);
|
weapon = state.ModelData.Weapon(EquipSlot.MainHand);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EquipSlot.OffHand when current.Type == _jobChangeState.BaseData.MainhandType.Offhand():
|
case EquipSlot.OffHand when current.Type == state.BaseData.MainhandType.Offhand():
|
||||||
{
|
{
|
||||||
if (_jobChange.TryGetValue(current.Type, out var data))
|
if (_jobChangeState.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 {state.ModelData.Weapon(EquipSlot.OffHand)} | {state.BaseData.Weapon(EquipSlot.OffHand)} to {data.Item1} for 0x{actor.Address:X}.");
|
||||||
_state.ChangeItem(_jobChangeState, EquipSlot.OffHand, data.Item1, data.Item2);
|
_state.ChangeItem(_jobChangeState, EquipSlot.OffHand, data.Item1, new ApplySettings(Source: data.Item2));
|
||||||
weapon = _jobChangeState.ModelData.Weapon(EquipSlot.OffHand);
|
weapon = state.ModelData.Weapon(EquipSlot.OffHand);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResetJobChange();
|
_jobChangeState.Reset();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ResetJobChange();
|
_jobChangeState.Reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,7 +120,7 @@ public sealed class AutoDesignApplier : IDisposable
|
||||||
break;
|
break;
|
||||||
case AutomationChanged.Type.ChangeIdentifier when set.Enabled:
|
case AutomationChanged.Type.ChangeIdentifier when set.Enabled:
|
||||||
// Remove fixed state from the old identifiers assigned and the old enabled set, if any.
|
// Remove fixed state from the old identifiers assigned and the old enabled set, if any.
|
||||||
var (oldIds, _, oldSet) = ((ActorIdentifier[], ActorIdentifier, AutoDesignSet?))bonusData!;
|
var (oldIds, _, _) = ((ActorIdentifier[], ActorIdentifier, AutoDesignSet?))bonusData!;
|
||||||
RemoveOld(oldIds);
|
RemoveOld(oldIds);
|
||||||
ApplyNew(set); // Does not need to disable oldSet because same identifiers.
|
ApplyNew(set); // Does not need to disable oldSet because same identifiers.
|
||||||
break;
|
break;
|
||||||
|
|
@ -277,8 +262,9 @@ public sealed class AutoDesignApplier : IDisposable
|
||||||
if (!_humans.IsHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId))
|
if (!_humans.IsHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var mergedDesign = _designMerger.Merge(set.Designs.Where(d => d.IsActive(actor)).Select(d => ((DesignBase?) d.Design, d.Type)), state.ModelData, true, false);
|
var mergedDesign = _designMerger.Merge(set.Designs.Where(d => d.IsActive(actor)).Select(d => ((DesignBase?)d.Design, d.Type)),
|
||||||
ApplyToState(state, mergedDesign, respectManual, fromJobChange, StateSource.Fixed);
|
state.ModelData, true, false);
|
||||||
|
_state.ApplyDesign(state, mergedDesign, new ApplySettings(0, StateSource.Fixed, respectManual, fromJobChange, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Get world-specific first and all-world afterward. </summary>
|
/// <summary> Get world-specific first and all-world afterward. </summary>
|
||||||
|
|
@ -304,218 +290,6 @@ public sealed class AutoDesignApplier : IDisposable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyToState(ActorState state, MergedDesign mergedDesign, bool respectManual, bool fromJobChange, StateSource source)
|
|
||||||
{
|
|
||||||
foreach (var slot in CrestExtensions.AllRelevantSet.Where(mergedDesign.Design.DoApplyCrest))
|
|
||||||
if (!respectManual || state.Sources[slot] is not StateSource.Manual)
|
|
||||||
_state.ChangeCrest(state, slot, mergedDesign.Design.DesignData.Crest(slot), mergedDesign.GetSource(slot, source));
|
|
||||||
|
|
||||||
foreach (var parameter in mergedDesign.Design.ApplyParameters.Iterate())
|
|
||||||
if (!respectManual || state.Sources[parameter] is not StateSource.Manual and not StateSource.Pending)
|
|
||||||
_state.ChangeCustomizeParameter(state, parameter, mergedDesign.Design.DesignData.Parameters[parameter], mergedDesign.GetSource(parameter, source));
|
|
||||||
|
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
|
||||||
{
|
|
||||||
if (mergedDesign.Design.DoApplyEquip(slot))
|
|
||||||
{
|
|
||||||
if (!respectManual || state.Sources[slot, false] is not StateSource.Manual)
|
|
||||||
_state.ChangeItem(state, slot, mergedDesign.Design.DesignData.Item(slot), mergedDesign.GetSource(slot, false, source));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mergedDesign.Design.DoApplyStain(slot))
|
|
||||||
{
|
|
||||||
if (!respectManual || state.Sources[slot, true] is not StateSource.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.Sources[weaponSlot, true] is not StateSource.Manual)
|
|
||||||
_state.ChangeStain(state, weaponSlot, mergedDesign.Design.DesignData.Stain(weaponSlot), mergedDesign.GetSource(weaponSlot, true, source));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mergedDesign.Design.DoApplyEquip(weaponSlot))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (respectManual && state.Sources[weaponSlot, false] is StateSource.Manual)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReduceEquip(ActorState state, in DesignData design, EquipFlag equipFlags, ref EquipFlag totalEquipFlags, bool respectManual,
|
|
||||||
StateSource source, bool fromJobChange)
|
|
||||||
{
|
|
||||||
equipFlags &= ~totalEquipFlags;
|
|
||||||
if (equipFlags == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
|
||||||
{
|
|
||||||
var flag = slot.ToFlag();
|
|
||||||
if (equipFlags.HasFlag(flag))
|
|
||||||
{
|
|
||||||
var item = design.Item(slot);
|
|
||||||
if (!_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.Id, out _))
|
|
||||||
{
|
|
||||||
if (!respectManual || state.Sources[slot, false] is not StateSource.Manual)
|
|
||||||
_state.ChangeItem(state, slot, item, source);
|
|
||||||
totalEquipFlags |= flag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var stainFlag = slot.ToStainFlag();
|
|
||||||
if (equipFlags.HasFlag(stainFlag))
|
|
||||||
{
|
|
||||||
if (!respectManual || state.Sources[slot, true] is not StateSource.Manual)
|
|
||||||
_state.ChangeStain(state, slot, design.Stain(slot), source);
|
|
||||||
totalEquipFlags |= stainFlag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (equipFlags.HasFlag(EquipFlag.Mainhand))
|
|
||||||
{
|
|
||||||
var item = design.Item(EquipSlot.MainHand);
|
|
||||||
var checkUnlock = !_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.Id, out _);
|
|
||||||
var checkState = !respectManual || state.Sources[EquipSlot.MainHand, false] is not StateSource.Manual;
|
|
||||||
if (checkUnlock && checkState)
|
|
||||||
{
|
|
||||||
if (fromJobChange)
|
|
||||||
{
|
|
||||||
_jobChange.TryAdd(item.Type, (item, source));
|
|
||||||
_jobChangeState = state;
|
|
||||||
}
|
|
||||||
else if (state.ModelData.Item(EquipSlot.MainHand).Type == item.Type)
|
|
||||||
{
|
|
||||||
_state.ChangeItem(state, EquipSlot.MainHand, item, source);
|
|
||||||
totalEquipFlags |= EquipFlag.Mainhand;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (equipFlags.HasFlag(EquipFlag.Offhand))
|
|
||||||
{
|
|
||||||
var item = design.Item(EquipSlot.OffHand);
|
|
||||||
var checkUnlock = !_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.Id, out _);
|
|
||||||
var checkState = !respectManual || state.Sources[EquipSlot.OffHand, false] is not StateSource.Manual;
|
|
||||||
if (checkUnlock && checkState)
|
|
||||||
{
|
|
||||||
if (fromJobChange)
|
|
||||||
{
|
|
||||||
_jobChange.TryAdd(item.Type, (item, source));
|
|
||||||
_jobChangeState = state;
|
|
||||||
}
|
|
||||||
else if (state.ModelData.Item(EquipSlot.OffHand).Type == item.Type)
|
|
||||||
{
|
|
||||||
_state.ChangeItem(state, EquipSlot.OffHand, item, source);
|
|
||||||
totalEquipFlags |= EquipFlag.Offhand;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (equipFlags.HasFlag(EquipFlag.MainhandStain))
|
|
||||||
{
|
|
||||||
if (!respectManual || state.Sources[EquipSlot.MainHand, true] is not StateSource.Manual)
|
|
||||||
_state.ChangeStain(state, EquipSlot.MainHand, design.Stain(EquipSlot.MainHand), source);
|
|
||||||
totalEquipFlags |= EquipFlag.MainhandStain;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (equipFlags.HasFlag(EquipFlag.OffhandStain))
|
|
||||||
{
|
|
||||||
if (!respectManual || state.Sources[EquipSlot.OffHand, true] is not StateSource.Manual)
|
|
||||||
_state.ChangeStain(state, EquipSlot.OffHand, design.Stain(EquipSlot.OffHand), source);
|
|
||||||
totalEquipFlags |= EquipFlag.OffhandStain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReduceCustomize(ActorState state, in DesignData design, CustomizeFlag customizeFlags, ref CustomizeFlag totalCustomizeFlags,
|
|
||||||
bool respectManual, StateSource source)
|
|
||||||
{
|
|
||||||
customizeFlags &= ~totalCustomizeFlags;
|
|
||||||
if (customizeFlags == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var customize = state.ModelData.Customize;
|
|
||||||
CustomizeFlag fixFlags = 0;
|
|
||||||
|
|
||||||
// Skip anything not human.
|
|
||||||
if (!state.ModelData.IsHuman || !design.IsHuman)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (customizeFlags.HasFlag(CustomizeFlag.Clan))
|
|
||||||
{
|
|
||||||
if (!respectManual || state.Sources[CustomizeIndex.Clan] is not StateSource.Manual)
|
|
||||||
fixFlags |= _customizations.ChangeClan(ref customize, design.Customize.Clan);
|
|
||||||
customizeFlags &= ~(CustomizeFlag.Clan | CustomizeFlag.Race);
|
|
||||||
totalCustomizeFlags |= CustomizeFlag.Clan | CustomizeFlag.Race;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (customizeFlags.HasFlag(CustomizeFlag.Gender))
|
|
||||||
{
|
|
||||||
if (!respectManual || state.Sources[CustomizeIndex.Gender] is not StateSource.Manual)
|
|
||||||
fixFlags |= _customizations.ChangeGender(ref customize, design.Customize.Gender);
|
|
||||||
customizeFlags &= ~CustomizeFlag.Gender;
|
|
||||||
totalCustomizeFlags |= CustomizeFlag.Gender;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fixFlags != 0)
|
|
||||||
_state.ChangeCustomize(state, customize, fixFlags, source);
|
|
||||||
|
|
||||||
if (customizeFlags.HasFlag(CustomizeFlag.Face))
|
|
||||||
{
|
|
||||||
if (!respectManual || state.Sources[CustomizeIndex.Face] is not StateSource.Manual)
|
|
||||||
_state.ChangeCustomize(state, CustomizeIndex.Face, design.Customize.Face, source);
|
|
||||||
customizeFlags &= ~CustomizeFlag.Face;
|
|
||||||
totalCustomizeFlags |= CustomizeFlag.Face;
|
|
||||||
}
|
|
||||||
|
|
||||||
var set = _customizations.Manager.GetSet(state.ModelData.Customize.Clan, state.ModelData.Customize.Gender);
|
|
||||||
var face = state.ModelData.Customize.Face;
|
|
||||||
foreach (var index in Enum.GetValues<CustomizeIndex>())
|
|
||||||
{
|
|
||||||
var flag = index.ToFlag();
|
|
||||||
if (!customizeFlags.HasFlag(flag))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var value = design.Customize[index];
|
|
||||||
if (CustomizeService.IsCustomizationValid(set, face, index, value, out var data))
|
|
||||||
{
|
|
||||||
if (data.HasValue && _config.UnlockedItemMode && !_customizeUnlocks.IsUnlocked(data.Value, out _))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!respectManual || state.Sources[index] is not StateSource.Manual)
|
|
||||||
_state.ChangeCustomize(state, index, value, source);
|
|
||||||
totalCustomizeFlags |= flag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReduceMeta(ActorState state, in DesignData design, MetaFlag apply, ref MetaFlag totalMetaFlags, bool respectManual, StateSource source)
|
|
||||||
{
|
|
||||||
apply &= ~totalMetaFlags;
|
|
||||||
foreach (var index in MetaExtensions.AllRelevant)
|
|
||||||
{
|
|
||||||
if (!respectManual || state.Sources[index] is not StateSource.Manual)
|
|
||||||
_state.ChangeMeta(state, index, design.GetMeta(index), source);
|
|
||||||
totalMetaFlags |= index.ToFlag();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static int NewGearsetId = -1;
|
internal static int NewGearsetId = -1;
|
||||||
|
|
||||||
private void OnEquippedGearset(string name, int id, int prior, byte _, byte job)
|
private void OnEquippedGearset(string name, int id, int prior, byte _, byte job)
|
||||||
|
|
|
||||||
|
|
@ -254,12 +254,11 @@ public class DesignEditor(
|
||||||
_forceFullItemOff = true;
|
_forceFullItemOff = true;
|
||||||
foreach (var slot in EquipSlotExtensions.FullSlots)
|
foreach (var slot in EquipSlotExtensions.FullSlots)
|
||||||
{
|
{
|
||||||
if (other.DoApplyEquip(slot))
|
ChangeEquip(design, slot,
|
||||||
ChangeItem(design, slot, other.DesignData.Item(slot));
|
other.DoApplyEquip(slot) ? other.DesignData.Item(slot) : null,
|
||||||
|
other.DoApplyStain(slot) ? other.DesignData.Stain(slot) : null);
|
||||||
if (other.DoApplyStain(slot))
|
|
||||||
ChangeStain(design, slot, other.DesignData.Stain(slot));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_forceFullItemOff = false;
|
_forceFullItemOff = false;
|
||||||
|
|
||||||
foreach (var slot in Enum.GetValues<CrestFlag>().Where(other.DoApplyCrest))
|
foreach (var slot in Enum.GetValues<CrestFlag>().Where(other.DoApplyCrest))
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ using Penumbra.GameData.Enums;
|
||||||
|
|
||||||
namespace Glamourer.Designs;
|
namespace Glamourer.Designs;
|
||||||
|
|
||||||
public class DesignManager : DesignEditor
|
public sealed class DesignManager : DesignEditor
|
||||||
{
|
{
|
||||||
public readonly DesignStorage Designs;
|
public readonly DesignStorage Designs;
|
||||||
private readonly HumanModelList _humans;
|
private readonly HumanModelList _humans;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,26 @@ public readonly record struct ApplySettings(
|
||||||
StateSource Source = StateSource.Manual,
|
StateSource Source = StateSource.Manual,
|
||||||
bool RespectManual = false,
|
bool RespectManual = false,
|
||||||
bool FromJobChange = false,
|
bool FromJobChange = false,
|
||||||
bool UseSingleSource = false);
|
bool UseSingleSource = false)
|
||||||
|
{
|
||||||
|
public static readonly ApplySettings Manual = new()
|
||||||
|
{
|
||||||
|
Key = 0,
|
||||||
|
Source = StateSource.Manual,
|
||||||
|
FromJobChange = false,
|
||||||
|
RespectManual = false,
|
||||||
|
UseSingleSource = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static readonly ApplySettings Game = new()
|
||||||
|
{
|
||||||
|
Key = 0,
|
||||||
|
Source = StateSource.Game,
|
||||||
|
FromJobChange = false,
|
||||||
|
RespectManual = false,
|
||||||
|
UseSingleSource = false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public interface IDesignEditor
|
public interface IDesignEditor
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -224,7 +224,7 @@ public class DesignMerger(
|
||||||
ret.Sources[CustomizeIndex.Face] = source;
|
ret.Sources[CustomizeIndex.Face] = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
var set = ret.Design.CustomizeSet;
|
var set = _customize.Manager.GetSet(customize.Clan, customize.Gender);
|
||||||
var face = customize.Face;
|
var face = customize.Face;
|
||||||
foreach (var index in Enum.GetValues<CustomizeIndex>())
|
foreach (var index in Enum.GetValues<CustomizeIndex>())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -48,22 +48,4 @@ public sealed class MergedDesign
|
||||||
public readonly Dictionary<FullEquipType, (EquipItem, StateSource)> Weapons = new(4);
|
public readonly Dictionary<FullEquipType, (EquipItem, StateSource)> Weapons = new(4);
|
||||||
public readonly SortedList<Mod, ModSettings> AssociatedMods = [];
|
public readonly SortedList<Mod, ModSettings> AssociatedMods = [];
|
||||||
public StateSources Sources = new();
|
public StateSources Sources = new();
|
||||||
|
|
||||||
public StateSource GetSource(EquipSlot slot, bool stain, StateSource actualSource)
|
|
||||||
=> GetSource(Sources[slot, stain], actualSource);
|
|
||||||
|
|
||||||
public StateSource GetSource(CrestFlag slot, StateSource actualSource)
|
|
||||||
=> GetSource(Sources[slot], actualSource);
|
|
||||||
|
|
||||||
public StateSource GetSource(CustomizeIndex type, StateSource actualSource)
|
|
||||||
=> GetSource(Sources[type], actualSource);
|
|
||||||
|
|
||||||
public StateSource GetSource(MetaIndex index, StateSource actualSource)
|
|
||||||
=> GetSource(Sources[index], actualSource);
|
|
||||||
|
|
||||||
public StateSource GetSource(CustomizeParameterFlag flag, StateSource actualSource)
|
|
||||||
=> GetSource(Sources[flag], actualSource);
|
|
||||||
|
|
||||||
public static StateSource GetSource(StateSource given, StateSource actualSource)
|
|
||||||
=> given is StateSource.Game ? StateSource.Game : actualSource;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ public ref struct CustomizeParameterDrawData(CustomizeParameterFlag flag, in Des
|
||||||
{
|
{
|
||||||
Locked = state.IsLocked,
|
Locked = state.IsLocked,
|
||||||
DisplayApplication = false,
|
DisplayApplication = false,
|
||||||
ValueSetter = v => manager.ChangeCustomizeParameter(state, flag, v, StateSource.Manual),
|
ValueSetter = v => manager.ChangeCustomizeParameter(state, flag, v, ApplySettings.Manual),
|
||||||
GameValue = state.BaseData.Parameters[flag],
|
GameValue = state.BaseData.Parameters[flag],
|
||||||
AllowRevert = true,
|
AllowRevert = true,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Glamourer.Automation;
|
using Glamourer.Automation;
|
||||||
using Glamourer.Events;
|
using Glamourer.Designs;
|
||||||
using Glamourer.Interop;
|
using Glamourer.Interop;
|
||||||
using Glamourer.Interop.Structs;
|
using Glamourer.Interop.Structs;
|
||||||
using Glamourer.State;
|
using Glamourer.State;
|
||||||
|
|
@ -32,7 +32,7 @@ public sealed class DesignQuickBar : Window, IDisposable
|
||||||
private readonly ImRaii.Style _windowPadding = new();
|
private readonly ImRaii.Style _windowPadding = new();
|
||||||
private readonly ImRaii.Color _windowColor = new();
|
private readonly ImRaii.Color _windowColor = new();
|
||||||
private DateTime _keyboardToggle = DateTime.UnixEpoch;
|
private DateTime _keyboardToggle = DateTime.UnixEpoch;
|
||||||
private int _numButtons = 0;
|
private int _numButtons;
|
||||||
|
|
||||||
public DesignQuickBar(Configuration config, DesignCombo designCombo, StateManager stateManager, IKeyState keyState,
|
public DesignQuickBar(Configuration config, DesignCombo designCombo, StateManager stateManager, IKeyState keyState,
|
||||||
ObjectManager objects, AutoDesignApplier autoDesignApplier)
|
ObjectManager objects, AutoDesignApplier autoDesignApplier)
|
||||||
|
|
@ -163,7 +163,7 @@ public sealed class DesignQuickBar : Window, IDisposable
|
||||||
|
|
||||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||||
using var _ = design!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest, applyParameters);
|
using var _ = design!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest, applyParameters);
|
||||||
_stateManager.ApplyDesign(design, state, StateSource.Manual);
|
_stateManager.ApplyDesign(state, design, ApplySettings.Manual);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawRevertButton(Vector2 buttonSize)
|
public void DrawRevertButton(Vector2 buttonSize)
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,8 @@ public ref struct EquipDrawData(EquipSlot slot, in DesignData designData)
|
||||||
public static EquipDrawData FromState(StateManager manager, ActorState state, EquipSlot slot)
|
public static EquipDrawData FromState(StateManager manager, ActorState state, EquipSlot slot)
|
||||||
=> new(slot, state.ModelData)
|
=> new(slot, state.ModelData)
|
||||||
{
|
{
|
||||||
ItemSetter = i => manager.ChangeItem(state, slot, i, StateSource.Manual),
|
ItemSetter = i => manager.ChangeItem(state, slot, i, ApplySettings.Manual),
|
||||||
StainSetter = i => manager.ChangeStain(state, slot, i, StateSource.Manual),
|
StainSetter = i => manager.ChangeStain(state, slot, i, ApplySettings.Manual),
|
||||||
Locked = state.IsLocked,
|
Locked = state.IsLocked,
|
||||||
DisplayApplication = false,
|
DisplayApplication = false,
|
||||||
GameItem = state.BaseData.Item(slot),
|
GameItem = state.BaseData.Item(slot),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Glamourer.Events;
|
using Glamourer.Designs;
|
||||||
using Glamourer.Interop;
|
using Glamourer.Interop;
|
||||||
using Glamourer.Interop.Penumbra;
|
using Glamourer.Interop.Penumbra;
|
||||||
using Glamourer.Services;
|
using Glamourer.Services;
|
||||||
|
|
@ -11,7 +11,7 @@ using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
namespace Glamourer.Gui;
|
namespace Glamourer.Gui;
|
||||||
|
|
||||||
public class PenumbraChangedItemTooltip : IDisposable
|
public sealed class PenumbraChangedItemTooltip : IDisposable
|
||||||
{
|
{
|
||||||
private readonly PenumbraService _penumbra;
|
private readonly PenumbraService _penumbra;
|
||||||
private readonly StateManager _stateManager;
|
private readonly StateManager _stateManager;
|
||||||
|
|
@ -111,24 +111,24 @@ public class PenumbraChangedItemTooltip : IDisposable
|
||||||
switch (ImGui.GetIO().KeyCtrl, ImGui.GetIO().KeyShift)
|
switch (ImGui.GetIO().KeyCtrl, ImGui.GetIO().KeyShift)
|
||||||
{
|
{
|
||||||
case (false, false):
|
case (false, false):
|
||||||
Glamourer.Log.Information($"Applying {item.Name} to Right Finger.");
|
Glamourer.Log.Debug($"Applying {item.Name} to Right Finger.");
|
||||||
SetLastItem(EquipSlot.RFinger, item, state);
|
SetLastItem(EquipSlot.RFinger, item, state);
|
||||||
_stateManager.ChangeItem(state, EquipSlot.RFinger, item, StateSource.Manual);
|
_stateManager.ChangeItem(state, EquipSlot.RFinger, item, ApplySettings.Manual);
|
||||||
break;
|
break;
|
||||||
case (false, true):
|
case (false, true):
|
||||||
Glamourer.Log.Information($"Applying {item.Name} to Left Finger.");
|
Glamourer.Log.Debug($"Applying {item.Name} to Left Finger.");
|
||||||
SetLastItem(EquipSlot.LFinger, item, state);
|
SetLastItem(EquipSlot.LFinger, item, state);
|
||||||
_stateManager.ChangeItem(state, EquipSlot.LFinger, item, StateSource.Manual);
|
_stateManager.ChangeItem(state, EquipSlot.LFinger, item, ApplySettings.Manual);
|
||||||
break;
|
break;
|
||||||
case (true, false) when last.Valid:
|
case (true, false) when last.Valid:
|
||||||
Glamourer.Log.Information($"Re-Applying {last.Name} to Right Finger.");
|
Glamourer.Log.Debug($"Re-Applying {last.Name} to Right Finger.");
|
||||||
SetLastItem(EquipSlot.RFinger, default, state);
|
SetLastItem(EquipSlot.RFinger, default, state);
|
||||||
_stateManager.ChangeItem(state, EquipSlot.RFinger, last, StateSource.Manual);
|
_stateManager.ChangeItem(state, EquipSlot.RFinger, last, ApplySettings.Manual);
|
||||||
break;
|
break;
|
||||||
case (true, true) when _lastItems[EquipSlot.LFinger.ToIndex()].Valid:
|
case (true, true) when _lastItems[EquipSlot.LFinger.ToIndex()].Valid:
|
||||||
Glamourer.Log.Information($"Re-Applying {last.Name} to Left Finger.");
|
Glamourer.Log.Debug($"Re-Applying {last.Name} to Left Finger.");
|
||||||
SetLastItem(EquipSlot.LFinger, default, state);
|
SetLastItem(EquipSlot.LFinger, default, state);
|
||||||
_stateManager.ChangeItem(state, EquipSlot.LFinger, last, StateSource.Manual);
|
_stateManager.ChangeItem(state, EquipSlot.LFinger, last, ApplySettings.Manual);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,15 +136,15 @@ public class PenumbraChangedItemTooltip : IDisposable
|
||||||
default:
|
default:
|
||||||
if (ImGui.GetIO().KeyCtrl && last.Valid)
|
if (ImGui.GetIO().KeyCtrl && last.Valid)
|
||||||
{
|
{
|
||||||
Glamourer.Log.Information($"Re-Applying {last.Name} to {slot.ToName()}.");
|
Glamourer.Log.Debug($"Re-Applying {last.Name} to {slot.ToName()}.");
|
||||||
SetLastItem(slot, default, state);
|
SetLastItem(slot, default, state);
|
||||||
_stateManager.ChangeItem(state, slot, last, StateSource.Manual);
|
_stateManager.ChangeItem(state, slot, last, ApplySettings.Manual);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Glamourer.Log.Information($"Applying {item.Name} to {slot.ToName()}.");
|
Glamourer.Log.Debug($"Applying {item.Name} to {slot.ToName()}.");
|
||||||
SetLastItem(slot, item, state);
|
SetLastItem(slot, item, state);
|
||||||
_stateManager.ChangeItem(state, slot, item, StateSource.Manual);
|
_stateManager.ChangeItem(state, slot, item, ApplySettings.Manual);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ using Dalamud.Plugin.Services;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
using Glamourer.Automation;
|
using Glamourer.Automation;
|
||||||
using Glamourer.Designs;
|
using Glamourer.Designs;
|
||||||
using Glamourer.Events;
|
|
||||||
using Glamourer.Gui.Customization;
|
using Glamourer.Gui.Customization;
|
||||||
using Glamourer.Gui.Equipment;
|
using Glamourer.Gui.Equipment;
|
||||||
using Glamourer.Interop;
|
using Glamourer.Interop;
|
||||||
|
|
@ -61,13 +60,13 @@ public class ActorPanel(
|
||||||
|
|
||||||
if (_importService.CreateDatTarget(out var dat))
|
if (_importService.CreateDatTarget(out var dat))
|
||||||
{
|
{
|
||||||
_stateManager.ChangeCustomize(_state!, dat.Customize, CustomizeApplicationFlags, StateSource.Manual);
|
_stateManager.ChangeEntireCustomize(_state!, dat.Customize, CustomizeApplicationFlags, ApplySettings.Manual);
|
||||||
Glamourer.Messager.NotificationMessage($"Applied games .dat file {dat.Description} customizations to {_state.Identifier}.",
|
Glamourer.Messager.NotificationMessage($"Applied games .dat file {dat.Description} customizations to {_state.Identifier}.",
|
||||||
NotificationType.Success, false);
|
NotificationType.Success, false);
|
||||||
}
|
}
|
||||||
else if (_importService.CreateCharaTarget(out var designBase, out var name))
|
else if (_importService.CreateCharaTarget(out var designBase, out var name))
|
||||||
{
|
{
|
||||||
_stateManager.ApplyDesign(designBase, _state!, StateSource.Manual);
|
_stateManager.ApplyDesign(_state!, designBase, ApplySettings.Manual);
|
||||||
Glamourer.Messager.NotificationMessage($"Applied Anamnesis .chara file {name} to {_state.Identifier}.", NotificationType.Success,
|
Glamourer.Messager.NotificationMessage($"Applied Anamnesis .chara file {name} to {_state.Identifier}.", NotificationType.Success,
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
|
@ -139,7 +138,7 @@ public class ActorPanel(
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_customizationDrawer.Draw(_state!.ModelData.Customize, _state.IsLocked, _lockedRedraw))
|
if (_customizationDrawer.Draw(_state!.ModelData.Customize, _state.IsLocked, _lockedRedraw))
|
||||||
_stateManager.ChangeCustomize(_state, _customizationDrawer.Customize, _customizationDrawer.Changed, StateSource.Manual);
|
_stateManager.ChangeEntireCustomize(_state, _customizationDrawer.Customize, _customizationDrawer.Changed, ApplySettings.Manual);
|
||||||
|
|
||||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.Wetness, _stateManager, _state));
|
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.Wetness, _stateManager, _state));
|
||||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||||
|
|
@ -159,7 +158,7 @@ public class ActorPanel(
|
||||||
var data = EquipDrawData.FromState(_stateManager, _state!, slot);
|
var data = EquipDrawData.FromState(_stateManager, _state!, slot);
|
||||||
_equipmentDrawer.DrawEquip(data);
|
_equipmentDrawer.DrawEquip(data);
|
||||||
if (usedAllStain)
|
if (usedAllStain)
|
||||||
_stateManager.ChangeStain(_state, slot, newAllStain, StateSource.Manual);
|
_stateManager.ChangeStain(_state, slot, newAllStain, ApplySettings.Manual);
|
||||||
}
|
}
|
||||||
|
|
||||||
var mainhand = EquipDrawData.FromState(_stateManager, _state, EquipSlot.MainHand);
|
var mainhand = EquipDrawData.FromState(_stateManager, _state, EquipSlot.MainHand);
|
||||||
|
|
@ -316,9 +315,9 @@ public class ActorPanel(
|
||||||
private void SaveDesignOpen()
|
private void SaveDesignOpen()
|
||||||
{
|
{
|
||||||
ImGui.OpenPopup("Save as Design");
|
ImGui.OpenPopup("Save as Design");
|
||||||
_newName = _state!.Identifier.ToName();
|
_newName = _state!.Identifier.ToName();
|
||||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||||
_newDesign = _converter.Convert(_state, applyGear, applyCustomize, applyCrest, applyParameters);
|
_newDesign = _converter.Convert(_state, applyGear, applyCustomize, applyCrest, applyParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveDesignDrawPopup()
|
private void SaveDesignDrawPopup()
|
||||||
|
|
@ -340,7 +339,7 @@ public class ActorPanel(
|
||||||
var text = ImGui.GetClipboardText();
|
var text = ImGui.GetClipboardText();
|
||||||
var design = _converter.FromBase64(text, applyCustomize, applyGear, out _)
|
var design = _converter.FromBase64(text, applyCustomize, applyGear, out _)
|
||||||
?? throw new Exception("The clipboard did not contain valid data.");
|
?? throw new Exception("The clipboard did not contain valid data.");
|
||||||
_stateManager.ApplyDesign(design, _state!, StateSource.Manual);
|
_stateManager.ApplyDesign(_state!, design, ApplySettings.Manual);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -395,8 +394,8 @@ public class ActorPanel(
|
||||||
|
|
||||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||||
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
|
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
|
||||||
_stateManager.ApplyDesign(_converter.Convert(_state!, applyGear, applyCustomize, applyCrest, applyParameters), state,
|
_stateManager.ApplyDesign(state, _converter.Convert(_state!, applyGear, applyCustomize, applyCrest, applyParameters),
|
||||||
StateSource.Manual);
|
ApplySettings.Manual);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawApplyToTarget()
|
private void DrawApplyToTarget()
|
||||||
|
|
@ -413,7 +412,7 @@ public class ActorPanel(
|
||||||
|
|
||||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||||
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
|
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
|
||||||
_stateManager.ApplyDesign(_converter.Convert(_state!, applyGear, applyCustomize, applyCrest, applyParameters), state,
|
_stateManager.ApplyDesign(state, _converter.Convert(_state!, applyGear, applyCustomize, applyCrest, applyParameters),
|
||||||
StateSource.Manual);
|
ApplySettings.Manual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
using Glamourer.Designs;
|
using Glamourer.Designs;
|
||||||
using Glamourer.Events;
|
|
||||||
using Glamourer.GameData;
|
using Glamourer.GameData;
|
||||||
using Glamourer.Interop;
|
using Glamourer.Interop;
|
||||||
using Glamourer.State;
|
using Glamourer.State;
|
||||||
|
|
@ -67,9 +66,9 @@ public class NpcAppearancePanel(NpcCombo _npcCombo, StateManager _state, ObjectM
|
||||||
if (ImGuiUtil.DrawDisabledButton("Apply", Vector2.Zero, string.Empty, disabled))
|
if (ImGuiUtil.DrawDisabledButton("Apply", Vector2.Zero, string.Empty, disabled))
|
||||||
{
|
{
|
||||||
foreach (var (slot, item, stain) in _designConverter.FromDrawData(data.Equip.ToArray(), data.Mainhand, data.Offhand, true))
|
foreach (var (slot, item, stain) in _designConverter.FromDrawData(data.Equip.ToArray(), data.Mainhand, data.Offhand, true))
|
||||||
_state.ChangeEquip(state!, slot, item, stain, StateSource.Manual);
|
_state.ChangeEquip(state!, slot, item, stain, ApplySettings.Manual);
|
||||||
_state.ChangeMeta(state!, MetaIndex.VisorState, data.VisorToggled, StateSource.Manual);
|
_state.ChangeMetaState(state!, MetaIndex.VisorState, data.VisorToggled, ApplySettings.Manual);
|
||||||
_state.ChangeCustomize(state!, data.Customize, CustomizeFlagExtensions.All, StateSource.Manual);
|
_state.ChangeEntireCustomize(state!, data.Customize, CustomizeFlagExtensions.All, ApplySettings.Manual);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ using Dalamud.Interface.Internal.Notifications;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||||
using Glamourer.Automation;
|
using Glamourer.Automation;
|
||||||
using Glamourer.Designs;
|
using Glamourer.Designs;
|
||||||
using Glamourer.Events;
|
|
||||||
using Glamourer.GameData;
|
using Glamourer.GameData;
|
||||||
using Glamourer.Gui.Customization;
|
using Glamourer.Gui.Customization;
|
||||||
using Glamourer.Gui.Equipment;
|
using Glamourer.Gui.Equipment;
|
||||||
|
|
@ -439,7 +438,7 @@ public class DesignPanel(
|
||||||
{
|
{
|
||||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||||
using var _ = _selector.Selected!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest, applyParameters);
|
using var _ = _selector.Selected!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest, applyParameters);
|
||||||
_state.ApplyDesign(_selector.Selected!, state, StateSource.Manual);
|
_state.ApplyDesign(state, _selector.Selected!, ApplySettings.Manual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -458,7 +457,7 @@ public class DesignPanel(
|
||||||
{
|
{
|
||||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||||
using var _ = _selector.Selected!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest, applyParameters);
|
using var _ = _selector.Selected!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest, applyParameters);
|
||||||
_state.ApplyDesign(_selector.Selected!, state, StateSource.Manual);
|
_state.ApplyDesign(state, _selector.Selected!, ApplySettings.Manual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,12 @@
|
||||||
using Dalamud.Interface.Internal.Notifications;
|
using Dalamud.Interface.Internal.Notifications;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
using Glamourer.Designs;
|
using Glamourer.Designs;
|
||||||
using Glamourer.Events;
|
|
||||||
using Glamourer.Gui.Customization;
|
using Glamourer.Gui.Customization;
|
||||||
using Glamourer.Gui.Equipment;
|
using Glamourer.Gui.Equipment;
|
||||||
using Glamourer.Gui.Tabs.DesignTab;
|
using Glamourer.Gui.Tabs.DesignTab;
|
||||||
using Glamourer.Interop;
|
using Glamourer.Interop;
|
||||||
using Glamourer.State;
|
using Glamourer.State;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using Lumina.Data.Parsing.Scd;
|
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
using OtterGui.Classes;
|
using OtterGui.Classes;
|
||||||
using OtterGui.Raii;
|
using OtterGui.Raii;
|
||||||
|
|
@ -202,7 +200,7 @@ public class NpcPanel(
|
||||||
{
|
{
|
||||||
var (applyGear, applyCustomize, _, _) = UiHelpers.ConvertKeysToFlags();
|
var (applyGear, applyCustomize, _, _) = UiHelpers.ConvertKeysToFlags();
|
||||||
var design = _converter.Convert(ToDesignData(), applyGear, applyCustomize, 0, 0);
|
var design = _converter.Convert(ToDesignData(), applyGear, applyCustomize, 0, 0);
|
||||||
_state.ApplyDesign(design, state, StateSource.Manual);
|
_state.ApplyDesign(state, design, ApplySettings.Manual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -221,7 +219,7 @@ public class NpcPanel(
|
||||||
{
|
{
|
||||||
var (applyGear, applyCustomize, _, _) = UiHelpers.ConvertKeysToFlags();
|
var (applyGear, applyCustomize, _, _) = UiHelpers.ConvertKeysToFlags();
|
||||||
var design = _converter.Convert(ToDesignData(), applyGear, applyCustomize, 0, 0);
|
var design = _converter.Convert(ToDesignData(), applyGear, applyCustomize, 0, 0);
|
||||||
_state.ApplyDesign(design, state, StateSource.Manual);
|
_state.ApplyDesign(state, design, ApplySettings.Manual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ public ref struct ToggleDrawData
|
||||||
Tooltip = "Hide or show your free company crest on this piece of gear.",
|
Tooltip = "Hide or show your free company crest on this piece of gear.",
|
||||||
Locked = state.IsLocked,
|
Locked = state.IsLocked,
|
||||||
CurrentValue = state.ModelData.Crest(slot),
|
CurrentValue = state.ModelData.Crest(slot),
|
||||||
SetValue = v => manager.ChangeCrest(state, slot, v, StateSource.Manual),
|
SetValue = v => manager.ChangeCrest(state, slot, v, ApplySettings.Manual),
|
||||||
};
|
};
|
||||||
|
|
||||||
public static ToggleDrawData FromState(MetaIndex index, StateManager manager, ActorState state)
|
public static ToggleDrawData FromState(MetaIndex index, StateManager manager, ActorState state)
|
||||||
|
|
@ -65,7 +65,7 @@ public ref struct ToggleDrawData
|
||||||
Tooltip = index.ToTooltip(),
|
Tooltip = index.ToTooltip(),
|
||||||
Locked = state.IsLocked,
|
Locked = state.IsLocked,
|
||||||
CurrentValue = state.ModelData.GetMeta(index),
|
CurrentValue = state.ModelData.GetMeta(index),
|
||||||
SetValue = b => manager.ChangeMeta(state, index, b, StateSource.Manual),
|
SetValue = b => manager.ChangeMetaState(state, index, b, ApplySettings.Manual),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ using Dalamud.Game.Text;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Glamourer.Events;
|
using Glamourer.Designs;
|
||||||
using Glamourer.Services;
|
using Glamourer.Services;
|
||||||
using Glamourer.State;
|
using Glamourer.State;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
|
|
@ -118,14 +118,14 @@ public class ContextMenuService : IDisposable
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var slot = item.Type.ToSlot();
|
var slot = item.Type.ToSlot();
|
||||||
_state.ChangeEquip(state, slot, item, 0, StateSource.Manual);
|
_state.ChangeEquip(state, slot, item, 0, ApplySettings.Manual);
|
||||||
if (item.Type.ValidOffhand().IsOffhandType())
|
if (item.Type.ValidOffhand().IsOffhandType())
|
||||||
{
|
{
|
||||||
if (item.PrimaryId.Id is > 1600 and < 1651
|
if (item.PrimaryId.Id is > 1600 and < 1651
|
||||||
&& _items.ItemData.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets))
|
&& _items.ItemData.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets))
|
||||||
_state.ChangeEquip(state, EquipSlot.Hands, gauntlets, 0, StateSource.Manual);
|
_state.ChangeEquip(state, EquipSlot.Hands, gauntlets, 0, ApplySettings.Manual);
|
||||||
if (_items.ItemData.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
|
if (_items.ItemData.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
|
||||||
_state.ChangeEquip(state, EquipSlot.OffHand, offhand, 0, StateSource.Manual);
|
_state.ChangeEquip(state, EquipSlot.OffHand, offhand, 0, ApplySettings.Manual);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -142,14 +142,14 @@ public class ContextMenuService : IDisposable
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var slot = item.Type.ToSlot();
|
var slot = item.Type.ToSlot();
|
||||||
_state.ChangeEquip(state, slot, item, 0, StateSource.Manual);
|
_state.ChangeEquip(state, slot, item, 0, ApplySettings.Manual);
|
||||||
if (item.Type.ValidOffhand().IsOffhandType())
|
if (item.Type.ValidOffhand().IsOffhandType())
|
||||||
{
|
{
|
||||||
if (item.PrimaryId.Id is > 1600 and < 1651
|
if (item.PrimaryId.Id is > 1600 and < 1651
|
||||||
&& _items.ItemData.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets))
|
&& _items.ItemData.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets))
|
||||||
_state.ChangeEquip(state, EquipSlot.Hands, gauntlets, 0, StateSource.Manual);
|
_state.ChangeEquip(state, EquipSlot.Hands, gauntlets, 0, ApplySettings.Manual);
|
||||||
if (_items.ItemData.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
|
if (_items.ItemData.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
|
||||||
_state.ChangeEquip(state, EquipSlot.OffHand, offhand, 0, StateSource.Manual);
|
_state.ChangeEquip(state, EquipSlot.OffHand, offhand, 0, ApplySettings.Manual);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -419,7 +419,7 @@ public class CommandService : IDisposable
|
||||||
if (!_objects.TryGetValue(identifier, out var actors))
|
if (!_objects.TryGetValue(identifier, out var actors))
|
||||||
{
|
{
|
||||||
if (_stateManager.TryGetValue(identifier, out var state))
|
if (_stateManager.TryGetValue(identifier, out var state))
|
||||||
_stateManager.ApplyDesign(design, state, StateSource.Manual);
|
_stateManager.ApplyDesign(state, design, ApplySettings.Manual);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -428,7 +428,7 @@ public class CommandService : IDisposable
|
||||||
if (_stateManager.GetOrCreate(actor.GetIdentifier(_actors), actor, out var state))
|
if (_stateManager.GetOrCreate(actor.GetIdentifier(_actors), actor, out var state))
|
||||||
{
|
{
|
||||||
ApplyModSettings(design, actor, applyMods);
|
ApplyModSettings(design, actor, applyMods);
|
||||||
_stateManager.ApplyDesign(design, state, StateSource.Manual);
|
_stateManager.ApplyDesign(state, design, ApplySettings.Manual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ public static class ServiceManagerA
|
||||||
private static ServiceManager AddState(this ServiceManager services)
|
private static ServiceManager AddState(this ServiceManager services)
|
||||||
=> services.AddSingleton<StateManager>()
|
=> services.AddSingleton<StateManager>()
|
||||||
.AddSingleton<StateApplier>()
|
.AddSingleton<StateApplier>()
|
||||||
.AddSingleton<StateEditor>()
|
.AddSingleton<InternalStateEditor>()
|
||||||
.AddSingleton<StateListener>()
|
.AddSingleton<StateListener>()
|
||||||
.AddSingleton<FunModule>();
|
.AddSingleton<FunModule>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using Glamourer.Designs;
|
using Glamourer.Designs;
|
||||||
using Glamourer.Events;
|
|
||||||
using Penumbra.GameData.Actors;
|
using Penumbra.GameData.Actors;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Dalamud.Game.ClientState.Conditions;
|
using Dalamud.Game.ClientState.Conditions;
|
||||||
|
|
|
||||||
234
Glamourer/State/InternalStateEditor.cs
Normal file
234
Glamourer/State/InternalStateEditor.cs
Normal file
|
|
@ -0,0 +1,234 @@
|
||||||
|
using Dalamud.Plugin.Services;
|
||||||
|
using Glamourer.Designs;
|
||||||
|
using Glamourer.Events;
|
||||||
|
using Glamourer.GameData;
|
||||||
|
using Glamourer.Services;
|
||||||
|
using Penumbra.GameData.DataContainers;
|
||||||
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
|
namespace Glamourer.State;
|
||||||
|
|
||||||
|
public class InternalStateEditor(
|
||||||
|
CustomizeService customizations,
|
||||||
|
HumanModelList humans,
|
||||||
|
ItemManager items,
|
||||||
|
GPoseService gPose,
|
||||||
|
ICondition condition)
|
||||||
|
{
|
||||||
|
/// <summary> Change the model id. If the actor is changed from a human to another human, customize and equipData are unused. </summary>
|
||||||
|
/// <remarks> We currently only allow changing things to humans, not humans to monsters. </remarks>
|
||||||
|
public bool ChangeModelId(ActorState state, uint modelId, in CustomizeArray customize, nint equipData, StateSource source,
|
||||||
|
out uint oldModelId, uint key = 0)
|
||||||
|
{
|
||||||
|
oldModelId = state.ModelData.ModelId;
|
||||||
|
|
||||||
|
// TODO think about this.
|
||||||
|
if (modelId != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!state.CanUnlock(key))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var oldIsHuman = state.ModelData.IsHuman;
|
||||||
|
state.ModelData.IsHuman = humans.IsHuman(modelId);
|
||||||
|
if (state.ModelData.IsHuman)
|
||||||
|
{
|
||||||
|
if (oldModelId == modelId)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
state.ModelData.ModelId = modelId;
|
||||||
|
if (oldIsHuman)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!state.AllowsRedraw(condition))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Fix up everything else to make sure the result is a valid human.
|
||||||
|
state.ModelData.Customize = CustomizeArray.Default;
|
||||||
|
state.ModelData.SetDefaultEquipment(items);
|
||||||
|
state.ModelData.SetHatVisible(true);
|
||||||
|
state.ModelData.SetWeaponVisible(true);
|
||||||
|
state.ModelData.SetVisor(false);
|
||||||
|
state.Sources[MetaIndex.ModelId] = source;
|
||||||
|
state.Sources[MetaIndex.HatState] = source;
|
||||||
|
state.Sources[MetaIndex.WeaponState] = source;
|
||||||
|
state.Sources[MetaIndex.VisorState] = source;
|
||||||
|
foreach (var slot in EquipSlotExtensions.FullSlots)
|
||||||
|
{
|
||||||
|
state.Sources[slot, true] = source;
|
||||||
|
state.Sources[slot, false] = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.Sources[CustomizeIndex.Clan] = source;
|
||||||
|
state.Sources[CustomizeIndex.Gender] = source;
|
||||||
|
var set = customizations.Manager.GetSet(state.ModelData.Customize.Clan, state.ModelData.Customize.Gender);
|
||||||
|
foreach (var index in Enum.GetValues<CustomizeIndex>().Where(set.IsAvailable))
|
||||||
|
state.Sources[index] = source;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!state.AllowsRedraw(condition))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.ModelData.LoadNonHuman(modelId, customize, equipData);
|
||||||
|
state.Sources[MetaIndex.ModelId] = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Change a customization value. </summary>
|
||||||
|
public bool ChangeCustomize(ActorState state, CustomizeIndex idx, CustomizeValue value, StateSource source,
|
||||||
|
out CustomizeValue old, uint key = 0)
|
||||||
|
{
|
||||||
|
old = state.ModelData.Customize[idx];
|
||||||
|
if (!state.CanUnlock(key))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.ModelData.Customize[idx] = value;
|
||||||
|
state.Sources[idx] = source;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Change an entire customization array according to functions. </summary>
|
||||||
|
public bool ChangeHumanCustomize(ActorState state, in CustomizeArray customizeInput, CustomizeFlag applyWhich,
|
||||||
|
Func<CustomizeIndex, StateSource> source, out CustomizeArray old, out CustomizeFlag changed, uint key = 0)
|
||||||
|
{
|
||||||
|
old = state.ModelData.Customize;
|
||||||
|
changed = 0;
|
||||||
|
if (!state.CanUnlock(key))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
(var customize, var applied, changed) = customizations.Combine(state.ModelData.Customize, customizeInput, applyWhich, true);
|
||||||
|
if (changed == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.ModelData.Customize = customize;
|
||||||
|
applied |= changed;
|
||||||
|
foreach (var type in Enum.GetValues<CustomizeIndex>())
|
||||||
|
{
|
||||||
|
if (applied.HasFlag(type.ToFlag()))
|
||||||
|
state.Sources[type] = source(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Change an entire customization array according to functions. </summary>
|
||||||
|
public bool ChangeHumanCustomize(ActorState state, in CustomizeArray customizeInput, Func<CustomizeIndex, bool> applyWhich,
|
||||||
|
Func<CustomizeIndex, StateSource> source, out CustomizeArray old, out CustomizeFlag changed, uint key = 0)
|
||||||
|
{
|
||||||
|
var apply = Enum.GetValues<CustomizeIndex>().Where(applyWhich).Aggregate((CustomizeFlag)0, (current, type) => current | type.ToFlag());
|
||||||
|
return ChangeHumanCustomize(state, customizeInput, apply, source, out old, out changed, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Change a single piece of equipment without stain. </summary>
|
||||||
|
public bool ChangeItem(ActorState state, EquipSlot slot, EquipItem item, StateSource source, out EquipItem oldItem, uint key = 0)
|
||||||
|
{
|
||||||
|
oldItem = state.ModelData.Item(slot);
|
||||||
|
if (!state.CanUnlock(key))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Can not change weapon type from expected type in state.
|
||||||
|
if (slot is EquipSlot.MainHand && item.Type != state.BaseData.MainhandType
|
||||||
|
|| slot is EquipSlot.OffHand && item.Type != state.BaseData.OffhandType)
|
||||||
|
{
|
||||||
|
if (!gPose.InGPose)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var old = oldItem;
|
||||||
|
gPose.AddActionOnLeave(() =>
|
||||||
|
{
|
||||||
|
if (old.Type == state.BaseData.Item(slot).Type)
|
||||||
|
ChangeItem(state, slot, old, state.Sources[slot, false], out _, key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
state.ModelData.SetItem(slot, item);
|
||||||
|
state.Sources[slot, false] = source;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Change a single piece of equipment including stain. </summary>
|
||||||
|
public bool ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainId stain, StateSource source, out EquipItem oldItem,
|
||||||
|
out StainId oldStain, uint key = 0)
|
||||||
|
{
|
||||||
|
oldItem = state.ModelData.Item(slot);
|
||||||
|
oldStain = state.ModelData.Stain(slot);
|
||||||
|
if (!state.CanUnlock(key))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Can not change weapon type from expected type in state.
|
||||||
|
if (slot is EquipSlot.MainHand && item.Type != state.BaseData.MainhandType
|
||||||
|
|| slot is EquipSlot.OffHand && item.Type != state.BaseData.OffhandType)
|
||||||
|
{
|
||||||
|
if (!gPose.InGPose)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var old = oldItem;
|
||||||
|
var oldS = oldStain;
|
||||||
|
gPose.AddActionOnLeave(() =>
|
||||||
|
{
|
||||||
|
if (old.Type == state.BaseData.Item(slot).Type)
|
||||||
|
ChangeEquip(state, slot, old, oldS, state.Sources[slot, false], out _, out _, key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
state.ModelData.SetItem(slot, item);
|
||||||
|
state.ModelData.SetStain(slot, stain);
|
||||||
|
state.Sources[slot, false] = source;
|
||||||
|
state.Sources[slot, true] = source;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Change only the stain of an equipment piece. </summary>
|
||||||
|
public bool ChangeStain(ActorState state, EquipSlot slot, StainId stain, StateSource source, out StainId oldStain, uint key = 0)
|
||||||
|
{
|
||||||
|
oldStain = state.ModelData.Stain(slot);
|
||||||
|
if (!state.CanUnlock(key))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.ModelData.SetStain(slot, stain);
|
||||||
|
state.Sources[slot, true] = source;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Change the crest of an equipment piece. </summary>
|
||||||
|
public bool ChangeCrest(ActorState state, CrestFlag slot, bool crest, StateSource source, out bool oldCrest, uint key = 0)
|
||||||
|
{
|
||||||
|
oldCrest = state.ModelData.Crest(slot);
|
||||||
|
if (!state.CanUnlock(key))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.ModelData.SetCrest(slot, crest);
|
||||||
|
state.Sources[slot] = source;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Change the customize flags of a character. </summary>
|
||||||
|
public bool ChangeParameter(ActorState state, CustomizeParameterFlag flag, CustomizeParameterValue value, StateSource source,
|
||||||
|
out CustomizeParameterValue oldValue, uint key = 0)
|
||||||
|
{
|
||||||
|
oldValue = state.ModelData.Parameters[flag];
|
||||||
|
if (!state.CanUnlock(key))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.ModelData.Parameters.Set(flag, value);
|
||||||
|
state.Sources[flag] = source;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ChangeMetaState(ActorState state, MetaIndex index, bool value, StateSource source, out bool oldValue,
|
||||||
|
uint key = 0)
|
||||||
|
{
|
||||||
|
oldValue = state.ModelData.GetMeta(index);
|
||||||
|
if (!state.CanUnlock(key))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.ModelData.SetMeta(index, value);
|
||||||
|
state.Sources[index] = source;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
30
Glamourer/State/JobChangeState.cs
Normal file
30
Glamourer/State/JobChangeState.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
using OtterGui.Services;
|
||||||
|
using Penumbra.GameData.Actors;
|
||||||
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
|
namespace Glamourer.State;
|
||||||
|
|
||||||
|
public sealed class JobChangeState : Dictionary<FullEquipType, (EquipItem, StateSource)>, IService
|
||||||
|
{
|
||||||
|
public ActorState? State { get; private set; }
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
State = null;
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasState
|
||||||
|
=> State != null;
|
||||||
|
|
||||||
|
public ActorIdentifier Identifier
|
||||||
|
=> State?.Identifier ?? ActorIdentifier.Invalid;
|
||||||
|
|
||||||
|
public void Set(ActorState state, IEnumerable<(EquipItem, StateSource)> items)
|
||||||
|
{
|
||||||
|
foreach (var (item, source) in items.Where(p => p.Item1.Valid))
|
||||||
|
TryAdd(item.Type, (item, source));
|
||||||
|
State = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using Glamourer.Designs;
|
using Glamourer.Designs;
|
||||||
using Glamourer.Events;
|
|
||||||
using Glamourer.GameData;
|
using Glamourer.GameData;
|
||||||
using Glamourer.Interop;
|
using Glamourer.Interop;
|
||||||
using Glamourer.Interop.Penumbra;
|
using Glamourer.Interop.Penumbra;
|
||||||
|
|
@ -277,6 +276,47 @@ public class StateApplier(
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Apply the entire state of an actor to all relevant actors, either via immediate redraw or piecewise. </summary>
|
||||||
|
/// <param name="state"> The state to apply. </param>
|
||||||
|
/// <param name="redraw"> Whether a redraw should be forced. </param>
|
||||||
|
/// <param name="withLock"> Whether a temporary lock should be applied for the redraw. </param>
|
||||||
|
/// <returns> The actor data for the actors who got changed. </returns>
|
||||||
|
public ActorData ApplyAll(ActorState state, bool redraw, bool withLock)
|
||||||
|
{
|
||||||
|
var actors = ChangeMetaState(state, MetaIndex.Wetness, true);
|
||||||
|
if (redraw)
|
||||||
|
{
|
||||||
|
if (withLock)
|
||||||
|
state.TempLock();
|
||||||
|
ForceRedraw(actors);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ChangeCustomize(actors, state.ModelData.Customize);
|
||||||
|
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||||
|
{
|
||||||
|
ChangeArmor(actors, slot, state.ModelData.Armor(slot), state.Sources[slot, false] is not StateSource.Ipc,
|
||||||
|
state.ModelData.IsHatVisible());
|
||||||
|
}
|
||||||
|
|
||||||
|
var mainhandActors = state.ModelData.MainhandType != state.BaseData.MainhandType ? actors.OnlyGPose() : actors;
|
||||||
|
ChangeMainhand(mainhandActors, state.ModelData.Item(EquipSlot.MainHand), state.ModelData.Stain(EquipSlot.MainHand));
|
||||||
|
var offhandActors = state.ModelData.OffhandType != state.BaseData.OffhandType ? actors.OnlyGPose() : actors;
|
||||||
|
ChangeOffhand(offhandActors, state.ModelData.Item(EquipSlot.OffHand), state.ModelData.Stain(EquipSlot.OffHand));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.ModelData.IsHuman)
|
||||||
|
{
|
||||||
|
ChangeMetaState(actors, MetaIndex.HatState, state.ModelData.IsHatVisible());
|
||||||
|
ChangeMetaState(actors, MetaIndex.WeaponState, state.ModelData.IsWeaponVisible());
|
||||||
|
ChangeMetaState(actors, MetaIndex.VisorState, state.ModelData.IsVisorToggled());
|
||||||
|
ChangeCrests(actors, state.ModelData.CrestVisibility);
|
||||||
|
ChangeParameters(actors, state.OnlyChangedParameters(), state.ModelData.Parameters, state.IsLocked);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actors;
|
||||||
|
}
|
||||||
|
|
||||||
private ActorData GetData(ActorState state)
|
private ActorData GetData(ActorState state)
|
||||||
{
|
{
|
||||||
_objects.Update();
|
_objects.Update();
|
||||||
|
|
|
||||||
|
|
@ -1,221 +1,334 @@
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using Glamourer.Designs;
|
using Glamourer.Designs;
|
||||||
|
using Glamourer.Designs.Links;
|
||||||
using Glamourer.Events;
|
using Glamourer.Events;
|
||||||
using Glamourer.GameData;
|
using Glamourer.GameData;
|
||||||
|
using Glamourer.Interop.Structs;
|
||||||
using Glamourer.Services;
|
using Glamourer.Services;
|
||||||
using Penumbra.GameData.DataContainers;
|
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
namespace Glamourer.State;
|
namespace Glamourer.State;
|
||||||
|
|
||||||
public class StateEditor(CustomizeService customizations, HumanModelList humans, ItemManager items, GPoseService gPose, ICondition condition)
|
public class StateEditor(
|
||||||
|
InternalStateEditor editor,
|
||||||
|
StateApplier applier,
|
||||||
|
StateChanged stateChanged,
|
||||||
|
JobChangeState jobChange,
|
||||||
|
Configuration config,
|
||||||
|
ItemManager items) : IDesignEditor
|
||||||
{
|
{
|
||||||
/// <summary> Change the model id. If the actor is changed from a human to another human, customize and equipData are unused. </summary>
|
protected readonly InternalStateEditor Editor = editor;
|
||||||
/// <remarks> We currently only allow changing things to humans, not humans to monsters. </remarks>
|
protected readonly StateApplier Applier = applier;
|
||||||
public bool ChangeModelId(ActorState state, uint modelId, in CustomizeArray customize, nint equipData, StateSource source,
|
protected readonly StateChanged StateChanged = stateChanged;
|
||||||
out uint oldModelId, uint key = 0)
|
protected readonly Configuration Config = config;
|
||||||
{
|
protected readonly ItemManager Items = items;
|
||||||
oldModelId = state.ModelData.ModelId;
|
|
||||||
|
|
||||||
// TODO think about this.
|
/// <summary> Turn an actor to. </summary>
|
||||||
if (modelId != 0)
|
public void ChangeModelId(ActorState state, uint modelId, CustomizeArray customize, nint equipData, StateSource source,
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!state.CanUnlock(key))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var oldIsHuman = state.ModelData.IsHuman;
|
|
||||||
state.ModelData.IsHuman = humans.IsHuman(modelId);
|
|
||||||
if (state.ModelData.IsHuman)
|
|
||||||
{
|
|
||||||
if (oldModelId == modelId)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
state.ModelData.ModelId = modelId;
|
|
||||||
if (oldIsHuman)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!state.AllowsRedraw(condition))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Fix up everything else to make sure the result is a valid human.
|
|
||||||
state.ModelData.Customize = CustomizeArray.Default;
|
|
||||||
state.ModelData.SetDefaultEquipment(items);
|
|
||||||
state.ModelData.SetHatVisible(true);
|
|
||||||
state.ModelData.SetWeaponVisible(true);
|
|
||||||
state.ModelData.SetVisor(false);
|
|
||||||
state.Sources[MetaIndex.ModelId] = source;
|
|
||||||
state.Sources[MetaIndex.HatState] = source;
|
|
||||||
state.Sources[MetaIndex.WeaponState] = source;
|
|
||||||
state.Sources[MetaIndex.VisorState] = source;
|
|
||||||
foreach (var slot in EquipSlotExtensions.FullSlots)
|
|
||||||
{
|
|
||||||
state.Sources[slot, true] = source;
|
|
||||||
state.Sources[slot, false] = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
state.Sources[CustomizeIndex.Clan] = source;
|
|
||||||
state.Sources[CustomizeIndex.Gender] = source;
|
|
||||||
var set = customizations.Manager.GetSet(state.ModelData.Customize.Clan, state.ModelData.Customize.Gender);
|
|
||||||
foreach (var index in Enum.GetValues<CustomizeIndex>().Where(set.IsAvailable))
|
|
||||||
state.Sources[index] = source;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!state.AllowsRedraw(condition))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
state.ModelData.LoadNonHuman(modelId, customize, equipData);
|
|
||||||
state.Sources[MetaIndex.ModelId] = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Change a customization value. </summary>
|
|
||||||
public bool ChangeCustomize(ActorState state, CustomizeIndex idx, CustomizeValue value, StateSource source,
|
|
||||||
out CustomizeValue old, uint key = 0)
|
|
||||||
{
|
|
||||||
old = state.ModelData.Customize[idx];
|
|
||||||
if (!state.CanUnlock(key))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
state.ModelData.Customize[idx] = value;
|
|
||||||
state.Sources[idx] = source;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Change an entire customization array according to flags. </summary>
|
|
||||||
public bool ChangeHumanCustomize(ActorState state, in CustomizeArray customizeInput, CustomizeFlag applyWhich, StateSource source,
|
|
||||||
out CustomizeArray old, out CustomizeFlag changed, uint key = 0)
|
|
||||||
{
|
|
||||||
old = state.ModelData.Customize;
|
|
||||||
changed = 0;
|
|
||||||
if (!state.CanUnlock(key))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
(var customize, var applied, changed) = customizations.Combine(state.ModelData.Customize, customizeInput, applyWhich, true);
|
|
||||||
if (changed == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
state.ModelData.Customize = customize;
|
|
||||||
applied |= changed;
|
|
||||||
foreach (var type in Enum.GetValues<CustomizeIndex>())
|
|
||||||
{
|
|
||||||
if (applied.HasFlag(type.ToFlag()))
|
|
||||||
state.Sources[type] = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Change a single piece of equipment without stain. </summary>
|
|
||||||
public bool ChangeItem(ActorState state, EquipSlot slot, EquipItem item, StateSource source, out EquipItem oldItem, uint key = 0)
|
|
||||||
{
|
|
||||||
oldItem = state.ModelData.Item(slot);
|
|
||||||
if (!state.CanUnlock(key))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Can not change weapon type from expected type in state.
|
|
||||||
if (slot is EquipSlot.MainHand && item.Type != state.BaseData.MainhandType
|
|
||||||
|| slot is EquipSlot.OffHand && item.Type != state.BaseData.OffhandType)
|
|
||||||
{
|
|
||||||
if (!gPose.InGPose)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var old = oldItem;
|
|
||||||
gPose.AddActionOnLeave(() =>
|
|
||||||
{
|
|
||||||
if (old.Type == state.BaseData.Item(slot).Type)
|
|
||||||
ChangeItem(state, slot, old, state.Sources[slot, false], out _, key);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
state.ModelData.SetItem(slot, item);
|
|
||||||
state.Sources[slot, false] = source;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Change a single piece of equipment including stain. </summary>
|
|
||||||
public bool ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainId stain, StateSource source, out EquipItem oldItem,
|
|
||||||
out StainId oldStain, uint key = 0)
|
|
||||||
{
|
|
||||||
oldItem = state.ModelData.Item(slot);
|
|
||||||
oldStain = state.ModelData.Stain(slot);
|
|
||||||
if (!state.CanUnlock(key))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Can not change weapon type from expected type in state.
|
|
||||||
if (slot is EquipSlot.MainHand && item.Type != state.BaseData.MainhandType
|
|
||||||
|| slot is EquipSlot.OffHand && item.Type != state.BaseData.OffhandType)
|
|
||||||
{
|
|
||||||
if (!gPose.InGPose)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var old = oldItem;
|
|
||||||
var oldS = oldStain;
|
|
||||||
gPose.AddActionOnLeave(() =>
|
|
||||||
{
|
|
||||||
if (old.Type == state.BaseData.Item(slot).Type)
|
|
||||||
ChangeEquip(state, slot, old, oldS, state.Sources[slot, false], out _, out _, key);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
state.ModelData.SetItem(slot, item);
|
|
||||||
state.ModelData.SetStain(slot, stain);
|
|
||||||
state.Sources[slot, false] = source;
|
|
||||||
state.Sources[slot, true] = source;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Change only the stain of an equipment piece. </summary>
|
|
||||||
public bool ChangeStain(ActorState state, EquipSlot slot, StainId stain, StateSource source, out StainId oldStain, uint key = 0)
|
|
||||||
{
|
|
||||||
oldStain = state.ModelData.Stain(slot);
|
|
||||||
if (!state.CanUnlock(key))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
state.ModelData.SetStain(slot, stain);
|
|
||||||
state.Sources[slot, true] = source;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Change the crest of an equipment piece. </summary>
|
|
||||||
public bool ChangeCrest(ActorState state, CrestFlag slot, bool crest, StateSource source, out bool oldCrest, uint key = 0)
|
|
||||||
{
|
|
||||||
oldCrest = state.ModelData.Crest(slot);
|
|
||||||
if (!state.CanUnlock(key))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
state.ModelData.SetCrest(slot, crest);
|
|
||||||
state.Sources[slot] = source;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Change the customize flags of a character. </summary>
|
|
||||||
public bool ChangeParameter(ActorState state, CustomizeParameterFlag flag, CustomizeParameterValue value, StateSource source,
|
|
||||||
out CustomizeParameterValue oldValue, uint key = 0)
|
|
||||||
{
|
|
||||||
oldValue = state.ModelData.Parameters[flag];
|
|
||||||
if (!state.CanUnlock(key))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
state.ModelData.Parameters.Set(flag, value);
|
|
||||||
state.Sources[flag] = source;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ChangeMetaState(ActorState state, MetaIndex index, bool value, StateSource source, out bool oldValue,
|
|
||||||
uint key = 0)
|
uint key = 0)
|
||||||
{
|
{
|
||||||
oldValue = state.ModelData.GetMeta(index);
|
if (!Editor.ChangeModelId(state, modelId, customize, equipData, source, out var old, key))
|
||||||
if (!state.CanUnlock(key))
|
return;
|
||||||
return false;
|
|
||||||
|
|
||||||
state.ModelData.SetMeta(index, value);
|
var actors = Applier.ForceRedraw(state, source is StateSource.Manual or StateSource.Ipc);
|
||||||
state.Sources[index] = source;
|
Glamourer.Log.Verbose(
|
||||||
return true;
|
$"Set model id in state {state.Identifier.Incognito(null)} from {old} to {modelId}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
|
StateChanged.Invoke(StateChanged.Type.Model, source, state, actors, (old, modelId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void ChangeCustomize(object data, CustomizeIndex idx, CustomizeValue value, ApplySettings settings)
|
||||||
|
{
|
||||||
|
if (data is not ActorState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Editor.ChangeCustomize(state, idx, value, settings.Source, out var old, settings.Key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var actors = Applier.ChangeCustomize(state, settings.Source is StateSource.Manual or StateSource.Ipc);
|
||||||
|
Glamourer.Log.Verbose(
|
||||||
|
$"Set {idx.ToDefaultName()} customizations in state {state.Identifier.Incognito(null)} from {old.Value} to {value.Value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
|
StateChanged.Invoke(StateChanged.Type.Customize, settings.Source, state, actors, (old, value, idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void ChangeEntireCustomize(object data, in CustomizeArray customizeInput, CustomizeFlag apply, ApplySettings settings)
|
||||||
|
{
|
||||||
|
if (data is not ActorState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Editor.ChangeHumanCustomize(state, customizeInput, apply, _ => settings.Source, out var old, out var applied, settings.Key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var actors = Applier.ChangeCustomize(state, settings.Source is StateSource.Manual or StateSource.Ipc);
|
||||||
|
Glamourer.Log.Verbose(
|
||||||
|
$"Set {applied} customizations in state {state.Identifier.Incognito(null)} from {old} to {customizeInput}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
|
StateChanged.Invoke(StateChanged.Type.EntireCustomize, settings.Source, state, actors, (old, applied));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void ChangeItem(object data, EquipSlot slot, EquipItem item, ApplySettings settings = default)
|
||||||
|
{
|
||||||
|
if (data is not ActorState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Editor.ChangeItem(state, slot, item, settings.Source, out var old, settings.Key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var type = slot.ToIndex() < 10 ? StateChanged.Type.Equip : StateChanged.Type.Weapon;
|
||||||
|
var actors = type is StateChanged.Type.Equip
|
||||||
|
? Applier.ChangeArmor(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc)
|
||||||
|
: Applier.ChangeWeapon(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc,
|
||||||
|
item.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType));
|
||||||
|
|
||||||
|
if (slot is EquipSlot.MainHand)
|
||||||
|
ApplyMainhandPeriphery(state, item, settings);
|
||||||
|
|
||||||
|
Glamourer.Log.Verbose(
|
||||||
|
$"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId}). [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
|
StateChanged.Invoke(type, settings.Source, state, actors, (old, item, slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void ChangeEquip(object data, EquipSlot slot, EquipItem? item, StainId? stain, ApplySettings settings)
|
||||||
|
{
|
||||||
|
switch (item.HasValue, stain.HasValue)
|
||||||
|
{
|
||||||
|
case (false, false): return;
|
||||||
|
case (true, false):
|
||||||
|
ChangeItem(data, slot, item!.Value, settings);
|
||||||
|
return;
|
||||||
|
case (false, true):
|
||||||
|
ChangeStain(data, slot, stain!.Value, settings);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data is not ActorState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Editor.ChangeEquip(state, slot, item ?? state.ModelData.Item(slot), stain ?? state.ModelData.Stain(slot), settings.Source,
|
||||||
|
out var old, out var oldStain, settings.Key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var type = slot.ToIndex() < 10 ? StateChanged.Type.Equip : StateChanged.Type.Weapon;
|
||||||
|
var actors = type is StateChanged.Type.Equip
|
||||||
|
? Applier.ChangeArmor(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc)
|
||||||
|
: Applier.ChangeWeapon(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc,
|
||||||
|
item!.Value.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType));
|
||||||
|
|
||||||
|
if (slot is EquipSlot.MainHand)
|
||||||
|
ApplyMainhandPeriphery(state, item, settings);
|
||||||
|
|
||||||
|
Glamourer.Log.Verbose(
|
||||||
|
$"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item!.Value.Name} ({item.Value.ItemId}) and its stain from {oldStain.Id} to {stain!.Value.Id}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
|
StateChanged.Invoke(type, settings.Source, state, actors, (old, item!.Value, slot));
|
||||||
|
StateChanged.Invoke(StateChanged.Type.Stain, settings.Source, state, actors, (oldStain, stain!.Value, slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void ChangeStain(object data, EquipSlot slot, StainId stain, ApplySettings settings)
|
||||||
|
{
|
||||||
|
if (data is not ActorState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Editor.ChangeStain(state, slot, stain, settings.Source, out var old, settings.Key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var actors = Applier.ChangeStain(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc);
|
||||||
|
Glamourer.Log.Verbose(
|
||||||
|
$"Set {slot.ToName()} stain in state {state.Identifier.Incognito(null)} from {old.Id} to {stain.Id}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
|
StateChanged.Invoke(StateChanged.Type.Stain, settings.Source, state, actors, (old, stain, slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void ChangeCrest(object data, CrestFlag slot, bool crest, ApplySettings settings)
|
||||||
|
{
|
||||||
|
if (data is not ActorState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Editor.ChangeCrest(state, slot, crest, settings.Source, out var old, settings.Key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var actors = Applier.ChangeCrests(state, settings.Source is StateSource.Manual or StateSource.Ipc);
|
||||||
|
Glamourer.Log.Verbose(
|
||||||
|
$"Set {slot.ToLabel()} crest in state {state.Identifier.Incognito(null)} from {old} to {crest}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
|
StateChanged.Invoke(StateChanged.Type.Crest, settings.Source, state, actors, (old, crest, slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void ChangeCustomizeParameter(object data, CustomizeParameterFlag flag, CustomizeParameterValue value, ApplySettings settings)
|
||||||
|
{
|
||||||
|
if (data is not ActorState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Also apply main color to highlights when highlights is off.
|
||||||
|
if (!state.ModelData.Customize.Highlights && flag is CustomizeParameterFlag.HairDiffuse)
|
||||||
|
ChangeCustomizeParameter(state, CustomizeParameterFlag.HairHighlight, value, settings);
|
||||||
|
|
||||||
|
if (!Editor.ChangeParameter(state, flag, value, settings.Source, out var old, settings.Key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var @new = state.ModelData.Parameters[flag];
|
||||||
|
var actors = Applier.ChangeParameters(state, flag, settings.Source is StateSource.Manual or StateSource.Ipc);
|
||||||
|
Glamourer.Log.Verbose(
|
||||||
|
$"Set {flag} crest in state {state.Identifier.Incognito(null)} from {old} to {@new}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
|
StateChanged.Invoke(StateChanged.Type.Parameter, settings.Source, state, actors, (old, @new, flag));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void ChangeMetaState(object data, MetaIndex index, bool value, ApplySettings settings)
|
||||||
|
{
|
||||||
|
if (data is not ActorState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Editor.ChangeMetaState(state, index, value, settings.Source, out var old, settings.Key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var actors = Applier.ChangeMetaState(state, index, settings.Source is StateSource.Manual or StateSource.Ipc);
|
||||||
|
Glamourer.Log.Verbose(
|
||||||
|
$"Set Head Gear Visibility in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
|
StateChanged.Invoke(StateChanged.Type.Other, settings.Source, state, actors, (old, value, MetaIndex.HatState));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void ApplyDesign(object data, MergedDesign mergedDesign, ApplySettings settings)
|
||||||
|
{
|
||||||
|
if (data is not ActorState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Editor.ChangeModelId(state, mergedDesign.Design.DesignData.ModelId, mergedDesign.Design.DesignData.Customize,
|
||||||
|
mergedDesign.Design.GetDesignDataRef().GetEquipmentPtr(), settings.Source, out var oldModelId, settings.Key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var requiresRedraw = oldModelId != mergedDesign.Design.DesignData.ModelId || !mergedDesign.Design.DesignData.IsHuman;
|
||||||
|
|
||||||
|
if (state.ModelData.IsHuman)
|
||||||
|
{
|
||||||
|
foreach (var slot in CrestExtensions.AllRelevantSet.Where(mergedDesign.Design.DoApplyCrest))
|
||||||
|
{
|
||||||
|
if (!settings.RespectManual || state.Sources[slot] is not StateSource.Manual)
|
||||||
|
Editor.ChangeCrest(state, slot, mergedDesign.Design.DesignData.Crest(slot), Source(slot),
|
||||||
|
out _, settings.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
var customizeFlags = mergedDesign.Design.ApplyCustomizeRaw;
|
||||||
|
if (mergedDesign.Design.DoApplyCustomize(CustomizeIndex.Clan))
|
||||||
|
customizeFlags |= CustomizeFlag.Race;
|
||||||
|
|
||||||
|
Func<CustomizeIndex, bool> applyWhich = settings.RespectManual
|
||||||
|
? i => customizeFlags.HasFlag(i.ToFlag()) && state.Sources[i] is not StateSource.Manual
|
||||||
|
: i => customizeFlags.HasFlag(i.ToFlag());
|
||||||
|
|
||||||
|
if (Editor.ChangeHumanCustomize(state, mergedDesign.Design.DesignData.Customize, applyWhich, i => Source(i), out _, out var changed,
|
||||||
|
settings.Key))
|
||||||
|
requiresRedraw |= changed.RequiresRedraw();
|
||||||
|
|
||||||
|
foreach (var parameter in mergedDesign.Design.ApplyParameters.Iterate())
|
||||||
|
{
|
||||||
|
if (settings.RespectManual && state.Sources[parameter] is StateSource.Manual or StateSource.Pending)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var source = Source(parameter);
|
||||||
|
if (source is StateSource.Manual)
|
||||||
|
source = StateSource.Pending;
|
||||||
|
Editor.ChangeParameter(state, parameter, mergedDesign.Design.DesignData.Parameters[parameter], source, out _, settings.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not apply highlights from a design if highlights is unchecked.
|
||||||
|
if (!state.ModelData.Customize.Highlights)
|
||||||
|
Editor.ChangeParameter(state, CustomizeParameterFlag.HairHighlight,
|
||||||
|
state.ModelData.Parameters[CustomizeParameterFlag.HairDiffuse],
|
||||||
|
state.Sources[CustomizeParameterFlag.HairDiffuse], out _, settings.Key);
|
||||||
|
|
||||||
|
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||||
|
{
|
||||||
|
if (mergedDesign.Design.DoApplyEquip(slot))
|
||||||
|
if (!settings.RespectManual || state.Sources[slot, false] is not StateSource.Manual)
|
||||||
|
Editor.ChangeItem(state, slot, mergedDesign.Design.DesignData.Item(slot),
|
||||||
|
Source(slot.ToState()), out _, settings.Key);
|
||||||
|
|
||||||
|
if (mergedDesign.Design.DoApplyStain(slot))
|
||||||
|
if (!settings.RespectManual || state.Sources[slot, true] is not StateSource.Manual)
|
||||||
|
Editor.ChangeStain(state, slot, mergedDesign.Design.DesignData.Stain(slot),
|
||||||
|
Source(slot.ToState(true)), out _, settings.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var weaponSlot in EquipSlotExtensions.WeaponSlots)
|
||||||
|
{
|
||||||
|
if (mergedDesign.Design.DoApplyStain(weaponSlot))
|
||||||
|
if (!settings.RespectManual || state.Sources[weaponSlot, true] is not StateSource.Manual)
|
||||||
|
Editor.ChangeStain(state, weaponSlot, mergedDesign.Design.DesignData.Stain(weaponSlot),
|
||||||
|
Source(weaponSlot.ToState(true)), out _, settings.Key);
|
||||||
|
|
||||||
|
if (!mergedDesign.Design.DoApplyEquip(weaponSlot))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (settings.RespectManual && state.Sources[weaponSlot, false] is StateSource.Manual)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var currentType = state.ModelData.Item(weaponSlot).Type;
|
||||||
|
if (!settings.FromJobChange && mergedDesign.Weapons.TryGetValue(currentType, out var weapon))
|
||||||
|
{
|
||||||
|
var source = settings.UseSingleSource ? settings.Source :
|
||||||
|
weapon.Item2 is StateSource.Game ? StateSource.Game : weapon.Item2;
|
||||||
|
Editor.ChangeItem(state, weaponSlot, weapon.Item1, source, out _,
|
||||||
|
settings.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.FromJobChange)
|
||||||
|
jobChange.Set(state, mergedDesign.Weapons.Values.Select(m =>
|
||||||
|
(m.Item1, settings.UseSingleSource ? settings.Source :
|
||||||
|
m.Item2 is StateSource.Game ? StateSource.Game : m.Item2)));
|
||||||
|
|
||||||
|
foreach (var meta in MetaExtensions.AllRelevant)
|
||||||
|
{
|
||||||
|
if (!settings.RespectManual || state.Sources[meta] is not StateSource.Manual)
|
||||||
|
Editor.ChangeMetaState(state, meta, mergedDesign.Design.DesignData.GetMeta(meta), Source(meta), out _, settings.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var actors = settings.Source is StateSource.Manual or StateSource.Ipc
|
||||||
|
? Applier.ApplyAll(state, requiresRedraw, false)
|
||||||
|
: ActorData.Invalid;
|
||||||
|
|
||||||
|
Glamourer.Log.Verbose(
|
||||||
|
$"Applied design to {state.Identifier.Incognito(null)}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
|
StateChanged.Invoke(StateChanged.Type.Design, state.Sources[MetaIndex.Wetness], state, actors, mergedDesign.Design);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
StateSource Source(StateIndex index)
|
||||||
|
{
|
||||||
|
if (settings.UseSingleSource)
|
||||||
|
return settings.Source;
|
||||||
|
|
||||||
|
var source = mergedDesign.Sources[index];
|
||||||
|
return source is StateSource.Game ? StateSource.Game : settings.Source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyDesign(object data, DesignBase design, ApplySettings settings)
|
||||||
|
=> ApplyDesign(data, new MergedDesign(design), settings with
|
||||||
|
{
|
||||||
|
FromJobChange = false,
|
||||||
|
RespectManual = false,
|
||||||
|
UseSingleSource = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary> Apply offhand item and potentially gauntlets if configured. </summary>
|
||||||
|
private void ApplyMainhandPeriphery(ActorState state, EquipItem? newMainhand, ApplySettings settings)
|
||||||
|
{
|
||||||
|
if (!Config.ChangeEntireItem || settings.Source is not StateSource.Manual)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var mh = newMainhand ?? state.ModelData.Item(EquipSlot.MainHand);
|
||||||
|
var offhand = newMainhand != null ? Items.GetDefaultOffhand(mh) : state.ModelData.Item(EquipSlot.OffHand);
|
||||||
|
if (offhand.Valid)
|
||||||
|
ChangeEquip(state, EquipSlot.OffHand, offhand, state.ModelData.Stain(EquipSlot.OffHand), settings);
|
||||||
|
|
||||||
|
if (mh is { Type: FullEquipType.Fists } && Items.ItemData.Tertiary.TryGetValue(mh.ItemId, out var gauntlets))
|
||||||
|
ChangeEquip(state, EquipSlot.Hands, newMainhand != null ? gauntlets : state.ModelData.Item(EquipSlot.Hands),
|
||||||
|
state.ModelData.Stain(EquipSlot.Hands), settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,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 void OnCustomizeChange(Model model, ref CustomizeArray customize)
|
||||||
{
|
{
|
||||||
if (!model.IsHuman)
|
if (!model.IsHuman)
|
||||||
return;
|
return;
|
||||||
|
|
@ -164,7 +164,7 @@ public class StateListener : IDisposable
|
||||||
var model = state.ModelData.Customize;
|
var model = state.ModelData.Customize;
|
||||||
if (customize.Gender != model.Gender || customize.Clan != model.Clan)
|
if (customize.Gender != model.Gender || customize.Clan != model.Clan)
|
||||||
{
|
{
|
||||||
_manager.ChangeCustomize(state, in customize, CustomizeFlagExtensions.AllRelevant, StateSource.Game);
|
_manager.ChangeEntireCustomize(state, in customize, CustomizeFlagExtensions.All, ApplySettings.Game);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -178,7 +178,7 @@ public class StateListener : IDisposable
|
||||||
if (newValue != oldValue)
|
if (newValue != oldValue)
|
||||||
{
|
{
|
||||||
if (set.Validate(index, newValue, out _, model.Face))
|
if (set.Validate(index, newValue, out _, model.Face))
|
||||||
_manager.ChangeCustomize(state, index, newValue, StateSource.Game);
|
_manager.ChangeCustomize(state, index, newValue, ApplySettings.Game);
|
||||||
else
|
else
|
||||||
customize[index] = oldValue;
|
customize[index] = oldValue;
|
||||||
}
|
}
|
||||||
|
|
@ -243,8 +243,8 @@ public class StateListener : IDisposable
|
||||||
var changed = changedItem.Weapon(stain);
|
var changed = changedItem.Weapon(stain);
|
||||||
if (current.Value == changed.Value && state.Sources[slot, false] is not StateSource.Fixed and not StateSource.Ipc)
|
if (current.Value == changed.Value && state.Sources[slot, false] is not StateSource.Fixed and not StateSource.Ipc)
|
||||||
{
|
{
|
||||||
_manager.ChangeItem(state, slot, currentItem, StateSource.Game);
|
_manager.ChangeItem(state, slot, currentItem, ApplySettings.Game);
|
||||||
_manager.ChangeStain(state, slot, current.Stain, StateSource.Game);
|
_manager.ChangeStain(state, slot, current.Stain, ApplySettings.Game);
|
||||||
switch (slot)
|
switch (slot)
|
||||||
{
|
{
|
||||||
case EquipSlot.MainHand:
|
case EquipSlot.MainHand:
|
||||||
|
|
@ -287,12 +287,12 @@ public class StateListener : IDisposable
|
||||||
case UpdateState.Transformed: break;
|
case UpdateState.Transformed: break;
|
||||||
case UpdateState.Change:
|
case UpdateState.Change:
|
||||||
if (state.Sources[slot, false] is not StateSource.Fixed and not StateSource.Ipc)
|
if (state.Sources[slot, false] is not StateSource.Fixed and not StateSource.Ipc)
|
||||||
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), StateSource.Game);
|
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), ApplySettings.Game);
|
||||||
else
|
else
|
||||||
apply = true;
|
apply = true;
|
||||||
|
|
||||||
if (state.Sources[slot, true] is not StateSource.Fixed and not StateSource.Ipc)
|
if (state.Sources[slot, true] is not StateSource.Fixed and not StateSource.Ipc)
|
||||||
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), StateSource.Game);
|
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), ApplySettings.Game);
|
||||||
else
|
else
|
||||||
apply = true;
|
apply = true;
|
||||||
break;
|
break;
|
||||||
|
|
@ -386,12 +386,12 @@ public class StateListener : IDisposable
|
||||||
case UpdateState.Change:
|
case UpdateState.Change:
|
||||||
var apply = false;
|
var apply = false;
|
||||||
if (state.Sources[slot, false] is not StateSource.Fixed and not StateSource.Ipc)
|
if (state.Sources[slot, false] is not StateSource.Fixed and not StateSource.Ipc)
|
||||||
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), StateSource.Game);
|
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), ApplySettings.Game);
|
||||||
else
|
else
|
||||||
apply = true;
|
apply = true;
|
||||||
|
|
||||||
if (state.Sources[slot, true] is not StateSource.Fixed and not StateSource.Ipc)
|
if (state.Sources[slot, true] is not StateSource.Fixed and not StateSource.Ipc)
|
||||||
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), StateSource.Game);
|
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), ApplySettings.Game);
|
||||||
else
|
else
|
||||||
apply = true;
|
apply = true;
|
||||||
|
|
||||||
|
|
@ -420,7 +420,7 @@ public class StateListener : IDisposable
|
||||||
{
|
{
|
||||||
case UpdateState.Change:
|
case UpdateState.Change:
|
||||||
if (state.Sources[slot] is not StateSource.Fixed and not StateSource.Ipc)
|
if (state.Sources[slot] is not StateSource.Fixed and not StateSource.Ipc)
|
||||||
_manager.ChangeCrest(state, slot, state.BaseData.Crest(slot), StateSource.Game);
|
_manager.ChangeCrest(state, slot, state.BaseData.Crest(slot), ApplySettings.Game);
|
||||||
else
|
else
|
||||||
value = state.ModelData.Crest(slot);
|
value = state.ModelData.Crest(slot);
|
||||||
break;
|
break;
|
||||||
|
|
@ -568,7 +568,7 @@ public class StateListener : IDisposable
|
||||||
if (state.Sources[MetaIndex.VisorState] is StateSource.Fixed or StateSource.Ipc)
|
if (state.Sources[MetaIndex.VisorState] is StateSource.Fixed or StateSource.Ipc)
|
||||||
value = state.ModelData.IsVisorToggled();
|
value = state.ModelData.IsVisorToggled();
|
||||||
else
|
else
|
||||||
_manager.ChangeMeta(state, MetaIndex.VisorState, value, StateSource.Game);
|
_manager.ChangeMetaState(state, MetaIndex.VisorState, value, ApplySettings.Game);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -601,7 +601,7 @@ public class StateListener : IDisposable
|
||||||
if (state.Sources[MetaIndex.HatState] is StateSource.Fixed or StateSource.Ipc)
|
if (state.Sources[MetaIndex.HatState] is StateSource.Fixed or StateSource.Ipc)
|
||||||
value = state.ModelData.IsHatVisible();
|
value = state.ModelData.IsHatVisible();
|
||||||
else
|
else
|
||||||
_manager.ChangeMeta(state, MetaIndex.HatState, value, StateSource.Game);
|
_manager.ChangeMetaState(state, MetaIndex.HatState, value, ApplySettings.Game);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -634,7 +634,7 @@ public class StateListener : IDisposable
|
||||||
if (state.Sources[MetaIndex.WeaponState] is StateSource.Fixed or StateSource.Ipc)
|
if (state.Sources[MetaIndex.WeaponState] is StateSource.Fixed or StateSource.Ipc)
|
||||||
value = state.ModelData.IsWeaponVisible();
|
value = state.ModelData.IsWeaponVisible();
|
||||||
else
|
else
|
||||||
_manager.ChangeMeta(state, MetaIndex.WeaponState, value, StateSource.Game);
|
_manager.ChangeMetaState(state, MetaIndex.WeaponState, value, ApplySettings.Game);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -724,7 +724,7 @@ public class StateListener : IDisposable
|
||||||
_customizeState = null;
|
_customizeState = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void ApplyParameters(ActorState state, Model model)
|
private void ApplyParameters(ActorState state, Model model)
|
||||||
{
|
{
|
||||||
if (!model.IsHuman)
|
if (!model.IsHuman)
|
||||||
return;
|
return;
|
||||||
|
|
@ -737,11 +737,11 @@ public class StateListener : IDisposable
|
||||||
{
|
{
|
||||||
case StateSource.Game:
|
case StateSource.Game:
|
||||||
if (state.BaseData.Parameters.Set(flag, newValue))
|
if (state.BaseData.Parameters.Set(flag, newValue))
|
||||||
_manager.ChangeCustomizeParameter(state, flag, newValue, StateSource.Game);
|
_manager.ChangeCustomizeParameter(state, flag, newValue, ApplySettings.Game);
|
||||||
break;
|
break;
|
||||||
case StateSource.Manual:
|
case StateSource.Manual:
|
||||||
if (state.BaseData.Parameters.Set(flag, newValue))
|
if (state.BaseData.Parameters.Set(flag, newValue))
|
||||||
_manager.ChangeCustomizeParameter(state, flag, newValue, StateSource.Game);
|
_manager.ChangeCustomizeParameter(state, flag, newValue, ApplySettings.Game);
|
||||||
else if (_config.UseAdvancedParameters)
|
else if (_config.UseAdvancedParameters)
|
||||||
model.ApplySingleParameterData(flag, state.ModelData.Parameters);
|
model.ApplySingleParameterData(flag, state.ModelData.Parameters);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -12,17 +12,17 @@ using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
namespace Glamourer.State;
|
namespace Glamourer.State;
|
||||||
|
|
||||||
public class StateManager(
|
public sealed class StateManager(
|
||||||
ActorManager _actors,
|
ActorManager _actors,
|
||||||
ItemManager _items,
|
ItemManager items,
|
||||||
StateChanged _event,
|
StateChanged @event,
|
||||||
StateApplier _applier,
|
StateApplier applier,
|
||||||
StateEditor _editor,
|
InternalStateEditor editor,
|
||||||
HumanModelList _humans,
|
HumanModelList _humans,
|
||||||
ICondition _condition,
|
|
||||||
IClientState _clientState,
|
IClientState _clientState,
|
||||||
Configuration _config)
|
Configuration config,
|
||||||
: IReadOnlyDictionary<ActorIdentifier, ActorState>
|
JobChangeState jobChange)
|
||||||
|
: StateEditor(editor, applier, @event, jobChange, config, items), IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
{
|
{
|
||||||
private readonly Dictionary<ActorIdentifier, ActorState> _states = [];
|
private readonly Dictionary<ActorIdentifier, ActorState> _states = [];
|
||||||
|
|
||||||
|
|
@ -93,7 +93,7 @@ public class StateManager(
|
||||||
// If the given actor is not a character, just return a default character.
|
// If the given actor is not a character, just return a default character.
|
||||||
if (!actor.IsCharacter)
|
if (!actor.IsCharacter)
|
||||||
{
|
{
|
||||||
ret.SetDefaultEquipment(_items);
|
ret.SetDefaultEquipment(Items);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,7 +127,7 @@ public class StateManager(
|
||||||
|
|
||||||
// We can not use the head slot data from the draw object if the hat is hidden.
|
// We can not use the head slot data from the draw object if the hat is hidden.
|
||||||
var head = ret.IsHatVisible() || ignoreHatState ? model.GetArmor(EquipSlot.Head) : actor.GetArmor(EquipSlot.Head);
|
var head = ret.IsHatVisible() || ignoreHatState ? model.GetArmor(EquipSlot.Head) : actor.GetArmor(EquipSlot.Head);
|
||||||
var headItem = _items.Identify(EquipSlot.Head, head.Set, head.Variant);
|
var headItem = Items.Identify(EquipSlot.Head, head.Set, head.Variant);
|
||||||
ret.SetItem(EquipSlot.Head, headItem);
|
ret.SetItem(EquipSlot.Head, headItem);
|
||||||
ret.SetStain(EquipSlot.Head, head.Stain);
|
ret.SetStain(EquipSlot.Head, head.Stain);
|
||||||
|
|
||||||
|
|
@ -135,7 +135,7 @@ public class StateManager(
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots.Skip(1))
|
foreach (var slot in EquipSlotExtensions.EqdpSlots.Skip(1))
|
||||||
{
|
{
|
||||||
var armor = model.GetArmor(slot);
|
var armor = model.GetArmor(slot);
|
||||||
var item = _items.Identify(slot, armor.Set, armor.Variant);
|
var item = Items.Identify(slot, armor.Set, armor.Variant);
|
||||||
ret.SetItem(slot, item);
|
ret.SetItem(slot, item);
|
||||||
ret.SetStain(slot, armor.Stain);
|
ret.SetStain(slot, armor.Stain);
|
||||||
}
|
}
|
||||||
|
|
@ -157,7 +157,7 @@ public class StateManager(
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||||
{
|
{
|
||||||
var armor = actor.GetArmor(slot);
|
var armor = actor.GetArmor(slot);
|
||||||
var item = _items.Identify(slot, armor.Set, armor.Variant);
|
var item = Items.Identify(slot, armor.Set, armor.Variant);
|
||||||
ret.SetItem(slot, item);
|
ret.SetItem(slot, item);
|
||||||
ret.SetStain(slot, armor.Stain);
|
ret.SetStain(slot, armor.Stain);
|
||||||
}
|
}
|
||||||
|
|
@ -172,8 +172,8 @@ public class StateManager(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the weapons regardless of source.
|
// Set the weapons regardless of source.
|
||||||
var mainItem = _items.Identify(EquipSlot.MainHand, main.Skeleton, main.Weapon, main.Variant);
|
var mainItem = Items.Identify(EquipSlot.MainHand, main.Skeleton, main.Weapon, main.Variant);
|
||||||
var offItem = _items.Identify(EquipSlot.OffHand, off.Skeleton, off.Weapon, off.Variant, mainItem.Type);
|
var offItem = Items.Identify(EquipSlot.OffHand, off.Skeleton, off.Weapon, off.Variant, mainItem.Type);
|
||||||
ret.SetItem(EquipSlot.MainHand, mainItem);
|
ret.SetItem(EquipSlot.MainHand, mainItem);
|
||||||
ret.SetStain(EquipSlot.MainHand, main.Stain);
|
ret.SetStain(EquipSlot.MainHand, main.Stain);
|
||||||
ret.SetItem(EquipSlot.OffHand, offItem);
|
ret.SetItem(EquipSlot.OffHand, offItem);
|
||||||
|
|
@ -197,7 +197,7 @@ public class StateManager(
|
||||||
if (mainhand.Skeleton.Id is < 1601 or >= 1651)
|
if (mainhand.Skeleton.Id is < 1601 or >= 1651)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var gauntlets = _items.Identify(EquipSlot.Hands, offhand.Skeleton, (Variant)offhand.Weapon.Id);
|
var gauntlets = Items.Identify(EquipSlot.Hands, offhand.Skeleton, (Variant)offhand.Weapon.Id);
|
||||||
offhand.Skeleton = (PrimaryId)(mainhand.Skeleton.Id + 50);
|
offhand.Skeleton = (PrimaryId)(mainhand.Skeleton.Id + 50);
|
||||||
offhand.Variant = mainhand.Variant;
|
offhand.Variant = mainhand.Variant;
|
||||||
offhand.Weapon = mainhand.Weapon;
|
offhand.Weapon = mainhand.Weapon;
|
||||||
|
|
@ -205,251 +205,10 @@ public class StateManager(
|
||||||
ret.SetStain(EquipSlot.Hands, mainhand.Stain);
|
ret.SetStain(EquipSlot.Hands, mainhand.Stain);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Change Values
|
|
||||||
|
|
||||||
/// <summary> Turn an actor human. </summary>
|
/// <summary> Turn an actor human. </summary>
|
||||||
public void TurnHuman(ActorState state, StateSource source, uint key = 0)
|
public void TurnHuman(ActorState state, StateSource source, uint key = 0)
|
||||||
=> ChangeModelId(state, 0, CustomizeArray.Default, nint.Zero, source, key);
|
=> ChangeModelId(state, 0, CustomizeArray.Default, nint.Zero, source, key);
|
||||||
|
|
||||||
/// <summary> Turn an actor to. </summary>
|
|
||||||
public void ChangeModelId(ActorState state, uint modelId, CustomizeArray customize, nint equipData, StateSource source,
|
|
||||||
uint key = 0)
|
|
||||||
{
|
|
||||||
if (!_editor.ChangeModelId(state, modelId, customize, equipData, source, out var old, key))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var actors = _applier.ForceRedraw(state, source is StateSource.Manual or StateSource.Ipc);
|
|
||||||
Glamourer.Log.Verbose(
|
|
||||||
$"Set model id in state {state.Identifier.Incognito(null)} from {old} to {modelId}. [Affecting {actors.ToLazyString("nothing")}.]");
|
|
||||||
_event.Invoke(StateChanged.Type.Model, source, state, actors, (old, modelId));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Change a customization value. </summary>
|
|
||||||
public void ChangeCustomize(ActorState state, CustomizeIndex idx, CustomizeValue value, StateSource source, uint key = 0)
|
|
||||||
{
|
|
||||||
if (!_editor.ChangeCustomize(state, idx, value, source, out var old, key))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var actors = _applier.ChangeCustomize(state, source is StateSource.Manual or StateSource.Ipc);
|
|
||||||
Glamourer.Log.Verbose(
|
|
||||||
$"Set {idx.ToDefaultName()} customizations in state {state.Identifier.Incognito(null)} from {old.Value} to {value.Value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
|
||||||
_event.Invoke(StateChanged.Type.Customize, source, state, actors, (old, value, idx));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Change an entire customization array according to flags. </summary>
|
|
||||||
public void ChangeCustomize(ActorState state, in CustomizeArray customizeInput, CustomizeFlag apply, StateSource source,
|
|
||||||
uint key = 0)
|
|
||||||
{
|
|
||||||
if (!_editor.ChangeHumanCustomize(state, customizeInput, apply, source, out var old, out var applied, key))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var actors = _applier.ChangeCustomize(state, source is StateSource.Manual or StateSource.Ipc);
|
|
||||||
Glamourer.Log.Verbose(
|
|
||||||
$"Set {applied} customizations in state {state.Identifier.Incognito(null)} from {old} to {customizeInput}. [Affecting {actors.ToLazyString("nothing")}.]");
|
|
||||||
_event.Invoke(StateChanged.Type.EntireCustomize, source, state, actors, (old, applied));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Change a single piece of equipment without stain. </summary>
|
|
||||||
/// <remarks> Do not use this in the same frame as ChangeStain, use <see cref="ChangeEquip(ActorState,EquipSlot,EquipItem,StainId,StateSource,uint)"/> instead. </remarks>
|
|
||||||
public void ChangeItem(ActorState state, EquipSlot slot, EquipItem item, StateSource source, uint key = 0)
|
|
||||||
{
|
|
||||||
if (!_editor.ChangeItem(state, slot, item, source, out var old, key))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var type = slot.ToIndex() < 10 ? StateChanged.Type.Equip : StateChanged.Type.Weapon;
|
|
||||||
var actors = type is StateChanged.Type.Equip
|
|
||||||
? _applier.ChangeArmor(state, slot, source is StateSource.Manual or StateSource.Ipc)
|
|
||||||
: _applier.ChangeWeapon(state, slot, source is StateSource.Manual or StateSource.Ipc,
|
|
||||||
item.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType));
|
|
||||||
|
|
||||||
if (slot is EquipSlot.MainHand)
|
|
||||||
ApplyMainhandPeriphery(state, item, source, key);
|
|
||||||
|
|
||||||
Glamourer.Log.Verbose(
|
|
||||||
$"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId}). [Affecting {actors.ToLazyString("nothing")}.]");
|
|
||||||
_event.Invoke(type, source, state, actors, (old, item, slot));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Change a single piece of equipment including stain. </summary>
|
|
||||||
public void ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainId stain, StateSource source, uint key = 0)
|
|
||||||
{
|
|
||||||
if (!_editor.ChangeEquip(state, slot, item, stain, source, out var old, out var oldStain, key))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var type = slot.ToIndex() < 10 ? StateChanged.Type.Equip : StateChanged.Type.Weapon;
|
|
||||||
var actors = type is StateChanged.Type.Equip
|
|
||||||
? _applier.ChangeArmor(state, slot, source is StateSource.Manual or StateSource.Ipc)
|
|
||||||
: _applier.ChangeWeapon(state, slot, source is StateSource.Manual or StateSource.Ipc,
|
|
||||||
item.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType));
|
|
||||||
|
|
||||||
if (slot is EquipSlot.MainHand)
|
|
||||||
ApplyMainhandPeriphery(state, item, source, key);
|
|
||||||
|
|
||||||
Glamourer.Log.Verbose(
|
|
||||||
$"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId}) and its stain from {oldStain.Id} to {stain.Id}. [Affecting {actors.ToLazyString("nothing")}.]");
|
|
||||||
_event.Invoke(type, source, state, actors, (old, item, slot));
|
|
||||||
_event.Invoke(StateChanged.Type.Stain, source, state, actors, (oldStain, stain, slot));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Change only the stain of an equipment piece. </summary>
|
|
||||||
/// <remarks> Do not use this in the same frame as ChangeEquip, use <see cref="ChangeEquip(ActorState,EquipSlot,EquipItem,StainId,StateSource,uint)"/> instead. </remarks>
|
|
||||||
public void ChangeStain(ActorState state, EquipSlot slot, StainId stain, StateSource source, uint key = 0)
|
|
||||||
{
|
|
||||||
if (!_editor.ChangeStain(state, slot, stain, source, out var old, key))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var actors = _applier.ChangeStain(state, slot, source is StateSource.Manual or StateSource.Ipc);
|
|
||||||
|
|
||||||
Glamourer.Log.Verbose(
|
|
||||||
$"Set {slot.ToName()} stain in state {state.Identifier.Incognito(null)} from {old.Id} to {stain.Id}. [Affecting {actors.ToLazyString("nothing")}.]");
|
|
||||||
_event.Invoke(StateChanged.Type.Stain, source, state, actors, (old, stain, slot));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Change the crest of an equipment piece. </summary>
|
|
||||||
public void ChangeCrest(ActorState state, CrestFlag slot, bool crest, StateSource source, uint key = 0)
|
|
||||||
{
|
|
||||||
if (!_editor.ChangeCrest(state, slot, crest, source, out var old, key))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var actors = _applier.ChangeCrests(state, source is StateSource.Manual or StateSource.Ipc);
|
|
||||||
Glamourer.Log.Verbose(
|
|
||||||
$"Set {slot.ToLabel()} crest in state {state.Identifier.Incognito(null)} from {old} to {crest}. [Affecting {actors.ToLazyString("nothing")}.]");
|
|
||||||
_event.Invoke(StateChanged.Type.Crest, source, state, actors, (old, crest, slot));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Change the crest of an equipment piece. </summary>
|
|
||||||
public void ChangeCustomizeParameter(ActorState state, CustomizeParameterFlag flag, CustomizeParameterValue value,
|
|
||||||
StateSource source, uint key = 0)
|
|
||||||
{
|
|
||||||
// Also apply main color to highlights when highlights is off.
|
|
||||||
if (!state.ModelData.Customize.Highlights && flag is CustomizeParameterFlag.HairDiffuse)
|
|
||||||
ChangeCustomizeParameter(state, CustomizeParameterFlag.HairHighlight, value, source, key);
|
|
||||||
|
|
||||||
if (!_editor.ChangeParameter(state, flag, value, source, out var old, key))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var @new = state.ModelData.Parameters[flag];
|
|
||||||
var actors = _applier.ChangeParameters(state, flag, source is StateSource.Manual or StateSource.Ipc);
|
|
||||||
Glamourer.Log.Verbose(
|
|
||||||
$"Set {flag} crest in state {state.Identifier.Incognito(null)} from {old} to {@new}. [Affecting {actors.ToLazyString("nothing")}.]");
|
|
||||||
_event.Invoke(StateChanged.Type.Parameter, source, state, actors, (old, @new, flag));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Change meta state. </summary>
|
|
||||||
public void ChangeMeta(ActorState state, MetaIndex meta, bool value, StateSource source, uint key = 0)
|
|
||||||
{
|
|
||||||
if (!_editor.ChangeMetaState(state, meta, value, source, out var old, key))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var actors = _applier.ChangeMetaState(state, meta, source is StateSource.Manual or StateSource.Ipc);
|
|
||||||
Glamourer.Log.Verbose(
|
|
||||||
$"Set Head Gear Visibility in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
|
||||||
_event.Invoke(StateChanged.Type.Other, source, state, actors, (old, value, MetaIndex.HatState));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public void ApplyDesign(DesignBase design, ActorState state, StateSource source, uint key = 0)
|
|
||||||
{
|
|
||||||
if (!_editor.ChangeModelId(state, design.DesignData.ModelId, design.DesignData.Customize, design.GetDesignDataRef().GetEquipmentPtr(),
|
|
||||||
source,
|
|
||||||
out var oldModelId, key))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var redraw = oldModelId != design.DesignData.ModelId || !design.DesignData.IsHuman;
|
|
||||||
if (design.DoApplyMeta(MetaIndex.Wetness))
|
|
||||||
_editor.ChangeMetaState(state, MetaIndex.Wetness, design.DesignData.IsWet(), source, out _, key);
|
|
||||||
|
|
||||||
if (state.ModelData.IsHuman)
|
|
||||||
{
|
|
||||||
if (design.DoApplyMeta(MetaIndex.HatState))
|
|
||||||
_editor.ChangeMetaState(state, MetaIndex.HatState, design.DesignData.IsHatVisible(), source, out _, key);
|
|
||||||
if (design.DoApplyMeta(MetaIndex.WeaponState))
|
|
||||||
_editor.ChangeMetaState(state, MetaIndex.WeaponState, design.DesignData.IsWeaponVisible(), source, out _, key);
|
|
||||||
if (design.DoApplyMeta(MetaIndex.VisorState))
|
|
||||||
_editor.ChangeMetaState(state, MetaIndex.VisorState, design.DesignData.IsVisorToggled(), source, out _, key);
|
|
||||||
|
|
||||||
var flags = state.AllowsRedraw(_condition)
|
|
||||||
? design.ApplyCustomize
|
|
||||||
: design.ApplyCustomize & ~CustomizeFlagExtensions.RedrawRequired;
|
|
||||||
_editor.ChangeHumanCustomize(state, design.DesignData.Customize, flags, source, out _, out var applied, key);
|
|
||||||
redraw |= applied.RequiresRedraw();
|
|
||||||
|
|
||||||
foreach (var slot in EquipSlotExtensions.FullSlots)
|
|
||||||
HandleEquip(slot, design.DoApplyEquip(slot), design.DoApplyStain(slot));
|
|
||||||
|
|
||||||
foreach (var slot in CrestExtensions.AllRelevantSet.Where(design.DoApplyCrest))
|
|
||||||
_editor.ChangeCrest(state, slot, design.DesignData.Crest(slot), source, out _, key);
|
|
||||||
|
|
||||||
var paramSource = source is StateSource.Manual
|
|
||||||
? StateSource.Pending
|
|
||||||
: source;
|
|
||||||
|
|
||||||
foreach (var flag in CustomizeParameterExtensions.AllFlags.Where(design.DoApplyParameter))
|
|
||||||
_editor.ChangeParameter(state, flag, design.DesignData.Parameters[flag], paramSource, out _, key);
|
|
||||||
|
|
||||||
// Do not apply highlights from a design if highlights is unchecked.
|
|
||||||
if (!state.ModelData.Customize.Highlights)
|
|
||||||
_editor.ChangeParameter(state, CustomizeParameterFlag.HairHighlight,
|
|
||||||
state.ModelData.Parameters[CustomizeParameterFlag.HairDiffuse],
|
|
||||||
state.Sources[CustomizeParameterFlag.HairDiffuse], out _, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
var actors = ApplyAll(state, redraw, false);
|
|
||||||
Glamourer.Log.Verbose(
|
|
||||||
$"Applied design to {state.Identifier.Incognito(null)}. [Affecting {actors.ToLazyString("nothing")}.]");
|
|
||||||
_event.Invoke(StateChanged.Type.Design, state.Sources[MetaIndex.Wetness], state, actors, design);
|
|
||||||
return;
|
|
||||||
|
|
||||||
void HandleEquip(EquipSlot slot, bool applyPiece, bool applyStain)
|
|
||||||
{
|
|
||||||
var unused = (applyPiece, applyStain) switch
|
|
||||||
{
|
|
||||||
(false, false) => false,
|
|
||||||
(true, false) => _editor.ChangeItem(state, slot, design.DesignData.Item(slot), source, out _, key),
|
|
||||||
(false, true) => _editor.ChangeStain(state, slot, design.DesignData.Stain(slot), source, out _, key),
|
|
||||||
(true, true) => _editor.ChangeEquip(state, slot, design.DesignData.Item(slot), design.DesignData.Stain(slot), source, out _,
|
|
||||||
out _, key),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ActorData ApplyAll(ActorState state, bool redraw, bool withLock)
|
|
||||||
{
|
|
||||||
var actors = _applier.ChangeMetaState(state, MetaIndex.Wetness, true);
|
|
||||||
if (redraw)
|
|
||||||
{
|
|
||||||
if (withLock)
|
|
||||||
state.TempLock();
|
|
||||||
_applier.ForceRedraw(actors);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_applier.ChangeCustomize(actors, state.ModelData.Customize);
|
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
|
||||||
{
|
|
||||||
_applier.ChangeArmor(actors, slot, state.ModelData.Armor(slot), state.Sources[slot, false] is not StateSource.Ipc,
|
|
||||||
state.ModelData.IsHatVisible());
|
|
||||||
}
|
|
||||||
|
|
||||||
var mainhandActors = state.ModelData.MainhandType != state.BaseData.MainhandType ? actors.OnlyGPose() : actors;
|
|
||||||
_applier.ChangeMainhand(mainhandActors, state.ModelData.Item(EquipSlot.MainHand), state.ModelData.Stain(EquipSlot.MainHand));
|
|
||||||
var offhandActors = state.ModelData.OffhandType != state.BaseData.OffhandType ? actors.OnlyGPose() : actors;
|
|
||||||
_applier.ChangeOffhand(offhandActors, state.ModelData.Item(EquipSlot.OffHand), state.ModelData.Stain(EquipSlot.OffHand));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.ModelData.IsHuman)
|
|
||||||
{
|
|
||||||
_applier.ChangeMetaState(actors, MetaIndex.HatState, state.ModelData.IsHatVisible());
|
|
||||||
_applier.ChangeMetaState(actors, MetaIndex.WeaponState, state.ModelData.IsWeaponVisible());
|
|
||||||
_applier.ChangeMetaState(actors, MetaIndex.VisorState, state.ModelData.IsVisorToggled());
|
|
||||||
_applier.ChangeCrests(actors, state.ModelData.CrestVisibility);
|
|
||||||
_applier.ChangeParameters(actors, state.OnlyChangedParameters(), state.ModelData.Parameters, state.IsLocked);
|
|
||||||
}
|
|
||||||
|
|
||||||
return actors;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetState(ActorState state, StateSource source, uint key = 0)
|
public void ResetState(ActorState state, StateSource source, uint key = 0)
|
||||||
{
|
{
|
||||||
if (!state.Unlock(key))
|
if (!state.Unlock(key))
|
||||||
|
|
@ -481,11 +240,11 @@ public class StateManager(
|
||||||
|
|
||||||
var actors = ActorData.Invalid;
|
var actors = ActorData.Invalid;
|
||||||
if (source is StateSource.Manual or StateSource.Ipc)
|
if (source is StateSource.Manual or StateSource.Ipc)
|
||||||
actors = ApplyAll(state, redraw, true);
|
actors = Applier.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, source, state, actors, null);
|
StateChanged.Invoke(StateChanged.Type.Reset, source, state, actors, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetAdvancedState(ActorState state, StateSource source, uint key = 0)
|
public void ResetAdvancedState(ActorState state, StateSource source, uint key = 0)
|
||||||
|
|
@ -500,10 +259,10 @@ public class StateManager(
|
||||||
|
|
||||||
var actors = ActorData.Invalid;
|
var actors = ActorData.Invalid;
|
||||||
if (source is StateSource.Manual or StateSource.Ipc)
|
if (source is StateSource.Manual or StateSource.Ipc)
|
||||||
actors = _applier.ChangeParameters(state, CustomizeParameterExtensions.All, true);
|
actors = Applier.ChangeParameters(state, CustomizeParameterExtensions.All, true);
|
||||||
Glamourer.Log.Verbose(
|
Glamourer.Log.Verbose(
|
||||||
$"Reset advanced customization state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
|
$"Reset advanced customization state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
_event.Invoke(StateChanged.Type.Reset, source, state, actors, null);
|
StateChanged.Invoke(StateChanged.Type.Reset, source, state, actors, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetStateFixed(ActorState state, bool respectManualPalettes, uint key = 0)
|
public void ResetStateFixed(ActorState state, bool respectManualPalettes, uint key = 0)
|
||||||
|
|
@ -583,26 +342,11 @@ public class StateManager(
|
||||||
if (!GetOrCreate(actor, out var state))
|
if (!GetOrCreate(actor, out var state))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ApplyAll(state, !actor.Model.IsHuman || CustomizeArray.Compare(actor.Model.GetCustomize(), state.ModelData.Customize).RequiresRedraw(),
|
Applier.ApplyAll(state,
|
||||||
|
!actor.Model.IsHuman || CustomizeArray.Compare(actor.Model.GetCustomize(), state.ModelData.Customize).RequiresRedraw(),
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteState(ActorIdentifier identifier)
|
public void DeleteState(ActorIdentifier identifier)
|
||||||
=> _states.Remove(identifier);
|
=> _states.Remove(identifier);
|
||||||
|
|
||||||
/// <summary> Apply offhand item and potentially gauntlets if configured. </summary>
|
|
||||||
private void ApplyMainhandPeriphery(ActorState state, EquipItem? newMainhand, StateSource source, uint key = 0)
|
|
||||||
{
|
|
||||||
if (!_config.ChangeEntireItem || source is not StateSource.Manual)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var mh = newMainhand ?? state.ModelData.Item(EquipSlot.MainHand);
|
|
||||||
var offhand = newMainhand != null ? _items.GetDefaultOffhand(mh) : state.ModelData.Item(EquipSlot.OffHand);
|
|
||||||
if (offhand.Valid)
|
|
||||||
ChangeEquip(state, EquipSlot.OffHand, offhand, state.ModelData.Stain(EquipSlot.OffHand), source, key);
|
|
||||||
|
|
||||||
if (mh is { Type: FullEquipType.Fists } && _items.ItemData.Tertiary.TryGetValue(mh.ItemId, out var gauntlets))
|
|
||||||
ChangeEquip(state, EquipSlot.Hands, newMainhand != null ? gauntlets : state.ModelData.Item(EquipSlot.Hands),
|
|
||||||
state.ModelData.Stain(EquipSlot.Hands), source, key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue