mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
Add initial complex group things.
This commit is contained in:
parent
012052daa0
commit
dc93eba34c
5 changed files with 294 additions and 0 deletions
180
Penumbra/Mods/Groups/ComplexModGroup.cs
Normal file
180
Penumbra/Mods/Groups/ComplexModGroup.cs
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
using Dalamud.Interface.ImGuiNotification;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using OtterGui.Classes;
|
||||||
|
using OtterGui.Extensions;
|
||||||
|
using Penumbra.Api.Enums;
|
||||||
|
using Penumbra.GameData.Data;
|
||||||
|
using Penumbra.Meta.Manipulations;
|
||||||
|
using Penumbra.Mods.Settings;
|
||||||
|
using Penumbra.Mods.SubMods;
|
||||||
|
using Penumbra.String.Classes;
|
||||||
|
using Penumbra.UI.ModsTab.Groups;
|
||||||
|
using Penumbra.Util;
|
||||||
|
|
||||||
|
namespace Penumbra.Mods.Groups;
|
||||||
|
|
||||||
|
public sealed class ComplexModGroup(Mod mod) : IModGroup
|
||||||
|
{
|
||||||
|
public Mod Mod { get; } = mod;
|
||||||
|
public string Name { get; set; } = "Option";
|
||||||
|
public string Description { get; set; } = string.Empty;
|
||||||
|
public string Image { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public GroupType Type
|
||||||
|
=> GroupType.Complex;
|
||||||
|
|
||||||
|
public GroupDrawBehaviour Behaviour
|
||||||
|
=> GroupDrawBehaviour.Complex;
|
||||||
|
|
||||||
|
public ModPriority Priority { get; set; }
|
||||||
|
public int Page { get; set; }
|
||||||
|
public Setting DefaultSettings { get; set; }
|
||||||
|
|
||||||
|
public readonly List<ComplexSubMod> Options = [];
|
||||||
|
public readonly List<ComplexDataContainer> Containers = [];
|
||||||
|
|
||||||
|
|
||||||
|
public FullPath? FindBestMatch(Utf8GamePath gamePath)
|
||||||
|
=> throw new NotImplementedException();
|
||||||
|
|
||||||
|
public IModOption? AddOption(string name, string description = "")
|
||||||
|
=> throw new NotImplementedException();
|
||||||
|
|
||||||
|
IReadOnlyList<IModOption> IModGroup.Options
|
||||||
|
=> Options;
|
||||||
|
|
||||||
|
IReadOnlyList<IModDataContainer> IModGroup.DataContainers
|
||||||
|
=> Containers;
|
||||||
|
|
||||||
|
public bool IsOption
|
||||||
|
=> Options.Count > 0;
|
||||||
|
|
||||||
|
public int GetIndex()
|
||||||
|
=> ModGroup.GetIndex(this);
|
||||||
|
|
||||||
|
public IModGroupEditDrawer EditDrawer(ModGroupEditDrawer editDrawer)
|
||||||
|
=> throw new NotImplementedException();
|
||||||
|
|
||||||
|
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, MetaDictionary manipulations)
|
||||||
|
{
|
||||||
|
foreach (var container in Containers.Where(c => c.Association.IsEnabled(setting)))
|
||||||
|
SubMod.AddContainerTo(container, redirections, manipulations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData> changedItems)
|
||||||
|
{
|
||||||
|
foreach (var container in Containers)
|
||||||
|
identifier.AddChangedItems(container, changedItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Setting FixSetting(Setting setting)
|
||||||
|
=> new(setting.Value & ((1ul << Options.Count) - 1));
|
||||||
|
|
||||||
|
public void WriteJson(JsonTextWriter jWriter, JsonSerializer serializer, DirectoryInfo? basePath = null)
|
||||||
|
{
|
||||||
|
ModSaveGroup.WriteJsonBase(jWriter, this);
|
||||||
|
jWriter.WritePropertyName("Options");
|
||||||
|
jWriter.WriteStartArray();
|
||||||
|
foreach (var option in Options)
|
||||||
|
{
|
||||||
|
jWriter.WriteStartObject();
|
||||||
|
SubMod.WriteModOption(jWriter, option);
|
||||||
|
if (!option.Conditions.IsZero)
|
||||||
|
{
|
||||||
|
jWriter.WritePropertyName("ConditionMask");
|
||||||
|
jWriter.WriteValue(option.Conditions.Mask.Value);
|
||||||
|
jWriter.WritePropertyName("ConditionValue");
|
||||||
|
jWriter.WriteValue(option.Conditions.Value.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option.Indentation > 0)
|
||||||
|
{
|
||||||
|
jWriter.WritePropertyName("Indentation");
|
||||||
|
jWriter.WriteValue(option.Indentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option.SubGroupLabel.Length > 0)
|
||||||
|
{
|
||||||
|
jWriter.WritePropertyName("SubGroup");
|
||||||
|
jWriter.WriteValue(option.SubGroupLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
jWriter.WriteEndObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
jWriter.WriteEndArray();
|
||||||
|
|
||||||
|
jWriter.WritePropertyName("Containers");
|
||||||
|
jWriter.WriteStartArray();
|
||||||
|
foreach (var container in Containers)
|
||||||
|
{
|
||||||
|
jWriter.WriteStartObject();
|
||||||
|
if (container.Name.Length > 0)
|
||||||
|
{
|
||||||
|
jWriter.WritePropertyName("Name");
|
||||||
|
jWriter.WriteValue(container.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!container.Association.IsZero)
|
||||||
|
{
|
||||||
|
jWriter.WritePropertyName("AssociationMask");
|
||||||
|
jWriter.WriteValue(container.Association.Mask.Value);
|
||||||
|
|
||||||
|
jWriter.WritePropertyName("AssociationValue");
|
||||||
|
jWriter.WriteValue(container.Association.Value.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
SubMod.WriteModContainer(jWriter, serializer, container, basePath ?? Mod.ModPath);
|
||||||
|
jWriter.WriteEndObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
jWriter.WriteEndArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public (int Redirections, int Swaps, int Manips) GetCounts()
|
||||||
|
=> ModGroup.GetCountsBase(this);
|
||||||
|
|
||||||
|
public static ComplexModGroup? Load(Mod mod, JObject json)
|
||||||
|
{
|
||||||
|
var ret = new ComplexModGroup(mod);
|
||||||
|
if (!ModSaveGroup.ReadJsonBase(json, ret))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var options = json["Options"];
|
||||||
|
if (options != null)
|
||||||
|
foreach (var child in options.Children())
|
||||||
|
{
|
||||||
|
if (ret.Options.Count == IModGroup.MaxComplexOptions)
|
||||||
|
{
|
||||||
|
Penumbra.Messager.NotificationMessage(
|
||||||
|
$"Complex Group {ret.Name} in {mod.Name} has more than {IModGroup.MaxComplexOptions} options, ignoring excessive options.",
|
||||||
|
NotificationType.Warning);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var subMod = new ComplexSubMod(ret, child);
|
||||||
|
ret.Options.Add(subMod);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix up conditions: No condition on itself.
|
||||||
|
foreach (var (option, index) in ret.Options.WithIndex())
|
||||||
|
{
|
||||||
|
option.Conditions = option.Conditions.Limit(ret.Options.Count);
|
||||||
|
option.Conditions = new MaskedSetting(option.Conditions.Mask.SetBit(index, false), option.Conditions.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var containers = json["Containers"];
|
||||||
|
if (containers != null)
|
||||||
|
foreach (var child in containers.Children())
|
||||||
|
{
|
||||||
|
var container = new ComplexDataContainer(ret, child);
|
||||||
|
container.Association = container.Association.Limit(ret.Options.Count);
|
||||||
|
ret.Containers.Add(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.DefaultSettings = ret.FixSetting(ret.DefaultSettings);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,11 +18,13 @@ public enum GroupDrawBehaviour
|
||||||
{
|
{
|
||||||
SingleSelection,
|
SingleSelection,
|
||||||
MultiSelection,
|
MultiSelection,
|
||||||
|
Complex,
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IModGroup
|
public interface IModGroup
|
||||||
{
|
{
|
||||||
public const int MaxMultiOptions = 32;
|
public const int MaxMultiOptions = 32;
|
||||||
|
public const int MaxComplexOptions = MaxMultiOptions;
|
||||||
public const int MaxCombiningOptions = 8;
|
public const int MaxCombiningOptions = 8;
|
||||||
|
|
||||||
public Mod Mod { get; }
|
public Mod Mod { get; }
|
||||||
|
|
|
||||||
46
Penumbra/Mods/SubMods/ComplexDataContainer.cs
Normal file
46
Penumbra/Mods/SubMods/ComplexDataContainer.cs
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using OtterGui.Extensions;
|
||||||
|
using Penumbra.Meta.Manipulations;
|
||||||
|
using Penumbra.Mods.Editor;
|
||||||
|
using Penumbra.Mods.Groups;
|
||||||
|
using Penumbra.String.Classes;
|
||||||
|
|
||||||
|
namespace Penumbra.Mods.SubMods;
|
||||||
|
|
||||||
|
public sealed class ComplexDataContainer(ComplexModGroup group) : IModDataContainer
|
||||||
|
{
|
||||||
|
public IMod Mod
|
||||||
|
=> Group.Mod;
|
||||||
|
|
||||||
|
public IModGroup Group { get; } = group;
|
||||||
|
|
||||||
|
public Dictionary<Utf8GamePath, FullPath> Files { get; set; } = [];
|
||||||
|
public Dictionary<Utf8GamePath, FullPath> FileSwaps { get; set; } = [];
|
||||||
|
public MetaDictionary Manipulations { get; set; } = new();
|
||||||
|
|
||||||
|
public MaskedSetting Association = MaskedSetting.Zero;
|
||||||
|
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string GetName()
|
||||||
|
=> Name.Length > 0 ? Name : $"Container {Group.DataContainers.IndexOf(this)}";
|
||||||
|
|
||||||
|
public string GetDirectoryName()
|
||||||
|
=> Name.Length > 0 ? Name : $"{Group.DataContainers.IndexOf(this)}";
|
||||||
|
|
||||||
|
public string GetFullName()
|
||||||
|
=> $"{Group.Name}: {GetName()}";
|
||||||
|
|
||||||
|
public (int GroupIndex, int DataIndex) GetDataIndices()
|
||||||
|
=> (Group.GetIndex(), Group.DataContainers.IndexOf(this));
|
||||||
|
|
||||||
|
public ComplexDataContainer(ComplexModGroup group, JToken json)
|
||||||
|
: this(group)
|
||||||
|
{
|
||||||
|
SubMod.LoadDataContainer(json, this, group.Mod.ModPath);
|
||||||
|
var mask = json["AssociationMask"]?.ToObject<ulong>() ?? 0;
|
||||||
|
var value = json["AssociationMask"]?.ToObject<ulong>() ?? 0;
|
||||||
|
Association = new MaskedSetting(mask, value);
|
||||||
|
Name = json["Name"]?.ToObject<string>() ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
39
Penumbra/Mods/SubMods/ComplexSubMod.cs
Normal file
39
Penumbra/Mods/SubMods/ComplexSubMod.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
using ImSharp;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using OtterGui.Extensions;
|
||||||
|
using Penumbra.Mods.Groups;
|
||||||
|
using Penumbra.Mods.Settings;
|
||||||
|
|
||||||
|
namespace Penumbra.Mods.SubMods;
|
||||||
|
|
||||||
|
public sealed class ComplexSubMod(ComplexModGroup group) : IModOption
|
||||||
|
{
|
||||||
|
public Mod Mod
|
||||||
|
=> group.Mod;
|
||||||
|
|
||||||
|
public IModGroup Group { get; } = group;
|
||||||
|
public string Name { get; set; } = "Option";
|
||||||
|
|
||||||
|
public string FullName
|
||||||
|
=> $"{Group.Name}: {Name}";
|
||||||
|
|
||||||
|
public MaskedSetting Conditions = MaskedSetting.Zero;
|
||||||
|
public int Indentation = 0;
|
||||||
|
public string SubGroupLabel = string.Empty;
|
||||||
|
|
||||||
|
public string Description { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public int GetIndex()
|
||||||
|
=> Group.Options.IndexOf(this);
|
||||||
|
|
||||||
|
public ComplexSubMod(ComplexModGroup group, JToken json)
|
||||||
|
: this(group)
|
||||||
|
{
|
||||||
|
SubMod.LoadOptionData(json, this);
|
||||||
|
var mask = json["ConditionMask"]?.ToObject<ulong>() ?? 0;
|
||||||
|
var value = json["ConditionMask"]?.ToObject<ulong>() ?? 0;
|
||||||
|
Conditions = new MaskedSetting(mask, value);
|
||||||
|
Indentation = json["Indentation"]?.ToObject<int>() ?? 0;
|
||||||
|
SubGroupLabel = json["SubGroup"]?.ToObject<string>() ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
27
Penumbra/Mods/SubMods/MaskedSetting.cs
Normal file
27
Penumbra/Mods/SubMods/MaskedSetting.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
using Penumbra.Mods.Groups;
|
||||||
|
using Penumbra.Mods.Settings;
|
||||||
|
|
||||||
|
namespace Penumbra.Mods.SubMods;
|
||||||
|
|
||||||
|
public readonly struct MaskedSetting(Setting mask, Setting value)
|
||||||
|
{
|
||||||
|
public const int MaxSettings = IModGroup.MaxMultiOptions;
|
||||||
|
public static readonly MaskedSetting Zero = new(Setting.Zero, Setting.Zero);
|
||||||
|
public static readonly MaskedSetting FullMask = new(Setting.AllBits(IModGroup.MaxComplexOptions), Setting.Zero);
|
||||||
|
|
||||||
|
public readonly Setting Mask = mask;
|
||||||
|
public readonly Setting Value = new(value.Value & mask.Value);
|
||||||
|
|
||||||
|
public MaskedSetting(ulong mask, ulong value)
|
||||||
|
: this(new Setting(mask), new Setting(value))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public MaskedSetting Limit(int numOptions)
|
||||||
|
=> new(Mask.Value & Setting.AllBits(numOptions).Value, Value.Value);
|
||||||
|
|
||||||
|
public bool IsZero
|
||||||
|
=> Mask.Value is 0;
|
||||||
|
|
||||||
|
public bool IsEnabled(Setting input)
|
||||||
|
=> (input.Value & Mask.Value) == Value.Value;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue