Added profile priority system, fixed a few issues
This commit is contained in:
@@ -46,7 +46,7 @@ public partial class CustomizePlusIpc
|
||||
.Select(x =>
|
||||
{
|
||||
string path = _profileFileSystem.FindLeaf(x, out var leaf) ? leaf.FullName() : x.Name.Text;
|
||||
return (x.UniqueId, x.Name.Text, path, x.Characters[0].ToNameWithoutOwnerName(), x.Enabled); //todo: proper update to v5
|
||||
return (x.UniqueId, x.Name.Text, path, x.Characters.Count > 0 ? x.Characters[0].ToNameWithoutOwnerName() : "", x.Enabled); //todo: proper update to v5
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
@@ -413,6 +413,7 @@ public unsafe sealed class ArmatureManager : IDisposable
|
||||
type is not ProfileChanged.Type.TemporaryProfileDeleted &&
|
||||
type is not ProfileChanged.Type.AddedCharacter &&
|
||||
type is not ProfileChanged.Type.RemovedCharacter &&
|
||||
type is not ProfileChanged.Type.PriorityChanged &&
|
||||
type is not ProfileChanged.Type.ChangedDefaultProfile &&
|
||||
type is not ProfileChanged.Type.ChangedDefaultLocalPlayerProfile)
|
||||
return;
|
||||
@@ -438,6 +439,26 @@ public unsafe sealed class ArmatureManager : IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
if(type == ProfileChanged.Type.PriorityChanged)
|
||||
{
|
||||
if (!profile.Enabled)
|
||||
return;
|
||||
|
||||
foreach (var character in profile.Characters)
|
||||
{
|
||||
if (!character.IsValid)
|
||||
continue;
|
||||
|
||||
foreach (var armature in GetArmaturesForCharacter(character))
|
||||
{
|
||||
armature.IsPendingProfileRebind = true;
|
||||
_logger.Debug($"ArmatureManager.OnProfileChange profile {profile} priority changed, planning rebind for armature {armature}");
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == ProfileChanged.Type.Toggled)
|
||||
{
|
||||
if (!profile.Enabled && profile.Armatures.Count == 0)
|
||||
|
||||
@@ -87,6 +87,7 @@ internal static class Constants
|
||||
internal static class Colors
|
||||
{
|
||||
public static Vector4 Normal = new Vector4(1, 1, 1, 1);
|
||||
public static Vector4 Info = new Vector4(0.3f, 0.5f, 1f, 1);
|
||||
public static Vector4 Warning = new Vector4(1, 0.5f, 0, 1);
|
||||
public static Vector4 Error = new Vector4(1, 0, 0, 1);
|
||||
}
|
||||
|
||||
@@ -168,6 +168,7 @@ public class CommandService : IDisposable
|
||||
break;
|
||||
}
|
||||
|
||||
//todo: support for multiple profiles
|
||||
Profile? targetProfile = null;
|
||||
|
||||
characterName = subArgumentList[0].Trim();
|
||||
|
||||
@@ -55,6 +55,11 @@ public sealed class Profile : ISavable
|
||||
|
||||
public ProfileType ProfileType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Profile priority when there are several profiles affecting same character
|
||||
/// </summary>
|
||||
public int Priority { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tells us if this profile is not persistent (ex. was made via IPC calls) and should have specific treatement like not being shown in UI, etc.
|
||||
/// WARNING, TEMPLATES FOR TEMPORARY PROFILES *ARE NOT* STORED IN TemplateManager
|
||||
@@ -107,6 +112,7 @@ public sealed class Profile : ISavable
|
||||
["Name"] = Name.Text,
|
||||
["Enabled"] = Enabled,
|
||||
["IsWriteProtected"] = IsWriteProtected,
|
||||
["Priority"] = Priority,
|
||||
["Templates"] = SerializeTemplates()
|
||||
};
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ public sealed class ProfileChanged() : EventWrapper<ProfileChanged.Type, Profile
|
||||
Deleted,
|
||||
Renamed,
|
||||
Toggled,
|
||||
PriorityChanged,
|
||||
AddedCharacter,
|
||||
RemovedCharacter,
|
||||
//ChangedCharacter,
|
||||
|
||||
@@ -13,6 +13,7 @@ using Penumbra.String;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using Penumbra.GameData.Gui;
|
||||
using System.Xml;
|
||||
|
||||
namespace CustomizePlus.Profiles;
|
||||
|
||||
@@ -53,20 +54,16 @@ public partial class ProfileManager : IDisposable
|
||||
|
||||
foreach (var profile in Profiles)
|
||||
{
|
||||
//This will solve any issues if file on disk was manually edited and we have more than a single active profile
|
||||
if (profile.Enabled)
|
||||
SetEnabled(profile, true, true);
|
||||
|
||||
if (_configuration.DefaultProfile == profile.UniqueId)
|
||||
DefaultProfile = profile;
|
||||
|
||||
if (_configuration.DefaultLocalPlayerProfile == profile.UniqueId)
|
||||
DefaultLocalPlayerProfile = profile;
|
||||
}
|
||||
|
||||
//insert temp profiles back into profile list
|
||||
if (temporaryProfiles.Count > 0)
|
||||
{
|
||||
Profiles.AddRange(temporaryProfiles);
|
||||
Profiles.Sort((x, y) => y.IsTemporary.CompareTo(x.IsTemporary));
|
||||
}
|
||||
|
||||
var failed = MoveInvalidNames(invalidNames);
|
||||
if (invalidNames.Count > 0)
|
||||
@@ -135,6 +132,8 @@ public partial class ProfileManager : IDisposable
|
||||
{
|
||||
var profile = LoadProfileV4V5(obj);
|
||||
|
||||
profile.Priority = obj["Priority"]?.ToObject<int>() ?? throw new ArgumentNullException("Priority");
|
||||
|
||||
if (obj["Characters"] is not JArray characterArray)
|
||||
return profile;
|
||||
|
||||
|
||||
@@ -248,51 +248,11 @@ public partial class ProfileManager : IDisposable
|
||||
if (profile.Enabled == value && !force)
|
||||
return;
|
||||
|
||||
var oldValue = profile.Enabled;
|
||||
profile.Enabled = value;
|
||||
|
||||
if (value)
|
||||
{
|
||||
_logger.Debug($"Setting {profile} as enabled...");
|
||||
SaveProfile(profile);
|
||||
|
||||
foreach (var otherProfile in Profiles)
|
||||
{
|
||||
if (otherProfile == profile || !otherProfile.Enabled || otherProfile.IsTemporary)
|
||||
continue;
|
||||
|
||||
bool shouldDisable = false;
|
||||
|
||||
//my god this is ugly
|
||||
foreach(var otherCharacter in otherProfile.Characters)
|
||||
{
|
||||
foreach(var currentCharacter in profile.Characters)
|
||||
{
|
||||
if(otherCharacter.MatchesIgnoringOwnership(currentCharacter))
|
||||
{
|
||||
shouldDisable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldDisable)
|
||||
break;
|
||||
}
|
||||
|
||||
if(shouldDisable)
|
||||
{
|
||||
_logger.Debug($"\t-> {otherProfile} disabled");
|
||||
SetEnabled(otherProfile, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (oldValue != value)
|
||||
{
|
||||
profile.Enabled = value;
|
||||
|
||||
SaveProfile(profile);
|
||||
|
||||
_event.Invoke(ProfileChanged.Type.Toggled, profile, value);
|
||||
}
|
||||
_event.Invoke(ProfileChanged.Type.Toggled, profile, value);
|
||||
}
|
||||
|
||||
public void SetEnabled(Guid guid, bool value)
|
||||
@@ -306,6 +266,21 @@ public partial class ProfileManager : IDisposable
|
||||
throw new ProfileNotFoundException();
|
||||
}
|
||||
|
||||
public void SetPriority(Profile profile, int value)
|
||||
{
|
||||
if (profile.Priority == value)
|
||||
return;
|
||||
|
||||
if (value > int.MaxValue || value < int.MinValue)
|
||||
return;
|
||||
|
||||
profile.Priority = value;
|
||||
|
||||
SaveProfile(profile);
|
||||
|
||||
_event.Invoke(ProfileChanged.Type.PriorityChanged, profile, value);
|
||||
}
|
||||
|
||||
public void DeleteTemplate(Profile profile, int templateIndex)
|
||||
{
|
||||
_logger.Debug($"Deleting template #{templateIndex} from {profile}...");
|
||||
@@ -408,6 +383,7 @@ public partial class ProfileManager : IDisposable
|
||||
|
||||
profile.Enabled = true;
|
||||
profile.ProfileType = ProfileType.Temporary;
|
||||
profile.Priority = int.MaxValue; //Make sure temporary profile is always at max priority
|
||||
|
||||
var permanentIdentifier = identifier.CreatePermanent();
|
||||
profile.Characters.Clear();
|
||||
@@ -423,9 +399,6 @@ public partial class ProfileManager : IDisposable
|
||||
|
||||
Profiles.Add(profile);
|
||||
|
||||
//Make sure temporary profiles come first, so they are returned by all other methods first
|
||||
Profiles.Sort((x, y) => y.IsTemporary.CompareTo(x.IsTemporary));
|
||||
|
||||
_logger.Debug($"Added temporary profile for {permanentIdentifier}");
|
||||
_event.Invoke(ProfileChanged.Type.TemporaryProfileAdded, profile, null);
|
||||
}
|
||||
@@ -457,7 +430,7 @@ public partial class ProfileManager : IDisposable
|
||||
if (!actor.Identifier(_actorManager, out var identifier))
|
||||
throw new ActorNotFoundException();
|
||||
|
||||
var profile = Profiles.FirstOrDefault(x => x.Characters[0] == identifier && x.IsTemporary);
|
||||
var profile = Profiles.FirstOrDefault(x => x.Characters.Count == 1 && x.Characters[0] == identifier && x.IsTemporary);
|
||||
if (profile == null)
|
||||
throw new ProfileNotFoundException();
|
||||
|
||||
@@ -478,7 +451,7 @@ public partial class ProfileManager : IDisposable
|
||||
if (enabledOnly)
|
||||
query = query.Where(x => x.Enabled);
|
||||
|
||||
var profile = query.FirstOrDefault();
|
||||
var profile = query.OrderByDescending(x => x.Priority).FirstOrDefault();
|
||||
|
||||
if (profile == null)
|
||||
return null;
|
||||
@@ -529,7 +502,7 @@ public partial class ProfileManager : IDisposable
|
||||
if (_templateEditorManager.IsEditorActive && _templateEditorManager.EditorProfile.Enabled && IsProfileAppliesToCurrentActor(_templateEditorManager.EditorProfile))
|
||||
yield return _templateEditorManager.EditorProfile;
|
||||
|
||||
foreach (var profile in Profiles)
|
||||
foreach (var profile in Profiles.OrderByDescending(x => x.Priority))
|
||||
{
|
||||
if(profile.Enabled && IsProfileAppliesToCurrentActor(profile))
|
||||
yield return profile;
|
||||
@@ -553,7 +526,7 @@ public partial class ProfileManager : IDisposable
|
||||
if (template == null)
|
||||
yield break;
|
||||
|
||||
foreach (var profile in Profiles)
|
||||
foreach (var profile in Profiles.OrderByDescending(x => x.Priority))
|
||||
if (profile.Templates.Contains(template))
|
||||
yield return profile;
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ public class StateMonitoringTab
|
||||
|
||||
private void DrawProfiles()
|
||||
{
|
||||
foreach (var profile in _profileManager.Profiles.OrderByDescending(x => x.Enabled))
|
||||
foreach (var profile in _profileManager.Profiles.OrderByDescending(x => x.Enabled).ThenByDescending(x => x.Priority))
|
||||
{
|
||||
DrawSingleProfile("root", profile);
|
||||
ImGui.Spacing();
|
||||
@@ -141,7 +141,7 @@ public class StateMonitoringTab
|
||||
//characterName = characterName.Incognify();
|
||||
#endif
|
||||
|
||||
var show = ImGui.CollapsingHeader($"[{(profile.Enabled ? "E" : "D")}] {name} on {characterName} [{profile.ProfileType}] [{profile.UniqueId}]###{prefix}-profile-{profile.UniqueId}");
|
||||
var show = ImGui.CollapsingHeader($"[{(profile.Enabled ? "E" : "D")}] [P:{profile.Priority}] {name} on {characterName} [{profile.ProfileType}] [{profile.UniqueId}]###{prefix}-profile-{profile.UniqueId}");
|
||||
|
||||
if (!show)
|
||||
return;
|
||||
|
||||
@@ -241,6 +241,7 @@ public class ProfileFileSystemSelector : FileSystemSelector<Profile, ProfileStat
|
||||
return false;
|
||||
}
|
||||
|
||||
//todo: priority check
|
||||
var identifier = _gameObjectService.GetCurrentPlayerActorIdentifier();
|
||||
if (leaf.Value.Enabled)
|
||||
state.Color = leaf.Value.Characters.Any(x => x.MatchesIgnoringOwnership(identifier)) ? ColorId.LocalCharacterEnabledProfile : ColorId.EnabledProfile;
|
||||
|
||||
@@ -18,6 +18,7 @@ using Penumbra.String;
|
||||
using static FFXIVClientStructs.FFXIV.Client.LayoutEngine.ILayoutInstance;
|
||||
using CustomizePlus.GameData.Extensions;
|
||||
using CustomizePlus.Core.Extensions;
|
||||
using Dalamud.Interface.Components;
|
||||
|
||||
namespace CustomizePlus.UI.Windows.MainWindow.Tabs.Profiles;
|
||||
|
||||
@@ -33,6 +34,7 @@ public class ProfilePanel
|
||||
private readonly TemplateEditorEvent _templateEditorEvent;
|
||||
|
||||
private string? _newName;
|
||||
private int? _newPriority;
|
||||
private Profile? _changedProfile;
|
||||
|
||||
private Action? _endAction;
|
||||
@@ -212,6 +214,30 @@ public class ProfilePanel
|
||||
}
|
||||
else
|
||||
ImGui.TextUnformatted(_selector.Selected!.Incognito);
|
||||
|
||||
ImGui.TableNextRow();
|
||||
|
||||
ImGuiUtil.DrawFrameColumn("Priority");
|
||||
ImGui.TableNextColumn();
|
||||
|
||||
var priority = _newPriority ?? _selector.Selected!.Priority;
|
||||
|
||||
ImGui.SetNextItemWidth(50);
|
||||
if (ImGui.InputInt("##Priority", ref priority, 0, 0))
|
||||
{
|
||||
_newPriority = priority;
|
||||
_changedProfile = _selector.Selected;
|
||||
}
|
||||
|
||||
if (ImGui.IsItemDeactivatedAfterEdit() && _changedProfile != null)
|
||||
{
|
||||
_manager.SetPriority(_changedProfile, priority);
|
||||
_newPriority = null;
|
||||
_changedProfile = null;
|
||||
}
|
||||
|
||||
ImGuiComponents.HelpMarker("Profiles with a higher number here take precedence before profiles with a lower number.\n" +
|
||||
"That means if two or more profiles affect same character, profile with higher priority will be applied to that character.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,6 +371,36 @@ public class ProfilePanel
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted(!_selector.IncognitoMode ? $"{character.ToNameWithoutOwnerName()}{character.TypeToString()}" : "Incognito");
|
||||
|
||||
var profiles = _manager.GetEnabledProfilesByActor(character).ToList();
|
||||
if (profiles.Count > 1)
|
||||
{
|
||||
//todo: make helper
|
||||
ImGui.SameLine();
|
||||
if(profiles.Any(x => x.IsTemporary))
|
||||
{
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, Constants.Colors.Error);
|
||||
ImGuiUtil.PrintIcon(FontAwesomeIcon.Lock);
|
||||
}
|
||||
else if (profiles[0] != _selector.Selected!)
|
||||
{
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, Constants.Colors.Warning);
|
||||
ImGuiUtil.PrintIcon(FontAwesomeIcon.ExclamationTriangle);
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, Constants.Colors.Info);
|
||||
ImGuiUtil.PrintIcon(FontAwesomeIcon.Star);
|
||||
}
|
||||
|
||||
ImGui.PopStyleColor();
|
||||
|
||||
if (profiles.Any(x => x.IsTemporary))
|
||||
ImGuiUtil.HoverTooltip("This character is being affected by temporary profile set by external plugin. This profile will not be applied!");
|
||||
else
|
||||
ImGuiUtil.HoverTooltip(profiles[0] != _selector.Selected! ? "Several profiles are trying to affect this character. This profile will not be applied!" :
|
||||
"Several profiles are trying to affect this character. This profile is being applied.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user