diff --git a/CustomizePlus.GameData/Extensions/ActorIdentifierExtensions.cs b/CustomizePlus.GameData/Extensions/ActorIdentifierExtensions.cs
index 823819c..122d125 100644
--- a/CustomizePlus.GameData/Extensions/ActorIdentifierExtensions.cs
+++ b/CustomizePlus.GameData/Extensions/ActorIdentifierExtensions.cs
@@ -1,6 +1,7 @@
using Dalamud.Game.ClientState.Objects.Enums;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
+using Penumbra.GameData.Interop;
using PenumbraExtensions = Penumbra.GameData.Actors.ActorIdentifierExtensions;
namespace CustomizePlus.GameData.Extensions;
@@ -80,48 +81,4 @@ public static bool IsAllowedForProfiles(this ActorIdentifier identifier)
return false;
}
}
-
- ///
- /// Get "true" actor for special actors. Returns ActorIdentifier.Invalid for non-special actors or if actor cannot be found.
- ///
- ///
- ///
- ///
- public static ActorIdentifier GetTrueActorForSpecialType(this ActorIdentifier identifier)
- {
- if (!identifier.IsValid)
- return ActorIdentifier.Invalid;
-
- if (identifier.Type != IdentifierType.Special)
- return ActorIdentifier.Invalid;
-
- if (PenumbraExtensions.Manager == null)
- throw new Exception("ActorIdentifier.Manager is not initialized");
-
- switch (identifier.Special)
- {
- case ScreenActor.GPosePlayer:
- case ScreenActor.CharacterScreen:
- case ScreenActor.FittingRoom:
- case ScreenActor.DyePreview:
- case ScreenActor.Portrait:
- return PenumbraExtensions.Manager.GetCurrentPlayer();
- case ScreenActor.ExamineScreen:
- var examineIdentifier = PenumbraExtensions.Manager.GetInspectPlayer();
-
- if (!examineIdentifier.IsValid)
- examineIdentifier = PenumbraExtensions.Manager.GetGlamourPlayer(); //returns ActorIdentifier.Invalid if player is invalid
-
- if (!examineIdentifier.IsValid)
- return ActorIdentifier.Invalid;
-
- return examineIdentifier;
- case ScreenActor.Card6:
- case ScreenActor.Card7:
- case ScreenActor.Card8:
- return PenumbraExtensions.Manager.GetCardPlayer();
- }
-
- return ActorIdentifier.Invalid;
- }
}
diff --git a/CustomizePlus/Armatures/Services/ArmatureManager.cs b/CustomizePlus/Armatures/Services/ArmatureManager.cs
index 47eecfd..c37758f 100644
--- a/CustomizePlus/Armatures/Services/ArmatureManager.cs
+++ b/CustomizePlus/Armatures/Services/ArmatureManager.cs
@@ -99,6 +99,15 @@ public void OnGameObjectMove(Actor actor)
ApplyRootTranslation(armature, actor);
}
+ ///
+ /// Force profile rebind for all armatures
+ ///
+ public void RebindAllArmatures()
+ {
+ foreach (var kvPair in Armatures)
+ kvPair.Value.IsPendingProfileRebind = true;
+ }
+
///
/// Deletes armatures which no longer have actor associated with them and creates armatures for new actors
///
@@ -542,9 +551,7 @@ private IEnumerable GetArmaturesForCharacterName(string characterName)
{
foreach(var kvPair in Armatures)
{
- var actorIdentifier = kvPair.Key;
- if (actorIdentifier.Type == IdentifierType.Special)
- actorIdentifier = actorIdentifier.GetTrueActorForSpecialType();
+ (var actorIdentifier, _) = _gameObjectService.GetTrueActorForSpecialTypeActor(kvPair.Key);
if(actorIdentifier.ToNameWithoutOwnerName() == characterName)
yield return kvPair.Value;
diff --git a/CustomizePlus/Configuration/Data/PluginConfiguration.cs b/CustomizePlus/Configuration/Data/PluginConfiguration.cs
index b4e1bd7..961dcf1 100644
--- a/CustomizePlus/Configuration/Data/PluginConfiguration.cs
+++ b/CustomizePlus/Configuration/Data/PluginConfiguration.cs
@@ -86,6 +86,17 @@ public class CommandSettingsEntries
public CommandSettingsEntries CommandSettings { get; set; } = new();
+ [Serializable]
+ public class ProfileApplicationSettingsEntries
+ {
+ public bool ApplyInCharacterWindow { get; set; } = true;
+ public bool ApplyInTryOn { get; set; } = true;
+ public bool ApplyInCards { get; set; } = true;
+ public bool ApplyInInspect { get; set; } = true;
+ }
+
+ public ProfileApplicationSettingsEntries ProfileApplicationSettings { get; set; } = new();
+
[JsonIgnore]
private readonly SaveService _saveService;
diff --git a/CustomizePlus/Game/Services/GameObjectService.cs b/CustomizePlus/Game/Services/GameObjectService.cs
index cd98820..d3177ce 100644
--- a/CustomizePlus/Game/Services/GameObjectService.cs
+++ b/CustomizePlus/Game/Services/GameObjectService.cs
@@ -7,6 +7,9 @@
using Penumbra.GameData.Interop;
using ObjectManager = CustomizePlus.GameData.Services.ObjectManager;
using DalamudGameObject = Dalamud.Game.ClientState.Objects.Types.GameObject;
+using ECommons.Configuration;
+using System;
+using CustomizePlus.Configuration.Data;
namespace CustomizePlus.Game.Services;
@@ -15,12 +18,18 @@ public class GameObjectService
private readonly ActorManager _actorManager;
private readonly IObjectTable _objectTable;
private readonly ObjectManager _objectManager;
+ private readonly PluginConfiguration _configuration;
- public GameObjectService(ActorManager actorManager, IObjectTable objectTable, ObjectManager objectManager)
+ public GameObjectService(
+ ActorManager actorManager,
+ IObjectTable objectTable,
+ ObjectManager objectManager,
+ PluginConfiguration configuration)
{
_actorManager = actorManager;
_objectTable = objectTable;
_objectManager = objectManager;
+ _configuration = configuration;
}
public string GetCurrentPlayerName()
@@ -54,8 +63,7 @@ public bool IsActorHasScalableRoot(Actor actor)
{
var identifier = kvPair.Key;
- if (kvPair.Key.Type == IdentifierType.Special)
- identifier = identifier.GetTrueActorForSpecialType();
+ (identifier, _) = GetTrueActorForSpecialTypeActor(identifier);
if (!identifier.IsValid)
continue;
@@ -80,4 +88,70 @@ public Actor GetLocalPlayerActor()
{
return _objectTable.CreateObjectReference(actor);
}
+
+
+ ///
+ /// Get "true" actor for special actors.
+ /// This should be used everywhere where resolving proper actor is crucial for proper profile application
+ /// as identifiers returned by object manager with type "Special" need special handling.
+ ///
+ public (ActorIdentifier, SpecialResult) GetTrueActorForSpecialTypeActor(ActorIdentifier identifier)
+ {
+ if (identifier.Type != IdentifierType.Special)
+ return (identifier, SpecialResult.Invalid);
+
+ if (_actorManager.ResolvePartyBannerPlayer(identifier.Special, out var id))
+ return _configuration.ProfileApplicationSettings.ApplyInCards ? (id, SpecialResult.PartyBanner) : (identifier, SpecialResult.Invalid);
+
+ if (_actorManager.ResolvePvPBannerPlayer(identifier.Special, out id))
+ return _configuration.ProfileApplicationSettings.ApplyInCards ? (id, SpecialResult.PvPBanner) : (identifier, SpecialResult.Invalid);
+
+ if (_actorManager.ResolveMahjongPlayer(identifier.Special, out id))
+ return _configuration.ProfileApplicationSettings.ApplyInCards ? (id, SpecialResult.Mahjong) : (identifier, SpecialResult.Invalid);
+
+ switch (identifier.Special)
+ {
+ case ScreenActor.GPosePlayer:
+ return (_actorManager.GetCurrentPlayer(), SpecialResult.GPosePlayer);
+ case ScreenActor.CharacterScreen when _configuration.ProfileApplicationSettings.ApplyInCharacterWindow:
+ return (_actorManager.GetCurrentPlayer(), SpecialResult.CharacterScreen);
+ case ScreenActor.FittingRoom when _configuration.ProfileApplicationSettings.ApplyInTryOn:
+ return (_actorManager.GetCurrentPlayer(), SpecialResult.FittingRoom);
+ case ScreenActor.DyePreview when _configuration.ProfileApplicationSettings.ApplyInTryOn:
+ return (_actorManager.GetCurrentPlayer(), SpecialResult.DyePreview);
+ case ScreenActor.Portrait when _configuration.ProfileApplicationSettings.ApplyInCards:
+ return (_actorManager.GetCurrentPlayer(), SpecialResult.Portrait);
+ case ScreenActor.ExamineScreen:
+ {
+ identifier = _actorManager.GetInspectPlayer();
+ if (identifier.IsValid)
+ return (_configuration.ProfileApplicationSettings.ApplyInInspect ? identifier : ActorIdentifier.Invalid, SpecialResult.Inspect);
+
+ identifier = _actorManager.GetCardPlayer();
+ if (identifier.IsValid)
+ return (_configuration.ProfileApplicationSettings.ApplyInInspect ? identifier : ActorIdentifier.Invalid, SpecialResult.Card);
+
+ return _configuration.ProfileApplicationSettings.ApplyInTryOn
+ ? (_actorManager.GetGlamourPlayer(), SpecialResult.Glamour) //returns ActorIdentifier.Invalid if player is invalid
+ : (identifier, SpecialResult.Invalid);
+ }
+ default: return (identifier, SpecialResult.Invalid);
+ }
+ }
+
+ public enum SpecialResult
+ {
+ PartyBanner,
+ PvPBanner,
+ Mahjong,
+ CharacterScreen,
+ FittingRoom,
+ DyePreview,
+ Portrait,
+ Inspect,
+ Card,
+ Glamour,
+ GPosePlayer,
+ Invalid,
+ }
}
diff --git a/CustomizePlus/Profiles/ProfileManager.cs b/CustomizePlus/Profiles/ProfileManager.cs
index 839c98e..d8a2cbf 100644
--- a/CustomizePlus/Profiles/ProfileManager.cs
+++ b/CustomizePlus/Profiles/ProfileManager.cs
@@ -26,6 +26,7 @@
using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
using System.Runtime.Serialization;
+using CustomizePlus.Game.Services;
namespace CustomizePlus.Profiles;
@@ -40,6 +41,7 @@ public class ProfileManager : IDisposable
private readonly Logger _logger;
private readonly PluginConfiguration _configuration;
private readonly ActorManager _actorManager;
+ private readonly GameObjectService _gameObjectService;
private readonly ProfileChanged _event;
private readonly TemplateChanged _templateChangedEvent;
private readonly ReloadEvent _reloadEvent;
@@ -56,6 +58,7 @@ public ProfileManager(
Logger logger,
PluginConfiguration configuration,
ActorManager actorManager,
+ GameObjectService gameObjectService,
ProfileChanged @event,
TemplateChanged templateChangedEvent,
ReloadEvent reloadEvent,
@@ -67,6 +70,7 @@ public ProfileManager(
_logger = logger;
_configuration = configuration;
_actorManager = actorManager;
+ _gameObjectService = gameObjectService;
_event = @event;
_templateChangedEvent = templateChangedEvent;
_templateChangedEvent.Subscribe(OnTemplateChange, TemplateChanged.Priority.ProfileManager);
@@ -477,8 +481,7 @@ public IEnumerable GetEnabledProfilesByActor(ActorIdentifier actorIdent
//performance: using textual override for ProfileAppliesTo here to not call
//GetGameObjectName every time we are trying to check object against profiles
- if (actorIdentifier.Type == IdentifierType.Special)
- actorIdentifier = actorIdentifier.GetTrueActorForSpecialType();
+ (actorIdentifier, _) = _gameObjectService.GetTrueActorForSpecialTypeActor(actorIdentifier);
if (!actorIdentifier.IsValid)
yield break;
diff --git a/CustomizePlus/UI/Windows/MainWindow/Tabs/Debug/StateMonitoringTab.cs b/CustomizePlus/UI/Windows/MainWindow/Tabs/Debug/StateMonitoringTab.cs
index eb36e40..75722a6 100644
--- a/CustomizePlus/UI/Windows/MainWindow/Tabs/Debug/StateMonitoringTab.cs
+++ b/CustomizePlus/UI/Windows/MainWindow/Tabs/Debug/StateMonitoringTab.cs
@@ -11,6 +11,7 @@
using CustomizePlus.GameData.Services;
using CustomizePlus.Core.Extensions;
using System.Numerics;
+using CustomizePlus.Game.Services;
namespace CustomizePlus.UI.Windows.MainWindow.Tabs.Debug;
@@ -20,17 +21,20 @@ public class StateMonitoringTab
private readonly TemplateManager _templateManager;
private readonly ArmatureManager _armatureManager;
private readonly ObjectManager _objectManager;
+ private readonly GameObjectService _gameObjectService;
public StateMonitoringTab(
ProfileManager profileManager,
TemplateManager templateManager,
ArmatureManager armatureManager,
- ObjectManager objectManager)
+ ObjectManager objectManager,
+ GameObjectService gameObjectService)
{
_profileManager = profileManager;
_templateManager = templateManager;
_armatureManager = armatureManager;
_objectManager = objectManager;
+ _gameObjectService = gameObjectService;
}
public void Draw()
@@ -106,8 +110,8 @@ private void DrawObjectManager()
ImGui.Text($"Special: {kvPair.Key.Special.ToString()}");
ImGui.Text($"ToName: {kvPair.Key.ToName()}");
ImGui.Text($"ToNameWithoutOwnerName: {kvPair.Key.ToNameWithoutOwnerName()}");
- if(kvPair.Key.Type == Penumbra.GameData.Enums.IdentifierType.Special)
- ImGui.Text($"True actor: {kvPair.Key.GetTrueActorForSpecialType().ToName()}");
+ (var actorIdentifier, var specialResult) = _gameObjectService.GetTrueActorForSpecialTypeActor(kvPair.Key);
+ ImGui.Text($"True actor: {actorIdentifier.ToName()} ({specialResult})");
ImGui.Spacing();
ImGui.Spacing();
diff --git a/CustomizePlus/UI/Windows/MainWindow/Tabs/SettingsTab.cs b/CustomizePlus/UI/Windows/MainWindow/Tabs/SettingsTab.cs
index fe66006..c6160c9 100644
--- a/CustomizePlus/UI/Windows/MainWindow/Tabs/SettingsTab.cs
+++ b/CustomizePlus/UI/Windows/MainWindow/Tabs/SettingsTab.cs
@@ -14,6 +14,7 @@
using CustomizePlus.Profiles;
using CustomizePlus.Templates;
using CustomizePlus.Core.Helpers;
+using CustomizePlus.Armatures.Services;
namespace CustomizePlus.UI.Windows.MainWindow.Tabs;
@@ -22,29 +23,23 @@ public class SettingsTab
private const uint DiscordColor = 0xFFDA8972;
private readonly PluginConfiguration _configuration;
- private readonly TemplateManager _templateManager;
- private readonly ProfileManager _profileManager;
+ private readonly ArmatureManager _armatureManager;
private readonly HookingService _hookingService;
- private readonly SaveService _saveService;
private readonly TemplateEditorManager _templateEditorManager;
private readonly CPlusChangeLog _changeLog;
private readonly MessageService _messageService;
public SettingsTab(
PluginConfiguration configuration,
- TemplateManager templateManager,
- ProfileManager profileManager,
+ ArmatureManager armatureManager,
HookingService hookingService,
- SaveService saveService,
TemplateEditorManager templateEditorManager,
CPlusChangeLog changeLog,
MessageService messageService)
{
_configuration = configuration;
- _templateManager = templateManager;
- _profileManager = profileManager;
+ _armatureManager = armatureManager;
_hookingService = hookingService;
- _saveService = saveService;
_templateEditorManager = templateEditorManager;
_changeLog = changeLog;
_messageService = messageService;
@@ -63,6 +58,7 @@ public void Draw()
using (var child2 = ImRaii.Child("SettingsChild"))
{
+ DrawProfileApplicationSettings();
DrawInterface();
DrawCommands();
DrawAdvancedSettings();
@@ -96,6 +92,73 @@ private void DrawPluginEnabledCheckbox()
}
#endregion
+ #region Profile application settings
+ private void DrawProfileApplicationSettings()
+ {
+ var isShouldDraw = ImGui.CollapsingHeader("Profile Application");
+
+ if (!isShouldDraw)
+ return;
+
+ DrawApplyInCharacterWindowCheckbox();
+ DrawApplyInTryOnCheckbox();
+ DrawApplyInCardsCheckbox();
+ DrawApplyInInspectCheckbox();
+ }
+
+ private void DrawApplyInCharacterWindowCheckbox()
+ {
+ var isChecked = _configuration.ProfileApplicationSettings.ApplyInCharacterWindow;
+
+ if (CtrlHelper.CheckboxWithTextAndHelp("##applyincharwindow", "Apply Profiles in Character Window",
+ "Apply profile for your character in your main character window, if it is set.", ref isChecked))
+ {
+ _configuration.ProfileApplicationSettings.ApplyInCharacterWindow = isChecked;
+ _configuration.Save();
+ _armatureManager.RebindAllArmatures();
+ }
+ }
+
+ private void DrawApplyInTryOnCheckbox()
+ {
+ var isChecked = _configuration.ProfileApplicationSettings.ApplyInTryOn;
+
+ if (CtrlHelper.CheckboxWithTextAndHelp("##applyintryon", "Apply Profiles in Try-On Window",
+ "Apply profile for your character in your try-on, dye preview or glamour plate window, if it is set.", ref isChecked))
+ {
+ _configuration.ProfileApplicationSettings.ApplyInTryOn = isChecked;
+ _configuration.Save();
+ _armatureManager.RebindAllArmatures();
+ }
+ }
+
+ private void DrawApplyInCardsCheckbox()
+ {
+ var isChecked = _configuration.ProfileApplicationSettings.ApplyInCards;
+
+ if (CtrlHelper.CheckboxWithTextAndHelp("##applyincards", "Apply Profiles in Adventurer Cards",
+ "Apply appropriate profile for the adventurer card you are currently looking at.", ref isChecked))
+ {
+ _configuration.ProfileApplicationSettings.ApplyInCards = isChecked;
+ _configuration.Save();
+ _armatureManager.RebindAllArmatures();
+ }
+ }
+
+ private void DrawApplyInInspectCheckbox()
+ {
+ var isChecked = _configuration.ProfileApplicationSettings.ApplyInInspect;
+
+ if (CtrlHelper.CheckboxWithTextAndHelp("##applyininspect", "Apply Profiles in Inspect Window",
+ "Apply appropriate profile for the character you are currently inspecting.", ref isChecked))
+ {
+ _configuration.ProfileApplicationSettings.ApplyInInspect = isChecked;
+ _configuration.Save();
+ _armatureManager.RebindAllArmatures();
+ }
+ }
+ #endregion
+
#region Chat Commands Settings
private void DrawCommands()
{