Compare commits

...

145 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
Ottermandias
b53124e708 Temporarily use custom address for ReadStainingTemplate. 2025-05-01 23:02:08 +02:00
Ottermandias
9a684c9ff5 Implement GetDesignListExtended. 2025-04-29 23:50:29 +02:00
Ottermandias
c7d1620c1e Update GameData. 2025-04-19 23:09:48 +02:00
Ottermandias
325b54031c Update libraries. 2025-04-19 22:28:06 +02:00
Caraxi
155a9d6266 Fix SetMetaState and SetMetaStateName not being registered 2025-04-15 14:26:30 +09:30
Actions User
4f6fb44f79 [CI] Updating repo.json for 1.3.8.5 2025-04-10 14:42:24 +00:00
Ottermandias
bfce99859f Update GameData. 2025-04-10 16:39:04 +02:00
Ottermandias
6c556d6a61 Update API. 2025-04-10 16:20:15 +02:00
Ottermandias
7ed42005dd Force higher Penumbra API version and use better IPC for cutscene parents and game objects. 2025-04-09 15:11:04 +02:00
Ottermandias
aad978f5f6 Pass actor in GearsetDataLoaded instead of looking it up. 2025-04-08 22:53:59 +02:00
Ottermandias
c0ad4aab51 Update for new ActorObjectManager. 2025-04-05 18:48:39 +02:00
Ottermandias
8fe0ac8195 Fix weapon color set issue. 2025-04-05 15:12:27 +02:00
Ottermandias
46f8818cee Add Incognito Modifier. 2025-04-04 22:35:48 +02:00
Actions User
118f51cc64 [CI] Updating repo.json for 1.3.8.4 2025-04-02 23:07:10 +00:00
Ottermandias
096d82741d Fix previous fix for weapons. 2025-04-03 01:04:58 +02:00
Actions User
b98cb31fd2 [CI] Updating repo.json for 1.3.8.3 2025-04-02 21:47:24 +00:00
Ottermandias
90813ce030 Update GameData. 2025-04-02 23:45:28 +02:00
Ottermandias
95bc52b2bc Check for valid humanity. 2025-04-02 23:45:07 +02:00
Ottermandias
d79e4b5853
Merge pull request #108 from keifufu/fix-linux-build
Fix linux build
2025-03-31 13:03:43 +02:00
keifufu
a40a6905be
newline be gone 2025-03-31 13:02:19 +02:00
keifufu
6b3a64ce14
fix linux build 2025-03-31 13:00:51 +02:00
Actions User
4fca1600ca [CI] Updating repo.json for 1.3.8.2 2025-03-29 17:06:25 +00:00
Ottermandias
b1e65e6f9d Fix some offsets. 2025-03-29 18:04:18 +01:00
Actions User
381b23fe0c [CI] Updating repo.json for 1.3.8.1 2025-03-28 16:29:25 +00:00
Ottermandias
782c4446b2 Update GameData. 2025-03-28 17:25:34 +01:00
Ottermandias
76eaa75d04 Update Penumbra.Api. 2025-03-28 17:23:25 +01:00
Ottermandias
361ed536a3 Fix issue with NPC automation due to missing job detection. 2025-03-28 17:22:57 +01:00
Ottermandias
b1d00e9812 Update GameData. 2025-03-28 15:54:26 +01:00
Ottermandias
b0abf865cb Change build step. 2025-03-28 15:54:10 +01:00
Ottermandias
d398381b52 Revert Dalamud staging on release, and update api level. 2025-03-28 14:17:16 +01:00
Actions User
d75d70bee5 [CI] Updating repo.json for 1.3.8.0 2025-03-28 13:16:18 +00:00
Ottermandias
296f1e90b5 🤷 2025-03-28 14:13:34 +01:00
Ottermandias
9d3dfbbece use staging build for release for now. 2025-03-28 14:05:53 +01:00
Ottermandias
d6d592f099 1.3.8.0 2025-03-28 13:58:19 +01:00
Ottermandias
00cb1b6643 Use CS sig. 2025-03-28 13:52:15 +01:00
Ottermandias
22babad789 Update. 2025-03-27 18:11:08 +01:00
Ottermandias
18ff905746 Add a chat command to clear temporary settings made by Glamourer. 2025-03-11 18:06:22 +01:00
Ottermandias
fd0d761b92 Fix small issue with invisible customizations applying. 2025-03-11 00:58:05 +01:00
Actions User
750d4f9eca [CI] Updating repo.json for 1.3.7.1 2025-03-10 22:59:41 +00:00
Ottermandias
25517525c9 Keep temporary mod settings manually applied by Glamourer when redrawing with automation changes. 2025-03-10 23:55:29 +01:00
Actions User
99a8e1e8c5 [CI] Updating repo.json for 1.3.7.0 2025-03-09 22:17:20 +00:00
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
165 changed files with 2422 additions and 1221 deletions

View file

@ -15,12 +15,12 @@ jobs:
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v1
with: with:
dotnet-version: '8.x.x' dotnet-version: '9.x.x'
- name: Restore dependencies - name: Restore dependencies
run: dotnet restore run: dotnet restore
- name: Download Dalamud - name: Download Dalamud
run: | 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" Expand-Archive -Force latest.zip "$env:AppData\XIVLauncher\addon\Hooks\dev"
- name: Build - name: Build
run: | run: |

View file

@ -15,7 +15,7 @@ jobs:
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v1
with: with:
dotnet-version: '8.x.x' dotnet-version: '9.x.x'
- name: Restore dependencies - name: Restore dependencies
run: dotnet restore run: dotnet restore
- name: Download Dalamud - name: Download Dalamud

@ -1 +1 @@
Subproject commit 9f9bdf0873899d2e45fabaca446bb1624303b418 Subproject commit 59a7ab5fa9941eb754757b62e4cb189e455e9514

View file

@ -7,6 +7,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig .editorconfig = .editorconfig
.github\workflows\release.yml = .github\workflows\release.yml .github\workflows\release.yml = .github\workflows\release.yml
Glamourer\Glamourer.json = Glamourer\Glamourer.json
repo.json = repo.json repo.json = repo.json
.github\workflows\test_release.yml = .github\workflows\test_release.yml .github\workflows\test_release.yml = .github\workflows\test_release.yml
EndProjectSection EndProjectSection
@ -29,30 +30,30 @@ Global
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{01EB903D-871F-4285-A8CF-6486561D5B5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {01EB903D-871F-4285-A8CF-6486561D5B5B}.Debug|Any CPU.ActiveCfg = Debug|x64
{01EB903D-871F-4285-A8CF-6486561D5B5B}.Debug|Any CPU.Build.0 = Debug|Any CPU {01EB903D-871F-4285-A8CF-6486561D5B5B}.Debug|Any CPU.Build.0 = Debug|x64
{01EB903D-871F-4285-A8CF-6486561D5B5B}.Release|Any CPU.ActiveCfg = Release|Any CPU {01EB903D-871F-4285-A8CF-6486561D5B5B}.Release|Any CPU.ActiveCfg = Release|x64
{01EB903D-871F-4285-A8CF-6486561D5B5B}.Release|Any CPU.Build.0 = Release|Any CPU {01EB903D-871F-4285-A8CF-6486561D5B5B}.Release|Any CPU.Build.0 = Release|x64
{29C589ED-7AF1-4DE9-82EF-33EBEF19AAFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {29C589ED-7AF1-4DE9-82EF-33EBEF19AAFA}.Debug|Any CPU.ActiveCfg = Debug|x64
{29C589ED-7AF1-4DE9-82EF-33EBEF19AAFA}.Debug|Any CPU.Build.0 = Debug|Any CPU {29C589ED-7AF1-4DE9-82EF-33EBEF19AAFA}.Debug|Any CPU.Build.0 = Debug|x64
{29C589ED-7AF1-4DE9-82EF-33EBEF19AAFA}.Release|Any CPU.ActiveCfg = Release|Any CPU {29C589ED-7AF1-4DE9-82EF-33EBEF19AAFA}.Release|Any CPU.ActiveCfg = Release|x64
{29C589ED-7AF1-4DE9-82EF-33EBEF19AAFA}.Release|Any CPU.Build.0 = Release|Any CPU {29C589ED-7AF1-4DE9-82EF-33EBEF19AAFA}.Release|Any CPU.Build.0 = Release|x64
{C0A2FAF8-C3AE-4B7B-ADDB-4AAC1A855428}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C0A2FAF8-C3AE-4B7B-ADDB-4AAC1A855428}.Debug|Any CPU.ActiveCfg = Debug|x64
{C0A2FAF8-C3AE-4B7B-ADDB-4AAC1A855428}.Debug|Any CPU.Build.0 = Debug|Any CPU {C0A2FAF8-C3AE-4B7B-ADDB-4AAC1A855428}.Debug|Any CPU.Build.0 = Debug|x64
{C0A2FAF8-C3AE-4B7B-ADDB-4AAC1A855428}.Release|Any CPU.ActiveCfg = Release|Any CPU {C0A2FAF8-C3AE-4B7B-ADDB-4AAC1A855428}.Release|Any CPU.ActiveCfg = Release|x64
{C0A2FAF8-C3AE-4B7B-ADDB-4AAC1A855428}.Release|Any CPU.Build.0 = Release|Any CPU {C0A2FAF8-C3AE-4B7B-ADDB-4AAC1A855428}.Release|Any CPU.Build.0 = Release|x64
{AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Debug|Any CPU.ActiveCfg = Debug|x64
{AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Debug|Any CPU.Build.0 = Debug|Any CPU {AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Debug|Any CPU.Build.0 = Debug|x64
{AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Release|Any CPU.ActiveCfg = Release|Any CPU {AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Release|Any CPU.ActiveCfg = Release|x64
{AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Release|Any CPU.Build.0 = Release|Any CPU {AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Release|Any CPU.Build.0 = Release|x64
{EF233CE2-F243-449E-BE05-72B9D110E419}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EF233CE2-F243-449E-BE05-72B9D110E419}.Debug|Any CPU.ActiveCfg = Debug|x64
{EF233CE2-F243-449E-BE05-72B9D110E419}.Debug|Any CPU.Build.0 = Debug|Any CPU {EF233CE2-F243-449E-BE05-72B9D110E419}.Debug|Any CPU.Build.0 = Debug|x64
{EF233CE2-F243-449E-BE05-72B9D110E419}.Release|Any CPU.ActiveCfg = Release|Any CPU {EF233CE2-F243-449E-BE05-72B9D110E419}.Release|Any CPU.ActiveCfg = Release|x64
{EF233CE2-F243-449E-BE05-72B9D110E419}.Release|Any CPU.Build.0 = Release|Any CPU {EF233CE2-F243-449E-BE05-72B9D110E419}.Release|Any CPU.Build.0 = Release|x64
{9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Debug|Any CPU.ActiveCfg = Debug|x64
{9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Debug|Any CPU.Build.0 = Debug|Any CPU {9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Debug|Any CPU.Build.0 = Debug|x64
{9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Release|Any CPU.ActiveCfg = Release|Any CPU {9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Release|Any CPU.ActiveCfg = Release|x64
{9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Release|Any CPU.Build.0 = Release|Any CPU {9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Release|Any CPU.Build.0 = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View file

@ -1,33 +1,43 @@
using Glamourer.Api.Enums; using Glamourer.Api.Enums;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.State; using Glamourer.State;
using OtterGui; using OtterGui.Extensions;
using OtterGui.Log; using OtterGui.Log;
using OtterGui.Services; using OtterGui.Services;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs;
using Penumbra.String; using Penumbra.String;
using ObjectManager = Glamourer.Interop.ObjectManager;
namespace Glamourer.Api; namespace Glamourer.Api;
public class ApiHelpers(ObjectManager objects, StateManager stateManager, ActorManager actors) : IApiService public class ApiHelpers(ActorObjectManager objects, StateManager stateManager, ActorManager actors) : IApiService
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal IEnumerable<ActorState> FindExistingStates(string actorName) internal IEnumerable<ActorState> FindExistingStates(string actorName, ushort worldId = ushort.MaxValue)
{ {
if (actorName.Length == 0 || !ByteString.FromString(actorName, out var byteString)) if (actorName.Length == 0 || !ByteString.FromString(actorName, out var byteString))
yield break; yield break;
foreach (var state in stateManager.Values.Where(state if (worldId == WorldId.AnyWorld.Id)
=> state.Identifier.Type is IdentifierType.Player && state.Identifier.PlayerName == byteString)) {
yield return state; 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)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal GlamourerApiEc FindExistingState(int objectIndex, out ActorState? state) internal GlamourerApiEc FindExistingState(int objectIndex, out ActorState? state)
{ {
var actor = objects[objectIndex]; var actor = objects.Objects[objectIndex];
var identifier = actor.GetIdentifier(actors); var identifier = actor.GetIdentifier(actors);
if (!identifier.IsValid) if (!identifier.IsValid)
{ {
@ -42,7 +52,7 @@ public class ApiHelpers(ObjectManager objects, StateManager stateManager, ActorM
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal ActorState? FindState(int objectIndex) internal ActorState? FindState(int objectIndex)
{ {
var actor = objects[objectIndex]; var actor = objects.Objects[objectIndex];
var identifier = actor.GetIdentifier(actors); var identifier = actor.GetIdentifier(actors);
if (identifier.IsValid && stateManager.GetOrCreate(identifier, actor, out var state)) if (identifier.IsValid && stateManager.GetOrCreate(identifier, actor, out var state))
return state; return state;
@ -73,10 +83,8 @@ public class ApiHelpers(ObjectManager objects, StateManager stateManager, ActorM
if (objectName.Length == 0 || !ByteString.FromString(objectName, out var byteString)) if (objectName.Length == 0 || !ByteString.FromString(objectName, out var byteString))
return []; return [];
objects.Update();
return stateManager.Values.Where(state => state.Identifier.Type is IdentifierType.Player && state.Identifier.PlayerName == byteString) return stateManager.Values.Where(state => state.Identifier.Type is IdentifierType.Player && state.Identifier.PlayerName == byteString)
.Concat(objects.Identifiers .Concat(objects
.Where(kvp => kvp.Key is { IsValid: true, Type: IdentifierType.Player } && kvp.Key.PlayerName == byteString) .Where(kvp => kvp.Key is { IsValid: true, Type: IdentifierType.Player } && kvp.Key.PlayerName == byteString)
.SelectWhere(kvp => .SelectWhere(kvp =>
{ {

View file

@ -2,15 +2,32 @@
using Glamourer.Api.Enums; using Glamourer.Api.Enums;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.State; using Glamourer.State;
using Newtonsoft.Json.Linq;
using OtterGui.Services; using OtterGui.Services;
namespace Glamourer.Api; namespace Glamourer.Api;
public class DesignsApi(ApiHelpers helpers, DesignManager designs, StateManager stateManager) : IGlamourerApiDesigns, IApiService public class DesignsApi(
ApiHelpers helpers,
DesignManager designs,
StateManager stateManager,
DesignFileSystem fileSystem,
DesignColors color,
DesignConverter converter)
: IGlamourerApiDesigns, IApiService
{ {
public Dictionary<Guid, string> GetDesignList() public Dictionary<Guid, string> GetDesignList()
=> designs.Designs.ToDictionary(d => d.Identifier, d => d.Name.Text); => designs.Designs.ToDictionary(d => d.Identifier, d => d.Name.Text);
public Dictionary<Guid, (string DisplayName, string FullPath, uint DisplayColor, bool ShownInQdb)> GetDesignListExtended()
=> 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) public GlamourerApiEc ApplyDesign(Guid designId, int objectIndex, uint key, ApplyFlag flags)
{ {
var args = ApiHelpers.Args("Design", designId, "Index", objectIndex, "Key", key, "Flags", flags); var args = ApiHelpers.Args("Design", designId, "Index", objectIndex, "Key", key, "Flags", flags);
@ -66,4 +83,56 @@ public class DesignsApi(ApiHelpers helpers, DesignManager designs, StateManager
return ApiHelpers.Return(GlamourerApiEc.Success, args); 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 class GlamourerApi(DesignsApi designs, StateApi state, ItemsApi items) : IGlamourerApi, IApiService
{ {
public const int CurrentApiVersionMajor = 1; public const int CurrentApiVersionMajor = 1;
public const int CurrentApiVersionMinor = 4; public const int CurrentApiVersionMinor = 7;
public (int Major, int Minor) ApiVersion public (int Major, int Minor) ApiVersion
=> (CurrentApiVersionMajor, CurrentApiVersionMinor); => (CurrentApiVersionMajor, CurrentApiVersionMinor);

View file

@ -24,8 +24,14 @@ public sealed class IpcProviders : IDisposable, IApiService
IpcSubscribers.ApiVersion.Provider(pi, api), IpcSubscribers.ApiVersion.Provider(pi, api),
IpcSubscribers.GetDesignList.Provider(pi, api.Designs), 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.ApplyDesign.Provider(pi, api.Designs),
IpcSubscribers.ApplyDesignName.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.SetItem.Provider(pi, api.Items),
IpcSubscribers.SetItemName.Provider(pi, api.Items), IpcSubscribers.SetItemName.Provider(pi, api.Items),
@ -36,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)), (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.SetBonusItem.Provider(pi, api.Items),
IpcSubscribers.SetBonusItemName.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.GetState.Provider(pi, api.State),
IpcSubscribers.GetStateName.Provider(pi, api.State), IpcSubscribers.GetStateName.Provider(pi, api.State),
IpcSubscribers.GetStateBase64.Provider(pi, api.State), IpcSubscribers.GetStateBase64.Provider(pi, api.State),
@ -46,6 +54,7 @@ public sealed class IpcProviders : IDisposable, IApiService
IpcSubscribers.RevertStateName.Provider(pi, api.State), IpcSubscribers.RevertStateName.Provider(pi, api.State),
IpcSubscribers.UnlockState.Provider(pi, api.State), IpcSubscribers.UnlockState.Provider(pi, api.State),
IpcSubscribers.UnlockStateName.Provider(pi, api.State), IpcSubscribers.UnlockStateName.Provider(pi, api.State),
IpcSubscribers.DeletePlayerState.Provider(pi, api.State),
IpcSubscribers.UnlockAll.Provider(pi, api.State), IpcSubscribers.UnlockAll.Provider(pi, api.State),
IpcSubscribers.RevertToAutomation.Provider(pi, api.State), IpcSubscribers.RevertToAutomation.Provider(pi, api.State),
IpcSubscribers.RevertToAutomationName.Provider(pi, api.State), IpcSubscribers.RevertToAutomationName.Provider(pi, api.State),

View file

@ -4,34 +4,31 @@ using Glamourer.Automation;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Designs.History; using Glamourer.Designs.History;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.Interop.Structs;
using Glamourer.State; using Glamourer.State;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui.Services; using OtterGui.Services;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using ObjectManager = Glamourer.Interop.ObjectManager; using Penumbra.GameData.Structs;
using StateChanged = Glamourer.Events.StateChanged; using StateChanged = Glamourer.Events.StateChanged;
namespace Glamourer.Api; namespace Glamourer.Api;
public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
{ {
private readonly ApiHelpers _helpers; private readonly ApiHelpers _helpers;
private readonly StateManager _stateManager; private readonly StateManager _stateManager;
private readonly DesignConverter _converter; private readonly DesignConverter _converter;
private readonly Configuration _config; private readonly AutoDesignApplier _autoDesigns;
private readonly AutoDesignApplier _autoDesigns; private readonly ActorObjectManager _objects;
private readonly ObjectManager _objects; private readonly StateChanged _stateChanged;
private readonly StateChanged _stateChanged; private readonly StateFinalized _stateFinalized;
private readonly StateFinalized _stateFinalized; private readonly GPoseService _gPose;
private readonly GPoseService _gPose;
public StateApi(ApiHelpers helpers, public StateApi(ApiHelpers helpers,
StateManager stateManager, StateManager stateManager,
DesignConverter converter, DesignConverter converter,
Configuration config,
AutoDesignApplier autoDesigns, AutoDesignApplier autoDesigns,
ObjectManager objects, ActorObjectManager objects,
StateChanged stateChanged, StateChanged stateChanged,
StateFinalized stateFinalized, StateFinalized stateFinalized,
GPoseService gPose) GPoseService gPose)
@ -39,7 +36,6 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
_helpers = helpers; _helpers = helpers;
_stateManager = stateManager; _stateManager = stateManager;
_converter = converter; _converter = converter;
_config = config;
_autoDesigns = autoDesigns; _autoDesigns = autoDesigns;
_objects = objects; _objects = objects;
_stateChanged = stateChanged; _stateChanged = stateChanged;
@ -204,6 +200,27 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
return ApiHelpers.Return(GlamourerApiEc.Success, args); return ApiHelpers.Return(GlamourerApiEc.Success, args);
} }
public GlamourerApiEc DeletePlayerState(string playerName, ushort worldId, uint key)
{
var args = ApiHelpers.Args("Name", playerName, "World", worldId, "Key", key);
var states = _helpers.FindExistingStates(playerName).ToList();
if (states.Count is 0)
return ApiHelpers.Return(GlamourerApiEc.NothingDone, args);
var anyLocked = false;
foreach (var state in states)
{
if (state.CanUnlock(key))
_stateManager.DeleteState(state.Identifier);
else
anyLocked = true;
}
return ApiHelpers.Return(anyLocked
? GlamourerApiEc.InvalidKey
: GlamourerApiEc.Success, args);
}
public int UnlockAll(uint key) public int UnlockAll(uint key)
=> _stateManager.Values.Count(state => state.Unlock(key)); => _stateManager.Values.Count(state => state.Unlock(key));
@ -219,7 +236,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
if (!state.CanUnlock(key)) if (!state.CanUnlock(key))
return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args); return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args);
RevertToAutomation(_objects[objectIndex], state, key, flags); RevertToAutomation(_objects.Objects[objectIndex], state, key, flags);
return ApiHelpers.Return(GlamourerApiEc.Success, args); return ApiHelpers.Return(GlamourerApiEc.Success, args);
} }
@ -272,15 +289,9 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
var source = (flags & ApplyFlag.Once) != 0 ? StateSource.IpcManual : StateSource.IpcFixed; var source = (flags & ApplyFlag.Once) != 0 ? StateSource.IpcManual : StateSource.IpcFixed;
switch (flags & (ApplyFlag.Equipment | ApplyFlag.Customization)) switch (flags & (ApplyFlag.Equipment | ApplyFlag.Customization))
{ {
case ApplyFlag.Equipment: case ApplyFlag.Equipment: _stateManager.ResetEquip(state, source, key); break;
_stateManager.ResetEquip(state, source, key); case ApplyFlag.Customization: _stateManager.ResetCustomize(state, source, key); break;
break; case ApplyFlag.Equipment | ApplyFlag.Customization: _stateManager.ResetState(state, source, key, true); break;
case ApplyFlag.Customization:
_stateManager.ResetCustomize(state, source, key);
break;
case ApplyFlag.Equipment | ApplyFlag.Customization:
_stateManager.ResetState(state, source, key);
break;
} }
ApiHelpers.Lock(state, key, flags); ApiHelpers.Lock(state, key, flags);
@ -288,7 +299,6 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
private GlamourerApiEc RevertToAutomation(ActorState state, uint key, ApplyFlag flags) private GlamourerApiEc RevertToAutomation(ActorState state, uint key, ApplyFlag flags)
{ {
_objects.Update();
if (!_objects.TryGetValue(state.Identifier, out var actors) || !actors.Valid) if (!_objects.TryGetValue(state.Identifier, out var actors) || !actors.Valid)
return GlamourerApiEc.ActorNotFound; return GlamourerApiEc.ActorNotFound;

View file

@ -38,7 +38,7 @@ public static class ApplicationTypeExtensions
var customizeFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeFlagExtensions.All : 0; var customizeFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeFlagExtensions.All : 0;
var parameterFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeParameterExtensions.All : 0; var parameterFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeParameterExtensions.All : 0;
var crestFlags = type.HasFlag(ApplicationType.GearCustomization) ? CrestExtensions.AllRelevant : 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.Weapons) ? MetaFlag.WeaponState : 0)
| (type.HasFlag(ApplicationType.Customizations) ? MetaFlag.Wetness : 0); | (type.HasFlag(ApplicationType.Customizations) ? MetaFlag.Wetness : 0);
var bonusFlags = type.HasFlag(ApplicationType.Armor) ? BonusExtensions.All : 0; var bonusFlags = type.HasFlag(ApplicationType.Armor) ? BonusExtensions.All : 0;
@ -47,7 +47,13 @@ public static class ApplicationTypeExtensions
} }
public static ApplicationCollection ApplyWhat(this ApplicationType type, IDesignStandIn designStandIn) public static ApplicationCollection ApplyWhat(this ApplicationType type, IDesignStandIn designStandIn)
=> designStandIn is not DesignBase design ? type.Collection() : type.Collection().Restrict(design.Application); {
if(designStandIn is not DesignBase design)
return type.Collection();
var ret = type.Collection().Restrict(design.Application);
ret.CustomizeRaw = ret.CustomizeRaw.FixApplication(design.CustomizeSet);
return ret;
}
public const EquipFlag WeaponFlags = EquipFlag.Mainhand | EquipFlag.Offhand; public const EquipFlag WeaponFlags = EquipFlag.Mainhand | EquipFlag.Offhand;
public const EquipFlag ArmorFlags = EquipFlag.Head | EquipFlag.Body | EquipFlag.Hands | EquipFlag.Legs | EquipFlag.Feet; public const EquipFlag ArmorFlags = EquipFlag.Head | EquipFlag.Body | EquipFlag.Hands | EquipFlag.Legs | EquipFlag.Feet;

View file

@ -11,29 +11,28 @@ using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using ObjectManager = Glamourer.Interop.ObjectManager;
namespace Glamourer.Automation; namespace Glamourer.Automation;
public sealed class AutoDesignApplier : IDisposable public sealed class AutoDesignApplier : IDisposable
{ {
private readonly Configuration _config; private readonly Configuration _config;
private readonly AutoDesignManager _manager; private readonly AutoDesignManager _manager;
private readonly StateManager _state; private readonly StateManager _state;
private readonly JobService _jobs; private readonly JobService _jobs;
private readonly EquippedGearset _equippedGearset; private readonly EquippedGearset _equippedGearset;
private readonly ActorManager _actors; private readonly ActorManager _actors;
private readonly AutomationChanged _event; private readonly AutomationChanged _event;
private readonly ObjectManager _objects; private readonly ActorObjectManager _objects;
private readonly WeaponLoading _weapons; private readonly WeaponLoading _weapons;
private readonly HumanModelList _humans; private readonly HumanModelList _humans;
private readonly DesignMerger _designMerger; private readonly DesignMerger _designMerger;
private readonly IClientState _clientState; private readonly IClientState _clientState;
private readonly JobChangeState _jobChangeState; private readonly JobChangeState _jobChangeState;
public AutoDesignApplier(Configuration config, AutoDesignManager manager, StateManager state, JobService jobs, ActorManager actors, public AutoDesignApplier(Configuration config, AutoDesignManager manager, StateManager state, JobService jobs, ActorManager actors,
AutomationChanged @event, ObjectManager objects, WeaponLoading weapons, HumanModelList humans, IClientState clientState, AutomationChanged @event, ActorObjectManager objects, WeaponLoading weapons, HumanModelList humans, IClientState clientState,
EquippedGearset equippedGearset, DesignMerger designMerger, JobChangeState jobChangeState) EquippedGearset equippedGearset, DesignMerger designMerger, JobChangeState jobChangeState)
{ {
_config = config; _config = config;
@ -154,7 +153,6 @@ public sealed class AutoDesignApplier : IDisposable
if (newSet is not { Enabled: true }) if (newSet is not { Enabled: true })
return; return;
_objects.Update();
foreach (var id in newSet.Identifiers) foreach (var id in newSet.Identifiers)
{ {
if (_objects.TryGetValue(id, out var data)) if (_objects.TryGetValue(id, out var data))

View file

@ -10,6 +10,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui; using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Extensions;
using OtterGui.Filesystem; using OtterGui.Filesystem;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;

View file

@ -8,6 +8,7 @@ using Glamourer.Services;
using Newtonsoft.Json; using Newtonsoft.Json;
using OtterGui; using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Extensions;
using OtterGui.Filesystem; using OtterGui.Filesystem;
using OtterGui.Widgets; using OtterGui.Widgets;
using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;
@ -31,6 +32,7 @@ public class DefaultDesignSettings
public bool ResetAdvancedDyes = false; public bool ResetAdvancedDyes = false;
public bool ShowQuickDesignBar = true; public bool ShowQuickDesignBar = true;
public bool ResetTemporarySettings = false; public bool ResetTemporarySettings = false;
public bool Locked = false;
} }
public class Configuration : IPluginConfiguration, ISavable public class Configuration : IPluginConfiguration, ISavable
@ -38,33 +40,37 @@ public class Configuration : IPluginConfiguration, ISavable
[JsonIgnore] [JsonIgnore]
public readonly EphemeralConfig Ephemeral; public readonly EphemeralConfig Ephemeral;
public bool UseRestrictedGearProtection { get; set; } = false; public bool AttachToPcp { get; set; } = true;
public bool OpenFoldersByDefault { get; set; } = false; public bool UseRestrictedGearProtection { get; set; } = false;
public bool AutoRedrawEquipOnChanges { get; set; } = false; public bool OpenFoldersByDefault { get; set; } = false;
public bool EnableAutoDesigns { get; set; } = true; public bool AutoRedrawEquipOnChanges { get; set; } = false;
public bool HideApplyCheckmarks { get; set; } = false; public bool EnableAutoDesigns { get; set; } = true;
public bool SmallEquip { get; set; } = false; public bool HideApplyCheckmarks { get; set; } = false;
public bool UnlockedItemMode { get; set; } = false; public bool SmallEquip { get; set; } = false;
public byte DisableFestivals { get; set; } = 1; public bool UnlockedItemMode { get; set; } = false;
public bool EnableGameContextMenu { get; set; } = true; public byte DisableFestivals { get; set; } = 1;
public bool HideWindowInCutscene { get; set; } = false; public bool EnableGameContextMenu { get; set; } = true;
public bool ShowAutomationSetEditing { get; set; } = true; public bool HideWindowInCutscene { get; set; } = false;
public bool ShowAllAutomatedApplicationRules { get; set; } = true; public bool ShowAutomationSetEditing { get; set; } = true;
public bool ShowUnlockedItemWarnings { get; set; } = true; public bool ShowAllAutomatedApplicationRules { get; set; } = true;
public bool RevertManualChangesOnZoneChange { get; set; } = false; public bool ShowUnlockedItemWarnings { get; set; } = true;
public bool ShowQuickBarInTabs { get; set; } = true; public bool RevertManualChangesOnZoneChange { get; set; } = false;
public bool OpenWindowAtStart { get; set; } = false; public bool ShowQuickBarInTabs { get; set; } = true;
public bool ShowWindowWhenUiHidden { get; set; } = false; public bool OpenWindowAtStart { get; set; } = false;
public bool KeepAdvancedDyesAttached { get; set; } = true; public bool ShowWindowWhenUiHidden { get; set; } = false;
public bool ShowPalettePlusImport { get; set; } = true; public bool KeepAdvancedDyesAttached { get; set; } = true;
public bool UseFloatForColors { get; set; } = true; public bool ShowPalettePlusImport { get; set; } = true;
public bool UseRgbForColors { get; set; } = true; public bool UseFloatForColors { get; set; } = true;
public bool ShowColorConfig { get; set; } = true; public bool UseRgbForColors { get; set; } = true;
public bool ChangeEntireItem { get; set; } = false; public bool ShowColorConfig { get; set; } = true;
public bool AlwaysApplyAssociatedMods { get; set; } = false; public bool ChangeEntireItem { get; set; } = false;
public bool UseTemporarySettings { get; set; } = true; public bool AlwaysApplyAssociatedMods { get; set; } = true;
public bool AllowDoubleClickToApply { get; set; } = false; public bool UseTemporarySettings { get; set; } = true;
public bool RespectManualOnAutomationUpdate { get; set; } = false; 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 HideDesignPanel { get; set; } = 0;
public DesignPanelFlag AutoExpandDesignPanel { get; set; } = 0; public DesignPanelFlag AutoExpandDesignPanel { get; set; } = 0;
@ -75,10 +81,11 @@ public class Configuration : IPluginConfiguration, ISavable
public RenameField ShowRename { get; set; } = RenameField.BothDataPrio; public RenameField ShowRename { get; set; } = RenameField.BothDataPrio;
public ModifiableHotkey ToggleQuickDesignBar { get; set; } = new(VirtualKey.NO_KEY); public ModifiableHotkey ToggleQuickDesignBar { get; set; } = new(VirtualKey.NO_KEY);
public DoubleModifier DeleteDesignModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift); public DoubleModifier DeleteDesignModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift);
public DoubleModifier IncognitoModifier { get; set; } = new(ModifierHotkey.Control);
public ChangeLogDisplayType ChangeLogDisplayType { get; set; } = ChangeLogDisplayType.New; public ChangeLogDisplayType ChangeLogDisplayType { get; set; } = ChangeLogDisplayType.New;
public QdbButtons QdbButtons { get; set; } = public QdbButtons QdbButtons { get; set; } =
QdbButtons.ApplyDesign | QdbButtons.RevertAll | QdbButtons.RevertAutomation | QdbButtons.RevertAdvanced; QdbButtons.ApplyDesign | QdbButtons.RevertAll | QdbButtons.RevertAutomation | QdbButtons.RevertAdvancedDyes;
[JsonConverter(typeof(SortModeConverter))] [JsonConverter(typeof(SortModeConverter))]
[JsonProperty(Order = int.MaxValue)] [JsonProperty(Order = int.MaxValue)]
@ -155,7 +162,7 @@ public class Configuration : IPluginConfiguration, ISavable
public static class Constants public static class Constants
{ {
public const int CurrentVersion = 7; public const int CurrentVersion = 8;
public static readonly ISortMode<Design>[] ValidSortModes = public static readonly ISortMode<Design>[] ValidSortModes =
[ [

View file

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

View file

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

View file

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

View file

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

View file

@ -40,7 +40,8 @@ public class DesignBase
} }
/// <summary> Used when importing .cma or .chara files. </summary> /// <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; _designData = designData;
ApplyCustomize = customizeFlags & CustomizeFlagExtensions.AllRelevant; 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[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["Hat"] = new QuadBool(_designData.IsHatVisible(), DoApplyMeta(MetaIndex.HatState)).ToJObject("Show", "Apply");
ret["Visor"] = new QuadBool(_designData.IsVisorToggled(), DoApplyMeta(MetaIndex.VisorState)).ToJObject("IsToggled", "Apply"); ret["VieraEars"] = new QuadBool(_designData.AreEarsVisible(), DoApplyMeta(MetaIndex.EarState)).ToJObject("Show", "Apply");
ret["Weapon"] = new QuadBool(_designData.IsWeaponVisible(), DoApplyMeta(MetaIndex.WeaponState)).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 else
{ {
@ -603,6 +605,10 @@ public class DesignBase
metaValue = QuadBool.FromJObject(equip["Visor"], "IsToggled", "Apply", QuadBool.NullFalse); metaValue = QuadBool.FromJObject(equip["Visor"], "IsToggled", "Apply", QuadBool.NullFalse);
design.SetApplyMeta(MetaIndex.VisorState, metaValue.Enabled); design.SetApplyMeta(MetaIndex.VisorState, metaValue.Enabled);
design._designData.SetVisor(metaValue.ForcedValue); 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; return;
void PrintWarning(string msg) void PrintWarning(string msg)

View file

@ -1,6 +1,7 @@
using Glamourer.Api.Enums; using Glamourer.Api.Enums;
using Glamourer.Services; using Glamourer.Services;
using OtterGui; using OtterGui;
using OtterGui.Extensions;
using Penumbra.GameData.DataContainers; using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;

View file

@ -3,11 +3,12 @@ using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using Glamourer.Gui; using Glamourer.Gui;
using Glamourer.Services; using Glamourer.Services;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui; using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Extensions;
namespace Glamourer.Designs; namespace Glamourer.Designs;

View file

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

View file

@ -41,11 +41,11 @@ public sealed class DesignFileSystem : FileSystem<Design>, IDisposable, ISavable
public struct CreationDate : ISortMode<Design> public struct CreationDate : ISortMode<Design>
{ {
public string Name public ReadOnlySpan<byte> Name
=> "Creation Date (Older First)"; => "Creation Date (Older First)"u8;
public string Description public ReadOnlySpan<byte> Description
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their creation date."; => "In each folder, sort all subfolders lexicographically, then sort all leaves using their creation date."u8;
public IEnumerable<IPath> GetChildren(Folder f) public IEnumerable<IPath> GetChildren(Folder f)
=> f.GetSubFolders().Cast<IPath>().Concat(f.GetLeaves().OrderBy(l => l.Value.CreationDate)); => 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 struct UpdateDate : ISortMode<Design>
{ {
public string Name public ReadOnlySpan<byte> Name
=> "Update Date (Older First)"; => "Update Date (Older First)"u8;
public string Description public ReadOnlySpan<byte> Description
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their last update date."; => "In each folder, sort all subfolders lexicographically, then sort all leaves using their last update date."u8;
public IEnumerable<IPath> GetChildren(Folder f) public IEnumerable<IPath> GetChildren(Folder f)
=> f.GetSubFolders().Cast<IPath>().Concat(f.GetLeaves().OrderBy(l => l.Value.LastEdit)); => 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 struct InverseCreationDate : ISortMode<Design>
{ {
public string Name public ReadOnlySpan<byte> Name
=> "Creation Date (Newer First)"; => "Creation Date (Newer First)"u8;
public string Description public ReadOnlySpan<byte> Description
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse creation date."; => "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse creation date."u8;
public IEnumerable<IPath> GetChildren(Folder f) public IEnumerable<IPath> GetChildren(Folder f)
=> f.GetSubFolders().Cast<IPath>().Concat(f.GetLeaves().OrderByDescending(l => l.Value.CreationDate)); => 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 struct InverseUpdateDate : ISortMode<Design>
{ {
public string Name public ReadOnlySpan<byte> Name
=> "Update Date (Newer First)"; => "Update Date (Newer First)"u8;
public string Description public ReadOnlySpan<byte> Description
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse last update date."; => "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse last update date."u8;
public IEnumerable<IPath> GetChildren(Folder f) public IEnumerable<IPath> GetChildren(Folder f)
=> f.GetSubFolders().Cast<IPath>().Concat(f.GetLeaves().OrderByDescending(l => l.Value.LastEdit)); => 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; return;
case DesignChanged.Type.Deleted: case DesignChanged.Type.Deleted:
if (FindLeaf(design, out var leaf1)) if (TryGetValue(design, out var leaf1))
Delete(leaf1); Delete(leaf1);
return; return;
case DesignChanged.Type.ReloadedAll: case DesignChanged.Type.ReloadedAll:
Reload(); Reload();
return; return;
case DesignChanged.Type.Renamed when (data as RenameTransaction?)?.Old is { } oldName: case DesignChanged.Type.Renamed when (data as RenameTransaction?)?.Old is { } oldName:
if (!FindLeaf(design, out var leaf2)) if (!TryGetValue(design, out var leaf2))
return; return;
var old = oldName.FixName(); var old = oldName.FixName();
@ -150,15 +150,6 @@ public sealed class DesignFileSystem : FileSystem<Design>, IDisposable, ISavable
? (string.Empty, false) ? (string.Empty, false)
: (DesignToIdentifier(design), true); : (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) internal static void MigrateOldPaths(SaveService saveService, Dictionary<string, string> oldPaths)
{ {
if (oldPaths.Count == 0) if (oldPaths.Count == 0)

View file

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

View file

@ -1,8 +1,8 @@
using Glamourer.Api.Enums; using Glamourer.Api.Enums;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.Interop.Structs;
using Glamourer.State; using Glamourer.State;
using OtterGui.Services; using OtterGui.Services;
using Penumbra.GameData.Interop;
namespace Glamourer.Designs.History; namespace Glamourer.Designs.History;
@ -152,7 +152,7 @@ public class EditorHistory : IDisposable, IService
{ {
if (!_stateEntries.TryGetValue(state, out var list)) if (!_stateEntries.TryGetValue(state, out var list))
{ {
list = new Queue(); list = [];
_stateEntries.Add(state, list); _stateEntries.Add(state, list);
} }
@ -163,7 +163,7 @@ public class EditorHistory : IDisposable, IService
{ {
if (!_designEntries.TryGetValue(design, out var list)) if (!_designEntries.TryGetValue(design, out var list))
{ {
list = new Queue(); list = [];
_designEntries.Add(design, list); _designEntries.Add(design, list);
} }

View file

@ -1,6 +1,6 @@
using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification;
using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Extensions;
using OtterGui.Services; using OtterGui.Services;
using Notification = OtterGui.Classes.Notification; using Notification = OtterGui.Classes.Notification;

View file

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

View file

@ -1,19 +1,33 @@
using OtterGui.Services; using OtterGui;
using OtterGui.Services;
namespace Glamourer.Designs.Special; 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) public Design? Design(IReadOnlyList<Design> localDesigns)
{ {
if (localDesigns.Count == 0) if (localDesigns.Count is 0)
return null; return null;
var idx = _rng.Next(0, localDesigns.Count); var idx = _rng.Next(0, localDesigns.Count);
Glamourer.Log.Verbose($"[Random Design] Chose design {idx + 1} out of {localDesigns.Count}: {localDesigns[idx].Incognito}."); if (localDesigns.Count is 1)
return localDesigns[idx]; {
_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() public Design? Design()
@ -24,12 +38,12 @@ public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileS
public Design? Design(IReadOnlyList<IDesignPredicate> predicates) public Design? Design(IReadOnlyList<IDesignPredicate> predicates)
{ {
if (predicates.Count == 0) return predicates.Count switch
return Design(); {
if (predicates.Count == 1) 0 => Design(),
return Design(predicates[0]); 1 => Design(predicates[0]),
_ => Design(IDesignPredicate.Get(predicates, designs, fileSystem).ToList()),
return Design(IDesignPredicate.Get(predicates, designs, fileSystem).ToList()); };
} }
public Design? Design(string restrictions) public Design? Design(string restrictions)

View file

@ -22,7 +22,7 @@ public interface IDesignPredicate
: designs; : designs;
private static (Design Design, string LowerName, string Identifier, string LowerPath) Transform(Design d, DesignFileSystem fs) 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 public static class RandomPredicate

View file

@ -20,6 +20,10 @@ public class EphemeralConfig : ISavable
public Guid SelectedQuickDesign { get; set; } = Guid.Empty; public Guid SelectedQuickDesign { get; set; } = Guid.Empty;
public int LastSeenVersion { get; set; } = GlamourerChangelog.LastChangelogVersion; 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] [JsonIgnore]
private readonly SaveService _saveService; private readonly SaveService _saveService;

View file

@ -11,7 +11,7 @@ namespace Glamourer.Events;
/// </list> /// </list>
/// </summary> /// </summary>
public sealed class GearsetDataLoaded() public sealed class GearsetDataLoaded()
: EventWrapper<Model, GearsetDataLoaded.Priority>(nameof(GearsetDataLoaded)) : EventWrapper<Actor, Model, GearsetDataLoaded.Priority>(nameof(GearsetDataLoaded))
{ {
public enum Priority public enum Priority
{ {

View file

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

View file

@ -3,6 +3,7 @@ using Glamourer.Designs.History;
using Glamourer.Interop.Structs; using Glamourer.Interop.Structs;
using Glamourer.State; using Glamourer.State;
using OtterGui.Classes; using OtterGui.Classes;
using Penumbra.GameData.Interop;
namespace Glamourer.Events; namespace Glamourer.Events;

View file

@ -2,6 +2,7 @@ using Glamourer.Api;
using Glamourer.Api.Enums; using Glamourer.Api.Enums;
using Glamourer.Interop.Structs; using Glamourer.Interop.Structs;
using OtterGui.Classes; using OtterGui.Classes;
using Penumbra.GameData.Interop;
namespace Glamourer.Events; namespace Glamourer.Events;

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"/> /// <seealso cref="State.StateListener.OnVisorChange"/>
StateListener = 0, StateListener = 0,
} }
} }

View file

@ -1,4 +1,5 @@
using OtterGui; using OtterGui;
using OtterGui.Extensions;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using Race = Penumbra.GameData.Enums.Race; using Race = Penumbra.GameData.Enums.Race;

View file

@ -6,12 +6,10 @@ using Glamourer.Gui;
using Glamourer.Interop; using Glamourer.Interop;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.State; using Glamourer.State;
using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Log; using OtterGui.Log;
using OtterGui.Services; using OtterGui.Services;
using Penumbra.GameData.Enums; using Penumbra.GameData.Interop;
using Penumbra.GameData.Files;
namespace Glamourer; namespace Glamourer;
@ -28,6 +26,7 @@ public class Glamourer : IDalamudPlugin
public static readonly Logger Log = new(); public static readonly Logger Log = new();
public static MessageService Messager { get; private set; } = null!; public static MessageService Messager { get; private set; } = null!;
public static DynamisIpc Dynamis { get; private set; } = null!;
private readonly ServiceManager _services; private readonly ServiceManager _services;
@ -37,6 +36,7 @@ public class Glamourer : IDalamudPlugin
{ {
_services = StaticServiceManager.CreateProvider(pluginInterface, Log, this); _services = StaticServiceManager.CreateProvider(pluginInterface, Log, this);
Messager = _services.GetService<MessageService>(); Messager = _services.GetService<MessageService>();
Dynamis = _services.GetService<DynamisIpc>();
_services.EnsureRequiredServices(); _services.EnsureRequiredServices();
_services.GetService<VisorService>(); _services.GetService<VisorService>();
@ -71,6 +71,7 @@ public class Glamourer : IDalamudPlugin
sb.Append($"> **`Festival Easter-Eggs: `** {config.DisableFestivals}\n"); sb.Append($"> **`Festival Easter-Eggs: `** {config.DisableFestivals}\n");
sb.Append($"> **`Apply Entire Weapon: `** {config.ChangeEntireItem}\n"); sb.Append($"> **`Apply Entire Weapon: `** {config.ChangeEntireItem}\n");
sb.Append($"> **`Apply Associated Mods:`** {config.AlwaysApplyAssociatedMods}\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($"> **`Hidden Panels: `** {config.HideDesignPanel}\n");
sb.Append($"> **`Show QDB: `** {config.Ephemeral.ShowDesignQuickBar}\n"); sb.Append($"> **`Show QDB: `** {config.Ephemeral.ShowDesignQuickBar}\n");
sb.Append($"> **`QDB Hotkey: `** {config.ToggleQuickDesignBar}\n"); sb.Append($"> **`QDB Hotkey: `** {config.ToggleQuickDesignBar}\n");
@ -82,7 +83,7 @@ public class Glamourer : IDalamudPlugin
var designManager = _services.GetService<DesignManager>(); var designManager = _services.GetService<DesignManager>();
var autoManager = _services.GetService<AutoDesignManager>(); var autoManager = _services.GetService<AutoDesignManager>();
var stateManager = _services.GetService<StateManager>(); var stateManager = _services.GetService<StateManager>();
var objectManager = _services.GetService<ObjectManager>(); var objectManager = _services.GetService<ActorObjectManager>();
var currentPlayer = objectManager.PlayerData.Identifier; var currentPlayer = objectManager.PlayerData.Identifier;
var states = stateManager.Where(kvp => objectManager.ContainsKey(kvp.Key)).ToList(); var states = stateManager.Where(kvp => objectManager.ContainsKey(kvp.Key)).ToList();

View file

@ -1,92 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Dalamud.NET.Sdk/13.1.0">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<LangVersion>preview</LangVersion>
<PlatformTarget>x64</PlatformTarget>
<RootNamespace>Glamourer</RootNamespace> <RootNamespace>Glamourer</RootNamespace>
<AssemblyName>Glamourer</AssemblyName> <AssemblyName>Glamourer</AssemblyName>
<FileVersion>9.0.0.1</FileVersion> <FileVersion>9.0.0.1</FileVersion>
<AssemblyVersion>9.0.0.1</AssemblyVersion> <AssemblyVersion>9.0.0.1</AssemblyVersion>
<Company>SoftOtter</Company>
<Product>Glamourer</Product> <Product>Glamourer</Product>
<Copyright>Copyright © 2023</Copyright> <Copyright>Copyright © 2025</Copyright>
<Deterministic>true</Deterministic>
<OutputType>Library</OutputType>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
<OutputPath>bin\$(Configuration)\</OutputPath> <OutputPath>bin\$(Configuration)\</OutputPath>
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);MSB3277</MSBuildWarningsAsMessages>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Remove="LegacyTattoo.raw" /> <None Remove="LegacyTattoo.raw" />
<None Include="Glamourer.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="LegacyTattoo.raw" /> <EmbeddedResource Include="LegacyTattoo.raw" />
</ItemGroup> </ItemGroup>
<PropertyGroup>
<DalamudLibPath>$(AppData)\XIVLauncher\addon\Hooks\dev\</DalamudLibPath>
</PropertyGroup>
<ItemGroup>
<Reference Include="Dalamud">
<HintPath>$(DalamudLibPath)Dalamud.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="FFXIVClientStructs">
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="ImGui.NET">
<HintPath>$(DalamudLibPath)ImGui.NET.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="ImGuiScene">
<HintPath>$(DalamudLibPath)ImGuiScene.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Lumina">
<HintPath>$(DalamudLibPath)Lumina.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Lumina.Excel">
<HintPath>$(DalamudLibPath)Lumina.Excel.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>$(DalamudLibPath)Newtonsoft.Json.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Glamourer.Api\Glamourer.Api.csproj" /> <ProjectReference Include="..\Glamourer.Api\Glamourer.Api.csproj" />
<ProjectReference Include="..\OtterGui\OtterGui.csproj" /> <ProjectReference Include="..\OtterGui\OtterGui.csproj" />
<ProjectReference Include="..\Penumbra.Api\Penumbra.Api.csproj" /> <ProjectReference Include="..\Penumbra.Api\Penumbra.Api.csproj" />
<ProjectReference Include="..\Penumbra.String\Penumbra.string.csproj" /> <ProjectReference Include="..\Penumbra.String\Penumbra.String.csproj" />
<ProjectReference Include="..\Penumbra.GameData\Penumbra.GameData.csproj" /> <ProjectReference Include="..\Penumbra.GameData\Penumbra.GameData.csproj" />
<PackageReference Include="Vortice.Direct3D11" Version="3.4.2-beta" /> <PackageReference Include="Vortice.Direct3D11" Version="3.4.2-beta" />
</ItemGroup> </ItemGroup>
@ -116,14 +56,4 @@
<InformationalVersion>$(GitCommitHash)</InformationalVersion> <InformationalVersion>$(GitCommitHash)</InformationalVersion>
</PropertyGroup> </PropertyGroup>
</Target> </Target>
<ItemGroup>
<None Update="Glamourer.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="if $(Configuration) == Release powershell Copy-Item -Force $(TargetDir)$(SolutionName).json -Destination $(SolutionDir)" />
</Target>
</Project> </Project>

View file

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

View file

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

View file

@ -1,16 +1,83 @@
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Glamourer.GameData; using Glamourer.GameData;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Text.EndObjects;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using System;
namespace Glamourer.Gui.Customization; namespace Glamourer.Gui.Customization;
public partial class CustomizationDrawer 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) private void DrawColorPicker(CustomizeIndex index)
{ {
@ -21,7 +88,7 @@ public partial class CustomizationDrawer
using (_ = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 2 * ImGuiHelpers.GlobalScale, current < 0)) 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); ImGui.OpenPopup(ColorPickerPopupName);
} }
@ -30,6 +97,9 @@ public partial class CustomizationDrawer
var data = _set.Data(_currentIndex, current, _customize.Face); var data = _set.Data(_currentIndex, current, _customize.Face);
UpdateValue(data.Value); UpdateValue(data.Value);
} }
DrawDragDropSource(index, custom);
DrawDragDropTarget(index);
} }
var npc = false; var npc = false;

View file

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

View file

@ -1,8 +1,9 @@
using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Interface.Textures.TextureWraps;
using Glamourer.GameData; using Glamourer.GameData;
using Glamourer.Unlocks; using Glamourer.Unlocks;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui;
using OtterGui.Extensions;
using OtterGui.Raii; using OtterGui.Raii;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
@ -34,7 +35,7 @@ public partial class CustomizationDrawer
var hasIcon = icon.TryGetWrap(out var wrap, out _); var hasIcon = icon.TryGetWrap(out var wrap, out _);
using (_ = ImRaii.Disabled(_locked || _currentIndex is CustomizeIndex.Face && _lockedRedraw)) 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); ImGui.OpenPopup(IconSelectorPopup);
} }
@ -88,7 +89,7 @@ public partial class CustomizationDrawer
: ImRaii.PushColor(ImGuiCol.Button, ColorId.FavoriteStarOn.Value(), isFavorite); : ImRaii.PushColor(ImGuiCol.Button, ColorId.FavoriteStarOn.Value(), isFavorite);
var hasIcon = icon.TryGetWrap(out var wrap, out var _); 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); UpdateValue(custom.Value);
ImGui.CloseCurrentPopup(); ImGui.CloseCurrentPopup();
@ -214,7 +215,7 @@ public partial class CustomizationDrawer
hasIcon = icon.TryGetWrap(out wrap, out _); 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)) (int)ImGui.GetStyle().FramePadding.X, Vector4.Zero, enabled ? Vector4.One : _redTint))
{ {
_customize.Set(featureIdx, enabled ? CustomizeValue.Zero : CustomizeValue.Max); _customize.Set(featureIdx, enabled ? CustomizeValue.Zero : CustomizeValue.Max);

View file

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

View file

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

View file

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

View file

@ -5,9 +5,10 @@ using Glamourer.Designs;
using Glamourer.Designs.History; using Glamourer.Designs.History;
using Glamourer.Designs.Special; using Glamourer.Designs.Special;
using Glamourer.Events; using Glamourer.Events;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Extensions;
using OtterGui.Log; using OtterGui.Log;
using OtterGui.Widgets; using OtterGui.Widgets;
@ -21,6 +22,7 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
protected readonly TabSelected TabSelected; protected readonly TabSelected TabSelected;
protected float InnerWidth; protected float InnerWidth;
private IDesignStandIn? _currentDesign; private IDesignStandIn? _currentDesign;
private bool _isCurrentSelectionDirty;
protected DesignComboBase(Func<IReadOnlyList<Tuple<IDesignStandIn, string>>> generator, Logger log, DesignChanged designChanged, protected DesignComboBase(Func<IReadOnlyList<Tuple<IDesignStandIn, string>>> generator, Logger log, DesignChanged designChanged,
TabSelected tabSelected, EphemeralConfig config, DesignColors designColors) TabSelected tabSelected, EphemeralConfig config, DesignColors designColors)
@ -83,17 +85,11 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
DrawRightAligned(quickDesign.ResolveName(false), "[Nothing]", DesignColors.MissingColor); 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) protected bool Draw(IDesignStandIn? currentDesign, string? label, float width)
{ {
_currentDesign = currentDesign; _currentDesign = currentDesign;
InnerWidth = 400 * ImGuiHelpers.GlobalScale; UpdateCurrentSelection();
InnerWidth = 400 * ImGuiHelpers.GlobalScale;
var name = label ?? "Select Design Here..."; var name = label ?? "Select Design Here...";
bool ret; bool ret;
using (_ = currentDesign != null ? ImRaii.PushColor(ImGuiCol.Text, DesignColors.GetColor(currentDesign as Design)) : null) using (_ = currentDesign != null ? ImRaii.PushColor(ImGuiCol.Text, DesignColors.GetColor(currentDesign as Design)) : null)
@ -127,37 +123,60 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
return filter.IsContained(path) || filter.IsContained(design.ResolveName(false)); 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) if (!ReferenceEquals(_currentDesign, CurrentSelection?.Item1))
{ CurrentSelectionIdx = -1;
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 (!priorState) base.OnMouseWheel(preview, ref _2, steps);
Cleanup(); }
break;
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) private void QuickSelectedDesignTooltip(IDesignStandIn? design)
@ -175,7 +194,7 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
ImGui.TextUnformatted("Currently resolving to "); ImGui.TextUnformatted("Currently resolving to ");
using var color = ImRaii.PushColor(ImGuiCol.Text, DesignColors.GetColor(linkedDesign)); using var color = ImRaii.PushColor(ImGuiCol.Text, DesignColors.GetColor(linkedDesign));
ImGui.SameLine(0, 0); ImGui.SameLine(0, 0);
ImGui.TextUnformatted(linkedDesign.Name); ImGui.TextUnformatted(linkedDesign.Name.Text);
} }
else else
{ {
@ -225,8 +244,7 @@ public abstract class DesignCombo : DesignComboBase
public sealed class QuickDesignCombo : DesignCombo public sealed class QuickDesignCombo : DesignCombo
{ {
public QuickDesignCombo(DesignManager designs, public QuickDesignCombo(DesignFileSystem fileSystem,
DesignFileSystem fileSystem,
Logger log, Logger log,
DesignChanged designChanged, DesignChanged designChanged,
TabSelected tabSelected, TabSelected tabSelected,
@ -234,9 +252,9 @@ public sealed class QuickDesignCombo : DesignCombo
DesignColors designColors) DesignColors designColors)
: base(log, designChanged, tabSelected, config, designColors, () => : base(log, designChanged, tabSelected, config, designColors, () =>
[ [
.. designs.Designs .. fileSystem
.Where(d => d.QuickDesign) .Where(kvp => kvp.Key.QuickDesign)
.Select(d => new Tuple<IDesignStandIn, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)) .Select(kvp => new Tuple<IDesignStandIn, string>(kvp.Key, kvp.Value.FullName()))
.OrderBy(d => d.Item2), .OrderBy(d => d.Item2),
]) ])
{ {
@ -277,7 +295,6 @@ public sealed class QuickDesignCombo : DesignCombo
} }
public sealed class LinkDesignCombo( public sealed class LinkDesignCombo(
DesignManager designs,
DesignFileSystem fileSystem, DesignFileSystem fileSystem,
Logger log, Logger log,
DesignChanged designChanged, DesignChanged designChanged,
@ -286,8 +303,8 @@ public sealed class LinkDesignCombo(
DesignColors designColors) DesignColors designColors)
: DesignCombo(log, designChanged, tabSelected, config, designColors, () => : DesignCombo(log, designChanged, tabSelected, config, designColors, () =>
[ [
.. designs.Designs .. fileSystem
.Select(d => new Tuple<IDesignStandIn, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)) .Select(kvp => new Tuple<IDesignStandIn, string>(kvp.Key, kvp.Value.FullName()))
.OrderBy(d => d.Item2), .OrderBy(d => d.Item2),
]); ]);
@ -301,8 +318,8 @@ public sealed class RandomDesignCombo(
DesignColors designColors) DesignColors designColors)
: DesignCombo(log, designChanged, tabSelected, config, designColors, () => : DesignCombo(log, designChanged, tabSelected, config, designColors, () =>
[ [
.. designs.Designs .. fileSystem
.Select(d => new Tuple<IDesignStandIn, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)) .Select(kvp => new Tuple<IDesignStandIn, string>(kvp.Key, kvp.Value.FullName()))
.OrderBy(d => d.Item2), .OrderBy(d => d.Item2),
]) ])
{ {
@ -328,7 +345,6 @@ public sealed class RandomDesignCombo(
} }
public sealed class SpecialDesignCombo( public sealed class SpecialDesignCombo(
DesignManager designs,
DesignFileSystem fileSystem, DesignFileSystem fileSystem,
TabSelected tabSelected, TabSelected tabSelected,
DesignColors designColors, DesignColors designColors,
@ -338,8 +354,8 @@ public sealed class SpecialDesignCombo(
EphemeralConfig config, EphemeralConfig config,
RandomDesignGenerator rng, RandomDesignGenerator rng,
QuickSelectedDesign quickSelectedDesign) QuickSelectedDesign quickSelectedDesign)
: DesignComboBase(() => designs.Designs : DesignComboBase(() => fileSystem
.Select(d => new Tuple<IDesignStandIn, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)) .Select(kvp => new Tuple<IDesignStandIn, string>(kvp.Key, kvp.Value.FullName()))
.OrderBy(d => d.Item2) .OrderBy(d => d.Item2)
.Prepend(new Tuple<IDesignStandIn, string>(new RandomDesign(rng), string.Empty)) .Prepend(new Tuple<IDesignStandIn, string>(new RandomDesign(rng), string.Empty))
.Prepend(new Tuple<IDesignStandIn, string>(quickSelectedDesign, string.Empty)) .Prepend(new Tuple<IDesignStandIn, string>(quickSelectedDesign, string.Empty))

View file

@ -6,28 +6,28 @@ using Dalamud.Interface.Windowing;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Glamourer.Automation; using Glamourer.Automation;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Interop;
using Glamourer.Interop.Penumbra; using Glamourer.Interop.Penumbra;
using Glamourer.Interop.Structs;
using Glamourer.State; using Glamourer.State;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Text; using OtterGui.Text;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Interop;
namespace Glamourer.Gui; namespace Glamourer.Gui;
[Flags] [Flags]
public enum QdbButtons public enum QdbButtons
{ {
ApplyDesign = 0x01, ApplyDesign = 0x01,
RevertAll = 0x02, RevertAll = 0x02,
RevertAutomation = 0x04, RevertAutomation = 0x04,
RevertAdvanced = 0x08, RevertAdvancedDyes = 0x08,
RevertEquip = 0x10, RevertEquip = 0x10,
RevertCustomize = 0x20, RevertCustomize = 0x20,
ReapplyAutomation = 0x40, ReapplyAutomation = 0x40,
ResetSettings = 0x80, ResetSettings = 0x80,
RevertAdvancedCustomization = 0x100,
} }
public sealed class DesignQuickBar : Window, IDisposable public sealed class DesignQuickBar : Window, IDisposable
@ -37,21 +37,21 @@ public sealed class DesignQuickBar : Window, IDisposable
? ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoDocking | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoMove ? ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoDocking | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoMove
: ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoDocking | ImGuiWindowFlags.NoFocusOnAppearing; : ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoDocking | ImGuiWindowFlags.NoFocusOnAppearing;
private readonly Configuration _config; private readonly Configuration _config;
private readonly QuickDesignCombo _designCombo; private readonly QuickDesignCombo _designCombo;
private readonly StateManager _stateManager; private readonly StateManager _stateManager;
private readonly AutoDesignApplier _autoDesignApplier; private readonly AutoDesignApplier _autoDesignApplier;
private readonly ObjectManager _objects; private readonly ActorObjectManager _objects;
private readonly PenumbraService _penumbra; private readonly PenumbraService _penumbra;
private readonly IKeyState _keyState; private readonly IKeyState _keyState;
private readonly ImRaii.Style _windowPadding = new(); private readonly ImRaii.Style _windowPadding = new();
private readonly ImRaii.Color _windowColor = new(); private readonly ImRaii.Color _windowColor = new();
private DateTime _keyboardToggle = DateTime.UnixEpoch; private DateTime _keyboardToggle = DateTime.UnixEpoch;
private int _numButtons; private int _numButtons;
private readonly StringBuilder _tooltipBuilder = new(512); private readonly StringBuilder _tooltipBuilder = new(512);
public DesignQuickBar(Configuration config, QuickDesignCombo designCombo, StateManager stateManager, IKeyState keyState, public DesignQuickBar(Configuration config, QuickDesignCombo designCombo, StateManager stateManager, IKeyState keyState,
ObjectManager objects, AutoDesignApplier autoDesignApplier, PenumbraService penumbra) ActorObjectManager objects, AutoDesignApplier autoDesignApplier, PenumbraService penumbra)
: base("Glamourer Quick Bar", ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoDocking) : base("Glamourer Quick Bar", ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoDocking)
{ {
_config = config; _config = config;
@ -64,6 +64,7 @@ public sealed class DesignQuickBar : Window, IDisposable
IsOpen = _config.Ephemeral.ShowDesignQuickBar; IsOpen = _config.Ephemeral.ShowDesignQuickBar;
DisableWindowSounds = true; DisableWindowSounds = true;
Size = Vector2.Zero; Size = Vector2.Zero;
RespectCloseHotkey = false;
} }
public void Dispose() public void Dispose()
@ -125,6 +126,7 @@ public sealed class DesignQuickBar : Window, IDisposable
DrawRevertEquipButton(buttonSize); DrawRevertEquipButton(buttonSize);
DrawRevertCustomizeButton(buttonSize); DrawRevertCustomizeButton(buttonSize);
DrawRevertAdvancedCustomization(buttonSize); DrawRevertAdvancedCustomization(buttonSize);
DrawRevertAdvancedDyes(buttonSize);
DrawRevertAutomationButton(buttonSize); DrawRevertAutomationButton(buttonSize);
DrawReapplyAutomationButton(buttonSize); DrawReapplyAutomationButton(buttonSize);
DrawResetSettingsButton(buttonSize); DrawResetSettingsButton(buttonSize);
@ -173,7 +175,7 @@ public sealed class DesignQuickBar : Window, IDisposable
available |= 2; available |= 2;
_tooltipBuilder.Append("Right-Click: Apply ") _tooltipBuilder.Append("Right-Click: Apply ")
.Append(design.ResolveName(_config.Ephemeral.IncognitoMode)) .Append(design.ResolveName(_config.Ephemeral.IncognitoMode))
.Append(" to {_targetIdentifier}."); .Append(" to ").Append(_config.Ephemeral.IncognitoMode ? _targetIdentifier.Incognito(null) : _targetIdentifier.ToName());
} }
if (available == 0) if (available == 0)
@ -222,7 +224,8 @@ public sealed class DesignQuickBar : Window, IDisposable
} }
if (available == 0) if (available == 0)
_tooltipBuilder.Append("Neither player character nor target are available, have state modified by Glamourer, or their state is locked."); _tooltipBuilder.Append(
"Neither player character nor target are available, have state modified by Glamourer, or their state is locked.");
var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.UndoAlt, buttonSize, available); var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.UndoAlt, buttonSize, available);
ImGui.SameLine(); ImGui.SameLine();
@ -258,9 +261,10 @@ public sealed class DesignQuickBar : Window, IDisposable
} }
if (available == 0) if (available == 0)
_tooltipBuilder.Append("Neither player character nor target are available, have state modified by Glamourer, or their state is locked."); _tooltipBuilder.Append(
"Neither player character nor target are available, have state modified by Glamourer, or their state is locked.");
var (clicked, id, data, state) = ResolveTarget(FontAwesomeIcon.SyncAlt, buttonSize, available); var (clicked, id, data, state) = ResolveTarget(FontAwesomeIcon.SyncAlt, buttonSize, available);
ImGui.SameLine(); ImGui.SameLine();
if (!clicked) if (!clicked)
return; return;
@ -300,7 +304,8 @@ public sealed class DesignQuickBar : Window, IDisposable
} }
if (available == 0) if (available == 0)
_tooltipBuilder.Append("Neither player character nor target are available, have state modified by Glamourer, or their state is locked."); _tooltipBuilder.Append(
"Neither player character nor target are available, have state modified by Glamourer, or their state is locked.");
var (clicked, id, data, state) = ResolveTarget(FontAwesomeIcon.Repeat, buttonSize, available); var (clicked, id, data, state) = ResolveTarget(FontAwesomeIcon.Repeat, buttonSize, available);
ImGui.SameLine(); ImGui.SameLine();
@ -316,7 +321,7 @@ public sealed class DesignQuickBar : Window, IDisposable
private void DrawRevertAdvancedCustomization(Vector2 buttonSize) private void DrawRevertAdvancedCustomization(Vector2 buttonSize)
{ {
if (!_config.QdbButtons.HasFlag(QdbButtons.RevertAdvanced)) if (!_config.QdbButtons.HasFlag(QdbButtons.RevertAdvancedCustomization))
return; return;
var available = 0; var available = 0;
@ -325,7 +330,7 @@ public sealed class DesignQuickBar : Window, IDisposable
if (_playerIdentifier.IsValid && _playerState is { IsLocked: false } && _playerData.Valid) if (_playerIdentifier.IsValid && _playerState is { IsLocked: false } && _playerData.Valid)
{ {
available |= 1; 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) if (_targetIdentifier.IsValid && _targetState is { IsLocked: false } && _targetData.Valid)
@ -333,7 +338,40 @@ public sealed class DesignQuickBar : Window, IDisposable
if (available != 0) if (available != 0)
_tooltipBuilder.Append('\n'); _tooltipBuilder.Append('\n');
available |= 2; 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(_targetIdentifier)
.Append(" to their game state."); .Append(" to their game state.");
} }
@ -344,7 +382,7 @@ public sealed class DesignQuickBar : Window, IDisposable
var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.Palette, buttonSize, available); var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.Palette, buttonSize, available);
ImGui.SameLine(); ImGui.SameLine();
if (clicked) if (clicked)
_stateManager.ResetAdvancedState(state!, StateSource.Manual); _stateManager.ResetAdvancedDyes(state!, StateSource.Manual);
} }
private void DrawRevertCustomizeButton(Vector2 buttonSize) private void DrawRevertCustomizeButton(Vector2 buttonSize)
@ -424,7 +462,9 @@ public sealed class DesignQuickBar : Window, IDisposable
if (_playerIdentifier.IsValid && _playerData.Valid) if (_playerIdentifier.IsValid && _playerData.Valid)
{ {
available |= 1; available |= 1;
_tooltipBuilder.Append("Left-Click: Reset all temporary settings applied by Glamourer (manually or through automation) to the collection affecting ") _tooltipBuilder
.Append(
"Left-Click: Reset all temporary settings applied by Glamourer (manually or through automation) to the collection affecting ")
.Append(_playerIdentifier) .Append(_playerIdentifier)
.Append('.'); .Append('.');
} }
@ -434,7 +474,9 @@ public sealed class DesignQuickBar : Window, IDisposable
if (available != 0) if (available != 0)
_tooltipBuilder.Append('\n'); _tooltipBuilder.Append('\n');
available |= 2; available |= 2;
_tooltipBuilder.Append("Right-Click: Reset all temporary settings applied by Glamourer (manually or through automation) to the collection affecting ") _tooltipBuilder
.Append(
"Right-Click: Reset all temporary settings applied by Glamourer (manually or through automation) to the collection affecting ")
.Append(_targetIdentifier) .Append(_targetIdentifier)
.Append('.'); .Append('.');
} }
@ -495,7 +537,9 @@ public sealed class DesignQuickBar : Window, IDisposable
++_numButtons; ++_numButtons;
} }
if (_config.QdbButtons.HasFlag(QdbButtons.RevertAdvanced)) if (_config.QdbButtons.HasFlag(QdbButtons.RevertAdvancedCustomization))
++_numButtons;
if (_config.QdbButtons.HasFlag(QdbButtons.RevertAdvancedDyes))
++_numButtons; ++_numButtons;
if (_config.QdbButtons.HasFlag(QdbButtons.RevertCustomize)) if (_config.QdbButtons.HasFlag(QdbButtons.RevertCustomize))
++_numButtons; ++_numButtons;

View file

@ -1,10 +1,11 @@
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.Unlocks; using Glamourer.Unlocks;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using Lumina.Excel.Sheets; using Lumina.Excel.Sheets;
using OtterGui; using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Extensions;
using OtterGui.Log; using OtterGui.Log;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Widgets; using OtterGui.Widgets;

View file

@ -27,6 +27,9 @@ public struct EquipDrawData(EquipSlot slot, in DesignData designData)
public readonly void SetStains(StainIds stains) public readonly void SetStains(StainIds stains)
=> _editor.ChangeStains(_object, Slot, stains, ApplySettings.Manual); => _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) public readonly void SetApplyItem(bool value)
{ {
var manager = (DesignManager)_editor; 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,15 +3,15 @@ using Dalamud.Interface.Utility;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.Gui.Materials; using Glamourer.Gui.Materials;
using Glamourer.Interop.Material;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.Unlocks; using Glamourer.Unlocks;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui.Extensions;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text; using OtterGui.Text;
using OtterGui.Text.EndObjects; using OtterGui.Text.EndObjects;
using OtterGui.Widgets; using OtterGui.Widgets;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers; using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
@ -32,20 +32,24 @@ public class EquipmentDrawer
private readonly Configuration _config; private readonly Configuration _config;
private readonly GPoseService _gPose; private readonly GPoseService _gPose;
private readonly AdvancedDyePopup _advancedDyes; private readonly AdvancedDyePopup _advancedDyes;
private readonly ItemCopyService _itemCopy;
private float _requiredComboWidthUnscaled; private float _requiredComboWidthUnscaled;
private float _requiredComboWidth; 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, 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; _items = items;
_textures = textures; _textures = textures;
_config = config; _config = config;
_gPose = gPose; _gPose = gPose;
_advancedDyes = advancedDyes; _advancedDyes = advancedDyes;
_itemCopy = itemCopy;
_stainData = items.Stains; _stainData = items.Stains;
_stainCombo = new GlamourerColorCombo(DefaultWidth - 20, _stainData, favorites); _stainCombo = new GlamourerColorCombo(DefaultWidth - 20, _stainData, favorites);
_itemCombo = EquipSlotExtensions.EqdpSlots.Select(e => new ItemCombo(gameData, items, e, Glamourer.Log, favorites)).ToArray(); _itemCombo = EquipSlotExtensions.EqdpSlots.Select(e => new ItemCombo(gameData, items, e, Glamourer.Log, favorites)).ToArray();
@ -78,6 +82,7 @@ public class EquipmentDrawer
_requiredComboWidth = _requiredComboWidthUnscaled * ImGuiHelpers.GlobalScale; _requiredComboWidth = _requiredComboWidthUnscaled * ImGuiHelpers.GlobalScale;
_advancedMaterialColor = ColorId.AdvancedDyeActive.Value(); _advancedMaterialColor = ColorId.AdvancedDyeActive.Value();
_dragTarget = EquipSlot.Unknown;
} }
private bool VerifyRestrictedGear(EquipDrawData data) private bool VerifyRestrictedGear(EquipDrawData data)
@ -183,6 +188,7 @@ public class EquipmentDrawer
return change; return change;
} }
#region Small #region Small
private void DrawEquipSmall(in EquipDrawData equipDrawData) private void DrawEquipSmall(in EquipDrawData equipDrawData)
@ -401,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)
: _stainCombo.Draw($"##stain{data.Slot}", stain.RgbaColor, stain.Name, found, stain.Gloss, width); : _stainCombo.Draw($"##stain{data.Slot}", stain.RgbaColor, stain.Name, found, stain.Gloss, width);
_itemCopy.HandleCopyPaste(data, index);
if (!change) if (!change)
DrawStainDragDrop(data, index, stain, found); DrawStainDragDrop(data, index, stain, found);
@ -425,8 +432,8 @@ public class EquipmentDrawer
using var dragSource = ImUtf8.DragDropSource(); using var dragSource = ImUtf8.DragDropSource();
if (dragSource.Success) if (dragSource.Success)
{ {
if (DragDropSource.SetPayload("stainDragDrop"u8)) DragDropSource.SetPayload("stainDragDrop"u8);
_draggedStain = stain; _draggedStain = stain;
ImUtf8.Text($"Dragging {stain.Name}..."); ImUtf8.Text($"Dragging {stain.Name}...");
} }
} }
@ -451,10 +458,12 @@ public class EquipmentDrawer
using var disabled = ImRaii.Disabled(data.Locked); using var disabled = ImRaii.Disabled(data.Locked);
var change = combo.Draw(data.CurrentItem.Name, data.CurrentItem.ItemId, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength, var change = combo.Draw(data.CurrentItem.Name, data.CurrentItem.ItemId, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength,
_requiredComboWidth); _requiredComboWidth);
DrawGearDragDrop(data);
if (change) if (change)
data.SetItem(combo.CurrentSelection); data.SetItem(combo.CurrentSelection);
else if (combo.CustomVariant.Id > 0) else if (combo.CustomVariant.Id > 0)
data.SetItem(_items.Identify(data.Slot, combo.CustomSetId, combo.CustomVariant)); 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), if (ResetOrClear(data.Locked, clear, data.AllowRevert, true, data.CurrentItem, data.GameItem, ItemManager.NothingItem(data.Slot),
out var item)) out var item))
@ -472,6 +481,14 @@ public class EquipmentDrawer
var change = combo.Draw(data.CurrentItem.Name, data.CurrentItem.Id.BonusItem, var change = combo.Draw(data.CurrentItem.Name, data.CurrentItem.Id.BonusItem,
small ? _comboLength - ImGui.GetFrameHeight() : _comboLength, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength,
_requiredComboWidth); _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) if (change)
data.SetItem(combo.CurrentSelection); data.SetItem(combo.CurrentSelection);
else if (combo.CustomVariant.Id > 0) else if (combo.CustomVariant.Id > 0)
@ -482,6 +499,50 @@ public class EquipmentDrawer
data.SetItem(item); 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, 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> in T currentItem, in T revertItem, in T clearItem, out T? item) where T : IEquatable<T>
{ {
@ -530,8 +591,13 @@ public class EquipmentDrawer
if (combo.Draw(mainhand.CurrentItem.Name, mainhand.CurrentItem.ItemId, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength, if (combo.Draw(mainhand.CurrentItem.Name, mainhand.CurrentItem.ItemId, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength,
_requiredComboWidth)) _requiredComboWidth))
changedItem = combo.CurrentSelection; changedItem = combo.CurrentSelection;
else if (ResetOrClear(mainhand.Locked || unknown, open, mainhand.AllowRevert, false, mainhand.CurrentItem, mainhand.GameItem, else if (combo.CustomVariant.Id > 0 && (drawAll || ItemData.ConvertWeaponId(combo.CustomSetId) == mainhand.CurrentItem.Type))
default, out var c)) 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; changedItem = c;
if (changedItem != null) if (changedItem != null)
@ -548,7 +614,8 @@ public class EquipmentDrawer
} }
if (unknown) 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) private void DrawOffhand(in EquipDrawData mainhand, in EquipDrawData offhand, out string label, bool small, bool clear, bool open)
@ -568,6 +635,10 @@ public class EquipmentDrawer
if (combo.Draw(offhand.CurrentItem.Name, offhand.CurrentItem.ItemId, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength, if (combo.Draw(offhand.CurrentItem.Name, offhand.CurrentItem.ItemId, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength,
_requiredComboWidth)) _requiredComboWidth))
offhand.SetItem(combo.CurrentSelection); 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); var defaultOffhand = _items.GetDefaultOffhand(mainhand.CurrentItem);
if (ResetOrClear(locked, clear, offhand.AllowRevert, true, offhand.CurrentItem, offhand.GameItem, defaultOffhand, out var item)) if (ResetOrClear(locked, clear, offhand.AllowRevert, true, offhand.CurrentItem, offhand.GameItem, defaultOffhand, out var item))
@ -622,6 +693,7 @@ public class EquipmentDrawer
{ {
ImUtf8.Text(label); ImUtf8.Text(label);
} }
if (hasAdvancedDyes) if (hasAdvancedDyes)
ImUtf8.HoverTooltip("This design has advanced dyes setup for this slot."u8); 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;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using Glamourer.Unlocks; using Glamourer.Unlocks;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui.Widgets; using OtterGui.Widgets;
using Penumbra.GameData.DataContainers; using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;

View file

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

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,8 +1,8 @@
using Glamourer.Services; using Glamourer.Services;
using Glamourer.Unlocks; using Glamourer.Unlocks;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Extensions;
using OtterGui.Log; using OtterGui.Log;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text; using OtterGui.Text;
@ -19,6 +19,10 @@ public sealed class WeaponCombo : FilterComboCache<EquipItem>
private ItemId _currentItem; private ItemId _currentItem;
private float _innerWidth; 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) public WeaponCombo(ItemManager items, FullEquipType type, Logger log, FavoriteManager favorites)
: base(() => GetWeapons(favorites, items, type), MouseWheelType.Control, log) : base(() => GetWeapons(favorites, items, type), MouseWheelType.Control, log)
{ {
@ -46,8 +50,9 @@ public sealed class WeaponCombo : FilterComboCache<EquipItem>
public bool Draw(string previewName, ItemId previewIdx, float width, float innerWidth) public bool Draw(string previewName, ItemId previewIdx, float width, float innerWidth)
{ {
_innerWidth = innerWidth; _innerWidth = innerWidth;
_currentItem = previewIdx; _currentItem = previewIdx;
CustomVariant = 0;
return Draw($"##{Label}", previewName, string.Empty, width, ImGui.GetTextLineHeightWithSpacing()); return Draw($"##{Label}", previewName, string.Empty, width, ImGui.GetTextLineHeightWithSpacing());
} }
@ -74,6 +79,24 @@ public sealed class WeaponCombo : FilterComboCache<EquipItem>
return ret; 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) protected override bool IsVisible(int globalIndex, LowerString filter)
=> base.IsVisible(globalIndex, filter) || Items[globalIndex].ModelString.StartsWith(filter.Lower); => 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.Interface.Windowing;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Glamourer.Gui.Materials; using Glamourer.Gui.Materials;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;

View file

@ -41,6 +41,10 @@ public class GlamourerChangelog
Add1_3_5_0(Changelog); Add1_3_5_0(Changelog);
Add1_3_6_0(Changelog); Add1_3_6_0(Changelog);
Add1_3_7_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() private (int, ChangeLogDisplayType) ConfigData()
@ -61,6 +65,66 @@ 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.")
.RegisterEntry(
"This is not thoroughly tested, but I decided to push to stable instead of testing because otherwise a lot of people would just go to testing just for early access again despite having no business doing so.",
1)
.RegisterEntry(
"I also do not use most of the functionality of Glamourer myself, so I am unable to even encounter most issues myself.", 1)
.RegisterEntry("If you encounter any issues, please report them quickly on the discord.", 1)
.RegisterEntry("Added a chat command to clear temporary settings applied by Glamourer to Penumbra.")
.RegisterEntry("Fixed small issues with customizations not applicable to your race still applying.");
private static void Add1_3_7_0(Changelog log) private static void Add1_3_7_0(Changelog log)
=> log.NextVersion("Version 1.3.7.0") => log.NextVersion("Version 1.3.7.0")
.RegisterImportant( .RegisterImportant(

View file

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

View file

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

View file

@ -3,7 +3,7 @@ using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Interop.Material; using Glamourer.Interop.Material;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui;
using OtterGui.Services; using OtterGui.Services;
using OtterGui.Text; using OtterGui.Text;
@ -18,7 +18,6 @@ public class MaterialDrawer(DesignManager _designManager, Configuration _config)
public const float GlossWidth = 100; public const float GlossWidth = 100;
public const float SpecularStrengthWidth = 125; public const float SpecularStrengthWidth = 125;
private EquipSlot _newSlot = EquipSlot.Head;
private int _newMaterialIdx; private int _newMaterialIdx;
private int _newRowIdx; private int _newRowIdx;
private MaterialValueIndex _newKey = MaterialValueIndex.FromSlot(EquipSlot.Head); private MaterialValueIndex _newKey = MaterialValueIndex.FromSlot(EquipSlot.Head);
@ -178,14 +177,42 @@ public class MaterialDrawer(DesignManager _designManager, Configuration _config)
public sealed class MaterialSlotCombo; 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) public void DrawNew(Design design)
{ {
if (EquipSlotCombo.Draw("##slot", "Choose a slot for an advanced dye row.", ref _newSlot)) DrawSlotCombo();
_newKey = MaterialValueIndex.FromSlot(_newSlot) with
{
MaterialIndex = (byte)_newMaterialIdx,
RowIndex = (byte)_newRowIdx,
};
ImUtf8.SameLineInner(); ImUtf8.SameLineInner();
DrawMaterialIdxDrag(); DrawMaterialIdxDrag();
ImUtf8.SameLineInner(); ImUtf8.SameLineInner();

View file

@ -1,39 +1,40 @@
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.Interop;
using Glamourer.Interop.Penumbra; using Glamourer.Interop.Penumbra;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.State; using Glamourer.State;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui.Raii; using OtterGui.Raii;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.GameData.Data; using Penumbra.GameData.Data;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
namespace Glamourer.Gui; namespace Glamourer.Gui;
public sealed class PenumbraChangedItemTooltip : IDisposable public sealed class PenumbraChangedItemTooltip : IDisposable
{ {
private readonly PenumbraService _penumbra; private readonly PenumbraService _penumbra;
private readonly StateManager _stateManager; private readonly StateManager _stateManager;
private readonly ItemManager _items; private readonly ItemManager _items;
private readonly ObjectManager _objects; private readonly ActorObjectManager _objects;
private readonly CustomizeService _customize; private readonly CustomizeService _customize;
private readonly GPoseService _gpose; private readonly GPoseService _gpose;
private readonly EquipItem[] _lastItems = new EquipItem[EquipFlagExtensions.NumEquipFlags / 2]; private readonly EquipItem[] _lastItems = new EquipItem[EquipFlagExtensions.NumEquipFlags / 2 + BonusExtensions.AllFlags.Count];
public IEnumerable<KeyValuePair<EquipSlot, EquipItem>> LastItems public IEnumerable<KeyValuePair<object, EquipItem>> LastItems
=> EquipSlotExtensions.EqdpSlots.Append(EquipSlot.MainHand).Append(EquipSlot.OffHand).Zip(_lastItems) => EquipSlotExtensions.EqdpSlots.Cast<object>().Append(EquipSlot.MainHand).Append(EquipSlot.OffHand)
.Select(p => new KeyValuePair<EquipSlot, EquipItem>(p.First, p.Second)); .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 ChangedItemType LastType { get; private set; } = ChangedItemType.None;
public uint LastId { get; private set; } = 0; public uint LastId { get; private set; }
public DateTime LastTooltip { get; private set; } = DateTime.MinValue; public DateTime LastTooltip { get; private set; } = DateTime.MinValue;
public DateTime LastClick { get; private set; } = DateTime.MinValue; public DateTime LastClick { get; private set; } = DateTime.MinValue;
public PenumbraChangedItemTooltip(PenumbraService penumbra, StateManager stateManager, ItemManager items, ObjectManager objects, public PenumbraChangedItemTooltip(PenumbraService penumbra, StateManager stateManager, ItemManager items, ActorObjectManager objects,
CustomizeService customize, GPoseService gpose) CustomizeService customize, GPoseService gpose)
{ {
_penumbra = penumbra; _penumbra = penumbra;
@ -72,6 +73,21 @@ public sealed class PenumbraChangedItemTooltip : IDisposable
if (!Player()) if (!Player())
return; return;
var bonusSlot = item.Type.ToBonus();
if (bonusSlot is not BonusItemFlag.Unknown)
{
// + 2 due to weapons.
var glasses = _lastItems[bonusSlot.ToSlot() + 2];
using (_ = !openTooltip ? null : ImRaii.Tooltip())
{
ImGui.TextUnformatted($"{prefix}Right-Click to apply to current actor.");
if (glasses.Valid)
ImGui.TextUnformatted($"{prefix}Control + Right-Click to re-apply {glasses.Name} to current actor.");
}
return;
}
var slot = item.Type.ToSlot(); var slot = item.Type.ToSlot();
var last = _lastItems[slot.ToIndex()]; var last = _lastItems[slot.ToIndex()];
switch (slot) switch (slot)
@ -109,6 +125,27 @@ public sealed class PenumbraChangedItemTooltip : IDisposable
public void ApplyItem(ActorState state, EquipItem item) public void ApplyItem(ActorState state, EquipItem item)
{ {
var bonusSlot = item.Type.ToBonus();
if (bonusSlot is not BonusItemFlag.Unknown)
{
// + 2 due to weapons.
var glasses = _lastItems[bonusSlot.ToSlot() + 2];
if (ImGui.GetIO().KeyCtrl && glasses.Valid)
{
Glamourer.Log.Debug($"Re-Applying {glasses.Name} to {bonusSlot.ToName()}.");
SetLastItem(bonusSlot, default, state);
_stateManager.ChangeBonusItem(state, bonusSlot, glasses, ApplySettings.Manual);
}
else
{
Glamourer.Log.Debug($"Applying {item.Name} to {bonusSlot.ToName()}.");
SetLastItem(bonusSlot, item, state);
_stateManager.ChangeBonusItem(state, bonusSlot, item, ApplySettings.Manual);
}
return;
}
var slot = item.Type.ToSlot(); var slot = item.Type.ToSlot();
var last = _lastItems[slot.ToIndex()]; var last = _lastItems[slot.ToIndex()];
switch (slot) switch (slot)
@ -265,7 +302,22 @@ public sealed class PenumbraChangedItemTooltip : IDisposable
{ {
var oldItem = state.ModelData.Item(slot); var oldItem = state.ModelData.Item(slot);
if (oldItem.Id != item.Id) if (oldItem.Id != item.Id)
_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

@ -10,9 +10,8 @@ using Glamourer.Gui.Customization;
using Glamourer.Gui.Equipment; using Glamourer.Gui.Equipment;
using Glamourer.Gui.Materials; using Glamourer.Gui.Materials;
using Glamourer.Interop; using Glamourer.Interop;
using Glamourer.Interop.Structs;
using Glamourer.State; using Glamourer.State;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Raii; using OtterGui.Raii;
@ -22,7 +21,6 @@ using Penumbra.GameData.Actors;
using Penumbra.GameData.DataContainers; using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using ObjectManager = Glamourer.Interop.ObjectManager;
namespace Glamourer.Gui.Tabs.ActorTab; namespace Glamourer.Gui.Tabs.ActorTab;
@ -35,7 +33,7 @@ public class ActorPanel
private readonly AutoDesignApplier _autoDesignApplier; private readonly AutoDesignApplier _autoDesignApplier;
private readonly Configuration _config; private readonly Configuration _config;
private readonly DesignConverter _converter; private readonly DesignConverter _converter;
private readonly ObjectManager _objects; private readonly ActorObjectManager _objects;
private readonly DesignManager _designManager; private readonly DesignManager _designManager;
private readonly ImportService _importService; private readonly ImportService _importService;
private readonly ICondition _conditions; private readonly ICondition _conditions;
@ -53,7 +51,7 @@ public class ActorPanel
AutoDesignApplier autoDesignApplier, AutoDesignApplier autoDesignApplier,
Configuration config, Configuration config,
DesignConverter converter, DesignConverter converter,
ObjectManager objects, ActorObjectManager objects,
DesignManager designManager, DesignManager designManager,
ImportService importService, ImportService importService,
ICondition conditions, ICondition conditions,
@ -87,7 +85,7 @@ public class ActorPanel
_rightButtons = _rightButtons =
[ [
new LockedButton(this), new LockedButton(this),
new HeaderDrawer.IncognitoButton(_config.Ephemeral), new HeaderDrawer.IncognitoButton(_config),
]; ];
} }
@ -106,7 +104,7 @@ public class ActorPanel
{ {
using var group = ImRaii.Group(); using var group = ImRaii.Group();
(_identifier, _data) = _selector.Selection; (_identifier, _data) = _selector.Selection;
_lockedRedraw = _identifier.Type is IdentifierType.Special _lockedRedraw = _identifier.Type is IdentifierType.Special || _objects.IsInLobby
|| _conditions[ConditionFlag.OccupiedInCutSceneEvent]; || _conditions[ConditionFlag.OccupiedInCutSceneEvent];
(_actorName, _actor) = GetHeaderName(); (_actorName, _actor) = GetHeaderName();
DrawHeader(); DrawHeader();
@ -240,6 +238,7 @@ public class ActorPanel
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
DrawEquipmentMetaToggles(); DrawEquipmentMetaToggles();
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
_equipmentDrawer.DrawDragDropTooltip();
} }
private void DrawParameterHeader() private void DrawParameterHeader()
@ -306,6 +305,12 @@ public class ActorPanel
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.WeaponState, _stateManager, _state!)); EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.WeaponState, _stateManager, _state!));
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromState(CrestFlag.OffHand, _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() private void DrawMonsterPanel()

View file

@ -1,19 +1,17 @@
using System.Security.AccessControl; using Dalamud.Interface;
using Dalamud.Interface; using Dalamud.Bindings.ImGui;
using Glamourer.Interop;
using Glamourer.Interop.Structs;
using ImGuiNET;
using OtterGui; using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text; using OtterGui.Text;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Tabs.ActorTab; namespace Glamourer.Gui.Tabs.ActorTab;
public class ActorSelector(ObjectManager objects, ActorManager actors, EphemeralConfig config) public class ActorSelector(ActorObjectManager objects, ActorManager actors, EphemeralConfig config)
{ {
private ActorIdentifier _identifier = ActorIdentifier.Invalid; private ActorIdentifier _identifier = ActorIdentifier.Invalid;
@ -89,11 +87,10 @@ public class ActorSelector(ObjectManager objects, ActorManager actors, Ephemeral
if (!child) if (!child)
return; return;
objects.Update();
_world = new WorldId(objects.Player.Valid ? objects.Player.HomeWorld : (ushort)0); _world = new WorldId(objects.Player.Valid ? objects.Player.HomeWorld : (ushort)0);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, _defaultItemSpacing); using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, _defaultItemSpacing);
var skips = ImGuiClip.GetNecessarySkips(ImGui.GetTextLineHeight()); var skips = ImGuiClip.GetNecessarySkips(ImGui.GetTextLineHeight());
var remainder = ImGuiClip.FilteredClippedDraw(objects.Identifiers.Where(p => p.Value.Objects.Any(a => a.Model)), skips, CheckFilter, var remainder = ImGuiClip.FilteredClippedDraw(objects.Where(p => p.Value.Objects.Any(a => a.Model)), skips, CheckFilter,
DrawSelectable); DrawSelectable);
ImGuiClip.DrawEndDummy(remainder, ImGui.GetTextLineHeight()); ImGuiClip.DrawEndDummy(remainder, ImGui.GetTextLineHeight());
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -4,7 +4,7 @@ using Glamourer.Automation;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Designs.Special; using Glamourer.Designs.Special;
using Glamourer.Events; using Glamourer.Events;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Services; using OtterGui.Services;
@ -278,7 +278,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable
private void LookupTooltip(IEnumerable<Design> designs) private void LookupTooltip(IEnumerable<Design> designs)
{ {
using var _ = ImRaii.Tooltip(); 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 ImGui.TextUnformatted(tt.Length == 0
? "Matches no currently existing designs." ? "Matches no currently existing designs."
: "Matches the following designs:"); : "Matches the following designs:");

View file

@ -6,8 +6,9 @@ using Glamourer.Designs.Special;
using Glamourer.Interop; using Glamourer.Interop;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.Unlocks; using Glamourer.Unlocks;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui;
using OtterGui.Extensions;
using OtterGui.Log; using OtterGui.Log;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text; using OtterGui.Text;
@ -31,7 +32,7 @@ public class SetPanel(
RandomRestrictionDrawer _randomDrawer) RandomRestrictionDrawer _randomDrawer)
{ {
private readonly JobGroupCombo _jobGroupCombo = new(_manager, _jobs, Glamourer.Log); private readonly JobGroupCombo _jobGroupCombo = new(_manager, _jobs, Glamourer.Log);
private readonly HeaderDrawer.Button[] _rightButtons = [new HeaderDrawer.IncognitoButton(_config.Ephemeral)]; private readonly HeaderDrawer.Button[] _rightButtons = [new HeaderDrawer.IncognitoButton(_config)];
private string? _tempName; private string? _tempName;
private int _dragIndex = -1; private int _dragIndex = -1;
@ -431,10 +432,10 @@ public class SetPanel(
if (source) if (source)
{ {
ImUtf8.Text($"Moving design #{index + 1:D2}..."); ImUtf8.Text($"Moving design #{index + 1:D2}...");
if (ImGui.SetDragDropPayload(dragDropLabel, nint.Zero, 0)) if (ImGui.SetDragDropPayload(dragDropLabel, null, 0))
{ {
_dragIndex = index; _dragIndex = index;
_selector._dragDesignIndex = index; _selector.DragDesignIndex = index;
} }
} }
} }

View file

@ -2,12 +2,12 @@
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Glamourer.Automation; using Glamourer.Automation;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.Interop; using Dalamud.Bindings.ImGui;
using ImGuiNET;
using OtterGui; using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Extensions;
using OtterGui.Raii; using OtterGui.Raii;
using Penumbra.GameData.Actors; using Penumbra.GameData.Interop;
using Penumbra.String; using Penumbra.String;
using ImGuiClip = OtterGui.ImGuiClip; using ImGuiClip = OtterGui.ImGuiClip;
@ -18,8 +18,7 @@ public class SetSelector : IDisposable
private readonly Configuration _config; private readonly Configuration _config;
private readonly AutoDesignManager _manager; private readonly AutoDesignManager _manager;
private readonly AutomationChanged _event; private readonly AutomationChanged _event;
private readonly ActorManager _actors; private readonly ActorObjectManager _objects;
private readonly ObjectManager _objects;
private readonly List<(AutoDesignSet, int)> _list = []; private readonly List<(AutoDesignSet, int)> _list = [];
public AutoDesignSet? Selection { get; private set; } public AutoDesignSet? Selection { get; private set; }
@ -38,14 +37,13 @@ public class SetSelector : IDisposable
private int _dragIndex = -1; private int _dragIndex = -1;
private Action? _endAction; private Action? _endAction;
internal int _dragDesignIndex = -1; internal int DragDesignIndex = -1;
public SetSelector(AutoDesignManager manager, AutomationChanged @event, Configuration config, ActorManager actors, ObjectManager objects) public SetSelector(AutoDesignManager manager, AutomationChanged @event, Configuration config, ActorObjectManager objects)
{ {
_manager = manager; _manager = manager;
_event = @event; _event = @event;
_config = config; _config = config;
_actors = actors;
_objects = objects; _objects = objects;
_event.Subscribe(OnAutomationChange, AutomationChanged.Priority.SetSelector); _event.Subscribe(OnAutomationChange, AutomationChanged.Priority.SetSelector);
} }
@ -94,7 +92,7 @@ public class SetSelector : IDisposable
} }
private LowerString _filter = LowerString.Empty; private LowerString _filter = LowerString.Empty;
private uint _enabledFilter = 0; private uint _enabledFilter;
private float _width; private float _width;
private Vector2 _defaultItemSpacing; private Vector2 _defaultItemSpacing;
private Vector2 _selectableSize; private Vector2 _selectableSize;
@ -146,7 +144,7 @@ public class SetSelector : IDisposable
ImGui.SameLine(); ImGui.SameLine();
var f = _enabledFilter; var f = _enabledFilter;
if (ImGui.CheckboxFlags("##enabledFilter", ref f, 3)) if (ImGui.CheckboxFlags("##enabledFilter", ref f, 3u))
{ {
_enabledFilter = _enabledFilter switch _enabledFilter = _enabledFilter switch
{ {
@ -177,7 +175,6 @@ public class SetSelector : IDisposable
UpdateList(); UpdateList();
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, _defaultItemSpacing); using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, _defaultItemSpacing);
_selectableSize = new Vector2(0, 2 * ImGui.GetTextLineHeight() + ImGui.GetStyle().ItemSpacing.Y); _selectableSize = new Vector2(0, 2 * ImGui.GetTextLineHeight() + ImGui.GetStyle().ItemSpacing.Y);
_objects.Update();
ImGuiClip.ClippedDraw(_list, DrawSetSelectable, _selectableSize.Y + 2 * ImGui.GetStyle().ItemSpacing.Y); ImGuiClip.ClippedDraw(_list, DrawSetSelectable, _selectableSize.Y + 2 * ImGui.GetStyle().ItemSpacing.Y);
_endAction?.Invoke(); _endAction?.Invoke();
_endAction = null; _endAction = null;
@ -186,7 +183,7 @@ public class SetSelector : IDisposable
private void DrawSetSelectable((AutoDesignSet Set, int Index) pair) private void DrawSetSelectable((AutoDesignSet Set, int Index) pair)
{ {
using var id = ImRaii.PushId(pair.Index); using var id = ImRaii.PushId(pair.Index);
using (var color = ImRaii.PushColor(ImGuiCol.Text, pair.Set.Enabled ? ColorId.EnabledAutoSet.Value() : ColorId.DisabledAutoSet.Value())) using (ImRaii.PushColor(ImGuiCol.Text, pair.Set.Enabled ? ColorId.EnabledAutoSet.Value() : ColorId.DisabledAutoSet.Value()))
{ {
if (ImGui.Selectable(GetSetName(pair.Set, pair.Index), pair.Set == Selection, ImGuiSelectableFlags.None, _selectableSize)) if (ImGui.Selectable(GetSetName(pair.Set, pair.Index), pair.Set == Selection, ImGuiSelectableFlags.None, _selectableSize))
{ {
@ -285,9 +282,9 @@ public class SetSelector : IDisposable
private void NewSetButton(Vector2 size) private void NewSetButton(Vector2 size)
{ {
var id = _actors.GetCurrentPlayer(); var id = _objects.Actors.GetCurrentPlayer();
if (!id.IsValid) if (!id.IsValid)
id = _actors.CreatePlayer(ByteString.FromSpanUnsafe("New Design"u8, true, false, true), ushort.MaxValue); id = _objects.Actors.CreatePlayer(ByteString.FromSpanUnsafe("New Design"u8, true, false, true), ushort.MaxValue);
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), size, if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), size,
$"Create a new Automatic Design Set for {id}. The associated player can be changed later.", !id.IsValid, true)) $"Create a new Automatic Design Set for {id}. The associated player can be changed later.", !id.IsValid, true))
_manager.AddDesignSet("New Automation Set", id); _manager.AddDesignSet("New Automation Set", id);
@ -332,15 +329,15 @@ public class SetSelector : IDisposable
} }
else if (ImGuiUtil.IsDropping("DesignDragDrop")) else if (ImGuiUtil.IsDropping("DesignDragDrop"))
{ {
if (_dragDesignIndex >= 0) if (DragDesignIndex >= 0)
{ {
var idx = _dragDesignIndex; var idx = DragDesignIndex;
var setTo = set; var setTo = set;
var setFrom = Selection!; var setFrom = Selection!;
_endAction = () => _manager.MoveDesignToSet(setFrom, idx, setTo); _endAction = () => _manager.MoveDesignToSet(setFrom, idx, setTo);
} }
_dragDesignIndex = -1; DragDesignIndex = -1;
} }
} }
} }
@ -350,7 +347,7 @@ public class SetSelector : IDisposable
if (source) if (source)
{ {
ImGui.TextUnformatted($"Moving design set {GetSetName(set, index)} from position {index + 1}..."); 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; _dragIndex = index;
} }
} }

View file

@ -1,18 +1,17 @@
using Dalamud.Interface; using Dalamud.Interface;
using Glamourer.GameData; using Glamourer.GameData;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Interop;
using Glamourer.Interop.Structs;
using Glamourer.State; using Glamourer.State;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Gui.Debug; using Penumbra.GameData.Gui.Debug;
using Penumbra.GameData.Interop;
namespace Glamourer.Gui.Tabs.DebugTab; namespace Glamourer.Gui.Tabs.DebugTab;
public class ActiveStatePanel(StateManager _stateManager, ObjectManager _objectManager) : IGameDataDrawer public class ActiveStatePanel(StateManager _stateManager, ActorObjectManager _objectManager) : IGameDataDrawer
{ {
public string Label public string Label
=> $"Active Actors ({_stateManager.Count})###Active Actors"; => $"Active Actors ({_stateManager.Count})###Active Actors";
@ -22,8 +21,7 @@ public class ActiveStatePanel(StateManager _stateManager, ObjectManager _objectM
public void Draw() public void Draw()
{ {
_objectManager.Update(); foreach (var (identifier, actors) in _objectManager)
foreach (var (identifier, actors) in _objectManager.Identifiers)
{ {
if (ImGuiUtil.DrawDisabledButton($"{FontAwesomeIcon.Trash.ToIconString()}##{actors.Label}", new Vector2(ImGui.GetFrameHeight()), if (ImGuiUtil.DrawDisabledButton($"{FontAwesomeIcon.Trash.ToIconString()}##{actors.Label}", new Vector2(ImGui.GetFrameHeight()),
string.Empty, !_stateManager.ContainsKey(identifier), true)) string.Empty, !_stateManager.ContainsKey(identifier), true))
@ -66,13 +64,15 @@ public class ActiveStatePanel(StateManager _stateManager, ObjectManager _objectM
static string ItemString(in DesignData data, EquipSlot slot) static string ItemString(in DesignData data, EquipSlot slot)
{ {
var item = data.Item(slot); var item = data.Item(slot);
return $"{item.Name} ({item.Id.ToDiscriminatingString()} {item.PrimaryId.Id}{(item.SecondaryId != 0 ? $"-{item.SecondaryId.Id}" : string.Empty)}-{item.Variant})"; return
$"{item.Name} ({item.Id.ToDiscriminatingString()} {item.PrimaryId.Id}{(item.SecondaryId != 0 ? $"-{item.SecondaryId.Id}" : string.Empty)}-{item.Variant})";
} }
static string BonusItemString(in DesignData data, BonusItemFlag slot) static string BonusItemString(in DesignData data, BonusItemFlag slot)
{ {
var item = data.BonusItem(slot); var item = data.BonusItem(slot);
return $"{item.Name} ({item.Id.ToDiscriminatingString()} {item.PrimaryId.Id}{(item.SecondaryId != 0 ? $"-{item.SecondaryId.Id}" : string.Empty)}-{item.Variant})"; return
$"{item.Name} ({item.Id.ToDiscriminatingString()} {item.PrimaryId.Id}{(item.SecondaryId != 0 ? $"-{item.SecondaryId.Id}" : string.Empty)}-{item.Variant})";
} }
PrintRow("Model ID", state.BaseData.ModelId, state.ModelData.ModelId, state.Sources[MetaIndex.ModelId]); PrintRow("Model ID", state.BaseData.ModelId, state.ModelData.ModelId, state.Sources[MetaIndex.ModelId]);
@ -87,6 +87,9 @@ public class ActiveStatePanel(StateManager _stateManager, ObjectManager _objectM
PrintRow("Visor Toggled", state.BaseData.IsVisorToggled(), state.ModelData.IsVisorToggled(), PrintRow("Visor Toggled", state.BaseData.IsVisorToggled(), state.ModelData.IsVisorToggled(),
state.Sources[MetaIndex.VisorState]); state.Sources[MetaIndex.VisorState]);
ImGui.TableNextRow(); 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(), PrintRow("Weapon Visible", state.BaseData.IsWeaponVisible(), state.ModelData.IsWeaponVisible(),
state.Sources[MetaIndex.WeaponState]); state.Sources[MetaIndex.WeaponState]);
ImGui.TableNextRow(); ImGui.TableNextRow();

View file

@ -1,13 +1,13 @@
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using Glamourer.Interop; using Dalamud.Bindings.ImGui;
using ImGuiNET;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text; using OtterGui.Text;
using Penumbra.GameData.Gui.Debug; using Penumbra.GameData.Gui.Debug;
using Penumbra.GameData.Interop;
namespace Glamourer.Gui.Tabs.DebugTab; namespace Glamourer.Gui.Tabs.DebugTab;
public unsafe class AdvancedCustomizationDrawer(ObjectManager objects) : IGameDataDrawer public unsafe class AdvancedCustomizationDrawer(ActorObjectManager objects) : IGameDataDrawer
{ {
public string Label public string Label
=> "Advanced Customizations"; => "Advanced Customizations";
@ -31,8 +31,8 @@ public unsafe class AdvancedCustomizationDrawer(ObjectManager objects) : IGameDa
return; return;
} }
DrawCBuffer("Customize"u8, model.AsHuman->CustomizeParameterCBuffer, 0); DrawCBuffer("Customize"u8, model.AsHuman->CustomizeParameterCBuffer, 0);
DrawCBuffer("Decal"u8, model.AsHuman->DecalColorCBuffer, 1); DrawCBuffer("Decal"u8, model.AsHuman->DecalColorCBuffer, 1);
DrawCBuffer("Unk1"u8, *(ConstantBuffer**)((byte*)model.AsHuman + 0xBA0), 2); DrawCBuffer("Unk1"u8, *(ConstantBuffer**)((byte*)model.AsHuman + 0xBA0), 2);
DrawCBuffer("Unk2"u8, *(ConstantBuffer**)((byte*)model.AsHuman + 0xBA8), 3); DrawCBuffer("Unk2"u8, *(ConstantBuffer**)((byte*)model.AsHuman + 0xBA8), 3);
} }

View file

@ -1,6 +1,7 @@
using Glamourer.Automation; using Glamourer.Automation;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui;
using OtterGui.Extensions;
using OtterGui.Raii; using OtterGui.Raii;
using Penumbra.GameData.Gui.Debug; using Penumbra.GameData.Gui.Debug;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,8 +1,11 @@
using Dalamud.Interface; using Dalamud.Interface;
using Glamourer.Designs; using Glamourer.Designs;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui;
using OtterGui.Extensions;
using OtterGui.Filesystem;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Gui.Debug; using Penumbra.GameData.Gui.Debug;
@ -18,6 +21,7 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
public void Draw() public void Draw()
{ {
DrawButtons();
foreach (var (design, idx) in _designManager.Designs.WithIndex()) foreach (var (design, idx) in _designManager.Designs.WithIndex())
{ {
using var t = ImRaii.TreeNode($"{design.Name}##{idx}"); using var t = ImRaii.TreeNode($"{design.Name}##{idx}");
@ -25,7 +29,8 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
continue; continue;
DrawDesign(design, _designFileSystem); 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()); design.WriteProtected());
using var font = ImRaii.PushFont(UiBuilder.MonoFont); using var font = ImRaii.PushFont(UiBuilder.MonoFont);
ImGuiUtil.TextWrapped(base64); ImGuiUtil.TextWrapped(base64);
@ -34,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) public static void DrawDesign(DesignBase design, DesignFileSystem? fileSystem)
{ {
using var table = ImRaii.Table("##equip", 8, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit); using var table = ImRaii.Table("##equip", 8, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit);
@ -52,7 +77,7 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
ImGui.TableNextRow(); ImGui.TableNextRow();
ImGuiUtil.DrawTableColumn("Design File System Path"); ImGuiUtil.DrawTableColumn("Design File System Path");
if (fileSystem != null) 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(); ImGui.TableNextRow();
ImGuiUtil.DrawTableColumn("Creation"); ImGuiUtil.DrawTableColumn("Creation");
@ -89,6 +114,7 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
ImGuiUtil.DrawTableColumn(index.ToName()); ImGuiUtil.DrawTableColumn(index.ToName());
ImGuiUtil.DrawTableColumn(design.DesignData.GetMeta(index).ToString()); ImGuiUtil.DrawTableColumn(design.DesignData.GetMeta(index).ToString());
ImGuiUtil.DrawTableColumn(design.DoApplyMeta(index) ? "Apply" : "Keep"); ImGuiUtil.DrawTableColumn(design.DoApplyMeta(index) ? "Apply" : "Keep");
ImGui.TableNextRow();
} }
ImGuiUtil.DrawTableColumn("Model ID"); ImGuiUtil.DrawTableColumn("Model ID");

View file

@ -1,8 +1,9 @@
using Dalamud.Interface; using Dalamud.Interface;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Services; using Glamourer.Services;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui;
using OtterGui.Extensions;
using OtterGui.Raii; using OtterGui.Raii;
using Penumbra.GameData.DataContainers; using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;

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 Glamourer.State;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using Penumbra.GameData.Gui.Debug; using Penumbra.GameData.Gui.Debug;
namespace Glamourer.Gui.Tabs.DebugTab; namespace Glamourer.Gui.Tabs.DebugTab;

View file

@ -3,24 +3,26 @@ using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures; using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Interop;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.State; using Glamourer.State;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui;
using OtterGui.Extensions;
using OtterGui.Text;
using Penumbra.GameData; using Penumbra.GameData;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Gui.Debug; using Penumbra.GameData.Gui.Debug;
using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Tabs.DebugTab; namespace Glamourer.Gui.Tabs.DebugTab;
public unsafe class GlamourPlatePanel : IGameDataDrawer public unsafe class GlamourPlatePanel : IGameDataDrawer
{ {
private readonly DesignManager _design; private readonly DesignManager _design;
private readonly ItemManager _items; private readonly ItemManager _items;
private readonly StateManager _state; private readonly StateManager _state;
private readonly ObjectManager _objects; private readonly ActorObjectManager _objects;
public string Label public string Label
=> "Glamour Plates"; => "Glamour Plates";
@ -28,7 +30,8 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer
public bool Disabled public bool Disabled
=> false; => false;
public GlamourPlatePanel(IGameInteropProvider interop, ItemManager items, DesignManager design, StateManager state, ObjectManager objects) public GlamourPlatePanel(IGameInteropProvider interop, ItemManager items, DesignManager design, StateManager state,
ActorObjectManager objects)
{ {
_items = items; _items = items;
_design = design; _design = design;
@ -42,24 +45,24 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer
var manager = MirageManager.Instance(); var manager = MirageManager.Instance();
using (ImRaii.Group()) using (ImRaii.Group())
{ {
ImGui.TextUnformatted("Address:"); ImUtf8.Text("Address:"u8);
ImGui.TextUnformatted("Number of Glamour Plates:"); ImUtf8.Text("Number of Glamour Plates:"u8);
ImGui.TextUnformatted("Glamour Plates Requested:"); ImUtf8.Text("Glamour Plates Requested:"u8);
ImGui.TextUnformatted("Glamour Plates Loaded:"); ImUtf8.Text("Glamour Plates Loaded:"u8);
ImGui.TextUnformatted("Is Applying Glamour Plates:"); ImUtf8.Text("Is Applying Glamour Plates:"u8);
} }
ImGui.SameLine(); ImGui.SameLine();
using (ImRaii.Group()) using (ImRaii.Group())
{ {
ImGuiUtil.CopyOnClickSelectable($"0x{(ulong)manager:X}"); ImUtf8.CopyOnClickSelectable($"0x{(ulong)manager:X}");
ImGui.TextUnformatted(manager == null ? "-" : manager->GlamourPlates.Length.ToString()); ImUtf8.Text(manager == null ? "-" : manager->GlamourPlates.Length.ToString());
ImGui.TextUnformatted(manager == null ? "-" : manager->GlamourPlatesRequested.ToString()); ImUtf8.Text(manager == null ? "-" : manager->GlamourPlatesRequested.ToString());
ImGui.SameLine(); ImGui.SameLine();
if (ImGui.SmallButton("Request Update")) if (ImUtf8.SmallButton("Request Update"u8))
RequestGlamour(); RequestGlamour();
ImGui.TextUnformatted(manager == null ? "-" : manager->GlamourPlatesLoaded.ToString()); ImUtf8.Text(manager == null ? "-" : manager->GlamourPlatesLoaded.ToString());
ImGui.TextUnformatted(manager == null ? "-" : manager->IsApplyingGlamourPlate.ToString()); ImUtf8.Text(manager == null ? "-" : manager->IsApplyingGlamourPlate.ToString());
} }
if (manager == null) if (manager == null)
@ -71,12 +74,12 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer
for (var i = 0; i < manager->GlamourPlates.Length; ++i) for (var i = 0; i < manager->GlamourPlates.Length; ++i)
{ {
using var tree = ImRaii.TreeNode($"Plate #{i + 1:D2}"); using var tree = ImUtf8.TreeNode($"Plate #{i + 1:D2}");
if (!tree) if (!tree)
continue; continue;
ref var plate = ref manager->GlamourPlates[i]; ref var plate = ref manager->GlamourPlates[i];
if (ImGuiUtil.DrawDisabledButton("Apply to Player", Vector2.Zero, string.Empty, !enabled)) if (ImUtf8.ButtonEx("Apply to Player"u8, ""u8, Vector2.Zero, !enabled))
{ {
var design = CreateDesign(plate); var design = CreateDesign(plate);
_state.ApplyDesign(state!, design, ApplySettings.Manual with { IsFinal = true }); _state.ApplyDesign(state!, design, ApplySettings.Manual with { IsFinal = true });
@ -85,14 +88,14 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer
using (ImRaii.Group()) using (ImRaii.Group())
{ {
foreach (var slot in EquipSlotExtensions.FullSlots) foreach (var slot in EquipSlotExtensions.FullSlots)
ImGui.TextUnformatted(slot.ToName()); ImUtf8.Text(slot.ToName());
} }
ImGui.SameLine(); ImGui.SameLine();
using (ImRaii.Group()) using (ImRaii.Group())
{ {
foreach (var (_, index) in EquipSlotExtensions.FullSlots.WithIndex()) foreach (var (_, index) in EquipSlotExtensions.FullSlots.WithIndex())
ImGui.TextUnformatted($"{plate.ItemIds[index]:D6}, {StainIds.FromGlamourPlate(plate, index)}"); ImUtf8.Text($"{plate.ItemIds[index]:D6}, {StainIds.FromGlamourPlate(plate, index)}");
} }
} }
} }

View file

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

View file

@ -3,10 +3,11 @@ using Dalamud.Interface.Utility;
using Dalamud.Plugin; using Dalamud.Plugin;
using Glamourer.Api.Enums; using Glamourer.Api.Enums;
using Glamourer.Api.IpcSubscribers; using Glamourer.Api.IpcSubscribers;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Services; using OtterGui.Services;
using OtterGui.Text;
namespace Glamourer.Gui.Tabs.DebugTab.IpcTester; namespace Glamourer.Gui.Tabs.DebugTab.IpcTester;
@ -15,6 +16,7 @@ public class DesignIpcTester(IDalamudPluginInterface pluginInterface) : IUiServi
private Dictionary<Guid, string> _designs = []; private Dictionary<Guid, string> _designs = [];
private int _gameObjectIndex; private int _gameObjectIndex;
private string _gameObjectName = string.Empty; private string _gameObjectName = string.Empty;
private string _designName = string.Empty;
private uint _key; private uint _key;
private ApplyFlag _flags = ApplyFlagEx.DesignDefault; private ApplyFlag _flags = ApplyFlagEx.DesignDefault;
private Guid? _design; private Guid? _design;
@ -30,6 +32,7 @@ public class DesignIpcTester(IDalamudPluginInterface pluginInterface) : IUiServi
IpcTesterHelpers.IndexInput(ref _gameObjectIndex); IpcTesterHelpers.IndexInput(ref _gameObjectIndex);
IpcTesterHelpers.KeyInput(ref _key); IpcTesterHelpers.KeyInput(ref _key);
IpcTesterHelpers.NameInput(ref _gameObjectName); IpcTesterHelpers.NameInput(ref _gameObjectName);
ImUtf8.InputText("##designName"u8, ref _designName, "Design Name..."u8);
ImGuiUtil.GuidInput("##identifier", "Design Identifier...", string.Empty, ref _design, ref _designText, ImGuiUtil.GuidInput("##identifier", "Design Identifier...", string.Empty, ref _design, ref _designText,
ImGui.GetContentRegionAvail().X); ImGui.GetContentRegionAvail().X);
IpcTesterHelpers.DrawFlagInput(ref _flags); IpcTesterHelpers.DrawFlagInput(ref _flags);
@ -54,6 +57,48 @@ public class DesignIpcTester(IDalamudPluginInterface pluginInterface) : IUiServi
IpcTesterHelpers.DrawIntro(ApplyDesignName.Label); IpcTesterHelpers.DrawIntro(ApplyDesignName.Label);
if (ImGuiUtil.DrawDisabledButton("Apply##Name", Vector2.Zero, string.Empty, !_design.HasValue)) if (ImGuiUtil.DrawDisabledButton("Apply##Name", Vector2.Zero, string.Empty, !_design.HasValue))
_lastError = new ApplyDesignName(pluginInterface).Invoke(_design!.Value, _gameObjectName, _key, _flags); _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() private void DrawDesignsPopup()

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,7 +2,7 @@
using Glamourer.GameData; using Glamourer.GameData;
using Glamourer.Interop; using Glamourer.Interop;
using Glamourer.Interop.Structs; using Glamourer.Interop.Structs;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text; using OtterGui.Text;
@ -11,13 +11,13 @@ using Penumbra.GameData.Enums;
using Penumbra.GameData.Gui.Debug; using Penumbra.GameData.Gui.Debug;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using ObjectManager = Glamourer.Interop.ObjectManager;
namespace Glamourer.Gui.Tabs.DebugTab; namespace Glamourer.Gui.Tabs.DebugTab;
public unsafe class ModelEvaluationPanel( public unsafe class ModelEvaluationPanel(
ObjectManager _objectManager, ActorObjectManager _objectManager,
VisorService _visorService, VisorService _visorService,
VieraEarService _vieraEarService,
UpdateSlotService _updateSlotService, UpdateSlotService _updateSlotService,
ChangeCustomizeService _changeCustomizeService, ChangeCustomizeService _changeCustomizeService,
CrestService _crestService, CrestService _crestService,
@ -34,7 +34,7 @@ public unsafe class ModelEvaluationPanel(
public void Draw() public void Draw()
{ {
ImGui.InputInt("Game Object Index", ref _gameObjectIndex, 0, 0); ImGui.InputInt("Game Object Index", ref _gameObjectIndex, 0, 0);
var actor = _objectManager[_gameObjectIndex]; var actor = _objectManager.Objects[_gameObjectIndex];
var model = actor.Model; var model = actor.Model;
using var table = ImRaii.Table("##evaluationTable", 4, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg); using var table = ImRaii.Table("##evaluationTable", 4, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
@ -46,9 +46,10 @@ public unsafe class ModelEvaluationPanel(
ImGuiUtil.DrawTableColumn("Address"); ImGuiUtil.DrawTableColumn("Address");
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGuiUtil.CopyOnClickSelectable(actor.ToString());
Glamourer.Dynamis.DrawPointer(actor);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGuiUtil.CopyOnClickSelectable(model.ToString()); Glamourer.Dynamis.DrawPointer(model);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
if (actor.IsCharacter) if (actor.IsCharacter)
{ {
@ -84,6 +85,7 @@ public unsafe class ModelEvaluationPanel(
ImGuiUtil.CopyOnClickSelectable(offhand.ToString()); ImGuiUtil.CopyOnClickSelectable(offhand.ToString());
DrawVisor(actor, model); DrawVisor(actor, model);
DrawVieraEars(actor, model);
DrawHatState(actor, model); DrawHatState(actor, model);
DrawWeaponState(actor, model); DrawWeaponState(actor, model);
DrawWetness(actor, model); DrawWetness(actor, model);
@ -135,6 +137,26 @@ public unsafe class ModelEvaluationPanel(
_visorService.SetVisorState(model, !VisorService.GetVisorState(model)); _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) private void DrawHatState(Actor actor, Model model)
{ {
using var id = ImRaii.PushId("HatState"); using var id = ImRaii.PushId("HatState");

View file

@ -3,18 +3,18 @@ using Dalamud.Interface.Utility;
using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.GameData; using Glamourer.GameData;
using Glamourer.Interop;
using Glamourer.State; using Glamourer.State;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Gui.Debug; using Penumbra.GameData.Gui.Debug;
using Penumbra.GameData.Interop;
using ImGuiClip = OtterGui.ImGuiClip; using ImGuiClip = OtterGui.ImGuiClip;
namespace Glamourer.Gui.Tabs.DebugTab; namespace Glamourer.Gui.Tabs.DebugTab;
public class NpcAppearancePanel(NpcCombo _npcCombo, StateManager _state, ObjectManager _objectManager, DesignConverter _designConverter) public class NpcAppearancePanel(NpcCombo npcCombo, StateManager stateManager, ActorObjectManager objectManager, DesignConverter designConverter)
: IGameDataDrawer : IGameDataDrawer
{ {
public string Label public string Label
@ -28,9 +28,9 @@ public class NpcAppearancePanel(NpcCombo _npcCombo, StateManager _state, ObjectM
public void Draw() public void Draw()
{ {
ImGui.Checkbox("Compare Customize (or Gear)", ref _customizeOrGear); ImUtf8.Checkbox("Compare Customize (or Gear)"u8, ref _customizeOrGear);
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
var resetScroll = ImGui.InputTextWithHint("##npcFilter", "Filter...", ref _npcFilter, 64); var resetScroll = ImUtf8.InputText("##npcFilter"u8, ref _npcFilter, "Filter..."u8);
using var table = ImRaii.Table("npcs", 7, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY | ImGuiTableFlags.SizingFixedFit, using var table = ImRaii.Table("npcs", 7, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY | ImGuiTableFlags.SizingFixedFit,
new Vector2(-1, 400 * ImGuiHelpers.GlobalScale)); new Vector2(-1, 400 * ImGuiHelpers.GlobalScale));
@ -40,19 +40,19 @@ public class NpcAppearancePanel(NpcCombo _npcCombo, StateManager _state, ObjectM
if (resetScroll) if (resetScroll)
ImGui.SetScrollY(0); ImGui.SetScrollY(0);
ImGui.TableSetupColumn("Button", ImGuiTableColumnFlags.WidthFixed); ImUtf8.TableSetupColumn("Button"u8, ImGuiTableColumnFlags.WidthFixed);
ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthFixed, ImGuiHelpers.GlobalScale * 300); ImUtf8.TableSetupColumn("Name"u8, ImGuiTableColumnFlags.WidthFixed, ImGuiHelpers.GlobalScale * 300);
ImGui.TableSetupColumn("Kind", ImGuiTableColumnFlags.WidthFixed); ImUtf8.TableSetupColumn("Kind"u8, ImGuiTableColumnFlags.WidthFixed);
ImGui.TableSetupColumn("Id", ImGuiTableColumnFlags.WidthFixed); ImUtf8.TableSetupColumn("Id"u8, ImGuiTableColumnFlags.WidthFixed);
ImGui.TableSetupColumn("Model", ImGuiTableColumnFlags.WidthFixed); ImUtf8.TableSetupColumn("Model"u8, ImGuiTableColumnFlags.WidthFixed);
ImGui.TableSetupColumn("Visor", ImGuiTableColumnFlags.WidthFixed); ImUtf8.TableSetupColumn("Visor"u8, ImGuiTableColumnFlags.WidthFixed);
ImGui.TableSetupColumn("Compare", ImGuiTableColumnFlags.WidthStretch); ImUtf8.TableSetupColumn("Compare"u8, ImGuiTableColumnFlags.WidthStretch);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
var skips = ImGuiClip.GetNecessarySkips(ImGui.GetFrameHeightWithSpacing()); var skips = ImGuiClip.GetNecessarySkips(ImGui.GetFrameHeightWithSpacing());
ImGui.TableNextRow(); ImGui.TableNextRow();
var idx = 0; var idx = 0;
var remainder = ImGuiClip.FilteredClippedDraw(_npcCombo.Items, skips, var remainder = ImGuiClip.FilteredClippedDraw(npcCombo.Items, skips,
d => d.Name.Contains(_npcFilter, StringComparison.OrdinalIgnoreCase), DrawData); d => d.Name.Contains(_npcFilter, StringComparison.OrdinalIgnoreCase), DrawData);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGuiClip.DrawEndDummy(remainder, ImGui.GetFrameHeightWithSpacing()); ImGuiClip.DrawEndDummy(remainder, ImGui.GetFrameHeightWithSpacing());
@ -61,43 +61,31 @@ public class NpcAppearancePanel(NpcCombo _npcCombo, StateManager _state, ObjectM
void DrawData(NpcData data) void DrawData(NpcData data)
{ {
using var id = ImRaii.PushId(idx++); using var id = ImRaii.PushId(idx++);
var disabled = !_state.GetOrCreate(_objectManager.Player, out var state); var disabled = !stateManager.GetOrCreate(objectManager.Player, out var state);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
if (ImGuiUtil.DrawDisabledButton("Apply", Vector2.Zero, string.Empty, disabled)) if (ImUtf8.ButtonEx("Apply"u8, ""u8, Vector2.Zero, disabled))
{ {
foreach (var (slot, item, stain) in _designConverter.FromDrawData(data.Equip.ToArray(), data.Mainhand, data.Offhand, true)) foreach (var (slot, item, stain) in designConverter.FromDrawData(data.Equip.ToArray(), data.Mainhand, data.Offhand, true))
_state.ChangeEquip(state!, slot, item, stain, ApplySettings.Manual); stateManager.ChangeEquip(state!, slot, item, stain, ApplySettings.Manual);
_state.ChangeMetaState(state!, MetaIndex.VisorState, data.VisorToggled, ApplySettings.Manual); stateManager.ChangeMetaState(state!, MetaIndex.VisorState, data.VisorToggled, ApplySettings.Manual);
_state.ChangeEntireCustomize(state!, data.Customize, CustomizeFlagExtensions.All, ApplySettings.Manual); stateManager.ChangeEntireCustomize(state!, data.Customize, CustomizeFlagExtensions.All, ApplySettings.Manual);
} }
ImGui.TableNextColumn(); ImUtf8.DrawFrameColumn(data.Name);
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(data.Name);
ImGui.TableNextColumn(); ImUtf8.DrawFrameColumn(data.Kind is ObjectKind.BattleNpc ? "B" : "E");
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(data.Kind is ObjectKind.BattleNpc ? "B" : "E");
ImGui.TableNextColumn(); ImUtf8.DrawFrameColumn(data.Id.Id.ToString());
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(data.Id.Id.ToString());
ImGui.TableNextColumn(); ImUtf8.DrawFrameColumn(data.ModelId.ToString());
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(data.ModelId.ToString());
using (_ = ImRaii.PushFont(UiBuilder.IconFont)) using (_ = ImRaii.PushFont(UiBuilder.IconFont))
{ {
ImGui.TableNextColumn(); ImUtf8.DrawFrameColumn(data.VisorToggled ? FontAwesomeIcon.Check.ToIconString() : FontAwesomeIcon.Times.ToIconString());
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(data.VisorToggled ? FontAwesomeIcon.Check.ToIconString() : FontAwesomeIcon.Times.ToIconString());
} }
using var mono = ImRaii.PushFont(UiBuilder.MonoFont); using var mono = ImRaii.PushFont(UiBuilder.MonoFont);
ImGui.TableNextColumn(); ImUtf8.DrawFrameColumn(_customizeOrGear ? data.Customize.ToString() : data.WriteGear());
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(_customizeOrGear ? data.Customize.ToString() : data.WriteGear());
} }
} }
} }

View file

@ -1,13 +1,13 @@
using Glamourer.Interop; using Dalamud.Bindings.ImGui;
using ImGuiNET;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Text;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Gui.Debug; using Penumbra.GameData.Gui.Debug;
using Penumbra.GameData.Interop;
namespace Glamourer.Gui.Tabs.DebugTab; namespace Glamourer.Gui.Tabs.DebugTab;
public class ObjectManagerPanel(ObjectManager _objectManager, ActorManager _actors) : IGameDataDrawer public class ObjectManagerPanel(ActorObjectManager _objectManager, ActorManager _actors) : IGameDataDrawer
{ {
public string Label public string Label
=> "Object Manager"; => "Object Manager";
@ -19,44 +19,45 @@ public class ObjectManagerPanel(ObjectManager _objectManager, ActorManager _acto
public void Draw() public void Draw()
{ {
_objectManager.Update(); _objectManager.Objects.DrawDebug();
using (var table = ImRaii.Table("##data", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit))
using (var table = ImUtf8.Table("##data"u8, 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit))
{ {
if (!table) if (!table)
return; return;
ImGuiUtil.DrawTableColumn("Last Update"); ImUtf8.DrawTableColumn("World"u8);
ImGuiUtil.DrawTableColumn(_objectManager.LastUpdate.ToString(CultureInfo.InvariantCulture)); ImUtf8.DrawTableColumn(_actors.Finished ? _actors.Data.ToWorldName(_objectManager.World) : "Service Missing");
ImUtf8.DrawTableColumn(_objectManager.World.ToString());
ImUtf8.DrawTableColumn("Player Character"u8);
ImUtf8.DrawTableColumn($"{_objectManager.Player.Utf8Name} ({_objectManager.Player.Index})");
ImGui.TableNextColumn();
ImUtf8.CopyOnClickSelectable(_objectManager.Player.ToString());
ImUtf8.DrawTableColumn("In GPose"u8);
ImUtf8.DrawTableColumn(_objectManager.IsInGPose.ToString());
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGuiUtil.DrawTableColumn("World"); ImUtf8.DrawTableColumn("In Lobby"u8);
ImGuiUtil.DrawTableColumn(_actors.Finished ? _actors.Data.ToWorldName(_objectManager.World) : "Service Missing"); ImUtf8.DrawTableColumn(_objectManager.IsInLobby.ToString());
ImGuiUtil.DrawTableColumn(_objectManager.World.ToString());
ImGuiUtil.DrawTableColumn("Player Character");
ImGuiUtil.DrawTableColumn($"{_objectManager.Player.Utf8Name} ({_objectManager.Player.Index})");
ImGui.TableNextColumn();
ImGuiUtil.CopyOnClickSelectable(_objectManager.Player.ToString());
ImGuiUtil.DrawTableColumn("In GPose");
ImGuiUtil.DrawTableColumn(_objectManager.IsInGPose.ToString());
ImGui.TableNextColumn(); ImGui.TableNextColumn();
if (_objectManager.IsInGPose) if (_objectManager.IsInGPose)
{ {
ImGuiUtil.DrawTableColumn("GPose Player"); ImUtf8.DrawTableColumn("GPose Player"u8);
ImGuiUtil.DrawTableColumn($"{_objectManager.GPosePlayer.Utf8Name} ({_objectManager.GPosePlayer.Index})"); ImUtf8.DrawTableColumn($"{_objectManager.GPosePlayer.Utf8Name} ({_objectManager.GPosePlayer.Index})");
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGuiUtil.CopyOnClickSelectable(_objectManager.GPosePlayer.ToString()); ImUtf8.CopyOnClickSelectable(_objectManager.GPosePlayer.ToString());
} }
ImGuiUtil.DrawTableColumn("Number of Players"); ImUtf8.DrawTableColumn("Number of Players"u8);
ImGuiUtil.DrawTableColumn(_objectManager.Count.ToString()); ImUtf8.DrawTableColumn(_objectManager.Count.ToString());
ImGui.TableNextColumn(); ImGui.TableNextColumn();
} }
var filterChanged = ImGui.InputTextWithHint("##Filter", "Filter...", ref _objectFilter, 64); var filterChanged = ImUtf8.InputText("##Filter"u8, ref _objectFilter, "Filter..."u8);
using var table2 = ImRaii.Table("##data2", 3, using var table2 = ImUtf8.Table("##data2"u8, 3,
ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersOuter | ImGuiTableFlags.ScrollY, ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersOuter | ImGuiTableFlags.ScrollY,
new Vector2(-1, 20 * ImGui.GetTextLineHeightWithSpacing())); new Vector2(-1, 20 * ImGui.GetTextLineHeightWithSpacing()));
if (!table2) if (!table2)
@ -69,13 +70,13 @@ public class ObjectManagerPanel(ObjectManager _objectManager, ActorManager _acto
var skips = ImGuiClip.GetNecessarySkips(ImGui.GetTextLineHeightWithSpacing()); var skips = ImGuiClip.GetNecessarySkips(ImGui.GetTextLineHeightWithSpacing());
ImGui.TableNextRow(); ImGui.TableNextRow();
var remainder = ImGuiClip.FilteredClippedDraw(_objectManager.Identifiers, skips, var remainder = ImGuiClip.FilteredClippedDraw(_objectManager, skips,
p => p.Value.Label.Contains(_objectFilter, StringComparison.OrdinalIgnoreCase), p p => p.Value.Label.Contains(_objectFilter, StringComparison.OrdinalIgnoreCase), p
=> =>
{ {
ImGuiUtil.DrawTableColumn(p.Key.ToString()); ImUtf8.DrawTableColumn(p.Key.ToString());
ImGuiUtil.DrawTableColumn(p.Value.Label); ImUtf8.DrawTableColumn(p.Value.Label);
ImGuiUtil.DrawTableColumn(string.Join(", ", p.Value.Objects.OrderBy(a => a.Index).Select(a => a.Index.ToString()))); ImUtf8.DrawTableColumn(string.Join(", ", p.Value.Objects.OrderBy(a => a.Index).Select(a => a.Index.ToString())));
}); });
ImGuiClip.DrawEndDummy(remainder, ImGui.GetTextLineHeightWithSpacing()); ImGuiClip.DrawEndDummy(remainder, ImGui.GetTextLineHeightWithSpacing());
} }

View file

@ -1,6 +1,6 @@
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Glamourer.Interop.Penumbra; using Glamourer.Interop.Penumbra;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
@ -49,7 +49,7 @@ public unsafe class PenumbraPanel(PenumbraService _penumbra, PenumbraChangedItem
ImGui.TableNextColumn(); ImGui.TableNextColumn();
var address = _drawObject.Address; var address = _drawObject.Address;
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale); 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)) ImGuiInputTextFlags.CharsHexadecimal))
_drawObject = address; _drawObject = address;
ImGuiUtil.DrawTableColumn(_penumbra.Available ImGuiUtil.DrawTableColumn(_penumbra.Available
@ -61,7 +61,7 @@ public unsafe class PenumbraPanel(PenumbraService _penumbra, PenumbraChangedItem
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale); ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
ImGui.InputInt("##CutsceneIndex", ref _gameObjectIndex, 0, 0); ImGui.InputInt("##CutsceneIndex", ref _gameObjectIndex, 0, 0);
ImGuiUtil.DrawTableColumn(_penumbra.Available ImGuiUtil.DrawTableColumn(_penumbra.Available
? _penumbra.CutsceneParent((ushort) _gameObjectIndex).ToString() ? _penumbra.CutsceneParent((ushort)_gameObjectIndex).ToString()
: "Penumbra Unavailable"); : "Penumbra Unavailable");
ImGuiUtil.DrawTableColumn("Redraw Object"); ImGuiUtil.DrawTableColumn("Redraw Object");
@ -76,7 +76,9 @@ public unsafe class PenumbraPanel(PenumbraService _penumbra, PenumbraChangedItem
} }
ImGuiUtil.DrawTableColumn("Last Tooltip Date"); 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(); ImGui.TableNextColumn();
ImGuiUtil.DrawTableColumn("Last Click Date"); ImGuiUtil.DrawTableColumn("Last Click Date");
@ -87,7 +89,13 @@ public unsafe class PenumbraPanel(PenumbraService _penumbra, PenumbraChangedItem
ImGui.Separator(); ImGui.Separator();
foreach (var (slot, item) in _penumbraTooltip.LastItems) foreach (var (slot, item) in _penumbraTooltip.LastItems)
{ {
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"); ImGuiUtil.DrawTableColumn(item.Valid ? item.Name : "None");
ImGui.TableNextColumn(); ImGui.TableNextColumn();
} }

View file

@ -3,10 +3,11 @@ using Glamourer.Interop.Structs;
using Glamourer.State; using Glamourer.State;
using OtterGui.Raii; using OtterGui.Raii;
using Penumbra.GameData.Gui.Debug; using Penumbra.GameData.Gui.Debug;
using Penumbra.GameData.Interop;
namespace Glamourer.Gui.Tabs.DebugTab; namespace Glamourer.Gui.Tabs.DebugTab;
public class RetainedStatePanel(StateManager _stateManager, ObjectManager _objectManager) : IGameDataDrawer public class RetainedStatePanel(StateManager _stateManager, ActorObjectManager _objectManager) : IGameDataDrawer
{ {
public string Label public string Label
=> "Retained States (Inactive Actors)"; => "Retained States (Inactive Actors)";

View file

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

View file

@ -1,5 +1,5 @@
using Glamourer.Designs; using Glamourer.Designs;
using ImGuiNET; using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Widgets; using OtterGui.Widgets;
@ -12,13 +12,6 @@ public sealed class DesignColorCombo(DesignColors _designColors, bool _skipAutom
: _designColors.Keys.OrderBy(k => k).Prepend(DesignColors.AutomaticName), : _designColors.Keys.OrderBy(k => k).Prepend(DesignColors.AutomaticName),
MouseWheelType.Control, Glamourer.Log) 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) protected override bool DrawSelectable(int globalIdx, bool selected)
{ {
var isAutomatic = !_skipAutomatic && globalIdx == 0; var isAutomatic = !_skipAutomatic && globalIdx == 0;

View file

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

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