Skip to content

Commit

Permalink
Actor resolving fixes for UI actors
Browse files Browse the repository at this point in the history
Profiles should now properly apply to UI actors in Crystalline Conflict and other places
Added ability to control which parts of the UI profiles are applying to
  • Loading branch information
RisaDev committed Apr 11, 2024
1 parent c663e3c commit 99990c9
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 64 deletions.
45 changes: 1 addition & 44 deletions CustomizePlus.GameData/Extensions/ActorIdentifierExtensions.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -80,48 +81,4 @@ public static bool IsAllowedForProfiles(this ActorIdentifier identifier)
return false;
}
}

/// <summary>
/// Get "true" actor for special actors. Returns ActorIdentifier.Invalid for non-special actors or if actor cannot be found.
/// </summary>
/// <param name="identifier"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
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;
}
}
13 changes: 10 additions & 3 deletions CustomizePlus/Armatures/Services/ArmatureManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,15 @@ public void OnGameObjectMove(Actor actor)
ApplyRootTranslation(armature, actor);
}

/// <summary>
/// Force profile rebind for all armatures
/// </summary>
public void RebindAllArmatures()
{
foreach (var kvPair in Armatures)
kvPair.Value.IsPendingProfileRebind = true;
}

/// <summary>
/// Deletes armatures which no longer have actor associated with them and creates armatures for new actors
/// </summary>
Expand Down Expand Up @@ -542,9 +551,7 @@ private IEnumerable<Armature> 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;
Expand Down
11 changes: 11 additions & 0 deletions CustomizePlus/Configuration/Data/PluginConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
80 changes: 77 additions & 3 deletions CustomizePlus/Game/Services/GameObjectService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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()
Expand Down Expand Up @@ -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;
Expand All @@ -80,4 +88,70 @@ public Actor GetLocalPlayerActor()
{
return _objectTable.CreateObjectReference(actor);
}


/// <summary>
/// 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.
/// </summary>
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,
}
}
7 changes: 5 additions & 2 deletions CustomizePlus/Profiles/ProfileManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
using System.Runtime.Serialization;
using CustomizePlus.Game.Services;

namespace CustomizePlus.Profiles;

Expand All @@ -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;
Expand All @@ -56,6 +58,7 @@ public ProfileManager(
Logger logger,
PluginConfiguration configuration,
ActorManager actorManager,
GameObjectService gameObjectService,
ProfileChanged @event,
TemplateChanged templateChangedEvent,
ReloadEvent reloadEvent,
Expand All @@ -67,6 +70,7 @@ public ProfileManager(
_logger = logger;
_configuration = configuration;
_actorManager = actorManager;
_gameObjectService = gameObjectService;
_event = @event;
_templateChangedEvent = templateChangedEvent;
_templateChangedEvent.Subscribe(OnTemplateChange, TemplateChanged.Priority.ProfileManager);
Expand Down Expand Up @@ -477,8 +481,7 @@ public IEnumerable<Profile> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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()
Expand Down Expand Up @@ -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();
Expand Down
Loading

0 comments on commit 99990c9

Please sign in to comment.