mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
250 lines
8.2 KiB
C#
250 lines
8.2 KiB
C#
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.GameData.Structs;
|
|
using Penumbra.Meta;
|
|
using Penumbra.Meta.Manipulations;
|
|
using Penumbra.Mods.Settings;
|
|
using Penumbra.Mods.SubMods;
|
|
using Penumbra.String.Classes;
|
|
using Penumbra.UI.ModsTab.Groups;
|
|
|
|
namespace Penumbra.Mods.Groups;
|
|
|
|
public class ImcModGroup(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.Imc;
|
|
|
|
public GroupDrawBehaviour Behaviour
|
|
=> GroupDrawBehaviour.MultiSelection;
|
|
|
|
public ModPriority Priority { get; set; } = ModPriority.Default;
|
|
public Setting DefaultSettings { get; set; } = Setting.Zero;
|
|
|
|
public ImcIdentifier Identifier;
|
|
public ImcEntry DefaultEntry;
|
|
public bool AllVariants;
|
|
|
|
|
|
public FullPath? FindBestMatch(Utf8GamePath gamePath)
|
|
=> null;
|
|
|
|
private bool _canBeDisabled;
|
|
|
|
public bool CanBeDisabled
|
|
{
|
|
get => _canBeDisabled;
|
|
set
|
|
{
|
|
_canBeDisabled = value;
|
|
if (!value)
|
|
{
|
|
OptionData.RemoveAll(m => m.IsDisableSubMod);
|
|
DefaultSettings = FixSetting(DefaultSettings);
|
|
}
|
|
else
|
|
{
|
|
if (!OptionData.Any(m => m.IsDisableSubMod))
|
|
OptionData.Add(ImcSubMod.DisableSubMod(this));
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool DefaultDisabled
|
|
=> IsDisabled(DefaultSettings);
|
|
|
|
public IModOption? AddOption(string name, string description = "")
|
|
{
|
|
var groupIdx = Mod.Groups.IndexOf(this);
|
|
if (groupIdx < 0)
|
|
return null;
|
|
|
|
var subMod = new ImcSubMod(this)
|
|
{
|
|
Name = name,
|
|
Description = description,
|
|
AttributeMask = 0,
|
|
};
|
|
OptionData.Add(subMod);
|
|
return subMod;
|
|
}
|
|
|
|
public readonly List<ImcSubMod> OptionData = [];
|
|
|
|
public IReadOnlyList<IModOption> Options
|
|
=> OptionData;
|
|
|
|
public IReadOnlyList<IModDataContainer> DataContainers
|
|
=> [];
|
|
|
|
public bool IsOption
|
|
=> OptionData.Count > 0;
|
|
|
|
public int GetIndex()
|
|
=> ModGroup.GetIndex(this);
|
|
|
|
public IModGroupEditDrawer EditDrawer(ModGroupEditDrawer editDrawer)
|
|
=> new ImcModGroupEditDrawer(editDrawer, this);
|
|
|
|
public ImcEntry GetEntry(ushort mask)
|
|
=> DefaultEntry with { AttributeMask = mask };
|
|
|
|
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, MetaDictionary manipulations)
|
|
{
|
|
if (IsDisabled(setting))
|
|
return;
|
|
|
|
var mask = GetCurrentMask(setting);
|
|
var entry = GetEntry(mask);
|
|
if (AllVariants)
|
|
{
|
|
var count = ImcChecker.GetVariantCount(Identifier);
|
|
if (count == 0)
|
|
manipulations.TryAdd(Identifier, entry);
|
|
else
|
|
for (var i = 0; i <= count; ++i)
|
|
manipulations.TryAdd(Identifier with { Variant = (Variant)i }, entry);
|
|
}
|
|
else
|
|
{
|
|
manipulations.TryAdd(Identifier, entry);
|
|
}
|
|
}
|
|
|
|
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
|
=> Identifier.AddChangedItems(identifier, changedItems, AllVariants);
|
|
|
|
public Setting FixSetting(Setting setting)
|
|
=> new(setting.Value & ((1ul << OptionData.Count) - 1));
|
|
|
|
public void WriteJson(JsonTextWriter jWriter, JsonSerializer serializer, DirectoryInfo? basePath = null)
|
|
{
|
|
ModSaveGroup.WriteJsonBase(jWriter, this);
|
|
var jObj = Identifier.AddToJson(new JObject());
|
|
jWriter.WritePropertyName(nameof(Identifier));
|
|
jObj.WriteTo(jWriter);
|
|
jWriter.WritePropertyName(nameof(DefaultEntry));
|
|
serializer.Serialize(jWriter, DefaultEntry);
|
|
jWriter.WritePropertyName(nameof(AllVariants));
|
|
jWriter.WriteValue(AllVariants);
|
|
jWriter.WritePropertyName("Options");
|
|
jWriter.WriteStartArray();
|
|
foreach (var option in OptionData)
|
|
{
|
|
jWriter.WriteStartObject();
|
|
SubMod.WriteModOption(jWriter, option);
|
|
if (option.IsDisableSubMod)
|
|
{
|
|
jWriter.WritePropertyName(nameof(option.IsDisableSubMod));
|
|
jWriter.WriteValue(true);
|
|
}
|
|
else
|
|
{
|
|
jWriter.WritePropertyName(nameof(option.AttributeMask));
|
|
jWriter.WriteValue(option.AttributeMask);
|
|
}
|
|
|
|
jWriter.WriteEndObject();
|
|
}
|
|
|
|
jWriter.WriteEndArray();
|
|
}
|
|
|
|
public (int Redirections, int Swaps, int Manips) GetCounts()
|
|
=> (0, 0, 1);
|
|
|
|
public static ImcModGroup? Load(Mod mod, JObject json)
|
|
{
|
|
var options = json["Options"];
|
|
var identifier = ImcIdentifier.FromJson(json[nameof(Identifier)] as JObject);
|
|
var ret = new ImcModGroup(mod)
|
|
{
|
|
Name = json[nameof(Name)]?.ToObject<string>() ?? string.Empty,
|
|
Description = json[nameof(Description)]?.ToObject<string>() ?? string.Empty,
|
|
Image = json[nameof(Image)]?.ToObject<string>() ?? string.Empty,
|
|
Priority = json[nameof(Priority)]?.ToObject<ModPriority>() ?? ModPriority.Default,
|
|
DefaultEntry = json[nameof(DefaultEntry)]?.ToObject<ImcEntry>() ?? new ImcEntry(),
|
|
AllVariants = json[nameof(AllVariants)]?.ToObject<bool>() ?? false,
|
|
};
|
|
if (ret.Name.Length == 0)
|
|
return null;
|
|
|
|
if (!identifier.HasValue || ret.DefaultEntry.MaterialId == 0)
|
|
{
|
|
Penumbra.Messager.NotificationMessage($"Could not add IMC group {ret.Name} because the associated IMC Entry is invalid.",
|
|
NotificationType.Warning);
|
|
return null;
|
|
}
|
|
|
|
var rollingMask = 0ul;
|
|
if (options != null)
|
|
foreach (var child in options.Children())
|
|
{
|
|
var subMod = new ImcSubMod(ret, child);
|
|
|
|
if (subMod.IsDisableSubMod)
|
|
ret._canBeDisabled = true;
|
|
|
|
if (subMod.IsDisableSubMod && ret.OptionData.FirstOrDefault(m => m.IsDisableSubMod) is { } disable)
|
|
{
|
|
Penumbra.Messager.NotificationMessage(
|
|
$"Could not add IMC option {subMod.Name} to {ret.Name} because it already contains {disable.Name} as disable option.",
|
|
NotificationType.Warning);
|
|
}
|
|
else if ((subMod.AttributeMask & rollingMask) != 0)
|
|
{
|
|
Penumbra.Messager.NotificationMessage(
|
|
$"Could not add IMC option {subMod.Name} to {ret.Name} because it contains attributes already in use.",
|
|
NotificationType.Warning);
|
|
}
|
|
else
|
|
{
|
|
rollingMask |= subMod.AttributeMask;
|
|
ret.OptionData.Add(subMod);
|
|
}
|
|
}
|
|
|
|
ret.Identifier = identifier.Value;
|
|
ret.DefaultSettings = json[nameof(DefaultSettings)]?.ToObject<Setting>() ?? Setting.Zero;
|
|
ret.DefaultSettings = ret.FixSetting(ret.DefaultSettings);
|
|
return ret;
|
|
}
|
|
|
|
private bool IsDisabled(Setting setting)
|
|
{
|
|
if (!CanBeDisabled)
|
|
return false;
|
|
|
|
var idx = OptionData.IndexOf(m => m.IsDisableSubMod);
|
|
if (idx >= 0)
|
|
return setting.HasFlag(idx);
|
|
|
|
Penumbra.Log.Warning("A IMC Group should be able to be disabled, but does not contain a disable option.");
|
|
return false;
|
|
}
|
|
|
|
private ushort GetCurrentMask(Setting setting)
|
|
{
|
|
var mask = DefaultEntry.AttributeMask;
|
|
for (var i = 0; i < OptionData.Count; ++i)
|
|
{
|
|
if (!setting.HasFlag(i))
|
|
continue;
|
|
|
|
var option = OptionData[i];
|
|
mask ^= option.AttributeMask;
|
|
}
|
|
|
|
return mask;
|
|
}
|
|
}
|