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,