From 3d421881f6682fa51c5eca18c07620e9b75f3b42 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Thu, 28 Mar 2024 15:23:19 +0100 Subject: [PATCH] Maybe fix weapon behavior with multiple restricted designs with identical weapon types. --- Glamourer/Automation/AutoDesignApplier.cs | 8 +-- Glamourer/Designs/Design.cs | 5 +- Glamourer/Designs/IDesignStandIn.cs | 3 +- Glamourer/Designs/Links/DesignMerger.cs | 18 ++--- Glamourer/Designs/Links/MergedDesign.cs | 67 +++++++++++++++++-- .../Designs/Special/QuickSelectedDesign.cs | 3 +- Glamourer/Designs/Special/RandomDesign.cs | 7 +- Glamourer/Designs/Special/RevertDesign.cs | 5 +- Glamourer/Interop/ContextMenuService.cs | 2 +- Glamourer/State/JobChangeState.cs | 18 +++-- Glamourer/State/StateEditor.cs | 4 +- Penumbra.GameData | 2 +- 12 files changed, 104 insertions(+), 38 deletions(-) diff --git a/Glamourer/Automation/AutoDesignApplier.cs b/Glamourer/Automation/AutoDesignApplier.cs index a3b912d..c739a75 100644 --- a/Glamourer/Automation/AutoDesignApplier.cs +++ b/Glamourer/Automation/AutoDesignApplier.cs @@ -77,7 +77,7 @@ public sealed class AutoDesignApplier : IDisposable { case EquipSlot.MainHand: { - if (_jobChangeState.TryGetValue(current.Type, out var data)) + if (_jobChangeState.TryGetValue(current.Type, actor.Job, out var data)) { Glamourer.Log.Verbose( $"Changing Mainhand from {state.ModelData.Weapon(EquipSlot.MainHand)} | {state.BaseData.Weapon(EquipSlot.MainHand)} to {data.Item1} for 0x{actor.Address:X}."); @@ -89,7 +89,7 @@ public sealed class AutoDesignApplier : IDisposable } case EquipSlot.OffHand when current.Type == state.BaseData.MainhandType.Offhand(): { - if (_jobChangeState.TryGetValue(current.Type, out var data)) + if (_jobChangeState.TryGetValue(current.Type, actor.Job, out var data)) { Glamourer.Log.Verbose( $"Changing Offhand from {state.ModelData.Weapon(EquipSlot.OffHand)} | {state.BaseData.Weapon(EquipSlot.OffHand)} to {data.Item1} for 0x{actor.Address:X}."); @@ -204,7 +204,7 @@ public sealed class AutoDesignApplier : IDisposable return; } - if (!_state.TryGetValue(id, out var state)) + if (!_state.GetOrCreate(actor, out var state)) return; if (oldJob.Id == newJob.Id && state.LastJob == newJob.Id) @@ -279,7 +279,7 @@ public sealed class AutoDesignApplier : IDisposable return; var mergedDesign = _designMerger.Merge( - set.Designs.Where(d => d.IsActive(actor)).SelectMany(d => d.Design.AllLinks.Select(l => (l.Design, l.Flags & d.Type))), + set.Designs.Where(d => d.IsActive(actor)).SelectMany(d => d.Design.AllLinks.Select(l => (l.Design, l.Flags & d.Type, d.Jobs.Flags))), state.ModelData.Customize, state.BaseData, true, _config.AlwaysApplyAssociatedMods); _state.ApplyDesign(state, mergedDesign, new ApplySettings(0, StateSource.Fixed, respectManual, fromJobChange, false, false, false)); } diff --git a/Glamourer/Designs/Design.cs b/Glamourer/Designs/Design.cs index 123f15a..83b6cfd 100644 --- a/Glamourer/Designs/Design.cs +++ b/Glamourer/Designs/Design.cs @@ -8,6 +8,7 @@ using Glamourer.State; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OtterGui.Classes; +using Penumbra.GameData.Structs; namespace Glamourer.Designs; @@ -50,8 +51,8 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn public string Incognito => Identifier.ToString()[..8]; - public IEnumerable<(IDesignStandIn Design, ApplicationType Flags)> AllLinks - => LinkContainer.GetAllLinks(this).Select(t => ((IDesignStandIn)t.Link.Link, t.Link.Type)); + public IEnumerable<(IDesignStandIn Design, ApplicationType Flags, JobFlag Jobs)> AllLinks + => LinkContainer.GetAllLinks(this).Select(t => ((IDesignStandIn)t.Link.Link, t.Link.Type, JobFlag.All)); #endregion diff --git a/Glamourer/Designs/IDesignStandIn.cs b/Glamourer/Designs/IDesignStandIn.cs index a6ee702..4865068 100644 --- a/Glamourer/Designs/IDesignStandIn.cs +++ b/Glamourer/Designs/IDesignStandIn.cs @@ -2,6 +2,7 @@ using Glamourer.Interop.Material; using Glamourer.State; using Newtonsoft.Json.Linq; +using Penumbra.GameData.Structs; namespace Glamourer.Designs; @@ -15,7 +16,7 @@ public interface IDesignStandIn : IEquatable public string SerializeName(); public StateSource AssociatedSource(); - public IEnumerable<(IDesignStandIn Design, ApplicationType Flags)> AllLinks { get; } + public IEnumerable<(IDesignStandIn Design, ApplicationType Flags, JobFlag Jobs)> AllLinks { get; } public void AddData(JObject jObj); diff --git a/Glamourer/Designs/Links/DesignMerger.cs b/Glamourer/Designs/Links/DesignMerger.cs index 92bc568..44f4db9 100644 --- a/Glamourer/Designs/Links/DesignMerger.cs +++ b/Glamourer/Designs/Links/DesignMerger.cs @@ -19,9 +19,9 @@ public class DesignMerger( { public MergedDesign Merge(LinkContainer designs, in CustomizeArray currentCustomize, in DesignData baseRef, bool respectOwnership, bool modAssociations) - => Merge(designs.Select(d => ((IDesignStandIn)d.Link, d.Type)), currentCustomize, baseRef, respectOwnership, modAssociations); + => Merge(designs.Select(d => ((IDesignStandIn)d.Link, d.Type, JobFlag.All)), currentCustomize, baseRef, respectOwnership, modAssociations); - public MergedDesign Merge(IEnumerable<(IDesignStandIn, ApplicationType)> designs, in CustomizeArray currentCustomize, in DesignData baseRef, + public MergedDesign Merge(IEnumerable<(IDesignStandIn, ApplicationType, JobFlag)> designs, in CustomizeArray currentCustomize, in DesignData baseRef, bool respectOwnership, bool modAssociations) { var ret = new MergedDesign(designManager); @@ -29,7 +29,7 @@ public class DesignMerger( var startBodyType = currentCustomize.BodyType; CustomizeFlag fixFlags = 0; respectOwnership &= _config.UnlockedItemMode; - foreach (var (design, type) in designs) + foreach (var (design, type, jobs) in designs) { if (type is 0) continue; @@ -44,8 +44,8 @@ public class DesignMerger( ReduceMeta(data, applyMeta, ret, source); ReduceCustomize(data, customizeFlags, ref fixFlags, ret, source, respectOwnership, startBodyType); ReduceEquip(data, equipFlags, ret, source, respectOwnership); - ReduceMainhands(data, equipFlags, ret, source, respectOwnership); - ReduceOffhands(data, equipFlags, ret, source, respectOwnership); + ReduceMainhands(data, jobs, equipFlags, ret, source, respectOwnership); + ReduceOffhands(data, jobs, equipFlags, ret, source, respectOwnership); ReduceCrests(data, crestFlags, ret, source); ReduceParameters(data, parameterFlags, ret, source); ReduceMods(design as Design, ret, modAssociations); @@ -170,7 +170,7 @@ public class DesignMerger( } } - private void ReduceMainhands(in DesignData design, EquipFlag equipFlags, MergedDesign ret, StateSource source, + private void ReduceMainhands(in DesignData design, JobFlag allowedJobs, EquipFlag equipFlags, MergedDesign ret, StateSource source, bool respectOwnership) { if (!equipFlags.HasFlag(EquipFlag.Mainhand)) @@ -186,10 +186,10 @@ public class DesignMerger( ret.Design.GetDesignDataRef().SetItem(EquipSlot.MainHand, weapon); } - ret.Weapons.TryAdd(weapon.Type, (weapon, source)); + ret.Weapons.TryAdd(weapon.Type, weapon, source, allowedJobs); } - private void ReduceOffhands(in DesignData design, EquipFlag equipFlags, MergedDesign ret, StateSource source, bool respectOwnership) + private void ReduceOffhands(in DesignData design, JobFlag allowedJobs, EquipFlag equipFlags, MergedDesign ret, StateSource source, bool respectOwnership) { if (!equipFlags.HasFlag(EquipFlag.Offhand)) return; @@ -205,7 +205,7 @@ public class DesignMerger( } if (weapon.Valid) - ret.Weapons.TryAdd(weapon.Type, (weapon, source)); + ret.Weapons.TryAdd(weapon.Type, weapon, source, allowedJobs); } private void ReduceCustomize(in DesignData design, CustomizeFlag customizeFlags, ref CustomizeFlag fixFlags, MergedDesign ret, diff --git a/Glamourer/Designs/Links/MergedDesign.cs b/Glamourer/Designs/Links/MergedDesign.cs index d36a284..131b074 100644 --- a/Glamourer/Designs/Links/MergedDesign.cs +++ b/Glamourer/Designs/Links/MergedDesign.cs @@ -5,6 +5,61 @@ using Penumbra.GameData.Structs; namespace Glamourer.Designs.Links; +public readonly struct WeaponList +{ + private readonly Dictionary> _list = new(4); + + public IEnumerable<(EquipItem, StateSource, JobFlag)> Values + => _list.Values.SelectMany(t => t); + + public void Clear() + => _list.Clear(); + + public bool TryAdd(FullEquipType type, EquipItem item, StateSource source, JobFlag flags) + { + if (!_list.TryGetValue(type, out var list)) + { + list = new List<(EquipItem, StateSource, JobFlag)>(2); + _list.Add(type, list); + } + + var remainingFlags = list.Select(t => t.Item3) + .Aggregate(flags, (current, existingFlags) => current & ~existingFlags); + + if (remainingFlags == 0) + return false; + + list.Add((item, source, remainingFlags)); + return true; + } + + public bool TryGet(FullEquipType type, JobId id, out (EquipItem, StateSource) ret) + { + if (!_list.TryGetValue(type, out var list)) + { + ret = default; + return false; + } + + var flag = (JobFlag)(1ul << id.Id); + + foreach (var (item, source, flags) in list) + { + if (flags.HasFlag(flag)) + { + ret = (item, source); + return true; + } + } + + ret = default; + return false; + } + + public WeaponList() + { } +} + public sealed class MergedDesign { public MergedDesign(DesignManager designManager) @@ -24,14 +79,14 @@ public sealed class MergedDesign { var weapon = design.DesignData.Item(EquipSlot.MainHand); if (weapon.Valid) - Weapons.TryAdd(weapon.Type, (weapon, StateSource.Manual)); + Weapons.TryAdd(weapon.Type, weapon, StateSource.Manual, JobFlag.All); } if (design.DoApplyEquip(EquipSlot.OffHand)) { var weapon = design.DesignData.Item(EquipSlot.OffHand); if (weapon.Valid) - Weapons.TryAdd(weapon.Type, (weapon, StateSource.Manual)); + Weapons.TryAdd(weapon.Type, weapon, StateSource.Manual, JobFlag.All); } } @@ -42,8 +97,8 @@ public sealed class MergedDesign AssociatedMods[mod] = settings; } - public readonly DesignBase Design; - public readonly Dictionary Weapons = new(4); - public readonly SortedList AssociatedMods = []; - public StateSources Sources = new(); + public readonly DesignBase Design; + public readonly WeaponList Weapons = new(); + public readonly SortedList AssociatedMods = []; + public StateSources Sources = new(); } diff --git a/Glamourer/Designs/Special/QuickSelectedDesign.cs b/Glamourer/Designs/Special/QuickSelectedDesign.cs index dd0f00f..f347085 100644 --- a/Glamourer/Designs/Special/QuickSelectedDesign.cs +++ b/Glamourer/Designs/Special/QuickSelectedDesign.cs @@ -4,6 +4,7 @@ using Glamourer.Interop.Material; using Glamourer.State; using Newtonsoft.Json.Linq; using OtterGui.Services; +using Penumbra.GameData.Structs; namespace Glamourer.Designs.Special; @@ -38,7 +39,7 @@ public class QuickSelectedDesign(QuickDesignCombo combo) : IDesignStandIn, IServ public StateSource AssociatedSource() => StateSource.Manual; - public IEnumerable<(IDesignStandIn Design, ApplicationType Flags)> AllLinks + public IEnumerable<(IDesignStandIn Design, ApplicationType Flags, JobFlag Jobs)> AllLinks => combo.Design?.AllLinks ?? []; public void AddData(JObject jObj) diff --git a/Glamourer/Designs/Special/RandomDesign.cs b/Glamourer/Designs/Special/RandomDesign.cs index b40ba92..c09fd2b 100644 --- a/Glamourer/Designs/Special/RandomDesign.cs +++ b/Glamourer/Designs/Special/RandomDesign.cs @@ -2,6 +2,7 @@ using Glamourer.Interop.Material; using Glamourer.State; using Newtonsoft.Json.Linq; +using Penumbra.GameData.Structs; namespace Glamourer.Designs.Special; @@ -45,7 +46,7 @@ public class RandomDesign(RandomDesignGenerator rng) : IDesignStandIn public StateSource AssociatedSource() => StateSource.Manual; - public IEnumerable<(IDesignStandIn Design, ApplicationType Flags)> AllLinks + public IEnumerable<(IDesignStandIn Design, ApplicationType Flags, JobFlag Jobs)> AllLinks { get { @@ -53,8 +54,8 @@ public class RandomDesign(RandomDesignGenerator rng) : IDesignStandIn if (_currentDesign == null) yield break; - foreach (var (link, type) in _currentDesign.AllLinks) - yield return (link, type); + foreach (var (link, type, jobs) in _currentDesign.AllLinks) + yield return (link, type, jobs); } } diff --git a/Glamourer/Designs/Special/RevertDesign.cs b/Glamourer/Designs/Special/RevertDesign.cs index 0f0207b..e450ff1 100644 --- a/Glamourer/Designs/Special/RevertDesign.cs +++ b/Glamourer/Designs/Special/RevertDesign.cs @@ -2,6 +2,7 @@ using Glamourer.Interop.Material; using Glamourer.State; using Newtonsoft.Json.Linq; +using Penumbra.GameData.Structs; namespace Glamourer.Designs.Special; @@ -28,9 +29,9 @@ public class RevertDesign : IDesignStandIn public StateSource AssociatedSource() => StateSource.Game; - public IEnumerable<(IDesignStandIn Design, ApplicationType Flags)> AllLinks + public IEnumerable<(IDesignStandIn Design, ApplicationType Flags, JobFlag Jobs)> AllLinks { - get { yield return (this, ApplicationType.All); } + get { yield return (this, ApplicationType.All, JobFlag.All); } } public void AddData(JObject jObj) diff --git a/Glamourer/Interop/ContextMenuService.cs b/Glamourer/Interop/ContextMenuService.cs index b80a247..8cd5391 100644 --- a/Glamourer/Interop/ContextMenuService.cs +++ b/Glamourer/Interop/ContextMenuService.cs @@ -119,7 +119,7 @@ public class ContextMenuService : IDisposable private bool HandleItem(ItemId id) { - var itemId = id.Id % 500000u; + var itemId = id.StripModifiers; return _items.ItemData.TryGetValue(itemId, EquipSlot.MainHand, out _lastItem); } diff --git a/Glamourer/State/JobChangeState.cs b/Glamourer/State/JobChangeState.cs index 84aa3cc..0fe1820 100644 --- a/Glamourer/State/JobChangeState.cs +++ b/Glamourer/State/JobChangeState.cs @@ -1,18 +1,21 @@ -using OtterGui.Services; +using Glamourer.Designs.Links; +using OtterGui.Services; using Penumbra.GameData.Actors; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; namespace Glamourer.State; -public sealed class JobChangeState : Dictionary, IService +public sealed class JobChangeState : IService { + private readonly WeaponList _weaponList = new(); + public ActorState? State { get; private set; } public void Reset() { State = null; - Clear(); + _weaponList.Clear(); } public bool HasState @@ -21,10 +24,13 @@ public sealed class JobChangeState : Dictionary State?.Identifier ?? ActorIdentifier.Invalid; - public void Set(ActorState state, IEnumerable<(EquipItem, StateSource)> items) + public bool TryGetValue(FullEquipType slot, JobId jobId, out (EquipItem, StateSource) data) + => _weaponList.TryGet(slot, jobId, out data); + + public void Set(ActorState state, IEnumerable<(EquipItem, StateSource, JobFlag)> items) { - foreach (var (item, source) in items.Where(p => p.Item1.Valid)) - TryAdd(item.Type, (item, source)); + foreach (var (item, source, flags) in items.Where(p => p.Item1.Valid)) + _weaponList.TryAdd(item.Type, item, source, flags); State = state; } } diff --git a/Glamourer/State/StateEditor.cs b/Glamourer/State/StateEditor.cs index 600ab17..9bae385 100644 --- a/Glamourer/State/StateEditor.cs +++ b/Glamourer/State/StateEditor.cs @@ -298,7 +298,7 @@ public class StateEditor( } var currentType = state.BaseData.Item(weaponSlot).Type; - if (mergedDesign.Weapons.TryGetValue(currentType, out var weapon)) + if (mergedDesign.Weapons.TryGet(currentType, state.LastJob, out var weapon)) { var source = settings.UseSingleSource ? settings.Source : weapon.Item2 is StateSource.Game ? StateSource.Game : settings.Source; @@ -311,7 +311,7 @@ public class StateEditor( if (settings.FromJobChange) jobChange.Set(state, mergedDesign.Weapons.Values.Select(m => (m.Item1, settings.UseSingleSource ? settings.Source : - m.Item2 is StateSource.Game ? StateSource.Game : settings.Source))); + m.Item2 is StateSource.Game ? StateSource.Game : settings.Source, m.Item3))); foreach (var meta in MetaExtensions.AllRelevant.Where(mergedDesign.Design.DoApplyMeta)) { diff --git a/Penumbra.GameData b/Penumbra.GameData index 6668764..04237f8 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit 66687643da2163c938575ad6949c8d0fbd03afe7 +Subproject commit 04237f8e80e2277ea99701bd240a09fcffe4db97