mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 10:17:23 +01:00
639 lines
31 KiB
C#
639 lines
31 KiB
C#
using Dalamud.Interface.ImGuiNotification;
|
|
using Dalamud.Plugin;
|
|
using Glamourer.Events;
|
|
using Glamourer.State;
|
|
using Newtonsoft.Json.Linq;
|
|
using OtterGui.Classes;
|
|
using Penumbra.Api.Enums;
|
|
using Penumbra.Api.Helpers;
|
|
using Penumbra.GameData.Interop;
|
|
using Penumbra.GameData.Structs;
|
|
|
|
namespace Glamourer.Interop.Penumbra;
|
|
|
|
public readonly record struct Mod(string Name, string DirectoryName) : IComparable<Mod>
|
|
{
|
|
public int CompareTo(Mod other)
|
|
{
|
|
var nameComparison = string.Compare(Name, other.Name, StringComparison.Ordinal);
|
|
if (nameComparison != 0)
|
|
return nameComparison;
|
|
|
|
return string.Compare(DirectoryName, other.DirectoryName, StringComparison.Ordinal);
|
|
}
|
|
}
|
|
|
|
public readonly record struct ModSettings(Dictionary<string, List<string>> Settings, int Priority, bool Enabled, bool ForceInherit, bool Remove)
|
|
{
|
|
public ModSettings()
|
|
: this(new Dictionary<string, List<string>>(), 0, false, false, false)
|
|
{ }
|
|
|
|
public static ModSettings Empty
|
|
=> new();
|
|
}
|
|
|
|
public class PenumbraService : IDisposable
|
|
{
|
|
public const int RequiredPenumbraBreakingVersion = 5;
|
|
public const int RequiredPenumbraFeatureVersion = 8;
|
|
|
|
private const int KeyFixed = -1610;
|
|
private const string NameFixed = "Glamourer (Automation)";
|
|
private const int KeyManual = -6160;
|
|
private const string NameManual = "Glamourer (Manually)";
|
|
|
|
private readonly IDalamudPluginInterface _pluginInterface;
|
|
private readonly Configuration _config;
|
|
private readonly EventSubscriber<ChangedItemType, uint> _tooltipSubscriber;
|
|
private readonly EventSubscriber<MouseButton, ChangedItemType, uint> _clickSubscriber;
|
|
private readonly EventSubscriber<nint, Guid, nint, nint, nint> _creatingCharacterBase;
|
|
private readonly EventSubscriber<nint, Guid, nint> _createdCharacterBase;
|
|
private readonly EventSubscriber<ModSettingChange, Guid, string, bool> _modSettingChanged;
|
|
private readonly EventSubscriber<JObject, string, Guid> _pcpParsed;
|
|
private readonly EventSubscriber<JObject, ushort, string> _pcpCreated;
|
|
|
|
private global::Penumbra.Api.IpcSubscribers.GetCollectionsByIdentifier? _collectionByIdentifier;
|
|
private global::Penumbra.Api.IpcSubscribers.GetCollections? _collections;
|
|
private global::Penumbra.Api.IpcSubscribers.RedrawObject? _redraw;
|
|
private global::Penumbra.Api.IpcSubscribers.GetCollectionForObject? _objectCollection;
|
|
private global::Penumbra.Api.IpcSubscribers.GetModList? _getMods;
|
|
private global::Penumbra.Api.IpcSubscribers.GetCollection? _currentCollection;
|
|
private global::Penumbra.Api.IpcSubscribers.GetCurrentModSettingsWithTemp? _getCurrentSettingsWithTemp;
|
|
private global::Penumbra.Api.IpcSubscribers.GetCurrentModSettings? _getCurrentSettings;
|
|
private global::Penumbra.Api.IpcSubscribers.GetAllModSettings? _getAllSettings;
|
|
private global::Penumbra.Api.IpcSubscribers.TryInheritMod? _inheritMod;
|
|
private global::Penumbra.Api.IpcSubscribers.TrySetMod? _setMod;
|
|
private global::Penumbra.Api.IpcSubscribers.TrySetModPriority? _setModPriority;
|
|
private global::Penumbra.Api.IpcSubscribers.TrySetModSetting? _setModSetting;
|
|
private global::Penumbra.Api.IpcSubscribers.TrySetModSettings? _setModSettings;
|
|
private global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettings? _setTemporaryModSettings;
|
|
private global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettingsPlayer? _setTemporaryModSettingsPlayer;
|
|
private global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettings? _removeTemporaryModSettings;
|
|
private global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettingsPlayer? _removeTemporaryModSettingsPlayer;
|
|
private global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettings? _removeAllTemporaryModSettings;
|
|
private global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettingsPlayer? _removeAllTemporaryModSettingsPlayer;
|
|
private global::Penumbra.Api.IpcSubscribers.QueryTemporaryModSettings? _queryTemporaryModSettings;
|
|
private global::Penumbra.Api.IpcSubscribers.QueryTemporaryModSettingsPlayer? _queryTemporaryModSettingsPlayer;
|
|
private global::Penumbra.Api.IpcSubscribers.OpenMainWindow? _openModPage;
|
|
private global::Penumbra.Api.IpcSubscribers.GetChangedItems? _getChangedItems;
|
|
private IReadOnlyList<(string ModDirectory, IReadOnlyDictionary<string, object?> ChangedItems)>? _changedItems;
|
|
private Func<string, (string ModDirectory, string ModName)[]>? _checkCurrentChangedItems;
|
|
private Func<int, int>? _checkCutsceneParent;
|
|
private Func<nint, nint>? _getGameObject;
|
|
|
|
private readonly IDisposable _initializedEvent;
|
|
private readonly IDisposable _disposedEvent;
|
|
|
|
private readonly PenumbraReloaded _penumbraReloaded;
|
|
|
|
public bool Available { get; private set; }
|
|
public int CurrentMajor { get; private set; }
|
|
public int CurrentMinor { get; private set; }
|
|
public DateTime AttachTime { get; private set; }
|
|
|
|
public PenumbraService(IDalamudPluginInterface pi, PenumbraReloaded penumbraReloaded, Configuration config)
|
|
{
|
|
_pluginInterface = pi;
|
|
_penumbraReloaded = penumbraReloaded;
|
|
_config = config;
|
|
_initializedEvent = global::Penumbra.Api.IpcSubscribers.Initialized.Subscriber(pi, Reattach);
|
|
_disposedEvent = global::Penumbra.Api.IpcSubscribers.Disposed.Subscriber(pi, Unattach);
|
|
_tooltipSubscriber = global::Penumbra.Api.IpcSubscribers.ChangedItemTooltip.Subscriber(pi);
|
|
_clickSubscriber = global::Penumbra.Api.IpcSubscribers.ChangedItemClicked.Subscriber(pi);
|
|
_createdCharacterBase = global::Penumbra.Api.IpcSubscribers.CreatedCharacterBase.Subscriber(pi);
|
|
_creatingCharacterBase = global::Penumbra.Api.IpcSubscribers.CreatingCharacterBase.Subscriber(pi);
|
|
_modSettingChanged = global::Penumbra.Api.IpcSubscribers.ModSettingChanged.Subscriber(pi);
|
|
_pcpCreated = global::Penumbra.Api.IpcSubscribers.CreatingPcp.Subscriber(pi);
|
|
_pcpParsed = global::Penumbra.Api.IpcSubscribers.ParsingPcp.Subscriber(pi);
|
|
Reattach();
|
|
}
|
|
|
|
public event Action<MouseButton, ChangedItemType, uint> Click
|
|
{
|
|
add => _clickSubscriber.Event += value;
|
|
remove => _clickSubscriber.Event -= value;
|
|
}
|
|
|
|
public event Action<ChangedItemType, uint> Tooltip
|
|
{
|
|
add => _tooltipSubscriber.Event += value;
|
|
remove => _tooltipSubscriber.Event -= value;
|
|
}
|
|
|
|
|
|
public event Action<nint, Guid, nint, nint, nint> CreatingCharacterBase
|
|
{
|
|
add => _creatingCharacterBase.Event += value;
|
|
remove => _creatingCharacterBase.Event -= value;
|
|
}
|
|
|
|
public event Action<nint, Guid, nint> CreatedCharacterBase
|
|
{
|
|
add => _createdCharacterBase.Event += value;
|
|
remove => _createdCharacterBase.Event -= value;
|
|
}
|
|
|
|
public event Action<ModSettingChange, Guid, string, bool> ModSettingChanged
|
|
{
|
|
add => _modSettingChanged.Event += value;
|
|
remove => _modSettingChanged.Event -= value;
|
|
}
|
|
|
|
public event Action<JObject, ushort, string> PcpCreated
|
|
{
|
|
add => _pcpCreated.Event += value;
|
|
remove => _pcpCreated.Event -= value;
|
|
}
|
|
|
|
public event Action<JObject, string, Guid> PcpParsed
|
|
{
|
|
add => _pcpParsed.Event += value;
|
|
remove => _pcpParsed.Event -= value;
|
|
}
|
|
|
|
public Dictionary<Guid, string> GetCollections()
|
|
=> Available ? _collections!.Invoke() : [];
|
|
|
|
public ModSettings GetModSettings(in Mod mod, out string source)
|
|
{
|
|
source = string.Empty;
|
|
if (!Available)
|
|
return ModSettings.Empty;
|
|
|
|
try
|
|
{
|
|
var collection = _currentCollection!.Invoke(ApiCollectionType.Current);
|
|
return GetSettings(collection!.Value.Id, mod.DirectoryName, mod.Name, out source);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Glamourer.Log.Error($"Error fetching mod settings for {mod.DirectoryName} from Penumbra:\n{ex}");
|
|
return ModSettings.Empty;
|
|
}
|
|
}
|
|
|
|
private ModSettings GetSettings(Guid collection, string modDirectory, string modName, out string source)
|
|
{
|
|
if (_getCurrentSettingsWithTemp != null)
|
|
{
|
|
source = string.Empty;
|
|
var (ec, tuple) = _getCurrentSettingsWithTemp!.Invoke(collection, modDirectory, modName, false, false, KeyFixed);
|
|
if (ec is not PenumbraApiEc.Success)
|
|
return ModSettings.Empty;
|
|
|
|
return tuple.HasValue
|
|
? new ModSettings(tuple.Value.Item3, tuple.Value.Item2, tuple.Value.Item1, false, false)
|
|
: ModSettings.Empty;
|
|
}
|
|
|
|
if (_queryTemporaryModSettings != null)
|
|
{
|
|
var tempEc = _queryTemporaryModSettings.Invoke(collection, modDirectory, out var tempTuple, out source, 0, modName);
|
|
if (tempEc is PenumbraApiEc.Success && tempTuple != null)
|
|
return new ModSettings(tempTuple.Value.Settings, tempTuple.Value.Priority, tempTuple.Value.Enabled,
|
|
tempTuple.Value.ForceInherit, false);
|
|
}
|
|
|
|
source = string.Empty;
|
|
var (ec2, tuple2) = _getCurrentSettings!.Invoke(collection, modDirectory, modName);
|
|
if (ec2 is not PenumbraApiEc.Success)
|
|
return ModSettings.Empty;
|
|
|
|
return tuple2.HasValue
|
|
? new ModSettings(tuple2.Value.Item3, tuple2.Value.Item2, tuple2.Value.Item1, false, false)
|
|
: ModSettings.Empty;
|
|
}
|
|
|
|
public (Guid Id, string Name)? CollectionByIdentifier(string identifier)
|
|
{
|
|
if (!Available)
|
|
return null;
|
|
|
|
var ret = _collectionByIdentifier!.Invoke(identifier);
|
|
if (ret.Count == 0)
|
|
return null;
|
|
|
|
return ret[0];
|
|
}
|
|
|
|
public IReadOnlyList<(Mod Mod, ModSettings Settings, int Count)> GetMods(IReadOnlyList<string> data)
|
|
{
|
|
if (!Available)
|
|
return [];
|
|
|
|
try
|
|
{
|
|
var allMods = _getMods!.Invoke();
|
|
var currentCollection = _currentCollection!.Invoke(ApiCollectionType.Current);
|
|
var withSettings = WithSettings(allMods, currentCollection!.Value.Id);
|
|
var withCounts = WithCounts(withSettings, allMods.Count);
|
|
return OrderList(withCounts, allMods.Count);
|
|
|
|
IEnumerable<(Mod Mod, ModSettings Settings)> WithSettings(Dictionary<string, string> mods, Guid collection)
|
|
{
|
|
if (_getAllSettings != null)
|
|
{
|
|
var allSettings = _getAllSettings.Invoke(collection, false, false, KeyFixed);
|
|
if (allSettings.Item1 is PenumbraApiEc.Success)
|
|
return mods.Select(m => (new Mod(m.Value, m.Key),
|
|
allSettings.Item2!.TryGetValue(m.Key, out var s)
|
|
? new ModSettings(s.Item3, s.Item2, s.Item1, s is { Item4: true, Item5: true }, false)
|
|
: ModSettings.Empty));
|
|
}
|
|
|
|
return mods.Select(m => (new Mod(m.Value, m.Key), GetSettings(collection, m.Key, m.Value, out _)));
|
|
}
|
|
|
|
IEnumerable<(Mod Mod, ModSettings Settings, int Count)> WithCounts(IEnumerable<(Mod Mod, ModSettings Settings)> mods, int count)
|
|
{
|
|
if (_changedItems != null && _changedItems.Count == count)
|
|
return mods.Select((m, idx) => (m.Mod, m.Settings, CountItems(_changedItems[idx].ChangedItems, data)));
|
|
|
|
return mods.Select(p => (p.Item1, p.Item2, CountItems(_getChangedItems!.Invoke(p.Item1.DirectoryName, p.Item1.Name), data)));
|
|
|
|
static int CountItems(IReadOnlyDictionary<string, object?> dict, IReadOnlyList<string> data)
|
|
=> data.Count(dict.ContainsKey);
|
|
}
|
|
|
|
static IReadOnlyList<(Mod Mod, ModSettings Settings, int Count)> OrderList(
|
|
IEnumerable<(Mod Mod, ModSettings Settings, int Count)> enumerable, int count)
|
|
{
|
|
var array = new (Mod Mod, ModSettings Settings, int Count)[count];
|
|
var i = 0;
|
|
foreach (var t in enumerable.OrderByDescending(p => p.Item2.Enabled)
|
|
.ThenByDescending(p => p.Item3)
|
|
.ThenBy(p => p.Item1.Name)
|
|
.ThenBy(p => p.Item1.DirectoryName)
|
|
.ThenByDescending(p => p.Item2.Priority))
|
|
array[i++] = t;
|
|
return array;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Glamourer.Log.Error($"Error fetching mods from Penumbra:\n{ex}");
|
|
return [];
|
|
}
|
|
}
|
|
|
|
public void OpenModPage(Mod mod)
|
|
{
|
|
if (!Available)
|
|
return;
|
|
|
|
if (_openModPage!.Invoke(TabType.Mods, mod.DirectoryName, mod.Name) == PenumbraApiEc.ModMissing)
|
|
Glamourer.Messager.NotificationMessage($"Could not open the mod {mod.Name}, no fitting mod was found in your Penumbra install.",
|
|
NotificationType.Info, false);
|
|
}
|
|
|
|
public (Guid Id, string Name) CurrentCollection
|
|
=> Available ? _currentCollection!.Invoke(ApiCollectionType.Current)!.Value : (Guid.Empty, "<Unavailable>");
|
|
|
|
/// <summary>
|
|
/// Try to set all mod settings as desired. Only sets when the mod should be enabled.
|
|
/// If it is disabled, ignore all other settings.
|
|
/// </summary>
|
|
public string SetMod(Mod mod, ModSettings settings, StateSource source, bool respectManual, Guid? collectionInput = null,
|
|
ObjectIndex? index = null)
|
|
{
|
|
if (!Available)
|
|
return "Penumbra is not available.";
|
|
|
|
var sb = new StringBuilder();
|
|
try
|
|
{
|
|
var collection = collectionInput ?? _currentCollection!.Invoke(ApiCollectionType.Current)!.Value.Id;
|
|
if (_config.UseTemporarySettings && _setTemporaryModSettings != null)
|
|
SetModTemporary(sb, mod, settings, collection, respectManual, index, source);
|
|
else
|
|
SetModPermanent(sb, mod, settings, collection);
|
|
|
|
return sb.ToString();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return sb.AppendLine(ex.Message).ToString();
|
|
}
|
|
}
|
|
|
|
public void RemoveAllTemporarySettings(Guid collection, StateSource source)
|
|
=> _removeAllTemporaryModSettings?.Invoke(collection, source.IsFixed() ? KeyFixed : KeyManual);
|
|
|
|
public void RemoveAllTemporarySettings(ObjectIndex index, StateSource source)
|
|
=> _removeAllTemporaryModSettingsPlayer?.Invoke(index.Index, source.IsFixed() ? KeyFixed : KeyManual);
|
|
|
|
public void ClearAllTemporarySettings(bool fix, bool manual)
|
|
{
|
|
if (!Available || _removeAllTemporaryModSettings == null)
|
|
return;
|
|
|
|
var collections = _collections!.Invoke();
|
|
foreach (var collection in collections)
|
|
{
|
|
if (fix)
|
|
RemoveAllTemporarySettings(collection.Key, StateSource.Fixed);
|
|
if (manual)
|
|
RemoveAllTemporarySettings(collection.Key, StateSource.Manual);
|
|
}
|
|
}
|
|
|
|
public (string ModDirectory, string ModName)[] CheckCurrentChangedItem(string changedItem)
|
|
=> _checkCurrentChangedItems?.Invoke(changedItem) ?? [];
|
|
|
|
private void SetModTemporary(StringBuilder sb, Mod mod, ModSettings settings, Guid collection, bool respectManual, ObjectIndex? index,
|
|
StateSource source)
|
|
{
|
|
var (key, name) = source.IsFixed() ? (KeyFixed, NameFixed) : (KeyManual, NameManual);
|
|
// Check for existing manual settings and do not apply fixed on top of them if respecting manual changes.
|
|
if (key is KeyFixed && respectManual)
|
|
{
|
|
var existingSource = string.Empty;
|
|
var ec = index.HasValue
|
|
? _queryTemporaryModSettingsPlayer?.Invoke(index.Value.Index, mod.DirectoryName, out _,
|
|
out existingSource, key, mod.Name)
|
|
?? PenumbraApiEc.InvalidArgument
|
|
: _queryTemporaryModSettings?.Invoke(collection, mod.DirectoryName, out _,
|
|
out existingSource, key, mod.Name)
|
|
?? PenumbraApiEc.InvalidArgument;
|
|
if (ec is PenumbraApiEc.Success && existingSource is NameManual)
|
|
{
|
|
Glamourer.Log.Debug(
|
|
$"Skipped applying mod settings for [{mod.Name}] through automation because manual settings from Glamourer existed.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
var ex = settings.Remove
|
|
? index.HasValue
|
|
? _removeTemporaryModSettingsPlayer!.Invoke(index.Value.Index, mod.DirectoryName, key, mod.Name)
|
|
: _removeTemporaryModSettings!.Invoke(collection, mod.DirectoryName, key, mod.Name)
|
|
: index.HasValue
|
|
? _setTemporaryModSettingsPlayer!.Invoke(index.Value.Index, mod.DirectoryName, settings.ForceInherit, settings.Enabled,
|
|
settings.Priority,
|
|
settings.Settings.ToDictionary(kvp => kvp.Key, kvp => (IReadOnlyList<string>)kvp.Value), name, key, mod.Name)
|
|
: _setTemporaryModSettings!.Invoke(collection, mod.DirectoryName, settings.ForceInherit, settings.Enabled, settings.Priority,
|
|
settings.Settings.ToDictionary(kvp => kvp.Key, kvp => (IReadOnlyList<string>)kvp.Value), name, key, mod.Name);
|
|
switch (ex)
|
|
{
|
|
case PenumbraApiEc.InvalidArgument:
|
|
sb.Append($"No actor with index {index!.Value.Index} could be identified.");
|
|
return;
|
|
case PenumbraApiEc.ModMissing:
|
|
sb.Append($"The mod {mod.Name} [{mod.DirectoryName}] could not be found.");
|
|
return;
|
|
case PenumbraApiEc.CollectionMissing:
|
|
sb.Append($"The collection {collection} could not be found.");
|
|
return;
|
|
case PenumbraApiEc.TemporarySettingImpossible:
|
|
sb.Append($"The collection {collection} can not have settings.");
|
|
return;
|
|
case PenumbraApiEc.TemporarySettingDisallowed:
|
|
sb.Append($"The mod {mod.Name} [{mod.DirectoryName}] already has temporary settings with a different key in {collection}.");
|
|
return;
|
|
case PenumbraApiEc.OptionGroupMissing:
|
|
case PenumbraApiEc.OptionMissing:
|
|
sb.Append($"The provided settings for {mod.Name} [{mod.DirectoryName}] did not correspond to its actual options.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void SetModPermanent(StringBuilder sb, Mod mod, ModSettings settings, Guid collection)
|
|
{
|
|
var ec = settings.ForceInherit
|
|
? _inheritMod!.Invoke(collection, mod.DirectoryName, true, mod.Name)
|
|
: _setMod!.Invoke(collection, mod.DirectoryName, settings.Enabled, mod.Name);
|
|
switch (ec)
|
|
{
|
|
case PenumbraApiEc.ModMissing:
|
|
sb.Append($"The mod {mod.Name} [{mod.DirectoryName}] could not be found.");
|
|
return;
|
|
case PenumbraApiEc.CollectionMissing:
|
|
sb.Append($"The collection {collection} could not be found.");
|
|
return;
|
|
}
|
|
|
|
if (settings.ForceInherit || !settings.Enabled)
|
|
return;
|
|
|
|
ec = _setModPriority!.Invoke(collection, mod.DirectoryName, settings.Priority, mod.Name);
|
|
Debug.Assert(ec is PenumbraApiEc.Success or PenumbraApiEc.NothingChanged, "Setting Priority should not be able to fail.");
|
|
|
|
foreach (var (setting, list) in settings.Settings)
|
|
{
|
|
ec = list.Count == 1
|
|
? _setModSetting!.Invoke(collection, mod.DirectoryName, setting, list[0], mod.Name)
|
|
: _setModSettings!.Invoke(collection, mod.DirectoryName, setting, list, mod.Name);
|
|
switch (ec)
|
|
{
|
|
case PenumbraApiEc.OptionGroupMissing: sb.AppendLine($"Could not find the option group {setting} in mod {mod.Name}."); break;
|
|
case PenumbraApiEc.OptionMissing:
|
|
sb.AppendLine($"Could not find all desired options in the option group {setting} in mod {mod.Name}.");
|
|
break;
|
|
case PenumbraApiEc.Success:
|
|
case PenumbraApiEc.NothingChanged:
|
|
break;
|
|
default:
|
|
sb.AppendLine($"Could not apply options in the option group {setting} in mod {mod.Name} for unknown reason {ec}.");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary> Obtain the name of the collection currently assigned to the player. </summary>
|
|
public Guid GetCurrentPlayerCollection()
|
|
{
|
|
if (!Available)
|
|
return Guid.Empty;
|
|
|
|
var (valid, _, (id, _)) = _objectCollection!.Invoke(0);
|
|
return valid ? id : Guid.Empty;
|
|
}
|
|
|
|
/// <summary> Obtain the name of the collection currently assigned to the given actor. </summary>
|
|
public Guid GetActorCollection(Actor actor, out string name)
|
|
{
|
|
if (!Available)
|
|
{
|
|
name = string.Empty;
|
|
return Guid.Empty;
|
|
}
|
|
|
|
(var valid, _, (var id, name)) = _objectCollection!.Invoke(actor.Index.Index);
|
|
return valid ? id : Guid.Empty;
|
|
}
|
|
|
|
/// <summary> Obtain the game object corresponding to a draw object. </summary>
|
|
public Actor GameObjectFromDrawObject(Model drawObject)
|
|
=> _getGameObject?.Invoke(drawObject.Address) ?? Actor.Null;
|
|
|
|
/// <summary> Obtain the parent of a cutscene actor if it is known. </summary>
|
|
public short CutsceneParent(ushort idx)
|
|
=> (short)(_checkCutsceneParent?.Invoke(idx) ?? -1);
|
|
|
|
/// <summary> Try to redraw the given actor. </summary>
|
|
public void RedrawObject(Actor actor, RedrawType settings)
|
|
{
|
|
if (!actor)
|
|
return;
|
|
|
|
RedrawObject(actor.Index, settings);
|
|
}
|
|
|
|
/// <summary> Try to redraw the given actor. </summary>
|
|
public void RedrawObject(ObjectIndex index, RedrawType settings)
|
|
{
|
|
if (!Available)
|
|
return;
|
|
|
|
try
|
|
{
|
|
_redraw!.Invoke(index.Index, settings);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Glamourer.Log.Debug($"Failure redrawing object:\n{e}");
|
|
}
|
|
}
|
|
|
|
/// <summary> Reattach to the currently running Penumbra IPC provider. Unattaches before if necessary. </summary>
|
|
public void Reattach()
|
|
{
|
|
try
|
|
{
|
|
Unattach();
|
|
|
|
AttachTime = DateTime.UtcNow;
|
|
try
|
|
{
|
|
(CurrentMajor, CurrentMinor) = new global::Penumbra.Api.IpcSubscribers.ApiVersion(_pluginInterface).Invoke();
|
|
}
|
|
catch
|
|
{
|
|
try
|
|
{
|
|
(CurrentMajor, CurrentMinor) = new global::Penumbra.Api.IpcSubscribers.Legacy.ApiVersions(_pluginInterface).Invoke();
|
|
}
|
|
catch
|
|
{
|
|
CurrentMajor = 0;
|
|
CurrentMinor = 0;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
if (CurrentMajor != RequiredPenumbraBreakingVersion || CurrentMinor < RequiredPenumbraFeatureVersion)
|
|
throw new Exception(
|
|
$"Invalid Version {CurrentMajor}.{CurrentMinor:D4}, required major Version {RequiredPenumbraBreakingVersion} with feature greater or equal to {RequiredPenumbraFeatureVersion}.");
|
|
|
|
_tooltipSubscriber.Enable();
|
|
_clickSubscriber.Enable();
|
|
_creatingCharacterBase.Enable();
|
|
_createdCharacterBase.Enable();
|
|
_pcpCreated.Enable();
|
|
_pcpParsed.Enable();
|
|
_modSettingChanged.Enable();
|
|
_collectionByIdentifier = new global::Penumbra.Api.IpcSubscribers.GetCollectionsByIdentifier(_pluginInterface);
|
|
_collections = new global::Penumbra.Api.IpcSubscribers.GetCollections(_pluginInterface);
|
|
_redraw = new global::Penumbra.Api.IpcSubscribers.RedrawObject(_pluginInterface);
|
|
_checkCutsceneParent = new global::Penumbra.Api.IpcSubscribers.GetCutsceneParentIndexFunc(_pluginInterface).Invoke();
|
|
_getGameObject = new global::Penumbra.Api.IpcSubscribers.GetGameObjectFromDrawObjectFunc(_pluginInterface).Invoke();
|
|
_objectCollection = new global::Penumbra.Api.IpcSubscribers.GetCollectionForObject(_pluginInterface);
|
|
_getMods = new global::Penumbra.Api.IpcSubscribers.GetModList(_pluginInterface);
|
|
_currentCollection = new global::Penumbra.Api.IpcSubscribers.GetCollection(_pluginInterface);
|
|
_getCurrentSettings = new global::Penumbra.Api.IpcSubscribers.GetCurrentModSettings(_pluginInterface);
|
|
_inheritMod = new global::Penumbra.Api.IpcSubscribers.TryInheritMod(_pluginInterface);
|
|
_setMod = new global::Penumbra.Api.IpcSubscribers.TrySetMod(_pluginInterface);
|
|
_setModPriority = new global::Penumbra.Api.IpcSubscribers.TrySetModPriority(_pluginInterface);
|
|
_setModSetting = new global::Penumbra.Api.IpcSubscribers.TrySetModSetting(_pluginInterface);
|
|
_setModSettings = new global::Penumbra.Api.IpcSubscribers.TrySetModSettings(_pluginInterface);
|
|
_openModPage = new global::Penumbra.Api.IpcSubscribers.OpenMainWindow(_pluginInterface);
|
|
_getChangedItems = new global::Penumbra.Api.IpcSubscribers.GetChangedItems(_pluginInterface);
|
|
_setTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettings(_pluginInterface);
|
|
_setTemporaryModSettingsPlayer = new global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettingsPlayer(_pluginInterface);
|
|
_removeTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettings(_pluginInterface);
|
|
_removeTemporaryModSettingsPlayer = new global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettingsPlayer(_pluginInterface);
|
|
_removeAllTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettings(_pluginInterface);
|
|
_removeAllTemporaryModSettingsPlayer =
|
|
new global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettingsPlayer(_pluginInterface);
|
|
_queryTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.QueryTemporaryModSettings(_pluginInterface);
|
|
_queryTemporaryModSettingsPlayer =
|
|
new global::Penumbra.Api.IpcSubscribers.QueryTemporaryModSettingsPlayer(_pluginInterface);
|
|
_getCurrentSettingsWithTemp = new global::Penumbra.Api.IpcSubscribers.GetCurrentModSettingsWithTemp(_pluginInterface);
|
|
_getAllSettings = new global::Penumbra.Api.IpcSubscribers.GetAllModSettings(_pluginInterface);
|
|
_changedItems = new global::Penumbra.Api.IpcSubscribers.GetChangedItemAdapterList(_pluginInterface).Invoke();
|
|
_checkCurrentChangedItems =
|
|
new global::Penumbra.Api.IpcSubscribers.CheckCurrentChangedItemFunc(_pluginInterface).Invoke();
|
|
|
|
Available = true;
|
|
_penumbraReloaded.Invoke();
|
|
Glamourer.Log.Debug("Glamourer attached to Penumbra.");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Unattach();
|
|
Glamourer.Log.Debug($"Could not attach to Penumbra:\n{e}");
|
|
}
|
|
}
|
|
|
|
/// <summary> Unattach from the currently running Penumbra IPC provider. </summary>
|
|
public void Unattach()
|
|
{
|
|
_tooltipSubscriber.Disable();
|
|
_clickSubscriber.Disable();
|
|
_creatingCharacterBase.Disable();
|
|
_createdCharacterBase.Disable();
|
|
_modSettingChanged.Disable();
|
|
_pcpCreated.Disable();
|
|
_pcpParsed.Disable();
|
|
if (Available)
|
|
{
|
|
_collectionByIdentifier = null;
|
|
_collections = null;
|
|
_redraw = null;
|
|
_getGameObject = null;
|
|
_checkCutsceneParent = null;
|
|
_objectCollection = null;
|
|
_getMods = null;
|
|
_currentCollection = null;
|
|
_getCurrentSettings = null;
|
|
_getCurrentSettingsWithTemp = null;
|
|
_getAllSettings = null;
|
|
_inheritMod = null;
|
|
_setMod = null;
|
|
_setModPriority = null;
|
|
_setModSetting = null;
|
|
_setModSettings = null;
|
|
_openModPage = null;
|
|
_setTemporaryModSettings = null;
|
|
_setTemporaryModSettingsPlayer = null;
|
|
_removeTemporaryModSettings = null;
|
|
_removeTemporaryModSettingsPlayer = null;
|
|
_removeAllTemporaryModSettings = null;
|
|
_removeAllTemporaryModSettingsPlayer = null;
|
|
_queryTemporaryModSettings = null;
|
|
_queryTemporaryModSettingsPlayer = null;
|
|
_getChangedItems = null;
|
|
_changedItems = null;
|
|
_checkCurrentChangedItems = null;
|
|
Available = false;
|
|
Glamourer.Log.Debug("Glamourer detached from Penumbra.");
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
ClearAllTemporarySettings(true, true);
|
|
Unattach();
|
|
_tooltipSubscriber.Dispose();
|
|
_clickSubscriber.Dispose();
|
|
_creatingCharacterBase.Dispose();
|
|
_createdCharacterBase.Dispose();
|
|
_initializedEvent.Dispose();
|
|
_disposedEvent.Dispose();
|
|
_modSettingChanged.Dispose();
|
|
_pcpCreated.Dispose();
|
|
_pcpParsed.Dispose();
|
|
}
|
|
}
|