mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 10:17:23 +01:00
So. Much. Stuff. Glamourer now works with all player actors, can change all customization, gear and stains. Also has a cool Legacy Tattoo icon.
This commit is contained in:
parent
fbb41636df
commit
052a2e7719
14 changed files with 1120 additions and 577 deletions
264
Glamourer.GameData/Customization/ActorCustomization.cs
Normal file
264
Glamourer.GameData/Customization/ActorCustomization.cs
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Customization
|
||||
{
|
||||
public unsafe struct LazyCustomization
|
||||
{
|
||||
public ActorCustomization* Address;
|
||||
|
||||
public LazyCustomization(IntPtr actorPtr)
|
||||
=> Address = (ActorCustomization*) (actorPtr + ActorCustomization.CustomizationOffset);
|
||||
|
||||
public ref ActorCustomization Value
|
||||
=> ref *Address;
|
||||
}
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct ActorCustomization
|
||||
{
|
||||
public const int CustomizationOffset = 0x1898;
|
||||
public const int CustomizationBytes = 26;
|
||||
|
||||
private byte _race;
|
||||
private byte _gender;
|
||||
public byte BodyType;
|
||||
public byte Height;
|
||||
public SubRace Clan;
|
||||
public byte Face;
|
||||
public byte Hairstyle;
|
||||
private byte _highlightsOn;
|
||||
public byte SkinColor;
|
||||
public byte EyeColorRight;
|
||||
public byte HairColor;
|
||||
public byte HighlightsColor;
|
||||
public byte FacialFeatures;
|
||||
public byte TattooColor;
|
||||
public byte Eyebrow;
|
||||
public byte EyeColorLeft;
|
||||
private byte _eyeShape;
|
||||
public byte Nose;
|
||||
public byte Jaw;
|
||||
private byte _mouth;
|
||||
public byte LipColor;
|
||||
public byte MuscleMass;
|
||||
public byte TailShape;
|
||||
public byte BustSize;
|
||||
private byte _facePaint;
|
||||
public byte FacePaintColor;
|
||||
|
||||
public Race Race
|
||||
{
|
||||
get => (Race) (_race > (byte) Race.Midlander ? _race + 1 : _race);
|
||||
set => _race = (byte) (value > Race.Highlander ? value - 1 : value);
|
||||
}
|
||||
|
||||
public Gender Gender
|
||||
{
|
||||
get => (Gender) (_gender + 1);
|
||||
set => _gender = (byte) (value - 1);
|
||||
}
|
||||
|
||||
public bool HighlightsOn
|
||||
{
|
||||
get => (_highlightsOn & 128) == 128;
|
||||
set => _highlightsOn = (byte) (value ? _highlightsOn | 128 : _highlightsOn & 127);
|
||||
}
|
||||
|
||||
public bool FacialFeature(int idx)
|
||||
=> (FacialFeatures & (1 << idx)) != 0;
|
||||
|
||||
public void FacialFeature(int idx, bool set)
|
||||
{
|
||||
if (set)
|
||||
FacialFeatures |= (byte) (1 << idx);
|
||||
else
|
||||
FacialFeatures &= (byte) ~(1 << idx);
|
||||
}
|
||||
|
||||
public byte EyeShape
|
||||
{
|
||||
get => (byte) (_eyeShape & 127);
|
||||
set => _eyeShape = (byte) ((value & 127) | (_eyeShape & 128));
|
||||
}
|
||||
|
||||
public bool SmallIris
|
||||
{
|
||||
get => (_eyeShape & 128) == 128;
|
||||
set => _eyeShape = (byte) (value ? _eyeShape | 128 : _eyeShape & 127);
|
||||
}
|
||||
|
||||
|
||||
public byte Mouth
|
||||
{
|
||||
get => (byte) (_mouth & 127);
|
||||
set => _mouth = (byte) ((value & 127) | (_mouth & 128));
|
||||
}
|
||||
|
||||
public bool Lipstick
|
||||
{
|
||||
get => (_mouth & 128) == 128;
|
||||
set => _mouth = (byte) (value ? _mouth | 128 : _mouth & 127);
|
||||
}
|
||||
|
||||
public byte FacePaint
|
||||
{
|
||||
get => (byte) (_facePaint & 127);
|
||||
set => _facePaint = (byte) ((value & 127) | (_facePaint & 128));
|
||||
}
|
||||
|
||||
public bool FacePaintReversed
|
||||
{
|
||||
get => (_facePaint & 128) == 128;
|
||||
set => _facePaint = (byte) (value ? _facePaint | 128 : _facePaint & 127);
|
||||
}
|
||||
|
||||
public unsafe void Read(IntPtr customizeAddress)
|
||||
{
|
||||
fixed (byte* ptr = &_race)
|
||||
{
|
||||
Buffer.MemoryCopy(customizeAddress.ToPointer(), ptr, CustomizationBytes, CustomizationBytes);
|
||||
}
|
||||
}
|
||||
|
||||
public byte this[CustomizationId id]
|
||||
{
|
||||
get => id switch
|
||||
{
|
||||
CustomizationId.Race => (byte) Race,
|
||||
CustomizationId.Gender => (byte) Gender,
|
||||
CustomizationId.BodyType => BodyType,
|
||||
CustomizationId.Height => Height,
|
||||
CustomizationId.Clan => (byte) Clan,
|
||||
CustomizationId.Face => Face,
|
||||
CustomizationId.Hairstyle => Hairstyle,
|
||||
CustomizationId.HighlightsOnFlag => _highlightsOn,
|
||||
CustomizationId.SkinColor => SkinColor,
|
||||
CustomizationId.EyeColorR => EyeColorRight,
|
||||
CustomizationId.HairColor => HairColor,
|
||||
CustomizationId.HighlightColor => HighlightsColor,
|
||||
CustomizationId.FacialFeaturesTattoos => FacialFeatures,
|
||||
CustomizationId.TattooColor => TattooColor,
|
||||
CustomizationId.Eyebrows => Eyebrow,
|
||||
CustomizationId.EyeColorL => EyeColorLeft,
|
||||
CustomizationId.EyeShape => EyeShape,
|
||||
CustomizationId.Nose => Nose,
|
||||
CustomizationId.Jaw => Jaw,
|
||||
CustomizationId.Mouth => Mouth,
|
||||
CustomizationId.LipColor => LipColor,
|
||||
CustomizationId.MuscleToneOrTailEarLength => MuscleMass,
|
||||
CustomizationId.TailEarShape => TailShape,
|
||||
CustomizationId.BustSize => BustSize,
|
||||
CustomizationId.FacePaint => FacePaint,
|
||||
CustomizationId.FacePaintColor => FacePaintColor,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(id), id, null),
|
||||
};
|
||||
set
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case CustomizationId.Race:
|
||||
Race = (Race) value;
|
||||
break;
|
||||
case CustomizationId.Gender:
|
||||
Gender = (Gender) value;
|
||||
break;
|
||||
case CustomizationId.BodyType:
|
||||
BodyType = value;
|
||||
break;
|
||||
case CustomizationId.Height:
|
||||
Height = value;
|
||||
break;
|
||||
case CustomizationId.Clan:
|
||||
Clan = (SubRace) value;
|
||||
break;
|
||||
case CustomizationId.Face:
|
||||
Face = value;
|
||||
break;
|
||||
case CustomizationId.Hairstyle:
|
||||
Hairstyle = value;
|
||||
break;
|
||||
case CustomizationId.HighlightsOnFlag:
|
||||
HighlightsOn = (value & 128) == 128;
|
||||
break;
|
||||
case CustomizationId.SkinColor:
|
||||
SkinColor = value;
|
||||
break;
|
||||
case CustomizationId.EyeColorR:
|
||||
EyeColorRight = value;
|
||||
break;
|
||||
case CustomizationId.HairColor:
|
||||
HairColor = value;
|
||||
break;
|
||||
case CustomizationId.HighlightColor:
|
||||
HighlightsColor = value;
|
||||
break;
|
||||
case CustomizationId.FacialFeaturesTattoos:
|
||||
FacialFeatures = value;
|
||||
break;
|
||||
case CustomizationId.TattooColor:
|
||||
TattooColor = value;
|
||||
break;
|
||||
case CustomizationId.Eyebrows:
|
||||
Eyebrow = value;
|
||||
break;
|
||||
case CustomizationId.EyeColorL:
|
||||
EyeColorLeft = value;
|
||||
break;
|
||||
case CustomizationId.EyeShape:
|
||||
EyeShape = value;
|
||||
break;
|
||||
case CustomizationId.Nose:
|
||||
Nose = value;
|
||||
break;
|
||||
case CustomizationId.Jaw:
|
||||
Jaw = value;
|
||||
break;
|
||||
case CustomizationId.Mouth:
|
||||
Mouth = value;
|
||||
break;
|
||||
case CustomizationId.LipColor:
|
||||
LipColor = value;
|
||||
break;
|
||||
case CustomizationId.MuscleToneOrTailEarLength:
|
||||
MuscleMass = value;
|
||||
break;
|
||||
case CustomizationId.TailEarShape:
|
||||
TailShape = value;
|
||||
break;
|
||||
case CustomizationId.BustSize:
|
||||
BustSize = value;
|
||||
break;
|
||||
case CustomizationId.FacePaint:
|
||||
FacePaint = value;
|
||||
break;
|
||||
case CustomizationId.FacePaintColor:
|
||||
FacePaintColor = value;
|
||||
break;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(id), id, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void Write(IntPtr actorAddress)
|
||||
{
|
||||
fixed (byte* ptr = &_race)
|
||||
{
|
||||
Buffer.MemoryCopy(ptr, (byte*) actorAddress + CustomizationOffset, CustomizationBytes, CustomizationBytes);
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe byte[] ToBytes()
|
||||
{
|
||||
var ret = new byte[CustomizationBytes];
|
||||
fixed (byte* ptr = &_race)
|
||||
{
|
||||
Marshal.Copy(new IntPtr(ptr), ret, 0, CustomizationBytes);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Glamourer.GameData/Customization/CustomName.cs
Normal file
46
Glamourer.GameData/Customization/CustomName.cs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
namespace Glamourer.Customization
|
||||
{
|
||||
public enum CustomName
|
||||
{
|
||||
Clan = 0,
|
||||
Gender,
|
||||
Reverse,
|
||||
OddEyes,
|
||||
IrisSmall,
|
||||
IrisLarge,
|
||||
MidlanderM,
|
||||
HighlanderM,
|
||||
WildwoodM,
|
||||
DuskwightM,
|
||||
PlainsfolkM,
|
||||
DunesfolkM,
|
||||
SeekerOfTheSunM,
|
||||
KeeperOfTheMoonM,
|
||||
SeawolfM,
|
||||
HellsguardM,
|
||||
RaenM,
|
||||
XaelaM,
|
||||
HelionM,
|
||||
LostM,
|
||||
RavaM,
|
||||
VeenaM,
|
||||
MidlanderF,
|
||||
HighlanderF,
|
||||
WildwoodF,
|
||||
DuskwightF,
|
||||
PlainsfolkF,
|
||||
DunesfolkF,
|
||||
SeekerOfTheSunF,
|
||||
KeeperOfTheMoonF,
|
||||
SeawolfF,
|
||||
HellsguardF,
|
||||
RaenF,
|
||||
XaelaF,
|
||||
HelionF,
|
||||
LostF,
|
||||
RavaF,
|
||||
VeenaF,
|
||||
|
||||
Num,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Customization
|
||||
{
|
||||
|
|
@ -34,7 +35,40 @@ namespace Glamourer.Customization
|
|||
|
||||
public static class CustomizationExtensions
|
||||
{
|
||||
public static CharaMakeParams.MenuType ToType(this CustomizationId customizationId, bool isHrothgar = false)
|
||||
public static string ToDefaultName(this CustomizationId customizationId)
|
||||
=> customizationId switch
|
||||
{
|
||||
CustomizationId.Race => "Race",
|
||||
CustomizationId.Gender => "Gender",
|
||||
CustomizationId.BodyType => "Body Type",
|
||||
CustomizationId.Height => "Height",
|
||||
CustomizationId.Clan => "Clan",
|
||||
CustomizationId.Face => "Head Style",
|
||||
CustomizationId.Hairstyle => "Hair Style",
|
||||
CustomizationId.HighlightsOnFlag => "Highlights",
|
||||
CustomizationId.SkinColor => "Skin Color",
|
||||
CustomizationId.EyeColorR => "Right Eye Color",
|
||||
CustomizationId.HairColor => "Hair Color",
|
||||
CustomizationId.HighlightColor => "Highlights Color",
|
||||
CustomizationId.FacialFeaturesTattoos => "Facial Features",
|
||||
CustomizationId.TattooColor => "Tattoo Color",
|
||||
CustomizationId.Eyebrows => "Eyebrow Style",
|
||||
CustomizationId.EyeColorL => "Left Eye Color",
|
||||
CustomizationId.EyeShape => "Eye Shape",
|
||||
CustomizationId.Nose => "Nose Style",
|
||||
CustomizationId.Jaw => "Jaw Style",
|
||||
CustomizationId.Mouth => "Mouth Style",
|
||||
CustomizationId.MuscleToneOrTailEarLength => "Muscle Tone",
|
||||
CustomizationId.TailEarShape => "Tail Shape",
|
||||
CustomizationId.BustSize => "Bust Size",
|
||||
CustomizationId.FacePaint => "Face Paint",
|
||||
CustomizationId.FacePaintColor => "Face Paint Color",
|
||||
CustomizationId.LipColor => "Lip Color",
|
||||
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(customizationId), customizationId, null),
|
||||
};
|
||||
|
||||
public static CharaMakeParams.MenuType ToType(this CustomizationId customizationId, Race race = Race.Midlander)
|
||||
=> customizationId switch
|
||||
{
|
||||
CustomizationId.Race => CharaMakeParams.MenuType.IconSelector,
|
||||
|
|
@ -58,13 +92,17 @@ namespace Glamourer.Customization
|
|||
CustomizationId.Jaw => CharaMakeParams.MenuType.ListSelector,
|
||||
CustomizationId.Mouth => CharaMakeParams.MenuType.ListSelector,
|
||||
CustomizationId.MuscleToneOrTailEarLength => CharaMakeParams.MenuType.Percentage,
|
||||
CustomizationId.TailEarShape => CharaMakeParams.MenuType.IconSelector,
|
||||
CustomizationId.BustSize => CharaMakeParams.MenuType.Percentage,
|
||||
CustomizationId.FacePaint => CharaMakeParams.MenuType.IconSelector,
|
||||
CustomizationId.FacePaintColor => CharaMakeParams.MenuType.ColorPicker,
|
||||
|
||||
CustomizationId.LipColor => isHrothgar ? CharaMakeParams.MenuType.IconSelector : CharaMakeParams.MenuType.ColorPicker,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(customizationId), customizationId, null),
|
||||
CustomizationId.TailEarShape => race == Race.Elezen || race == Race.Lalafell
|
||||
? CharaMakeParams.MenuType.ListSelector
|
||||
: CharaMakeParams.MenuType.IconSelector,
|
||||
CustomizationId.LipColor => race == Race.Hrothgar
|
||||
? CharaMakeParams.MenuType.IconSelector
|
||||
: CharaMakeParams.MenuType.ColorPicker,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(customizationId), customizationId, null),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,5 +31,8 @@ namespace Glamourer.Customization
|
|||
|
||||
public ImGuiScene.TextureWrap GetIcon(uint iconId)
|
||||
=> _options!.GetIcon(iconId);
|
||||
|
||||
public string GetName(CustomName name)
|
||||
=> _options!.GetName(name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ using Race = Penumbra.GameData.Enums.Race;
|
|||
|
||||
namespace Glamourer.Customization
|
||||
{
|
||||
public class CustomizationOptions
|
||||
public partial class CustomizationOptions
|
||||
{
|
||||
internal static readonly Race[] Races = ((Race[]) Enum.GetValues(typeof(Race))).Skip(1).ToArray();
|
||||
internal static readonly SubRace[] Clans = ((SubRace[]) Enum.GetValues(typeof(SubRace))).Skip(1).ToArray();
|
||||
|
|
@ -82,7 +82,7 @@ namespace Glamourer.Customization
|
|||
SubRace.Midlander => gender == Gender.Male ? (0x1200, 0x1300) : (0x0D00, 0x0E00),
|
||||
SubRace.Highlander => gender == Gender.Male ? (0x1C00, 0x1D00) : (0x1700, 0x1800),
|
||||
SubRace.Wildwood => gender == Gender.Male ? (0x2600, 0x2700) : (0x2100, 0x2200),
|
||||
SubRace.Duskwright => gender == Gender.Male ? (0x3000, 0x3100) : (0x2B00, 0x2C00),
|
||||
SubRace.Duskwight => gender == Gender.Male ? (0x3000, 0x3100) : (0x2B00, 0x2C00),
|
||||
SubRace.Plainsfolk => gender == Gender.Male ? (0x3A00, 0x3B00) : (0x3500, 0x3600),
|
||||
SubRace.Dunesfolk => gender == Gender.Male ? (0x4400, 0x4500) : (0x3F00, 0x4000),
|
||||
SubRace.SeekerOfTheSun => gender == Gender.Male ? (0x4E00, 0x4F00) : (0x4900, 0x4A00),
|
||||
|
|
@ -91,7 +91,7 @@ namespace Glamourer.Customization
|
|||
SubRace.Hellsguard => gender == Gender.Male ? (0x6C00, 0x6D00) : (0x6700, 0x6800),
|
||||
SubRace.Raen => gender == Gender.Male ? (0x7100, 0x7700) : (0x7600, 0x7200),
|
||||
SubRace.Xaela => gender == Gender.Male ? (0x7B00, 0x8100) : (0x8000, 0x7C00),
|
||||
SubRace.Hellion => gender == Gender.Male ? (0x8500, 0x8600) : (0x0000, 0x0000),
|
||||
SubRace.Helion => gender == Gender.Male ? (0x8500, 0x8600) : (0x0000, 0x0000),
|
||||
SubRace.Lost => gender == Gender.Male ? (0x8C00, 0x8F00) : (0x0000, 0x0000),
|
||||
SubRace.Rava => gender == Gender.Male ? (0x0000, 0x0000) : (0x9E00, 0x9F00),
|
||||
SubRace.Veena => gender == Gender.Male ? (0x0000, 0x0000) : (0xA800, 0xA900),
|
||||
|
|
@ -106,11 +106,11 @@ namespace Glamourer.Customization
|
|||
{
|
||||
var row = _customizeSheet.GetRow(value);
|
||||
return row == null
|
||||
? new Customization(id, (byte) (index + 1), value, 0)
|
||||
: new Customization(id, row.FeatureID, row.Icon, (ushort) row.RowId);
|
||||
? new Customization(id, (byte) (index + 1), value, 0)
|
||||
: new Customization(id, row.FeatureID, row.Icon, (ushort) row.RowId);
|
||||
}
|
||||
|
||||
private int GetListSize(CharaMakeParams row, CustomizationId id)
|
||||
private static int GetListSize(CharaMakeParams row, CustomizationId id)
|
||||
{
|
||||
var menu = row.Menus.Cast<CharaMakeParams.Menu?>().FirstOrDefault(m => m!.Value.Customization == id);
|
||||
return menu?.Size ?? 0;
|
||||
|
|
@ -187,6 +187,7 @@ namespace Glamourer.Customization
|
|||
set.SetAvailable(CustomizationId.FacePaint);
|
||||
set.SetAvailable(CustomizationId.FacePaintColor);
|
||||
}
|
||||
|
||||
if (set.TailEarShapes.Count > 0)
|
||||
set.SetAvailable(CustomizationId.TailEarShape);
|
||||
if (set.Faces.Count > 0)
|
||||
|
|
@ -204,12 +205,48 @@ namespace Glamourer.Customization
|
|||
|
||||
set.FeaturesTattoos = featureDict;
|
||||
|
||||
set.OptionName = ((CustomizationId[]) Enum.GetValues(typeof(CustomizationId))).Select(c =>
|
||||
{
|
||||
var menu = row.Menus
|
||||
.Cast<CharaMakeParams.Menu?>()
|
||||
.FirstOrDefault(m => m!.Value.Customization == c);
|
||||
if (menu == null)
|
||||
{
|
||||
if (c == CustomizationId.HighlightsOnFlag)
|
||||
return _lobby.GetRow(237)?.Text.ToString() ?? "Highlights";
|
||||
|
||||
return c.ToDefaultName();
|
||||
}
|
||||
|
||||
if (c == CustomizationId.FacialFeaturesTattoos)
|
||||
return
|
||||
$"{_lobby.GetRow(1741)?.Text.ToString() ?? "Facial Features"} & {_lobby.GetRow(1742)?.Text.ToString() ?? "Tattoos"}";
|
||||
|
||||
var textRow = _lobby.GetRow(menu.Value.Id);
|
||||
return textRow?.Text.ToString() ?? c.ToDefaultName();
|
||||
}).ToArray();
|
||||
|
||||
set._types = ((CustomizationId[]) Enum.GetValues(typeof(CustomizationId))).Select(c =>
|
||||
{
|
||||
if (c == CustomizationId.HighlightColor)
|
||||
return CharaMakeParams.MenuType.ColorPicker;
|
||||
|
||||
if (c == CustomizationId.EyeColorL)
|
||||
return CharaMakeParams.MenuType.ColorPicker;
|
||||
|
||||
var menu = row.Menus
|
||||
.Cast<CharaMakeParams.Menu?>()
|
||||
.FirstOrDefault(m => m!.Value.Customization == c);
|
||||
return menu?.Type ?? CharaMakeParams.MenuType.ListSelector;
|
||||
}).ToArray();
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
private readonly ExcelSheet<CharaMakeCustomize> _customizeSheet;
|
||||
private readonly ExcelSheet<CharaMakeParams> _listSheet;
|
||||
private readonly ExcelSheet<HairMakeType> _hairSheet;
|
||||
private readonly ExcelSheet<Lobby> _lobby;
|
||||
private readonly CmpFile _cmpFile;
|
||||
private readonly Customization[] _highlightPicker;
|
||||
private readonly Customization[] _eyeColorPicker;
|
||||
|
|
@ -218,6 +255,10 @@ namespace Glamourer.Customization
|
|||
private readonly Customization[] _lipColorPickerDark;
|
||||
private readonly Customization[] _lipColorPickerLight;
|
||||
private readonly Customization[] _tattooColorPicker;
|
||||
private readonly string[] _names = new string[(int) CustomName.Num];
|
||||
|
||||
public string GetName(CustomName name)
|
||||
=> _names[(int) name];
|
||||
|
||||
private static Language FromClientLanguage(ClientLanguage language)
|
||||
=> language switch
|
||||
|
|
@ -233,6 +274,7 @@ namespace Glamourer.Customization
|
|||
{
|
||||
_cmpFile = new CmpFile(pi);
|
||||
_customizeSheet = pi.Data.GetExcelSheet<CharaMakeCustomize>();
|
||||
_lobby = pi.Data.GetExcelSheet<Lobby>();
|
||||
var tmp = pi.Data.Excel.GetType()!.GetMethod("GetSheet", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||||
.MakeGenericMethod(typeof(CharaMakeParams))!.Invoke(pi.Data.Excel, new object?[]
|
||||
{
|
||||
|
|
@ -242,6 +284,7 @@ namespace Glamourer.Customization
|
|||
}) as ExcelSheet<CharaMakeParams>;
|
||||
_listSheet = tmp!;
|
||||
_hairSheet = pi.Data.GetExcelSheet<HairMakeType>();
|
||||
SetNames(pi);
|
||||
|
||||
_highlightPicker = CreateColorPicker(CustomizationId.HighlightColor, 256, 192);
|
||||
_lipColorPickerDark = CreateColorPicker(CustomizationId.LipColor, 512, 96);
|
||||
|
|
@ -258,5 +301,55 @@ namespace Glamourer.Customization
|
|||
_list[ToIndex(race, gender)] = GetSet(race, gender);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetNames(DalamudPluginInterface pi)
|
||||
{
|
||||
var subRace = pi.Data.GetExcelSheet<Tribe>();
|
||||
_names[(int) CustomName.Clan] = _lobby.GetRow(102)?.Text ?? "Clan";
|
||||
_names[(int) CustomName.Gender] = _lobby.GetRow(103)?.Text ?? "Gender";
|
||||
_names[(int) CustomName.Reverse] = _lobby.GetRow(2135)?.Text ?? "Reverse";
|
||||
_names[(int) CustomName.OddEyes] = _lobby.GetRow(2125)?.Text ?? "Odd Eyes";
|
||||
_names[(int) CustomName.IrisSmall] = _lobby.GetRow(1076)?.Text ?? "Small";
|
||||
_names[(int) CustomName.IrisLarge] = _lobby.GetRow(1075)?.Text ?? "Large";
|
||||
_names[(int) CustomName.MidlanderM] = subRace.GetRow((int) SubRace.Midlander)?.Masculine.ToString() ?? SubRace.Midlander.ToName();
|
||||
_names[(int) CustomName.MidlanderF] = subRace.GetRow((int) SubRace.Midlander)?.Feminine.ToString() ?? SubRace.Midlander.ToName();
|
||||
_names[(int) CustomName.HighlanderM] =
|
||||
subRace.GetRow((int) SubRace.Highlander)?.Masculine.ToString() ?? SubRace.Highlander.ToName();
|
||||
_names[(int) CustomName.HighlanderF] = subRace.GetRow((int) SubRace.Highlander)?.Feminine.ToString() ?? SubRace.Highlander.ToName();
|
||||
_names[(int) CustomName.WildwoodM] = subRace.GetRow((int) SubRace.Wildwood)?.Masculine.ToString() ?? SubRace.Wildwood.ToName();
|
||||
_names[(int) CustomName.WildwoodF] = subRace.GetRow((int) SubRace.Wildwood)?.Feminine.ToString() ?? SubRace.Wildwood.ToName();
|
||||
_names[(int) CustomName.DuskwightM] = subRace.GetRow((int) SubRace.Duskwight)?.Masculine.ToString() ?? SubRace.Duskwight.ToName();
|
||||
_names[(int) CustomName.DuskwightF] = subRace.GetRow((int) SubRace.Duskwight)?.Feminine.ToString() ?? SubRace.Duskwight.ToName();
|
||||
_names[(int) CustomName.PlainsfolkM] =
|
||||
subRace.GetRow((int) SubRace.Plainsfolk)?.Masculine.ToString() ?? SubRace.Plainsfolk.ToName();
|
||||
_names[(int) CustomName.PlainsfolkF] = subRace.GetRow((int) SubRace.Plainsfolk)?.Feminine.ToString() ?? SubRace.Plainsfolk.ToName();
|
||||
_names[(int) CustomName.DunesfolkM] = subRace.GetRow((int) SubRace.Dunesfolk)?.Masculine.ToString() ?? SubRace.Dunesfolk.ToName();
|
||||
_names[(int) CustomName.DunesfolkF] = subRace.GetRow((int) SubRace.Dunesfolk)?.Feminine.ToString() ?? SubRace.Dunesfolk.ToName();
|
||||
_names[(int) CustomName.SeekerOfTheSunM] =
|
||||
subRace.GetRow((int) SubRace.SeekerOfTheSun)?.Masculine.ToString() ?? SubRace.SeekerOfTheSun.ToName();
|
||||
_names[(int) CustomName.SeekerOfTheSunF] =
|
||||
subRace.GetRow((int) SubRace.SeekerOfTheSun)?.Feminine.ToString() ?? SubRace.SeekerOfTheSun.ToName();
|
||||
_names[(int) CustomName.KeeperOfTheMoonM] =
|
||||
subRace.GetRow((int) SubRace.KeeperOfTheMoon)?.Masculine.ToString() ?? SubRace.KeeperOfTheMoon.ToName();
|
||||
_names[(int) CustomName.KeeperOfTheMoonF] =
|
||||
subRace.GetRow((int) SubRace.KeeperOfTheMoon)?.Feminine.ToString() ?? SubRace.KeeperOfTheMoon.ToName();
|
||||
_names[(int) CustomName.SeawolfM] = subRace.GetRow((int) SubRace.Seawolf)?.Masculine.ToString() ?? SubRace.Seawolf.ToName();
|
||||
_names[(int) CustomName.SeawolfF] = subRace.GetRow((int) SubRace.Seawolf)?.Feminine.ToString() ?? SubRace.Seawolf.ToName();
|
||||
_names[(int) CustomName.HellsguardM] =
|
||||
subRace.GetRow((int) SubRace.Hellsguard)?.Masculine.ToString() ?? SubRace.Hellsguard.ToName();
|
||||
_names[(int) CustomName.HellsguardF] = subRace.GetRow((int) SubRace.Hellsguard)?.Feminine.ToString() ?? SubRace.Hellsguard.ToName();
|
||||
_names[(int) CustomName.RaenM] = subRace.GetRow((int) SubRace.Raen)?.Masculine.ToString() ?? SubRace.Raen.ToName();
|
||||
_names[(int) CustomName.RaenF] = subRace.GetRow((int) SubRace.Raen)?.Feminine.ToString() ?? SubRace.Raen.ToName();
|
||||
_names[(int) CustomName.XaelaM] = subRace.GetRow((int) SubRace.Xaela)?.Masculine.ToString() ?? SubRace.Xaela.ToName();
|
||||
_names[(int) CustomName.XaelaF] = subRace.GetRow((int) SubRace.Xaela)?.Feminine.ToString() ?? SubRace.Xaela.ToName();
|
||||
_names[(int) CustomName.HelionM] = subRace.GetRow((int) SubRace.Helion)?.Masculine.ToString() ?? SubRace.Helion.ToName();
|
||||
_names[(int) CustomName.HelionF] = subRace.GetRow((int) SubRace.Helion)?.Feminine.ToString() ?? SubRace.Helion.ToName();
|
||||
_names[(int) CustomName.LostM] = subRace.GetRow((int) SubRace.Lost)?.Masculine.ToString() ?? SubRace.Lost.ToName();
|
||||
_names[(int) CustomName.LostF] = subRace.GetRow((int) SubRace.Lost)?.Feminine.ToString() ?? SubRace.Lost.ToName();
|
||||
_names[(int) CustomName.RavaM] = subRace.GetRow((int) SubRace.Rava)?.Masculine.ToString() ?? SubRace.Rava.ToName();
|
||||
_names[(int) CustomName.RavaF] = subRace.GetRow((int) SubRace.Rava)?.Feminine.ToString() ?? SubRace.Rava.ToName();
|
||||
_names[(int) CustomName.VeenaM] = subRace.GetRow((int) SubRace.Veena)?.Masculine.ToString() ?? SubRace.Veena.ToName();
|
||||
_names[(int) CustomName.VeenaF] = subRace.GetRow((int) SubRace.Veena)?.Feminine.ToString() ?? SubRace.Veena.ToName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ImGuiScene;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Customization
|
||||
|
|
@ -11,7 +10,6 @@ namespace Glamourer.Customization
|
|||
public const int DefaultAvailable =
|
||||
(1 << (int) CustomizationId.Height)
|
||||
| (1 << (int) CustomizationId.Hairstyle)
|
||||
| (1 << (int) CustomizationId.HighlightsOnFlag)
|
||||
| (1 << (int) CustomizationId.SkinColor)
|
||||
| (1 << (int) CustomizationId.EyeColorR)
|
||||
| (1 << (int) CustomizationId.EyeColorL)
|
||||
|
|
@ -54,6 +52,7 @@ namespace Glamourer.Customization
|
|||
public int NumMouthShapes { get; internal set; }
|
||||
|
||||
|
||||
public IReadOnlyList<string> OptionName { get; internal set; } = null!;
|
||||
public IReadOnlyList<Customization> Faces { get; internal set; } = null!;
|
||||
public IReadOnlyList<Customization> HairStyles { get; internal set; } = null!;
|
||||
public IReadOnlyList<Customization> TailEarShapes { get; internal set; } = null!;
|
||||
|
|
@ -70,7 +69,10 @@ namespace Glamourer.Customization
|
|||
public IReadOnlyList<Customization> LipColorsLight { get; internal set; } = null!;
|
||||
public IReadOnlyList<Customization> LipColorsDark { get; internal set; } = null!;
|
||||
|
||||
public IReadOnlyDictionary<CustomizationId, string> OptionName { get; internal set; } = null!;
|
||||
public IReadOnlyList<CharaMakeParams.MenuType> _types { get; internal set; } = null!;
|
||||
|
||||
public string Option(CustomizationId id)
|
||||
=> OptionName[(int) id];
|
||||
|
||||
public Customization FacialFeature(int faceIdx, int idx)
|
||||
=> FeaturesTattoos[faceIdx - 1][idx];
|
||||
|
|
@ -151,6 +153,10 @@ namespace Glamourer.Customization
|
|||
};
|
||||
}
|
||||
|
||||
public CharaMakeParams.MenuType Type(CustomizationId id)
|
||||
=> _types[(int) id];
|
||||
|
||||
|
||||
public int Count(CustomizationId id)
|
||||
{
|
||||
if (!IsAvailable(id))
|
||||
|
|
|
|||
|
|
@ -1,247 +0,0 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Customization
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct CustomizationStruct
|
||||
{
|
||||
public CustomizationStruct(IntPtr ptr)
|
||||
=> _ptr = (byte*) ptr;
|
||||
|
||||
private readonly byte* _ptr;
|
||||
|
||||
public byte this[CustomizationId id]
|
||||
{
|
||||
get => id switch
|
||||
{
|
||||
// Needs to handle the Highlander Race in enum.
|
||||
CustomizationId.Race => (byte) (_ptr[(int) CustomizationId.Race] > 1 ? _ptr[(int) CustomizationId.Race] + 1 : 1),
|
||||
// Needs to handle Gender.Unknown = 0.
|
||||
CustomizationId.Gender => (byte) (_ptr[(int) id] + 1),
|
||||
// Just a flag.
|
||||
CustomizationId.HighlightsOnFlag => (byte) ((_ptr[(int) id] & 128) == 128 ? 1 : 0),
|
||||
// Eye also includes iris flag at bit 128.
|
||||
CustomizationId.EyeShape => (byte) (_ptr[(int) CustomizationId.EyeShape] & 127),
|
||||
// Mouth also includes Lipstick flag at bit 128.
|
||||
CustomizationId.Mouth => (byte) (_ptr[(int) CustomizationId.Mouth] & 127),
|
||||
// FacePaint also includes Reverse bit at 128.
|
||||
CustomizationId.FacePaint => (byte) (_ptr[(int) CustomizationId.FacePaint] & 127),
|
||||
_ => _ptr[(int) id],
|
||||
};
|
||||
set
|
||||
{
|
||||
_ptr[(int) id] = id switch
|
||||
{
|
||||
CustomizationId.Race => (byte) (value > (byte) Race.Midlander ? value - 1 : value),
|
||||
CustomizationId.Gender => (byte) (value - 1),
|
||||
CustomizationId.HighlightsOnFlag => (byte) (value != 0 ? 128 : 0),
|
||||
CustomizationId.EyeShape => (byte) ((_ptr[(int) CustomizationId.EyeShape] & 128) | (value & 127)),
|
||||
CustomizationId.Mouth => (byte) ((_ptr[(int) CustomizationId.Mouth] & 128) | (value & 127)),
|
||||
CustomizationId.FacePaint => (byte) ((_ptr[(int) CustomizationId.FacePaint] & 128) | (value & 127)),
|
||||
_ => value,
|
||||
};
|
||||
|
||||
if (Race != Race.Hrothgar)
|
||||
return;
|
||||
|
||||
// Handle Hrothgar Face being dependent on hairstyle, 2 faces per hairstyle.
|
||||
switch (id)
|
||||
{
|
||||
case CustomizationId.Hairstyle:
|
||||
_ptr[(int) CustomizationId.Face] = (byte) ((value + 1) / 2);
|
||||
break;
|
||||
case CustomizationId.Face:
|
||||
_ptr[(int) CustomizationId.Hairstyle] = (byte) (value * 2 - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Race Race
|
||||
{
|
||||
get => (Race) this[CustomizationId.Race];
|
||||
set => this[CustomizationId.Race] = (byte) value;
|
||||
}
|
||||
|
||||
public Gender Gender
|
||||
{
|
||||
get => (Gender) this[CustomizationId.Gender];
|
||||
set => this[CustomizationId.Gender] = (byte) value;
|
||||
}
|
||||
|
||||
public byte BodyType
|
||||
{
|
||||
get => this[CustomizationId.BodyType];
|
||||
set => this[CustomizationId.BodyType] = value;
|
||||
}
|
||||
|
||||
public byte Height
|
||||
{
|
||||
get => _ptr[(int) CustomizationId.Height];
|
||||
set => _ptr[(int) CustomizationId.Height] = value;
|
||||
}
|
||||
|
||||
public SubRace Clan
|
||||
{
|
||||
get => (SubRace) this[CustomizationId.Clan];
|
||||
set => this[CustomizationId.Clan] = (byte) value;
|
||||
}
|
||||
|
||||
public byte Face
|
||||
{
|
||||
get => this[CustomizationId.Face];
|
||||
set => this[CustomizationId.Face] = value;
|
||||
}
|
||||
|
||||
public byte Hairstyle
|
||||
{
|
||||
get => this[CustomizationId.Hairstyle];
|
||||
set => this[CustomizationId.Hairstyle] = value;
|
||||
}
|
||||
|
||||
public bool HighlightsOn
|
||||
{
|
||||
get => this[CustomizationId.HighlightsOnFlag] == 1;
|
||||
set => this[CustomizationId.HighlightsOnFlag] = (byte) (value ? 1 : 0);
|
||||
}
|
||||
|
||||
public byte SkinColor
|
||||
{
|
||||
get => this[CustomizationId.SkinColor];
|
||||
set => this[CustomizationId.SkinColor] = value;
|
||||
}
|
||||
|
||||
public byte EyeColorRight
|
||||
{
|
||||
get => this[CustomizationId.EyeColorR];
|
||||
set => this[CustomizationId.EyeColorR] = value;
|
||||
}
|
||||
|
||||
public byte HairColor
|
||||
{
|
||||
get => this[CustomizationId.HairColor];
|
||||
set => this[CustomizationId.HairColor] = value;
|
||||
}
|
||||
|
||||
public byte HighlightsColor
|
||||
{
|
||||
get => this[CustomizationId.HighlightColor];
|
||||
set => this[CustomizationId.HighlightColor] = value;
|
||||
}
|
||||
|
||||
public bool FacialFeature(int idx)
|
||||
=> (this[CustomizationId.FacialFeaturesTattoos] & (1 << idx)) != 0;
|
||||
|
||||
public void FacialFeature(int idx, bool set)
|
||||
{
|
||||
if (set)
|
||||
this[CustomizationId.FacialFeaturesTattoos] |= (byte) (1 << idx);
|
||||
else
|
||||
this[CustomizationId.FacialFeaturesTattoos] &= (byte) ~(1 << idx);
|
||||
}
|
||||
|
||||
public byte FacialFeatures
|
||||
{
|
||||
get => this[CustomizationId.FacialFeaturesTattoos];
|
||||
set => this[CustomizationId.FacialFeaturesTattoos] = value;
|
||||
}
|
||||
|
||||
public byte TattooColor
|
||||
{
|
||||
get => this[CustomizationId.TattooColor];
|
||||
set => this[CustomizationId.TattooColor] = value;
|
||||
}
|
||||
|
||||
public byte Eyebrow
|
||||
{
|
||||
get => this[CustomizationId.Eyebrows];
|
||||
set => this[CustomizationId.Eyebrows] = value;
|
||||
}
|
||||
|
||||
public byte EyeColorLeft
|
||||
{
|
||||
get => this[CustomizationId.EyeColorL];
|
||||
set => this[CustomizationId.EyeColorL] = value;
|
||||
}
|
||||
|
||||
public byte Eye
|
||||
{
|
||||
get => this[CustomizationId.EyeShape];
|
||||
set => this[CustomizationId.EyeShape] = value;
|
||||
}
|
||||
|
||||
public bool SmallIris
|
||||
{
|
||||
get => (_ptr[(int) CustomizationId.EyeShape] & 128) == 128;
|
||||
set => _ptr[(int) CustomizationId.EyeShape] = (byte) (this[CustomizationId.EyeShape] | (value ? 128u : 0u));
|
||||
}
|
||||
|
||||
public byte Nose
|
||||
{
|
||||
get => _ptr[(int) CustomizationId.Nose];
|
||||
set => _ptr[(int) CustomizationId.Nose] = value;
|
||||
}
|
||||
|
||||
public byte Jaw
|
||||
{
|
||||
get => _ptr[(int) CustomizationId.Jaw];
|
||||
set => _ptr[(int) CustomizationId.Jaw] = value;
|
||||
}
|
||||
|
||||
public byte Mouth
|
||||
{
|
||||
get => this[CustomizationId.Mouth];
|
||||
set => this[CustomizationId.Mouth] = value;
|
||||
}
|
||||
|
||||
public bool LipstickSet
|
||||
{
|
||||
get => (_ptr[(int) CustomizationId.Mouth] & 128) == 128;
|
||||
set => _ptr[(int) CustomizationId.Mouth] = (byte) (this[CustomizationId.Mouth] | (value ? 128u : 0u));
|
||||
}
|
||||
|
||||
public byte LipColor
|
||||
{
|
||||
get => _ptr[(int) CustomizationId.LipColor];
|
||||
set => _ptr[(int) CustomizationId.LipColor] = value;
|
||||
}
|
||||
|
||||
public byte MuscleMass
|
||||
{
|
||||
get => _ptr[(int) CustomizationId.MuscleToneOrTailEarLength];
|
||||
set => _ptr[(int) CustomizationId.MuscleToneOrTailEarLength] = value;
|
||||
}
|
||||
|
||||
public byte TailShape
|
||||
{
|
||||
get => _ptr[(int) CustomizationId.TailEarShape];
|
||||
set => _ptr[(int) CustomizationId.TailEarShape] = value;
|
||||
}
|
||||
|
||||
public byte BustSize
|
||||
{
|
||||
get => _ptr[(int) CustomizationId.BustSize];
|
||||
set => _ptr[(int) CustomizationId.BustSize] = value;
|
||||
}
|
||||
|
||||
public byte FacePaint
|
||||
{
|
||||
get => this[CustomizationId.FacePaint];
|
||||
set => this[CustomizationId.FacePaint] = value;
|
||||
}
|
||||
|
||||
public bool FacePaintReversed
|
||||
{
|
||||
get => (_ptr[(int) CustomizationId.FacePaint] & 128) == 128;
|
||||
set => _ptr[(int) CustomizationId.FacePaint] = (byte) (this[CustomizationId.FacePaint] | (value ? 128u : 0u));
|
||||
}
|
||||
|
||||
public byte FacePaintColor
|
||||
{
|
||||
get => _ptr[(int) CustomizationId.FacePaintColor];
|
||||
set => _ptr[(int) CustomizationId.FacePaintColor] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,5 +12,6 @@ namespace Glamourer.Customization
|
|||
public CustomizationSet GetList(SubRace race, Gender gender);
|
||||
|
||||
public ImGuiScene.TextureWrap GetIcon(uint iconId);
|
||||
public string GetName(CustomName name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue