mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 18:27:24 +01:00
Current State.
This commit is contained in:
parent
a5998b84ba
commit
2ce8076e9a
20 changed files with 226 additions and 359 deletions
|
|
@ -1,126 +0,0 @@
|
|||
using Lumina.Data;
|
||||
using Lumina.Excel;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
|
||||
namespace Glamourer.GameData;
|
||||
|
||||
/// <summary> A custom version of CharaMakeParams that is easier to parse. </summary>
|
||||
[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> Race { get; set; } = null!;
|
||||
public LazyRow<Tribe> 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<Race>(gameData, parser.ReadColumn<uint>(0), language);
|
||||
Tribe = new LazyRow<Tribe>(gameData, parser.ReadColumn<uint>(1), language);
|
||||
Gender = parser.ReadColumn<sbyte>(2);
|
||||
int currentOffset;
|
||||
for (var i = 0; i < NumMenus; ++i)
|
||||
{
|
||||
currentOffset = 3 + i;
|
||||
Menus[i].Id = parser.ReadColumn<uint>(0 * NumMenus + currentOffset);
|
||||
Menus[i].InitVal = parser.ReadColumn<byte>(1 * NumMenus + currentOffset);
|
||||
Menus[i].Type = (MenuType)parser.ReadColumn<byte>(2 * NumMenus + currentOffset);
|
||||
Menus[i].Size = parser.ReadColumn<byte>(3 * NumMenus + currentOffset);
|
||||
Menus[i].LookAt = parser.ReadColumn<byte>(4 * NumMenus + currentOffset);
|
||||
Menus[i].Mask = parser.ReadColumn<uint>(5 * NumMenus + currentOffset);
|
||||
Menus[i].Customize = parser.ReadColumn<uint>(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<uint>(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<byte>(j * NumMenus + currentOffset);
|
||||
}
|
||||
|
||||
currentOffset = 3 + (MaxNumValues + 7 + NumGraphics) * NumMenus;
|
||||
for (var i = 0; i < NumVoices; ++i)
|
||||
Voices[i] = parser.ReadColumn<byte>(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<int>(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<ulong>(currentOffset + 0),
|
||||
Top = parser.ReadColumn<ulong>(currentOffset + 1),
|
||||
Gloves = parser.ReadColumn<ulong>(currentOffset + 2),
|
||||
Legs = parser.ReadColumn<ulong>(currentOffset + 3),
|
||||
Shoes = parser.ReadColumn<ulong>(currentOffset + 4),
|
||||
Weapon = parser.ReadColumn<ulong>(currentOffset + 5),
|
||||
SubWeapon = parser.ReadColumn<ulong>(currentOffset + 6),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<byte> Voices { get; internal init; } = null!;
|
||||
public IReadOnlyList<CharaMakeParams.MenuType> Types { get; internal set; } = null!;
|
||||
public IReadOnlyDictionary<CharaMakeParams.MenuType, CustomizeIndex[]> Order { get; internal set; } = null!;
|
||||
public IReadOnlyList<byte> Voices { get; internal init; } = null!;
|
||||
public IReadOnlyList<MenuType> Types { get; internal set; } = null!;
|
||||
public IReadOnlyDictionary<MenuType, CustomizeIndex[]> 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<CustomizeData> 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,
|
||||
|
|
|
|||
|
|
@ -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(
|
|||
}
|
||||
|
||||
/// <summary> Some data can not be set independently of the rest, so we need a post-processing step to finalize. </summary>
|
||||
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<CharaMakeCustomize> _customizeSheet = _gameData.GetExcelSheet<CharaMakeCustomize>(ClientLanguage.English)!;
|
||||
private readonly ExcelSheet<Lobby> _lobbySheet = _gameData.GetExcelSheet<Lobby>(ClientLanguage.English)!;
|
||||
private readonly ExcelSheet<HairMakeType> _hairSheet = _gameData.GetExcelSheet<HairMakeType>(ClientLanguage.English)!;
|
||||
private readonly ExcelSheet<Tribe> _tribeSheet = _gameData.GetExcelSheet<Tribe>(ClientLanguage.English)!;
|
||||
private readonly ExcelSheet<CharaMakeCustomize> _customizeSheet = _gameData.GetExcelSheet<CharaMakeCustomize>(ClientLanguage.English);
|
||||
private readonly ExcelSheet<Lobby> _lobbySheet = _gameData.GetExcelSheet<Lobby>(ClientLanguage.English);
|
||||
private readonly ExcelSheet<RawRow> _hairSheet = _gameData.GetExcelSheet<RawRow>(ClientLanguage.English, "HairMakeType");
|
||||
private readonly ExcelSheet<Tribe> _tribeSheet = _gameData.GetExcelSheet<Tribe>(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<CharaMakeParams> _charaMakeSheet = _gameData.Excel
|
||||
.GetType()
|
||||
.GetMethod("GetSheet", BindingFlags.Instance | BindingFlags.NonPublic)?
|
||||
.MakeGenericMethod(typeof(CharaMakeParams))
|
||||
.Invoke(_gameData.Excel, ["charamaketype", _gameData.Language.ToLumina(), null])! as ExcelSheet<CharaMakeParams>
|
||||
?? null!;
|
||||
private readonly ExcelSheet<CharaMakeType> _charaMakeSheet = _gameData.Excel.GetSheet<CharaMakeType>();
|
||||
|
||||
/// <summary> Obtain available skin and hair colors for the given clan and gender. </summary>
|
||||
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",
|
||||
};
|
||||
|
||||
/// <summary> Obtain available hairstyles via reflection from the Hair sheet for the given subrace and gender. </summary>
|
||||
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<CustomizeData>(row.Unknown30);
|
||||
var numHairs = row.ReadUInt8Column(30);
|
||||
var hairList = new List<CustomizeData>(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(
|
|||
}
|
||||
|
||||
/// <summary> Specific icons for tails or ears. </summary>
|
||||
private CustomizeData[] GetTailEarShapes(CharaMakeParams row)
|
||||
=> row.Menus.Cast<CharaMakeParams.Menu?>()
|
||||
.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);
|
||||
|
||||
/// <summary> Specific icons for faces. </summary>
|
||||
private CustomizeData[] GetFaces(CharaMakeParams row)
|
||||
=> row.Menus.Cast<CharaMakeParams.Menu?>().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);
|
||||
|
||||
/// <summary> Specific icons for Hrothgar patterns. </summary>
|
||||
private CustomizeData[] HrothgarFurPattern(CharaMakeParams row)
|
||||
=> row.Menus.Cast<CharaMakeParams.Menu?>()
|
||||
.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() ?? [];
|
||||
}
|
||||
|
||||
/// <summary> Get face paints from the hair sheet via reflection since there are also unlockable face paints. </summary>
|
||||
private CustomizeData[] GetFacePaints(SubRace race, Gender gender)
|
||||
{
|
||||
var row = _hairSheet.GetRow(((uint)race - 1) * 2 - 1 + (uint)gender)!;
|
||||
var paintList = new List<CustomizeData>(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<CustomizeData>(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(
|
|||
}
|
||||
|
||||
/// <summary> Get List sizes. </summary>
|
||||
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<CharaMakeParams.Menu?>().FirstOrDefault(m => m!.Value.Customize == gameId);
|
||||
return menu?.Size ?? 0;
|
||||
var menu = row.CharaMakeStruct.FirstOrNull(m => m.Customize == gameId);
|
||||
return menu?.SubMenuNum ?? 0;
|
||||
}
|
||||
|
||||
/// <summary> Get generic Features. </summary>
|
||||
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);
|
||||
|
||||
/// <summary> Create generic color sets from the parameters. </summary>
|
||||
private static CustomizeData[] CreateColors(ColorParameters colorParameters, CustomizeIndex index, int offset, int num,
|
||||
|
|
@ -264,28 +250,27 @@ internal class CustomizeSetFactory(
|
|||
}
|
||||
|
||||
/// <summary> Set the specific option names for the given set of parameters. </summary>
|
||||
private string[] GetOptionNames(CharaMakeParams row)
|
||||
private string[] GetOptionNames(CharaMakeType row)
|
||||
{
|
||||
var nameArray = Enum.GetValues<CustomizeIndex>().Select(c =>
|
||||
{
|
||||
// Find the first menu that corresponds to the Id.
|
||||
var byteId = c.ToByteAndMask().ByteIdx;
|
||||
var menu = row.Menus
|
||||
.Cast<CharaMakeParams.Menu?>()
|
||||
.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(
|
|||
}
|
||||
|
||||
/// <summary> Get the manu types for all available options. </summary>
|
||||
private static CharaMakeParams.MenuType[] GetMenuTypes(CharaMakeParams row)
|
||||
private static MenuType[] GetMenuTypes(CharaMakeType row)
|
||||
{
|
||||
// Set up the menu types for all customizations.
|
||||
return Enum.GetValues<CustomizeIndex>().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<CharaMakeParams.Menu?>()
|
||||
.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();
|
||||
}
|
||||
|
||||
/// <summary> Set the availability of options according to actual availability. </summary>
|
||||
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<CharaMakeParams.MenuType>())
|
||||
foreach (var type in Enum.GetValues<MenuType>())
|
||||
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.
|
||||
/// </summary>
|
||||
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];
|
||||
|
|
|
|||
14
Glamourer/GameData/MenuType.cs
Normal file
14
Glamourer/GameData/MenuType.cs
Normal file
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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<NpcData>
|
|||
/// <summary> Create data from event NPCs. </summary>
|
||||
private static List<NpcData> CreateEnpcData(IDataManager data, DictENpc eNpcs)
|
||||
{
|
||||
var enpcSheet = data.GetExcelSheet<ENpcBase>()!;
|
||||
var enpcSheet = data.GetExcelSheet<ENpcBase>();
|
||||
var list = new List<NpcData>(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<NpcData>
|
|||
{
|
||||
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<NpcData>
|
|||
/// <summary> Create data from battle NPCs. </summary>
|
||||
private static List<NpcData> CreateBnpcData(IDataManager data, DictBNpc bNpcs, DictBNpcNames bNpcNames)
|
||||
{
|
||||
var bnpcSheet = data.GetExcelSheet<BNpcBase>()!;
|
||||
var list = new List<NpcData>((int)bnpcSheet.RowCount);
|
||||
var bnpcSheet = data.GetExcelSheet<BNpcBase>();
|
||||
var list = new List<NpcData>(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<NpcData>
|
|||
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<NpcData>
|
|||
/// <summary> Apply equipment from a NpcEquip row. </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary> Apply equipment from a ENpcBase Row row. </summary>
|
||||
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<NpcData>
|
|||
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<NpcData>
|
|||
/// <summary> Obtain customizations from a ENpcBase row and check if the human is valid. </summary>
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<EquipItem>
|
|||
|
||||
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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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<EquipItem>
|
|||
|
||||
private static string GetLabel(IDataManager gameData, EquipSlot slot)
|
||||
{
|
||||
var sheet = gameData.GetExcelSheet<Addon>()!;
|
||||
var sheet = gameData.GetExcelSheet<Addon>();
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public class UnlockOverview(
|
|||
|
||||
foreach (var type in Enum.GetValues<FullEquipType>())
|
||||
{
|
||||
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))
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ public sealed unsafe class CrestService : EventWrapperRef3<Actor, CrestFlag, boo
|
|||
flags &= CrestExtensions.AllRelevant;
|
||||
var currentCrests = gameObject.CrestBitfield;
|
||||
using var update = _inUpdate.EnterMethod();
|
||||
_crestChangeHook.Original(gameObject.AsCharacter, (byte) flags);
|
||||
_crestChangeHook.Original(&gameObject.AsCharacter->DrawData, (byte) flags);
|
||||
gameObject.CrestBitfield = currentCrests;
|
||||
}
|
||||
|
||||
|
|
@ -62,14 +62,14 @@ public sealed unsafe class CrestService : EventWrapperRef3<Actor, CrestFlag, boo
|
|||
_crestChangeHook.Dispose();
|
||||
}
|
||||
|
||||
private delegate void CrestChangeDelegate(Character* character, byte crestFlags);
|
||||
private delegate void CrestChangeDelegate(DrawDataContainer* container, byte crestFlags);
|
||||
|
||||
[Signature(Sigs.CrestChange, DetourName = nameof(CrestChangeDetour))]
|
||||
private readonly Hook<CrestChangeDelegate> _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<Actor, CrestFlag, boo
|
|||
}
|
||||
|
||||
Glamourer.Log.Verbose(
|
||||
$"Called CrestChange on {(ulong)character:X} with {crestFlags:X} and prior flags {((Actor)character).CrestBitfield}.");
|
||||
$"Called CrestChange on {(ulong)container:X} with {crestFlags:X} and prior flags {actor.CrestBitfield}.");
|
||||
using var _ = _inUpdate.EnterMethod();
|
||||
_crestChangeHook.Original(character, crestFlags);
|
||||
_crestChangeHook.Original(container, crestFlags);
|
||||
}
|
||||
|
||||
public static bool GetModelCrest(Actor gameObject, CrestFlag slot)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ using Dalamud.Utility.Signatures;
|
|||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Penumbra.GameData.Structs;
|
||||
using System.ComponentModel;
|
||||
using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
|
@ -16,12 +18,11 @@ public unsafe class ScalingService : IDisposable
|
|||
interop.InitializeFromAttributes(this);
|
||||
_setupMountHook =
|
||||
interop.HookFromAddress<SetupMount>((nint)MountContainer.MemberFunctionPointers.SetupMount, SetupMountDetour);
|
||||
_setupOrnamentHook = interop.HookFromAddress<SetupOrnament>((nint)Ornament.MemberFunctionPointers.SetupOrnament, SetupOrnamentDetour);
|
||||
_calculateHeightHook =
|
||||
interop.HookFromAddress<CalculateHeight>((nint)Character.MemberFunctionPointers.CalculateHeight, CalculateHeightDetour);
|
||||
interop.HookFromAddress<CalculateHeight>((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<SetupMount> _setupMountHook;
|
||||
|
||||
private readonly Hook<SetupOrnament> _setupOrnamentHook;
|
||||
// TODO: Use client structs sig.
|
||||
[Signature(Sigs.UpdateOrnament, DetourName = nameof(UpdateOrnamentDetour))]
|
||||
private readonly Hook<UpdateOrnament> _updateOrnamentHook = null!;
|
||||
|
||||
private readonly Hook<CalculateHeight> _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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<Lumina.Excel.GeneratedSheets.Item> ItemSheet;
|
||||
public readonly DictStain Stains;
|
||||
public readonly ItemData ItemData;
|
||||
public readonly DictBonusItems DictBonusItems;
|
||||
public readonly RestrictedGear RestrictedGear;
|
||||
public readonly ObjectIdentification ObjectIdentification;
|
||||
public readonly ExcelSheet<Item> 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<Lumina.Excel.GeneratedSheets.Item>()!;
|
||||
ItemSheet = gameData.GetExcelSheet<Item>();
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Item
|
|||
public bool IsUnlocked(CustomItemId itemId, out DateTimeOffset time)
|
||||
{
|
||||
// Pseudo items are always unlocked.
|
||||
if (itemId.Id >= _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<Item
|
|||
private static Dictionary<ItemId, UnlockRequirements> CreateUnlockData(IDataManager gameData, ItemManager items)
|
||||
{
|
||||
var ret = new Dictionary<ItemId, UnlockRequirements>();
|
||||
var cabinet = gameData.GetExcelSheet<Cabinet>()!;
|
||||
var cabinet = gameData.GetExcelSheet<Cabinet>();
|
||||
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<GilShopItem>()!;
|
||||
var gilShop = gameData.GetExcelSheet<GilShop>()!;
|
||||
foreach (var row in gilShopItem)
|
||||
var gilShopItem = gameData.GetSubrowExcelSheet<GilShopItem>();
|
||||
var gilShop = gameData.GetExcelSheet<GilShop>();
|
||||
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)
|
||||
|
|
|
|||
2
OtterGui
2
OtterGui
|
|
@ -1 +1 @@
|
|||
Subproject commit d9486ae54b5a4b61cf74f79ed27daa659eb1ce5b
|
||||
Subproject commit 8ba88eff15326bb28ed5e6157f5252c114d40b5f
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 77d52b02d21e770b30c08f89bdf06e0cb75562f7
|
||||
Subproject commit fb81a0b55d3c68f2b26357fac3049c79fb0c22fb
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
"RepoUrl": "https://github.com/Ottermandias/Glamourer",
|
||||
"ApplicableVersion": "any",
|
||||
"DalamudApiLevel": 10,
|
||||
"TestingDalamudApiLevel": 10,
|
||||
"TestingDalamudApiLevel": 11,
|
||||
"IsHide": "False",
|
||||
"IsTestingExclusive": "False",
|
||||
"DownloadCount": 1,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue