mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2026-01-03 14:23:43 +01:00
Add associated collections.
This commit is contained in:
parent
fe3ce166e3
commit
d3bdb8d3d9
9 changed files with 211 additions and 14 deletions
|
|
@ -4,6 +4,7 @@ using Glamourer.Customization;
|
|||
using Glamourer.Designs;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using Glamourer.Interop.Structs;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
|
|
@ -29,6 +30,7 @@ public class AutoDesignApplier : IDisposable
|
|||
private readonly AutomationChanged _event;
|
||||
private readonly ObjectManager _objects;
|
||||
private readonly WeaponLoading _weapons;
|
||||
private readonly PenumbraService _penumbra;
|
||||
|
||||
private ActorState? _jobChangeState;
|
||||
private EquipItem _jobChangeMainhand;
|
||||
|
|
@ -36,7 +38,7 @@ public class AutoDesignApplier : IDisposable
|
|||
|
||||
public AutoDesignApplier(Configuration config, AutoDesignManager manager, StateManager state, JobService jobs,
|
||||
CustomizationService customizations, ActorService actors, ItemUnlockManager itemUnlocks, CustomizeUnlockManager customizeUnlocks,
|
||||
AutomationChanged @event, ObjectManager objects, WeaponLoading weapons)
|
||||
AutomationChanged @event, ObjectManager objects, WeaponLoading weapons, PenumbraService penumbra)
|
||||
{
|
||||
_config = config;
|
||||
_manager = manager;
|
||||
|
|
@ -49,6 +51,7 @@ public class AutoDesignApplier : IDisposable
|
|||
_event = @event;
|
||||
_objects = objects;
|
||||
_weapons = weapons;
|
||||
_penumbra = penumbra;
|
||||
_jobs.JobChanged += OnJobChange;
|
||||
_event.Subscribe(OnAutomationChange, AutomationChanged.Priority.AutoDesignApplier);
|
||||
_weapons.Subscribe(OnWeaponLoading, WeaponLoading.Priority.AutoDesignApplier);
|
||||
|
|
@ -126,7 +129,10 @@ public class AutoDesignApplier : IDisposable
|
|||
{
|
||||
Reduce(data.Objects[0], state, newSet, false, false);
|
||||
foreach (var actor in data.Objects)
|
||||
{
|
||||
_penumbra.SetCollection(actor, ReduceCollections(actor, set));
|
||||
_state.ReapplyState(actor);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_objects.TryGetValueAllWorld(id, out data) || _objects.TryGetValueNonOwned(id, out data))
|
||||
|
|
@ -136,6 +142,7 @@ public class AutoDesignApplier : IDisposable
|
|||
var specificId = actor.GetIdentifier(_actors.AwaitedService);
|
||||
if (_state.GetOrCreate(specificId, actor, out var state))
|
||||
{
|
||||
_penumbra.SetCollection(actor, ReduceCollections(actor, set));
|
||||
Reduce(actor, state, newSet, false, false);
|
||||
_state.ReapplyState(actor);
|
||||
}
|
||||
|
|
@ -194,6 +201,7 @@ public class AutoDesignApplier : IDisposable
|
|||
var respectManual = state.LastJob == newJob.Id;
|
||||
state.LastJob = actor.Job;
|
||||
Reduce(actor, state, set, respectManual, true);
|
||||
_penumbra.SetCollection(actor, ReduceCollections(actor, set));
|
||||
_state.ReapplyState(actor);
|
||||
}
|
||||
|
||||
|
|
@ -206,6 +214,29 @@ public class AutoDesignApplier : IDisposable
|
|||
return;
|
||||
|
||||
Reduce(actor, state, set, false, false);
|
||||
_penumbra.SetCollection(actor, ReduceCollections(actor, set));
|
||||
}
|
||||
|
||||
public unsafe Collection ReduceCollections(Actor actor, AutoDesignSet set)
|
||||
{
|
||||
Collection collection = new Collection();
|
||||
foreach (var design in set.Designs)
|
||||
{
|
||||
if (!design.IsActive(actor))
|
||||
continue;
|
||||
|
||||
if (design.ApplicationType is 0)
|
||||
continue;
|
||||
|
||||
if (actor.AsCharacter->CharacterData.ModelCharaId != design?.Design?.DesignData.ModelId)
|
||||
continue;
|
||||
|
||||
if (design.Design.AssociatedCollection.IsAssociable())
|
||||
{
|
||||
collection = design.Design.AssociatedCollection;
|
||||
}
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
|
||||
public bool Reduce(Actor actor, ActorIdentifier identifier, [NotNullWhen(true)] out ActorState? state)
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ public sealed class Design : DesignBase, ISavable
|
|||
public string[] Tags { get; internal set; } = Array.Empty<string>();
|
||||
public int Index { get; internal set; }
|
||||
public SortedList<Mod, ModSettings> AssociatedMods { get; private set; } = new();
|
||||
public Collection AssociatedCollection { get; internal set; } = new();
|
||||
|
||||
public string Incognito
|
||||
=> Identifier.ToString()[..8];
|
||||
|
|
@ -64,6 +65,7 @@ public sealed class Design : DesignBase, ISavable
|
|||
["Equipment"] = SerializeEquipment(),
|
||||
["Customize"] = SerializeCustomize(),
|
||||
["Mods"] = SerializeMods(),
|
||||
["Collection"] = AssociatedCollection.Name,
|
||||
}
|
||||
;
|
||||
return ret;
|
||||
|
|
@ -124,6 +126,7 @@ public sealed class Design : DesignBase, ISavable
|
|||
Description = json["Description"]?.ToObject<string>() ?? string.Empty,
|
||||
Tags = ParseTags(json),
|
||||
LastEdit = json["LastEdit"]?.ToObject<DateTimeOffset>() ?? creationDate,
|
||||
AssociatedCollection = new Collection(json["Collection"]?.ToObject<string>() ?? string.Empty),
|
||||
};
|
||||
if (design.LastEdit < creationDate)
|
||||
design.LastEdit = creationDate;
|
||||
|
|
|
|||
|
|
@ -251,6 +251,26 @@ public class DesignManager
|
|||
_event.Invoke(DesignChanged.Type.RemovedMod, design, (mod, settings));
|
||||
}
|
||||
|
||||
/// <summary> Change an associated collection to a design. </summary>
|
||||
public void ChangeAssociatedCollection(Design design, Collection collection)
|
||||
{
|
||||
var oldAssociatedCollection = design.AssociatedCollection;
|
||||
if (oldAssociatedCollection == collection)
|
||||
return;
|
||||
|
||||
design.AssociatedCollection = collection;
|
||||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
_saveService.QueueSave(design);
|
||||
if (collection.IsAssociable())
|
||||
{
|
||||
Glamourer.Log.Debug($"Removed associated collection from design {design.Identifier}.");
|
||||
} else
|
||||
{
|
||||
Glamourer.Log.Debug($"Set associated collection {collection.Name} to design {design.Identifier}.");
|
||||
}
|
||||
_event.Invoke(DesignChanged.Type.ChangedAssociatedCollection, design, oldAssociatedCollection);
|
||||
}
|
||||
|
||||
/// <summary> Set the write protection status of a design. </summary>
|
||||
public void SetWriteProtection(Design design, bool value)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -46,6 +46,9 @@ public sealed class DesignChanged : EventWrapper<Action<DesignChanged.Type, Desi
|
|||
/// <summary> An existing design had an existing associated mod removed. Data is the Mod and its Settings [(Mod, ModSettings)]. </summary>
|
||||
RemovedMod,
|
||||
|
||||
/// <summary> An existing design had an associated collection changed. Data is the prior collection [string]. </summary>
|
||||
ChangedAssociatedCollection,
|
||||
|
||||
/// <summary> An existing design had a customization changed. Data is the old value, the new value and the type [(CustomizeValue, CustomizeValue, CustomizeIndex)]. </summary>
|
||||
Customize,
|
||||
|
||||
|
|
|
|||
45
Glamourer/Gui/Tabs/DesignTab/CollectionAssociationTab.cs
Normal file
45
Glamourer/Gui/Tabs/DesignTab/CollectionAssociationTab.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
using System.Numerics;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Utility;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DesignTab;
|
||||
|
||||
public class CollectionAssociationTab
|
||||
{
|
||||
private readonly DesignFileSystemSelector _selector;
|
||||
private readonly DesignManager _manager;
|
||||
private readonly CollectionCombo _collectionCombo;
|
||||
|
||||
public CollectionAssociationTab(PenumbraService penumbra, DesignFileSystemSelector selector, DesignManager manager)
|
||||
{
|
||||
_selector = selector;
|
||||
_manager = manager;
|
||||
_collectionCombo = new CollectionCombo(penumbra);
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
if (!ImGui.CollapsingHeader("Collection Association"))
|
||||
return;
|
||||
var width = new Vector2(ImGui.GetContentRegionAvail().X, 0);
|
||||
var changed = _collectionCombo.Draw("##new", !_selector.Selected!.AssociatedCollection.IsAssociable() ? "Select Collection" : _selector.Selected!.AssociatedCollection.Name, string.Empty,
|
||||
width.X, ImGui.GetTextLineHeight());
|
||||
var currentCollection = _collectionCombo.CurrentSelection;
|
||||
if (changed)
|
||||
{
|
||||
if (!currentCollection.IsAssociable()) return;
|
||||
_manager.ChangeAssociatedCollection(_selector.Selected!, currentCollection);
|
||||
}
|
||||
if (ImGui.Button($"Remove Associated Collection"))
|
||||
{
|
||||
_manager.ChangeAssociatedCollection(_selector.Selected!, new Collection());
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Glamourer/Gui/Tabs/DesignTab/CollectionCombo.cs
Normal file
32
Glamourer/Gui/Tabs/DesignTab/CollectionCombo.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Widgets;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DesignTab;
|
||||
|
||||
public sealed class CollectionCombo : FilterComboCache<Collection>
|
||||
{
|
||||
public CollectionCombo(PenumbraService penumbra)
|
||||
: base(penumbra.GetAllCollections)
|
||||
{
|
||||
SearchByParts = false;
|
||||
}
|
||||
|
||||
protected override bool DrawSelectable(int globalIdx, bool selected)
|
||||
{
|
||||
using var id = ImRaii.PushId(globalIdx);
|
||||
var collection = Items[globalIdx];
|
||||
bool ret;
|
||||
using (var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.Text)))
|
||||
{
|
||||
ret = ImGui.Selectable(collection.Name, selected);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ using Glamourer.Events;
|
|||
using Glamourer.Gui.Customization;
|
||||
using Glamourer.Gui.Equipment;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Glamourer.Structs;
|
||||
|
|
@ -37,22 +38,26 @@ public class DesignPanel
|
|||
private readonly DesignConverter _converter;
|
||||
private readonly DatFileService _datFileService;
|
||||
private readonly FileDialogManager _fileDialog = new();
|
||||
private readonly PenumbraService _penumbra;
|
||||
private readonly CollectionAssociationTab _collectionAssociation;
|
||||
|
||||
public DesignPanel(DesignFileSystemSelector selector, CustomizationDrawer customizationDrawer, DesignManager manager, ObjectManager objects,
|
||||
StateManager state, EquipmentDrawer equipmentDrawer, CustomizationService customizationService, ModAssociationsTab modAssociations,
|
||||
DesignDetailTab designDetails, DesignConverter converter, DatFileService datFileService)
|
||||
StateManager state, EquipmentDrawer equipmentDrawer, CustomizationService customizationService, ModAssociationsTab modAssociations, CollectionAssociationTab collectionAssociation,
|
||||
DesignDetailTab designDetails, DesignConverter converter, DatFileService datFileService, PenumbraService penumbra)
|
||||
{
|
||||
_selector = selector;
|
||||
_customizationDrawer = customizationDrawer;
|
||||
_manager = manager;
|
||||
_objects = objects;
|
||||
_state = state;
|
||||
_equipmentDrawer = equipmentDrawer;
|
||||
_customizationService = customizationService;
|
||||
_modAssociations = modAssociations;
|
||||
_designDetails = designDetails;
|
||||
_converter = converter;
|
||||
_datFileService = datFileService;
|
||||
_selector = selector;
|
||||
_customizationDrawer = customizationDrawer;
|
||||
_manager = manager;
|
||||
_objects = objects;
|
||||
_state = state;
|
||||
_equipmentDrawer = equipmentDrawer;
|
||||
_customizationService = customizationService;
|
||||
_modAssociations = modAssociations;
|
||||
_collectionAssociation = collectionAssociation;
|
||||
_designDetails = designDetails;
|
||||
_converter = converter;
|
||||
_datFileService = datFileService;
|
||||
_penumbra = penumbra;
|
||||
}
|
||||
|
||||
private HeaderDrawer.Button LockButton()
|
||||
|
|
@ -326,6 +331,7 @@ public class DesignPanel
|
|||
_designDetails.Draw();
|
||||
DrawApplicationRules();
|
||||
_modAssociations.Draw();
|
||||
_collectionAssociation.Draw();
|
||||
}
|
||||
|
||||
private void DrawButtonRow()
|
||||
|
|
@ -374,7 +380,10 @@ public class DesignPanel
|
|||
return;
|
||||
|
||||
if (_state.GetOrCreate(id, data.Objects[0], out var state))
|
||||
{
|
||||
_state.ApplyDesign(_selector.Selected!, state, StateChanged.Source.Manual);
|
||||
_penumbra.SetCollection(data.Objects[0], _selector.Selected!.AssociatedCollection);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawApplyToTarget()
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Utility;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Interop.Structs;
|
||||
using Penumbra.Api;
|
||||
|
|
@ -36,6 +37,17 @@ public readonly record struct ModSettings(IDictionary<string, IList<string>> Set
|
|||
public static ModSettings Empty
|
||||
=> new();
|
||||
}
|
||||
public readonly record struct Collection(string Name) : IComparable<Collection>
|
||||
{
|
||||
public bool IsAssociable()
|
||||
{
|
||||
return !Name.IsNullOrEmpty();
|
||||
}
|
||||
public int CompareTo(Collection other)
|
||||
{
|
||||
return string.Compare(Name, other.Name, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe class PenumbraService : IDisposable
|
||||
{
|
||||
|
|
@ -54,7 +66,9 @@ public unsafe class PenumbraService : IDisposable
|
|||
private FuncSubscriber<int, (bool, bool, string)> _objectCollection;
|
||||
private FuncSubscriber<IList<(string, string)>> _getMods;
|
||||
private FuncSubscriber<ApiCollectionType, string> _currentCollection;
|
||||
private FuncSubscriber<IList<string>> _getAllCollections;
|
||||
private FuncSubscriber<string, string, string, bool, CurrentSettings> _getCurrentSettings;
|
||||
private FuncSubscriber<int, string, bool, bool, (PenumbraApiEc, string)> _setCurrentCollection;
|
||||
private FuncSubscriber<string, string, string, bool, PenumbraApiEc> _setMod;
|
||||
private FuncSubscriber<string, string, string, int, PenumbraApiEc> _setModPriority;
|
||||
private FuncSubscriber<string, string, string, string, string, PenumbraApiEc> _setModSetting;
|
||||
|
|
@ -141,6 +155,25 @@ public unsafe class PenumbraService : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<Collection> GetAllCollections()
|
||||
{
|
||||
if (!Available)
|
||||
return Array.Empty<Collection>();
|
||||
|
||||
try
|
||||
{
|
||||
var allCollections = _getAllCollections.Invoke();
|
||||
return allCollections
|
||||
.Select(c => new Collection(c))
|
||||
.ToList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Log.Error($"Error fetching collections from Penumbra:\n{ex}");
|
||||
return Array.Empty<Collection>();
|
||||
}
|
||||
}
|
||||
|
||||
public string CurrentCollection
|
||||
=> Available ? _currentCollection.Invoke(ApiCollectionType.Current) : "<Unavailable>";
|
||||
|
||||
|
|
@ -195,6 +228,24 @@ public unsafe class PenumbraService : IDisposable
|
|||
return sb.AppendLine(ex.Message).ToString();
|
||||
}
|
||||
}
|
||||
public string SetCollection(Actor actor, Collection collection)
|
||||
{
|
||||
if (!Available)
|
||||
return "Penumbra is not available.";
|
||||
|
||||
var sb = new StringBuilder();
|
||||
try
|
||||
{
|
||||
var ec = _setCurrentCollection.Invoke(actor.AsObject -> ObjectIndex, collection.Name, true, false);
|
||||
if (ec.Item1 is PenumbraApiEc.CollectionMissing)
|
||||
sb.AppendLine($"The collection {collection.Name}] could not be found.");
|
||||
return sb.ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return sb.AppendLine(ex.Message).ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Obtain the name of the collection currently assigned to the player. </summary>
|
||||
public string GetCurrentPlayerCollection()
|
||||
|
|
@ -254,6 +305,8 @@ public unsafe class PenumbraService : IDisposable
|
|||
_getMods = Ipc.GetMods.Subscriber(_pluginInterface);
|
||||
_currentCollection = Ipc.GetCollectionForType.Subscriber(_pluginInterface);
|
||||
_getCurrentSettings = Ipc.GetCurrentModSettings.Subscriber(_pluginInterface);
|
||||
_getAllCollections = Ipc.GetCollections.Subscriber(_pluginInterface);
|
||||
_setCurrentCollection = Ipc.SetCollectionForObject.Subscriber( _pluginInterface);
|
||||
_setMod = Ipc.TrySetMod.Subscriber(_pluginInterface);
|
||||
_setModPriority = Ipc.TrySetModPriority.Subscriber(_pluginInterface);
|
||||
_setModSetting = Ipc.TrySetModSetting.Subscriber(_pluginInterface);
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ public static class ServiceManager
|
|||
.AddSingleton<DesignTab>()
|
||||
.AddSingleton<DesignCombo>()
|
||||
.AddSingleton<ModAssociationsTab>()
|
||||
.AddSingleton<CollectionAssociationTab>()
|
||||
.AddSingleton<DesignDetailTab>()
|
||||
.AddSingleton<UnlockTable>()
|
||||
.AddSingleton<UnlockOverview>()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue