Compare commits

...

105 commits

Author SHA1 Message Date
Actions User
5b6517aae8 [CI] Updating repo.json for 1.5.1.5 2025-11-28 22:09:08 +00:00
Ottermandias
aadcf771e7 1.5.1.5 2025-11-28 23:06:53 +01:00
Ottermandias
bf4673a1d9 Save Remove and Inherit for mod associations. 2025-11-07 23:30:18 +01:00
Ottermandias
76b214c643 Fix try-on interaction with Penumbra for facewear. 2025-11-07 23:30:18 +01:00
Ottermandias
434a5a809e Make old backup files overwrite instead of throwing. 2025-11-07 23:30:18 +01:00
Actions User
88fe25f69e [CI] Updating repo.json for testing_1.5.1.4 2025-10-23 15:40:25 +00:00
Ottermandias
bef1e39ac3 Update Libraries. 2025-10-23 17:37:27 +02:00
Ottermandias
c604d5dbe5 Add DeletePlayerState. 2025-10-23 17:25:59 +02:00
Ottermandias
a0d912a395 Fix issue with reverting state of unavailable actors. 2025-10-23 17:25:59 +02:00
Actions User
a56852f918 [CI] Updating repo.json for 1.5.1.3 2025-10-07 11:02:02 +00:00
Ottermandias
4228fc1b89 fu 2025-10-07 12:59:39 +02:00
Ottermandias
e644b8da28 Fix span issue. 2025-10-07 12:53:55 +02:00
Ottermandias
ace3a8f755 Again. 2025-10-07 12:43:40 +02:00
Ottermandias
76ed347cbf Update signatures. 2025-10-07 12:28:18 +02:00
Ottermandias
c3469a1687 Fix facewear advanced dyes, fix backup service not running in task, update libraries. 2025-09-28 23:55:44 +02:00
Ottermandias
0a9693daea
Update CodeService.cs 2025-09-15 20:29:13 +02:00
Actions User
414bd8bee7 [CI] Updating repo.json for 1.5.1.2 2025-08-28 16:52:43 +00:00
Ottermandias
8e1745d67a Once more with feeling 2025-08-28 18:47:57 +02:00
Actions User
889f01a724 [CI] Updating repo.json for 1.5.1.1 2025-08-26 09:58:08 +00:00
Ottermandias
6e62905fa7 Fix staging incompatibility with CS. 2025-08-26 11:55:00 +02:00
Actions User
654787fa0d [CI] Updating repo.json for 1.5.1.0 2025-08-25 08:45:28 +00:00
Ottermandias
835ba23935 1.5.1.0 2025-08-25 10:43:14 +02:00
Ottermandias
389a8781d6 Update library. 2025-08-25 10:39:38 +02:00
Actions User
3eabe591df [CI] Updating repo.json for testing_1.5.0.9 2025-08-24 13:59:02 +00:00
Ottermandias
487d3b9399 Update PCP Service. 2025-08-24 15:49:29 +02:00
Actions User
4d4e4669dd [CI] Updating repo.json for testing_1.5.0.8 2025-08-22 18:34:49 +00:00
Ottermandias
fb065549e9 Add PCP Service. 2025-08-22 20:32:32 +02:00
Ottermandias
2c34154915 Update API. 2025-08-22 20:32:32 +02:00
Actions User
3704051b0f [CI] Updating repo.json for 1.5.0.7 2025-08-17 08:45:55 +00:00
Ottermandias
b2b8f2b6eb Make glamourers visor toggle trigger static visors. (?!?) 2025-08-17 10:43:26 +02:00
Ottermandias
22e6c0655b Add ear state when toggling meta application via button. 2025-08-16 11:59:08 +02:00
Ottermandias
bb2ba0cf11 Add glasses to advanced dye slot combo. 2025-08-16 11:59:08 +02:00
Ottermandias
e854386b23 Update OtterGui 2025-08-16 11:59:08 +02:00
Actions User
49d24df2e7 [CI] Updating repo.json for 1.5.0.6 2025-08-12 12:53:44 +00:00
Ottermandias
c9b291c2f3 Add new parameter to LoadWeapon hook. 2025-08-12 14:47:09 +02:00
Actions User
65f789880d [CI] Updating repo.json for 1.5.0.5 2025-08-12 10:32:03 +00:00
Ottermandias
abf998a727 Update GameData 2025-08-12 12:29:55 +02:00
Ottermandias
4cc191cb25 Add viera ear visibility to application rules. 2025-08-11 20:53:44 +02:00
Ottermandias
dc431c10a5 Add chat command to toggle automation. 2025-08-11 19:59:27 +02:00
Ottermandias
26862ba78f Update ChangedEquipData. 2025-08-11 19:59:27 +02:00
Actions User
e4374337f2 [CI] Updating repo.json for 1.5.0.4 2025-08-09 18:50:50 +00:00
Ottermandias
240c889fff Fix changed equipment access to use new size. 2025-08-09 20:48:46 +02:00
Ottermandias
612cd31c3e Fix some caravans. 2025-08-09 19:02:32 +02:00
Actions User
304b362002 [CI] Updating repo.json for 1.5.0.3 2025-08-09 16:55:27 +00:00
Ottermandias
8f34f197d0 Another try. 2025-08-09 18:53:22 +02:00
Actions User
1c97266a93 [CI] Updating repo.json for 1.5.0.2 2025-08-09 16:41:32 +00:00
Ottermandias
a9caddafd5 Maybe fix design crashes. 2025-08-09 18:39:14 +02:00
Actions User
34bf95dddb [CI] Updating repo.json for 1.5.0.1 2025-08-09 11:03:48 +00:00
Ottermandias
4761b8f584 Need staging again... 2025-08-09 13:01:48 +02:00
Ottermandias
0d94aae732 Fix popups not working early. 2025-08-09 12:11:42 +02:00
Ottermandias
e83f328cdc Fix resizable child. 2025-08-09 11:58:52 +02:00
Ottermandias
52fd29c478 Woops 2025-08-09 11:52:17 +02:00
Ottermandias
a8b79993df Make QDB ignore close hotkey. 2025-08-09 11:47:11 +02:00
Ottermandias
98574558e5 Set Repo API level to 13 and remove stg from future releases. 2025-08-08 23:07:08 +02:00
Actions User
557cbf23ce [CI] Updating repo.json for 1.5.0.0 2025-08-08 21:06:10 +00:00
Ottermandias
56753ae7ba Use staging for release. 2025-08-08 23:03:58 +02:00
Ottermandias
b66df624f7 Update Gamedata. 2025-08-08 23:01:54 +02:00
Ottermandias
be78f1447b 1.5.0.0 2025-08-08 15:56:08 +02:00
Ottermandias
97e32a3cb7 Update GameData. 2025-08-08 15:48:19 +02:00
Ottermandias
4472920536 Move Viera Ears sig to gamedata. 2025-08-08 15:46:24 +02:00
Ottermandias
ac6a726f57 Remaining API13 updates. 2025-08-08 15:39:05 +02:00
Ottermandias
00d550f4fe Add viera ear flags 2025-08-08 15:38:51 +02:00
Ottermandias
0f98fac157 Add auto-locking to design defaults. 2025-08-08 15:36:47 +02:00
Ottermandias
c25f0f72db Update for ottergui. 2025-08-08 15:36:14 +02:00
Karou
72e05e23bc stupid detached head.... 2025-08-07 21:53:01 -04:00
Karou
2c3bed6ba5 Api 13 grunt work 2025-08-07 21:50:23 -04:00
Ottermandias
d6df9885dc Update GameData. 2025-08-02 00:07:38 +02:00
Ottermandias
e7936500e0
Merge pull request #111 from CordeliaMist/Fix-RevertNotFiringFinalize
Correct StateFinalized not invoking in certain areas
2025-07-28 18:00:26 +02:00
Cordelia Mist
4ef4e65d46 Ensure that reverts called by API invoke their StateFinalizationType upon completing a reset state. 2025-07-28 08:43:13 -07:00
Cordelia Mist
40b4a8fd7a Ensure Revert via Command invokes a StateFinalization type for Reset 2025-07-28 08:37:57 -07:00
Actions User
8a1f03c272 [CI] Updating repo.json for 1.4.0.3 2025-07-14 15:12:49 +00:00
Ottermandias
8ed479eddf Fix issue with invalid bonus items. 2025-07-14 17:09:42 +02:00
Ottermandias
c0a278ca2c Make designs update on mousewheel. 2025-06-15 23:34:00 +02:00
Actions User
2e9a7004c6 [CI] Updating repo.json for 1.4.0.2 2025-06-13 15:21:04 +00:00
Ottermandias
75c76a92b9 Optimize design combos and file system. 2025-06-13 17:17:58 +02:00
Ottermandias
282935c6d6 Allow drag & drop of equipment pieces. 2025-06-03 18:40:41 +02:00
Actions User
d7b189b714 [CI] Updating repo.json for 1.4.0.1 2025-05-29 00:57:24 +00:00
Ottermandias
e3da3f356c Merge branch 'main' of github.com:Ottermandias/Glamourer 2025-05-29 02:55:23 +02:00
Ottermandias
66bed4217f Fix staining template reading. 2025-05-29 02:55:20 +02:00
Ottermandias
56bbf6593a Fix button counting. 2025-05-29 02:55:11 +02:00
Actions User
b8e1e7c384 [CI] Updating repo.json for 1.4.0.0 2025-05-28 11:58:09 +00:00
Ottermandias
a0d2c39f45 1.4.0.0 2025-05-28 13:56:06 +02:00
Ottermandias
07df3186c2 Better. 2025-05-27 14:38:04 +02:00
Ottermandias
5b59e74417 Use CS ReadStainingTemplate again. 2025-05-27 12:06:29 +02:00
Ottermandias
b4485f028d Batch some multi-design changes to skip unnecessary computations. 2025-05-23 15:21:14 +02:00
Ottermandias
74674cfa0c Make combos start from preview selection if possible. 2025-05-23 10:47:47 +02:00
Ottermandias
f192c17c9b Allow drag & drop for color buttons. 2025-05-21 17:46:56 +02:00
Ottermandias
aa1ac29182 Make design selector resizable. 2025-05-21 17:16:55 +02:00
Ottermandias
081ac6bf8b Split ResetAdvanced into two parts. 2025-05-21 17:09:41 +02:00
Ottermandias
e4b32343ae Update libraries. 2025-05-21 17:08:17 +02:00
Ottermandias
c93370ec92 Again. 2025-05-09 00:06:19 +02:00
Ottermandias
9abd7f2767 Make Dynamis IPC working. 2025-05-08 23:44:16 +02:00
Ottermandias
8a9877bb01 Add testing Dynamis IPC for debugging. 2025-05-06 00:31:26 +02:00
Ottermandias
c1e1476fa6 Fix some issues with glamourer not searching mods by name. 2025-05-06 00:30:47 +02:00
Ottermandias
b1abbb8e77 Support model id input in weapon combo, support middle-mouse pipette. 2025-05-06 00:30:27 +02:00
Ottermandias
fcb0660def Implement new IPC methods and API 1.6 2025-05-04 00:36:39 +02:00
Ottermandias
a6073e2a42 Update old PR slightly. 2025-05-03 23:36:03 +02:00
Ottermandias
2c87077918
Merge pull request #107 from Diorik/random_no_repeat
Prevent Random Repeats
2025-05-03 23:29:29 +02:00
Ottermandias
4e0a9f62b9
Merge pull request #109 from Caraxi/api-fix
Fix `SetMetaState` and `SetMetaStateName` not being registered
2025-05-02 17:28:10 +02:00
Actions User
39636f5293 [CI] Updating repo.json for 1.3.8.6 2025-05-01 21:04:23 +00:00
Caraxi
155a9d6266 Fix SetMetaState and SetMetaStateName not being registered 2025-04-15 14:26:30 +09:30
Diorik
5ca151b675 PreventRandom use WeakReference, reroll rand instead of changing list 2025-02-06 13:29:47 -06:00
Diorik
67fd65d366 Make PreventRandomc figurable, clean up logic
Will no longer hold design reference or make redundant copy of list
2025-02-06 12:49:23 -06:00
Diorik
45981f2fee
Merge branch 'Ottermandias:main' into random_no_repeat 2025-02-06 11:26:07 -06:00
Diorik
cf308fc118 Prevent repeating random design
Cache the last selected random design and prevent it from being chosen again.
2025-02-04 01:54:34 -06:00
140 changed files with 1770 additions and 447 deletions

View file

@ -20,7 +20,7 @@ jobs:
run: dotnet restore
- name: Download Dalamud
run: |
Invoke-WebRequest -Uri https://goatcorp.github.io/dalamud-distrib/latest.zip -OutFile latest.zip
Invoke-WebRequest -Uri https://goatcorp.github.io/dalamud-distrib/stg/latest.zip -OutFile latest.zip
Expand-Archive -Force latest.zip "$env:AppData\XIVLauncher\addon\Hooks\dev"
- name: Build
run: |

@ -1 +1 @@
Subproject commit 2158bd4bbcb6cefe3ce48e6d8b32e134cbee9a91
Subproject commit 59a7ab5fa9941eb754757b62e4cb189e455e9514

View file

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

View file

@ -2,19 +2,31 @@
using Glamourer.Api.Enums;
using Glamourer.Designs;
using Glamourer.State;
using Newtonsoft.Json.Linq;
using OtterGui.Services;
namespace Glamourer.Api;
public class DesignsApi(ApiHelpers helpers, DesignManager designs, StateManager stateManager, DesignFileSystem fileSystem, DesignColors color)
public class DesignsApi(
ApiHelpers helpers,
DesignManager designs,
StateManager stateManager,
DesignFileSystem fileSystem,
DesignColors color,
DesignConverter converter)
: IGlamourerApiDesigns, IApiService
{
public Dictionary<Guid, string> GetDesignList()
=> designs.Designs.ToDictionary(d => d.Identifier, d => d.Name.Text);
public Dictionary<Guid, (string DisplayName, string FullPath, uint DisplayColor, bool ShownInQdb)> GetDesignListExtended()
=> designs.Designs.ToDictionary(d => d.Identifier,
d => (d.Name.Text, fileSystem.FindLeaf(d, out var leaf) ? leaf.FullName() : d.Name.Text, color.GetColor(d), d.QuickDesign));
=> fileSystem.ToDictionary(kvp => kvp.Key.Identifier,
kvp => (kvp.Key.Name.Text, kvp.Value.FullName(), color.GetColor(kvp.Key), kvp.Key.QuickDesign));
public (string DisplayName, string FullPath, uint DisplayColor, bool ShowInQdb) GetExtendedDesignData(Guid designId)
=> designs.Designs.ByIdentifier(designId) is { } d
? (d.Name.Text, fileSystem.TryGetValue(d, out var leaf) ? leaf.FullName() : d.Name.Text, color.GetColor(d), d.QuickDesign)
: (string.Empty, string.Empty, 0, false);
public GlamourerApiEc ApplyDesign(Guid designId, int objectIndex, uint key, ApplyFlag flags)
{
@ -71,4 +83,56 @@ public class DesignsApi(ApiHelpers helpers, DesignManager designs, StateManager
return ApiHelpers.Return(GlamourerApiEc.Success, args);
}
public (GlamourerApiEc, Guid) AddDesign(string designInput, string name)
{
var args = ApiHelpers.Args("DesignData", designInput, "Name", name);
if (converter.FromBase64(designInput, true, true, out _) is not { } designBase)
try
{
var jObj = JObject.Parse(designInput);
designBase = converter.FromJObject(jObj, true, true);
if (designBase is null)
return (ApiHelpers.Return(GlamourerApiEc.CouldNotParse, args), Guid.Empty);
}
catch (Exception ex)
{
Glamourer.Log.Error($"Failure parsing data for AddDesign due to\n{ex}");
return (ApiHelpers.Return(GlamourerApiEc.CouldNotParse, args), Guid.Empty);
}
try
{
var design = designBase is Design d
? designs.CreateClone(d, name, true)
: designs.CreateClone(designBase, name, true);
return (ApiHelpers.Return(GlamourerApiEc.Success, args), design.Identifier);
}
catch (Exception ex)
{
Glamourer.Log.Error($"Unknown error creating design via IPC:\n{ex}");
return (ApiHelpers.Return(GlamourerApiEc.UnknownError, args), Guid.Empty);
}
}
public GlamourerApiEc DeleteDesign(Guid designId)
{
var args = ApiHelpers.Args("DesignId", designId);
if (designs.Designs.ByIdentifier(designId) is not { } design)
return ApiHelpers.Return(GlamourerApiEc.NothingDone, args);
designs.Delete(design);
return ApiHelpers.Return(GlamourerApiEc.Success, args);
}
public string? GetDesignBase64(Guid designId)
=> designs.Designs.ByIdentifier(designId) is { } design
? converter.ShareBase64(design)
: null;
public JObject? GetDesignJObject(Guid designId)
=> designs.Designs.ByIdentifier(designId) is { } design
? converter.ShareJObject(design)
: null;
}

View file

@ -6,7 +6,7 @@ namespace Glamourer.Api;
public class GlamourerApi(DesignsApi designs, StateApi state, ItemsApi items) : IGlamourerApi, IApiService
{
public const int CurrentApiVersionMajor = 1;
public const int CurrentApiVersionMinor = 5;
public const int CurrentApiVersionMinor = 7;
public (int Major, int Minor) ApiVersion
=> (CurrentApiVersionMajor, CurrentApiVersionMinor);

View file

@ -25,8 +25,13 @@ public sealed class IpcProviders : IDisposable, IApiService
IpcSubscribers.GetDesignList.Provider(pi, api.Designs),
IpcSubscribers.GetDesignListExtended.Provider(pi, api.Designs),
IpcSubscribers.GetExtendedDesignData.Provider(pi, api.Designs),
IpcSubscribers.ApplyDesign.Provider(pi, api.Designs),
IpcSubscribers.ApplyDesignName.Provider(pi, api.Designs),
IpcSubscribers.AddDesign.Provider(pi, api.Designs),
IpcSubscribers.DeleteDesign.Provider(pi, api.Designs),
IpcSubscribers.GetDesignBase64.Provider(pi, api.Designs),
IpcSubscribers.GetDesignJObject.Provider(pi, api.Designs),
IpcSubscribers.SetItem.Provider(pi, api.Items),
IpcSubscribers.SetItemName.Provider(pi, api.Items),
@ -37,6 +42,8 @@ public sealed class IpcProviders : IDisposable, IApiService
(a, b, c, d, e, f) => (int)api.Items.SetItemName(a, (ApiEquipSlot)b, c, [d], e, (ApplyFlag)f)),
IpcSubscribers.SetBonusItem.Provider(pi, api.Items),
IpcSubscribers.SetBonusItemName.Provider(pi, api.Items),
IpcSubscribers.SetMetaState.Provider(pi, api.Items),
IpcSubscribers.SetMetaStateName.Provider(pi, api.Items),
IpcSubscribers.GetState.Provider(pi, api.State),
IpcSubscribers.GetStateName.Provider(pi, api.State),
IpcSubscribers.GetStateBase64.Provider(pi, api.State),
@ -47,6 +54,7 @@ public sealed class IpcProviders : IDisposable, IApiService
IpcSubscribers.RevertStateName.Provider(pi, api.State),
IpcSubscribers.UnlockState.Provider(pi, api.State),
IpcSubscribers.UnlockStateName.Provider(pi, api.State),
IpcSubscribers.DeletePlayerState.Provider(pi, api.State),
IpcSubscribers.UnlockAll.Provider(pi, api.State),
IpcSubscribers.RevertToAutomation.Provider(pi, api.State),
IpcSubscribers.RevertToAutomationName.Provider(pi, api.State),

View file

@ -8,6 +8,7 @@ using Glamourer.State;
using Newtonsoft.Json.Linq;
using OtterGui.Services;
using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs;
using StateChanged = Glamourer.Events.StateChanged;
namespace Glamourer.Api;
@ -17,7 +18,6 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
private readonly ApiHelpers _helpers;
private readonly StateManager _stateManager;
private readonly DesignConverter _converter;
private readonly Configuration _config;
private readonly AutoDesignApplier _autoDesigns;
private readonly ActorObjectManager _objects;
private readonly StateChanged _stateChanged;
@ -27,7 +27,6 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
public StateApi(ApiHelpers helpers,
StateManager stateManager,
DesignConverter converter,
Configuration config,
AutoDesignApplier autoDesigns,
ActorObjectManager objects,
StateChanged stateChanged,
@ -37,7 +36,6 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
_helpers = helpers;
_stateManager = stateManager;
_converter = converter;
_config = config;
_autoDesigns = autoDesigns;
_objects = objects;
_stateChanged = stateChanged;
@ -202,6 +200,27 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
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)
=> _stateManager.Values.Count(state => state.Unlock(key));
@ -272,7 +291,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
{
case ApplyFlag.Equipment: _stateManager.ResetEquip(state, source, key); break;
case ApplyFlag.Customization: _stateManager.ResetCustomize(state, source, key); break;
case ApplyFlag.Equipment | ApplyFlag.Customization: _stateManager.ResetState(state, source, key); break;
case ApplyFlag.Equipment | ApplyFlag.Customization: _stateManager.ResetState(state, source, key, true); break;
}
ApiHelpers.Lock(state, key, flags);

View file

@ -38,7 +38,7 @@ public static class ApplicationTypeExtensions
var customizeFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeFlagExtensions.All : 0;
var parameterFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeParameterExtensions.All : 0;
var crestFlags = type.HasFlag(ApplicationType.GearCustomization) ? CrestExtensions.AllRelevant : 0;
var metaFlags = (type.HasFlag(ApplicationType.Armor) ? MetaFlag.HatState | MetaFlag.VisorState : 0)
var metaFlags = (type.HasFlag(ApplicationType.Armor) ? MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.EarState : 0)
| (type.HasFlag(ApplicationType.Weapons) ? MetaFlag.WeaponState : 0)
| (type.HasFlag(ApplicationType.Customizations) ? MetaFlag.Wetness : 0);
var bonusFlags = type.HasFlag(ApplicationType.Armor) ? BonusExtensions.All : 0;

View file

@ -32,6 +32,7 @@ public class DefaultDesignSettings
public bool ResetAdvancedDyes = false;
public bool ShowQuickDesignBar = true;
public bool ResetTemporarySettings = false;
public bool Locked = false;
}
public class Configuration : IPluginConfiguration, ISavable
@ -39,33 +40,37 @@ public class Configuration : IPluginConfiguration, ISavable
[JsonIgnore]
public readonly EphemeralConfig Ephemeral;
public bool UseRestrictedGearProtection { get; set; } = false;
public bool OpenFoldersByDefault { get; set; } = false;
public bool AutoRedrawEquipOnChanges { get; set; } = false;
public bool EnableAutoDesigns { get; set; } = true;
public bool HideApplyCheckmarks { get; set; } = false;
public bool SmallEquip { get; set; } = false;
public bool UnlockedItemMode { get; set; } = false;
public byte DisableFestivals { get; set; } = 1;
public bool EnableGameContextMenu { get; set; } = true;
public bool HideWindowInCutscene { get; set; } = false;
public bool ShowAutomationSetEditing { get; set; } = true;
public bool ShowAllAutomatedApplicationRules { get; set; } = true;
public bool ShowUnlockedItemWarnings { get; set; } = true;
public bool RevertManualChangesOnZoneChange { get; set; } = false;
public bool ShowQuickBarInTabs { get; set; } = true;
public bool OpenWindowAtStart { get; set; } = false;
public bool ShowWindowWhenUiHidden { get; set; } = false;
public bool KeepAdvancedDyesAttached { get; set; } = true;
public bool ShowPalettePlusImport { get; set; } = true;
public bool UseFloatForColors { get; set; } = true;
public bool UseRgbForColors { get; set; } = true;
public bool ShowColorConfig { get; set; } = true;
public bool ChangeEntireItem { get; set; } = false;
public bool AlwaysApplyAssociatedMods { get; set; } = false;
public bool UseTemporarySettings { get; set; } = true;
public bool AllowDoubleClickToApply { get; set; } = false;
public bool RespectManualOnAutomationUpdate { get; set; } = false;
public bool AttachToPcp { get; set; } = true;
public bool UseRestrictedGearProtection { get; set; } = false;
public bool OpenFoldersByDefault { get; set; } = false;
public bool AutoRedrawEquipOnChanges { get; set; } = false;
public bool EnableAutoDesigns { get; set; } = true;
public bool HideApplyCheckmarks { get; set; } = false;
public bool SmallEquip { get; set; } = false;
public bool UnlockedItemMode { get; set; } = false;
public byte DisableFestivals { get; set; } = 1;
public bool EnableGameContextMenu { get; set; } = true;
public bool HideWindowInCutscene { get; set; } = false;
public bool ShowAutomationSetEditing { get; set; } = true;
public bool ShowAllAutomatedApplicationRules { get; set; } = true;
public bool ShowUnlockedItemWarnings { get; set; } = true;
public bool RevertManualChangesOnZoneChange { get; set; } = false;
public bool ShowQuickBarInTabs { get; set; } = true;
public bool OpenWindowAtStart { get; set; } = false;
public bool ShowWindowWhenUiHidden { get; set; } = false;
public bool KeepAdvancedDyesAttached { get; set; } = true;
public bool ShowPalettePlusImport { get; set; } = true;
public bool UseFloatForColors { get; set; } = true;
public bool UseRgbForColors { get; set; } = true;
public bool ShowColorConfig { get; set; } = true;
public bool ChangeEntireItem { get; set; } = false;
public bool AlwaysApplyAssociatedMods { get; set; } = true;
public bool UseTemporarySettings { get; set; } = true;
public bool AllowDoubleClickToApply { get; set; } = false;
public bool RespectManualOnAutomationUpdate { get; set; } = false;
public bool PreventRandomRepeats { get; set; } = false;
public string PcpFolder { get; set; } = "PCP";
public string PcpColor { get; set; } = "";
public DesignPanelFlag HideDesignPanel { get; set; } = 0;
public DesignPanelFlag AutoExpandDesignPanel { get; set; } = 0;
@ -80,7 +85,7 @@ public class Configuration : IPluginConfiguration, ISavable
public ChangeLogDisplayType ChangeLogDisplayType { get; set; } = ChangeLogDisplayType.New;
public QdbButtons QdbButtons { get; set; } =
QdbButtons.ApplyDesign | QdbButtons.RevertAll | QdbButtons.RevertAutomation | QdbButtons.RevertAdvanced;
QdbButtons.ApplyDesign | QdbButtons.RevertAll | QdbButtons.RevertAutomation | QdbButtons.RevertAdvancedDyes;
[JsonConverter(typeof(SortModeConverter))]
[JsonProperty(Order = int.MaxValue)]
@ -157,7 +162,7 @@ public class Configuration : IPluginConfiguration, ISavable
public static class Constants
{
public const int CurrentVersion = 7;
public const int CurrentVersion = 8;
public static readonly ISortMode<Design>[] ValidSortModes =
[

View file

@ -1,5 +1,5 @@
using Glamourer.Designs;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui.Text;
using OtterGui.Text.EndObjects;

View file

@ -1,6 +1,6 @@
using Glamourer.Api.Enums;
using Glamourer.GameData;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using Penumbra.GameData.Enums;
namespace Glamourer.Designs;
@ -19,13 +19,13 @@ public record struct ApplicationCollection(
public static readonly ApplicationCollection None = new(0, 0, CustomizeFlag.BodyType, 0, 0, 0);
public static readonly ApplicationCollection Equipment = new(EquipFlagExtensions.All, BonusExtensions.All,
CustomizeFlag.BodyType, CrestExtensions.AllRelevant, 0, MetaFlag.HatState | MetaFlag.WeaponState | MetaFlag.VisorState);
CustomizeFlag.BodyType, CrestExtensions.AllRelevant, 0, MetaFlag.HatState | MetaFlag.WeaponState | MetaFlag.VisorState | MetaFlag.EarState);
public static readonly ApplicationCollection Customizations = new(0, 0, CustomizeFlagExtensions.AllRelevant, 0,
CustomizeParameterExtensions.All, MetaFlag.Wetness);
public static readonly ApplicationCollection Default = new(EquipFlagExtensions.All, BonusExtensions.All,
CustomizeFlagExtensions.AllRelevant, CrestExtensions.AllRelevant, 0, MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState);
CustomizeFlagExtensions.AllRelevant, CrestExtensions.AllRelevant, 0, MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState | MetaFlag.EarState);
public static ApplicationCollection FromKeys()
=> (ImGui.GetIO().KeyCtrl, ImGui.GetIO().KeyShift) switch
@ -47,7 +47,7 @@ public record struct ApplicationCollection(
Equip = 0;
BonusItem = 0;
Crest = 0;
Meta &= ~(MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState);
Meta &= ~(MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState | MetaFlag.EarState);
}
public void RemoveCustomize()

View file

@ -1,7 +1,7 @@
using Glamourer.Api.Enums;
using Glamourer.GameData;
using Glamourer.State;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using Penumbra.GameData.Enums;
namespace Glamourer.Designs;

View file

@ -100,7 +100,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
public new JObject JsonSerialize()
{
var ret = new JObject()
var ret = new JObject
{
["FileVersion"] = FileVersion,
["Identifier"] = Identifier,
@ -131,12 +131,17 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
var ret = new JArray();
foreach (var (mod, settings) in AssociatedMods)
{
var obj = new JObject()
var obj = new JObject
{
["Name"] = mod.Name,
["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)
{
obj["Priority"] = settings.Priority;

View file

@ -40,7 +40,8 @@ public class DesignBase
}
/// <summary> Used when importing .cma or .chara files. </summary>
internal DesignBase(CustomizeService customize, in DesignData designData, EquipFlag equipFlags, CustomizeFlag customizeFlags, BonusItemFlag bonusFlags)
internal DesignBase(CustomizeService customize, in DesignData designData, EquipFlag equipFlags, CustomizeFlag customizeFlags,
BonusItemFlag bonusFlags)
{
_designData = designData;
ApplyCustomize = customizeFlags & CustomizeFlagExtensions.AllRelevant;
@ -254,9 +255,10 @@ public class DesignBase
ret[slot.ToString()] = Serialize(item.Id, stains, crest, DoApplyEquip(slot), DoApplyStain(slot), DoApplyCrest(crestSlot));
}
ret["Hat"] = new QuadBool(_designData.IsHatVisible(), DoApplyMeta(MetaIndex.HatState)).ToJObject("Show", "Apply");
ret["Visor"] = new QuadBool(_designData.IsVisorToggled(), DoApplyMeta(MetaIndex.VisorState)).ToJObject("IsToggled", "Apply");
ret["Weapon"] = new QuadBool(_designData.IsWeaponVisible(), DoApplyMeta(MetaIndex.WeaponState)).ToJObject("Show", "Apply");
ret["Hat"] = new QuadBool(_designData.IsHatVisible(), DoApplyMeta(MetaIndex.HatState)).ToJObject("Show", "Apply");
ret["VieraEars"] = new QuadBool(_designData.AreEarsVisible(), DoApplyMeta(MetaIndex.EarState)).ToJObject("Show", "Apply");
ret["Visor"] = new QuadBool(_designData.IsVisorToggled(), DoApplyMeta(MetaIndex.VisorState)).ToJObject("IsToggled", "Apply");
ret["Weapon"] = new QuadBool(_designData.IsWeaponVisible(), DoApplyMeta(MetaIndex.WeaponState)).ToJObject("Show", "Apply");
}
else
{
@ -603,6 +605,10 @@ public class DesignBase
metaValue = QuadBool.FromJObject(equip["Visor"], "IsToggled", "Apply", QuadBool.NullFalse);
design.SetApplyMeta(MetaIndex.VisorState, metaValue.Enabled);
design._designData.SetVisor(metaValue.ForcedValue);
metaValue = QuadBool.FromJObject(equip["VieraEars"], "Show", "Apply", QuadBool.NullTrue);
design.SetApplyMeta(MetaIndex.EarState, metaValue.Enabled);
design._designData.SetEarsVisible(metaValue.ForcedValue);
return;
void PrintWarning(string msg)

View file

@ -3,7 +3,7 @@ using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.Utility.Raii;
using Glamourer.Gui;
using Glamourer.Services;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OtterGui;

View file

@ -287,6 +287,7 @@ public unsafe struct DesignData
MetaIndex.HatState => IsHatVisible(),
MetaIndex.VisorState => IsVisorToggled(),
MetaIndex.WeaponState => IsWeaponVisible(),
MetaIndex.EarState => AreEarsVisible(),
_ => false,
};
@ -297,6 +298,7 @@ public unsafe struct DesignData
MetaIndex.HatState => SetHatVisible(value),
MetaIndex.VisorState => SetVisor(value),
MetaIndex.WeaponState => SetWeaponVisible(value),
MetaIndex.EarState => SetEarsVisible(value),
_ => false,
};
@ -340,6 +342,9 @@ public unsafe struct DesignData
public readonly bool IsWeaponVisible()
=> (_states & 0x08) == 0x08;
public readonly bool AreEarsVisible()
=> (_states & 0x10) == 0x00;
public bool SetWeaponVisible(bool value)
{
if (value == IsWeaponVisible())
@ -349,6 +354,15 @@ public unsafe struct DesignData
return true;
}
public bool SetEarsVisible(bool value)
{
if (value == AreEarsVisible())
return false;
_states = (byte)(value ? _states & ~0x10 : _states | 0x10);
return true;
}
public void SetDefaultEquipment(ItemManager items)
{
foreach (var slot in EquipSlotExtensions.EqdpSlots)
@ -386,6 +400,7 @@ public unsafe struct DesignData
SetHatVisible(true);
SetWeaponVisible(true);
SetEarsVisible(true);
SetVisor(false);
fixed (uint* ptr = _itemIds)
{

View file

@ -41,11 +41,11 @@ public sealed class DesignFileSystem : FileSystem<Design>, IDisposable, ISavable
public struct CreationDate : ISortMode<Design>
{
public string Name
=> "Creation Date (Older First)";
public ReadOnlySpan<byte> Name
=> "Creation Date (Older First)"u8;
public string Description
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their creation date.";
public ReadOnlySpan<byte> Description
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their creation date."u8;
public IEnumerable<IPath> GetChildren(Folder f)
=> f.GetSubFolders().Cast<IPath>().Concat(f.GetLeaves().OrderBy(l => l.Value.CreationDate));
@ -53,11 +53,11 @@ public sealed class DesignFileSystem : FileSystem<Design>, IDisposable, ISavable
public struct UpdateDate : ISortMode<Design>
{
public string Name
=> "Update Date (Older First)";
public ReadOnlySpan<byte> Name
=> "Update Date (Older First)"u8;
public string Description
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their last update date.";
public ReadOnlySpan<byte> Description
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their last update date."u8;
public IEnumerable<IPath> GetChildren(Folder f)
=> f.GetSubFolders().Cast<IPath>().Concat(f.GetLeaves().OrderBy(l => l.Value.LastEdit));
@ -65,11 +65,11 @@ public sealed class DesignFileSystem : FileSystem<Design>, IDisposable, ISavable
public struct InverseCreationDate : ISortMode<Design>
{
public string Name
=> "Creation Date (Newer First)";
public ReadOnlySpan<byte> Name
=> "Creation Date (Newer First)"u8;
public string Description
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse creation date.";
public ReadOnlySpan<byte> Description
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse creation date."u8;
public IEnumerable<IPath> GetChildren(Folder f)
=> f.GetSubFolders().Cast<IPath>().Concat(f.GetLeaves().OrderByDescending(l => l.Value.CreationDate));
@ -77,11 +77,11 @@ public sealed class DesignFileSystem : FileSystem<Design>, IDisposable, ISavable
public struct InverseUpdateDate : ISortMode<Design>
{
public string Name
=> "Update Date (Newer First)";
public ReadOnlySpan<byte> Name
=> "Update Date (Newer First)"u8;
public string Description
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse last update date.";
public ReadOnlySpan<byte> Description
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse last update date."u8;
public IEnumerable<IPath> GetChildren(Folder f)
=> f.GetSubFolders().Cast<IPath>().Concat(f.GetLeaves().OrderByDescending(l => l.Value.LastEdit));
@ -114,14 +114,14 @@ public sealed class DesignFileSystem : FileSystem<Design>, IDisposable, ISavable
return;
case DesignChanged.Type.Deleted:
if (FindLeaf(design, out var leaf1))
if (TryGetValue(design, out var leaf1))
Delete(leaf1);
return;
case DesignChanged.Type.ReloadedAll:
Reload();
return;
case DesignChanged.Type.Renamed when (data as RenameTransaction?)?.Old is { } oldName:
if (!FindLeaf(design, out var leaf2))
if (!TryGetValue(design, out var leaf2))
return;
var old = oldName.FixName();
@ -150,15 +150,6 @@ public sealed class DesignFileSystem : FileSystem<Design>, IDisposable, ISavable
? (string.Empty, false)
: (DesignToIdentifier(design), true);
// Search the entire filesystem for the leaf corresponding to a design.
public bool FindLeaf(Design design, [NotNullWhen(true)] out Leaf? leaf)
{
leaf = Root.GetAllDescendants(ISortMode<Design>.Lexicographical)
.OfType<Leaf>()
.FirstOrDefault(l => l.Value == design);
return leaf != null;
}
internal static void MigrateOldPaths(SaveService saveService, Dictionary<string, string> oldPaths)
{
if (oldPaths.Count == 0)

View file

@ -6,12 +6,13 @@ using Glamourer.GameData;
using Glamourer.Interop.Material;
using Glamourer.Interop.Penumbra;
using Glamourer.Services;
using OtterGui.Extensions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OtterGui.Extensions;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
namespace Glamourer.Designs;
public sealed class DesignManager : DesignEditor
@ -110,6 +111,7 @@ public sealed class DesignManager : DesignEditor
QuickDesign = Config.DefaultDesignSettings.ShowQuickDesignBar,
ResetTemporarySettings = Config.DefaultDesignSettings.ResetTemporarySettings,
};
design.SetWriteProtected(Config.DefaultDesignSettings.Locked);
Designs.Add(design);
Glamourer.Log.Debug($"Added new design {design.Identifier}.");
SaveService.ImmediateSave(design);
@ -134,6 +136,7 @@ public sealed class DesignManager : DesignEditor
ResetTemporarySettings = Config.DefaultDesignSettings.ResetTemporarySettings,
};
design.SetWriteProtected(Config.DefaultDesignSettings.Locked);
Designs.Add(design);
Glamourer.Log.Debug($"Added new design {design.Identifier} by cloning Temporary Design.");
SaveService.ImmediateSave(design);
@ -153,6 +156,7 @@ public sealed class DesignManager : DesignEditor
Name = actualName,
Index = Designs.Count,
};
design.SetWriteProtected(Config.DefaultDesignSettings.Locked);
Designs.Add(design);
Glamourer.Log.Debug(
$"Added new design {design.Identifier} by cloning {clone.Identifier.ToString()}.");
@ -225,7 +229,7 @@ public sealed class DesignManager : DesignEditor
design.Tags = design.Tags.Append(tag).OrderBy(t => t).ToArray();
design.LastEdit = DateTimeOffset.UtcNow;
var idx = design.Tags.IndexOf(tag);
var idx = design.Tags.AsEnumerable().IndexOf(tag);
SaveService.QueueSave(design);
Glamourer.Log.Debug($"Added tag {tag} at {idx} to design {design.Identifier}.");
DesignChanged.Invoke(DesignChanged.Type.AddedTag, design, new TagAddedTransaction(tag, idx));
@ -258,7 +262,7 @@ public sealed class DesignManager : DesignEditor
SaveService.QueueSave(design);
Glamourer.Log.Debug($"Renamed tag {oldTag} at {tagIdx} to {newTag} in design {design.Identifier} and reordered tags.");
DesignChanged.Invoke(DesignChanged.Type.ChangedTag, design,
new TagChangedTransaction(oldTag, newTag, tagIdx, design.Tags.IndexOf(newTag)));
new TagChangedTransaction(oldTag, newTag, tagIdx, design.Tags.AsEnumerable().IndexOf(newTag)));
}
/// <summary> Add an associated mod to a design. </summary>
@ -553,7 +557,7 @@ public sealed class DesignManager : DesignEditor
try
{
File.Move(SaveService.FileNames.MigrationDesignFile,
Path.ChangeExtension(SaveService.FileNames.MigrationDesignFile, ".json.bak"));
Path.ChangeExtension(SaveService.FileNames.MigrationDesignFile, ".json.bak"), true);
Glamourer.Log.Information($"Moved migrated design file {SaveService.FileNames.MigrationDesignFile} to backup file.");
}
catch (Exception ex)

View file

@ -152,7 +152,7 @@ public class EditorHistory : IDisposable, IService
{
if (!_stateEntries.TryGetValue(state, out var list))
{
list = new Queue();
list = [];
_stateEntries.Add(state, list);
}
@ -163,7 +163,7 @@ public class EditorHistory : IDisposable, IService
{
if (!_designEntries.TryGetValue(design, out var list))
{
list = new Queue();
list = [];
_designEntries.Add(design, list);
}

View file

@ -10,14 +10,15 @@ public enum MetaIndex
VisorState = StateIndex.MetaVisorState,
WeaponState = StateIndex.MetaWeaponState,
ModelId = StateIndex.MetaModelId,
EarState = StateIndex.MetaEarState,
}
public static class MetaExtensions
{
public static readonly IReadOnlyList<MetaIndex> AllRelevant =
[MetaIndex.Wetness, MetaIndex.HatState, MetaIndex.VisorState, MetaIndex.WeaponState];
[MetaIndex.Wetness, MetaIndex.HatState, MetaIndex.VisorState, MetaIndex.WeaponState, MetaIndex.EarState];
public const MetaFlag All = MetaFlag.Wetness | MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState;
public const MetaFlag All = MetaFlag.Wetness | MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState | MetaFlag.EarState;
public static MetaFlag ToFlag(this MetaIndex index)
=> index switch
@ -26,6 +27,7 @@ public static class MetaExtensions
MetaIndex.HatState => MetaFlag.HatState,
MetaIndex.VisorState => MetaFlag.VisorState,
MetaIndex.WeaponState => MetaFlag.WeaponState,
MetaIndex.EarState => MetaFlag.EarState,
_ => (MetaFlag)byte.MaxValue,
};
@ -36,7 +38,8 @@ public static class MetaExtensions
MetaFlag.HatState => MetaIndex.HatState,
MetaFlag.VisorState => MetaIndex.VisorState,
MetaFlag.WeaponState => MetaIndex.WeaponState,
_ => (MetaIndex)byte.MaxValue,
MetaFlag.EarState => MetaIndex.EarState,
_ => (MetaIndex)byte.MaxValue,
};
public static IEnumerable<MetaIndex> ToIndices(this MetaFlag index)
@ -49,6 +52,8 @@ public static class MetaExtensions
yield return MetaIndex.VisorState;
if (index.HasFlag(MetaFlag.WeaponState))
yield return MetaIndex.WeaponState;
if (index.HasFlag(MetaFlag.EarState))
yield return MetaIndex.EarState;
}
public static string ToName(this MetaIndex index)
@ -58,6 +63,7 @@ public static class MetaExtensions
MetaIndex.VisorState => "Visor Toggled",
MetaIndex.WeaponState => "Weapon Visible",
MetaIndex.Wetness => "Force Wetness",
MetaIndex.EarState => "Ears Visible",
_ => "Unknown Meta",
};
@ -68,6 +74,7 @@ public static class MetaExtensions
MetaIndex.VisorState => "Toggle the visor state of the characters head gear.",
MetaIndex.WeaponState => "Hide or show the characters weapons when not drawn.",
MetaIndex.Wetness => "Force the character to be wet or not.",
MetaIndex.EarState => "Hide or show the characters ears through the head gear. (Viera only)",
_ => string.Empty,
};
}

View file

@ -1,19 +1,33 @@
using OtterGui.Services;
using OtterGui;
using OtterGui.Services;
namespace Glamourer.Designs.Special;
public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileSystem) : IService
public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileSystem, Configuration config) : IService
{
private readonly Random _rng = new();
private readonly Random _rng = new();
private readonly WeakReference<Design> _lastDesign = new(null!, false);
public Design? Design(IReadOnlyList<Design> localDesigns)
{
if (localDesigns.Count == 0)
if (localDesigns.Count is 0)
return null;
var idx = _rng.Next(0, localDesigns.Count);
Glamourer.Log.Verbose($"[Random Design] Chose design {idx + 1} out of {localDesigns.Count}: {localDesigns[idx].Incognito}.");
return localDesigns[idx];
if (localDesigns.Count is 1)
{
_lastDesign.SetTarget(localDesigns[idx]);
return localDesigns[idx];
}
if (config.PreventRandomRepeats && _lastDesign.TryGetTarget(out var lastDesign))
while (lastDesign == localDesigns[idx])
idx = _rng.Next(0, localDesigns.Count);
var design = localDesigns[idx];
Glamourer.Log.Verbose($"[Random Design] Chose design {idx + 1} out of {localDesigns.Count}: {design.Incognito}.");
_lastDesign.SetTarget(design);
return design;
}
public Design? Design()
@ -24,12 +38,12 @@ public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileS
public Design? Design(IReadOnlyList<IDesignPredicate> predicates)
{
if (predicates.Count == 0)
return Design();
if (predicates.Count == 1)
return Design(predicates[0]);
return Design(IDesignPredicate.Get(predicates, designs, fileSystem).ToList());
return predicates.Count switch
{
0 => Design(),
1 => Design(predicates[0]),
_ => Design(IDesignPredicate.Get(predicates, designs, fileSystem).ToList()),
};
}
public Design? Design(string restrictions)

View file

@ -22,7 +22,7 @@ public interface IDesignPredicate
: designs;
private static (Design Design, string LowerName, string Identifier, string LowerPath) Transform(Design d, DesignFileSystem fs)
=> (d, d.Name.Lower, d.Identifier.ToString(), fs.FindLeaf(d, out var l) ? l.FullName().ToLowerInvariant() : string.Empty);
=> (d, d.Name.Lower, d.Identifier.ToString(), fs.TryGetValue(d, out var l) ? l.FullName().ToLowerInvariant() : string.Empty);
}
public static class RandomPredicate

View file

@ -20,6 +20,10 @@ public class EphemeralConfig : ISavable
public Guid SelectedQuickDesign { get; set; } = Guid.Empty;
public int LastSeenVersion { get; set; } = GlamourerChangelog.LastChangelogVersion;
public float CurrentDesignSelectorWidth { get; set; } = 200f;
public float DesignSelectorMinimumScale { get; set; } = 0.1f;
public float DesignSelectorMaximumScale { get; set; } = 0.5f;
[JsonIgnore]
private readonly SaveService _saveService;

View file

@ -15,5 +15,8 @@ public sealed class PenumbraReloaded()
/// <seealso cref="Interop.VisorService.Restore"/>
VisorService = 0,
/// <seealso cref="Interop.VieraEarService.Restore"/>
VieraEarService = 0,
}
}

View file

@ -0,0 +1,22 @@
using OtterGui.Classes;
using Penumbra.GameData.Interop;
namespace Glamourer.Events;
/// <summary>
/// Triggered when the state of viera ear visibility for any draw object is changed.
/// <list type="number">
/// <item>Parameter is the model with a changed viera ear visibility state. </item>
/// <item>Parameter is the new state. </item>
/// <item>Parameter is whether to call the original function. </item>
/// </list>
/// </summary>
public sealed class VieraEarStateChanged()
: EventWrapperRef2<Actor, bool, VieraEarStateChanged.Priority>(nameof(VieraEarStateChanged))
{
public enum Priority
{
/// <seealso cref="State.StateListener.OnVieraEarChange"/>
StateListener = 0,
}
}

View file

@ -19,4 +19,4 @@ public sealed class VisorStateChanged()
/// <seealso cref="State.StateListener.OnVisorChange"/>
StateListener = 0,
}
}
}

View file

@ -26,6 +26,7 @@ public class Glamourer : IDalamudPlugin
public static readonly Logger Log = new();
public static MessageService Messager { get; private set; } = null!;
public static DynamisIpc Dynamis { get; private set; } = null!;
private readonly ServiceManager _services;
@ -35,6 +36,7 @@ public class Glamourer : IDalamudPlugin
{
_services = StaticServiceManager.CreateProvider(pluginInterface, Log, this);
Messager = _services.GetService<MessageService>();
Dynamis = _services.GetService<DynamisIpc>();
_services.EnsureRequiredServices();
_services.GetService<VisorService>();
@ -69,6 +71,7 @@ public class Glamourer : IDalamudPlugin
sb.Append($"> **`Festival Easter-Eggs: `** {config.DisableFestivals}\n");
sb.Append($"> **`Apply Entire Weapon: `** {config.ChangeEntireItem}\n");
sb.Append($"> **`Apply Associated Mods:`** {config.AlwaysApplyAssociatedMods}\n");
sb.Append($"> **`Attach to PCP: `** {config.AttachToPcp}\n");
sb.Append($"> **`Hidden Panels: `** {config.HideDesignPanel}\n");
sb.Append($"> **`Show QDB: `** {config.Ephemeral.ShowDesignQuickBar}\n");
sb.Append($"> **`QDB Hotkey: `** {config.ToggleQuickDesignBar}\n");

View file

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

View file

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

View file

@ -1,4 +1,4 @@
using ImGuiNET;
using Dalamud.Bindings.ImGui;
namespace Glamourer.Gui;

View file

@ -1,16 +1,83 @@
using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Glamourer.GameData;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Text.EndObjects;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using System;
namespace Glamourer.Gui.Customization;
public partial class CustomizationDrawer
{
private const string ColorPickerPopupName = "ColorPicker";
private const string ColorPickerPopupName = "ColorPicker";
private CustomizeValue _draggedColorValue;
private CustomizeIndex _draggedColorType;
private void DrawDragDropSource(CustomizeIndex index, CustomizeData custom)
{
using var dragDropSource = ImUtf8.DragDropSource();
if (!dragDropSource)
return;
if (!DragDropSource.SetPayload("##colorDragDrop"u8))
_draggedColorValue = _customize[index];
ImUtf8.Text(
$"Dragging {(custom.Color == 0 ? $"{_currentOption} (NPC)" : _currentOption)} #{_draggedColorValue.Value}...");
_draggedColorType = index;
}
private void DrawDragDropTarget(CustomizeIndex index)
{
using var dragDropTarget = ImUtf8.DragDropTarget();
if (!dragDropTarget.Success || !dragDropTarget.IsDropping("##colorDragDrop"u8))
return;
var idx = _set.DataByValue(_draggedColorType, _draggedColorValue, out var draggedData, _customize.Face);
var bestMatch = _draggedColorValue;
if (draggedData.HasValue)
{
var draggedColor = draggedData.Value.Color;
var targetData = _set.Data(index, idx);
if (targetData.Color != draggedColor)
{
var bestDiff = Diff(targetData.Color, draggedColor);
var count = _set.Count(index);
for (var i = 0; i < count; ++i)
{
targetData = _set.Data(index, i);
if (targetData.Color == draggedColor)
{
UpdateValue(_draggedColorValue);
return;
}
var diff = Diff(targetData.Color, draggedColor);
if (diff >= bestDiff)
continue;
bestDiff = diff;
bestMatch = (CustomizeValue)i;
}
}
}
UpdateValue(bestMatch);
return;
static uint Diff(uint color1, uint color2)
{
var r = (color1 & 0xFF) - (color2 & 0xFF);
var g = ((color1 >> 8) & 0xFF) - ((color2 >> 8) & 0xFF);
var b = ((color1 >> 16) & 0xFF) - ((color2 >> 16) & 0xFF);
return 30 * r * r + 59 * g * g + 11 * b * b;
}
}
private void DrawColorPicker(CustomizeIndex index)
{
@ -21,7 +88,7 @@ public partial class CustomizationDrawer
using (_ = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 2 * ImGuiHelpers.GlobalScale, current < 0))
{
if (ImGui.ColorButton($"{_customize[index].Value}##color", color, ImGuiColorEditFlags.None, _framedIconSize))
if (ImGui.ColorButton($"{_customize[index].Value}##color", color, ImGuiColorEditFlags.NoDragDrop, _framedIconSize))
{
ImGui.OpenPopup(ColorPickerPopupName);
}
@ -30,6 +97,9 @@ public partial class CustomizationDrawer
var data = _set.Data(_currentIndex, current, _customize.Face);
UpdateValue(data.Value);
}
DrawDragDropSource(index, custom);
DrawDragDropTarget(index);
}
var npc = false;

View file

@ -1,5 +1,5 @@
using Dalamud.Interface;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Enums;

View file

@ -1,7 +1,7 @@
using Dalamud.Interface.Textures.TextureWraps;
using Glamourer.GameData;
using Glamourer.Unlocks;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Extensions;
using OtterGui.Raii;
@ -35,7 +35,7 @@ public partial class CustomizationDrawer
var hasIcon = icon.TryGetWrap(out var wrap, out _);
using (_ = ImRaii.Disabled(_locked || _currentIndex is CustomizeIndex.Face && _lockedRedraw))
{
if (ImGui.ImageButton(wrap?.ImGuiHandle ?? icon.GetWrapOrEmpty().ImGuiHandle, _iconSize))
if (ImGui.ImageButton(wrap?.Handle ?? icon.GetWrapOrEmpty().Handle, _iconSize))
{
ImGui.OpenPopup(IconSelectorPopup);
}
@ -89,7 +89,7 @@ public partial class CustomizationDrawer
: ImRaii.PushColor(ImGuiCol.Button, ColorId.FavoriteStarOn.Value(), isFavorite);
var hasIcon = icon.TryGetWrap(out var wrap, out var _);
if (ImGui.ImageButton(wrap?.ImGuiHandle ?? icon.GetWrapOrEmpty().ImGuiHandle, _iconSize))
if (ImGui.ImageButton(wrap?.Handle ?? icon.GetWrapOrEmpty().Handle, _iconSize))
{
UpdateValue(custom.Value);
ImGui.CloseCurrentPopup();
@ -215,7 +215,7 @@ public partial class CustomizationDrawer
hasIcon = icon.TryGetWrap(out wrap, out _);
}
if (ImGui.ImageButton(wrap?.ImGuiHandle ?? icon.GetWrapOrEmpty().ImGuiHandle, _iconSize, Vector2.Zero, Vector2.One,
if (ImGui.ImageButton(wrap?.Handle ?? icon.GetWrapOrEmpty().Handle, _iconSize, Vector2.Zero, Vector2.One,
(int)ImGui.GetStyle().FramePadding.X, Vector4.Zero, enabled ? Vector4.One : _redTint))
{
_customize.Set(featureIdx, enabled ? CustomizeValue.Zero : CustomizeValue.Max);

View file

@ -1,4 +1,4 @@
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;
using OtterGuiInternal;

View file

@ -4,7 +4,7 @@ using Dalamud.Plugin.Services;
using Glamourer.GameData;
using Glamourer.Services;
using Glamourer.Unlocks;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Enums;

View file

@ -3,7 +3,7 @@ using Glamourer.Designs;
using Glamourer.GameData;
using Glamourer.Interop.PalettePlus;
using Glamourer.State;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Services;
@ -287,13 +287,13 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import
}
private ImGuiColorEditFlags GetFlags()
=> Format | Display | ImGuiColorEditFlags.HDR | ImGuiColorEditFlags.NoOptions;
=> Format | Display | ImGuiColorEditFlags.Hdr | ImGuiColorEditFlags.NoOptions;
private ImGuiColorEditFlags Format
=> config.UseFloatForColors ? ImGuiColorEditFlags.Float : ImGuiColorEditFlags.Uint8;
private ImGuiColorEditFlags Display
=> config.UseRgbForColors ? ImGuiColorEditFlags.DisplayRGB : ImGuiColorEditFlags.DisplayHSV;
=> config.UseRgbForColors ? ImGuiColorEditFlags.DisplayRgb : ImGuiColorEditFlags.DisplayHsv;
private ImRaii.IEndObject EnsureSize()
{

View file

@ -5,7 +5,7 @@ using Glamourer.Designs;
using Glamourer.Designs.History;
using Glamourer.Designs.Special;
using Glamourer.Events;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Extensions;
@ -22,6 +22,7 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
protected readonly TabSelected TabSelected;
protected float InnerWidth;
private IDesignStandIn? _currentDesign;
private bool _isCurrentSelectionDirty;
protected DesignComboBase(Func<IReadOnlyList<Tuple<IDesignStandIn, string>>> generator, Logger log, DesignChanged designChanged,
TabSelected tabSelected, EphemeralConfig config, DesignColors designColors)
@ -84,17 +85,11 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
DrawRightAligned(quickDesign.ResolveName(false), "[Nothing]", DesignColors.MissingColor);
}
protected override int UpdateCurrentSelected(int currentSelected)
{
CurrentSelectionIdx = Items.IndexOf(p => _currentDesign == p.Item1);
UpdateSelection(CurrentSelectionIdx >= 0 ? Items[CurrentSelectionIdx] : null);
return CurrentSelectionIdx;
}
protected bool Draw(IDesignStandIn? currentDesign, string? label, float width)
{
_currentDesign = currentDesign;
InnerWidth = 400 * ImGuiHelpers.GlobalScale;
UpdateCurrentSelection();
InnerWidth = 400 * ImGuiHelpers.GlobalScale;
var name = label ?? "Select Design Here...";
bool ret;
using (_ = currentDesign != null ? ImRaii.PushColor(ImGuiCol.Text, DesignColors.GetColor(currentDesign as Design)) : null)
@ -128,37 +123,60 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
return filter.IsContained(path) || filter.IsContained(design.ResolveName(false));
}
private void OnDesignChanged(DesignChanged.Type type, Design design, ITransaction? _ = null)
protected override void OnMouseWheel(string preview, ref int _2, int steps)
{
switch (type)
{
case DesignChanged.Type.Created:
case DesignChanged.Type.Renamed:
case DesignChanged.Type.ChangedColor:
case DesignChanged.Type.Deleted:
case DesignChanged.Type.QuickDesignBar:
var priorState = IsInitialized;
if (priorState)
Cleanup();
CurrentSelectionIdx = Items.IndexOf(s => ReferenceEquals(s.Item1, CurrentSelection?.Item1));
if (CurrentSelectionIdx >= 0)
{
UpdateSelection(Items[CurrentSelectionIdx]);
}
else if (Items.Count > 0)
{
CurrentSelectionIdx = 0;
UpdateSelection(Items[0]);
}
else
{
UpdateSelection(null);
}
if (!ReferenceEquals(_currentDesign, CurrentSelection?.Item1))
CurrentSelectionIdx = -1;
if (!priorState)
Cleanup();
break;
base.OnMouseWheel(preview, ref _2, steps);
}
private void UpdateCurrentSelection()
{
if (!_isCurrentSelectionDirty)
return;
var priorState = IsInitialized;
if (priorState)
Cleanup();
CurrentSelectionIdx = Items.IndexOf(s => ReferenceEquals(s.Item1, CurrentSelection?.Item1));
if (CurrentSelectionIdx >= 0)
{
UpdateSelection(Items[CurrentSelectionIdx]);
}
else if (Items.Count > 0)
{
CurrentSelectionIdx = 0;
UpdateSelection(Items[0]);
}
else
{
UpdateSelection(null);
}
if (!priorState)
Cleanup();
_isCurrentSelectionDirty = false;
}
protected override int UpdateCurrentSelected(int currentSelected)
{
CurrentSelectionIdx = Items.IndexOf(p => _currentDesign == p.Item1);
UpdateSelection(CurrentSelectionIdx >= 0 ? Items[CurrentSelectionIdx] : null);
return CurrentSelectionIdx;
}
private void OnDesignChanged(DesignChanged.Type type, Design? _1, ITransaction? _2 = null)
{
_isCurrentSelectionDirty = type switch
{
DesignChanged.Type.Created => true,
DesignChanged.Type.Renamed => true,
DesignChanged.Type.ChangedColor => true,
DesignChanged.Type.Deleted => true,
DesignChanged.Type.QuickDesignBar => true,
_ => _isCurrentSelectionDirty,
};
}
private void QuickSelectedDesignTooltip(IDesignStandIn? design)
@ -176,7 +194,7 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
ImGui.TextUnformatted("Currently resolving to ");
using var color = ImRaii.PushColor(ImGuiCol.Text, DesignColors.GetColor(linkedDesign));
ImGui.SameLine(0, 0);
ImGui.TextUnformatted(linkedDesign.Name);
ImGui.TextUnformatted(linkedDesign.Name.Text);
}
else
{
@ -226,8 +244,7 @@ public abstract class DesignCombo : DesignComboBase
public sealed class QuickDesignCombo : DesignCombo
{
public QuickDesignCombo(DesignManager designs,
DesignFileSystem fileSystem,
public QuickDesignCombo(DesignFileSystem fileSystem,
Logger log,
DesignChanged designChanged,
TabSelected tabSelected,
@ -235,9 +252,9 @@ public sealed class QuickDesignCombo : DesignCombo
DesignColors designColors)
: base(log, designChanged, tabSelected, config, designColors, () =>
[
.. designs.Designs
.Where(d => d.QuickDesign)
.Select(d => new Tuple<IDesignStandIn, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty))
.. fileSystem
.Where(kvp => kvp.Key.QuickDesign)
.Select(kvp => new Tuple<IDesignStandIn, string>(kvp.Key, kvp.Value.FullName()))
.OrderBy(d => d.Item2),
])
{
@ -278,7 +295,6 @@ public sealed class QuickDesignCombo : DesignCombo
}
public sealed class LinkDesignCombo(
DesignManager designs,
DesignFileSystem fileSystem,
Logger log,
DesignChanged designChanged,
@ -287,8 +303,8 @@ public sealed class LinkDesignCombo(
DesignColors designColors)
: DesignCombo(log, designChanged, tabSelected, config, designColors, () =>
[
.. designs.Designs
.Select(d => new Tuple<IDesignStandIn, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty))
.. fileSystem
.Select(kvp => new Tuple<IDesignStandIn, string>(kvp.Key, kvp.Value.FullName()))
.OrderBy(d => d.Item2),
]);
@ -302,8 +318,8 @@ public sealed class RandomDesignCombo(
DesignColors designColors)
: DesignCombo(log, designChanged, tabSelected, config, designColors, () =>
[
.. designs.Designs
.Select(d => new Tuple<IDesignStandIn, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty))
.. fileSystem
.Select(kvp => new Tuple<IDesignStandIn, string>(kvp.Key, kvp.Value.FullName()))
.OrderBy(d => d.Item2),
])
{
@ -329,7 +345,6 @@ public sealed class RandomDesignCombo(
}
public sealed class SpecialDesignCombo(
DesignManager designs,
DesignFileSystem fileSystem,
TabSelected tabSelected,
DesignColors designColors,
@ -339,8 +354,8 @@ public sealed class SpecialDesignCombo(
EphemeralConfig config,
RandomDesignGenerator rng,
QuickSelectedDesign quickSelectedDesign)
: DesignComboBase(() => designs.Designs
.Select(d => new Tuple<IDesignStandIn, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty))
: DesignComboBase(() => fileSystem
.Select(kvp => new Tuple<IDesignStandIn, string>(kvp.Key, kvp.Value.FullName()))
.OrderBy(d => d.Item2)
.Prepend(new Tuple<IDesignStandIn, string>(new RandomDesign(rng), string.Empty))
.Prepend(new Tuple<IDesignStandIn, string>(quickSelectedDesign, string.Empty))

View file

@ -8,7 +8,7 @@ using Glamourer.Automation;
using Glamourer.Designs;
using Glamourer.Interop.Penumbra;
using Glamourer.State;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui.Classes;
using OtterGui.Text;
using Penumbra.GameData.Actors;
@ -19,14 +19,15 @@ namespace Glamourer.Gui;
[Flags]
public enum QdbButtons
{
ApplyDesign = 0x01,
RevertAll = 0x02,
RevertAutomation = 0x04,
RevertAdvanced = 0x08,
RevertEquip = 0x10,
RevertCustomize = 0x20,
ReapplyAutomation = 0x40,
ResetSettings = 0x80,
ApplyDesign = 0x01,
RevertAll = 0x02,
RevertAutomation = 0x04,
RevertAdvancedDyes = 0x08,
RevertEquip = 0x10,
RevertCustomize = 0x20,
ReapplyAutomation = 0x40,
ResetSettings = 0x80,
RevertAdvancedCustomization = 0x100,
}
public sealed class DesignQuickBar : Window, IDisposable
@ -63,6 +64,7 @@ public sealed class DesignQuickBar : Window, IDisposable
IsOpen = _config.Ephemeral.ShowDesignQuickBar;
DisableWindowSounds = true;
Size = Vector2.Zero;
RespectCloseHotkey = false;
}
public void Dispose()
@ -124,6 +126,7 @@ public sealed class DesignQuickBar : Window, IDisposable
DrawRevertEquipButton(buttonSize);
DrawRevertCustomizeButton(buttonSize);
DrawRevertAdvancedCustomization(buttonSize);
DrawRevertAdvancedDyes(buttonSize);
DrawRevertAutomationButton(buttonSize);
DrawReapplyAutomationButton(buttonSize);
DrawResetSettingsButton(buttonSize);
@ -318,7 +321,7 @@ public sealed class DesignQuickBar : Window, IDisposable
private void DrawRevertAdvancedCustomization(Vector2 buttonSize)
{
if (!_config.QdbButtons.HasFlag(QdbButtons.RevertAdvanced))
if (!_config.QdbButtons.HasFlag(QdbButtons.RevertAdvancedCustomization))
return;
var available = 0;
@ -327,7 +330,7 @@ public sealed class DesignQuickBar : Window, IDisposable
if (_playerIdentifier.IsValid && _playerState is { IsLocked: false } && _playerData.Valid)
{
available |= 1;
_tooltipBuilder.Append("Left-Click: Revert the advanced customizations and dyes of the player character to their game state.");
_tooltipBuilder.Append("Left-Click: Revert the advanced customizations of the player character to their game state.");
}
if (_targetIdentifier.IsValid && _targetState is { IsLocked: false } && _targetData.Valid)
@ -335,7 +338,40 @@ public sealed class DesignQuickBar : Window, IDisposable
if (available != 0)
_tooltipBuilder.Append('\n');
available |= 2;
_tooltipBuilder.Append("Right-Click: Revert the advanced customizations and dyes of ")
_tooltipBuilder.Append("Right-Click: Revert the advanced customizations of ")
.Append(_targetIdentifier)
.Append(" to their game state.");
}
if (available == 0)
_tooltipBuilder.Append("Neither player character nor target are available or their state is locked.");
var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.PaintBrush, buttonSize, available);
ImGui.SameLine();
if (clicked)
_stateManager.ResetAdvancedCustomizations(state!, StateSource.Manual);
}
private void DrawRevertAdvancedDyes(Vector2 buttonSize)
{
if (!_config.QdbButtons.HasFlag(QdbButtons.RevertAdvancedDyes))
return;
var available = 0;
_tooltipBuilder.Clear();
if (_playerIdentifier.IsValid && _playerState is { IsLocked: false } && _playerData.Valid)
{
available |= 1;
_tooltipBuilder.Append("Left-Click: Revert the advanced dyes of the player character to their game state.");
}
if (_targetIdentifier.IsValid && _targetState is { IsLocked: false } && _targetData.Valid)
{
if (available != 0)
_tooltipBuilder.Append('\n');
available |= 2;
_tooltipBuilder.Append("Right-Click: Revert the advanced dyes of ")
.Append(_targetIdentifier)
.Append(" to their game state.");
}
@ -346,7 +382,7 @@ public sealed class DesignQuickBar : Window, IDisposable
var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.Palette, buttonSize, available);
ImGui.SameLine();
if (clicked)
_stateManager.ResetAdvancedState(state!, StateSource.Manual);
_stateManager.ResetAdvancedDyes(state!, StateSource.Manual);
}
private void DrawRevertCustomizeButton(Vector2 buttonSize)
@ -501,7 +537,9 @@ public sealed class DesignQuickBar : Window, IDisposable
++_numButtons;
}
if (_config.QdbButtons.HasFlag(QdbButtons.RevertAdvanced))
if (_config.QdbButtons.HasFlag(QdbButtons.RevertAdvancedCustomization))
++_numButtons;
if (_config.QdbButtons.HasFlag(QdbButtons.RevertAdvancedDyes))
++_numButtons;
if (_config.QdbButtons.HasFlag(QdbButtons.RevertCustomize))
++_numButtons;

View file

@ -1,7 +1,7 @@
using Dalamud.Plugin.Services;
using Glamourer.Services;
using Glamourer.Unlocks;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using Lumina.Excel.Sheets;
using OtterGui;
using OtterGui.Classes;

View file

@ -27,6 +27,9 @@ public struct EquipDrawData(EquipSlot slot, in DesignData designData)
public readonly void SetStains(StainIds stains)
=> _editor.ChangeStains(_object, Slot, stains, ApplySettings.Manual);
public readonly void SetStain(int which, StainId stain)
=> _editor.ChangeStains(_object, Slot, CurrentStains.With(which, stain), ApplySettings.Manual);
public readonly void SetApplyItem(bool value)
{
var manager = (DesignManager)_editor;

View file

@ -0,0 +1,83 @@
using Glamourer.Services;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Equipment;
[InlineArray(13)]
public struct EquipItemSlotCache
{
private EquipItem _element;
public EquipItem Dragged
{
get => this[^1];
set => this[^1] = value;
}
public void Clear()
=> ((Span<EquipItem>)this).Clear();
public EquipItem this[EquipSlot slot]
{
get => this[(int)slot.ToIndex()];
set => this[(int)slot.ToIndex()] = value;
}
public void Update(ItemManager items, in EquipItem item, EquipSlot startSlot)
{
if (item.Id == Dragged.Id && item.Type == Dragged.Type)
return;
switch (startSlot)
{
case EquipSlot.MainHand:
{
Clear();
this[EquipSlot.MainHand] = item;
if (item.Type is FullEquipType.Sword)
this[EquipSlot.OffHand] = items.FindClosestShield(item.ItemId, out var shield) ? shield : default;
else
this[EquipSlot.OffHand] = items.ItemData.Secondary.GetValueOrDefault(item.ItemId);
break;
}
case EquipSlot.OffHand:
{
Clear();
if (item.Type is FullEquipType.Shield)
this[EquipSlot.MainHand] = items.FindClosestSword(item.ItemId, out var sword) ? sword : default;
else
this[EquipSlot.MainHand] = items.ItemData.Primary.GetValueOrDefault(item.ItemId);
this[EquipSlot.OffHand] = item;
break;
}
default:
{
this[EquipSlot.MainHand] = default;
this[EquipSlot.OffHand] = default;
foreach (var slot in EquipSlotExtensions.EqdpSlots)
{
if (startSlot == slot)
{
this[slot] = item;
continue;
}
var slotItem = items.Identify(slot, item.PrimaryId, item.Variant);
if (!slotItem.Valid || slotItem.ItemId.Id is not 0 != item.ItemId.Id is not 0)
{
slotItem = items.Identify(EquipSlot.OffHand, item.PrimaryId, item.SecondaryId, 1, item.Type);
if (slotItem.ItemId.Id is not 0 != item.ItemId.Id is not 0)
slotItem = default;
}
this[slot] = slotItem;
}
break;
}
}
Dragged = item;
}
}

View file

@ -3,16 +3,15 @@ using Dalamud.Interface.Utility;
using Dalamud.Plugin.Services;
using Glamourer.Events;
using Glamourer.Gui.Materials;
using Glamourer.Interop.Material;
using Glamourer.Services;
using Glamourer.Unlocks;
using ImGuiNET;
using OtterGui;
using Dalamud.Bindings.ImGui;
using OtterGui.Extensions;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Text.EndObjects;
using OtterGui.Widgets;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@ -33,20 +32,24 @@ public class EquipmentDrawer
private readonly Configuration _config;
private readonly GPoseService _gPose;
private readonly AdvancedDyePopup _advancedDyes;
private readonly ItemCopyService _itemCopy;
private float _requiredComboWidthUnscaled;
private float _requiredComboWidth;
private Stain? _draggedStain;
private Stain? _draggedStain;
private EquipItemSlotCache _draggedItem;
private EquipSlot _dragTarget;
public EquipmentDrawer(FavoriteManager favorites, IDataManager gameData, ItemManager items, TextureService textures,
Configuration config, GPoseService gPose, AdvancedDyePopup advancedDyes)
Configuration config, GPoseService gPose, AdvancedDyePopup advancedDyes, ItemCopyService itemCopy)
{
_items = items;
_textures = textures;
_config = config;
_gPose = gPose;
_advancedDyes = advancedDyes;
_itemCopy = itemCopy;
_stainData = items.Stains;
_stainCombo = new GlamourerColorCombo(DefaultWidth - 20, _stainData, favorites);
_itemCombo = EquipSlotExtensions.EqdpSlots.Select(e => new ItemCombo(gameData, items, e, Glamourer.Log, favorites)).ToArray();
@ -79,6 +82,7 @@ public class EquipmentDrawer
_requiredComboWidth = _requiredComboWidthUnscaled * ImGuiHelpers.GlobalScale;
_advancedMaterialColor = ColorId.AdvancedDyeActive.Value();
_dragTarget = EquipSlot.Unknown;
}
private bool VerifyRestrictedGear(EquipDrawData data)
@ -184,6 +188,7 @@ public class EquipmentDrawer
return change;
}
#region Small
private void DrawEquipSmall(in EquipDrawData equipDrawData)
@ -402,6 +407,7 @@ public class EquipmentDrawer
? _stainCombo.Draw($"##stain{data.Slot}", stain.RgbaColor, stain.Name, found, stain.Gloss)
: _stainCombo.Draw($"##stain{data.Slot}", stain.RgbaColor, stain.Name, found, stain.Gloss, width);
_itemCopy.HandleCopyPaste(data, index);
if (!change)
DrawStainDragDrop(data, index, stain, found);
@ -426,8 +432,8 @@ public class EquipmentDrawer
using var dragSource = ImUtf8.DragDropSource();
if (dragSource.Success)
{
if (DragDropSource.SetPayload("stainDragDrop"u8))
_draggedStain = stain;
DragDropSource.SetPayload("stainDragDrop"u8);
_draggedStain = stain;
ImUtf8.Text($"Dragging {stain.Name}...");
}
}
@ -452,10 +458,12 @@ public class EquipmentDrawer
using var disabled = ImRaii.Disabled(data.Locked);
var change = combo.Draw(data.CurrentItem.Name, data.CurrentItem.ItemId, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength,
_requiredComboWidth);
DrawGearDragDrop(data);
if (change)
data.SetItem(combo.CurrentSelection);
else if (combo.CustomVariant.Id > 0)
data.SetItem(_items.Identify(data.Slot, combo.CustomSetId, combo.CustomVariant));
_itemCopy.HandleCopyPaste(data);
if (ResetOrClear(data.Locked, clear, data.AllowRevert, true, data.CurrentItem, data.GameItem, ItemManager.NothingItem(data.Slot),
out var item))
@ -473,6 +481,14 @@ public class EquipmentDrawer
var change = combo.Draw(data.CurrentItem.Name, data.CurrentItem.Id.BonusItem,
small ? _comboLength - ImGui.GetFrameHeight() : _comboLength,
_requiredComboWidth);
if (ImGui.IsItemHovered() && ImGui.GetIO().KeyCtrl)
{
if (ImGui.IsKeyPressed(ImGuiKey.C))
_itemCopy.Copy(combo.CurrentSelection);
else if (ImGui.IsKeyPressed(ImGuiKey.V))
_itemCopy.Paste(data.Slot.ToEquipType(), data.SetItem);
}
if (change)
data.SetItem(combo.CurrentSelection);
else if (combo.CustomVariant.Id > 0)
@ -483,6 +499,50 @@ public class EquipmentDrawer
data.SetItem(item);
}
private void DrawGearDragDrop(in EquipDrawData data)
{
if (data.CurrentItem.Valid)
{
using var dragSource = ImUtf8.DragDropSource();
if (dragSource.Success)
{
DragDropSource.SetPayload("equipDragDrop"u8);
_draggedItem.Update(_items, data.CurrentItem, data.Slot);
}
}
using var dragTarget = ImUtf8.DragDropTarget();
if (!dragTarget)
return;
var item = _draggedItem[data.Slot];
if (!item.Valid)
return;
_dragTarget = data.Slot;
if (!dragTarget.IsDropping("equipDragDrop"u8))
return;
data.SetItem(item);
_draggedItem.Clear();
}
public unsafe void DrawDragDropTooltip()
{
var payload = ImGui.GetDragDropPayload().Handle;
if (payload is null)
return;
if (!MemoryMarshal.CreateReadOnlySpanFromNullTerminated((byte*)Unsafe.AsPointer(ref payload->DataType_0)).SequenceEqual("equipDragDrop"u8))
return;
using var tt = ImUtf8.Tooltip();
if (_dragTarget is EquipSlot.Unknown)
ImUtf8.Text($"Dragging {_draggedItem.Dragged.Name}...");
else
ImUtf8.Text($"Converting to {_draggedItem[_dragTarget].Name}...");
}
private static bool ResetOrClear<T>(bool locked, bool clicked, bool allowRevert, bool allowClear,
in T currentItem, in T revertItem, in T clearItem, out T? item) where T : IEquatable<T>
{
@ -531,8 +591,13 @@ public class EquipmentDrawer
if (combo.Draw(mainhand.CurrentItem.Name, mainhand.CurrentItem.ItemId, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength,
_requiredComboWidth))
changedItem = combo.CurrentSelection;
else if (ResetOrClear(mainhand.Locked || unknown, open, mainhand.AllowRevert, false, mainhand.CurrentItem, mainhand.GameItem,
default, out var c))
else if (combo.CustomVariant.Id > 0 && (drawAll || ItemData.ConvertWeaponId(combo.CustomSetId) == mainhand.CurrentItem.Type))
changedItem = _items.Identify(mainhand.Slot, combo.CustomSetId, combo.CustomWeaponId, combo.CustomVariant);
_itemCopy.HandleCopyPaste(mainhand);
DrawGearDragDrop(mainhand);
if (ResetOrClear(mainhand.Locked || unknown, open, mainhand.AllowRevert, false, mainhand.CurrentItem, mainhand.GameItem,
default, out var c))
changedItem = c;
if (changedItem != null)
@ -549,7 +614,8 @@ public class EquipmentDrawer
}
if (unknown)
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, "The weapon type could not be identified, thus changing it to other weapons of that type is not possible."u8);
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled,
"The weapon type could not be identified, thus changing it to other weapons of that type is not possible."u8);
}
private void DrawOffhand(in EquipDrawData mainhand, in EquipDrawData offhand, out string label, bool small, bool clear, bool open)
@ -569,6 +635,10 @@ public class EquipmentDrawer
if (combo.Draw(offhand.CurrentItem.Name, offhand.CurrentItem.ItemId, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength,
_requiredComboWidth))
offhand.SetItem(combo.CurrentSelection);
else if (combo.CustomVariant.Id > 0 && ItemData.ConvertWeaponId(combo.CustomSetId) == offhand.CurrentItem.Type)
offhand.SetItem(_items.Identify(mainhand.Slot, combo.CustomSetId, combo.CustomWeaponId, combo.CustomVariant));
_itemCopy.HandleCopyPaste(offhand);
DrawGearDragDrop(offhand);
var defaultOffhand = _items.GetDefaultOffhand(mainhand.CurrentItem);
if (ResetOrClear(locked, clear, offhand.AllowRevert, true, offhand.CurrentItem, offhand.GameItem, defaultOffhand, out var item))
@ -623,6 +693,7 @@ public class EquipmentDrawer
{
ImUtf8.Text(label);
}
if (hasAdvancedDyes)
ImUtf8.HoverTooltip("This design has advanced dyes setup for this slot."u8);
}

View file

@ -2,7 +2,7 @@
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Glamourer.Unlocks;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui.Widgets;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Structs;

View file

@ -1,7 +1,7 @@
using Dalamud.Plugin.Services;
using Glamourer.Services;
using Glamourer.Unlocks;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using Lumina.Excel.Sheets;
using OtterGui.Classes;
using OtterGui.Extensions;

View file

@ -0,0 +1,73 @@
using Glamourer.Services;
using Dalamud.Bindings.ImGui;
using OtterGui.Services;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Equipment;
public class ItemCopyService(ItemManager items, DictStain stainData) : IUiService
{
public EquipItem? Item { get; private set; }
public Stain? Stain { get; private set; }
public void Copy(in EquipItem item)
=> Item = item;
public void Copy(in Stain stain)
=> Stain = stain;
public void Paste(int which, Action<int, StainId> setter)
{
if (Stain is { } stain)
setter(which, stain.RowIndex);
}
public void Paste(FullEquipType type, Action<EquipItem> setter)
{
if (Item is not { } item)
return;
if (type != item.Type)
{
if (type.IsBonus())
item = items.Identify(type.ToBonus(), item.PrimaryId, item.Variant);
else if (type.IsEquipment() || type.IsAccessory())
item = items.Identify(type.ToSlot(), item.PrimaryId, item.Variant);
else
item = items.Identify(type.ToSlot(), item.PrimaryId, item.SecondaryId, item.Variant);
}
if (item.Valid && item.Type == type)
setter(item);
}
public void HandleCopyPaste(in EquipDrawData data)
{
if (ImGui.GetIO().KeyCtrl)
{
if (ImGui.IsItemHovered() && ImGui.IsMouseClicked(ImGuiMouseButton.Middle))
Paste(data.CurrentItem.Type, data.SetItem);
}
else if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled) && ImGui.IsMouseClicked(ImGuiMouseButton.Middle))
{
Copy(data.CurrentItem);
}
}
public void HandleCopyPaste(in EquipDrawData data, int which)
{
if (ImGui.GetIO().KeyCtrl)
{
if (ImGui.IsItemHovered() && ImGui.IsMouseClicked(ImGuiMouseButton.Middle))
Paste(which, data.SetStain);
}
else if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)
&& ImGui.IsMouseClicked(ImGuiMouseButton.Middle)
&& stainData.TryGetValue(data.CurrentStains[which].Id, out var stain))
{
Copy(stain);
}
}
}

View file

@ -1,7 +1,6 @@
using Glamourer.Services;
using Glamourer.Unlocks;
using ImGuiNET;
using OtterGui;
using Dalamud.Bindings.ImGui;
using OtterGui.Classes;
using OtterGui.Extensions;
using OtterGui.Log;
@ -20,6 +19,10 @@ public sealed class WeaponCombo : FilterComboCache<EquipItem>
private ItemId _currentItem;
private float _innerWidth;
public PrimaryId CustomSetId { get; private set; }
public SecondaryId CustomWeaponId { get; private set; }
public Variant CustomVariant { get; private set; }
public WeaponCombo(ItemManager items, FullEquipType type, Logger log, FavoriteManager favorites)
: base(() => GetWeapons(favorites, items, type), MouseWheelType.Control, log)
{
@ -47,8 +50,9 @@ public sealed class WeaponCombo : FilterComboCache<EquipItem>
public bool Draw(string previewName, ItemId previewIdx, float width, float innerWidth)
{
_innerWidth = innerWidth;
_currentItem = previewIdx;
_innerWidth = innerWidth;
_currentItem = previewIdx;
CustomVariant = 0;
return Draw($"##{Label}", previewName, string.Empty, width, ImGui.GetTextLineHeightWithSpacing());
}
@ -75,6 +79,24 @@ public sealed class WeaponCombo : FilterComboCache<EquipItem>
return ret;
}
protected override void OnClosePopup()
{
// If holding control while the popup closes, try to parse the input as a full tuple of set id, weapon id and variant, and set a custom item for that.
if (!ImGui.GetIO().KeyCtrl)
return;
var split = Filter.Text.Split('-', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (split.Length != 3
|| !ushort.TryParse(split[0], out var setId)
|| !ushort.TryParse(split[1], out var weaponId)
|| !byte.TryParse(split[2], out var variant))
return;
CustomSetId = setId;
CustomWeaponId = weaponId;
CustomVariant = variant;
}
protected override bool IsVisible(int globalIndex, LowerString filter)
=> base.IsVisible(globalIndex, filter) || Items[globalIndex].ModelString.StartsWith(filter.Lower);

View file

@ -3,7 +3,7 @@ using Dalamud.Interface.Utility;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin.Services;
using Glamourer.Gui.Materials;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;

View file

@ -42,6 +42,9 @@ public class GlamourerChangelog
Add1_3_6_0(Changelog);
Add1_3_7_0(Changelog);
Add1_3_8_0(Changelog);
Add1_4_0_0(Changelog);
Add1_5_0_0(Changelog);
Add1_5_1_0(Changelog);
}
private (int, ChangeLogDisplayType) ConfigData()
@ -62,6 +65,54 @@ public class GlamourerChangelog
}
}
private static void Add1_5_1_0(Changelog log)
=> log.NextVersion("Version 1.5.1.0")
.RegisterHighlight("Added support for Penumbras PCP functionality to add the current state of the character as a design.")
.RegisterEntry("On import, a design for the PCP is created and, if possible, applied to the character.", 1)
.RegisterEntry("No automation is assigned.", 1)
.RegisterEntry("Finer control about this can be found in the settings.", 1)
.RegisterEntry("Fixed an issue with static visors not toggling through Glamourer (1.5.0.7).")
.RegisterEntry("The advanced dye slot combo now contains glasses (1.5.0.7).")
.RegisterEntry("Several fixes for patch-related issues (1.5.0.1 - 1.5.0.6");
private static void Add1_5_0_0(Changelog log)
=> log.NextVersion("Version 1.5.0.0")
.RegisterImportant("Updated for game version 7.30 and Dalamud API13, which uses a new GUI backend. Some things may not work as expected. Please let me know any issues you encounter.")
.RegisterHighlight("Added the new Viera Ears state to designs. Old designs will not apply the state.")
.RegisterHighlight("Added the option to make newly created designs write-protected by default to the design defaults.")
.RegisterEntry("Fixed issues with reverting state and IPC.")
.RegisterEntry("Fixed an issue when using the mousewheel to scroll through designs (1.4.0.3).")
.RegisterEntry("Fixed an issue with invalid bonus items (1.4.0.3).")
.RegisterHighlight("Added drag & drop of equipment pieces which will try to match the corresponding model IDs in other slots if possible (1.4.0.2).")
.RegisterEntry("Heavily optimized some issues when having many designs and creating new ones or updating them (1.4.0.2)")
.RegisterEntry("Fixed an issue with staining templates (1.4.0.1).")
.RegisterEntry("Fixed an issue with the QDB buttons not counting correctly (1.4.0.1).");
private static void Add1_4_0_0(Changelog log)
=> log.NextVersion("Version 1.4.0.0")
.RegisterHighlight("The design selector width is now draggable within certain restrictions that depend on the total window width.")
.RegisterEntry("The current behavior may not be final, let me know if you have any comments.", 1)
.RegisterEntry("Regular customization colors can now be dragged & dropped onto other customizations.")
.RegisterEntry(
"If no identical color is available in the target slot, the most similar color available (for certain values of similar) will be chosen instead.",
1)
.RegisterEntry("Resetting advanced dyes and customizations has been split into two buttons for the quick design bar.")
.RegisterEntry("Weapons now also support custom ID input in the combo search box.")
.RegisterEntry("Added new IPC methods GetExtendedDesignData, AddDesign, DeleteDesign, GetDesignBase64, GetDesignJObject.")
.RegisterEntry("Added the option to prevent immediate repeats for random design selection (Thanks Diorik!).")
.RegisterEntry("Optimized some multi-design changes when selecting many designs and changing them at once.")
.RegisterEntry("Fixed item combos not starting from the currently selected item when scrolling them via mouse wheel.")
.RegisterEntry("Fixed some issue with Glamourer not searching mods by name for mod associations in some cases.")
.RegisterEntry("Fixed the IPC methods SetMetaState and SetMetaStateName not working (Thanks Caraxi!).")
.RegisterEntry("Added new IPC method GetDesignListExtended. (1.3.8.6)")
.RegisterEntry(
"Improved the naming of NPCs for identifiers by using Haselnussbombers new naming functionality (Thanks Hasel!). (1.3.8.6)")
.RegisterEntry(
"Added a modifier key separate from the delete modifier key that is used for less important key-checks, specifically toggling incognito mode. (1.3.8.5)")
.RegisterEntry("Used better Penumbra IPC for some things. (1.3.8.5)")
.RegisterEntry("Fixed an issue with advanced dyes for weapons. (1.3.8.5)")
.RegisterEntry("Fixed an issue with NPC automation due to missing job detection. (1.3.8.1)");
private static void Add1_3_8_0(Changelog log)
=> log.NextVersion("Version 1.3.8.0")
.RegisterImportant("Updated Glamourer for update 7.20 and Dalamud API 12.")

View file

@ -12,7 +12,7 @@ using Glamourer.Gui.Tabs.NpcTab;
using Glamourer.Gui.Tabs.SettingsTab;
using Glamourer.Gui.Tabs.UnlocksTab;
using Glamourer.Interop.Penumbra;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Custom;

View file

@ -8,7 +8,7 @@ using FFXIVClientStructs.Interop;
using Glamourer.Designs;
using Glamourer.Interop.Material;
using Glamourer.State;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui.Raii;
using OtterGui.Services;
using OtterGui.Text;
@ -91,7 +91,7 @@ public sealed unsafe class AdvancedDyePopup(
var modelHandle = model == null ? null : model->ModelResourceHandle;
var path = materialHandle == null
? string.Empty
: ByteString.FromSpanUnsafe(materialHandle->ResourceHandle.FileName.AsSpan(), true).ToString();
: ByteString.FromSpanUnsafe(materialHandle->FileName.AsSpan(), true).ToString();
var gamePath = modelHandle == null
? string.Empty
: modelHandle->GetMaterialFileNameBySlot(index.MaterialIndex).ToString();

View file

@ -3,7 +3,7 @@ using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Glamourer.Designs;
using Glamourer.Interop.Material;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Services;
using OtterGui.Text;
@ -18,7 +18,6 @@ public class MaterialDrawer(DesignManager _designManager, Configuration _config)
public const float GlossWidth = 100;
public const float SpecularStrengthWidth = 125;
private EquipSlot _newSlot = EquipSlot.Head;
private int _newMaterialIdx;
private int _newRowIdx;
private MaterialValueIndex _newKey = MaterialValueIndex.FromSlot(EquipSlot.Head);
@ -178,14 +177,42 @@ public class MaterialDrawer(DesignManager _designManager, Configuration _config)
public sealed class MaterialSlotCombo;
private void DrawSlotCombo()
{
var width = ImUtf8.CalcTextSize(EquipSlot.OffHand.ToName()).X + ImGui.GetFrameHeightWithSpacing();
ImGui.SetNextItemWidth(width);
using var combo = ImUtf8.Combo("##slot"u8, _newKey.SlotName());
if (combo)
{
var currentSlot = _newKey.ToEquipSlot();
foreach (var tmpSlot in EquipSlotExtensions.FullSlots)
{
if (ImUtf8.Selectable(tmpSlot.ToName(), tmpSlot == currentSlot) && currentSlot != tmpSlot)
_newKey = MaterialValueIndex.FromSlot(tmpSlot) with
{
MaterialIndex = (byte)_newMaterialIdx,
RowIndex = (byte)_newRowIdx,
};
}
var currentBonus = _newKey.ToBonusSlot();
foreach (var bonusSlot in BonusExtensions.AllFlags)
{
if (ImUtf8.Selectable(bonusSlot.ToName(), bonusSlot == currentBonus) && bonusSlot != currentBonus)
_newKey = MaterialValueIndex.FromSlot(bonusSlot) with
{
MaterialIndex = (byte)_newMaterialIdx,
RowIndex = (byte)_newRowIdx,
};
}
}
ImUtf8.HoverTooltip("Choose a slot for an advanced dye row."u8);
}
public void DrawNew(Design design)
{
if (EquipSlotCombo.Draw("##slot", "Choose a slot for an advanced dye row.", ref _newSlot))
_newKey = MaterialValueIndex.FromSlot(_newSlot) with
{
MaterialIndex = (byte)_newMaterialIdx,
RowIndex = (byte)_newRowIdx,
};
DrawSlotCombo();
ImUtf8.SameLineInner();
DrawMaterialIdxDrag();
ImUtf8.SameLineInner();

View file

@ -3,7 +3,7 @@ using Glamourer.Events;
using Glamourer.Interop.Penumbra;
using Glamourer.Services;
using Glamourer.State;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui.Raii;
using Penumbra.Api.Enums;
using Penumbra.GameData.Data;
@ -22,11 +22,12 @@ public sealed class PenumbraChangedItemTooltip : IDisposable
private readonly CustomizeService _customize;
private readonly GPoseService _gpose;
private readonly EquipItem[] _lastItems = new EquipItem[EquipFlagExtensions.NumEquipFlags / 2];
private readonly EquipItem[] _lastItems = new EquipItem[EquipFlagExtensions.NumEquipFlags / 2 + BonusExtensions.AllFlags.Count];
public IEnumerable<KeyValuePair<EquipSlot, EquipItem>> LastItems
=> EquipSlotExtensions.EqdpSlots.Append(EquipSlot.MainHand).Append(EquipSlot.OffHand).Zip(_lastItems)
.Select(p => new KeyValuePair<EquipSlot, EquipItem>(p.First, p.Second));
public IEnumerable<KeyValuePair<object, EquipItem>> LastItems
=> EquipSlotExtensions.EqdpSlots.Cast<object>().Append(EquipSlot.MainHand).Append(EquipSlot.OffHand)
.Concat(BonusExtensions.AllFlags.Cast<object>()).Zip(_lastItems)
.Select(p => new KeyValuePair<object, EquipItem>(p.First, p.Second));
public ChangedItemType LastType { get; private set; } = ChangedItemType.None;
public uint LastId { get; private set; }
@ -72,6 +73,21 @@ public sealed class PenumbraChangedItemTooltip : IDisposable
if (!Player())
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 last = _lastItems[slot.ToIndex()];
switch (slot)
@ -109,6 +125,27 @@ public sealed class PenumbraChangedItemTooltip : IDisposable
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 last = _lastItems[slot.ToIndex()];
switch (slot)
@ -265,7 +302,22 @@ public sealed class PenumbraChangedItemTooltip : IDisposable
{
var oldItem = state.ModelData.Item(slot);
if (oldItem.Id != item.Id)
_lastItems[slot.ToIndex()] = oldItem;
last = 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

@ -11,7 +11,7 @@ using Glamourer.Gui.Equipment;
using Glamourer.Gui.Materials;
using Glamourer.Interop;
using Glamourer.State;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;
@ -238,6 +238,7 @@ public class ActorPanel
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
DrawEquipmentMetaToggles();
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
_equipmentDrawer.DrawDragDropTooltip();
}
private void DrawParameterHeader()
@ -304,6 +305,12 @@ public class ActorPanel
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.WeaponState, _stateManager, _state!));
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromState(CrestFlag.OffHand, _stateManager, _state!));
}
ImGui.SameLine();
using (_ = ImRaii.Group())
{
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.EarState, _stateManager, _state!));
}
}
private void DrawMonsterPanel()

View file

@ -1,5 +1,5 @@
using Dalamud.Interface;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;

View file

@ -1,5 +1,5 @@
using Dalamud.Interface.Utility;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui.Widgets;
namespace Glamourer.Gui.Tabs.ActorTab;

View file

@ -1,5 +1,5 @@
using Dalamud.Interface.Utility;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui.Widgets;
namespace Glamourer.Gui.Tabs.AutomationTab;

View file

@ -1,12 +1,12 @@
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Utility;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Custom;
using OtterGui.Extensions;
using OtterGui.Log;
using OtterGui.Widgets;
using Penumbra.GameData.DataContainers;
using OtterGui.Custom;
namespace Glamourer.Gui.Tabs.AutomationTab;

View file

@ -1,5 +1,5 @@
using Dalamud.Game.ClientState.Objects.Enums;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using Penumbra.GameData.Actors;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Gui;

View file

@ -4,7 +4,7 @@ using Glamourer.Automation;
using Glamourer.Designs;
using Glamourer.Designs.Special;
using Glamourer.Events;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Services;
@ -278,7 +278,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable
private void LookupTooltip(IEnumerable<Design> designs)
{
using var _ = ImRaii.Tooltip();
var tt = string.Join('\n', designs.Select(d => _designFileSystem.FindLeaf(d, out var l) ? l.FullName() : d.Name.Text).OrderBy(t => t));
var tt = string.Join('\n', designs.Select(d => _designFileSystem.TryGetValue(d, out var l) ? l.FullName() : d.Name.Text).OrderBy(t => t));
ImGui.TextUnformatted(tt.Length == 0
? "Matches no currently existing designs."
: "Matches the following designs:");

View file

@ -6,7 +6,7 @@ using Glamourer.Designs.Special;
using Glamourer.Interop;
using Glamourer.Services;
using Glamourer.Unlocks;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Extensions;
using OtterGui.Log;
@ -432,7 +432,7 @@ public class SetPanel(
if (source)
{
ImUtf8.Text($"Moving design #{index + 1:D2}...");
if (ImGui.SetDragDropPayload(dragDropLabel, nint.Zero, 0))
if (ImGui.SetDragDropPayload(dragDropLabel, null, 0))
{
_dragIndex = index;
_selector.DragDesignIndex = index;

View file

@ -2,7 +2,7 @@
using Dalamud.Interface.Utility;
using Glamourer.Automation;
using Glamourer.Events;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Extensions;
@ -144,7 +144,7 @@ public class SetSelector : IDisposable
ImGui.SameLine();
var f = _enabledFilter;
if (ImGui.CheckboxFlags("##enabledFilter", ref f, 3))
if (ImGui.CheckboxFlags("##enabledFilter", ref f, 3u))
{
_enabledFilter = _enabledFilter switch
{
@ -347,7 +347,7 @@ public class SetSelector : IDisposable
if (source)
{
ImGui.TextUnformatted($"Moving design set {GetSetName(set, index)} from position {index + 1}...");
if (ImGui.SetDragDropPayload(dragDropLabel, nint.Zero, 0))
if (ImGui.SetDragDropPayload(dragDropLabel, null, 0))
_dragIndex = index;
}
}

View file

@ -2,7 +2,7 @@
using Glamourer.GameData;
using Glamourer.Designs;
using Glamourer.State;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Enums;
@ -87,6 +87,9 @@ public class ActiveStatePanel(StateManager _stateManager, ActorObjectManager _ob
PrintRow("Visor Toggled", state.BaseData.IsVisorToggled(), state.ModelData.IsVisorToggled(),
state.Sources[MetaIndex.VisorState]);
ImGui.TableNextRow();
PrintRow("Viera Ears Visible", state.BaseData.AreEarsVisible(), state.ModelData.AreEarsVisible(),
state.Sources[MetaIndex.EarState]);
ImGui.TableNextRow();
PrintRow("Weapon Visible", state.BaseData.IsWeaponVisible(), state.ModelData.IsWeaponVisible(),
state.Sources[MetaIndex.WeaponState]);
ImGui.TableNextRow();

View file

@ -1,5 +1,5 @@
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.GameData.Gui.Debug;

View file

@ -1,5 +1,5 @@
using Glamourer.Automation;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Extensions;
using OtterGui.Raii;

View file

@ -1,7 +1,7 @@
using Dalamud.Interface;
using Glamourer.GameData;
using Glamourer.Services;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;

View file

@ -1,5 +1,5 @@
using Glamourer.Unlocks;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Enums;

View file

@ -1,5 +1,5 @@
using Glamourer.Interop;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using Penumbra.GameData.Files;
using Penumbra.GameData.Gui.Debug;

View file

@ -1,4 +1,4 @@
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui.Raii;
using OtterGui.Services;
using OtterGui.Widgets;

View file

@ -1,5 +1,4 @@
using Glamourer.Gui.Tabs.DebugTab.IpcTester;
using ImGuiNET;
using Microsoft.Extensions.DependencyInjection;
using OtterGui.Raii;
using Penumbra.GameData.Gui.Debug;
@ -36,6 +35,7 @@ public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees)
provider.GetRequiredService<ModelEvaluationPanel>(),
provider.GetRequiredService<ObjectManagerPanel>(),
provider.GetRequiredService<PenumbraPanel>(),
provider.GetRequiredService<DynamisPanel>(),
provider.GetRequiredService<IpcTesterPanel>(),
provider.GetRequiredService<DatFilePanel>(),
provider.GetRequiredService<GlamourPlatePanel>(),

View file

@ -1,7 +1,7 @@
using Dalamud.Interface;
using Glamourer.Designs;
using Glamourer.Utility;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using Newtonsoft.Json.Linq;
using OtterGui;
using OtterGui.Raii;

View file

@ -1,9 +1,11 @@
using Dalamud.Interface;
using Glamourer.Designs;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Extensions;
using OtterGui.Filesystem;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Gui.Debug;
@ -19,6 +21,7 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
public void Draw()
{
DrawButtons();
foreach (var (design, idx) in _designManager.Designs.WithIndex())
{
using var t = ImRaii.TreeNode($"{design.Name}##{idx}");
@ -26,7 +29,8 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
continue;
DrawDesign(design, _designFileSystem);
var base64 = DesignBase64Migration.CreateOldBase64(design.DesignData, design.Application.Equip, design.Application.Customize, design.Application.Meta,
var base64 = DesignBase64Migration.CreateOldBase64(design.DesignData, design.Application.Equip, design.Application.Customize,
design.Application.Meta,
design.WriteProtected());
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
ImGuiUtil.TextWrapped(base64);
@ -35,6 +39,26 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
}
}
private void DrawButtons()
{
if (ImUtf8.Button("Generate 500 Test Designs"u8))
for (var i = 0; i < 500; ++i)
{
var design = _designManager.CreateEmpty($"Test Designs/Test Design {i}", true);
_designManager.AddTag(design, "_DebugTest");
}
ImUtf8.SameLineInner();
if (ImUtf8.Button("Remove All Test Designs"u8))
{
var designs = _designManager.Designs.Where(d => d.Tags.Contains("_DebugTest")).ToArray();
foreach (var design in designs)
_designManager.Delete(design);
if (_designFileSystem.Find("Test Designs", out var path) && path is DesignFileSystem.Folder { TotalChildren: 0 })
_designFileSystem.Delete(path);
}
}
public static void DrawDesign(DesignBase design, DesignFileSystem? fileSystem)
{
using var table = ImRaii.Table("##equip", 8, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit);
@ -53,7 +77,7 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
ImGui.TableNextRow();
ImGuiUtil.DrawTableColumn("Design File System Path");
if (fileSystem != null)
ImGuiUtil.DrawTableColumn(fileSystem.FindLeaf(d, out var leaf) ? leaf.FullName() : "No Path Known");
ImGuiUtil.DrawTableColumn(fileSystem.TryGetValue(d, out var leaf) ? leaf.FullName() : "No Path Known");
ImGui.TableNextRow();
ImGuiUtil.DrawTableColumn("Creation");
@ -90,6 +114,7 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
ImGuiUtil.DrawTableColumn(index.ToName());
ImGuiUtil.DrawTableColumn(design.DesignData.GetMeta(index).ToString());
ImGuiUtil.DrawTableColumn(design.DoApplyMeta(index) ? "Apply" : "Keep");
ImGui.TableNextRow();
}
ImGuiUtil.DrawTableColumn("Model ID");

View file

@ -1,7 +1,7 @@
using Dalamud.Interface;
using Glamourer.Designs;
using Glamourer.Services;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Extensions;
using OtterGui.Raii;

View file

@ -0,0 +1,16 @@
using OtterGui.Services;
using Penumbra.GameData.Gui.Debug;
namespace Glamourer.Gui.Tabs.DebugTab;
public class DynamisPanel(DynamisIpc dynamis) : IGameDataDrawer
{
public string Label
=> "Dynamis Interop";
public void Draw()
=> dynamis.DrawDebugInfo();
public bool Disabled
=> false;
}

View file

@ -1,5 +1,5 @@
using Glamourer.State;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using Penumbra.GameData.Gui.Debug;
namespace Glamourer.Gui.Tabs.DebugTab;

View file

@ -5,7 +5,7 @@ using FFXIVClientStructs.FFXIV.Client.Game;
using Glamourer.Designs;
using Glamourer.Services;
using Glamourer.State;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Extensions;
using OtterGui.Text;

View file

@ -1,5 +1,5 @@
using FFXIVClientStructs.FFXIV.Client.Game;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Gui.Debug;

View file

@ -3,10 +3,11 @@ using Dalamud.Interface.Utility;
using Dalamud.Plugin;
using Glamourer.Api.Enums;
using Glamourer.Api.IpcSubscribers;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Services;
using OtterGui.Text;
namespace Glamourer.Gui.Tabs.DebugTab.IpcTester;
@ -15,6 +16,7 @@ public class DesignIpcTester(IDalamudPluginInterface pluginInterface) : IUiServi
private Dictionary<Guid, string> _designs = [];
private int _gameObjectIndex;
private string _gameObjectName = string.Empty;
private string _designName = string.Empty;
private uint _key;
private ApplyFlag _flags = ApplyFlagEx.DesignDefault;
private Guid? _design;
@ -30,6 +32,7 @@ public class DesignIpcTester(IDalamudPluginInterface pluginInterface) : IUiServi
IpcTesterHelpers.IndexInput(ref _gameObjectIndex);
IpcTesterHelpers.KeyInput(ref _key);
IpcTesterHelpers.NameInput(ref _gameObjectName);
ImUtf8.InputText("##designName"u8, ref _designName, "Design Name..."u8);
ImGuiUtil.GuidInput("##identifier", "Design Identifier...", string.Empty, ref _design, ref _designText,
ImGui.GetContentRegionAvail().X);
IpcTesterHelpers.DrawFlagInput(ref _flags);
@ -54,6 +57,48 @@ public class DesignIpcTester(IDalamudPluginInterface pluginInterface) : IUiServi
IpcTesterHelpers.DrawIntro(ApplyDesignName.Label);
if (ImGuiUtil.DrawDisabledButton("Apply##Name", Vector2.Zero, string.Empty, !_design.HasValue))
_lastError = new ApplyDesignName(pluginInterface).Invoke(_design!.Value, _gameObjectName, _key, _flags);
IpcTesterHelpers.DrawIntro(GetExtendedDesignData.Label);
if (_design.HasValue)
{
var (display, path, color, draw) = new GetExtendedDesignData(pluginInterface).Invoke(_design.Value);
if (path.Length > 0)
ImUtf8.Text($"{display} ({path}){(draw ? " in QDB"u8 : ""u8)}", color);
else
ImUtf8.Text("No Data"u8);
}
else
{
ImUtf8.Text("No Data"u8);
}
IpcTesterHelpers.DrawIntro(GetDesignBase64.Label);
if (ImUtf8.Button("To Clipboard##Base64"u8) && _design.HasValue)
{
var data = new GetDesignBase64(pluginInterface).Invoke(_design.Value);
ImUtf8.SetClipboardText(data);
}
IpcTesterHelpers.DrawIntro(AddDesign.Label);
if (ImUtf8.Button("Add from Clipboard"u8))
try
{
var data = ImUtf8.GetClipboardText();
_lastError = new AddDesign(pluginInterface).Invoke(data, _designName, out var newDesign);
if (_lastError is GlamourerApiEc.Success)
{
_design = newDesign;
_designText = newDesign.ToString();
}
}
catch
{
_lastError = GlamourerApiEc.UnknownError;
}
IpcTesterHelpers.DrawIntro(DeleteDesign.Label);
if (ImUtf8.Button("Delete##Design"u8) && _design.HasValue)
_lastError = new DeleteDesign(pluginInterface).Invoke(_design.Value);
}
private void DrawDesignsPopup()

View file

@ -1,6 +1,6 @@
using Glamourer.Api.Enums;
using Glamourer.Designs;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using static Penumbra.GameData.Files.ShpkFile;

View file

@ -1,7 +1,7 @@
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using Glamourer.Api.IpcSubscribers;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using Penumbra.GameData.Gui.Debug;
namespace Glamourer.Gui.Tabs.DebugTab.IpcTester;

View file

@ -1,7 +1,7 @@
using Dalamud.Plugin;
using Glamourer.Api.Enums;
using Glamourer.Api.IpcSubscribers;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Services;

View file

@ -5,7 +5,7 @@ using Glamourer.Api.Enums;
using Glamourer.Api.Helpers;
using Glamourer.Api.IpcSubscribers;
using Glamourer.Designs;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OtterGui;

View file

@ -1,7 +1,7 @@
using Dalamud.Interface.Utility;
using Glamourer.Services;
using Glamourer.Unlocks;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Enums;

View file

@ -2,7 +2,7 @@
using Glamourer.GameData;
using Glamourer.Interop;
using Glamourer.Interop.Structs;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
@ -17,6 +17,7 @@ namespace Glamourer.Gui.Tabs.DebugTab;
public unsafe class ModelEvaluationPanel(
ActorObjectManager _objectManager,
VisorService _visorService,
VieraEarService _vieraEarService,
UpdateSlotService _updateSlotService,
ChangeCustomizeService _changeCustomizeService,
CrestService _crestService,
@ -45,9 +46,10 @@ public unsafe class ModelEvaluationPanel(
ImGuiUtil.DrawTableColumn("Address");
ImGui.TableNextColumn();
ImGuiUtil.CopyOnClickSelectable(actor.ToString());
Glamourer.Dynamis.DrawPointer(actor);
ImGui.TableNextColumn();
ImGuiUtil.CopyOnClickSelectable(model.ToString());
Glamourer.Dynamis.DrawPointer(model);
ImGui.TableNextColumn();
if (actor.IsCharacter)
{
@ -83,6 +85,7 @@ public unsafe class ModelEvaluationPanel(
ImGuiUtil.CopyOnClickSelectable(offhand.ToString());
DrawVisor(actor, model);
DrawVieraEars(actor, model);
DrawHatState(actor, model);
DrawWeaponState(actor, model);
DrawWetness(actor, model);
@ -134,6 +137,26 @@ public unsafe class ModelEvaluationPanel(
_visorService.SetVisorState(model, !VisorService.GetVisorState(model));
}
private void DrawVieraEars(Actor actor, Model model)
{
using var id = ImRaii.PushId("Viera Ears");
ImGuiUtil.DrawTableColumn("Viera Ears");
ImGuiUtil.DrawTableColumn(actor.IsCharacter ? actor.ShowVieraEars.ToString() : "No Character");
ImGuiUtil.DrawTableColumn(model.IsHuman ? model.VieraEarsVisible.ToString() : "No Human");
ImGui.TableNextColumn();
if (!model.IsHuman)
return;
if (ImGui.SmallButton("Set True"))
_vieraEarService.SetVieraEarState(model, true);
ImGui.SameLine();
if (ImGui.SmallButton("Set False"))
_vieraEarService.SetVieraEarState(model, false);
ImGui.SameLine();
if (ImGui.SmallButton("Toggle"))
_vieraEarService.SetVieraEarState(model, !model.VieraEarsVisible);
}
private void DrawHatState(Actor actor, Model model)
{
using var id = ImRaii.PushId("HatState");

View file

@ -4,7 +4,7 @@ using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Glamourer.Designs;
using Glamourer.GameData;
using Glamourer.State;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.GameData.Enums;

View file

@ -1,4 +1,4 @@
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Text;
using Penumbra.GameData.Actors;

View file

@ -1,6 +1,6 @@
using Dalamud.Interface.Utility;
using Glamourer.Interop.Penumbra;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;
using Penumbra.Api.Enums;
@ -49,7 +49,7 @@ public unsafe class PenumbraPanel(PenumbraService _penumbra, PenumbraChangedItem
ImGui.TableNextColumn();
var address = _drawObject.Address;
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
if (ImGui.InputScalar("##drawObjectPtr", ImGuiDataType.U64, (nint)(&address), nint.Zero, nint.Zero, "%llx",
if (ImGui.InputScalar("##drawObjectPtr", ImGuiDataType.U64, ref address, nint.Zero, nint.Zero, "%llx",
ImGuiInputTextFlags.CharsHexadecimal))
_drawObject = address;
ImGuiUtil.DrawTableColumn(_penumbra.Available
@ -61,7 +61,7 @@ public unsafe class PenumbraPanel(PenumbraService _penumbra, PenumbraChangedItem
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
ImGui.InputInt("##CutsceneIndex", ref _gameObjectIndex, 0, 0);
ImGuiUtil.DrawTableColumn(_penumbra.Available
? _penumbra.CutsceneParent((ushort) _gameObjectIndex).ToString()
? _penumbra.CutsceneParent((ushort)_gameObjectIndex).ToString()
: "Penumbra Unavailable");
ImGuiUtil.DrawTableColumn("Redraw Object");
@ -76,7 +76,9 @@ public unsafe class PenumbraPanel(PenumbraService _penumbra, PenumbraChangedItem
}
ImGuiUtil.DrawTableColumn("Last Tooltip Date");
ImGuiUtil.DrawTableColumn(_penumbraTooltip.LastTooltip > DateTime.MinValue ? $"{_penumbraTooltip.LastTooltip.ToLongTimeString()} ({_penumbraTooltip.LastType} {_penumbraTooltip.LastId})" : "Never");
ImGuiUtil.DrawTableColumn(_penumbraTooltip.LastTooltip > DateTime.MinValue
? $"{_penumbraTooltip.LastTooltip.ToLongTimeString()} ({_penumbraTooltip.LastType} {_penumbraTooltip.LastId})"
: "Never");
ImGui.TableNextColumn();
ImGuiUtil.DrawTableColumn("Last Click Date");
@ -87,7 +89,13 @@ public unsafe class PenumbraPanel(PenumbraService _penumbra, PenumbraChangedItem
ImGui.Separator();
foreach (var (slot, item) in _penumbraTooltip.LastItems)
{
ImGuiUtil.DrawTableColumn($"{slot.ToName()} Revert-Item");
switch (slot)
{
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");
ImGui.TableNextColumn();
}

View file

@ -1,7 +1,7 @@
using Dalamud.Interface.Utility;
using Glamourer.Services;
using Glamourer.Unlocks;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Enums;

View file

@ -1,7 +1,6 @@
using Glamourer.Designs;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Extensions;
using OtterGui.Raii;
using OtterGui.Widgets;
@ -13,13 +12,6 @@ public sealed class DesignColorCombo(DesignColors _designColors, bool _skipAutom
: _designColors.Keys.OrderBy(k => k).Prepend(DesignColors.AutomaticName),
MouseWheelType.Control, Glamourer.Log)
{
protected override void OnMouseWheel(string preview, ref int current, int steps)
{
if (CurrentSelectionIdx < 0)
CurrentSelectionIdx = Items.IndexOf(preview);
base.OnMouseWheel(preview, ref current, steps);
}
protected override bool DrawSelectable(int globalIdx, bool selected)
{
var isAutomatic = !_skipAutomatic && globalIdx == 0;

View file

@ -2,7 +2,7 @@
using Dalamud.Interface.ImGuiNotification;
using Glamourer.Designs;
using Glamourer.Services;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;
@ -189,10 +189,7 @@ public class DesignDetailTab
else if (_selector.Selected!.Color.Length != 0)
{
ImGui.SameLine();
var size = new Vector2(ImGui.GetFrameHeight());
using var font = ImRaii.PushFont(UiBuilder.IconFont);
ImGuiUtil.DrawTextButton(FontAwesomeIcon.ExclamationCircle.ToIconString(), size, 0, _colors.MissingColor);
ImUtf8.HoverTooltip("The color associated with this design does not exist."u8);
ImUtf8.Icon(FontAwesomeIcon.ExclamationCircle, "The color associated with this design does not exist."u8, _colors.MissingColor);
}
ImUtf8.DrawFrameColumn("Creation Date"u8);

View file

@ -5,13 +5,14 @@ using Glamourer.Designs;
using Glamourer.Designs.History;
using Glamourer.Events;
using Glamourer.Services;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Filesystem;
using OtterGui.FileSystem.Selector;
using OtterGui.Log;
using OtterGui.Raii;
using OtterGui.Text;
namespace Glamourer.Gui.Tabs.DesignTab;
@ -45,6 +46,29 @@ public sealed class DesignFileSystemSelector : FileSystemSelector<Design, Design
public record struct DesignState(uint Color)
{ }
protected override float CurrentWidth
=> _config.Ephemeral.CurrentDesignSelectorWidth * ImUtf8.GlobalScale;
protected override float MinimumAbsoluteRemainder
=> 470 * ImUtf8.GlobalScale;
protected override float MinimumScaling
=> _config.Ephemeral.DesignSelectorMinimumScale;
protected override float MaximumScaling
=> _config.Ephemeral.DesignSelectorMaximumScale;
protected override void SetSize(Vector2 size)
{
base.SetSize(size);
var adaptedSize = MathF.Round(size.X / ImUtf8.GlobalScale);
if (adaptedSize == _config.Ephemeral.CurrentDesignSelectorWidth)
return;
_config.Ephemeral.CurrentDesignSelectorWidth = adaptedSize;
_config.Ephemeral.Save();
}
public DesignFileSystemSelector(DesignManager designManager, DesignFileSystem fileSystem, IKeyState keyState, DesignChanged @event,
Configuration config, DesignConverter converter, TabSelected selectionEvent, Logger log, DesignColors designColors,
DesignApplier designApplier)
@ -151,7 +175,7 @@ public sealed class DesignFileSystemSelector : FileSystemSelector<Design, Design
var flag = selected ? ImGuiTreeNodeFlags.Selected | LeafFlags : LeafFlags;
var name = IncognitoMode ? leaf.Value.Incognito : leaf.Value.Name.Text;
using var color = ImRaii.PushColor(ImGuiCol.Text, state.Color);
using var _ = ImRaii.TreeNode(name, flag);
using var _ = ImUtf8.TreeNode(name, flag);
if (_config.AllowDoubleClickToApply && ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(ImGuiMouseButton.Left))
_designApplier.ApplyToPlayer(leaf.Value);
}

View file

@ -3,7 +3,7 @@ using Dalamud.Interface.Utility;
using Glamourer.Automation;
using Glamourer.Designs;
using Glamourer.Designs.Links;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Services;
@ -195,7 +195,7 @@ public class DesignLinkDrawer(
{
if (source)
{
ImGui.SetDragDropPayload("DraggingLink", IntPtr.Zero, 0);
ImGui.SetDragDropPayload("DraggingLink", null, 0);
ImGui.TextUnformatted($"Reordering {design.Name}...");
_dragDropIndex = index;
_dragDropOrder = order;

View file

@ -12,7 +12,7 @@ using Glamourer.Gui.Equipment;
using Glamourer.Gui.Materials;
using Glamourer.Interop;
using Glamourer.State;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;
@ -130,6 +130,7 @@ public class DesignPanel
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
DrawEquipmentMetaToggles();
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
_equipmentDrawer.DrawDragDropTooltip();
}
private void DrawEquipmentMetaToggles()
@ -153,6 +154,12 @@ public class DesignPanel
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.WeaponState, _manager, _selector.Selected!));
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.OffHand, _manager, _selector.Selected!));
}
ImGui.SameLine();
using (var _ = ImRaii.Group())
{
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.EarState, _manager, _selector.Selected!));
}
}
private void DrawCustomize()
@ -264,11 +271,9 @@ public class DesignPanel
DrawCrestApplication();
ImUtf8.IconDummy();
DrawMetaApplication();
ImUtf8.IconDummy();
DrawBonusSlotApplication();
}
ImGui.SameLine(ImGui.GetContentRegionAvail().X / 2);
ImGui.SameLine(210 * ImUtf8.GlobalScale + ImGui.GetStyle().ItemSpacing.X);
using (var _ = ImRaii.Group())
{
void ApplyEquip(string label, EquipFlag allFlags, bool stain, IEnumerable<EquipSlot> slots)
@ -310,6 +315,9 @@ public class DesignPanel
ImUtf8.IconDummy();
DrawParameterApplication();
ImUtf8.IconDummy();
DrawBonusSlotApplication();
}
}
@ -318,7 +326,7 @@ public class DesignPanel
var enabled = _config.DeleteDesignModifier.IsActive();
bool? equip = null;
bool? customize = null;
var size = new Vector2(200 * ImUtf8.GlobalScale, 0);
var size = new Vector2(210 * ImUtf8.GlobalScale, 0);
if (ImUtf8.ButtonEx("Disable Everything"u8,
"Disable application of everything, including any existing advanced dyes, advanced customizations, crests and wetness."u8, size,
!enabled))
@ -396,6 +404,7 @@ public class DesignPanel
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.HatState, equip.Value);
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.VisorState, equip.Value);
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.WeaponState, equip.Value);
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.EarState, equip.Value);
}
if (customize.HasValue)
@ -408,6 +417,7 @@ public class DesignPanel
"Apply Hat Visibility",
"Apply Visor State",
"Apply Weapon Visibility",
"Apply Viera Ear Visibility",
];
private void DrawMetaApplication()

View file

@ -2,7 +2,7 @@
using Dalamud.Interface.Utility;
using Glamourer.Designs;
using Glamourer.Interop;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui.Classes;
using OtterGui.Widgets;
@ -16,7 +16,7 @@ public class DesignTab(DesignFileSystemSelector _selector, DesignPanel _panel, I
public void DrawContent()
{
_selector.Draw(GetDesignSelectorSize());
_selector.Draw();
if (_importService.CreateCharaTarget(out var designBase, out var name))
{
var newDesign = _manager.CreateClone(designBase, name, true);
@ -27,7 +27,4 @@ public class DesignTab(DesignFileSystemSelector _selector, DesignPanel _panel, I
_panel.Draw();
_importService.CreateCharaSource();
}
public float GetDesignSelectorSize()
=> 200f * ImGuiHelpers.GlobalScale;
}

View file

@ -5,7 +5,7 @@ using Dalamud.Utility;
using Glamourer.Designs;
using Glamourer.Interop.Penumbra;
using Glamourer.State;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Extensions;
@ -71,6 +71,8 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelect
private void DrawApplyAllButton()
{
var (id, name) = penumbra.CurrentCollection;
if (config.Ephemeral.IncognitoMode)
name = id.ShortGuid();
if (ImGuiUtil.DrawDisabledButton($"Try Applying All Associated Mods to {name}##applyAll",
new Vector2(ImGui.GetContentRegionAvail().X, 0), string.Empty, id == Guid.Empty))
ApplyAll();

View file

@ -1,6 +1,6 @@
using Dalamud.Interface.Utility;
using Glamourer.Interop.Penumbra;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui.Classes;
using OtterGui.Log;
using OtterGui.Raii;

View file

@ -2,8 +2,7 @@
using Dalamud.Interface.Utility;
using Glamourer.Designs;
using Glamourer.Interop.Material;
using ImGuiNET;
using OtterGui;
using Dalamud.Bindings.ImGui;
using OtterGui.Extensions;
using OtterGui.Raii;
using OtterGui.Text;
@ -11,7 +10,11 @@ using static Glamourer.Gui.Tabs.HeaderDrawer;
namespace Glamourer.Gui.Tabs.DesignTab;
public class MultiDesignPanel(DesignFileSystemSelector selector, DesignManager editor, DesignColors colors, Configuration config)
public class MultiDesignPanel(
DesignFileSystemSelector selector,
DesignManager editor,
DesignColors colors,
Configuration config)
{
private readonly Button[] _leftButtons = [];
private readonly Button[] _rightButtons = [new IncognitoButton(config)];
@ -201,16 +204,21 @@ public class MultiDesignPanel(DesignFileSystemSelector selector, DesignManager e
? $"All {_numDesigns} selected designs are already displayed in the quick design bar."
: $"Display all {_numDesigns} selected designs in the quick design bar. Changes {diff} designs.";
if (ImUtf8.ButtonEx("Display Selected Designs in QDB"u8, tt, buttonWidth, diff == 0))
{
foreach (var design in selector.SelectedPaths.OfType<DesignFileSystem.Leaf>())
editor.SetQuickDesign(design.Value, true);
}
ImGui.SameLine();
tt = _numQuickDesignEnabled == 0
? $"All {_numDesigns} selected designs are already hidden in the quick design bar."
: $"Hide all {_numDesigns} selected designs in the quick design bar. Changes {_numQuickDesignEnabled} designs.";
if (ImUtf8.ButtonEx("Hide Selected Designs in QDB"u8, tt, buttonWidth, _numQuickDesignEnabled == 0))
{
foreach (var design in selector.SelectedPaths.OfType<DesignFileSystem.Leaf>())
editor.SetQuickDesign(design.Value, false);
}
ImGui.Separator();
}
@ -327,8 +335,10 @@ public class MultiDesignPanel(DesignFileSystemSelector selector, DesignManager e
: $"Set the color of {_addDesigns.Count} designs to \"{_colorCombo.CurrentSelection}\"\n\n\t{string.Join("\n\t", _addDesigns.Select(m => m.Name.Text))}";
ImGui.SameLine();
if (ImUtf8.ButtonEx(label, tooltip, width, _addDesigns.Count == 0))
{
foreach (var design in _addDesigns)
editor.ChangeColor(design, _colorCombo.CurrentSelection!);
}
label = _removeDesigns.Count > 0
? $"Unset {_removeDesigns.Count} Designs"
@ -338,8 +348,10 @@ public class MultiDesignPanel(DesignFileSystemSelector selector, DesignManager e
: $"Set {_removeDesigns.Count} designs to use automatic color again:\n\n\t{string.Join("\n\t", _removeDesigns.Select(m => m.Item1.Name.Text))}";
ImGui.SameLine();
if (ImUtf8.ButtonEx(label, tooltip, width, _removeDesigns.Count == 0))
{
foreach (var (design, _) in _removeDesigns)
editor.ChangeColor(design, string.Empty);
}
ImGui.Separator();
}
@ -455,7 +467,8 @@ public class MultiDesignPanel(DesignFileSystemSelector selector, DesignManager e
foreach (var design in selector.SelectedPaths.OfType<DesignFileSystem.Leaf>().Select(l => l.Value))
{
editor.ChangeApplyMulti(design, equip, customize, equip, customize.HasValue && !customize.Value ? false : null, null, equip, equip, equip);
editor.ChangeApplyMulti(design, equip, customize, equip, customize.HasValue && !customize.Value ? false : null, null, equip, equip,
equip);
if (equip.HasValue)
{
editor.ChangeApplyMeta(design, MetaIndex.HatState, equip.Value);
@ -477,7 +490,7 @@ public class MultiDesignPanel(DesignFileSystemSelector selector, DesignManager e
foreach (var leaf in selector.SelectedPaths.OfType<DesignFileSystem.Leaf>())
{
var index = leaf.Value.Tags.IndexOf(_tag);
var index = leaf.Value.Tags.AsEnumerable().IndexOf(_tag);
if (index >= 0)
_removeDesigns.Add((leaf.Value, index));
else

View file

@ -1,6 +1,6 @@
using Dalamud.Interface;
using Dalamud.Interface.Utility;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii;

View file

@ -2,7 +2,7 @@
using Glamourer.Designs;
using Glamourer.GameData;
using Glamourer.Services;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

View file

@ -6,7 +6,7 @@ using Glamourer.Gui.Customization;
using Glamourer.Gui.Equipment;
using Glamourer.Gui.Tabs.DesignTab;
using Glamourer.State;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;

View file

@ -1,5 +1,5 @@
using Glamourer.GameData;
using ImGuiNET;
using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Extensions;
using OtterGui.Raii;

Some files were not shown because too many files have changed in this diff Show more