mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 10:17:23 +01:00
blep
This commit is contained in:
parent
cb2e2f0128
commit
941bba1518
39 changed files with 2569 additions and 1579 deletions
|
|
@ -6,59 +6,59 @@ namespace Glamourer.Customization;
|
|||
|
||||
public unsafe struct Customize
|
||||
{
|
||||
private readonly CustomizeData* _data;
|
||||
public readonly CustomizeData* Data;
|
||||
|
||||
public Customize(CustomizeData* data)
|
||||
=> _data = data;
|
||||
=> Data = data;
|
||||
|
||||
public Race Race
|
||||
{
|
||||
get => (Race)_data->Data[0];
|
||||
set => _data->Data[0] = (byte)value;
|
||||
get => (Race)Data->Data[0];
|
||||
set => Data->Data[0] = (byte)value;
|
||||
}
|
||||
|
||||
// Skip Unknown Gender
|
||||
public Gender Gender
|
||||
{
|
||||
get => (Gender)(_data->Data[1] + 1);
|
||||
set => _data->Data[1] = (byte)(value - 1);
|
||||
get => (Gender)(Data->Data[1] + 1);
|
||||
set => Data->Data[1] = (byte)(value - 1);
|
||||
}
|
||||
|
||||
public ref byte BodyType
|
||||
=> ref _data->Data[2];
|
||||
=> ref Data->Data[2];
|
||||
|
||||
public ref byte Height
|
||||
=> ref _data->Data[3];
|
||||
=> ref Data->Data[3];
|
||||
|
||||
public SubRace Clan
|
||||
{
|
||||
get => (SubRace)_data->Data[4];
|
||||
set => _data->Data[4] = (byte)value;
|
||||
get => (SubRace)Data->Data[4];
|
||||
set => Data->Data[4] = (byte)value;
|
||||
}
|
||||
|
||||
public ref byte Face
|
||||
=> ref _data->Data[5];
|
||||
=> ref Data->Data[5];
|
||||
|
||||
public ref byte Hairstyle
|
||||
=> ref _data->Data[6];
|
||||
=> ref Data->Data[6];
|
||||
|
||||
public bool HighlightsOn
|
||||
{
|
||||
get => _data->Data[7] >> 7 == 1;
|
||||
set => _data->Data[7] = (byte)(value ? _data->Data[7] | 0x80 : _data->Data[7] & 0x7F);
|
||||
get => Data->Data[7] >> 7 == 1;
|
||||
set => Data->Data[7] = (byte)(value ? Data->Data[7] | 0x80 : Data->Data[7] & 0x7F);
|
||||
}
|
||||
|
||||
public ref byte SkinColor
|
||||
=> ref _data->Data[8];
|
||||
=> ref Data->Data[8];
|
||||
|
||||
public ref byte EyeColorRight
|
||||
=> ref _data->Data[9];
|
||||
=> ref Data->Data[9];
|
||||
|
||||
public ref byte HairColor
|
||||
=> ref _data->Data[10];
|
||||
=> ref Data->Data[10];
|
||||
|
||||
public ref byte HighlightsColor
|
||||
=> ref _data->Data[11];
|
||||
=> ref Data->Data[11];
|
||||
|
||||
public readonly ref struct FacialFeatureStruct
|
||||
{
|
||||
|
|
@ -84,73 +84,73 @@ public unsafe struct Customize
|
|||
}
|
||||
|
||||
public FacialFeatureStruct FacialFeatures
|
||||
=> new(_data->Data + 12);
|
||||
=> new(Data->Data + 12);
|
||||
|
||||
public ref byte TattooColor
|
||||
=> ref _data->Data[13];
|
||||
=> ref Data->Data[13];
|
||||
|
||||
public ref byte Eyebrows
|
||||
=> ref _data->Data[14];
|
||||
=> ref Data->Data[14];
|
||||
|
||||
public ref byte EyeColorLeft
|
||||
=> ref _data->Data[15];
|
||||
=> ref Data->Data[15];
|
||||
|
||||
public byte EyeShape
|
||||
{
|
||||
get => (byte)(_data->Data[16] & 0x7F);
|
||||
set => _data->Data[16] = (byte)((value & 0x7F) | (_data->Data[16] & 0x80));
|
||||
get => (byte)(Data->Data[16] & 0x7F);
|
||||
set => Data->Data[16] = (byte)((value & 0x7F) | (Data->Data[16] & 0x80));
|
||||
}
|
||||
|
||||
public bool SmallIris
|
||||
{
|
||||
get => _data->Data[16] >> 7 == 1;
|
||||
set => _data->Data[16] = (byte)(value ? _data->Data[16] | 0x80 : _data->Data[16] & 0x7F);
|
||||
get => Data->Data[16] >> 7 == 1;
|
||||
set => Data->Data[16] = (byte)(value ? Data->Data[16] | 0x80 : Data->Data[16] & 0x7F);
|
||||
}
|
||||
|
||||
public ref byte Nose
|
||||
=> ref _data->Data[17];
|
||||
=> ref Data->Data[17];
|
||||
|
||||
public ref byte Jaw
|
||||
=> ref _data->Data[18];
|
||||
=> ref Data->Data[18];
|
||||
|
||||
public byte Mouth
|
||||
{
|
||||
get => (byte)(_data->Data[19] & 0x7F);
|
||||
set => _data->Data[19] = (byte)((value & 0x7F) | (_data->Data[19] & 0x80));
|
||||
get => (byte)(Data->Data[19] & 0x7F);
|
||||
set => Data->Data[19] = (byte)((value & 0x7F) | (Data->Data[19] & 0x80));
|
||||
}
|
||||
|
||||
public bool Lipstick
|
||||
{
|
||||
get => _data->Data[19] >> 7 == 1;
|
||||
set => _data->Data[19] = (byte)(value ? _data->Data[19] | 0x80 : _data->Data[19] & 0x7F);
|
||||
get => Data->Data[19] >> 7 == 1;
|
||||
set => Data->Data[19] = (byte)(value ? Data->Data[19] | 0x80 : Data->Data[19] & 0x7F);
|
||||
}
|
||||
|
||||
public ref byte LipColor
|
||||
=> ref _data->Data[20];
|
||||
=> ref Data->Data[20];
|
||||
|
||||
public ref byte MuscleMass
|
||||
=> ref _data->Data[21];
|
||||
=> ref Data->Data[21];
|
||||
|
||||
public ref byte TailShape
|
||||
=> ref _data->Data[22];
|
||||
=> ref Data->Data[22];
|
||||
|
||||
public ref byte BustSize
|
||||
=> ref _data->Data[23];
|
||||
=> ref Data->Data[23];
|
||||
|
||||
public byte FacePaint
|
||||
{
|
||||
get => (byte)(_data->Data[24] & 0x7F);
|
||||
set => _data->Data[24] = (byte)((value & 0x7F) | (_data->Data[24] & 0x80));
|
||||
get => (byte)(Data->Data[24] & 0x7F);
|
||||
set => Data->Data[24] = (byte)((value & 0x7F) | (Data->Data[24] & 0x80));
|
||||
}
|
||||
|
||||
public bool FacePaintReversed
|
||||
{
|
||||
get => _data->Data[24] >> 7 == 1;
|
||||
set => _data->Data[24] = (byte)(value ? _data->Data[24] | 0x80 : _data->Data[24] & 0x7F);
|
||||
get => Data->Data[24] >> 7 == 1;
|
||||
set => Data->Data[24] = (byte)(value ? Data->Data[24] | 0x80 : Data->Data[24] & 0x7F);
|
||||
}
|
||||
|
||||
public ref byte FacePaintColor
|
||||
=> ref _data->Data[25];
|
||||
=> ref Data->Data[25];
|
||||
|
||||
public static readonly CustomizeData Default = GenerateDefault();
|
||||
public static readonly CustomizeData Empty = new();
|
||||
|
|
@ -165,12 +165,12 @@ public unsafe struct Customize
|
|||
CustomizationId.Clan => (byte)Clan,
|
||||
CustomizationId.Face => Face,
|
||||
CustomizationId.Hairstyle => Hairstyle,
|
||||
CustomizationId.HighlightsOnFlag => _data->Data[7],
|
||||
CustomizationId.HighlightsOnFlag => Data->Data[7],
|
||||
CustomizationId.SkinColor => SkinColor,
|
||||
CustomizationId.EyeColorR => EyeColorRight,
|
||||
CustomizationId.HairColor => HairColor,
|
||||
CustomizationId.HighlightColor => HighlightsColor,
|
||||
CustomizationId.FacialFeaturesTattoos => _data->Data[12],
|
||||
CustomizationId.FacialFeaturesTattoos => Data->Data[12],
|
||||
CustomizationId.TattooColor => TattooColor,
|
||||
CustomizationId.Eyebrows => Eyebrows,
|
||||
CustomizationId.EyeColorL => EyeColorLeft,
|
||||
|
|
@ -204,7 +204,7 @@ public unsafe struct Customize
|
|||
case CustomizationId.EyeColorR: EyeColorRight = value; break;
|
||||
case CustomizationId.HairColor: HairColor = value; break;
|
||||
case CustomizationId.HighlightColor: HighlightsColor = value; break;
|
||||
case CustomizationId.FacialFeaturesTattoos: _data->Data[12] = value; break;
|
||||
case CustomizationId.FacialFeaturesTattoos: Data->Data[12] = value; break;
|
||||
case CustomizationId.TattooColor: TattooColor = value; break;
|
||||
case CustomizationId.Eyebrows: Eyebrows = value; break;
|
||||
case CustomizationId.EyeColorL: EyeColorLeft = value; break;
|
||||
|
|
@ -224,7 +224,7 @@ public unsafe struct Customize
|
|||
}
|
||||
|
||||
public bool Equals(Customize other)
|
||||
=> throw new NotImplementedException();
|
||||
=> CustomizeData.Equals(Data, other.Data);
|
||||
|
||||
public byte this[CustomizationId id]
|
||||
{
|
||||
|
|
@ -268,5 +268,8 @@ public unsafe struct Customize
|
|||
}
|
||||
|
||||
public void Load(Customize other)
|
||||
=> _data->Read(other._data);
|
||||
=> Data->Read(other.Data);
|
||||
|
||||
public void Write(IntPtr target)
|
||||
=> Data->Write((void*)target);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,8 +137,8 @@ public partial class CustomizationOptions
|
|||
public CustomizationSet GetSet(SubRace race, Gender gender)
|
||||
{
|
||||
var (skin, hair) = GetColors(race, gender);
|
||||
var row = _listSheet.GetRow(((uint)race - 1) * 2 - 1 + (uint)gender)!;
|
||||
|
||||
var row = _listSheet.GetRow(((uint)race - 1) * 2 - 1 + (uint)gender)!;
|
||||
var hrothgar = race.ToRace() == Race.Hrothgar;
|
||||
// Create the initial set with all the easily accessible parameters available for anyone.
|
||||
var set = new CustomizationSet(race, gender)
|
||||
{
|
||||
|
|
@ -148,8 +148,8 @@ public partial class CustomizationOptions
|
|||
EyeColors = _eyeColorPicker,
|
||||
HighlightColors = _highlightPicker,
|
||||
TattooColors = _tattooColorPicker,
|
||||
LipColorsDark = race.ToRace() == Race.Hrothgar ? HrothgarFurPattern(row) : _lipColorPickerDark,
|
||||
LipColorsLight = race.ToRace() == Race.Hrothgar ? Array.Empty<Customization>() : _lipColorPickerLight,
|
||||
LipColorsDark = hrothgar ? HrothgarFurPattern(row) : _lipColorPickerDark,
|
||||
LipColorsLight = hrothgar ? Array.Empty<Customization>() : _lipColorPickerLight,
|
||||
FacePaintColorsDark = _facePaintColorPickerDark,
|
||||
FacePaintColorsLight = _facePaintColorPickerLight,
|
||||
Faces = GetFaces(row),
|
||||
|
|
@ -164,9 +164,9 @@ public partial class CustomizationOptions
|
|||
|
||||
SetAvailability(set, row);
|
||||
SetFacialFeatures(set, row);
|
||||
SetHairByFace(set);
|
||||
SetMenuTypes(set, row);
|
||||
SetNames(set, row);
|
||||
|
||||
|
||||
return set;
|
||||
}
|
||||
|
|
@ -218,6 +218,32 @@ public partial class CustomizationOptions
|
|||
.Select((c, i) => new Customization(id, (byte)(light ? 128 + i : 0 + i), c, (ushort)(offset + i)))
|
||||
.ToArray();
|
||||
|
||||
|
||||
private void SetHairByFace(CustomizationSet set)
|
||||
{
|
||||
if (set.Race != Race.Hrothgar)
|
||||
{
|
||||
set.HairByFace = Enumerable.Repeat(set.HairStyles, set.Faces.Count + 1).ToArray();
|
||||
return;
|
||||
}
|
||||
|
||||
var tmp = new IReadOnlyList<Customization>[set.Faces.Count + 1];
|
||||
tmp[0] = set.HairStyles;
|
||||
|
||||
for (var i = 1; i <= set.Faces.Count; ++i)
|
||||
{
|
||||
bool Valid(Customization c)
|
||||
{
|
||||
var data = _customizeSheet.GetRow(c.CustomizeId)?.Unknown6 ?? 0;
|
||||
return data == 0 || data == i + set.Faces.Count;
|
||||
}
|
||||
|
||||
tmp[i] = set.HairStyles.Where(Valid).ToArray();
|
||||
}
|
||||
|
||||
set.HairByFace = tmp;
|
||||
}
|
||||
|
||||
private static void SetMenuTypes(CustomizationSet set, CharaMakeParams row)
|
||||
{
|
||||
// Set up the menu types for all customizations.
|
||||
|
|
@ -270,6 +296,7 @@ public partial class CustomizationOptions
|
|||
{
|
||||
var count = set.Faces.Count;
|
||||
var featureDict = new List<IReadOnlyList<Customization>>(count);
|
||||
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
var legacyTattoo = new Customization(CustomizationId.FacialFeaturesTattoos, 1 << 7, 137905, (ushort)((i + 1) * 8));
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.VisualBasic;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Customization;
|
||||
|
|
@ -52,6 +53,7 @@ public class CustomizationSet
|
|||
public IReadOnlyList<string> OptionName { get; internal set; } = null!;
|
||||
public IReadOnlyList<Customization> Faces { get; internal init; } = null!;
|
||||
public IReadOnlyList<Customization> HairStyles { get; internal init; } = null!;
|
||||
public IReadOnlyList<IReadOnlyList<Customization>> HairByFace { get; internal set; } = null!;
|
||||
public IReadOnlyList<Customization> TailEarShapes { get; internal init; } = null!;
|
||||
public IReadOnlyList<IReadOnlyList<Customization>> FeaturesTattoos { get; internal set; } = null!;
|
||||
public IReadOnlyList<Customization> FacePaints { get; internal init; } = null!;
|
||||
|
|
@ -74,13 +76,22 @@ public class CustomizationSet
|
|||
=> OptionName[(int)id];
|
||||
|
||||
public Customization FacialFeature(int faceIdx, int idx)
|
||||
=> FeaturesTattoos[faceIdx - 1][idx];
|
||||
{
|
||||
faceIdx = HrothgarFaceHack((byte) faceIdx) - 1;
|
||||
if (faceIdx < FeaturesTattoos.Count)
|
||||
return FeaturesTattoos[HrothgarFaceHack((byte)faceIdx)][idx];
|
||||
|
||||
return FeaturesTattoos[0][idx];
|
||||
}
|
||||
|
||||
private byte HrothgarFaceHack(byte value)
|
||||
=> value is > 4 and < 9 && Clan.ToRace() == Race.Hrothgar ? (byte)(value - 4) : value;
|
||||
|
||||
public int DataByValue(CustomizationId id, byte value, out Customization? custom)
|
||||
{
|
||||
var type = id.ToType();
|
||||
custom = null;
|
||||
if (type == CharaMakeParams.MenuType.Percentage || type == CharaMakeParams.MenuType.ListSelector)
|
||||
if (type is CharaMakeParams.MenuType.Percentage or CharaMakeParams.MenuType.ListSelector)
|
||||
{
|
||||
if (value < Count(id))
|
||||
{
|
||||
|
|
@ -91,9 +102,9 @@ public class CustomizationSet
|
|||
return -1;
|
||||
}
|
||||
|
||||
int Get(IEnumerable<Customization> list, ref Customization? output)
|
||||
int Get(IEnumerable<Customization> list, byte v, ref Customization? output)
|
||||
{
|
||||
var (val, idx) = list.Cast<Customization?>().Select((c, i) => (c, i)).FirstOrDefault(c => c.c!.Value.Value == value);
|
||||
var (val, idx) = list.Cast<Customization?>().Select((c, i) => (c, i)).FirstOrDefault(c => c.c!.Value.Value == v);
|
||||
if (val == null)
|
||||
return -1;
|
||||
|
||||
|
|
@ -103,27 +114,27 @@ public class CustomizationSet
|
|||
|
||||
return id switch
|
||||
{
|
||||
CustomizationId.SkinColor => Get(SkinColors, ref custom),
|
||||
CustomizationId.EyeColorL => Get(EyeColors, ref custom),
|
||||
CustomizationId.EyeColorR => Get(EyeColors, ref custom),
|
||||
CustomizationId.HairColor => Get(HairColors, ref custom),
|
||||
CustomizationId.HighlightColor => Get(HighlightColors, ref custom),
|
||||
CustomizationId.TattooColor => Get(TattooColors, ref custom),
|
||||
CustomizationId.LipColor => Get(LipColorsDark.Concat(LipColorsLight), ref custom),
|
||||
CustomizationId.FacePaintColor => Get(FacePaintColorsDark.Concat(FacePaintColorsLight), ref custom),
|
||||
CustomizationId.SkinColor => Get(SkinColors, value, ref custom),
|
||||
CustomizationId.EyeColorL => Get(EyeColors, value, ref custom),
|
||||
CustomizationId.EyeColorR => Get(EyeColors, value, ref custom),
|
||||
CustomizationId.HairColor => Get(HairColors, value, ref custom),
|
||||
CustomizationId.HighlightColor => Get(HighlightColors, value, ref custom),
|
||||
CustomizationId.TattooColor => Get(TattooColors, value, ref custom),
|
||||
CustomizationId.LipColor => Get(LipColorsDark.Concat(LipColorsLight), value, ref custom),
|
||||
CustomizationId.FacePaintColor => Get(FacePaintColorsDark.Concat(FacePaintColorsLight), value, ref custom),
|
||||
|
||||
CustomizationId.Face => Get(Faces, ref custom),
|
||||
CustomizationId.Hairstyle => Get(HairStyles, ref custom),
|
||||
CustomizationId.TailEarShape => Get(TailEarShapes, ref custom),
|
||||
CustomizationId.FacePaint => Get(FacePaints, ref custom),
|
||||
CustomizationId.FacialFeaturesTattoos => Get(FeaturesTattoos[0], ref custom),
|
||||
CustomizationId.Face => Get(Faces, HrothgarFaceHack(value), ref custom),
|
||||
CustomizationId.Hairstyle => Get(HairStyles, value, ref custom),
|
||||
CustomizationId.TailEarShape => Get(TailEarShapes, value, ref custom),
|
||||
CustomizationId.FacePaint => Get(FacePaints, value, ref custom),
|
||||
CustomizationId.FacialFeaturesTattoos => Get(FeaturesTattoos[0], value, ref custom),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(id), id, null),
|
||||
};
|
||||
}
|
||||
|
||||
public Customization Data(CustomizationId id, int idx)
|
||||
public Customization Data(CustomizationId id, int idx, byte face = 0)
|
||||
{
|
||||
if (idx > Count(id))
|
||||
if (idx > Count(id, face = HrothgarFaceHack(face)))
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
switch (id.ToType())
|
||||
|
|
@ -135,7 +146,7 @@ public class CustomizationSet
|
|||
return id switch
|
||||
{
|
||||
CustomizationId.Face => Faces[idx],
|
||||
CustomizationId.Hairstyle => HairStyles[idx],
|
||||
CustomizationId.Hairstyle => face < HairByFace.Count ? HairByFace[face][idx] : HairStyles[idx],
|
||||
CustomizationId.TailEarShape => TailEarShapes[idx],
|
||||
CustomizationId.FacePaint => FacePaints[idx],
|
||||
CustomizationId.FacialFeaturesTattoos => FeaturesTattoos[0][idx],
|
||||
|
|
@ -162,10 +173,13 @@ public class CustomizationSet
|
|||
ret[(int)CustomizationId.EyeColorL] = CustomizationId.EyeColorR;
|
||||
ret[(int)CustomizationId.EyeColorR] = CustomizationId.TattooColor;
|
||||
|
||||
return ret.Skip(2).Where(set.IsAvailable).GroupBy(set.Type).ToDictionary(k => k.Key, k => k.ToArray());
|
||||
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>())
|
||||
dict.TryAdd(type, Array.Empty<CustomizationId>());
|
||||
return dict;
|
||||
}
|
||||
|
||||
public int Count(CustomizationId id)
|
||||
public int Count(CustomizationId id, byte face = 0)
|
||||
{
|
||||
if (!IsAvailable(id))
|
||||
return 0;
|
||||
|
|
@ -176,7 +190,7 @@ public class CustomizationSet
|
|||
return id switch
|
||||
{
|
||||
CustomizationId.Face => Faces.Count,
|
||||
CustomizationId.Hairstyle => HairStyles.Count,
|
||||
CustomizationId.Hairstyle => (face = HrothgarFaceHack(face)) < HairByFace.Count ? HairByFace[face].Count : 0,
|
||||
CustomizationId.HighlightsOnFlag => 2,
|
||||
CustomizationId.SkinColor => SkinColors.Count,
|
||||
CustomizationId.EyeColorR => EyeColors.Count,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.Utility;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using OtterGui.Raii;
|
||||
using Companion = Lumina.Excel.GeneratedSheets.Companion;
|
||||
|
||||
namespace Glamourer;
|
||||
|
|
@ -26,16 +26,37 @@ public class ModelData
|
|||
FirstName = $"{name} #{model.RowId:D4}";
|
||||
AllNames = $"#{model.RowId:D4}\n{name}";
|
||||
}
|
||||
|
||||
public uint Id
|
||||
=> Model.RowId;
|
||||
}
|
||||
|
||||
private readonly SortedList<uint, Data> _models;
|
||||
private readonly SortedList<uint, Data> _models;
|
||||
private readonly Dictionary<ulong, Data> _modelByData;
|
||||
|
||||
public IReadOnlyDictionary<uint, Data> Models
|
||||
=> _models;
|
||||
|
||||
public unsafe ulong KeyFromCharacterBase(CharacterBase* drawObject)
|
||||
{
|
||||
var type = (*(delegate* unmanaged<CharacterBase*, uint>**)drawObject)[50](drawObject);
|
||||
var unk = (ulong)*((byte*)drawObject + 0x8E8) << 8;
|
||||
return type switch
|
||||
{
|
||||
1 => type | unk,
|
||||
2 => type | unk | ((ulong)*(ushort*)((byte*)drawObject + 0x908) << 16),
|
||||
3 => type | unk | ((ulong)*(ushort*)((byte*)drawObject + 0x8F0) << 16) | ((ulong)**(ushort**)((byte*)drawObject + 0x910) << 32) | ((ulong)**(ushort**)((byte*)drawObject + 0x910) << 40),
|
||||
_ => 0u,
|
||||
};
|
||||
}
|
||||
|
||||
public unsafe bool FromCharacterBase(CharacterBase* drawObject, out Data data)
|
||||
=> _modelByData.TryGetValue(KeyFromCharacterBase(drawObject), out data);
|
||||
|
||||
|
||||
public ModelData(DataManager dataManager)
|
||||
{
|
||||
var modelSheet = dataManager.GetExcelSheet<ModelChara>();
|
||||
var modelSheet = dataManager.GetExcelSheet<ModelChara>()!;
|
||||
|
||||
_models = new SortedList<uint, Data>(NpcNames.ModelCharas.Count);
|
||||
|
||||
|
|
@ -71,5 +92,20 @@ public class ModelData
|
|||
UpdateData(model, name);
|
||||
}
|
||||
}
|
||||
|
||||
_modelByData = new Dictionary<ulong, Data>((int)modelSheet.RowCount);
|
||||
foreach (var mdl in modelSheet)
|
||||
{
|
||||
var unk5 = (ulong)mdl.Unknown5 << 8;
|
||||
var key = mdl.Type switch
|
||||
{
|
||||
1 => mdl.Type | unk5,
|
||||
2 => mdl.Type | unk5 | ((ulong)mdl.Model << 16),
|
||||
3 => mdl.Type | unk5 | ((ulong)mdl.Model << 16) | ((ulong)mdl.Base << 32) | ((ulong)mdl.Base << 40),
|
||||
_ => 0u,
|
||||
};
|
||||
if (key != 0)
|
||||
_modelByData.TryAdd(key, _models.TryGetValue(mdl.RowId, out var d) ? d : new Data(mdl, string.Empty));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ using Dalamud.Logging;
|
|||
using Dalamud.Utility;
|
||||
using Lumina.Excel;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Race = Penumbra.GameData.Enums.Race;
|
||||
|
|
@ -356,16 +355,6 @@ public class RestrictedGear
|
|||
AddItem(37474, 37479); // Common Makai Harbinger's Fingerless Gloves <-> Common Makai Harrower's Fingerless Gloves
|
||||
AddItem(37475, 37480); // Common Makai Harbinger's Leggings <-> Common Makai Harrower's Quartertights
|
||||
AddItem(37476, 37481); // Common Makai Harbinger's Boots <-> Common Makai Harrower's Longboots
|
||||
AddItem(23003, 23008); // Mun'gaek Hat <-> Eastern Socialite's Hat
|
||||
AddItem(23004, 23009); // Mun'gaek Uibok <-> Eastern Socialite's Cheongsam
|
||||
AddItem(23005, 23010); // Mun'gaek Cuffs <-> Eastern Socialite's Gloves
|
||||
AddItem(23006, 23011); // Mun'gaek Trousers <-> Eastern Socialite's Skirt
|
||||
AddItem(23007, 23012); // Mun'gaek Boots <-> Eastern Socialite's Boots
|
||||
AddItem(24148, 24153); // Far Eastern Officer's Hat <-> Far Eastern Maiden's Hat
|
||||
AddItem(24149, 24154); // Far Eastern Officer's Robe <-> Far Eastern Maiden's Tunic
|
||||
AddItem(24150, 24155); // Far Eastern Officer's Armband <-> Far Eastern Maiden's Armband
|
||||
AddItem(24151, 24156); // Far Eastern Officer's Bottoms <-> Far Eastern Maiden's Bottoms
|
||||
AddItem(24152, 24157); // Far Eastern Officer's Boots <-> Far Eastern Maiden's Boots
|
||||
AddItem(13323, 13322); // Scion Thief's Tunic <-> Scion Conjurer's Dalmatica
|
||||
AddItem(13693, 10034, true, false); // Scion Thief's Halfgloves -> The Emperor's New Gloves
|
||||
AddItem(13694, 13691); // Scion Thief's Gaskins <-> Scion Conjurer's Chausses
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
using System;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Structs;
|
||||
|
||||
|
||||
// Turn EquipSlot into a bitfield flag enum.
|
||||
[Flags]
|
||||
public enum CharacterEquipMask : ushort
|
||||
{
|
||||
None = 0,
|
||||
MainHand = 0b000000000001,
|
||||
OffHand = 0b000000000010,
|
||||
Head = 0b000000000100,
|
||||
Body = 0b000000001000,
|
||||
Hands = 0b000000010000,
|
||||
Legs = 0b000000100000,
|
||||
Feet = 0b000001000000,
|
||||
Ears = 0b000010000000,
|
||||
Neck = 0b000100000000,
|
||||
Wrists = 0b001000000000,
|
||||
RFinger = 0b010000000000,
|
||||
LFinger = 0b100000000000,
|
||||
All = 0b111111111111,
|
||||
}
|
||||
|
||||
public static class CharacterEquipMaskExtensions
|
||||
{
|
||||
public static bool Fits(this CharacterEquipMask mask, EquipSlot slot)
|
||||
=> slot switch
|
||||
{
|
||||
EquipSlot.Unknown => false,
|
||||
EquipSlot.Head => mask.HasFlag(CharacterEquipMask.Head),
|
||||
EquipSlot.Body => mask.HasFlag(CharacterEquipMask.Body),
|
||||
EquipSlot.Hands => mask.HasFlag(CharacterEquipMask.Hands),
|
||||
EquipSlot.Legs => mask.HasFlag(CharacterEquipMask.Legs),
|
||||
EquipSlot.Feet => mask.HasFlag(CharacterEquipMask.Feet),
|
||||
EquipSlot.Ears => mask.HasFlag(CharacterEquipMask.Ears),
|
||||
EquipSlot.Neck => mask.HasFlag(CharacterEquipMask.Neck),
|
||||
EquipSlot.Wrists => mask.HasFlag(CharacterEquipMask.Wrists),
|
||||
EquipSlot.RFinger => mask.HasFlag(CharacterEquipMask.RFinger),
|
||||
EquipSlot.LFinger => mask.HasFlag(CharacterEquipMask.LFinger),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
|
@ -21,6 +21,9 @@ public readonly struct Item
|
|||
public bool HasSubModel
|
||||
=> Base.ModelSub != 0;
|
||||
|
||||
public bool IsBothHand
|
||||
=> (EquipSlot)Base.EquipSlotCategory.Row == EquipSlot.BothHand;
|
||||
|
||||
// Create a new item from its sheet list with the given name and either the inferred equip slot or the given one.
|
||||
public Item(Lumina.Excel.GeneratedSheets.Item item, string name, EquipSlot slot = EquipSlot.Unknown)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue