Skip to content

Commit

Permalink
Merge branch 'combining'
Browse files Browse the repository at this point in the history
  • Loading branch information
Ottermandias committed Jan 15, 2025
2 parents 9559bd7 + 795fa73 commit d2a8cec
Show file tree
Hide file tree
Showing 13 changed files with 542 additions and 48 deletions.
2 changes: 1 addition & 1 deletion OtterGui
196 changes: 196 additions & 0 deletions Penumbra/Mods/Groups/CombiningModGroup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
using Dalamud.Interface.ImGuiNotification;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OtterGui;
using OtterGui.Classes;
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;

/// <summary> Groups that allow all available options to be selected at once. </summary>
public sealed class CombiningModGroup : IModGroup
{
public GroupType Type
=> GroupType.Combining;

public GroupDrawBehaviour Behaviour
=> GroupDrawBehaviour.MultiSelection;

public Mod Mod { get; }
public string Name { get; set; } = "Group";
public string Description { get; set; } = string.Empty;
public string Image { get; set; } = string.Empty;
public ModPriority Priority { get; set; }
public int Page { get; set; }
public Setting DefaultSettings { get; set; }
public readonly List<CombiningSubMod> OptionData = [];
public List<CombinedDataContainer> Data { get; private set; }

/// <summary> Groups that allow all available options to be selected at once. </summary>
public CombiningModGroup(Mod mod)
{
Mod = mod;
Data = [new CombinedDataContainer(this)];
}

IReadOnlyList<IModOption> IModGroup.Options
=> OptionData;

public IReadOnlyList<IModDataContainer> DataContainers
=> Data;

public bool IsOption
=> OptionData.Count > 0;

public FullPath? FindBestMatch(Utf8GamePath gamePath)
{
foreach (var path in Data.SelectWhere(o
=> (o.Files.TryGetValue(gamePath, out var file) || o.FileSwaps.TryGetValue(gamePath, out file), file)))
return path;

return null;
}

public IModOption? AddOption(string name, string description = "")
{
var groupIdx = Mod.Groups.IndexOf(this);
if (groupIdx < 0)
return null;

var subMod = new CombiningSubMod(this)
{
Name = name,
Description = description,
};
return OptionData.AddNewWithPowerSet(Data, subMod, () => new CombinedDataContainer(this), IModGroup.MaxCombiningOptions)
? subMod
: null;
}

public static CombiningModGroup? Load(Mod mod, JObject json)
{
var ret = new CombiningModGroup(mod, true);
if (!ModSaveGroup.ReadJsonBase(json, ret))
return null;

var options = json["Options"];
if (options != null)
foreach (var child in options.Children())
{
if (ret.OptionData.Count == IModGroup.MaxCombiningOptions)
{
Penumbra.Messager.NotificationMessage(
$"Combining Group {ret.Name} in {mod.Name} has more than {IModGroup.MaxCombiningOptions} options, ignoring excessive options.",
NotificationType.Warning);
break;
}

var subMod = new CombiningSubMod(ret, child);
ret.OptionData.Add(subMod);
}

var requiredContainers = 1 << ret.OptionData.Count;
var containers = json["Containers"];
if (containers != null)
foreach (var child in containers.Children())
{
if (requiredContainers <= ret.Data.Count)
{
Penumbra.Messager.NotificationMessage(
$"Combining Group {ret.Name} in {mod.Name} has more data containers than it can support with {ret.OptionData.Count} options, ignoring excessive containers.",
NotificationType.Warning);
break;
}

var container = new CombinedDataContainer(ret, child);
ret.Data.Add(container);
}

if (requiredContainers > ret.Data.Count)
{
Penumbra.Messager.NotificationMessage(
$"Combining Group {ret.Name} in {mod.Name} has not enough data containers for its {ret.OptionData.Count} options, filling with empty containers.",
NotificationType.Warning);
ret.Data.EnsureCapacity(requiredContainers);
ret.Data.AddRange(Enumerable.Repeat(0, requiredContainers - ret.Data.Count).Select(_ => new CombinedDataContainer(ret)));
}

ret.DefaultSettings = ret.FixSetting(ret.DefaultSettings);

return ret;
}

public int GetIndex()
=> ModGroup.GetIndex(this);

public IModGroupEditDrawer EditDrawer(ModGroupEditDrawer editDrawer)
=> new CombiningModGroupEditDrawer(editDrawer, this);

public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, MetaDictionary manipulations)
=> Data[setting.AsIndex].AddDataTo(redirections, manipulations);

public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
{
foreach (var container in DataContainers)
identifier.AddChangedItems(container, changedItems);
}

public void WriteJson(JsonTextWriter jWriter, JsonSerializer serializer, DirectoryInfo? basePath = null)
{
ModSaveGroup.WriteJsonBase(jWriter, this);
jWriter.WritePropertyName("Options");
jWriter.WriteStartArray();
foreach (var option in OptionData)
{
jWriter.WriteStartObject();
SubMod.WriteModOption(jWriter, option);
jWriter.WriteEndObject();
}

jWriter.WriteEndArray();

jWriter.WritePropertyName("Containers");
jWriter.WriteStartArray();
foreach (var container in Data)
{
jWriter.WriteStartObject();
if (container.Name.Length > 0)
{
jWriter.WritePropertyName("Name");
jWriter.WriteValue(container.Name);
}

SubMod.WriteModContainer(jWriter, serializer, container, basePath ?? Mod.ModPath);
jWriter.WriteEndObject();
}

jWriter.WriteEndArray();
}

public (int Redirections, int Swaps, int Manips) GetCounts()
=> ModGroup.GetCountsBase(this);

public Setting FixSetting(Setting setting)
=> new(Math.Min(setting.Value, (ulong)(Data.Count - 1)));

/// <summary> Create a group without a mod only for saving it in the creator. </summary>
internal static CombiningModGroup WithoutMod(string name)
=> new(null!)
{
Name = name,
};

/// <summary> For loading when no empty container should be created. </summary>
private CombiningModGroup(Mod mod, bool _)
{
Mod = mod;
Data = [];
}
}
3 changes: 2 additions & 1 deletion Penumbra/Mods/Groups/IModGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public enum GroupDrawBehaviour

public interface IModGroup
{
public const int MaxMultiOptions = 32;
public const int MaxMultiOptions = 32;
public const int MaxCombiningOptions = 8;

public Mod Mod { get; }
public string Name { get; set; }
Expand Down
48 changes: 48 additions & 0 deletions Penumbra/Mods/Manager/OptionEditor/CombiningModGroupEditor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using OtterGui;
using OtterGui.Classes;
using OtterGui.Services;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Settings;
using Penumbra.Mods.SubMods;
using Penumbra.Services;

namespace Penumbra.Mods.Manager.OptionEditor;

public sealed class CombiningModGroupEditor(CommunicatorService communicator, SaveService saveService, Configuration config)
: ModOptionEditor<CombiningModGroup, CombiningSubMod>(communicator, saveService, config), IService
{
protected override CombiningModGroup CreateGroup(Mod mod, string newName, ModPriority priority, SaveType saveType = SaveType.ImmediateSync)
=> new(mod)
{
Name = newName,
Priority = priority,
};

protected override CombiningSubMod? CloneOption(CombiningModGroup group, IModOption option)
=> throw new NotImplementedException();

protected override void RemoveOption(CombiningModGroup group, int optionIndex)
{
if (group.OptionData.RemoveWithPowerSet(group.Data, optionIndex))
group.DefaultSettings.RemoveBit(optionIndex);
}

protected override bool MoveOption(CombiningModGroup group, int optionIdxFrom, int optionIdxTo)
{
if (!group.OptionData.MoveWithPowerSet(group.Data, ref optionIdxFrom, ref optionIdxTo))
return false;

group.DefaultSettings.MoveBit(optionIdxFrom, optionIdxTo);
return true;
}

public void SetDisplayName(CombinedDataContainer container, string name, SaveType saveType = SaveType.Queue)
{
if (container.Name == name)
return;

container.Name = name;
SaveService.Save(saveType, new ModSaveGroup(container.Group, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, container.Group.Mod, container.Group, null, null, -1);
}
}
55 changes: 35 additions & 20 deletions Penumbra/Mods/Manager/OptionEditor/ModGroupEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public class ModGroupEditor(
SingleModGroupEditor singleEditor,
MultiModGroupEditor multiEditor,
ImcModGroupEditor imcEditor,
CombiningModGroupEditor combiningEditor,
CommunicatorService communicator,
SaveService saveService,
Configuration config) : IService
Expand All @@ -50,6 +51,9 @@ public MultiModGroupEditor MultiEditor
public ImcModGroupEditor ImcEditor
=> imcEditor;

public CombiningModGroupEditor CombiningEditor
=> combiningEditor;

/// <summary> Change the settings stored as default options in a mod.</summary>
public void ChangeModGroupDefaultOption(IModGroup group, Setting defaultOption)
{
Expand Down Expand Up @@ -223,52 +227,60 @@ public void DeleteOption(IModOption option)
case ImcSubMod i:
ImcEditor.DeleteOption(i);
return;
case CombiningSubMod c:
CombiningEditor.DeleteOption(c);
return;
}
}

public IModOption? AddOption(IModGroup group, IModOption option)
=> group switch
{
SingleModGroup s => SingleEditor.AddOption(s, option),
MultiModGroup m => MultiEditor.AddOption(m, option),
ImcModGroup i => ImcEditor.AddOption(i, option),
_ => null,
SingleModGroup s => SingleEditor.AddOption(s, option),
MultiModGroup m => MultiEditor.AddOption(m, option),
ImcModGroup i => ImcEditor.AddOption(i, option),
CombiningModGroup c => CombiningEditor.AddOption(c, option),
_ => null,
};

public IModOption? AddOption(IModGroup group, string newName)
=> group switch
{
SingleModGroup s => SingleEditor.AddOption(s, newName),
MultiModGroup m => MultiEditor.AddOption(m, newName),
ImcModGroup i => ImcEditor.AddOption(i, newName),
_ => null,
SingleModGroup s => SingleEditor.AddOption(s, newName),
MultiModGroup m => MultiEditor.AddOption(m, newName),
ImcModGroup i => ImcEditor.AddOption(i, newName),
CombiningModGroup c => CombiningEditor.AddOption(c, newName),
_ => null,
};

public IModGroup? AddModGroup(Mod mod, GroupType type, string newName, SaveType saveType = SaveType.ImmediateSync)
=> type switch
{
GroupType.Single => SingleEditor.AddModGroup(mod, newName, saveType),
GroupType.Multi => MultiEditor.AddModGroup(mod, newName, saveType),
GroupType.Imc => ImcEditor.AddModGroup(mod, newName, default, default, saveType),
_ => null,
GroupType.Single => SingleEditor.AddModGroup(mod, newName, saveType),
GroupType.Multi => MultiEditor.AddModGroup(mod, newName, saveType),
GroupType.Imc => ImcEditor.AddModGroup(mod, newName, default, default, saveType),
GroupType.Combining => CombiningEditor.AddModGroup(mod, newName, saveType),
_ => null,
};

public (IModGroup?, int, bool) FindOrAddModGroup(Mod mod, GroupType type, string name, SaveType saveType = SaveType.ImmediateSync)
=> type switch
{
GroupType.Single => SingleEditor.FindOrAddModGroup(mod, name, saveType),
GroupType.Multi => MultiEditor.FindOrAddModGroup(mod, name, saveType),
GroupType.Imc => ImcEditor.FindOrAddModGroup(mod, name, saveType),
_ => (null, -1, false),
GroupType.Single => SingleEditor.FindOrAddModGroup(mod, name, saveType),
GroupType.Multi => MultiEditor.FindOrAddModGroup(mod, name, saveType),
GroupType.Imc => ImcEditor.FindOrAddModGroup(mod, name, saveType),
GroupType.Combining => CombiningEditor.FindOrAddModGroup(mod, name, saveType),
_ => (null, -1, false),
};

public (IModOption?, int, bool) FindOrAddOption(IModGroup group, string name, SaveType saveType = SaveType.ImmediateSync)
=> group switch
{
SingleModGroup s => SingleEditor.FindOrAddOption(s, name, saveType),
MultiModGroup m => MultiEditor.FindOrAddOption(m, name, saveType),
ImcModGroup i => ImcEditor.FindOrAddOption(i, name, saveType),
_ => (null, -1, false),
SingleModGroup s => SingleEditor.FindOrAddOption(s, name, saveType),
MultiModGroup m => MultiEditor.FindOrAddOption(m, name, saveType),
ImcModGroup i => ImcEditor.FindOrAddOption(i, name, saveType),
CombiningModGroup c => CombiningEditor.FindOrAddOption(c, name, saveType),
_ => (null, -1, false),
};

public void MoveOption(IModOption option, int toIdx)
Expand All @@ -284,6 +296,9 @@ public void MoveOption(IModOption option, int toIdx)
case ImcSubMod i:
ImcEditor.MoveOption(i, toIdx);
return;
case CombiningSubMod c:
CombiningEditor.MoveOption(c, toIdx);
return;
}
}
}
Loading

0 comments on commit d2a8cec

Please sign in to comment.