Added ability to apply profile to several characters

This commit is contained in:
RisaDev
2024-10-19 02:55:38 +03:00
parent 3ff5806322
commit 7085cf616c
14 changed files with 324 additions and 164 deletions

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using CustomizePlus.Armatures.Data;
using CustomizePlus.Core.Data;
using CustomizePlus.Core.Extensions;
@@ -32,7 +33,8 @@ public sealed class Profile : ISavable
/* [Obsolete("To be removed in the future versions")]
public LowerString CharacterName { get; set; } = LowerString.Empty;*/
public ActorIdentifier Character { get; set; } = ActorIdentifier.Invalid;
//public ActorIdentifier Character { get; set; } = ActorIdentifier.Invalid;
public List<ActorIdentifier> Characters { get; set; } = new();
public LowerString Name { get; set; } = LowerString.Empty;
@@ -78,7 +80,7 @@ public sealed class Profile : ISavable
/// <param name="original"></param>
public Profile(Profile original) : this()
{
Character = original.Character;
Characters = original.Characters.ToList();
foreach (var template in original.Templates)
{
@@ -88,7 +90,7 @@ public sealed class Profile : ISavable
public override string ToString()
{
return $"Profile '{Name.Text.Incognify()}' on {Character.Incognito(null)} [{UniqueId}]";
return $"Profile '{Name.Text.Incognify()}' on {string.Join(',', Characters.Select(x => x.Incognito(null)))} [{UniqueId}]";
}
#region Serialization
@@ -101,7 +103,7 @@ public sealed class Profile : ISavable
["UniqueId"] = UniqueId,
["CreationDate"] = CreationDate,
["ModifiedDate"] = ModifiedDate,
["Character"] = Character.ToJson(),
["Characters"] = SerializeCharacters(),
["Name"] = Name.Text,
["Enabled"] = Enabled,
["IsWriteProtected"] = IsWriteProtected,
@@ -124,6 +126,16 @@ public sealed class Profile : ISavable
return ret;
}
private JArray SerializeCharacters()
{
var ret = new JArray();
foreach (var character in Characters)
{
ret.Add(character.ToJson());
}
return ret;
}
#endregion
//Loading is in ProfileManager

View File

@@ -15,7 +15,9 @@ public sealed class ProfileChanged() : EventWrapper<ProfileChanged.Type, Profile
Deleted,
Renamed,
Toggled,
ChangedCharacter,
AddedCharacter,
RemovedCharacter,
//ChangedCharacter,
AddedTemplate,
RemovedTemplate,
MovedTemplate,

View File

@@ -101,30 +101,30 @@ public partial class ProfileManager : IDisposable
var nameWordsCnt = characterName.Split(' ').Length;
if (_reverseNameDicts.TryGetID(ObjectKind.EventNpc, characterName, out var id))
profile.Character = _actorManager.CreateNpc(ObjectKind.EventNpc, new NpcId(id));
profile.Characters.Add(_actorManager.CreateNpc(ObjectKind.EventNpc, new NpcId(id)));
else if (_reverseNameDicts.TryGetID(ObjectKind.BattleNpc, characterName, out id))
profile.Character = _actorManager.CreateNpc(ObjectKind.BattleNpc, new NpcId(id));
profile.Characters.Add(_actorManager.CreateNpc(ObjectKind.BattleNpc, new NpcId(id)));
else if (_reverseNameDicts.TryGetID(ObjectKind.MountType, characterName, out id))
{
var currentPlayer = _actorManager.GetCurrentPlayer();
profile.Character = _actorManager.CreateOwned(currentPlayer.PlayerName, currentPlayer.HomeWorld, ObjectKind.MountType, new NpcId(id));
profile.Characters.Add(_actorManager.CreateOwned(currentPlayer.PlayerName, currentPlayer.HomeWorld, ObjectKind.MountType, new NpcId(id)));
}
else if (_reverseNameDicts.TryGetID(ObjectKind.Companion, characterName, out id))
{
var currentPlayer = _actorManager.GetCurrentPlayer();
profile.Character = _actorManager.CreateOwned(currentPlayer.PlayerName, currentPlayer.HomeWorld, ObjectKind.Companion, new NpcId(id));
profile.Characters.Add(_actorManager.CreateOwned(currentPlayer.PlayerName, currentPlayer.HomeWorld, ObjectKind.Companion, new NpcId(id)));
}
else if (nameWordsCnt == 2)
profile.Character = _actorManager.CreatePlayer(ByteString.FromStringUnsafe(characterName, false), WorldId.AnyWorld);
profile.Characters.Add(_actorManager.CreatePlayer(ByteString.FromStringUnsafe(characterName, false), WorldId.AnyWorld));
else
{
_logger.Warning($"Unable to automatically migrate \"{profile.Name}\" to V5, unknown character name: {characterName}");
_messageService.NotificationMessage($"Unable to detect character type for profile \"{profile.Name}\", please set character for this profile manually.", Dalamud.Interface.ImGuiNotification.NotificationType.Error);
}
if (profile.Character.IsValid)
if (profile.Characters.Count > 0)
{
_logger.Debug($"Upgraded profile \"{profile.Name}\" to V5: {characterName} -> {profile.Character}. Save queued.");
_logger.Debug($"Upgraded profile \"{profile.Name}\" to V5: {characterName} -> {profile.Characters[0]}. Save queued.");
_saveService.QueueSave(profile);
}
@@ -135,14 +135,32 @@ public partial class ProfileManager : IDisposable
{
var profile = LoadProfileV4V5(obj);
var character = _actorManager.FromJson(obj["Character"] as JObject);
if (obj["Characters"] is not JArray characterArray)
return profile;
profile.Character = character;
foreach(var characterObj in characterArray)
{
if (characterObj is not JObject characterObjCast)
{
//todo: warning
continue;
}
var character = _actorManager.FromJson(characterObjCast);
if(!character.IsValid)
{
//todo: warning
continue;
}
profile.Characters.Add(character);
}
return profile;
}
//V4 and V5 are mostly not different, so common loading logic is here
//V4 and V5 are mostly the same, so common loading logic is here
private Profile LoadProfileV4V5(JObject obj)
{
var creationDate = obj["CreationDate"]?.ToObject<DateTimeOffset>() ?? throw new ArgumentNullException("CreationDate");

View File

@@ -177,15 +177,14 @@ public partial class ProfileManager : IDisposable
}
/// <summary>
/// Change character associated with profile
/// Add character to profile
/// </summary>
public void ChangeCharacter(Profile profile, ActorIdentifier actorIdentifier)
public void AddCharacter(Profile profile, ActorIdentifier actorIdentifier)
{
if (!actorIdentifier.IsValid || actorIdentifier.MatchesIgnoringOwnership(profile.Character))
if (!actorIdentifier.IsValid || profile.Characters.Any(x => actorIdentifier.MatchesIgnoringOwnership(x)) || profile.IsTemporary)
return;
var oldCharacter = profile.Character;
profile.Character = actorIdentifier;
profile.Characters.Add(actorIdentifier);
//Called so all other active profiles for new character name get disabled
//saving is performed there
@@ -193,8 +192,28 @@ public partial class ProfileManager : IDisposable
SaveProfile(profile);
_logger.Debug($"Changed character for profile {profile.UniqueId}.");
_event.Invoke(ProfileChanged.Type.ChangedCharacter, profile, oldCharacter);
_logger.Debug($"Add character for profile {profile.UniqueId}.");
_event.Invoke(ProfileChanged.Type.AddedCharacter, profile, actorIdentifier);
}
/// <summary>
/// Delete character from profile
/// </summary>
public void DeleteCharacter(Profile profile, ActorIdentifier actorIdentifier)
{
if (!actorIdentifier.IsValid || !profile.Characters.Any(x => actorIdentifier.MatchesIgnoringOwnership(x)) || profile.IsTemporary)
return;
profile.Characters.Remove(actorIdentifier);
//Called so all other active profiles for new character name get disabled
//saving is performed there
//SetEnabled(profile, profile.Enabled, true); //todo
SaveProfile(profile);
_logger.Debug($"Removed character from profile {profile.UniqueId}.");
_event.Invoke(ProfileChanged.Type.RemovedCharacter, profile, actorIdentifier);
}
/// <summary>
@@ -235,11 +254,34 @@ public partial class ProfileManager : IDisposable
{
_logger.Debug($"Setting {profile} as enabled...");
foreach (var otherProfile in Profiles
.Where(x => x.Character.MatchesIgnoringOwnership(profile.Character) && x != profile && x.Enabled && !x.IsTemporary))
foreach (var otherProfile in Profiles)
{
_logger.Debug($"\t-> {otherProfile} disabled");
SetEnabled(otherProfile, false);
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);
}
}
}
@@ -366,12 +408,15 @@ public partial class ProfileManager : IDisposable
profile.Enabled = true;
profile.ProfileType = ProfileType.Temporary;
profile.Character = identifier.CreatePermanent(); //warn: identifier must not be AnyWorld or stuff will break!
var existingProfile = Profiles.FirstOrDefault(x => x.Character.MatchesIgnoringOwnership(profile.Character) && x.IsTemporary);
var permanentIdentifier = identifier.CreatePermanent();
profile.Characters.Clear();
profile.Characters.Add(permanentIdentifier); //warn: identifier must not be AnyWorld or stuff will break!
var existingProfile = Profiles.FirstOrDefault(p => p.Characters.Count == 1 && p.Characters[0].MatchesIgnoringOwnership(permanentIdentifier) && p.IsTemporary);
if (existingProfile != null)
{
_logger.Debug($"Temporary profile for {existingProfile.Character.Incognito(null)} already exists, removing...");
_logger.Debug($"Temporary profile for {permanentIdentifier.Incognito(null)} already exists, removing...");
Profiles.Remove(existingProfile);
_event.Invoke(ProfileChanged.Type.TemporaryProfileDeleted, existingProfile, null);
}
@@ -381,16 +426,19 @@ public partial class ProfileManager : IDisposable
//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 {profile.Character}");
_logger.Debug($"Added temporary profile for {permanentIdentifier}");
_event.Invoke(ProfileChanged.Type.TemporaryProfileAdded, profile, null);
}
public void RemoveTemporaryProfile(Profile profile)
{
if (!profile.IsTemporary)
return;
if (!Profiles.Remove(profile))
throw new ProfileNotFoundException();
_logger.Debug($"Removed temporary profile for {profile.Character.Incognito(null)}");
_logger.Debug($"Removed temporary profile for {profile.Characters[0].Incognito(null)}");
_event.Invoke(ProfileChanged.Type.TemporaryProfileDeleted, profile, null);
}
@@ -409,7 +457,7 @@ public partial class ProfileManager : IDisposable
if (!actor.Identifier(_actorManager, out var identifier))
throw new ActorNotFoundException();
var profile = Profiles.FirstOrDefault(x => x.Character == identifier && x.IsTemporary);
var profile = Profiles.FirstOrDefault(x => x.Characters[0] == identifier && x.IsTemporary);
if (profile == null)
throw new ProfileNotFoundException();
@@ -426,7 +474,7 @@ public partial class ProfileManager : IDisposable
if (!actorIdentifier.IsValid)
return null;
var query = Profiles.Where(x => x.Character.MatchesIgnoringOwnership(actorIdentifier) && !x.IsTemporary);
var query = Profiles.Where(p => p.Characters.Any(x => x.MatchesIgnoringOwnership(actorIdentifier)) && !p.IsTemporary);
if (enabledOnly)
query = query.Where(x => x.Enabled);
@@ -475,7 +523,7 @@ public partial class ProfileManager : IDisposable
if (actorIdentifier.Type == IdentifierType.Owned && !actorIdentifier.IsOwnedByLocalPlayer())
return false;
return profile.Character.MatchesIgnoringOwnership(actorIdentifier);
return profile.Characters.Any(x => x.MatchesIgnoringOwnership(actorIdentifier));
}
if (_templateEditorManager.IsEditorActive && _templateEditorManager.EditorProfile.Enabled && IsProfileAppliesToCurrentActor(_templateEditorManager.EditorProfile))
@@ -585,7 +633,7 @@ public partial class ProfileManager : IDisposable
if (!Profiles.Remove(profile))
return;
_logger.Debug($"ProfileManager.OnArmatureChange: Removed unused temporary profile for {profile.Character.Incognito(null)}");
_logger.Debug($"ProfileManager.OnArmatureChange: Removed unused temporary profile for {profile.Characters[0].Incognito(null)}");
_event.Invoke(ProfileChanged.Type.TemporaryProfileDeleted, profile, null);
}