diff --git a/Glamourer/GameData/CharaMakeParams.cs b/Glamourer/GameData/CharaMakeParams.cs
deleted file mode 100644
index 4db5825..0000000
--- a/Glamourer/GameData/CharaMakeParams.cs
+++ /dev/null
@@ -1,126 +0,0 @@
-using Lumina.Data;
-using Lumina.Excel;
-using Lumina.Excel.GeneratedSheets;
-
-namespace Glamourer.GameData;
-
-/// A custom version of CharaMakeParams that is easier to parse.
-[Sheet("CharaMakeParams")]
-public class CharaMakeParams : ExcelRow
-{
- public const int NumMenus = 28;
- public const int NumVoices = 12;
- public const int NumGraphics = 10;
- public const int MaxNumValues = 100;
- public const int NumFaces = 8;
- public const int NumFeatures = 7;
- public const int NumEquip = 3;
-
- public enum MenuType
- {
- ListSelector = 0,
- IconSelector = 1,
- ColorPicker = 2,
- DoubleColorPicker = 3,
- IconCheckmark = 4,
- Percentage = 5,
- Checkmark = 6, // custom
- Nothing = 7, // custom
- List1Selector = 8, // custom, 1-indexed lists
- }
-
- public struct Menu
- {
- public uint Id;
- public byte InitVal;
- public MenuType Type;
- public byte Size;
- public byte LookAt;
- public uint Mask;
- public uint Customize;
- public uint[] Values;
- public byte[] Graphic;
- }
-
- public struct FacialFeatures
- {
- public uint[] Icons;
- }
-
- public LazyRow Race { get; set; } = null!;
- public LazyRow Tribe { get; set; } = null!;
- public sbyte Gender { get; set; }
-
- public Menu[] Menus { get; set; } = new Menu[NumMenus];
- public byte[] Voices { get; set; } = new byte[NumVoices];
- public FacialFeatures[] FacialFeatureByFace { get; set; } = new FacialFeatures[NumFaces];
-
- public CharaMakeType.CharaMakeTypeUnkData3347Obj[] Equip { get; set; } = new CharaMakeType.CharaMakeTypeUnkData3347Obj[NumEquip];
-
- public override void PopulateData(RowParser parser, Lumina.GameData gameData, Language language)
- {
- RowId = parser.RowId;
- SubRowId = parser.SubRowId;
- Race = new LazyRow(gameData, parser.ReadColumn(0), language);
- Tribe = new LazyRow(gameData, parser.ReadColumn(1), language);
- Gender = parser.ReadColumn(2);
- int currentOffset;
- for (var i = 0; i < NumMenus; ++i)
- {
- currentOffset = 3 + i;
- Menus[i].Id = parser.ReadColumn(0 * NumMenus + currentOffset);
- Menus[i].InitVal = parser.ReadColumn(1 * NumMenus + currentOffset);
- Menus[i].Type = (MenuType)parser.ReadColumn(2 * NumMenus + currentOffset);
- Menus[i].Size = parser.ReadColumn(3 * NumMenus + currentOffset);
- Menus[i].LookAt = parser.ReadColumn(4 * NumMenus + currentOffset);
- Menus[i].Mask = parser.ReadColumn(5 * NumMenus + currentOffset);
- Menus[i].Customize = parser.ReadColumn(6 * NumMenus + currentOffset);
- Menus[i].Values = new uint[Menus[i].Size];
-
- switch (Menus[i].Type)
- {
- case MenuType.ColorPicker:
- case MenuType.DoubleColorPicker:
- case MenuType.Percentage:
- break;
- default:
- currentOffset += 7 * NumMenus;
- for (var j = 0; j < Menus[i].Size; ++j)
- Menus[i].Values[j] = parser.ReadColumn(j * NumMenus + currentOffset);
- break;
- }
-
- Menus[i].Graphic = new byte[NumGraphics];
- currentOffset = 3 + (MaxNumValues + 7) * NumMenus + i;
- for (var j = 0; j < NumGraphics; ++j)
- Menus[i].Graphic[j] = parser.ReadColumn(j * NumMenus + currentOffset);
- }
-
- currentOffset = 3 + (MaxNumValues + 7 + NumGraphics) * NumMenus;
- for (var i = 0; i < NumVoices; ++i)
- Voices[i] = parser.ReadColumn(currentOffset++);
-
- for (var i = 0; i < NumFaces; ++i)
- {
- currentOffset = 3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + NumVoices + i;
- FacialFeatureByFace[i].Icons = new uint[NumFeatures];
- for (var j = 0; j < NumFeatures; ++j)
- FacialFeatureByFace[i].Icons[j] = (uint)parser.ReadColumn(j * NumFaces + currentOffset);
- }
-
- for (var i = 0; i < NumEquip; ++i)
- {
- currentOffset = 3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + NumVoices + NumFaces * NumFeatures + i * 7;
- Equip[i] = new CharaMakeType.CharaMakeTypeUnkData3347Obj()
- {
- Helmet = parser.ReadColumn(currentOffset + 0),
- Top = parser.ReadColumn(currentOffset + 1),
- Gloves = parser.ReadColumn(currentOffset + 2),
- Legs = parser.ReadColumn(currentOffset + 3),
- Shoes = parser.ReadColumn(currentOffset + 4),
- Weapon = parser.ReadColumn(currentOffset + 5),
- SubWeapon = parser.ReadColumn(currentOffset + 6),
- };
- }
- }
-}
diff --git a/Glamourer/GameData/CustomizeSet.cs b/Glamourer/GameData/CustomizeSet.cs
index 0c80e13..7fcf1d2 100644
--- a/Glamourer/GameData/CustomizeSet.cs
+++ b/Glamourer/GameData/CustomizeSet.cs
@@ -1,6 +1,7 @@
using OtterGui;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
+using Race = Penumbra.GameData.Enums.Race;
namespace Glamourer.GameData;
@@ -38,9 +39,9 @@ public class CustomizeSet
public string Option(CustomizeIndex index)
=> OptionName[(int)index];
- public IReadOnlyList Voices { get; internal init; } = null!;
- public IReadOnlyList Types { get; internal set; } = null!;
- public IReadOnlyDictionary Order { get; internal set; } = null!;
+ public IReadOnlyList Voices { get; internal init; } = null!;
+ public IReadOnlyList Types { get; internal set; } = null!;
+ public IReadOnlyDictionary Order { get; internal set; } = null!;
// Always list selector.
@@ -97,9 +98,9 @@ public class CustomizeSet
return type switch
{
- CharaMakeParams.MenuType.ListSelector => GetInteger0(out custom),
- CharaMakeParams.MenuType.List1Selector => GetInteger1(out custom),
- CharaMakeParams.MenuType.IconSelector => index switch
+ MenuType.ListSelector => GetInteger0(out custom),
+ MenuType.List1Selector => GetInteger1(out custom),
+ MenuType.IconSelector => index switch
{
CustomizeIndex.Face => Get(Faces, HrothgarFaceHack(value), out custom),
CustomizeIndex.Hairstyle => Get((face = HrothgarFaceHack(face)).Value < HairByFace.Count ? HairByFace[face.Value] : HairStyles,
@@ -109,7 +110,7 @@ public class CustomizeSet
CustomizeIndex.LipColor => Get(LipColorsDark, value, out custom),
_ => Invalid(out custom),
},
- CharaMakeParams.MenuType.ColorPicker => index switch
+ MenuType.ColorPicker => index switch
{
CustomizeIndex.SkinColor => Get(SkinColors, value, out custom),
CustomizeIndex.EyeColorLeft => Get(EyeColors, value, out custom),
@@ -121,16 +122,16 @@ public class CustomizeSet
CustomizeIndex.FacePaintColor => Get(FacePaintColorsDark.Concat(FacePaintColorsLight), value, out custom),
_ => Invalid(out custom),
},
- CharaMakeParams.MenuType.DoubleColorPicker => index switch
+ MenuType.DoubleColorPicker => index switch
{
CustomizeIndex.LipColor => Get(LipColorsDark.Concat(LipColorsLight), value, out custom),
CustomizeIndex.FacePaintColor => Get(FacePaintColorsDark.Concat(FacePaintColorsLight), value, out custom),
_ => Invalid(out custom),
},
- CharaMakeParams.MenuType.IconCheckmark => GetBool(index, value, out custom),
- CharaMakeParams.MenuType.Percentage => GetInteger0(out custom),
- CharaMakeParams.MenuType.Checkmark => GetBool(index, value, out custom),
- _ => Invalid(out custom),
+ MenuType.IconCheckmark => GetBool(index, value, out custom),
+ MenuType.Percentage => GetInteger0(out custom),
+ MenuType.Checkmark => GetBool(index, value, out custom),
+ _ => Invalid(out custom),
};
int Get(IEnumerable list, CustomizeValue v, out CustomizeData? output)
@@ -208,10 +209,10 @@ public class CustomizeSet
switch (Types[(int)index])
{
- case CharaMakeParams.MenuType.Percentage: return new CustomizeData(index, (CustomizeValue)idx, 0, (ushort)idx);
- case CharaMakeParams.MenuType.ListSelector: return new CustomizeData(index, (CustomizeValue)idx, 0, (ushort)idx);
- case CharaMakeParams.MenuType.List1Selector: return new CustomizeData(index, (CustomizeValue)(idx + 1), 0, (ushort)idx);
- case CharaMakeParams.MenuType.Checkmark: return new CustomizeData(index, CustomizeValue.Bool(idx != 0), 0, (ushort)idx);
+ case MenuType.Percentage: return new CustomizeData(index, (CustomizeValue)idx, 0, (ushort)idx);
+ case MenuType.ListSelector: return new CustomizeData(index, (CustomizeValue)idx, 0, (ushort)idx);
+ case MenuType.List1Selector: return new CustomizeData(index, (CustomizeValue)(idx + 1), 0, (ushort)idx);
+ case MenuType.Checkmark: return new CustomizeData(index, CustomizeValue.Bool(idx != 0), 0, (ushort)idx);
}
return index switch
@@ -241,7 +242,7 @@ public class CustomizeSet
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
- public CharaMakeParams.MenuType Type(CustomizeIndex index)
+ public MenuType Type(CustomizeIndex index)
=> Types[(int)index];
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
@@ -256,9 +257,9 @@ public class CustomizeSet
return Type(index) switch
{
- CharaMakeParams.MenuType.Percentage => 101,
- CharaMakeParams.MenuType.IconCheckmark => 2,
- CharaMakeParams.MenuType.Checkmark => 2,
+ MenuType.Percentage => 101,
+ MenuType.IconCheckmark => 2,
+ MenuType.Checkmark => 2,
_ => index switch
{
CustomizeIndex.Face => Faces.Count,
diff --git a/Glamourer/GameData/CustomizeSetFactory.cs b/Glamourer/GameData/CustomizeSetFactory.cs
index f626750..36cdb1b 100644
--- a/Glamourer/GameData/CustomizeSetFactory.cs
+++ b/Glamourer/GameData/CustomizeSetFactory.cs
@@ -1,10 +1,10 @@
-using Dalamud;
-using Dalamud.Game;
+using Dalamud.Game;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
using Lumina.Excel;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using OtterGui.Classes;
+using Penumbra.GameData;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Race = Penumbra.GameData.Enums.Race;
@@ -32,7 +32,7 @@ internal class CustomizeSetFactory(
var set = new CustomizeSet(race, gender)
{
Name = GetName(race, gender),
- Voices = row.Voices,
+ Voices = row.VoiceStruct,
HairStyles = GetHairStyles(race, gender),
HairColors = hair,
SkinColors = skin,
@@ -59,7 +59,7 @@ internal class CustomizeSetFactory(
}
/// Some data can not be set independently of the rest, so we need a post-processing step to finalize.
- private void SetPostProcessing(CustomizeSet set, CharaMakeParams row)
+ private void SetPostProcessing(CustomizeSet set, in CharaMakeType row)
{
SetAvailability(set, row);
SetFacialFeatures(set, row);
@@ -112,10 +112,10 @@ internal class CustomizeSetFactory(
}
private readonly ColorParameters _colorParameters = new(_gameData, _log);
- private readonly ExcelSheet _customizeSheet = _gameData.GetExcelSheet(ClientLanguage.English)!;
- private readonly ExcelSheet _lobbySheet = _gameData.GetExcelSheet(ClientLanguage.English)!;
- private readonly ExcelSheet _hairSheet = _gameData.GetExcelSheet(ClientLanguage.English)!;
- private readonly ExcelSheet _tribeSheet = _gameData.GetExcelSheet(ClientLanguage.English)!;
+ private readonly ExcelSheet _customizeSheet = _gameData.GetExcelSheet(ClientLanguage.English);
+ private readonly ExcelSheet _lobbySheet = _gameData.GetExcelSheet(ClientLanguage.English);
+ private readonly ExcelSheet _hairSheet = _gameData.GetExcelSheet(ClientLanguage.English, "HairMakeType");
+ private readonly ExcelSheet _tribeSheet = _gameData.GetExcelSheet(ClientLanguage.English);
// Those color pickers are shared between all races.
private readonly CustomizeData[] _highlightPicker = CreateColors(_colors, CustomizeIndex.HighlightsColor, 256, 192);
@@ -126,12 +126,7 @@ internal class CustomizeSetFactory(
private readonly CustomizeData[] _facePaintColorPickerLight = CreateColors(_colors, CustomizeIndex.FacePaintColor, 1152, 96, true);
private readonly CustomizeData[] _tattooColorPicker = CreateColors(_colors, CustomizeIndex.TattooColor, 0, 192);
- private readonly ExcelSheet _charaMakeSheet = _gameData.Excel
- .GetType()
- .GetMethod("GetSheet", BindingFlags.Instance | BindingFlags.NonPublic)?
- .MakeGenericMethod(typeof(CharaMakeParams))
- .Invoke(_gameData.Excel, ["charamaketype", _gameData.Language.ToLumina(), null])! as ExcelSheet
- ?? null!;
+ private readonly ExcelSheet _charaMakeSheet = _gameData.Excel.GetSheet();
/// Obtain available skin and hair colors for the given clan and gender.
private (CustomizeData[] Skin, CustomizeData[] Hair) GetSkinHairColors(SubRace race, Gender gender)
@@ -150,29 +145,28 @@ internal class CustomizeSetFactory(
private string GetName(SubRace race, Gender gender)
=> gender switch
{
- Gender.Male => _tribeSheet.GetRow((uint)race)?.Masculine.ToDalamudString().TextValue ?? race.ToName(),
- Gender.Female => _tribeSheet.GetRow((uint)race)?.Feminine.ToDalamudString().TextValue ?? race.ToName(),
+ Gender.Male => _tribeSheet.TryGetRow((uint)race, out var row) ? row.Masculine.ExtractText() : race.ToName(),
+ Gender.Female => _tribeSheet.TryGetRow((uint)race, out var row) ? row.Feminine.ExtractText() : race.ToName(),
_ => "Unknown",
};
/// Obtain available hairstyles via reflection from the Hair sheet for the given subrace and gender.
private CustomizeData[] GetHairStyles(SubRace race, Gender gender)
{
- var row = _hairSheet.GetRow(((uint)race - 1) * 2 - 1 + (uint)gender)!;
+ var row = _hairSheet.GetRow(((uint)race - 1) * 2 - 1 + (uint)gender);
// Unknown30 is the number of available hairstyles.
- var hairList = new List(row.Unknown30);
+ var numHairs = row.ReadUInt8Column(30);
+ var hairList = new List(numHairs);
// Hairstyles can be found starting at Unknown66.
- for (var i = 0; i < row.Unknown30; ++i)
+ for (var i = 0; i < numHairs; ++i)
{
- var name = $"Unknown{66 + i * 9}";
- var customizeIdx = (uint?)row.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.Instance)?.GetValue(row)
- ?? uint.MaxValue;
+ // Hairs start at Unknown66.
+ var customizeIdx = row.ReadUInt32Column(66 + i * 9);
if (customizeIdx == uint.MaxValue)
continue;
// Hair Row from CustomizeSheet might not be set in case of unlockable hair.
- var hairRow = _customizeSheet.GetRow(customizeIdx);
- if (hairRow == null)
+ if (_customizeSheet.TryGetRow(customizeIdx, out var hairRow))
hairList.Add(new CustomizeData(CustomizeIndex.Hairstyle, (CustomizeValue)i, customizeIdx));
else if (_icons.IconExists(hairRow.Icon))
hairList.Add(new CustomizeData(CustomizeIndex.Hairstyle, (CustomizeValue)hairRow.FeatureID, hairRow.Icon,
@@ -183,45 +177,40 @@ internal class CustomizeSetFactory(
}
/// Specific icons for tails or ears.
- private CustomizeData[] GetTailEarShapes(CharaMakeParams row)
- => row.Menus.Cast()
- .FirstOrDefault(m => m!.Value.Customize == CustomizeIndex.TailShape.ToByteAndMask().ByteIdx)?.Values
- .Select((v, i) => FromValueAndIndex(CustomizeIndex.TailShape, v, i)).ToArray()
- ?? [];
+ private CustomizeData[] GetTailEarShapes(CharaMakeType row)
+ => ExtractValues(row, CustomizeIndex.TailShape);
/// Specific icons for faces.
- private CustomizeData[] GetFaces(CharaMakeParams row)
- => row.Menus.Cast().FirstOrDefault(m => m!.Value.Customize == CustomizeIndex.Face.ToByteAndMask().ByteIdx)
- ?.Values
- .Select((v, i) => FromValueAndIndex(CustomizeIndex.Face, v, i)).ToArray()
- ?? [];
+ private CustomizeData[] GetFaces(CharaMakeType row)
+ => ExtractValues(row, CustomizeIndex.Face);
/// Specific icons for Hrothgar patterns.
- private CustomizeData[] HrothgarFurPattern(CharaMakeParams row)
- => row.Menus.Cast()
- .FirstOrDefault(m => m!.Value.Customize == CustomizeIndex.LipColor.ToByteAndMask().ByteIdx)?.Values
- .Select((v, i) => FromValueAndIndex(CustomizeIndex.LipColor, v, i)).ToArray()
- ?? [];
+ private CustomizeData[] HrothgarFurPattern(CharaMakeType row)
+ => ExtractValues(row, CustomizeIndex.LipColor);
+
+ private CustomizeData[] ExtractValues(CharaMakeType row, CustomizeIndex type)
+ {
+ var data = row.CharaMakeStruct.FirstOrNull(m => m.Customize == CustomizeIndex.TailShape.ToByteAndMask().ByteIdx);
+ return data?.SubMenuParam.Take(data.Value.SubMenuNum).Select((v, i) => FromValueAndIndex(type, v, i)).ToArray() ?? [];
+ }
/// Get face paints from the hair sheet via reflection since there are also unlockable face paints.
private CustomizeData[] GetFacePaints(SubRace race, Gender gender)
{
- var row = _hairSheet.GetRow(((uint)race - 1) * 2 - 1 + (uint)gender)!;
- var paintList = new List(row.Unknown37);
+ var row = _hairSheet.GetRow(((uint)race - 1) * 2 - 1 + (uint)gender);
// Number of available face paints is at Unknown37.
- for (var i = 0; i < row.Unknown37; ++i)
+ var numPaints = row.ReadUInt8Column(37);
+ var paintList = new List(numPaints);
+
+ for (var i = 0; i < numPaints; ++i)
{
// Face paints start at Unknown73.
- var name = $"Unknown{73 + i * 9}";
- var customizeIdx =
- (uint?)row.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.Instance)?.GetValue(row)
- ?? uint.MaxValue;
+ var customizeIdx = row.ReadUInt32Column(73 + i * 9);
if (customizeIdx == uint.MaxValue)
continue;
- var paintRow = _customizeSheet.GetRow(customizeIdx);
// Face paint Row from CustomizeSheet might not be set in case of unlockable face paints.
- if (paintRow != null)
+ if (_customizeSheet.TryGetRow(customizeIdx, out var paintRow))
paintList.Add(new CustomizeData(CustomizeIndex.FacePaint, (CustomizeValue)paintRow.FeatureID, paintRow.Icon,
(ushort)paintRow.RowId));
else
@@ -232,21 +221,18 @@ internal class CustomizeSetFactory(
}
/// Get List sizes.
- private static int GetListSize(CharaMakeParams row, CustomizeIndex index)
+ private static int GetListSize(CharaMakeType row, CustomizeIndex index)
{
var gameId = index.ToByteAndMask().ByteIdx;
- var menu = row.Menus.Cast().FirstOrDefault(m => m!.Value.Customize == gameId);
- return menu?.Size ?? 0;
+ var menu = row.CharaMakeStruct.FirstOrNull(m => m.Customize == gameId);
+ return menu?.SubMenuNum ?? 0;
}
/// Get generic Features.
private CustomizeData FromValueAndIndex(CustomizeIndex id, uint value, int index)
- {
- var row = _customizeSheet.GetRow(value);
- return row == null
- ? new CustomizeData(id, (CustomizeValue)(index + 1), value)
- : new CustomizeData(id, (CustomizeValue)row.FeatureID, row.Icon, (ushort)row.RowId);
- }
+ => _customizeSheet.TryGetRow(value, out var row)
+ ? new CustomizeData(id, (CustomizeValue)row.FeatureID, row.Icon, (ushort)row.RowId)
+ : new CustomizeData(id, (CustomizeValue)(index + 1), value);
/// Create generic color sets from the parameters.
private static CustomizeData[] CreateColors(ColorParameters colorParameters, CustomizeIndex index, int offset, int num,
@@ -264,28 +250,27 @@ internal class CustomizeSetFactory(
}
/// Set the specific option names for the given set of parameters.
- private string[] GetOptionNames(CharaMakeParams row)
+ private string[] GetOptionNames(CharaMakeType row)
{
var nameArray = Enum.GetValues().Select(c =>
{
// Find the first menu that corresponds to the Id.
var byteId = c.ToByteAndMask().ByteIdx;
- var menu = row.Menus
- .Cast()
- .FirstOrDefault(m => m!.Value.Customize == byteId);
+ var menu = row.CharaMakeStruct.FirstOrNull(m => m.Customize == byteId);
if (menu == null)
{
// If none exists and the id corresponds to highlights, set the Highlights name.
if (c == CustomizeIndex.Highlights)
- return string.Intern(_lobbySheet.GetRow(237)?.Text.ToDalamudString().ToString() ?? "Highlights");
+ return string.Intern(_lobbySheet.TryGetRow(237, out var text) ? text.Text.ExtractText() : "Highlights");
// Otherwise there is an error and we use the default name.
return c.ToDefaultName();
}
// Otherwise all is normal, get the menu name or if it does not work the default name.
- var textRow = _lobbySheet.GetRow(menu.Value.Id);
- return string.Intern(textRow?.Text.ToDalamudString().ToString() ?? c.ToDefaultName());
+ return string.Intern(_lobbySheet.TryGetRow(menu.Value.Menu.RowId, out var textRow)
+ ? textRow.Text.ExtractText()
+ : c.ToDefaultName());
}).ToArray();
// Add names for both eye colors.
@@ -306,7 +291,7 @@ internal class CustomizeSetFactory(
}
/// Get the manu types for all available options.
- private static CharaMakeParams.MenuType[] GetMenuTypes(CharaMakeParams row)
+ private static MenuType[] GetMenuTypes(CharaMakeType row)
{
// Set up the menu types for all customizations.
return Enum.GetValues().Select(c =>
@@ -318,13 +303,13 @@ internal class CustomizeSetFactory(
case CustomizeIndex.EyeColorLeft:
case CustomizeIndex.EyeColorRight:
case CustomizeIndex.FacePaintColor:
- return CharaMakeParams.MenuType.ColorPicker;
- case CustomizeIndex.BodyType: return CharaMakeParams.MenuType.Nothing;
+ return MenuType.ColorPicker;
+ case CustomizeIndex.BodyType: return MenuType.Nothing;
case CustomizeIndex.FacePaintReversed:
case CustomizeIndex.Highlights:
case CustomizeIndex.SmallIris:
case CustomizeIndex.Lipstick:
- return CharaMakeParams.MenuType.Checkmark;
+ return MenuType.Checkmark;
case CustomizeIndex.FacialFeature1:
case CustomizeIndex.FacialFeature2:
case CustomizeIndex.FacialFeature3:
@@ -333,24 +318,22 @@ internal class CustomizeSetFactory(
case CustomizeIndex.FacialFeature6:
case CustomizeIndex.FacialFeature7:
case CustomizeIndex.LegacyTattoo:
- return CharaMakeParams.MenuType.IconCheckmark;
+ return MenuType.IconCheckmark;
}
var gameId = c.ToByteAndMask().ByteIdx;
// Otherwise find the first menu corresponding to the id.
// If there is none, assume a list.
- var menu = row.Menus
- .Cast()
- .FirstOrDefault(m => m!.Value.Customize == gameId);
- var ret = menu?.Type ?? CharaMakeParams.MenuType.ListSelector;
- if (c is CustomizeIndex.TailShape && ret is CharaMakeParams.MenuType.ListSelector)
- ret = CharaMakeParams.MenuType.List1Selector;
+ var menu = row.CharaMakeStruct.FirstOrNull(m => m.Customize == gameId);
+ var ret = (MenuType)(menu?.SubMenuType ?? (byte)MenuType.ListSelector);
+ if (c is CustomizeIndex.TailShape && ret is MenuType.ListSelector)
+ ret = MenuType.List1Selector;
return ret;
}).ToArray();
}
/// Set the availability of options according to actual availability.
- private static void SetAvailability(CustomizeSet set, CharaMakeParams row)
+ private static void SetAvailability(CustomizeSet set, CharaMakeType row)
{
Set(true, CustomizeIndex.Height);
Set(set.Faces.Count > 0, CustomizeIndex.Face);
@@ -401,7 +384,7 @@ internal class CustomizeSetFactory(
ret[(int)CustomizeIndex.EyeColorRight] = CustomizeIndex.TattooColor;
var dict = ret.Skip(2).Where(set.IsAvailable).GroupBy(set.Type).ToDictionary(k => k.Key, k => k.ToArray());
- foreach (var type in Enum.GetValues())
+ foreach (var type in Enum.GetValues())
dict.TryAdd(type, []);
set.Order = dict;
}
@@ -425,7 +408,7 @@ internal class CustomizeSetFactory(
bool Valid(CustomizeData c)
{
- var data = _customizeSheet.GetRow(c.CustomizeId)?.Unknown6 ?? 0;
+ var data = _customizeSheet.TryGetRow(c.CustomizeId, out var customize) ? customize.Unknown0 : 0;
return data == 0 || data == i + set.Faces.Count;
}
}
@@ -437,7 +420,7 @@ internal class CustomizeSetFactory(
/// Create a list of lists of facial features and the legacy tattoo.
/// Facial Features are bools in a bitfield, so we supply an "off" and an "on" value for simplicity of use.
///
- private static void SetFacialFeatures(CustomizeSet set, CharaMakeParams row)
+ private static void SetFacialFeatures(CustomizeSet set, in CharaMakeType row)
{
var count = set.Faces.Count;
set.FacialFeature1 = new List<(CustomizeData, CustomizeData)>(count);
@@ -446,14 +429,14 @@ internal class CustomizeSetFactory(
var tmp = Enumerable.Repeat(0, 7).Select(_ => new (CustomizeData, CustomizeData)[count + 1]).ToArray();
for (var i = 0; i < count; ++i)
{
- var data = row.FacialFeatureByFace[i].Icons;
- tmp[0][i + 1] = Create(CustomizeIndex.FacialFeature1, data[0]);
- tmp[1][i + 1] = Create(CustomizeIndex.FacialFeature2, data[1]);
- tmp[2][i + 1] = Create(CustomizeIndex.FacialFeature3, data[2]);
- tmp[3][i + 1] = Create(CustomizeIndex.FacialFeature4, data[3]);
- tmp[4][i + 1] = Create(CustomizeIndex.FacialFeature5, data[4]);
- tmp[5][i + 1] = Create(CustomizeIndex.FacialFeature6, data[5]);
- tmp[6][i + 1] = Create(CustomizeIndex.FacialFeature7, data[6]);
+ var data = row.FacialFeatureOption[i];
+ tmp[0][i + 1] = Create(CustomizeIndex.FacialFeature1, (uint)data.Option1);
+ tmp[1][i + 1] = Create(CustomizeIndex.FacialFeature2, (uint)data.Option2);
+ tmp[2][i + 1] = Create(CustomizeIndex.FacialFeature3, (uint)data.Option3);
+ tmp[3][i + 1] = Create(CustomizeIndex.FacialFeature4, (uint)data.Option4);
+ tmp[4][i + 1] = Create(CustomizeIndex.FacialFeature5, (uint)data.Option5);
+ tmp[5][i + 1] = Create(CustomizeIndex.FacialFeature6, (uint)data.Option6);
+ tmp[6][i + 1] = Create(CustomizeIndex.FacialFeature7, (uint)data.Option7);
}
set.FacialFeature1 = tmp[0];
diff --git a/Glamourer/GameData/MenuType.cs b/Glamourer/GameData/MenuType.cs
new file mode 100644
index 0000000..a1d727b
--- /dev/null
+++ b/Glamourer/GameData/MenuType.cs
@@ -0,0 +1,14 @@
+namespace Glamourer.GameData;
+
+public enum MenuType
+{
+ ListSelector = 0,
+ IconSelector = 1,
+ ColorPicker = 2,
+ DoubleColorPicker = 3,
+ IconCheckmark = 4,
+ Percentage = 5,
+ Checkmark = 6, // custom
+ Nothing = 7, // custom
+ List1Selector = 8, // custom, 1-indexed lists
+}
diff --git a/Glamourer/GameData/NpcCustomizeSet.cs b/Glamourer/GameData/NpcCustomizeSet.cs
index 3c683a5..72ed4b4 100644
--- a/Glamourer/GameData/NpcCustomizeSet.cs
+++ b/Glamourer/GameData/NpcCustomizeSet.cs
@@ -1,7 +1,7 @@
using Dalamud.Plugin.Services;
using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using OtterGui.Services;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Structs;
@@ -52,15 +52,14 @@ public class NpcCustomizeSet : IAsyncDataContainer, IReadOnlyList
/// Create data from event NPCs.
private static List CreateEnpcData(IDataManager data, DictENpc eNpcs)
{
- var enpcSheet = data.GetExcelSheet()!;
+ var enpcSheet = data.GetExcelSheet();
var list = new List(eNpcs.Count);
// Go through all event NPCs already collected into a dictionary.
foreach (var (id, name) in eNpcs)
{
- var row = enpcSheet.GetRow(id.Id);
// We only accept NPCs with valid names.
- if (row == null || name.IsNullOrWhitespace())
+ if (!enpcSheet.TryGetRow(id.Id, out var row) || name.IsNullOrWhitespace())
continue;
// Check if the customization is a valid human.
@@ -72,14 +71,14 @@ public class NpcCustomizeSet : IAsyncDataContainer, IReadOnlyList
{
Name = name,
Customize = customize,
- ModelId = row.ModelChara.Row,
+ ModelId = row.ModelChara.RowId,
Id = id,
Kind = ObjectKind.EventNpc,
};
// Event NPCs have a reference to NpcEquip but also contain the appearance in their own row.
// Prefer the NpcEquip reference if it is set and the own does not appear to be set, otherwise use the own.
- if (row.NpcEquip.Row != 0 && row.NpcEquip.Value is { } equip && row is { ModelBody: 0, ModelLegs: 0 })
+ if (row.NpcEquip.RowId != 0 && row.NpcEquip.Value is { } equip && row is { ModelBody: 0, ModelLegs: 0 })
ApplyNpcEquip(ref ret, equip);
else
ApplyNpcEquip(ref ret, row);
@@ -93,14 +92,14 @@ public class NpcCustomizeSet : IAsyncDataContainer, IReadOnlyList
/// Create data from battle NPCs.
private static List CreateBnpcData(IDataManager data, DictBNpc bNpcs, DictBNpcNames bNpcNames)
{
- var bnpcSheet = data.GetExcelSheet()!;
- var list = new List((int)bnpcSheet.RowCount);
+ var bnpcSheet = data.GetExcelSheet();
+ var list = new List(bnpcSheet.Count);
// We go through all battle NPCs in the sheet because the dictionary refers to names.
foreach (var baseRow in bnpcSheet)
{
// Only accept humans.
- if (baseRow.ModelChara.Value!.Type != 1)
+ if (baseRow.ModelChara.Value.Type != 1)
continue;
var bnpcNameIds = bNpcNames[baseRow.RowId];
@@ -109,15 +108,15 @@ public class NpcCustomizeSet : IAsyncDataContainer, IReadOnlyList
continue;
// Check if the customization is a valid human.
- var (valid, customize) = FromBnpcCustomize(baseRow.BNpcCustomize.Value!);
+ var (valid, customize) = FromBnpcCustomize(baseRow.BNpcCustomize.Value);
if (!valid)
continue;
- var equip = baseRow.NpcEquip.Value!;
+ var equip = baseRow.NpcEquip.Value;
var ret = new NpcData
{
Customize = customize,
- ModelId = baseRow.ModelChara.Row,
+ ModelId = baseRow.ModelChara.RowId,
Id = baseRow.RowId,
Kind = ObjectKind.BattleNpc,
};
@@ -186,36 +185,36 @@ public class NpcCustomizeSet : IAsyncDataContainer, IReadOnlyList
/// Apply equipment from a NpcEquip row.
private static void ApplyNpcEquip(ref NpcData data, NpcEquip row)
{
- data.Set(0, row.ModelHead | (row.DyeHead.Row << 24) | ((ulong)row.Dye2Head.Row << 32));
- data.Set(1, row.ModelBody | (row.DyeBody.Row << 24) | ((ulong)row.Dye2Body.Row << 32));
- data.Set(2, row.ModelHands | (row.DyeHands.Row << 24) | ((ulong)row.Dye2Hands.Row << 32));
- data.Set(3, row.ModelLegs | (row.DyeLegs.Row << 24) | ((ulong)row.Dye2Legs.Row << 32));
- data.Set(4, row.ModelFeet | (row.DyeFeet.Row << 24) | ((ulong)row.Dye2Feet.Row << 32));
- data.Set(5, row.ModelEars | (row.DyeEars.Row << 24) | ((ulong)row.Dye2Ears.Row << 32));
- data.Set(6, row.ModelNeck | (row.DyeNeck.Row << 24) | ((ulong)row.Dye2Neck.Row << 32));
- data.Set(7, row.ModelWrists | (row.DyeWrists.Row << 24) | ((ulong)row.Dye2Wrists.Row << 32));
- data.Set(8, row.ModelRightRing | (row.DyeRightRing.Row << 24) | ((ulong)row.Dye2RightRing.Row << 32));
- data.Set(9, row.ModelLeftRing | (row.DyeLeftRing.Row << 24) | ((ulong)row.Dye2LeftRing.Row << 32));
- data.Mainhand = new CharacterWeapon(row.ModelMainHand | ((ulong)row.DyeMainHand.Row << 48) | ((ulong)row.Dye2MainHand.Row << 56));
- data.Offhand = new CharacterWeapon(row.ModelOffHand | ((ulong)row.DyeOffHand.Row << 48) | ((ulong)row.Dye2OffHand.Row << 56));
+ data.Set(0, row.ModelHead | (row.DyeHead.RowId << 24) | ((ulong)row.Dye2Head.RowId << 32));
+ data.Set(1, row.ModelBody | (row.DyeBody.RowId << 24) | ((ulong)row.Dye2Body.RowId << 32));
+ data.Set(2, row.ModelHands | (row.DyeHands.RowId << 24) | ((ulong)row.Dye2Hands.RowId << 32));
+ data.Set(3, row.ModelLegs | (row.DyeLegs.RowId << 24) | ((ulong)row.Dye2Legs.RowId << 32));
+ data.Set(4, row.ModelFeet | (row.DyeFeet.RowId << 24) | ((ulong)row.Dye2Feet.RowId << 32));
+ data.Set(5, row.ModelEars | (row.DyeEars.RowId << 24) | ((ulong)row.Dye2Ears.RowId << 32));
+ data.Set(6, row.ModelNeck | (row.DyeNeck.RowId << 24) | ((ulong)row.Dye2Neck.RowId << 32));
+ data.Set(7, row.ModelWrists | (row.DyeWrists.RowId << 24) | ((ulong)row.Dye2Wrists.RowId << 32));
+ data.Set(8, row.ModelRightRing | (row.DyeRightRing.RowId << 24) | ((ulong)row.Dye2RightRing.RowId << 32));
+ data.Set(9, row.ModelLeftRing | (row.DyeLeftRing.RowId << 24) | ((ulong)row.Dye2LeftRing.RowId << 32));
+ data.Mainhand = new CharacterWeapon(row.ModelMainHand | ((ulong)row.DyeMainHand.RowId << 48) | ((ulong)row.Dye2MainHand.RowId << 56));
+ data.Offhand = new CharacterWeapon(row.ModelOffHand | ((ulong)row.DyeOffHand.RowId << 48) | ((ulong)row.Dye2OffHand.RowId << 56));
data.VisorToggled = row.Visor;
}
/// Apply equipment from a ENpcBase Row row.
private static void ApplyNpcEquip(ref NpcData data, ENpcBase row)
{
- data.Set(0, row.ModelHead | (row.DyeHead.Row << 24) | ((ulong)row.Dye2Head.Row << 32));
- data.Set(1, row.ModelBody | (row.DyeBody.Row << 24) | ((ulong)row.Dye2Body.Row << 32));
- data.Set(2, row.ModelHands | (row.DyeHands.Row << 24) | ((ulong)row.Dye2Hands.Row << 32));
- data.Set(3, row.ModelLegs | (row.DyeLegs.Row << 24) | ((ulong)row.Dye2Legs.Row << 32));
- data.Set(4, row.ModelFeet | (row.DyeFeet.Row << 24) | ((ulong)row.Dye2Feet.Row << 32));
- data.Set(5, row.ModelEars | (row.DyeEars.Row << 24) | ((ulong)row.Dye2Ears.Row << 32));
- data.Set(6, row.ModelNeck | (row.DyeNeck.Row << 24) | ((ulong)row.Dye2Neck.Row << 32));
- data.Set(7, row.ModelWrists | (row.DyeWrists.Row << 24) | ((ulong)row.Dye2Wrists.Row << 32));
- data.Set(8, row.ModelRightRing | (row.DyeRightRing.Row << 24) | ((ulong)row.Dye2RightRing.Row << 32));
- data.Set(9, row.ModelLeftRing | (row.DyeLeftRing.Row << 24) | ((ulong)row.Dye2LeftRing.Row << 32));
- data.Mainhand = new CharacterWeapon(row.ModelMainHand | ((ulong)row.DyeMainHand.Row << 48) | ((ulong)row.Dye2MainHand.Row << 56));
- data.Offhand = new CharacterWeapon(row.ModelOffHand | ((ulong)row.DyeOffHand.Row << 48) | ((ulong)row.Dye2OffHand.Row << 56));
+ data.Set(0, row.ModelHead | (row.DyeHead.RowId << 24) | ((ulong)row.Dye2Head.RowId << 32));
+ data.Set(1, row.ModelBody | (row.DyeBody.RowId << 24) | ((ulong)row.Dye2Body.RowId << 32));
+ data.Set(2, row.ModelHands | (row.DyeHands.RowId << 24) | ((ulong)row.Dye2Hands.RowId << 32));
+ data.Set(3, row.ModelLegs | (row.DyeLegs.RowId << 24) | ((ulong)row.Dye2Legs.RowId << 32));
+ data.Set(4, row.ModelFeet | (row.DyeFeet.RowId << 24) | ((ulong)row.Dye2Feet.RowId << 32));
+ data.Set(5, row.ModelEars | (row.DyeEars.RowId << 24) | ((ulong)row.Dye2Ears.RowId << 32));
+ data.Set(6, row.ModelNeck | (row.DyeNeck.RowId << 24) | ((ulong)row.Dye2Neck.RowId << 32));
+ data.Set(7, row.ModelWrists | (row.DyeWrists.RowId << 24) | ((ulong)row.Dye2Wrists.RowId << 32));
+ data.Set(8, row.ModelRightRing | (row.DyeRightRing.RowId << 24) | ((ulong)row.Dye2RightRing.RowId << 32));
+ data.Set(9, row.ModelLeftRing | (row.DyeLeftRing.RowId << 24) | ((ulong)row.Dye2LeftRing.RowId << 32));
+ data.Mainhand = new CharacterWeapon(row.ModelMainHand | ((ulong)row.DyeMainHand.RowId << 48) | ((ulong)row.Dye2MainHand.RowId << 56));
+ data.Offhand = new CharacterWeapon(row.ModelOffHand | ((ulong)row.DyeOffHand.RowId << 48) | ((ulong)row.Dye2OffHand.RowId << 56));
data.VisorToggled = row.Visor;
}
@@ -223,11 +222,11 @@ public class NpcCustomizeSet : IAsyncDataContainer, IReadOnlyList
private static (bool, CustomizeArray) FromBnpcCustomize(BNpcCustomize bnpcCustomize)
{
var customize = new CustomizeArray();
- customize.SetByIndex(0, (CustomizeValue)(byte)bnpcCustomize.Race.Row);
+ customize.SetByIndex(0, (CustomizeValue)(byte)bnpcCustomize.Race.RowId);
customize.SetByIndex(1, (CustomizeValue)bnpcCustomize.Gender);
customize.SetByIndex(2, (CustomizeValue)bnpcCustomize.BodyType);
customize.SetByIndex(3, (CustomizeValue)bnpcCustomize.Height);
- customize.SetByIndex(4, (CustomizeValue)(byte)bnpcCustomize.Tribe.Row);
+ customize.SetByIndex(4, (CustomizeValue)(byte)bnpcCustomize.Tribe.RowId);
customize.SetByIndex(5, (CustomizeValue)bnpcCustomize.Face);
customize.SetByIndex(6, (CustomizeValue)bnpcCustomize.HairStyle);
customize.SetByIndex(7, (CustomizeValue)bnpcCustomize.HairHighlight);
@@ -261,15 +260,15 @@ public class NpcCustomizeSet : IAsyncDataContainer, IReadOnlyList
/// Obtain customizations from a ENpcBase row and check if the human is valid.
private static (bool, CustomizeArray) FromEnpcBase(ENpcBase enpcBase)
{
- if (enpcBase.ModelChara.Value?.Type != 1)
+ if (enpcBase.ModelChara.ValueNullable?.Type != 1)
return (false, CustomizeArray.Default);
var customize = new CustomizeArray();
- customize.SetByIndex(0, (CustomizeValue)(byte)enpcBase.Race.Row);
+ customize.SetByIndex(0, (CustomizeValue)(byte)enpcBase.Race.RowId);
customize.SetByIndex(1, (CustomizeValue)enpcBase.Gender);
customize.SetByIndex(2, (CustomizeValue)enpcBase.BodyType);
customize.SetByIndex(3, (CustomizeValue)enpcBase.Height);
- customize.SetByIndex(4, (CustomizeValue)(byte)enpcBase.Tribe.Row);
+ customize.SetByIndex(4, (CustomizeValue)(byte)enpcBase.Tribe.RowId);
customize.SetByIndex(5, (CustomizeValue)enpcBase.Face);
customize.SetByIndex(6, (CustomizeValue)enpcBase.HairStyle);
customize.SetByIndex(7, (CustomizeValue)enpcBase.HairHighlight);
diff --git a/Glamourer/Glamourer.json b/Glamourer/Glamourer.json
index 08c18f5..1e9edf7 100644
--- a/Glamourer/Glamourer.json
+++ b/Glamourer/Glamourer.json
@@ -8,7 +8,7 @@
"AssemblyVersion": "9.0.0.1",
"RepoUrl": "https://github.com/Ottermandias/Glamourer",
"ApplicableVersion": "any",
- "DalamudApiLevel": 10,
+ "DalamudApiLevel": 11,
"ImageUrls": null,
"IconUrl": "https://raw.githubusercontent.com/Ottermandias/Glamourer/master/images/icon.png"
}
\ No newline at end of file
diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.Icon.cs b/Glamourer/Gui/Customization/CustomizationDrawer.Icon.cs
index baedc05..486fdb4 100644
--- a/Glamourer/Gui/Customization/CustomizationDrawer.Icon.cs
+++ b/Glamourer/Gui/Customization/CustomizationDrawer.Icon.cs
@@ -193,7 +193,7 @@ public partial class CustomizationDrawer
private void DrawMultiIcons()
{
- var options = _set.Order[CharaMakeParams.MenuType.IconCheckmark];
+ var options = _set.Order[MenuType.IconCheckmark];
using var group = ImRaii.Group();
var face = _set.DataByValue(CustomizeIndex.Face, _customize.Face, out _, _customize.Face) < 0 ? _set.Faces[0].Value : _customize.Face;
foreach (var (featureIdx, idx) in options.WithIndex())
diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.cs b/Glamourer/Gui/Customization/CustomizationDrawer.cs
index 6b6cc0a..4ec6146 100644
--- a/Glamourer/Gui/Customization/CustomizationDrawer.cs
+++ b/Glamourer/Gui/Customization/CustomizationDrawer.cs
@@ -121,22 +121,22 @@ public partial class CustomizationDrawer(
_set = _service.Manager.GetSet(_customize.Clan, _customize.Gender);
- foreach (var id in _set.Order[CharaMakeParams.MenuType.Percentage])
+ foreach (var id in _set.Order[MenuType.Percentage])
PercentageSelector(id);
- Functions.IteratePairwise(_set.Order[CharaMakeParams.MenuType.IconSelector], DrawIconSelector, ImGui.SameLine);
+ Functions.IteratePairwise(_set.Order[MenuType.IconSelector], DrawIconSelector, ImGui.SameLine);
DrawMultiIconSelector();
- foreach (var id in _set.Order[CharaMakeParams.MenuType.ListSelector])
+ foreach (var id in _set.Order[MenuType.ListSelector])
DrawListSelector(id, false);
- foreach (var id in _set.Order[CharaMakeParams.MenuType.List1Selector])
+ foreach (var id in _set.Order[MenuType.List1Selector])
DrawListSelector(id, true);
- Functions.IteratePairwise(_set.Order[CharaMakeParams.MenuType.ColorPicker], DrawColorPicker, ImGui.SameLine);
+ Functions.IteratePairwise(_set.Order[MenuType.ColorPicker], DrawColorPicker, ImGui.SameLine);
- Functions.IteratePairwise(_set.Order[CharaMakeParams.MenuType.Checkmark], DrawCheckbox,
+ Functions.IteratePairwise(_set.Order[MenuType.Checkmark], DrawCheckbox,
() => ImGui.SameLine(_comboSelectorSize - _framedIconSize.X + ImGui.GetStyle().WindowPadding.X));
return Changed != 0 || ChangeApply != _initialApply;
}
diff --git a/Glamourer/Gui/Equipment/BonusItemCombo.cs b/Glamourer/Gui/Equipment/BonusItemCombo.cs
index 735a23f..c333a87 100644
--- a/Glamourer/Gui/Equipment/BonusItemCombo.cs
+++ b/Glamourer/Gui/Equipment/BonusItemCombo.cs
@@ -2,7 +2,7 @@
using Glamourer.Services;
using Glamourer.Unlocks;
using ImGuiNET;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Log;
@@ -91,8 +91,8 @@ public sealed class BonusItemCombo : FilterComboCache
return slot switch
{
- BonusItemFlag.Glasses => sheet.GetRow(16050)?.Text.ToString() ?? "Facewear",
- BonusItemFlag.UnkSlot => sheet.GetRow(16051)?.Text.ToString() ?? "Facewear",
+ BonusItemFlag.Glasses => sheet.TryGetRow(16050, out var text) ? text.Text.ToString() : "Facewear",
+ BonusItemFlag.UnkSlot => sheet.TryGetRow(16051, out var text) ? text.Text.ToString() : "Facewear",
_ => string.Empty,
};
diff --git a/Glamourer/Gui/Equipment/ItemCombo.cs b/Glamourer/Gui/Equipment/ItemCombo.cs
index 1dac468..f7c75e1 100644
--- a/Glamourer/Gui/Equipment/ItemCombo.cs
+++ b/Glamourer/Gui/Equipment/ItemCombo.cs
@@ -2,7 +2,7 @@
using Glamourer.Services;
using Glamourer.Unlocks;
using ImGuiNET;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Log;
@@ -88,20 +88,20 @@ public sealed class ItemCombo : FilterComboCache
private static string GetLabel(IDataManager gameData, EquipSlot slot)
{
- var sheet = gameData.GetExcelSheet()!;
+ var sheet = gameData.GetExcelSheet();
return slot switch
{
- EquipSlot.Head => sheet.GetRow(740)?.Text.ToString() ?? "Head",
- EquipSlot.Body => sheet.GetRow(741)?.Text.ToString() ?? "Body",
- EquipSlot.Hands => sheet.GetRow(742)?.Text.ToString() ?? "Hands",
- EquipSlot.Legs => sheet.GetRow(744)?.Text.ToString() ?? "Legs",
- EquipSlot.Feet => sheet.GetRow(745)?.Text.ToString() ?? "Feet",
- EquipSlot.Ears => sheet.GetRow(746)?.Text.ToString() ?? "Ears",
- EquipSlot.Neck => sheet.GetRow(747)?.Text.ToString() ?? "Neck",
- EquipSlot.Wrists => sheet.GetRow(748)?.Text.ToString() ?? "Wrists",
- EquipSlot.RFinger => sheet.GetRow(749)?.Text.ToString() ?? "Right Ring",
- EquipSlot.LFinger => sheet.GetRow(750)?.Text.ToString() ?? "Left Ring",
+ EquipSlot.Head => sheet.TryGetRow(740, out var text) ? text.Text.ToString() : "Head",
+ EquipSlot.Body => sheet.TryGetRow(741, out var text) ? text.Text.ToString() : "Body",
+ EquipSlot.Hands => sheet.TryGetRow(742, out var text) ? text.Text.ToString() : "Hands",
+ EquipSlot.Legs => sheet.TryGetRow(744, out var text) ? text.Text.ToString() : "Legs",
+ EquipSlot.Feet => sheet.TryGetRow(745, out var text) ? text.Text.ToString() : "Feet",
+ EquipSlot.Ears => sheet.TryGetRow(746, out var text) ? text.Text.ToString() : "Ears",
+ EquipSlot.Neck => sheet.TryGetRow(747, out var text) ? text.Text.ToString() : "Neck",
+ EquipSlot.Wrists => sheet.TryGetRow(748, out var text) ? text.Text.ToString() : "Wrists",
+ EquipSlot.RFinger => sheet.TryGetRow(749, out var text) ? text.Text.ToString() : "Right Ring",
+ EquipSlot.LFinger => sheet.TryGetRow(750, out var text) ? text.Text.ToString() : "Left Ring",
_ => string.Empty,
};
}
diff --git a/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs b/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs
index efe2448..afc4f7b 100644
--- a/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs
+++ b/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs
@@ -40,7 +40,7 @@ public class UnlockOverview(
foreach (var type in Enum.GetValues())
{
- if (type.IsOffhandType() || !items.ItemData.ByType.TryGetValue(type, out var value) || value.Count == 0)
+ if (type.IsOffhandType() || type.IsBonus() || !items.ItemData.ByType.TryGetValue(type, out var value) || value.Count == 0)
continue;
if (ImGui.Selectable(type.ToName(), _selected1 == type))
diff --git a/Glamourer/Interop/CrestService.cs b/Glamourer/Interop/CrestService.cs
index 2b6f1ac..8e217b6 100644
--- a/Glamourer/Interop/CrestService.cs
+++ b/Glamourer/Interop/CrestService.cs
@@ -47,7 +47,7 @@ public sealed unsafe class CrestService : EventWrapperRef3DrawData, (byte) flags);
gameObject.CrestBitfield = currentCrests;
}
@@ -62,14 +62,14 @@ public sealed unsafe class CrestService : EventWrapperRef3 _crestChangeHook = null!;
- private void CrestChangeDetour(Character* character, byte crestFlags)
+ private void CrestChangeDetour(DrawDataContainer* container, byte crestFlags)
{
- var actor = (Actor)character;
+ var actor = (Actor)container->OwnerObject;
foreach (var slot in CrestExtensions.AllRelevantSet)
{
var newValue = ((CrestFlag)crestFlags).HasFlag(slot);
@@ -78,9 +78,9 @@ public sealed unsafe class CrestService : EventWrapperRef3((nint)MountContainer.MemberFunctionPointers.SetupMount, SetupMountDetour);
- _setupOrnamentHook = interop.HookFromAddress((nint)Ornament.MemberFunctionPointers.SetupOrnament, SetupOrnamentDetour);
_calculateHeightHook =
- interop.HookFromAddress((nint)Character.MemberFunctionPointers.CalculateHeight, CalculateHeightDetour);
+ interop.HookFromAddress((nint)HeightContainer.MemberFunctionPointers.CalculateHeight, CalculateHeightDetour);
_setupMountHook.Enable();
- _setupOrnamentHook.Enable();
+ _updateOrnamentHook.Enable();
_placeMinionHook.Enable();
_calculateHeightHook.Enable();
}
@@ -29,19 +30,21 @@ public unsafe class ScalingService : IDisposable
public void Dispose()
{
_setupMountHook.Dispose();
- _setupOrnamentHook.Dispose();
+ _updateOrnamentHook.Dispose();
_placeMinionHook.Dispose();
_calculateHeightHook.Dispose();
}
private delegate void SetupMount(MountContainer* container, short mountId, uint unk1, uint unk2, uint unk3, byte unk4);
- private delegate void SetupOrnament(Ornament* ornament, uint* unk1, float* unk2);
+ private delegate void UpdateOrnament(OrnamentContainer* ornament);
private delegate void PlaceMinion(Companion* character);
private delegate float CalculateHeight(Character* character);
private readonly Hook _setupMountHook;
- private readonly Hook _setupOrnamentHook;
+ // TODO: Use client structs sig.
+ [Signature(Sigs.UpdateOrnament, DetourName = nameof(UpdateOrnamentDetour))]
+ private readonly Hook _updateOrnamentHook = null!;
private readonly Hook _calculateHeightHook;
@@ -57,19 +60,12 @@ public unsafe class ScalingService : IDisposable
SetScaleCustomize(container->OwnerObject, race, clan, gender);
}
- private void SetupOrnamentDetour(Ornament* ornament, uint* unk1, float* unk2)
+ private void UpdateOrnamentDetour(OrnamentContainer* container)
{
- var character = ornament->GetParentCharacter();
- if (character == null)
- {
- _setupOrnamentHook.Original(ornament, unk1, unk2);
- return;
- }
-
- var (race, clan, gender) = GetScaleRelevantCustomize(character);
- SetScaleCustomize(character, character->GameObject.DrawObject);
- _setupOrnamentHook.Original(ornament, unk1, unk2);
- SetScaleCustomize(character, race, clan, gender);
+ var (race, clan, gender) = GetScaleRelevantCustomize(container->OwnerObject);
+ SetScaleCustomize(container->OwnerObject, container->OwnerObject->DrawObject);
+ _updateOrnamentHook.Original(container);
+ SetScaleCustomize(container->OwnerObject, race, clan, gender);
}
private void PlaceMinionDetour(Companion* companion)
diff --git a/Glamourer/Services/CommandService.cs b/Glamourer/Services/CommandService.cs
index b18c817..6fb32e2 100644
--- a/Glamourer/Services/CommandService.cs
+++ b/Glamourer/Services/CommandService.cs
@@ -548,7 +548,7 @@ public class CommandService : IDisposable, IApiService
if (baseValue != null)
{
var v = baseValue.Value;
- if (set.Type(customizeIndex) is CharaMakeParams.MenuType.ListSelector)
+ if (set.Type(customizeIndex) is MenuType.ListSelector)
--v;
set.DataByValue(customizeIndex, new CustomizeValue(v), out var data, customize.Face);
if (data != null)
diff --git a/Glamourer/Services/ItemManager.cs b/Glamourer/Services/ItemManager.cs
index 07a4829..5d6f074 100644
--- a/Glamourer/Services/ItemManager.cs
+++ b/Glamourer/Services/ItemManager.cs
@@ -1,5 +1,6 @@
using Dalamud.Plugin.Services;
using Lumina.Excel;
+using Lumina.Excel.Sheets;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
@@ -16,12 +17,12 @@ public class ItemManager
private readonly Configuration _config;
- public readonly ObjectIdentification ObjectIdentification;
- public readonly ExcelSheet ItemSheet;
- public readonly DictStain Stains;
- public readonly ItemData ItemData;
- public readonly DictBonusItems DictBonusItems;
- public readonly RestrictedGear RestrictedGear;
+ public readonly ObjectIdentification ObjectIdentification;
+ public readonly ExcelSheet- ItemSheet;
+ public readonly DictStain Stains;
+ public readonly ItemData ItemData;
+ public readonly DictBonusItems DictBonusItems;
+ public readonly RestrictedGear RestrictedGear;
public readonly EquipItem DefaultSword;
@@ -29,13 +30,13 @@ public class ItemManager
ItemData itemData, DictStain stains, RestrictedGear restrictedGear, DictBonusItems dictBonusItems)
{
_config = config;
- ItemSheet = gameData.GetExcelSheet()!;
+ ItemSheet = gameData.GetExcelSheet
- ();
ObjectIdentification = objectIdentification;
ItemData = itemData;
Stains = stains;
RestrictedGear = restrictedGear;
DictBonusItems = dictBonusItems;
- DefaultSword = EquipItem.FromMainhand(ItemSheet.GetRow(1601)!); // Weathered Shortsword
+ DefaultSword = EquipItem.FromMainhand(ItemSheet.GetRow(1601)); // Weathered Shortsword
}
public (bool, CharacterArmor) ResolveRestrictedGear(CharacterArmor armor, EquipSlot slot, Race race, Gender gender)
diff --git a/Glamourer/Unlocks/CustomizeUnlockManager.cs b/Glamourer/Unlocks/CustomizeUnlockManager.cs
index 801f211..18f3cac 100644
--- a/Glamourer/Unlocks/CustomizeUnlockManager.cs
+++ b/Glamourer/Unlocks/CustomizeUnlockManager.cs
@@ -8,7 +8,7 @@ using Glamourer.GameData;
using Glamourer.Events;
using Glamourer.Interop;
using Glamourer.Services;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using Penumbra.GameData;
using Penumbra.GameData.Enums;
@@ -183,25 +183,25 @@ public class CustomizeUnlockManager : IDisposable, ISavable
var list = customizations.Manager.GetSet(clan, gender);
foreach (var hair in list.HairStyles)
{
- var x = sheet.FirstOrDefault(f => f.FeatureID == hair.Value.Value);
+ var x = sheet.FirstOrNull(f => f.FeatureID == hair.Value.Value);
if (x?.IsPurchasable == true)
{
- var name = x.FeatureID == 61
+ var name = x.Value.FeatureID == 61
? "Eternal Bond"
- : x.HintItem.Value?.Name.ToDalamudString().ToString().Replace("Modern Aesthetics - ", string.Empty)
+ : x.Value.HintItem.ValueNullable?.Name.ExtractText().Replace("Modern Aesthetics - ", string.Empty)
?? string.Empty;
- ret.TryAdd(hair, (x.Data, name));
+ ret.TryAdd(hair, (x.Value.Data, name));
}
}
foreach (var paint in list.FacePaints)
{
- var x = sheet.FirstOrDefault(f => f.FeatureID == paint.Value.Value);
+ var x = sheet.FirstOrNull(f => f.FeatureID == paint.Value.Value);
if (x?.IsPurchasable == true)
{
- var name = x.HintItem.Value?.Name.ToDalamudString().ToString().Replace("Modern Cosmetics - ", string.Empty)
+ var name = x.Value.HintItem.ValueNullable?.Name.ExtractText().Replace("Modern Cosmetics - ", string.Empty)
?? string.Empty;
- ret.TryAdd(paint, (x.Data, name));
+ ret.TryAdd(paint, (x.Value.Data, name));
}
}
}
diff --git a/Glamourer/Unlocks/ItemUnlockManager.cs b/Glamourer/Unlocks/ItemUnlockManager.cs
index 0b95b94..0fc1675 100644
--- a/Glamourer/Unlocks/ItemUnlockManager.cs
+++ b/Glamourer/Unlocks/ItemUnlockManager.cs
@@ -3,11 +3,11 @@ using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using Glamourer.Events;
using Glamourer.Services;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.Sheets;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
-using Cabinet = Lumina.Excel.GeneratedSheets.Cabinet;
+using Cabinet = Lumina.Excel.Sheets.Cabinet;
namespace Glamourer.Unlocks;
@@ -192,7 +192,7 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary
- = _items.ItemSheet.RowCount)
+ if (itemId.Id >= (uint) _items.ItemSheet.Count)
{
time = DateTimeOffset.MinValue;
return true;
@@ -273,32 +273,31 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary
- CreateUnlockData(IDataManager gameData, ItemManager items)
{
var ret = new Dictionary();
- var cabinet = gameData.GetExcelSheet()!;
+ var cabinet = gameData.GetExcelSheet();
foreach (var row in cabinet)
{
- if (items.ItemData.TryGetValue(row.Item.Row, EquipSlot.MainHand, out var item))
+ if (items.ItemData.TryGetValue(row.Item.RowId, EquipSlot.MainHand, out var item))
ret.TryAdd(item.ItemId, new UnlockRequirements(row.RowId, 0, 0, 0, UnlockType.Cabinet));
}
- var gilShopItem = gameData.GetExcelSheet()!;
- var gilShop = gameData.GetExcelSheet()!;
- foreach (var row in gilShopItem)
+ var gilShopItem = gameData.GetSubrowExcelSheet();
+ var gilShop = gameData.GetExcelSheet();
+ foreach (var row in gilShopItem.SelectMany(g => g))
{
- if (!items.ItemData.TryGetValue(row.Item.Row, EquipSlot.MainHand, out var item))
+ if (!items.ItemData.TryGetValue(row.Item.RowId, EquipSlot.MainHand, out var item))
continue;
- var quest1 = row.QuestRequired[0].Row;
- var quest2 = row.QuestRequired[1].Row;
- var achievement = row.AchievementRequired.Row;
+ var quest1 = row.QuestRequired[0].RowId;
+ var quest2 = row.QuestRequired[1].RowId;
+ var achievement = row.AchievementRequired.RowId;
var state = row.StateRequired;
- var shop = gilShop.GetRow(row.RowId);
- if (shop != null && shop.Quest.Row != 0)
+ if (gilShop.TryGetRow(row.RowId, out var shop) && shop.Quest.RowId != 0)
{
if (quest1 == 0)
- quest1 = shop.Quest.Row;
+ quest1 = shop.Quest.RowId;
else if (quest2 == 0)
- quest2 = shop.Quest.Row;
+ quest2 = shop.Quest.RowId;
}
var type = (quest1 != 0 ? UnlockType.Quest1 : 0)
diff --git a/OtterGui b/OtterGui
index d9486ae..8ba88ef 160000
--- a/OtterGui
+++ b/OtterGui
@@ -1 +1 @@
-Subproject commit d9486ae54b5a4b61cf74f79ed27daa659eb1ce5b
+Subproject commit 8ba88eff15326bb28ed5e6157f5252c114d40b5f
diff --git a/Penumbra.GameData b/Penumbra.GameData
index 77d52b0..fb81a0b 160000
--- a/Penumbra.GameData
+++ b/Penumbra.GameData
@@ -1 +1 @@
-Subproject commit 77d52b02d21e770b30c08f89bdf06e0cb75562f7
+Subproject commit fb81a0b55d3c68f2b26357fac3049c79fb0c22fb
diff --git a/repo.json b/repo.json
index b309240..067cd7e 100644
--- a/repo.json
+++ b/repo.json
@@ -22,7 +22,7 @@
"RepoUrl": "https://github.com/Ottermandias/Glamourer",
"ApplicableVersion": "any",
"DalamudApiLevel": 10,
- "TestingDalamudApiLevel": 10,
+ "TestingDalamudApiLevel": 11,
"IsHide": "False",
"IsTestingExclusive": "False",
"DownloadCount": 1,