Compare commits

..

No commits in common. "main" and "1.5.1.0" have entirely different histories.

35 changed files with 101 additions and 397 deletions

View file

@ -9,15 +9,13 @@ jobs:
build: build:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v2
with: with:
submodules: recursive submodules: recursive
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v5 uses: actions/setup-dotnet@v1
with: with:
dotnet-version: | dotnet-version: '9.x.x'
10.x.x
9.x.x
- name: Restore dependencies - name: Restore dependencies
run: dotnet restore run: dotnet restore
- name: Download Dalamud - name: Download Dalamud

View file

@ -9,15 +9,13 @@ jobs:
build: build:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v2
with: with:
submodules: recursive submodules: recursive
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v5 uses: actions/setup-dotnet@v1
with: with:
dotnet-version: | dotnet-version: '9.x.x'
10.x.x
9.x.x
- name: Restore dependencies - name: Restore dependencies
run: dotnet restore run: dotnet restore
- name: Download Dalamud - name: Download Dalamud

@ -1 +1 @@
Subproject commit 5b6730d46f17bdd02a441e23e2141576cf7acf53 Subproject commit 54c1944dc7db704733b4788520e494761bb0b58e

View file

@ -1,13 +1,13 @@
using Glamourer.Api.Enums; using Glamourer.Api.Enums;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.State; using Glamourer.State;
using OtterGui;
using OtterGui.Extensions; using OtterGui.Extensions;
using OtterGui.Log; using OtterGui.Log;
using OtterGui.Services; using OtterGui.Services;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs;
using Penumbra.String; using Penumbra.String;
namespace Glamourer.Api; namespace Glamourer.Api;
@ -15,24 +15,15 @@ namespace Glamourer.Api;
public class ApiHelpers(ActorObjectManager objects, StateManager stateManager, ActorManager actors) : IApiService public class ApiHelpers(ActorObjectManager objects, StateManager stateManager, ActorManager actors) : IApiService
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal IEnumerable<ActorState> FindExistingStates(string actorName, ushort worldId = ushort.MaxValue) internal IEnumerable<ActorState> FindExistingStates(string actorName)
{ {
if (actorName.Length == 0 || !ByteString.FromString(actorName, out var byteString)) if (actorName.Length == 0 || !ByteString.FromString(actorName, out var byteString))
yield break; yield break;
if (worldId == WorldId.AnyWorld.Id)
{
foreach (var state in stateManager.Values.Where(state foreach (var state in stateManager.Values.Where(state
=> state.Identifier.Type is IdentifierType.Player && state.Identifier.PlayerName == byteString)) => state.Identifier.Type is IdentifierType.Player && state.Identifier.PlayerName == byteString))
yield return state; yield return state;
} }
else
{
var identifier = actors.CreatePlayer(byteString, worldId);
if (stateManager.TryGetValue(identifier, out var state))
yield return state;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal GlamourerApiEc FindExistingState(int objectIndex, out ActorState? state) internal GlamourerApiEc FindExistingState(int objectIndex, out ActorState? state)

View file

@ -3,17 +3,14 @@ using OtterGui.Services;
namespace Glamourer.Api; namespace Glamourer.Api;
public class GlamourerApi(Configuration config, DesignsApi designs, StateApi state, ItemsApi items) : IGlamourerApi, IApiService public class GlamourerApi(DesignsApi designs, StateApi state, ItemsApi items) : IGlamourerApi, IApiService
{ {
public const int CurrentApiVersionMajor = 1; public const int CurrentApiVersionMajor = 1;
public const int CurrentApiVersionMinor = 7; public const int CurrentApiVersionMinor = 6;
public (int Major, int Minor) ApiVersion public (int Major, int Minor) ApiVersion
=> (CurrentApiVersionMajor, CurrentApiVersionMinor); => (CurrentApiVersionMajor, CurrentApiVersionMinor);
public bool AutoReloadGearEnabled
=> config.AutoRedrawEquipOnChanges;
public IGlamourerApiDesigns Designs public IGlamourerApiDesigns Designs
=> designs; => designs;

View file

@ -22,7 +22,6 @@ public sealed class IpcProviders : IDisposable, IApiService
new FuncProvider<(int Major, int Minor)>(pi, "Glamourer.ApiVersions", () => api.ApiVersion), // backward compatibility new FuncProvider<(int Major, int Minor)>(pi, "Glamourer.ApiVersions", () => api.ApiVersion), // backward compatibility
new FuncProvider<int>(pi, "Glamourer.ApiVersion", () => api.ApiVersion.Major), // backward compatibility new FuncProvider<int>(pi, "Glamourer.ApiVersion", () => api.ApiVersion.Major), // backward compatibility
IpcSubscribers.ApiVersion.Provider(pi, api), IpcSubscribers.ApiVersion.Provider(pi, api),
IpcSubscribers.AutoReloadGearEnabled.Provider(pi, api),
IpcSubscribers.GetDesignList.Provider(pi, api.Designs), IpcSubscribers.GetDesignList.Provider(pi, api.Designs),
IpcSubscribers.GetDesignListExtended.Provider(pi, api.Designs), IpcSubscribers.GetDesignListExtended.Provider(pi, api.Designs),
@ -51,18 +50,13 @@ public sealed class IpcProviders : IDisposable, IApiService
IpcSubscribers.GetStateBase64Name.Provider(pi, api.State), IpcSubscribers.GetStateBase64Name.Provider(pi, api.State),
IpcSubscribers.ApplyState.Provider(pi, api.State), IpcSubscribers.ApplyState.Provider(pi, api.State),
IpcSubscribers.ApplyStateName.Provider(pi, api.State), IpcSubscribers.ApplyStateName.Provider(pi, api.State),
IpcSubscribers.ReapplyState.Provider(pi, api.State),
IpcSubscribers.ReapplyStateName.Provider(pi, api.State),
IpcSubscribers.RevertState.Provider(pi, api.State), IpcSubscribers.RevertState.Provider(pi, api.State),
IpcSubscribers.RevertStateName.Provider(pi, api.State), IpcSubscribers.RevertStateName.Provider(pi, api.State),
IpcSubscribers.UnlockState.Provider(pi, api.State), IpcSubscribers.UnlockState.Provider(pi, api.State),
IpcSubscribers.CanUnlock.Provider(pi, api.State),
IpcSubscribers.UnlockStateName.Provider(pi, api.State), IpcSubscribers.UnlockStateName.Provider(pi, api.State),
IpcSubscribers.DeletePlayerState.Provider(pi, api.State),
IpcSubscribers.UnlockAll.Provider(pi, api.State), IpcSubscribers.UnlockAll.Provider(pi, api.State),
IpcSubscribers.RevertToAutomation.Provider(pi, api.State), IpcSubscribers.RevertToAutomation.Provider(pi, api.State),
IpcSubscribers.RevertToAutomationName.Provider(pi, api.State), IpcSubscribers.RevertToAutomationName.Provider(pi, api.State),
IpcSubscribers.AutoReloadGearChanged.Provider(pi, api.State),
IpcSubscribers.StateChanged.Provider(pi, api.State), IpcSubscribers.StateChanged.Provider(pi, api.State),
IpcSubscribers.StateChangedWithType.Provider(pi, api.State), IpcSubscribers.StateChangedWithType.Provider(pi, api.State),
IpcSubscribers.StateFinalized.Provider(pi, api.State), IpcSubscribers.StateFinalized.Provider(pi, api.State),
@ -81,5 +75,3 @@ public sealed class IpcProviders : IDisposable, IApiService
_disposedProvider.Dispose(); _disposedProvider.Dispose();
} }
} }

View file

@ -8,7 +8,6 @@ using Glamourer.State;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui.Services; using OtterGui.Services;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs;
using StateChanged = Glamourer.Events.StateChanged; using StateChanged = Glamourer.Events.StateChanged;
namespace Glamourer.Api; namespace Glamourer.Api;
@ -18,9 +17,9 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
private readonly ApiHelpers _helpers; private readonly ApiHelpers _helpers;
private readonly StateManager _stateManager; private readonly StateManager _stateManager;
private readonly DesignConverter _converter; private readonly DesignConverter _converter;
private readonly Configuration _config;
private readonly AutoDesignApplier _autoDesigns; private readonly AutoDesignApplier _autoDesigns;
private readonly ActorObjectManager _objects; private readonly ActorObjectManager _objects;
private readonly AutoRedrawChanged _autoRedraw;
private readonly StateChanged _stateChanged; private readonly StateChanged _stateChanged;
private readonly StateFinalized _stateFinalized; private readonly StateFinalized _stateFinalized;
private readonly GPoseService _gPose; private readonly GPoseService _gPose;
@ -28,9 +27,9 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
public StateApi(ApiHelpers helpers, public StateApi(ApiHelpers helpers,
StateManager stateManager, StateManager stateManager,
DesignConverter converter, DesignConverter converter,
Configuration config,
AutoDesignApplier autoDesigns, AutoDesignApplier autoDesigns,
ActorObjectManager objects, ActorObjectManager objects,
AutoRedrawChanged autoRedraw,
StateChanged stateChanged, StateChanged stateChanged,
StateFinalized stateFinalized, StateFinalized stateFinalized,
GPoseService gPose) GPoseService gPose)
@ -38,13 +37,12 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
_helpers = helpers; _helpers = helpers;
_stateManager = stateManager; _stateManager = stateManager;
_converter = converter; _converter = converter;
_config = config;
_autoDesigns = autoDesigns; _autoDesigns = autoDesigns;
_objects = objects; _objects = objects;
_autoRedraw = autoRedraw;
_stateChanged = stateChanged; _stateChanged = stateChanged;
_stateFinalized = stateFinalized; _stateFinalized = stateFinalized;
_gPose = gPose; _gPose = gPose;
_autoRedraw.Subscribe(OnAutoRedrawChange, AutoRedrawChanged.Priority.StateApi);
_stateChanged.Subscribe(OnStateChanged, Events.StateChanged.Priority.GlamourerIpc); _stateChanged.Subscribe(OnStateChanged, Events.StateChanged.Priority.GlamourerIpc);
_stateFinalized.Subscribe(OnStateFinalized, Events.StateFinalized.Priority.StateApi); _stateFinalized.Subscribe(OnStateFinalized, Events.StateFinalized.Priority.StateApi);
_gPose.Subscribe(OnGPoseChange, GPoseService.Priority.StateApi); _gPose.Subscribe(OnGPoseChange, GPoseService.Priority.StateApi);
@ -52,7 +50,6 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
public void Dispose() public void Dispose()
{ {
_autoRedraw.Unsubscribe(OnAutoRedrawChange);
_stateChanged.Unsubscribe(OnStateChanged); _stateChanged.Unsubscribe(OnStateChanged);
_stateFinalized.Unsubscribe(OnStateFinalized); _stateFinalized.Unsubscribe(OnStateFinalized);
_gPose.Unsubscribe(OnGPoseChange); _gPose.Unsubscribe(OnGPoseChange);
@ -126,48 +123,6 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
return ApiHelpers.Return(GlamourerApiEc.Success, args); return ApiHelpers.Return(GlamourerApiEc.Success, args);
} }
public GlamourerApiEc ReapplyState(int objectIndex, uint key, ApplyFlag flags)
{
var args = ApiHelpers.Args("Index", objectIndex, "Key", key, "Flags", flags);
if (_helpers.FindExistingState(objectIndex, out var state) is not GlamourerApiEc.Success)
return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args);
if (state is null)
return ApiHelpers.Return(GlamourerApiEc.NothingDone, args);
if (!state.CanUnlock(key))
return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args);
Reapply(_objects.Objects[objectIndex], state, key, flags);
return ApiHelpers.Return(GlamourerApiEc.Success, args);
}
public GlamourerApiEc ReapplyStateName(string playerName, uint key, ApplyFlag flags)
{
var args = ApiHelpers.Args("Name", playerName, "Key", key, "Flags", flags);
var states = _helpers.FindExistingStates(playerName);
var any = false;
var anyReapplied = false;
foreach (var state in states)
{
any = true;
if (!state.CanUnlock(key))
continue;
anyReapplied = true;
anyReapplied |= Reapply(state, key, flags) is GlamourerApiEc.Success;
}
if (any)
ApiHelpers.Return(GlamourerApiEc.NothingDone, args);
if (!anyReapplied)
return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args);
return ApiHelpers.Return(GlamourerApiEc.Success, args);
}
public GlamourerApiEc RevertState(int objectIndex, uint key, ApplyFlag flags) public GlamourerApiEc RevertState(int objectIndex, uint key, ApplyFlag flags)
{ {
var args = ApiHelpers.Args("Index", objectIndex, "Key", key, "Flags", flags); var args = ApiHelpers.Args("Index", objectIndex, "Key", key, "Flags", flags);
@ -225,20 +180,6 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
return ApiHelpers.Return(GlamourerApiEc.Success, args); return ApiHelpers.Return(GlamourerApiEc.Success, args);
} }
public GlamourerApiEc CanUnlock(int objectIndex, uint key, out bool isLocked, out bool canUnlock)
{
var args = ApiHelpers.Args("Index", objectIndex, "Key", key);
isLocked = false;
canUnlock = true;
if (_helpers.FindExistingState(objectIndex, out var state) is not GlamourerApiEc.Success)
return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args);
if (state is null)
return ApiHelpers.Return(GlamourerApiEc.Success, args);
isLocked = state.IsLocked;
canUnlock = state.CanUnlock(key);
return ApiHelpers.Return(GlamourerApiEc.Success, args);
}
public GlamourerApiEc UnlockStateName(string playerName, uint key) public GlamourerApiEc UnlockStateName(string playerName, uint key)
{ {
var args = ApiHelpers.Args("Name", playerName, "Key", key); var args = ApiHelpers.Args("Name", playerName, "Key", key);
@ -261,27 +202,6 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
return ApiHelpers.Return(GlamourerApiEc.Success, args); return ApiHelpers.Return(GlamourerApiEc.Success, args);
} }
public GlamourerApiEc DeletePlayerState(string playerName, ushort worldId, uint key)
{
var args = ApiHelpers.Args("Name", playerName, "World", worldId, "Key", key);
var states = _helpers.FindExistingStates(playerName).ToList();
if (states.Count is 0)
return ApiHelpers.Return(GlamourerApiEc.NothingDone, args);
var anyLocked = false;
foreach (var state in states)
{
if (state.CanUnlock(key))
_stateManager.DeleteState(state.Identifier);
else
anyLocked = true;
}
return ApiHelpers.Return(anyLocked
? GlamourerApiEc.InvalidKey
: GlamourerApiEc.Success, args);
}
public int UnlockAll(uint key) public int UnlockAll(uint key)
=> _stateManager.Values.Count(state => state.Unlock(key)); => _stateManager.Values.Count(state => state.Unlock(key));
@ -331,7 +251,6 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
return ApiHelpers.Return(GlamourerApiEc.Success, args); return ApiHelpers.Return(GlamourerApiEc.Success, args);
} }
public event Action<bool>? AutoReloadGearChanged;
public event Action<nint>? StateChanged; public event Action<nint>? StateChanged;
public event Action<IntPtr, StateChangeType>? StateChangedWithType; public event Action<IntPtr, StateChangeType>? StateChangedWithType;
public event Action<IntPtr, StateFinalizationType>? StateFinalized; public event Action<IntPtr, StateFinalizationType>? StateFinalized;
@ -346,27 +265,9 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
ApiHelpers.Lock(state, key, flags); ApiHelpers.Lock(state, key, flags);
} }
private GlamourerApiEc Reapply(ActorState state, uint key, ApplyFlag flags)
{
if (!_objects.TryGetValue(state.Identifier, out var actors) || !actors.Valid)
return GlamourerApiEc.ActorNotFound;
foreach (var actor in actors.Objects)
Reapply(actor, state, key, flags);
return GlamourerApiEc.Success;
}
private void Reapply(Actor actor, ActorState state, uint key, ApplyFlag flags)
{
var source = flags.HasFlag(ApplyFlag.Once) ? StateSource.IpcFixed : StateSource.IpcManual;
_stateManager.ReapplyState(actor, state, false, source, true);
ApiHelpers.Lock(state, key, flags);
}
private void Revert(ActorState state, uint key, ApplyFlag flags) private void Revert(ActorState state, uint key, ApplyFlag flags)
{ {
var source = flags.HasFlag(ApplyFlag.Once) ? StateSource.IpcFixed : StateSource.IpcManual; var source = (flags & ApplyFlag.Once) != 0 ? StateSource.IpcManual : StateSource.IpcFixed;
switch (flags & (ApplyFlag.Equipment | ApplyFlag.Customization)) switch (flags & (ApplyFlag.Equipment | ApplyFlag.Customization))
{ {
case ApplyFlag.Equipment: _stateManager.ResetEquip(state, source, key); break; case ApplyFlag.Equipment: _stateManager.ResetEquip(state, source, key); break;
@ -424,8 +325,8 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
}; };
} }
private void OnAutoRedrawChange(bool autoReload) private void OnGPoseChange(bool gPose)
=> AutoReloadGearChanged?.Invoke(autoReload); => GPoseChanged?.Invoke(gPose);
private void OnStateChanged(StateChangeType type, StateSource _2, ActorState _3, ActorData actors, ITransaction? _5) private void OnStateChanged(StateChangeType type, StateSource _2, ActorState _3, ActorData actors, ITransaction? _5)
{ {
@ -446,7 +347,4 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
foreach (var actor in actors.Objects) foreach (var actor in actors.Objects)
StateFinalized.Invoke(actor.Address, type); StateFinalized.Invoke(actor.Address, type);
} }
private void OnGPoseChange(bool gPose)
=> GPoseChanged?.Invoke(gPose);
} }

View file

@ -100,7 +100,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
public new JObject JsonSerialize() public new JObject JsonSerialize()
{ {
var ret = new JObject var ret = new JObject()
{ {
["FileVersion"] = FileVersion, ["FileVersion"] = FileVersion,
["Identifier"] = Identifier, ["Identifier"] = Identifier,
@ -131,17 +131,12 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
var ret = new JArray(); var ret = new JArray();
foreach (var (mod, settings) in AssociatedMods) foreach (var (mod, settings) in AssociatedMods)
{ {
var obj = new JObject var obj = new JObject()
{ {
["Name"] = mod.Name, ["Name"] = mod.Name,
["Directory"] = mod.DirectoryName, ["Directory"] = mod.DirectoryName,
["Enabled"] = settings.Enabled,
}; };
if (settings.Remove)
obj["Remove"] = true;
else if (settings.ForceInherit)
obj["Inherit"] = true;
else
obj["Enabled"] = settings.Enabled;
if (settings.Enabled) if (settings.Enabled)
{ {
obj["Priority"] = settings.Priority; obj["Priority"] = settings.Priority;

View file

@ -6,13 +6,12 @@ using Glamourer.GameData;
using Glamourer.Interop.Material; using Glamourer.Interop.Material;
using Glamourer.Interop.Penumbra; using Glamourer.Interop.Penumbra;
using Glamourer.Services; using Glamourer.Services;
using OtterGui.Extensions;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui.Extensions;
using Penumbra.GameData.DataContainers; using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
namespace Glamourer.Designs; namespace Glamourer.Designs;
public sealed class DesignManager : DesignEditor public sealed class DesignManager : DesignEditor
@ -229,7 +228,7 @@ public sealed class DesignManager : DesignEditor
design.Tags = design.Tags.Append(tag).OrderBy(t => t).ToArray(); design.Tags = design.Tags.Append(tag).OrderBy(t => t).ToArray();
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
var idx = design.Tags.AsEnumerable().IndexOf(tag); var idx = design.Tags.IndexOf(tag);
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Added tag {tag} at {idx} to design {design.Identifier}."); Glamourer.Log.Debug($"Added tag {tag} at {idx} to design {design.Identifier}.");
DesignChanged.Invoke(DesignChanged.Type.AddedTag, design, new TagAddedTransaction(tag, idx)); DesignChanged.Invoke(DesignChanged.Type.AddedTag, design, new TagAddedTransaction(tag, idx));
@ -262,7 +261,7 @@ public sealed class DesignManager : DesignEditor
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Renamed tag {oldTag} at {tagIdx} to {newTag} in design {design.Identifier} and reordered tags."); Glamourer.Log.Debug($"Renamed tag {oldTag} at {tagIdx} to {newTag} in design {design.Identifier} and reordered tags.");
DesignChanged.Invoke(DesignChanged.Type.ChangedTag, design, DesignChanged.Invoke(DesignChanged.Type.ChangedTag, design,
new TagChangedTransaction(oldTag, newTag, tagIdx, design.Tags.AsEnumerable().IndexOf(newTag))); new TagChangedTransaction(oldTag, newTag, tagIdx, design.Tags.IndexOf(newTag)));
} }
/// <summary> Add an associated mod to a design. </summary> /// <summary> Add an associated mod to a design. </summary>
@ -557,7 +556,7 @@ public sealed class DesignManager : DesignEditor
try try
{ {
File.Move(SaveService.FileNames.MigrationDesignFile, File.Move(SaveService.FileNames.MigrationDesignFile,
Path.ChangeExtension(SaveService.FileNames.MigrationDesignFile, ".json.bak"), true); Path.ChangeExtension(SaveService.FileNames.MigrationDesignFile, ".json.bak"));
Glamourer.Log.Information($"Moved migrated design file {SaveService.FileNames.MigrationDesignFile} to backup file."); Glamourer.Log.Information($"Moved migrated design file {SaveService.FileNames.MigrationDesignFile} to backup file.");
} }
catch (Exception ex) catch (Exception ex)

View file

@ -1,16 +0,0 @@
using OtterGui.Classes;
namespace Glamourer.Events;
/// <summary>
/// Triggered when the auto-reload gear setting is changed in glamourer configuration.
/// </summary>
public sealed class AutoRedrawChanged()
: EventWrapper<bool, AutoRedrawChanged.Priority>(nameof(AutoRedrawChanged))
{
public enum Priority
{
/// <seealso cref="Api.StateApi.OnGPoseChange"/>
StateApi = int.MinValue,
}
}

View file

@ -1,4 +1,4 @@
<Project Sdk="Dalamud.NET.Sdk/14.0.1"> <Project Sdk="Dalamud.NET.Sdk/13.0.0">
<PropertyGroup> <PropertyGroup>
<RootNamespace>Glamourer</RootNamespace> <RootNamespace>Glamourer</RootNamespace>
<AssemblyName>Glamourer</AssemblyName> <AssemblyName>Glamourer</AssemblyName>

View file

@ -8,7 +8,7 @@
"AssemblyVersion": "9.0.0.1", "AssemblyVersion": "9.0.0.1",
"RepoUrl": "https://github.com/Ottermandias/Glamourer", "RepoUrl": "https://github.com/Ottermandias/Glamourer",
"ApplicableVersion": "any", "ApplicableVersion": "any",
"DalamudApiLevel": 14, "DalamudApiLevel": 13,
"ImageUrls": null, "ImageUrls": null,
"IconUrl": "https://raw.githubusercontent.com/Ottermandias/Glamourer/master/images/icon.png" "IconUrl": "https://raw.githubusercontent.com/Ottermandias/Glamourer/master/images/icon.png"
} }

View file

@ -102,8 +102,6 @@ public class MainWindow : Window, IDisposable
SelectTab = _config.Ephemeral.SelectedTab; SelectTab = _config.Ephemeral.SelectedTab;
_event.Subscribe(OnTabSelected, TabSelected.Priority.MainWindow); _event.Subscribe(OnTabSelected, TabSelected.Priority.MainWindow);
IsOpen = _config.OpenWindowAtStart; IsOpen = _config.OpenWindowAtStart;
_penumbra.DrawSettingsSection += Settings.DrawPenumbraIntegrationSettings;
} }
public void OpenSettings() public void OpenSettings()
@ -122,10 +120,7 @@ public class MainWindow : Window, IDisposable
} }
public void Dispose() public void Dispose()
{ => _event.Unsubscribe(OnTabSelected);
_event.Unsubscribe(OnTabSelected);
_penumbra.DrawSettingsSection -= Settings.DrawPenumbraIntegrationSettings;
}
public override void Draw() public override void Draw()
{ {

View file

@ -22,12 +22,11 @@ public sealed class PenumbraChangedItemTooltip : IDisposable
private readonly CustomizeService _customize; private readonly CustomizeService _customize;
private readonly GPoseService _gpose; private readonly GPoseService _gpose;
private readonly EquipItem[] _lastItems = new EquipItem[EquipFlagExtensions.NumEquipFlags / 2 + BonusExtensions.AllFlags.Count]; private readonly EquipItem[] _lastItems = new EquipItem[EquipFlagExtensions.NumEquipFlags / 2];
public IEnumerable<KeyValuePair<object, EquipItem>> LastItems public IEnumerable<KeyValuePair<EquipSlot, EquipItem>> LastItems
=> EquipSlotExtensions.EqdpSlots.Cast<object>().Append(EquipSlot.MainHand).Append(EquipSlot.OffHand) => EquipSlotExtensions.EqdpSlots.Append(EquipSlot.MainHand).Append(EquipSlot.OffHand).Zip(_lastItems)
.Concat(BonusExtensions.AllFlags.Cast<object>()).Zip(_lastItems) .Select(p => new KeyValuePair<EquipSlot, EquipItem>(p.First, p.Second));
.Select(p => new KeyValuePair<object, EquipItem>(p.First, p.Second));
public ChangedItemType LastType { get; private set; } = ChangedItemType.None; public ChangedItemType LastType { get; private set; } = ChangedItemType.None;
public uint LastId { get; private set; } public uint LastId { get; private set; }
@ -73,21 +72,6 @@ public sealed class PenumbraChangedItemTooltip : IDisposable
if (!Player()) if (!Player())
return; return;
var bonusSlot = item.Type.ToBonus();
if (bonusSlot is not BonusItemFlag.Unknown)
{
// + 2 due to weapons.
var glasses = _lastItems[bonusSlot.ToSlot() + 2];
using (_ = !openTooltip ? null : ImRaii.Tooltip())
{
ImGui.TextUnformatted($"{prefix}Right-Click to apply to current actor.");
if (glasses.Valid)
ImGui.TextUnformatted($"{prefix}Control + Right-Click to re-apply {glasses.Name} to current actor.");
}
return;
}
var slot = item.Type.ToSlot(); var slot = item.Type.ToSlot();
var last = _lastItems[slot.ToIndex()]; var last = _lastItems[slot.ToIndex()];
switch (slot) switch (slot)
@ -125,27 +109,6 @@ public sealed class PenumbraChangedItemTooltip : IDisposable
public void ApplyItem(ActorState state, EquipItem item) public void ApplyItem(ActorState state, EquipItem item)
{ {
var bonusSlot = item.Type.ToBonus();
if (bonusSlot is not BonusItemFlag.Unknown)
{
// + 2 due to weapons.
var glasses = _lastItems[bonusSlot.ToSlot() + 2];
if (ImGui.GetIO().KeyCtrl && glasses.Valid)
{
Glamourer.Log.Debug($"Re-Applying {glasses.Name} to {bonusSlot.ToName()}.");
SetLastItem(bonusSlot, default, state);
_stateManager.ChangeBonusItem(state, bonusSlot, glasses, ApplySettings.Manual);
}
else
{
Glamourer.Log.Debug($"Applying {item.Name} to {bonusSlot.ToName()}.");
SetLastItem(bonusSlot, item, state);
_stateManager.ChangeBonusItem(state, bonusSlot, item, ApplySettings.Manual);
}
return;
}
var slot = item.Type.ToSlot(); var slot = item.Type.ToSlot();
var last = _lastItems[slot.ToIndex()]; var last = _lastItems[slot.ToIndex()];
switch (slot) switch (slot)
@ -302,22 +265,7 @@ public sealed class PenumbraChangedItemTooltip : IDisposable
{ {
var oldItem = state.ModelData.Item(slot); var oldItem = state.ModelData.Item(slot);
if (oldItem.Id != item.Id) if (oldItem.Id != item.Id)
last = oldItem; _lastItems[slot.ToIndex()] = oldItem;
}
}
private void SetLastItem(BonusItemFlag slot, EquipItem item, ActorState state)
{
ref var last = ref _lastItems[slot.ToSlot() + 2];
if (!item.Valid)
{
last = default;
}
else
{
var oldItem = state.ModelData.BonusItem(slot);
if (oldItem.Id != item.Id)
last = oldItem;
} }
} }

View file

@ -1,5 +1,8 @@
using Glamourer.Api.Enums; using Glamourer.Api.Enums;
using Glamourer.Designs;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using OtterGui;
using static Penumbra.GameData.Files.ShpkFile;
namespace Glamourer.Gui.Tabs.DebugTab.IpcTester; namespace Glamourer.Gui.Tabs.DebugTab.IpcTester;

View file

@ -33,11 +33,6 @@ public class IpcTesterPanel(
ImGui.SameLine(); ImGui.SameLine();
ImGui.TextUnformatted($"({major}.{minor:D4})"); ImGui.TextUnformatted($"({major}.{minor:D4})");
ImGui.TextUnformatted(AutoReloadGearEnabled.Label);
var autoRedraw = new AutoReloadGearEnabled(pluginInterface).Invoke();
ImGui.SameLine();
ImGui.TextUnformatted(autoRedraw ? "Enabled" : "Disabled");
designs.Draw(); designs.Draw();
items.Draw(); items.Draw();
state.Draw(); state.Draw();
@ -54,7 +49,6 @@ public class IpcTesterPanel(
return; return;
Glamourer.Log.Debug("[IPCTester] Subscribed to IPC events for IPC tester."); Glamourer.Log.Debug("[IPCTester] Subscribed to IPC events for IPC tester.");
state.AutoRedrawChanged.Enable();
state.GPoseChanged.Enable(); state.GPoseChanged.Enable();
state.StateChanged.Enable(); state.StateChanged.Enable();
state.StateFinalized.Enable(); state.StateFinalized.Enable();
@ -78,7 +72,6 @@ public class IpcTesterPanel(
Glamourer.Log.Debug("[IPCTester] Unsubscribed from IPC events for IPC tester."); Glamourer.Log.Debug("[IPCTester] Unsubscribed from IPC events for IPC tester.");
_subscribed = false; _subscribed = false;
state.AutoRedrawChanged.Disable();
state.GPoseChanged.Disable(); state.GPoseChanged.Disable();
state.StateChanged.Disable(); state.StateChanged.Disable();
state.StateFinalized.Disable(); state.StateFinalized.Disable();

View file

@ -1,11 +1,11 @@
using Dalamud.Bindings.ImGui; using Dalamud.Interface;
using Dalamud.Interface;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Dalamud.Plugin; using Dalamud.Plugin;
using Glamourer.Api.Enums; using Glamourer.Api.Enums;
using Glamourer.Api.Helpers; using Glamourer.Api.Helpers;
using Glamourer.Api.IpcSubscribers; using Glamourer.Api.IpcSubscribers;
using Glamourer.Designs; using Glamourer.Designs;
using Dalamud.Bindings.ImGui;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui; using OtterGui;
@ -31,10 +31,6 @@ public class StateIpcTester : IUiService, IDisposable
private string _base64State = string.Empty; private string _base64State = string.Empty;
private string? _getStateString; private string? _getStateString;
public readonly EventSubscriber<bool> AutoRedrawChanged;
private bool _lastAutoRedrawChangeValue;
private DateTime _lastAutoRedrawChangeTime;
public readonly EventSubscriber<nint, StateChangeType> StateChanged; public readonly EventSubscriber<nint, StateChangeType> StateChanged;
private nint _lastStateChangeActor; private nint _lastStateChangeActor;
private ByteString _lastStateChangeName = ByteString.Empty; private ByteString _lastStateChangeName = ByteString.Empty;
@ -56,11 +52,9 @@ public class StateIpcTester : IUiService, IDisposable
public StateIpcTester(IDalamudPluginInterface pluginInterface) public StateIpcTester(IDalamudPluginInterface pluginInterface)
{ {
_pluginInterface = pluginInterface; _pluginInterface = pluginInterface;
AutoRedrawChanged = AutoReloadGearChanged.Subscriber(_pluginInterface, OnAutoRedrawChanged);
StateChanged = StateChangedWithType.Subscriber(_pluginInterface, OnStateChanged); StateChanged = StateChangedWithType.Subscriber(_pluginInterface, OnStateChanged);
StateFinalized = Api.IpcSubscribers.StateFinalized.Subscriber(_pluginInterface, OnStateFinalized); StateFinalized = Api.IpcSubscribers.StateFinalized.Subscriber(_pluginInterface, OnStateFinalized);
GPoseChanged = Api.IpcSubscribers.GPoseChanged.Subscriber(_pluginInterface, OnGPoseChange); GPoseChanged = Api.IpcSubscribers.GPoseChanged.Subscriber(_pluginInterface, OnGPoseChange);
AutoRedrawChanged.Disable();
StateChanged.Disable(); StateChanged.Disable();
StateFinalized.Disable(); StateFinalized.Disable();
GPoseChanged.Disable(); GPoseChanged.Disable();
@ -68,7 +62,6 @@ public class StateIpcTester : IUiService, IDisposable
public void Dispose() public void Dispose()
{ {
AutoRedrawChanged.Dispose();
StateChanged.Dispose(); StateChanged.Dispose();
StateFinalized.Dispose(); StateFinalized.Dispose();
GPoseChanged.Dispose(); GPoseChanged.Dispose();
@ -90,8 +83,6 @@ public class StateIpcTester : IUiService, IDisposable
IpcTesterHelpers.DrawIntro("Last Error"); IpcTesterHelpers.DrawIntro("Last Error");
ImGui.TextUnformatted(_lastError.ToString()); ImGui.TextUnformatted(_lastError.ToString());
IpcTesterHelpers.DrawIntro("Last Auto Redraw Change");
ImGui.TextUnformatted($"{_lastAutoRedrawChangeValue} at {_lastAutoRedrawChangeTime.ToLocalTime().TimeOfDay}");
IpcTesterHelpers.DrawIntro("Last State Change"); IpcTesterHelpers.DrawIntro("Last State Change");
PrintChangeName(); PrintChangeName();
IpcTesterHelpers.DrawIntro("Last State Finalization"); IpcTesterHelpers.DrawIntro("Last State Finalization");
@ -147,14 +138,6 @@ public class StateIpcTester : IUiService, IDisposable
if (ImUtf8.Button("Apply Base64##Name"u8)) if (ImUtf8.Button("Apply Base64##Name"u8))
_lastError = new ApplyStateName(_pluginInterface).Invoke(_base64State, _gameObjectName, _key, _flags); _lastError = new ApplyStateName(_pluginInterface).Invoke(_base64State, _gameObjectName, _key, _flags);
IpcTesterHelpers.DrawIntro(ReapplyState.Label);
if (ImUtf8.Button("Reapply##Idx"u8))
_lastError = new ReapplyState(_pluginInterface).Invoke(_gameObjectIndex, _key, _flags);
IpcTesterHelpers.DrawIntro(ReapplyStateName.Label);
if (ImUtf8.Button("Reapply##Name"u8))
_lastError = new ReapplyStateName(_pluginInterface).Invoke(_gameObjectName, _key, _flags);
IpcTesterHelpers.DrawIntro(RevertState.Label); IpcTesterHelpers.DrawIntro(RevertState.Label);
if (ImUtf8.Button("Revert##Idx"u8)) if (ImUtf8.Button("Revert##Idx"u8))
_lastError = new RevertState(_pluginInterface).Invoke(_gameObjectIndex, _key, _flags); _lastError = new RevertState(_pluginInterface).Invoke(_gameObjectIndex, _key, _flags);
@ -242,12 +225,6 @@ public class StateIpcTester : IUiService, IDisposable
ImUtf8.Text($"at {_lastStateFinalizeTime.ToLocalTime().TimeOfDay}"); ImUtf8.Text($"at {_lastStateFinalizeTime.ToLocalTime().TimeOfDay}");
} }
private void OnAutoRedrawChanged(bool value)
{
_lastAutoRedrawChangeValue = value;
_lastAutoRedrawChangeTime = DateTime.UtcNow;
}
private void OnStateChanged(nint actor, StateChangeType type) private void OnStateChanged(nint actor, StateChangeType type)
{ {
_lastStateChangeActor = actor; _lastStateChangeActor = actor;

View file

@ -89,13 +89,7 @@ public unsafe class PenumbraPanel(PenumbraService _penumbra, PenumbraChangedItem
ImGui.Separator(); ImGui.Separator();
foreach (var (slot, item) in _penumbraTooltip.LastItems) foreach (var (slot, item) in _penumbraTooltip.LastItems)
{ {
switch (slot) ImGuiUtil.DrawTableColumn($"{slot.ToName()} Revert-Item");
{
case EquipSlot e: ImGuiUtil.DrawTableColumn($"{e.ToName()} Revert-Item"); break;
case BonusItemFlag f: ImGuiUtil.DrawTableColumn($"{f.ToName()} Revert-Item"); break;
default: ImGuiUtil.DrawTableColumn("Unk Revert-Item"); break;
}
ImGuiUtil.DrawTableColumn(item.Valid ? item.Name : "None"); ImGuiUtil.DrawTableColumn(item.Valid ? item.Name : "None");
ImGui.TableNextColumn(); ImGui.TableNextColumn();
} }

View file

@ -71,8 +71,6 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelect
private void DrawApplyAllButton() private void DrawApplyAllButton()
{ {
var (id, name) = penumbra.CurrentCollection; var (id, name) = penumbra.CurrentCollection;
if (config.Ephemeral.IncognitoMode)
name = id.ShortGuid();
if (ImGuiUtil.DrawDisabledButton($"Try Applying All Associated Mods to {name}##applyAll", if (ImGuiUtil.DrawDisabledButton($"Try Applying All Associated Mods to {name}##applyAll",
new Vector2(ImGui.GetContentRegionAvail().X, 0), string.Empty, id == Guid.Empty)) new Vector2(ImGui.GetContentRegionAvail().X, 0), string.Empty, id == Guid.Empty))
ApplyAll(); ApplyAll();

View file

@ -490,7 +490,7 @@ public class MultiDesignPanel(
foreach (var leaf in selector.SelectedPaths.OfType<DesignFileSystem.Leaf>()) foreach (var leaf in selector.SelectedPaths.OfType<DesignFileSystem.Leaf>())
{ {
var index = leaf.Value.Tags.AsEnumerable().IndexOf(_tag); var index = leaf.Value.Tags.IndexOf(_tag);
if (index >= 0) if (index >= 0)
_removeDesigns.Add((leaf.Value, index)); _removeDesigns.Add((leaf.Value, index));
else else

View file

@ -6,11 +6,9 @@ using Dalamud.Interface.Utility;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Glamourer.Automation; using Glamourer.Automation;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Events;
using Glamourer.Gui.Tabs.DesignTab; using Glamourer.Gui.Tabs.DesignTab;
using Glamourer.Interop; using Glamourer.Interop;
using Glamourer.Interop.PalettePlus; using Glamourer.Interop.PalettePlus;
using Glamourer.Interop.Penumbra;
using Glamourer.Services; using Glamourer.Services;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
@ -32,7 +30,6 @@ public class SettingsTab(
CodeDrawer codeDrawer, CodeDrawer codeDrawer,
Glamourer glamourer, Glamourer glamourer,
AutoDesignApplier autoDesignApplier, AutoDesignApplier autoDesignApplier,
AutoRedrawChanged autoRedraw,
PcpService pcpService) PcpService pcpService)
: ITab : ITab
{ {
@ -72,12 +69,6 @@ public class SettingsTab(
MainWindow.DrawSupportButtons(glamourer, changelog.Changelog); MainWindow.DrawSupportButtons(glamourer, changelog.Changelog);
} }
public void DrawPenumbraIntegrationSettings()
{
DrawPenumbraIntegrationSettings1();
DrawPenumbraIntegrationSettings2();
}
private void DrawBehaviorSettings() private void DrawBehaviorSettings()
{ {
if (!ImUtf8.CollapsingHeader("Glamourer Behavior"u8)) if (!ImUtf8.CollapsingHeader("Glamourer Behavior"u8))
@ -98,27 +89,9 @@ public class SettingsTab(
Checkbox("Enable Festival Easter-Eggs"u8, Checkbox("Enable Festival Easter-Eggs"u8,
"Glamourer may do some fun things on specific dates. Disable this if you do not want your experience disrupted by this."u8, "Glamourer may do some fun things on specific dates. Disable this if you do not want your experience disrupted by this."u8,
config.DisableFestivals == 0, v => config.DisableFestivals = v ? (byte)0 : (byte)2); config.DisableFestivals == 0, v => config.DisableFestivals = v ? (byte)0 : (byte)2);
DrawPenumbraIntegrationSettings1();
Checkbox("Revert Manual Changes on Zone Change"u8,
"Restores the old behaviour of reverting your character to its game or automation base whenever you change the zone."u8,
config.RevertManualChangesOnZoneChange, v => config.RevertManualChangesOnZoneChange = v);
PaletteImportButton();
DrawPenumbraIntegrationSettings2();
Checkbox("Prevent Random Design Repeats"u8,
"When using random designs, prevent the same design from being chosen twice in a row."u8,
config.PreventRandomRepeats, v => config.PreventRandomRepeats = v);
ImGui.NewLine();
}
private void DrawPenumbraIntegrationSettings1()
{
Checkbox("Auto-Reload Gear"u8, Checkbox("Auto-Reload Gear"u8,
"Automatically reload equipment pieces on your own character when changing any mod options in Penumbra in their associated collection."u8, "Automatically reload equipment pieces on your own character when changing any mod options in Penumbra in their associated collection."u8,
config.AutoRedrawEquipOnChanges, v => config.AutoRedrawEquipOnChanges, v => config.AutoRedrawEquipOnChanges = v);
{
config.AutoRedrawEquipOnChanges = v;
autoRedraw.Invoke(v);
});
Checkbox("Attach to PCP-Handling"u8, Checkbox("Attach to PCP-Handling"u8,
"Add the actor's glamourer state when a PCP is created by Penumbra, and create a design and apply it if possible when a PCP is installed by Penumbra."u8, "Add the actor's glamourer state when a PCP is created by Penumbra, and create a design and apply it if possible when a PCP is installed by Penumbra."u8,
config.AttachToPcp, pcpService.Set); config.AttachToPcp, pcpService.Set);
@ -128,10 +101,10 @@ public class SettingsTab(
pcpService.CleanPcpDesigns(); pcpService.CleanPcpDesigns();
if (!active) if (!active)
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"\nHold {config.DeleteDesignModifier} while clicking."); ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"\nHold {config.DeleteDesignModifier} while clicking.");
} Checkbox("Revert Manual Changes on Zone Change"u8,
"Restores the old behaviour of reverting your character to its game or automation base whenever you change the zone."u8,
private void DrawPenumbraIntegrationSettings2() config.RevertManualChangesOnZoneChange, v => config.RevertManualChangesOnZoneChange = v);
{ PaletteImportButton();
Checkbox("Always Apply Associated Mods"u8, Checkbox("Always Apply Associated Mods"u8,
"Whenever a design is applied to a character (including via automation), Glamourer will try to apply its associated mod settings to the collection currently associated with that character, if it is available.\n\n"u8 "Whenever a design is applied to a character (including via automation), Glamourer will try to apply its associated mod settings to the collection currently associated with that character, if it is available.\n\n"u8
+ "Glamourer will NOT revert these applied settings automatically. This may mess up your collection and configuration.\n\n"u8 + "Glamourer will NOT revert these applied settings automatically. This may mess up your collection and configuration.\n\n"u8
@ -141,6 +114,10 @@ public class SettingsTab(
"Apply all settings as temporary settings so they will be reset when Glamourer or the game shut down."u8, "Apply all settings as temporary settings so they will be reset when Glamourer or the game shut down."u8,
config.UseTemporarySettings, config.UseTemporarySettings,
v => config.UseTemporarySettings = v); v => config.UseTemporarySettings = v);
Checkbox("Prevent Random Design Repeats"u8,
"When using random designs, prevent the same design from being chosen twice in a row."u8,
config.PreventRandomRepeats, v => config.PreventRandomRepeats = v);
ImGui.NewLine();
} }
private void DrawDesignDefaultSettings() private void DrawDesignDefaultSettings()

View file

@ -62,7 +62,7 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
var drawData = type switch var drawData = type switch
{ {
MaterialValueIndex.DrawObjectType.Human => GetTempSlot((Human*)characterBase, (HumanSlot)slotId), MaterialValueIndex.DrawObjectType.Human => GetTempSlot((Human*)characterBase, slotId),
_ => GetTempSlot((Weapon*)characterBase), _ => GetTempSlot((Weapon*)characterBase),
}; };
var mode = PrepareColorSet.GetMode(material); var mode = PrepareColorSet.GetMode(material);
@ -192,24 +192,13 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
} }
/// <summary> We need to get the temporary set, variant and stain that is currently being set if it is available. </summary> /// <summary> We need to get the temporary set, variant and stain that is currently being set if it is available. </summary>
private static CharacterWeapon GetTempSlot(Human* human, HumanSlot slotId) private static CharacterWeapon GetTempSlot(Human* human, byte slotId)
{ {
if (human->ChangedEquipData is null) if (human->ChangedEquipData == null)
return slotId.ToSpecificEnum() switch return ((Model)human).GetArmor(((uint)slotId).ToEquipSlot()).ToWeapon(0);
{
EquipSlot slot => ((Model)human).GetArmor(slot).ToWeapon(0),
BonusItemFlag bonus => ((Model)human).GetBonus(bonus).ToWeapon(0),
_ => default,
};
if (!slotId.ToSlotIndex(out var index)) var item = (ChangedEquipData*)human->ChangedEquipData + slotId;
return default;
var item = (ChangedEquipData*)human->ChangedEquipData + index;
if (index < 10)
return ((CharacterArmor*)item)->ToWeapon(0); return ((CharacterArmor*)item)->ToWeapon(0);
return new CharacterWeapon(item->BonusModel, 0, item->BonusVariant, StainIds.None);
} }
/// <summary> /// <summary>

View file

@ -1,5 +1,6 @@
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
using Lumina.Data.Files;
using Penumbra.GameData.Files.MaterialStructs; using Penumbra.GameData.Files.MaterialStructs;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Texture = FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Texture; using Texture = FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Texture;
@ -68,9 +69,9 @@ public static unsafe class MaterialService
return null; return null;
var material = (MaterialResourceHandle*) model.AsCharacterBase->MaterialsSpan[index].Value; var material = (MaterialResourceHandle*) model.AsCharacterBase->MaterialsSpan[index].Value;
if (material == null || material->DataSet == null || material->DataSetSize < sizeof(ColorTable.Table) || !material->HasColorTable) if (material == null || material->ColorTable == null)
return null; return null;
return (ColorTable.Table*)material->DataSet; return (ColorTable.Table*)material->ColorTable;
} }
} }

View file

@ -69,13 +69,13 @@ public sealed unsafe class PrepareColorSet
public static bool TryGetColorTable(MaterialResourceHandle* material, StainIds stainIds, public static bool TryGetColorTable(MaterialResourceHandle* material, StainIds stainIds,
out ColorTable.Table table) out ColorTable.Table table)
{ {
if (material->DataSet == null || material->DataSetSize < sizeof(ColorTable.Table) || !material->HasColorTable) if (material->ColorTable == null)
{ {
table = default; table = default;
return false; return false;
} }
var newTable = *(ColorTable.Table*)material->DataSet; var newTable = *(ColorTable.Table*)material->ColorTable;
if (GetDyeTable(material, out var dyeTable)) if (GetDyeTable(material, out var dyeTable))
{ {
if (stainIds.Stain1.Id != 0) if (stainIds.Stain1.Id != 0)

View file

@ -1,6 +1,5 @@
using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification;
using Dalamud.Plugin; using Dalamud.Plugin;
using Dalamud.Plugin.Ipc.Exceptions;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.State; using Glamourer.State;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@ -37,7 +36,7 @@ public readonly record struct ModSettings(Dictionary<string, List<string>> Setti
public class PenumbraService : IDisposable public class PenumbraService : IDisposable
{ {
public const int RequiredPenumbraBreakingVersion = 5; public const int RequiredPenumbraBreakingVersion = 5;
public const int RequiredPenumbraFeatureVersion = 13; public const int RequiredPenumbraFeatureVersion = 8;
private const int KeyFixed = -1610; private const int KeyFixed = -1610;
private const string NameFixed = "Glamourer (Automation)"; private const string NameFixed = "Glamourer (Automation)";
@ -78,8 +77,6 @@ public class PenumbraService : IDisposable
private global::Penumbra.Api.IpcSubscribers.QueryTemporaryModSettingsPlayer? _queryTemporaryModSettingsPlayer; private global::Penumbra.Api.IpcSubscribers.QueryTemporaryModSettingsPlayer? _queryTemporaryModSettingsPlayer;
private global::Penumbra.Api.IpcSubscribers.OpenMainWindow? _openModPage; private global::Penumbra.Api.IpcSubscribers.OpenMainWindow? _openModPage;
private global::Penumbra.Api.IpcSubscribers.GetChangedItems? _getChangedItems; private global::Penumbra.Api.IpcSubscribers.GetChangedItems? _getChangedItems;
private global::Penumbra.Api.IpcSubscribers.RegisterSettingsSection? _registerSettingsSection;
private global::Penumbra.Api.IpcSubscribers.UnregisterSettingsSection? _unregisterSettingsSection;
private IReadOnlyList<(string ModDirectory, IReadOnlyDictionary<string, object?> ChangedItems)>? _changedItems; private IReadOnlyList<(string ModDirectory, IReadOnlyDictionary<string, object?> ChangedItems)>? _changedItems;
private Func<string, (string ModDirectory, string ModName)[]>? _checkCurrentChangedItems; private Func<string, (string ModDirectory, string ModName)[]>? _checkCurrentChangedItems;
private Func<int, int>? _checkCutsceneParent; private Func<int, int>? _checkCutsceneParent;
@ -155,11 +152,6 @@ public class PenumbraService : IDisposable
remove => _pcpParsed.Event -= value; remove => _pcpParsed.Event -= value;
} }
public event Action? DrawSettingsSection;
private void InvokeDrawSettingsSection()
=> DrawSettingsSection?.Invoke();
public Dictionary<Guid, string> GetCollections() public Dictionary<Guid, string> GetCollections()
=> Available ? _collections!.Invoke() : []; => Available ? _collections!.Invoke() : [];
@ -573,10 +565,6 @@ public class PenumbraService : IDisposable
_changedItems = new global::Penumbra.Api.IpcSubscribers.GetChangedItemAdapterList(_pluginInterface).Invoke(); _changedItems = new global::Penumbra.Api.IpcSubscribers.GetChangedItemAdapterList(_pluginInterface).Invoke();
_checkCurrentChangedItems = _checkCurrentChangedItems =
new global::Penumbra.Api.IpcSubscribers.CheckCurrentChangedItemFunc(_pluginInterface).Invoke(); new global::Penumbra.Api.IpcSubscribers.CheckCurrentChangedItemFunc(_pluginInterface).Invoke();
_registerSettingsSection = new global::Penumbra.Api.IpcSubscribers.RegisterSettingsSection(_pluginInterface);
_unregisterSettingsSection = new global::Penumbra.Api.IpcSubscribers.UnregisterSettingsSection(_pluginInterface);
_registerSettingsSection.Invoke(InvokeDrawSettingsSection);
Available = true; Available = true;
_penumbraReloaded.Invoke(); _penumbraReloaded.Invoke();
@ -599,15 +587,6 @@ public class PenumbraService : IDisposable
_modSettingChanged.Disable(); _modSettingChanged.Disable();
_pcpCreated.Disable(); _pcpCreated.Disable();
_pcpParsed.Disable(); _pcpParsed.Disable();
try
{
_unregisterSettingsSection?.Invoke(InvokeDrawSettingsSection);
}
catch (IpcNotReadyError)
{
// Ignore.
}
if (Available) if (Available)
{ {
_collectionByIdentifier = null; _collectionByIdentifier = null;
@ -638,8 +617,6 @@ public class PenumbraService : IDisposable
_getChangedItems = null; _getChangedItems = null;
_changedItems = null; _changedItems = null;
_checkCurrentChangedItems = null; _checkCurrentChangedItems = null;
_registerSettingsSection = null;
_unregisterSettingsSection = null;
Available = false; Available = false;
Glamourer.Log.Debug("Glamourer detached from Penumbra."); Glamourer.Log.Debug("Glamourer detached from Penumbra.");
} }

View file

@ -1,10 +1,9 @@
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Log; using OtterGui.Log;
using OtterGui.Services;
namespace Glamourer.Services; namespace Glamourer.Services;
public class BackupService : IAsyncService public class BackupService
{ {
private readonly Logger _logger; private readonly Logger _logger;
private readonly DirectoryInfo _configDirectory; private readonly DirectoryInfo _configDirectory;
@ -15,7 +14,7 @@ public class BackupService : IAsyncService
_logger = logger; _logger = logger;
_fileNames = GlamourerFiles(fileNames); _fileNames = GlamourerFiles(fileNames);
_configDirectory = new DirectoryInfo(fileNames.ConfigDirectory); _configDirectory = new DirectoryInfo(fileNames.ConfigDirectory);
Awaiter = Task.Run(() => Backup.CreateAutomaticBackup(logger, new DirectoryInfo(fileNames.ConfigDirectory), _fileNames)); Backup.CreateAutomaticBackup(logger, _configDirectory, _fileNames);
} }
/// <summary> Create a permanent backup with a given name for migrations. </summary> /// <summary> Create a permanent backup with a given name for migrations. </summary>
@ -41,9 +40,4 @@ public class BackupService : IAsyncService
return list; return list;
} }
public Task Awaiter { get; }
public bool Finished
=> Awaiter.IsCompletedSuccessfully;
} }

View file

@ -50,8 +50,7 @@ public class CodeService
| CodeFlag.OopsMiqote | CodeFlag.OopsMiqote
| CodeFlag.OopsRoegadyn | CodeFlag.OopsRoegadyn
| CodeFlag.OopsAuRa | CodeFlag.OopsAuRa
| CodeFlag.OopsHrothgar | CodeFlag.OopsHrothgar;
| CodeFlag.OopsViera;
public const CodeFlag FullCodes = CodeFlag.Face | CodeFlag.Manderville | CodeFlag.Smiles; public const CodeFlag FullCodes = CodeFlag.Face | CodeFlag.Manderville | CodeFlag.Smiles;
@ -251,4 +250,3 @@ public class CodeService
_ => (false, 0, string.Empty, string.Empty, string.Empty), _ => (false, 0, string.Empty, string.Empty, string.Empty),
}; };
} }

View file

@ -1,3 +1,4 @@
using Dalamud.Game.ClientState.Objects;
using Dalamud.Interface.DragDrop; using Dalamud.Interface.DragDrop;
using Dalamud.Plugin; using Dalamud.Plugin;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
@ -16,7 +17,6 @@ public class DalamudServices
services.AddDalamudService<ICommandManager>(pi); services.AddDalamudService<ICommandManager>(pi);
services.AddDalamudService<IDataManager>(pi); services.AddDalamudService<IDataManager>(pi);
services.AddDalamudService<IClientState>(pi); services.AddDalamudService<IClientState>(pi);
services.AddDalamudService<IPlayerState>(pi);
services.AddDalamudService<ICondition>(pi); services.AddDalamudService<ICondition>(pi);
services.AddDalamudService<IGameGui>(pi); services.AddDalamudService<IGameGui>(pi);
services.AddDalamudService<IChatGui>(pi); services.AddDalamudService<IChatGui>(pi);

View file

@ -385,7 +385,7 @@ public class StateApplier(
var actors = ChangeMetaState(state, MetaIndex.Wetness, true); var actors = ChangeMetaState(state, MetaIndex.Wetness, true);
if (redraw) if (redraw)
{ {
if (withLock && actors.Valid) if (withLock)
state.TempLock(); state.TempLock();
ForceRedraw(actors); ForceRedraw(actors);
} }

View file

@ -1,18 +1,18 @@
{ {
"version": 1, "version": 1,
"dependencies": { "dependencies": {
"net10.0-windows7.0": { "net9.0-windows7.0": {
"DalamudPackager": { "DalamudPackager": {
"type": "Direct", "type": "Direct",
"requested": "[14.0.1, )", "requested": "[13.0.0, )",
"resolved": "14.0.1", "resolved": "13.0.0",
"contentHash": "y0WWyUE6dhpGdolK3iKgwys05/nZaVf4ZPtIjpLhJBZvHxkkiE23zYRo7K7uqAgoK/QvK5cqF6l3VG5AbgC6KA==" "contentHash": "Mb3cUDSK/vDPQ8gQIeuCw03EMYrej1B4J44a1AvIJ9C759p9XeqdU9Hg4WgOmlnlPe0G7ILTD32PKSUpkQNa8w=="
}, },
"DotNet.ReproducibleBuilds": { "DotNet.ReproducibleBuilds": {
"type": "Direct", "type": "Direct",
"requested": "[1.2.39, )", "requested": "[1.2.25, )",
"resolved": "1.2.39", "resolved": "1.2.25",
"contentHash": "fcFN01tDTIQqDuTwr1jUQK/geofiwjG5DycJQOnC72i1SsLAk1ELe+apBOuZ11UMQG8YKFZG1FgvjZPbqHyatg==" "contentHash": "xCXiw7BCxHJ8pF6wPepRUddlh2dlQlbr81gXA72hdk4FLHkKXas7EH/n+fk5UCA/YfMqG1Z6XaPiUjDbUNBUzg=="
}, },
"Vortice.Direct3D11": { "Vortice.Direct3D11": {
"type": "Direct", "type": "Direct",
@ -32,7 +32,10 @@
"FlatSharp.Runtime": { "FlatSharp.Runtime": {
"type": "Transitive", "type": "Transitive",
"resolved": "7.9.0", "resolved": "7.9.0",
"contentHash": "Bm8+WqzEsWNpxqrD5x4x+zQ8dyINlToCreM5FI2oNSfUVc9U9ZB+qztX/jd8rlJb3r0vBSlPwVLpw0xBtPa3Vw==" "contentHash": "Bm8+WqzEsWNpxqrD5x4x+zQ8dyINlToCreM5FI2oNSfUVc9U9ZB+qztX/jd8rlJb3r0vBSlPwVLpw0xBtPa3Vw==",
"dependencies": {
"System.Memory": "4.5.5"
}
}, },
"JetBrains.Annotations": { "JetBrains.Annotations": {
"type": "Transitive", "type": "Transitive",
@ -65,6 +68,11 @@
"SharpGen.Runtime": "2.1.2-beta" "SharpGen.Runtime": "2.1.2-beta"
} }
}, },
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.5",
"contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw=="
},
"Vortice.DirectX": { "Vortice.DirectX": {
"type": "Transitive", "type": "Transitive",
"resolved": "3.4.2-beta", "resolved": "3.4.2-beta",
@ -108,8 +116,8 @@
"FlatSharp.Compiler": "[7.9.0, )", "FlatSharp.Compiler": "[7.9.0, )",
"FlatSharp.Runtime": "[7.9.0, )", "FlatSharp.Runtime": "[7.9.0, )",
"OtterGui": "[1.0.0, )", "OtterGui": "[1.0.0, )",
"Penumbra.Api": "[5.13.0, )", "Penumbra.Api": "[5.10.0, )",
"Penumbra.String": "[1.0.7, )" "Penumbra.String": "[1.0.6, )"
} }
}, },
"penumbra.string": { "penumbra.string": {

@ -1 +1 @@
Subproject commit ff1e6543845e3b8c53a5f8b240bc38faffb1b3bf Subproject commit 4a9b71a93e76aa5eed818542288329e34ec0dd89

@ -1 +1 @@
Subproject commit 1750c41b53e1000c99a7fb9d8a0f082aef639a41 Subproject commit af41b1787acef9df7dc83619fe81e63a36443ee5

@ -1 +1 @@
Subproject commit 0e973ed6eace6afd31cd298f8c58f76fa8d5ef60 Subproject commit 15e7c8eb41867e6bbd3fe6a8885404df087bc7e7

@ -1 +1 @@
Subproject commit 9bd016fbef5fb2de467dd42165267fdd93cd9592 Subproject commit 878acce46e286867d6ef1f8ecedb390f7bac34fd

View file

@ -17,19 +17,19 @@
"Character" "Character"
], ],
"InternalName": "Glamourer", "InternalName": "Glamourer",
"AssemblyVersion": "1.5.1.6", "AssemblyVersion": "1.5.0.7",
"TestingAssemblyVersion": "1.5.1.6", "TestingAssemblyVersion": "1.5.0.9",
"RepoUrl": "https://github.com/Ottermandias/Glamourer", "RepoUrl": "https://github.com/Ottermandias/Glamourer",
"ApplicableVersion": "any", "ApplicableVersion": "any",
"DalamudApiLevel": 14, "DalamudApiLevel": 13,
"TestingDalamudApiLevel": 14, "TestingDalamudApiLevel": 13,
"IsHide": "False", "IsHide": "False",
"IsTestingExclusive": "False", "IsTestingExclusive": "False",
"DownloadCount": 1, "DownloadCount": 1,
"LastUpdate": 1618608322, "LastUpdate": 1618608322,
"DownloadLinkInstall": "https://github.com/Ottermandias/Glamourer/releases/download/1.5.1.6/Glamourer.zip", "DownloadLinkInstall": "https://github.com/Ottermandias/Glamourer/releases/download/1.5.0.7/Glamourer.zip",
"DownloadLinkUpdate": "https://github.com/Ottermandias/Glamourer/releases/download/1.5.1.6/Glamourer.zip", "DownloadLinkUpdate": "https://github.com/Ottermandias/Glamourer/releases/download/1.5.0.7/Glamourer.zip",
"DownloadLinkTesting": "https://github.com/Ottermandias/Glamourer/releases/download/1.5.1.6/Glamourer.zip", "DownloadLinkTesting": "https://github.com/Ottermandias/Glamourer/releases/download/testing_1.5.0.9/Glamourer.zip",
"IconUrl": "https://raw.githubusercontent.com/Ottermandias/Glamourer/main/images/icon.png" "IconUrl": "https://raw.githubusercontent.com/Ottermandias/Glamourer/main/images/icon.png"
} }
] ]