mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-15 21:24:18 +01:00
Add support for Global EQP Changes.
This commit is contained in:
parent
f9527970cb
commit
ed083f2a4c
15 changed files with 471 additions and 133 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 539d138700543e7c2c6c918f9f68e33228111e4d
|
||||
Subproject commit 07cc26f196984a44711b3bc4c412947d863288bd
|
||||
|
|
@ -5,7 +5,6 @@ using Penumbra.Mods;
|
|||
using Penumbra.Communication;
|
||||
using Penumbra.Mods.Editor;
|
||||
using Penumbra.String.Classes;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Collections.Cache;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ using Penumbra.Interop.Services;
|
|||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Mods.Editor;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
|
|
@ -21,6 +20,7 @@ public class MetaCache : IDisposable, IEnumerable<KeyValuePair<MetaManipulation,
|
|||
private GmpCache _gmpCache = new();
|
||||
private CmpCache _cmpCache = new();
|
||||
private readonly ImcCache _imcCache = new();
|
||||
private GlobalEqpCache _globalEqpCache = new();
|
||||
|
||||
public bool TryGetValue(MetaManipulation manip, [NotNullWhen(true)] out IMod? mod)
|
||||
{
|
||||
|
|
@ -69,6 +69,7 @@ public class MetaCache : IDisposable, IEnumerable<KeyValuePair<MetaManipulation,
|
|||
_cmpCache.Reset();
|
||||
_imcCache.Reset(_collection);
|
||||
_manipulations.Clear();
|
||||
_globalEqpCache.Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
@ -96,6 +97,9 @@ public class MetaCache : IDisposable, IEnumerable<KeyValuePair<MetaManipulation,
|
|||
_manipulations[manip] = mod;
|
||||
}
|
||||
|
||||
if (manip.ManipulationType is MetaManipulation.Type.GlobalEqp)
|
||||
return _globalEqpCache.Add(manip.GlobalEqp);
|
||||
|
||||
if (!_manager.CharacterUtility.Ready)
|
||||
return true;
|
||||
|
||||
|
|
@ -119,6 +123,10 @@ public class MetaCache : IDisposable, IEnumerable<KeyValuePair<MetaManipulation,
|
|||
lock (_manipulations)
|
||||
{
|
||||
var ret = _manipulations.Remove(manip, out mod);
|
||||
|
||||
if (manip.ManipulationType is MetaManipulation.Type.GlobalEqp)
|
||||
return _globalEqpCache.Remove(manip.GlobalEqp);
|
||||
|
||||
if (!_manager.CharacterUtility.Ready)
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -183,6 +191,9 @@ public class MetaCache : IDisposable, IEnumerable<KeyValuePair<MetaManipulation,
|
|||
public MetaList.MetaReverter TemporarilySetEstFile(EstManipulation.EstType type)
|
||||
=> _estCache.TemporarilySetFiles(_manager, type);
|
||||
|
||||
public unsafe EqpEntry ApplyGlobalEqp(EqpEntry baseEntry, CharacterArmor* armor)
|
||||
=> _globalEqpCache.Apply(baseEntry, armor);
|
||||
|
||||
|
||||
/// <summary> Try to obtain a manipulated IMC file. </summary>
|
||||
public bool GetImcFile(Utf8GamePath path, [NotNullWhen(true)] out Meta.Files.ImcFile? file)
|
||||
|
|
@ -193,7 +204,7 @@ public class MetaCache : IDisposable, IEnumerable<KeyValuePair<MetaManipulation,
|
|||
var eqdpFile = _eqdpCache.EqdpFile(race, accessory);
|
||||
if (eqdpFile != null)
|
||||
return primaryId.Id < eqdpFile.Count ? eqdpFile[primaryId] : default;
|
||||
else
|
||||
|
||||
return Meta.Files.ExpandedEqdpFile.GetDefault(_manager, race, accessory, primaryId);
|
||||
}
|
||||
|
||||
|
|
@ -219,6 +230,7 @@ public class MetaCache : IDisposable, IEnumerable<KeyValuePair<MetaManipulation,
|
|||
MetaManipulation.Type.Gmp => _gmpCache.ApplyMod(_manager, manip.Gmp),
|
||||
MetaManipulation.Type.Rsp => _cmpCache.ApplyMod(_manager, manip.Rsp),
|
||||
MetaManipulation.Type.Imc => _imcCache.ApplyMod(_manager, _collection, manip.Imc),
|
||||
MetaManipulation.Type.GlobalEqp => false,
|
||||
MetaManipulation.Type.Unknown => false,
|
||||
_ => false,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using Penumbra.String.Classes;
|
|||
using Penumbra.Collections.Cache;
|
||||
using Penumbra.Interop.Services;
|
||||
using Penumbra.Mods.Editor;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Penumbra.Collections;
|
||||
|
||||
|
|
@ -114,4 +115,7 @@ public partial class ModCollection
|
|||
public MetaList.MetaReverter TemporarilySetEstFile(CharacterUtility utility, EstManipulation.EstType type)
|
||||
=> _cache?.Meta.TemporarilySetEstFile(type)
|
||||
?? utility.TemporarilyResetResource((MetaIndex)type);
|
||||
|
||||
public unsafe EqpEntry ApplyGlobalEqp(EqpEntry baseEntry, CharacterArmor* armor)
|
||||
=> _cache?.Meta.ApplyGlobalEqp(baseEntry, armor) ?? baseEntry;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,6 +187,9 @@ public partial class TexToolsMeta
|
|||
b.Write(manip.Gmp.Entry.UnknownTotal);
|
||||
}
|
||||
|
||||
break;
|
||||
case MetaManipulation.Type.GlobalEqp:
|
||||
// Not Supported
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public partial class TexToolsMeta
|
|||
var headerStart = reader.ReadUInt32();
|
||||
reader.BaseStream.Seek(headerStart, SeekOrigin.Begin);
|
||||
|
||||
List<(MetaManipulation.Type type, uint offset, int size)> entries = new();
|
||||
List<(MetaManipulation.Type type, uint offset, int size)> entries = [];
|
||||
for (var i = 0; i < numHeaders; ++i)
|
||||
{
|
||||
var currentOffset = reader.BaseStream.Position;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ public unsafe class EqpHook : FastHook<EqpHook.Delegate>
|
|||
{
|
||||
using var eqp = _metaState.ResolveEqpData(_metaState.EqpCollection.ModCollection);
|
||||
Task.Result.Original(utility, flags, armor);
|
||||
*flags = _metaState.EqpCollection.ModCollection.ApplyGlobalEqp(*flags, armor);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
80
Penumbra/Meta/Manipulations/GlobalEqpCache.cs
Normal file
80
Penumbra/Meta/Manipulations/GlobalEqpCache.cs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Penumbra.Meta.Manipulations;
|
||||
|
||||
public struct GlobalEqpCache : IService
|
||||
{
|
||||
private readonly HashSet<PrimaryId> _doNotHideEarrings = [];
|
||||
private readonly HashSet<PrimaryId> _doNotHideNecklace = [];
|
||||
private readonly HashSet<PrimaryId> _doNotHideBracelets = [];
|
||||
private readonly HashSet<PrimaryId> _doNotHideRingL = [];
|
||||
private readonly HashSet<PrimaryId> _doNotHideRingR = [];
|
||||
private bool _doNotHideVieraHats;
|
||||
private bool _doNotHideHrothgarHats;
|
||||
|
||||
public GlobalEqpCache()
|
||||
{ }
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_doNotHideEarrings.Clear();
|
||||
_doNotHideNecklace.Clear();
|
||||
_doNotHideBracelets.Clear();
|
||||
_doNotHideRingL.Clear();
|
||||
_doNotHideRingR.Clear();
|
||||
_doNotHideHrothgarHats = false;
|
||||
_doNotHideVieraHats = false;
|
||||
}
|
||||
|
||||
public unsafe EqpEntry Apply(EqpEntry original, CharacterArmor* armor)
|
||||
{
|
||||
if (_doNotHideVieraHats)
|
||||
original |= EqpEntry.HeadShowVieraHat;
|
||||
|
||||
if (_doNotHideHrothgarHats)
|
||||
original |= EqpEntry.HeadShowHrothgarHat;
|
||||
|
||||
if (_doNotHideEarrings.Contains(armor[5].Set))
|
||||
original |= EqpEntry.HeadShowEarrings | EqpEntry.HeadShowEarringsAura | EqpEntry.HeadShowEarringsHuman;
|
||||
|
||||
if (_doNotHideNecklace.Contains(armor[6].Set))
|
||||
original |= EqpEntry.BodyShowNecklace | EqpEntry.HeadShowNecklace;
|
||||
|
||||
if (_doNotHideBracelets.Contains(armor[7].Set))
|
||||
original |= EqpEntry.BodyShowBracelet | EqpEntry.HandShowBracelet;
|
||||
|
||||
if (_doNotHideBracelets.Contains(armor[8].Set))
|
||||
original |= EqpEntry.HandShowRingR;
|
||||
|
||||
if (_doNotHideBracelets.Contains(armor[9].Set))
|
||||
original |= EqpEntry.HandShowRingL;
|
||||
return original;
|
||||
}
|
||||
|
||||
public bool Add(GlobalEqpManipulation manipulation)
|
||||
=> manipulation.Type switch
|
||||
{
|
||||
GlobalEqpType.DoNotHideEarrings => _doNotHideEarrings.Add(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideNecklace => _doNotHideNecklace.Add(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideBracelets => _doNotHideBracelets.Add(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideRingR => _doNotHideRingR.Add(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideRingL => _doNotHideRingL.Add(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideHrothgarHats => !_doNotHideHrothgarHats && (_doNotHideHrothgarHats = true),
|
||||
GlobalEqpType.DoNotHideVieraHats => !_doNotHideVieraHats && (_doNotHideVieraHats = true),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
public bool Remove(GlobalEqpManipulation manipulation)
|
||||
=> manipulation.Type switch
|
||||
{
|
||||
GlobalEqpType.DoNotHideEarrings => _doNotHideEarrings.Remove(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideNecklace => _doNotHideNecklace.Remove(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideBracelets => _doNotHideBracelets.Remove(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideRingR => _doNotHideRingR.Remove(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideRingL => _doNotHideRingL.Remove(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideHrothgarHats => _doNotHideHrothgarHats && !(_doNotHideHrothgarHats = false),
|
||||
GlobalEqpType.DoNotHideVieraHats => _doNotHideVieraHats && !(_doNotHideVieraHats = false),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
50
Penumbra/Meta/Manipulations/GlobalEqpManipulation.cs
Normal file
50
Penumbra/Meta/Manipulations/GlobalEqpManipulation.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Interop.Structs;
|
||||
|
||||
namespace Penumbra.Meta.Manipulations;
|
||||
|
||||
public readonly struct GlobalEqpManipulation : IMetaManipulation<GlobalEqpManipulation>
|
||||
{
|
||||
public GlobalEqpType Type { get; init; }
|
||||
public PrimaryId Condition { get; init; }
|
||||
|
||||
public bool Validate()
|
||||
{
|
||||
if (!Enum.IsDefined(Type))
|
||||
return false;
|
||||
|
||||
if (Type is GlobalEqpType.DoNotHideVieraHats or GlobalEqpType.DoNotHideHrothgarHats)
|
||||
return Condition == 0;
|
||||
|
||||
return Condition != 0;
|
||||
}
|
||||
|
||||
|
||||
public bool Equals(GlobalEqpManipulation other)
|
||||
=> Type == other.Type
|
||||
&& Condition.Equals(other.Condition);
|
||||
|
||||
public int CompareTo(GlobalEqpManipulation other)
|
||||
{
|
||||
var typeComp = Type.CompareTo(other);
|
||||
return typeComp != 0 ? typeComp : Condition.Id.CompareTo(other.Condition.Id);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
=> obj is GlobalEqpManipulation other && Equals(other);
|
||||
|
||||
public override int GetHashCode()
|
||||
=> HashCode.Combine((int)Type, Condition);
|
||||
|
||||
public static bool operator ==(GlobalEqpManipulation left, GlobalEqpManipulation right)
|
||||
=> left.Equals(right);
|
||||
|
||||
public static bool operator !=(GlobalEqpManipulation left, GlobalEqpManipulation right)
|
||||
=> !left.Equals(right);
|
||||
|
||||
public override string ToString()
|
||||
=> $"Global EQP - {Type}{(Condition != 0 ? $" - {Condition.Id}" : string.Empty)}";
|
||||
|
||||
public MetaIndex FileIndex()
|
||||
=> (MetaIndex)(-1);
|
||||
}
|
||||
61
Penumbra/Meta/Manipulations/GlobalEqpType.cs
Normal file
61
Penumbra/Meta/Manipulations/GlobalEqpType.cs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
namespace Penumbra.Meta.Manipulations;
|
||||
|
||||
public enum GlobalEqpType
|
||||
{
|
||||
DoNotHideEarrings,
|
||||
DoNotHideNecklace,
|
||||
DoNotHideBracelets,
|
||||
DoNotHideRingR,
|
||||
DoNotHideRingL,
|
||||
DoNotHideHrothgarHats,
|
||||
DoNotHideVieraHats,
|
||||
}
|
||||
|
||||
public static class GlobalEqpExtensions
|
||||
{
|
||||
public static bool HasCondition(this GlobalEqpType type)
|
||||
=> type switch
|
||||
{
|
||||
GlobalEqpType.DoNotHideEarrings => true,
|
||||
GlobalEqpType.DoNotHideNecklace => true,
|
||||
GlobalEqpType.DoNotHideBracelets => true,
|
||||
GlobalEqpType.DoNotHideRingR => true,
|
||||
GlobalEqpType.DoNotHideRingL => true,
|
||||
GlobalEqpType.DoNotHideHrothgarHats => false,
|
||||
GlobalEqpType.DoNotHideVieraHats => false,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
|
||||
public static ReadOnlySpan<byte> ToName(this GlobalEqpType type)
|
||||
=> type switch
|
||||
{
|
||||
GlobalEqpType.DoNotHideEarrings => "Do Not Hide Earrings"u8,
|
||||
GlobalEqpType.DoNotHideNecklace => "Do Not Hide Necklaces"u8,
|
||||
GlobalEqpType.DoNotHideBracelets => "Do Not Hide Bracelets"u8,
|
||||
GlobalEqpType.DoNotHideRingR => "Do Not Hide Rings (Right Finger)"u8,
|
||||
GlobalEqpType.DoNotHideRingL => "Do Not Hide Rings (Left Finger)"u8,
|
||||
GlobalEqpType.DoNotHideHrothgarHats => "Do Not Hide Hats for Hrothgar"u8,
|
||||
GlobalEqpType.DoNotHideVieraHats => "Do Not Hide Hats for Viera"u8,
|
||||
_ => "\0"u8,
|
||||
};
|
||||
|
||||
public static ReadOnlySpan<byte> ToDescription(this GlobalEqpType type)
|
||||
=> type switch
|
||||
{
|
||||
GlobalEqpType.DoNotHideEarrings => "Prevents the game from hiding earrings through other models when a specific earring is worn."u8,
|
||||
GlobalEqpType.DoNotHideNecklace =>
|
||||
"Prevents the game from hiding necklaces through other models when a specific necklace is worn."u8,
|
||||
GlobalEqpType.DoNotHideBracelets =>
|
||||
"Prevents the game from hiding bracelets through other models when a specific bracelet is worn."u8,
|
||||
GlobalEqpType.DoNotHideRingR =>
|
||||
"Prevents the game from hiding rings worn on the right finger through other models when a specific ring is worn on the right finger."u8,
|
||||
GlobalEqpType.DoNotHideRingL =>
|
||||
"Prevents the game from hiding rings worn on the left finger through other models when a specific ring is worn on the left finger."u8,
|
||||
GlobalEqpType.DoNotHideHrothgarHats =>
|
||||
"Prevents the game from hiding any hats for Hrothgar that are normally flagged to not display on them."u8,
|
||||
GlobalEqpType.DoNotHideVieraHats =>
|
||||
"Prevents the game from hiding any hats for Viera that are normally flagged to not display on them."u8,
|
||||
_ => "\0"u8,
|
||||
};
|
||||
}
|
||||
|
|
@ -28,6 +28,7 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
|
|||
Est = 4,
|
||||
Gmp = 5,
|
||||
Rsp = 6,
|
||||
GlobalEqp = 7,
|
||||
}
|
||||
|
||||
[FieldOffset(0)]
|
||||
|
|
@ -54,6 +55,10 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
|
|||
[JsonIgnore]
|
||||
public readonly ImcManipulation Imc = default;
|
||||
|
||||
[FieldOffset(0)]
|
||||
[JsonIgnore]
|
||||
public readonly GlobalEqpManipulation GlobalEqp = default;
|
||||
|
||||
[FieldOffset(15)]
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
[JsonProperty("Type")]
|
||||
|
|
@ -70,6 +75,7 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
|
|||
Type.Est => Est,
|
||||
Type.Gmp => Gmp,
|
||||
Type.Rsp => Rsp,
|
||||
Type.GlobalEqp => GlobalEqp,
|
||||
_ => null,
|
||||
};
|
||||
init
|
||||
|
|
@ -100,6 +106,10 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
|
|||
Imc = m;
|
||||
ManipulationType = m.Validate(true) ? Type.Imc : Type.Unknown;
|
||||
return;
|
||||
case GlobalEqpManipulation m:
|
||||
GlobalEqp = m;
|
||||
ManipulationType = m.Validate() ? Type.GlobalEqp : Type.Unknown;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -114,6 +124,7 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
|
|||
Type.Est => Est.Validate(),
|
||||
Type.Gmp => Gmp.Validate(),
|
||||
Type.Rsp => Rsp.Validate(),
|
||||
Type.GlobalEqp => GlobalEqp.Validate(),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
|
@ -154,6 +165,12 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
|
|||
ManipulationType = Type.Imc;
|
||||
}
|
||||
|
||||
public MetaManipulation(GlobalEqpManipulation eqp)
|
||||
{
|
||||
GlobalEqp = eqp;
|
||||
ManipulationType = Type.GlobalEqp;
|
||||
}
|
||||
|
||||
public static implicit operator MetaManipulation(EqpManipulation eqp)
|
||||
=> new(eqp);
|
||||
|
||||
|
|
@ -172,6 +189,9 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
|
|||
public static implicit operator MetaManipulation(ImcManipulation imc)
|
||||
=> new(imc);
|
||||
|
||||
public static implicit operator MetaManipulation(GlobalEqpManipulation eqp)
|
||||
=> new(eqp);
|
||||
|
||||
public bool EntryEquals(MetaManipulation other)
|
||||
{
|
||||
if (ManipulationType != other.ManipulationType)
|
||||
|
|
@ -185,6 +205,7 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
|
|||
Type.Est => Est.Entry.Equals(other.Est.Entry),
|
||||
Type.Rsp => Rsp.Entry.Equals(other.Rsp.Entry),
|
||||
Type.Imc => Imc.Entry.Equals(other.Imc.Entry),
|
||||
Type.GlobalEqp => true,
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
}
|
||||
|
|
@ -202,6 +223,7 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
|
|||
Type.Est => Est.Equals(other.Est),
|
||||
Type.Rsp => Rsp.Equals(other.Rsp),
|
||||
Type.Imc => Imc.Equals(other.Imc),
|
||||
Type.GlobalEqp => GlobalEqp.Equals(other.GlobalEqp),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
|
@ -219,6 +241,7 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
|
|||
Type.Est => Est.Copy(other.Est.Entry),
|
||||
Type.Rsp => Rsp.Copy(other.Rsp.Entry),
|
||||
Type.Imc => Imc.Copy(other.Imc.Entry),
|
||||
Type.GlobalEqp => GlobalEqp,
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
}
|
||||
|
|
@ -235,6 +258,7 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
|
|||
Type.Est => Est.GetHashCode(),
|
||||
Type.Rsp => Rsp.GetHashCode(),
|
||||
Type.Imc => Imc.GetHashCode(),
|
||||
Type.GlobalEqp => GlobalEqp.GetHashCode(),
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
|
|
@ -255,6 +279,7 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
|
|||
Type.Est => Est.ToString(),
|
||||
Type.Rsp => Rsp.ToString(),
|
||||
Type.Imc => Imc.ToString(),
|
||||
Type.GlobalEqp => GlobalEqp.ToString(),
|
||||
_ => "Invalid",
|
||||
};
|
||||
|
||||
|
|
@ -268,6 +293,7 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
|
|||
Type.Est => $"{Est.Entry}",
|
||||
Type.Gmp => $"{Gmp.Entry.Value}",
|
||||
Type.Rsp => $"{Rsp.Entry}",
|
||||
Type.GlobalEqp => string.Empty,
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ public class ModMetaEditor(ModManager modManager)
|
|||
private readonly HashSet<GmpManipulation> _gmp = [];
|
||||
private readonly HashSet<EstManipulation> _est = [];
|
||||
private readonly HashSet<RspManipulation> _rsp = [];
|
||||
private readonly HashSet<GlobalEqpManipulation> _globalEqp = [];
|
||||
|
||||
public int OtherImcCount { get; private set; }
|
||||
public int OtherEqpCount { get; private set; }
|
||||
|
|
@ -19,6 +20,7 @@ public class ModMetaEditor(ModManager modManager)
|
|||
public int OtherGmpCount { get; private set; }
|
||||
public int OtherEstCount { get; private set; }
|
||||
public int OtherRspCount { get; private set; }
|
||||
public int OtherGlobalEqpCount { get; private set; }
|
||||
|
||||
public bool Changes { get; private set; }
|
||||
|
||||
|
|
@ -40,6 +42,9 @@ public class ModMetaEditor(ModManager modManager)
|
|||
public IReadOnlySet<RspManipulation> Rsp
|
||||
=> _rsp;
|
||||
|
||||
public IReadOnlySet<GlobalEqpManipulation> GlobalEqp
|
||||
=> _globalEqp;
|
||||
|
||||
public bool CanAdd(MetaManipulation m)
|
||||
{
|
||||
return m.ManipulationType switch
|
||||
|
|
@ -50,6 +55,7 @@ public class ModMetaEditor(ModManager modManager)
|
|||
MetaManipulation.Type.Est => !_est.Contains(m.Est),
|
||||
MetaManipulation.Type.Gmp => !_gmp.Contains(m.Gmp),
|
||||
MetaManipulation.Type.Rsp => !_rsp.Contains(m.Rsp),
|
||||
MetaManipulation.Type.GlobalEqp => !_globalEqp.Contains(m.GlobalEqp),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
|
@ -64,6 +70,7 @@ public class ModMetaEditor(ModManager modManager)
|
|||
MetaManipulation.Type.Est => _est.Add(m.Est),
|
||||
MetaManipulation.Type.Gmp => _gmp.Add(m.Gmp),
|
||||
MetaManipulation.Type.Rsp => _rsp.Add(m.Rsp),
|
||||
MetaManipulation.Type.GlobalEqp => _globalEqp.Add(m.GlobalEqp),
|
||||
_ => false,
|
||||
};
|
||||
Changes |= added;
|
||||
|
|
@ -80,6 +87,7 @@ public class ModMetaEditor(ModManager modManager)
|
|||
MetaManipulation.Type.Est => _est.Remove(m.Est),
|
||||
MetaManipulation.Type.Gmp => _gmp.Remove(m.Gmp),
|
||||
MetaManipulation.Type.Rsp => _rsp.Remove(m.Rsp),
|
||||
MetaManipulation.Type.GlobalEqp => _globalEqp.Remove(m.GlobalEqp),
|
||||
_ => false,
|
||||
};
|
||||
Changes |= deleted;
|
||||
|
|
@ -100,6 +108,7 @@ public class ModMetaEditor(ModManager modManager)
|
|||
_gmp.Clear();
|
||||
_est.Clear();
|
||||
_rsp.Clear();
|
||||
_globalEqp.Clear();
|
||||
Changes = true;
|
||||
}
|
||||
|
||||
|
|
@ -111,6 +120,7 @@ public class ModMetaEditor(ModManager modManager)
|
|||
OtherGmpCount = 0;
|
||||
OtherEstCount = 0;
|
||||
OtherRspCount = 0;
|
||||
OtherGlobalEqpCount = 0;
|
||||
foreach (var option in mod.AllDataContainers)
|
||||
{
|
||||
if (option == currentOption)
|
||||
|
|
@ -138,6 +148,9 @@ public class ModMetaEditor(ModManager modManager)
|
|||
case MetaManipulation.Type.Rsp:
|
||||
++OtherRspCount;
|
||||
break;
|
||||
case MetaManipulation.Type.GlobalEqp:
|
||||
++OtherGlobalEqpCount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -179,6 +192,9 @@ public class ModMetaEditor(ModManager modManager)
|
|||
case MetaManipulation.Type.Rsp:
|
||||
_rsp.Add(manip.Rsp);
|
||||
break;
|
||||
case MetaManipulation.Type.GlobalEqp:
|
||||
_globalEqp.Add(manip.GlobalEqp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -191,5 +207,6 @@ public class ModMetaEditor(ModManager modManager)
|
|||
.Concat(_eqp.Select(m => (MetaManipulation)m))
|
||||
.Concat(_est.Select(m => (MetaManipulation)m))
|
||||
.Concat(_gmp.Select(m => (MetaManipulation)m))
|
||||
.Concat(_rsp.Select(m => (MetaManipulation)m));
|
||||
.Concat(_rsp.Select(m => (MetaManipulation)m))
|
||||
.Concat(_globalEqp.Select(m => (MetaManipulation)m));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ using Penumbra.String.Classes;
|
|||
|
||||
namespace Penumbra.Mods.SubMods;
|
||||
|
||||
|
||||
public interface IModDataContainer
|
||||
{
|
||||
public IMod Mod { get; }
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using Dalamud.Interface;
|
|||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Text;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Interop.Structs;
|
||||
|
|
@ -75,6 +76,8 @@ public partial class ModEditWindow
|
|||
_editor.MetaEditor.OtherGmpCount);
|
||||
DrawEditHeader(_editor.MetaEditor.Rsp, "Racial Scaling Edits (RSP)###RSP", 5, RspRow.Draw, RspRow.DrawNew,
|
||||
_editor.MetaEditor.OtherRspCount);
|
||||
DrawEditHeader(_editor.MetaEditor.GlobalEqp, "Global Equipment Parameter Edits Edits (Global EQP)###GEQP", 4, GlobalEqpRow.Draw,
|
||||
GlobalEqpRow.DrawNew, _editor.MetaEditor.OtherGlobalEqpCount);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -702,6 +705,69 @@ public partial class ModEditWindow
|
|||
}
|
||||
}
|
||||
|
||||
private static class GlobalEqpRow
|
||||
{
|
||||
private static GlobalEqpManipulation _new = new()
|
||||
{
|
||||
Type = GlobalEqpType.DoNotHideEarrings,
|
||||
Condition = 1,
|
||||
};
|
||||
|
||||
public static void DrawNew(MetaFileManager metaFileManager, ModEditor editor, Vector2 iconSize)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
CopyToClipboardButton("Copy all current global EQP manipulations to clipboard.", iconSize,
|
||||
editor.MetaEditor.GlobalEqp.Select(m => (MetaManipulation)m));
|
||||
ImGui.TableNextColumn();
|
||||
var canAdd = editor.MetaEditor.CanAdd(_new);
|
||||
var tt = canAdd ? "Stage this edit." : "This entry is already manipulated.";
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), iconSize, tt, !canAdd, true))
|
||||
editor.MetaEditor.Add(_new);
|
||||
|
||||
// Identifier
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth(250 * ImUtf8.GlobalScale);
|
||||
using (var combo = ImUtf8.Combo("##geqpType"u8, _new.Type.ToName()))
|
||||
{
|
||||
if (combo)
|
||||
foreach (var type in Enum.GetValues<GlobalEqpType>())
|
||||
{
|
||||
if (ImUtf8.Selectable(type.ToName(), type == _new.Type))
|
||||
_new = new GlobalEqpManipulation
|
||||
{
|
||||
Type = type,
|
||||
Condition = type.HasCondition() ? _new.Type.HasCondition() ? _new.Condition : 1 : 0,
|
||||
};
|
||||
ImUtf8.HoverTooltip(type.ToDescription());
|
||||
}
|
||||
}
|
||||
|
||||
ImUtf8.HoverTooltip(_new.Type.ToDescription());
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
if (!_new.Type.HasCondition())
|
||||
return;
|
||||
|
||||
if (IdInput("##geqpCond", 100 * ImUtf8.GlobalScale, _new.Condition.Id, out var newId, 1, ushort.MaxValue, _new.Condition.Id <= 1))
|
||||
_new = _new with { Condition = newId };
|
||||
}
|
||||
|
||||
public static void Draw(MetaFileManager metaFileManager, GlobalEqpManipulation meta, ModEditor editor, Vector2 iconSize)
|
||||
{
|
||||
DrawMetaButtons(meta, editor, iconSize);
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
|
||||
ImUtf8.Text(meta.Type.ToName());
|
||||
ImUtf8.HoverTooltip(meta.Type.ToDescription());
|
||||
ImGui.TableNextColumn();
|
||||
if (meta.Type.HasCondition())
|
||||
{
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
|
||||
ImUtf8.Text($"{meta.Condition.Id}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A number input for ids with a optional max id of given width.
|
||||
// Returns true if newId changed against currentId.
|
||||
private static bool IdInput(string label, float width, ushort currentId, out ushort newId, int minId, int maxId, bool border)
|
||||
|
|
|
|||
|
|
@ -76,6 +76,26 @@ public static class IdentifierExtensions
|
|||
case MetaManipulation.Type.Rsp:
|
||||
changedItems.TryAdd($"{manip.Rsp.SubRace.ToName()} {manip.Rsp.Attribute.ToFullString()}", null);
|
||||
break;
|
||||
case MetaManipulation.Type.GlobalEqp:
|
||||
var path = manip.GlobalEqp.Type switch
|
||||
{
|
||||
GlobalEqpType.DoNotHideEarrings => GamePaths.Accessory.Mdl.Path(manip.GlobalEqp.Condition, GenderRace.MidlanderMale,
|
||||
EquipSlot.Ears),
|
||||
GlobalEqpType.DoNotHideNecklace => GamePaths.Accessory.Mdl.Path(manip.GlobalEqp.Condition, GenderRace.MidlanderMale,
|
||||
EquipSlot.Neck),
|
||||
GlobalEqpType.DoNotHideBracelets => GamePaths.Accessory.Mdl.Path(manip.GlobalEqp.Condition, GenderRace.MidlanderMale,
|
||||
EquipSlot.Wrists),
|
||||
GlobalEqpType.DoNotHideRingR => GamePaths.Accessory.Mdl.Path(manip.GlobalEqp.Condition, GenderRace.MidlanderMale,
|
||||
EquipSlot.RFinger),
|
||||
GlobalEqpType.DoNotHideRingL => GamePaths.Accessory.Mdl.Path(manip.GlobalEqp.Condition, GenderRace.MidlanderMale,
|
||||
EquipSlot.LFinger),
|
||||
GlobalEqpType.DoNotHideHrothgarHats => string.Empty,
|
||||
GlobalEqpType.DoNotHideVieraHats => string.Empty,
|
||||
_ => string.Empty,
|
||||
};
|
||||
if (path.Length > 0)
|
||||
identifier.Identify(changedItems, path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue