mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 10:17:23 +01:00
Simple changes for 6.1
This commit is contained in:
parent
b141da40b0
commit
e6e033bb8b
8 changed files with 539 additions and 529 deletions
|
|
@ -2,123 +2,124 @@
|
||||||
using Lumina.Excel;
|
using Lumina.Excel;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
|
||||||
namespace Glamourer.Customization
|
namespace Glamourer.Customization;
|
||||||
|
|
||||||
|
[Sheet("CharaMakeParams")]
|
||||||
|
public class CharaMakeParams : ExcelRow
|
||||||
{
|
{
|
||||||
[Sheet("CharaMakeParams")]
|
public const int NumMenus = 28;
|
||||||
public class CharaMakeParams : ExcelRow
|
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
|
||||||
{
|
{
|
||||||
public const int NumMenus = 28;
|
ListSelector = 0,
|
||||||
public const int NumVoices = 12;
|
IconSelector = 1,
|
||||||
public const int NumGraphics = 10;
|
ColorPicker = 2,
|
||||||
public const int MaxNumValues = 100;
|
DoubleColorPicker = 3,
|
||||||
public const int NumFaces = 8;
|
MultiIconSelector = 4,
|
||||||
public const int NumFeatures = 7;
|
Percentage = 5,
|
||||||
public const int NumEquip = 3;
|
}
|
||||||
|
|
||||||
public enum MenuType
|
public struct Menu
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public byte InitVal;
|
||||||
|
public MenuType Type;
|
||||||
|
public byte Size;
|
||||||
|
public byte LookAt;
|
||||||
|
public uint Mask;
|
||||||
|
public CustomizationId Customization;
|
||||||
|
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.UnkData3347Obj[] Equip { get; set; } = new CharaMakeType.UnkData3347Obj[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);
|
||||||
|
for (var i = 0; i < NumMenus; ++i)
|
||||||
{
|
{
|
||||||
ListSelector = 0,
|
Menus[i].Id = parser.ReadColumn<uint>(3 + 0 * NumMenus + i);
|
||||||
IconSelector = 1,
|
Menus[i].InitVal = parser.ReadColumn<byte>(3 + 1 * NumMenus + i);
|
||||||
ColorPicker = 2,
|
Menus[i].Type = (MenuType)parser.ReadColumn<byte>(3 + 2 * NumMenus + i);
|
||||||
DoubleColorPicker = 3,
|
Menus[i].Size = parser.ReadColumn<byte>(3 + 3 * NumMenus + i);
|
||||||
MultiIconSelector = 4,
|
Menus[i].LookAt = parser.ReadColumn<byte>(3 + 4 * NumMenus + i);
|
||||||
Percentage = 5,
|
Menus[i].Mask = parser.ReadColumn<uint>(3 + 5 * NumMenus + i);
|
||||||
}
|
Menus[i].Customization = (CustomizationId)parser.ReadColumn<uint>(3 + 6 * NumMenus + i);
|
||||||
|
Menus[i].Values = new uint[Menus[i].Size];
|
||||||
|
|
||||||
public struct Menu
|
switch (Menus[i].Type)
|
||||||
{
|
|
||||||
public uint Id;
|
|
||||||
public byte InitVal;
|
|
||||||
public MenuType Type;
|
|
||||||
public byte Size;
|
|
||||||
public byte LookAt;
|
|
||||||
public uint Mask;
|
|
||||||
public CustomizationId Customization;
|
|
||||||
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);
|
|
||||||
for (var i = 0; i < NumMenus; ++i)
|
|
||||||
{
|
{
|
||||||
Menus[i].Id = parser.ReadColumn<uint>(3 + 0 * NumMenus + i);
|
case MenuType.ColorPicker:
|
||||||
Menus[i].InitVal = parser.ReadColumn<byte>(3 + 1 * NumMenus + i);
|
case MenuType.DoubleColorPicker:
|
||||||
Menus[i].Type = (MenuType) parser.ReadColumn<byte>(3 + 2 * NumMenus + i);
|
case MenuType.Percentage:
|
||||||
Menus[i].Size = parser.ReadColumn<byte>(3 + 3 * NumMenus + i);
|
break;
|
||||||
Menus[i].LookAt = parser.ReadColumn<byte>(3 + 4 * NumMenus + i);
|
default:
|
||||||
Menus[i].Mask = parser.ReadColumn<uint>(3 + 5 * NumMenus + i);
|
for (var j = 0; j < Menus[i].Size; ++j)
|
||||||
Menus[i].Customization = (CustomizationId) parser.ReadColumn<uint>(3 + 6 * NumMenus + i);
|
Menus[i].Values[j] = parser.ReadColumn<uint>(3 + (7 + j) * NumMenus + i);
|
||||||
Menus[i].Values = new uint[Menus[i].Size];
|
break;
|
||||||
|
|
||||||
switch (Menus[i].Type)
|
|
||||||
{
|
|
||||||
case MenuType.ColorPicker:
|
|
||||||
case MenuType.DoubleColorPicker:
|
|
||||||
case MenuType.Percentage:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
for (var j = 0; j < Menus[i].Size; ++j)
|
|
||||||
Menus[i].Values[j] = parser.ReadColumn<uint>(3 + (7 + j) * NumMenus + i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Menus[i].Graphic = new byte[NumGraphics];
|
|
||||||
for (var j = 0; j < NumGraphics; ++j)
|
|
||||||
Menus[i].Graphic[j] = parser.ReadColumn<byte>(3 + (MaxNumValues + 7 + j) * NumMenus + i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < NumVoices; ++i)
|
Menus[i].Graphic = new byte[NumGraphics];
|
||||||
Voices[i] = parser.ReadColumn<byte>(3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + i);
|
for (var j = 0; j < NumGraphics; ++j)
|
||||||
|
Menus[i].Graphic[j] = parser.ReadColumn<byte>(3 + (MaxNumValues + 7 + j) * NumMenus + i);
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < NumFaces; ++i)
|
for (var i = 0; i < NumVoices; ++i)
|
||||||
{
|
Voices[i] = parser.ReadColumn<byte>(3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + i);
|
||||||
FacialFeatureByFace[i].Icons = new uint[NumFeatures];
|
|
||||||
for (var j = 0; j < NumFeatures; ++j)
|
|
||||||
FacialFeatureByFace[i].Icons[j] =
|
|
||||||
(uint) parser.ReadColumn<int>(3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + NumVoices + j * NumFaces + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < NumEquip; ++i)
|
for (var i = 0; i < NumFaces; ++i)
|
||||||
|
{
|
||||||
|
FacialFeatureByFace[i].Icons = new uint[NumFeatures];
|
||||||
|
for (var j = 0; j < NumFeatures; ++j)
|
||||||
{
|
{
|
||||||
Equip[i] = new CharaMakeType.CharaMakeTypeUnkData3347Obj()
|
FacialFeatureByFace[i].Icons[j] =
|
||||||
{
|
(uint)parser.ReadColumn<int>(3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + NumVoices + j * NumFaces + i);
|
||||||
Helmet = parser.ReadColumn<ulong>(
|
|
||||||
3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + NumVoices + NumFaces * NumFeatures + i * 7 + 0),
|
|
||||||
Top = parser.ReadColumn<ulong>(
|
|
||||||
3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + NumVoices + NumFaces * NumFeatures + i * 7 + 1),
|
|
||||||
Gloves = parser.ReadColumn<ulong>(
|
|
||||||
3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + NumVoices + NumFaces * NumFeatures + i * 7 + 2),
|
|
||||||
Legs = parser.ReadColumn<ulong>(
|
|
||||||
3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + NumVoices + NumFaces * NumFeatures + i * 7 + 3),
|
|
||||||
Shoes = parser.ReadColumn<ulong>(
|
|
||||||
3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + NumVoices + NumFaces * NumFeatures + i * 7 + 4),
|
|
||||||
Weapon = parser.ReadColumn<ulong>(
|
|
||||||
3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + NumVoices + NumFaces * NumFeatures + i * 7 + 5),
|
|
||||||
SubWeapon = parser.ReadColumn<ulong>(
|
|
||||||
3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + NumVoices + NumFaces * NumFeatures + i * 7 + 6),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < NumEquip; ++i)
|
||||||
|
{
|
||||||
|
Equip[i] = new CharaMakeType.UnkData3347Obj()
|
||||||
|
{
|
||||||
|
Helmet = parser.ReadColumn<ulong>(
|
||||||
|
3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + NumVoices + NumFaces * NumFeatures + i * 7 + 0),
|
||||||
|
Top = parser.ReadColumn<ulong>(
|
||||||
|
3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + NumVoices + NumFaces * NumFeatures + i * 7 + 1),
|
||||||
|
Gloves = parser.ReadColumn<ulong>(
|
||||||
|
3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + NumVoices + NumFaces * NumFeatures + i * 7 + 2),
|
||||||
|
Legs = parser.ReadColumn<ulong>(
|
||||||
|
3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + NumVoices + NumFaces * NumFeatures + i * 7 + 3),
|
||||||
|
Shoes = parser.ReadColumn<ulong>(
|
||||||
|
3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + NumVoices + NumFaces * NumFeatures + i * 7 + 4),
|
||||||
|
Weapon = parser.ReadColumn<ulong>(
|
||||||
|
3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + NumVoices + NumFaces * NumFeatures + i * 7 + 5),
|
||||||
|
SubWeapon = parser.ReadColumn<ulong>(
|
||||||
|
3 + (MaxNumValues + 7 + NumGraphics) * NumMenus + NumVoices + NumFaces * NumFeatures + i * 7 + 6),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ namespace Glamourer.Customization
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
public struct CharacterCustomization
|
public struct CharacterCustomization
|
||||||
{
|
{
|
||||||
public const int CustomizationOffset = 0xDD8;
|
public const int CustomizationOffset = 0x830;
|
||||||
public const int CustomizationBytes = 26;
|
public const int CustomizationBytes = 26;
|
||||||
|
|
||||||
public static CharacterCustomization Default = new()
|
public static CharacterCustomization Default = new()
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ namespace Glamourer.Customization
|
||||||
if (set.Faces.Count > 0)
|
if (set.Faces.Count > 0)
|
||||||
set.SetAvailable(CustomizationId.Face);
|
set.SetAvailable(CustomizationId.Face);
|
||||||
|
|
||||||
var count = race.ToRace() == Race.Hrothgar ? set.HairStyles.Count : set.Faces.Count;
|
var count = set.Faces.Count;
|
||||||
var featureDict = new List<IReadOnlyList<Customization>>(count);
|
var featureDict = new List<IReadOnlyList<Customization>>(count);
|
||||||
for (var i = 0; i < count; ++i)
|
for (var i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,17 @@ namespace Glamourer;
|
||||||
|
|
||||||
public static class CharacterExtensions
|
public static class CharacterExtensions
|
||||||
{
|
{
|
||||||
public const int WetnessOffset = 0x19E4;
|
public const int WetnessOffset = 0x1ADA;
|
||||||
public const byte WetnessFlag = 0x08;
|
public const byte WetnessFlag = 0x80;
|
||||||
public const int StateFlagsOffset = 0xDF6;
|
public const int HatVisibleOffset = 0x84E;
|
||||||
public const byte HatHiddenFlag = 0x01;
|
public const int VisorToggledOffset = 0x84F;
|
||||||
public const byte VisorToggledFlag = 0x10;
|
public const byte HatHiddenFlag = 0x01;
|
||||||
public const int AlphaOffset = 0x18B8;
|
public const byte VisorToggledFlag = 0x08;
|
||||||
public const int WeaponHiddenOffset = 0xCD4;
|
public const int AlphaOffset = 0x19E0;
|
||||||
public const byte WeaponHiddenFlag = 0x02;
|
public const int WeaponHiddenOffset1 = 0x84F;
|
||||||
|
public const int WeaponHiddenOffset2 = 0x72C; // maybe
|
||||||
|
public const byte WeaponHiddenFlag1 = 0x01;
|
||||||
|
public const byte WeaponHiddenFlag2 = 0x02;
|
||||||
|
|
||||||
public static unsafe bool IsWet(this Character a)
|
public static unsafe bool IsWet(this Character a)
|
||||||
=> (*((byte*)a.Address + WetnessOffset) & WetnessFlag) != 0;
|
=> (*((byte*)a.Address + WetnessOffset) & WetnessFlag) != 0;
|
||||||
|
|
@ -29,49 +32,62 @@ public static class CharacterExtensions
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe ref byte StateFlags(this Character a)
|
public static unsafe bool IsHatVisible(this Character a)
|
||||||
=> ref *((byte*)a.Address + StateFlagsOffset);
|
=> (*((byte*)a.Address + HatVisibleOffset) & HatHiddenFlag) == 0;
|
||||||
|
|
||||||
public static bool SetStateFlag(this Character a, bool value, byte flag)
|
public static unsafe bool SetHatVisible(this Character a, bool visible)
|
||||||
{
|
{
|
||||||
var current = a.StateFlags();
|
var current = IsHatVisible(a);
|
||||||
var previousValue = (current & flag) != 0;
|
if (current == visible)
|
||||||
if (previousValue == value)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (value)
|
if (visible)
|
||||||
a.StateFlags() = (byte)(current | flag);
|
*((byte*)a.Address + HatVisibleOffset) = (byte)(*((byte*)a.Address + HatVisibleOffset) & ~HatHiddenFlag);
|
||||||
else
|
else
|
||||||
a.StateFlags() = (byte)(current & ~flag);
|
*((byte*)a.Address + HatVisibleOffset) = (byte)(*((byte*)a.Address + HatVisibleOffset) | HatHiddenFlag);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsHatHidden(this Character a)
|
public static unsafe bool IsVisorToggled(this Character a)
|
||||||
=> (a.StateFlags() & HatHiddenFlag) != 0;
|
=> (*((byte*)a.Address + VisorToggledOffset) & VisorToggledFlag) == VisorToggledFlag;
|
||||||
|
|
||||||
|
public static unsafe bool SetVisorToggled(this Character a, bool toggled)
|
||||||
|
{
|
||||||
|
var current = IsVisorToggled(a);
|
||||||
|
if (current == toggled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (toggled)
|
||||||
|
*((byte*)a.Address + VisorToggledOffset) = (byte)(*((byte*)a.Address + VisorToggledOffset) | VisorToggledFlag);
|
||||||
|
else
|
||||||
|
*((byte*)a.Address + VisorToggledOffset) = (byte)(*((byte*)a.Address + VisorToggledOffset) & ~VisorToggledFlag);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static unsafe bool IsWeaponHidden(this Character a)
|
public static unsafe bool IsWeaponHidden(this Character a)
|
||||||
=> (a.StateFlags() & WeaponHiddenFlag) != 0
|
=> (*((byte*)a.Address + WeaponHiddenOffset1) & WeaponHiddenFlag1) == WeaponHiddenFlag1
|
||||||
&& (*((byte*)a.Address + WeaponHiddenOffset) & WeaponHiddenFlag) != 0;
|
&& (*((byte*)a.Address + WeaponHiddenOffset2) & WeaponHiddenFlag2) == WeaponHiddenFlag2;
|
||||||
|
|
||||||
public static bool IsVisorToggled(this Character a)
|
|
||||||
=> (a.StateFlags() & VisorToggledFlag) != 0;
|
|
||||||
|
|
||||||
public static bool SetHatHidden(this Character a, bool value)
|
|
||||||
=> SetStateFlag(a, value, HatHiddenFlag);
|
|
||||||
|
|
||||||
public static unsafe bool SetWeaponHidden(this Character a, bool value)
|
public static unsafe bool SetWeaponHidden(this Character a, bool value)
|
||||||
{
|
{
|
||||||
var ret = SetStateFlag(a, value, WeaponHiddenFlag);
|
var hidden = IsWeaponHidden(a);
|
||||||
var val = *((byte*)a.Address + WeaponHiddenOffset);
|
if (hidden == value)
|
||||||
if (value)
|
return false;
|
||||||
*((byte*)a.Address + WeaponHiddenOffset) = (byte)(val | WeaponHiddenFlag);
|
|
||||||
else
|
|
||||||
*((byte*)a.Address + WeaponHiddenOffset) = (byte)(val & ~WeaponHiddenFlag);
|
|
||||||
return ret || (val & WeaponHiddenFlag) != 0 != value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool SetVisorToggled(this Character a, bool value)
|
var val1 = *((byte*)a.Address + WeaponHiddenOffset1);
|
||||||
=> SetStateFlag(a, value, VisorToggledFlag);
|
var val2 = *((byte*)a.Address + WeaponHiddenOffset2);
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
*((byte*)a.Address + WeaponHiddenOffset1) = (byte)(val1 | WeaponHiddenFlag1);
|
||||||
|
*((byte*)a.Address + WeaponHiddenOffset2) = (byte)(val2 | WeaponHiddenFlag2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*((byte*)a.Address + WeaponHiddenOffset1) = (byte)(val1 & ~WeaponHiddenFlag1);
|
||||||
|
*((byte*)a.Address + WeaponHiddenOffset2) = (byte)(val2 & ~WeaponHiddenFlag2);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static unsafe ref float Alpha(this Character a)
|
public static unsafe ref float Alpha(this Character a)
|
||||||
=> ref *(float*)((byte*)a.Address + AlphaOffset);
|
=> ref *(float*)((byte*)a.Address + AlphaOffset);
|
||||||
|
|
|
||||||
|
|
@ -8,394 +8,386 @@ using Newtonsoft.Json.Linq;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
namespace Glamourer
|
namespace Glamourer;
|
||||||
|
|
||||||
|
public class CharacterSaveConverter : JsonConverter
|
||||||
{
|
{
|
||||||
public class CharacterSaveConverter : JsonConverter
|
public override bool CanConvert(Type objectType)
|
||||||
|
=> objectType == typeof(CharacterSave);
|
||||||
|
|
||||||
|
public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
public override bool CanConvert(Type objectType)
|
var token = JToken.Load(reader);
|
||||||
=> objectType == typeof(CharacterSave);
|
var s = token.ToObject<string>();
|
||||||
|
return CharacterSave.FromString(s!);
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
var token = JToken.Load(reader);
|
|
||||||
var s = token.ToObject<string>();
|
|
||||||
return CharacterSave.FromString(s!);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanWrite
|
|
||||||
=> true;
|
|
||||||
|
|
||||||
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
if (value != null)
|
|
||||||
{
|
|
||||||
var s = ((CharacterSave) value).ToBase64();
|
|
||||||
serializer.Serialize(writer, s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonConverter(typeof(CharacterSaveConverter))]
|
public override bool CanWrite
|
||||||
public class CharacterSave
|
=> true;
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
public const byte CurrentVersion = 2;
|
if (value != null)
|
||||||
public const byte TotalSizeVersion1 = 1 + 1 + 2 + 56 + CharacterCustomization.CustomizationBytes;
|
|
||||||
public const byte TotalSizeVersion2 = 1 + 1 + 2 + 56 + CharacterCustomization.CustomizationBytes + 4 + 1;
|
|
||||||
|
|
||||||
public const byte TotalSize = TotalSizeVersion2;
|
|
||||||
|
|
||||||
private readonly byte[] _bytes = new byte[TotalSize];
|
|
||||||
|
|
||||||
public CharacterSave()
|
|
||||||
{
|
{
|
||||||
_bytes[0] = CurrentVersion;
|
var s = ((CharacterSave)value).ToBase64();
|
||||||
Alpha = 1.0f;
|
serializer.Serialize(writer, s);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public CharacterSave Copy()
|
}
|
||||||
{
|
|
||||||
var ret = new CharacterSave();
|
[JsonConverter(typeof(CharacterSaveConverter))]
|
||||||
_bytes.CopyTo((Span<byte>) ret._bytes);
|
public class CharacterSave
|
||||||
return ret;
|
{
|
||||||
}
|
public const byte CurrentVersion = 2;
|
||||||
|
public const byte TotalSizeVersion1 = 1 + 1 + 2 + 56 + CharacterCustomization.CustomizationBytes;
|
||||||
public byte Version
|
public const byte TotalSizeVersion2 = 1 + 1 + 2 + 56 + CharacterCustomization.CustomizationBytes + 4 + 1;
|
||||||
=> _bytes[0];
|
|
||||||
|
public const byte TotalSize = TotalSizeVersion2;
|
||||||
public bool WriteCustomizations
|
|
||||||
{
|
private readonly byte[] _bytes = new byte[TotalSize];
|
||||||
get => (_bytes[1] & 0x01) != 0;
|
|
||||||
set => _bytes[1] = (byte) (value ? _bytes[1] | 0x01 : _bytes[1] & ~0x01);
|
public CharacterSave()
|
||||||
}
|
{
|
||||||
|
_bytes[0] = CurrentVersion;
|
||||||
public bool IsWet
|
Alpha = 1.0f;
|
||||||
{
|
}
|
||||||
get => (_bytes[1] & 0x02) != 0;
|
|
||||||
set => _bytes[1] = (byte) (value ? _bytes[1] | 0x02 : _bytes[1] & ~0x02);
|
public CharacterSave Copy()
|
||||||
}
|
{
|
||||||
|
var ret = new CharacterSave();
|
||||||
public bool SetHatState
|
_bytes.CopyTo((Span<byte>)ret._bytes);
|
||||||
{
|
return ret;
|
||||||
get => (_bytes[1] & 0x04) != 0;
|
}
|
||||||
set => _bytes[1] = (byte) (value ? _bytes[1] | 0x04 : _bytes[1] & ~0x04);
|
|
||||||
}
|
public byte Version
|
||||||
|
=> _bytes[0];
|
||||||
public bool SetWeaponState
|
|
||||||
{
|
public bool WriteCustomizations
|
||||||
get => (_bytes[1] & 0x08) != 0;
|
{
|
||||||
set => _bytes[1] = (byte) (value ? _bytes[1] | 0x08 : _bytes[1] & ~0x08);
|
get => (_bytes[1] & 0x01) != 0;
|
||||||
}
|
set => _bytes[1] = (byte)(value ? _bytes[1] | 0x01 : _bytes[1] & ~0x01);
|
||||||
|
}
|
||||||
public bool SetVisorState
|
|
||||||
{
|
public bool IsWet
|
||||||
get => (_bytes[1] & 0x10) != 0;
|
{
|
||||||
set => _bytes[1] = (byte) (value ? _bytes[1] | 0x10 : _bytes[1] & ~0x10);
|
get => (_bytes[1] & 0x02) != 0;
|
||||||
}
|
set => _bytes[1] = (byte)(value ? _bytes[1] | 0x02 : _bytes[1] & ~0x02);
|
||||||
|
}
|
||||||
public bool WriteProtected
|
|
||||||
{
|
public bool SetHatState
|
||||||
get => (_bytes[1] & 0x20) != 0;
|
{
|
||||||
set => _bytes[1] = (byte) (value ? _bytes[1] | 0x20 : _bytes[1] & ~0x20);
|
get => (_bytes[1] & 0x04) != 0;
|
||||||
}
|
set => _bytes[1] = (byte)(value ? _bytes[1] | 0x04 : _bytes[1] & ~0x04);
|
||||||
|
}
|
||||||
public byte StateFlags
|
|
||||||
{
|
public bool SetWeaponState
|
||||||
get => _bytes[64 + CharacterCustomization.CustomizationBytes];
|
{
|
||||||
set => _bytes[64 + CharacterCustomization.CustomizationBytes] = value;
|
get => (_bytes[1] & 0x08) != 0;
|
||||||
}
|
set => _bytes[1] = (byte)(value ? _bytes[1] | 0x08 : _bytes[1] & ~0x08);
|
||||||
|
}
|
||||||
public bool HatState
|
|
||||||
{
|
public bool SetVisorState
|
||||||
get => (StateFlags & 0x01) == 0;
|
{
|
||||||
set => StateFlags = (byte) (value ? StateFlags & ~0x01 : StateFlags | 0x01);
|
get => (_bytes[1] & 0x10) != 0;
|
||||||
}
|
set => _bytes[1] = (byte)(value ? _bytes[1] | 0x10 : _bytes[1] & ~0x10);
|
||||||
|
}
|
||||||
public bool VisorState
|
|
||||||
{
|
public bool WriteProtected
|
||||||
get => (StateFlags & 0x10) != 0;
|
{
|
||||||
set => StateFlags = (byte) (value ? StateFlags | 0x10 : StateFlags & ~0x10);
|
get => (_bytes[1] & 0x20) != 0;
|
||||||
}
|
set => _bytes[1] = (byte)(value ? _bytes[1] | 0x20 : _bytes[1] & ~0x20);
|
||||||
|
}
|
||||||
public bool WeaponState
|
|
||||||
{
|
public byte StateFlags
|
||||||
get => (StateFlags & 0x02) == 0;
|
{
|
||||||
set => StateFlags = (byte) (value ? StateFlags & ~0x02 : StateFlags | 0x02);
|
get => _bytes[64 + CharacterCustomization.CustomizationBytes];
|
||||||
}
|
set => _bytes[64 + CharacterCustomization.CustomizationBytes] = value;
|
||||||
|
}
|
||||||
public CharacterEquipMask WriteEquipment
|
|
||||||
{
|
public bool HatState
|
||||||
get => (CharacterEquipMask) (_bytes[2] | (_bytes[3] << 8));
|
{
|
||||||
set
|
get => (StateFlags & 0x01) == 0;
|
||||||
{
|
set => StateFlags = (byte)(value ? StateFlags & ~0x01 : StateFlags | 0x01);
|
||||||
_bytes[2] = (byte) ((ushort) value & 0xFF);
|
}
|
||||||
_bytes[3] = (byte) ((ushort) value >> 8);
|
|
||||||
}
|
public bool VisorState
|
||||||
}
|
{
|
||||||
|
get => (StateFlags & 0x10) != 0;
|
||||||
private static Dictionary<EquipSlot, (int, int, bool)> Offsets()
|
set => StateFlags = (byte)(value ? StateFlags | 0x10 : StateFlags & ~0x10);
|
||||||
{
|
}
|
||||||
var stainOffsetWeapon = (int) Marshal.OffsetOf<CharacterWeapon>("Stain");
|
|
||||||
var stainOffsetEquip = (int) Marshal.OffsetOf<CharacterArmor>("Stain");
|
public bool WeaponState
|
||||||
|
{
|
||||||
(int, int, bool) ToOffsets(IntPtr offset, bool weapon)
|
get => (StateFlags & 0x02) == 0;
|
||||||
{
|
set => StateFlags = (byte)(value ? StateFlags & ~0x02 : StateFlags | 0x02);
|
||||||
var off = 4 + CharacterCustomization.CustomizationBytes + (int) offset;
|
}
|
||||||
return weapon ? (off, off + stainOffsetWeapon, weapon) : (off, off + stainOffsetEquip, weapon);
|
|
||||||
}
|
public CharacterEquipMask WriteEquipment
|
||||||
|
{
|
||||||
return new Dictionary<EquipSlot, (int, int, bool)>(12)
|
get => (CharacterEquipMask)(_bytes[2] | (_bytes[3] << 8));
|
||||||
{
|
set
|
||||||
[EquipSlot.MainHand] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("MainHand"), true),
|
{
|
||||||
[EquipSlot.OffHand] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("OffHand"), true),
|
_bytes[2] = (byte)((ushort)value & 0xFF);
|
||||||
[EquipSlot.Head] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Head"), false),
|
_bytes[3] = (byte)((ushort)value >> 8);
|
||||||
[EquipSlot.Body] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Body"), false),
|
}
|
||||||
[EquipSlot.Hands] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Hands"), false),
|
}
|
||||||
[EquipSlot.Legs] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Legs"), false),
|
|
||||||
[EquipSlot.Feet] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Feet"), false),
|
private static Dictionary<EquipSlot, (int, int, bool)> Offsets()
|
||||||
[EquipSlot.Ears] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Ears"), false),
|
{
|
||||||
[EquipSlot.Neck] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Neck"), false),
|
var stainOffsetWeapon = (int)Marshal.OffsetOf<CharacterWeapon>("Stain");
|
||||||
[EquipSlot.Wrists] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Wrists"), false),
|
var stainOffsetEquip = (int)Marshal.OffsetOf<CharacterArmor>("Stain");
|
||||||
[EquipSlot.RFinger] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("RFinger"), false),
|
|
||||||
[EquipSlot.LFinger] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("LFinger"), false),
|
(int, int, bool) ToOffsets(IntPtr offset, bool weapon)
|
||||||
};
|
{
|
||||||
}
|
var off = 4 + CharacterCustomization.CustomizationBytes + (int)offset;
|
||||||
|
return weapon ? (off, off + stainOffsetWeapon, weapon) : (off, off + stainOffsetEquip, weapon);
|
||||||
private static readonly IReadOnlyDictionary<EquipSlot, (int, int, bool)> FieldOffsets = Offsets();
|
}
|
||||||
|
|
||||||
public bool WriteStain(EquipSlot slot, StainId stainId)
|
return new Dictionary<EquipSlot, (int, int, bool)>(12)
|
||||||
{
|
{
|
||||||
if (WriteProtected)
|
[EquipSlot.MainHand] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("MainHand"), true),
|
||||||
return false;
|
[EquipSlot.OffHand] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("OffHand"), true),
|
||||||
|
[EquipSlot.Head] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Head"), false),
|
||||||
var (_, stainOffset, _) = FieldOffsets[slot];
|
[EquipSlot.Body] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Body"), false),
|
||||||
if (_bytes[stainOffset] == (byte) stainId)
|
[EquipSlot.Hands] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Hands"), false),
|
||||||
return false;
|
[EquipSlot.Legs] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Legs"), false),
|
||||||
|
[EquipSlot.Feet] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Feet"), false),
|
||||||
_bytes[stainOffset] = stainId.Value;
|
[EquipSlot.Ears] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Ears"), false),
|
||||||
return true;
|
[EquipSlot.Neck] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Neck"), false),
|
||||||
}
|
[EquipSlot.Wrists] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Wrists"), false),
|
||||||
|
[EquipSlot.RFinger] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("RFinger"), false),
|
||||||
private bool WriteItem(int offset, SetId id, WeaponType type, ushort variant, bool weapon)
|
[EquipSlot.LFinger] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("LFinger"), false),
|
||||||
{
|
};
|
||||||
var idBytes = BitConverter.GetBytes(id.Value);
|
}
|
||||||
|
|
||||||
static bool WriteIfDifferent(ref byte x, byte y)
|
private static readonly IReadOnlyDictionary<EquipSlot, (int, int, bool)> FieldOffsets = Offsets();
|
||||||
{
|
|
||||||
if (x == y)
|
public bool WriteStain(EquipSlot slot, StainId stainId)
|
||||||
return false;
|
{
|
||||||
|
if (WriteProtected)
|
||||||
x = y;
|
return false;
|
||||||
return true;
|
|
||||||
}
|
var (_, stainOffset, _) = FieldOffsets[slot];
|
||||||
|
if (_bytes[stainOffset] == (byte)stainId)
|
||||||
var ret = WriteIfDifferent(ref _bytes[offset], idBytes[0]);
|
return false;
|
||||||
ret |= WriteIfDifferent(ref _bytes[offset + 1], idBytes[1]);
|
|
||||||
if (weapon)
|
_bytes[stainOffset] = stainId.Value;
|
||||||
{
|
return true;
|
||||||
var typeBytes = BitConverter.GetBytes(type.Value);
|
}
|
||||||
var variantBytes = BitConverter.GetBytes(variant);
|
|
||||||
ret |= WriteIfDifferent(ref _bytes[offset + 2], typeBytes[0]);
|
private bool WriteItem(int offset, SetId id, WeaponType type, ushort variant, bool weapon)
|
||||||
ret |= WriteIfDifferent(ref _bytes[offset + 3], typeBytes[1]);
|
{
|
||||||
ret |= WriteIfDifferent(ref _bytes[offset + 4], variantBytes[0]);
|
var idBytes = BitConverter.GetBytes(id.Value);
|
||||||
ret |= WriteIfDifferent(ref _bytes[offset + 5], variantBytes[1]);
|
|
||||||
}
|
static bool WriteIfDifferent(ref byte x, byte y)
|
||||||
else
|
{
|
||||||
{
|
if (x == y)
|
||||||
ret |= WriteIfDifferent(ref _bytes[offset + 2], (byte) variant);
|
return false;
|
||||||
}
|
|
||||||
|
x = y;
|
||||||
return ret;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool WriteItem(Item item)
|
var ret = WriteIfDifferent(ref _bytes[offset], idBytes[0]);
|
||||||
{
|
ret |= WriteIfDifferent(ref _bytes[offset + 1], idBytes[1]);
|
||||||
if (WriteProtected)
|
if (weapon)
|
||||||
return false;
|
{
|
||||||
|
var typeBytes = BitConverter.GetBytes(type.Value);
|
||||||
var (itemOffset, _, isWeapon) = FieldOffsets[item.EquippableTo];
|
var variantBytes = BitConverter.GetBytes(variant);
|
||||||
var (id, type, variant) = item.MainModel;
|
ret |= WriteIfDifferent(ref _bytes[offset + 2], typeBytes[0]);
|
||||||
var ret = WriteItem(itemOffset, id, type, variant, isWeapon);
|
ret |= WriteIfDifferent(ref _bytes[offset + 3], typeBytes[1]);
|
||||||
if (item.EquippableTo == EquipSlot.MainHand && item.HasSubModel)
|
ret |= WriteIfDifferent(ref _bytes[offset + 4], variantBytes[0]);
|
||||||
{
|
ret |= WriteIfDifferent(ref _bytes[offset + 5], variantBytes[1]);
|
||||||
var (subOffset, _, _) = FieldOffsets[EquipSlot.OffHand];
|
}
|
||||||
var (subId, subType, subVariant) = item.SubModel;
|
else
|
||||||
ret |= WriteItem(subOffset, subId, subType, subVariant, true);
|
{
|
||||||
}
|
ret |= WriteIfDifferent(ref _bytes[offset + 2], (byte)variant);
|
||||||
|
}
|
||||||
return ret;
|
|
||||||
}
|
return ret;
|
||||||
|
}
|
||||||
public unsafe float Alpha
|
|
||||||
{
|
public bool WriteItem(Item item)
|
||||||
get
|
{
|
||||||
{
|
if (WriteProtected)
|
||||||
fixed (byte* ptr = &_bytes[60 + CharacterCustomization.CustomizationBytes])
|
return false;
|
||||||
{
|
|
||||||
return *(float*) ptr;
|
var (itemOffset, _, isWeapon) = FieldOffsets[item.EquippableTo];
|
||||||
}
|
var (id, type, variant) = item.MainModel;
|
||||||
}
|
var ret = WriteItem(itemOffset, id, type, variant, isWeapon);
|
||||||
set
|
if (item.EquippableTo == EquipSlot.MainHand && item.HasSubModel)
|
||||||
{
|
{
|
||||||
fixed (byte* ptr = _bytes)
|
var (subOffset, _, _) = FieldOffsets[EquipSlot.OffHand];
|
||||||
{
|
var (subId, subType, subVariant) = item.SubModel;
|
||||||
*(ptr + 60 + CharacterCustomization.CustomizationBytes + 0) = *((byte*) &value + 0);
|
ret |= WriteItem(subOffset, subId, subType, subVariant, true);
|
||||||
*(ptr + 60 + CharacterCustomization.CustomizationBytes + 1) = *((byte*) &value + 1);
|
}
|
||||||
*(ptr + 60 + CharacterCustomization.CustomizationBytes + 2) = *((byte*) &value + 2);
|
|
||||||
*(ptr + 60 + CharacterCustomization.CustomizationBytes + 3) = *((byte*) &value + 3);
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
public unsafe float Alpha
|
||||||
|
{
|
||||||
public void Load(CharacterCustomization customization)
|
get
|
||||||
{
|
{
|
||||||
WriteCustomizations = true;
|
fixed (byte* ptr = &_bytes[60 + CharacterCustomization.CustomizationBytes])
|
||||||
customization.WriteBytes(_bytes, 4);
|
{
|
||||||
}
|
return *(float*)ptr;
|
||||||
|
}
|
||||||
public void Load(CharacterEquipment equipment, CharacterEquipMask mask = CharacterEquipMask.All)
|
}
|
||||||
{
|
set
|
||||||
WriteEquipment = mask;
|
{
|
||||||
equipment.WriteBytes(_bytes, 4 + CharacterCustomization.CustomizationBytes);
|
fixed (byte* ptr = _bytes)
|
||||||
}
|
{
|
||||||
|
*(ptr + 60 + CharacterCustomization.CustomizationBytes + 0) = *((byte*)&value + 0);
|
||||||
public string ToBase64()
|
*(ptr + 60 + CharacterCustomization.CustomizationBytes + 1) = *((byte*)&value + 1);
|
||||||
=> Convert.ToBase64String(_bytes);
|
*(ptr + 60 + CharacterCustomization.CustomizationBytes + 2) = *((byte*)&value + 2);
|
||||||
|
*(ptr + 60 + CharacterCustomization.CustomizationBytes + 3) = *((byte*)&value + 3);
|
||||||
private static void CheckSize(int length, int requiredLength)
|
}
|
||||||
{
|
}
|
||||||
if (length != requiredLength)
|
}
|
||||||
throw new Exception(
|
|
||||||
$"Can not parse Base64 string into CharacterSave:\n\tInvalid size {length} instead of {requiredLength}.");
|
public void Load(CharacterCustomization customization)
|
||||||
}
|
{
|
||||||
|
WriteCustomizations = true;
|
||||||
private static void CheckRange(int idx, byte value, byte min, byte max)
|
customization.WriteBytes(_bytes, 4);
|
||||||
{
|
}
|
||||||
if (value < min || value > max)
|
|
||||||
throw new Exception(
|
public void Load(CharacterEquipment equipment, CharacterEquipMask mask = CharacterEquipMask.All)
|
||||||
$"Can not parse Base64 string into CharacterSave:\n\tInvalid value {value} in byte {idx}, should be in [{min},{max}].");
|
{
|
||||||
}
|
WriteEquipment = mask;
|
||||||
|
equipment.WriteBytes(_bytes, 4 + CharacterCustomization.CustomizationBytes);
|
||||||
private static void CheckCharacterMask(byte val1, byte val2)
|
}
|
||||||
{
|
|
||||||
var mask = (CharacterEquipMask) (val1 | (val2 << 8));
|
public string ToBase64()
|
||||||
if (mask > CharacterEquipMask.All)
|
=> Convert.ToBase64String(_bytes);
|
||||||
throw new Exception($"Can not parse Base64 string into CharacterSave:\n\tInvalid value {mask} in byte 3 and 4.");
|
|
||||||
}
|
private static void CheckSize(int length, int requiredLength)
|
||||||
|
{
|
||||||
public void LoadCharacter(Character a)
|
if (length != requiredLength)
|
||||||
{
|
throw new Exception(
|
||||||
WriteCustomizations = true;
|
$"Can not parse Base64 string into CharacterSave:\n\tInvalid size {length} instead of {requiredLength}.");
|
||||||
Load(new CharacterCustomization(a));
|
}
|
||||||
|
|
||||||
Load(new CharacterEquipment(a));
|
private static void CheckRange(int idx, byte value, byte min, byte max)
|
||||||
|
{
|
||||||
SetHatState = true;
|
if (value < min || value > max)
|
||||||
SetVisorState = true;
|
throw new Exception(
|
||||||
SetWeaponState = true;
|
$"Can not parse Base64 string into CharacterSave:\n\tInvalid value {value} in byte {idx}, should be in [{min},{max}].");
|
||||||
StateFlags = a.StateFlags();
|
}
|
||||||
|
|
||||||
IsWet = a.IsWet();
|
private static void CheckCharacterMask(byte val1, byte val2)
|
||||||
Alpha = a.Alpha();
|
{
|
||||||
}
|
var mask = (CharacterEquipMask)(val1 | (val2 << 8));
|
||||||
|
if (mask > CharacterEquipMask.All)
|
||||||
|
throw new Exception($"Can not parse Base64 string into CharacterSave:\n\tInvalid value {mask} in byte 3 and 4.");
|
||||||
public void Apply(Character a)
|
}
|
||||||
{
|
|
||||||
Glamourer.RevertableDesigns.Add(a);
|
public void LoadCharacter(Character a)
|
||||||
|
{
|
||||||
if (WriteCustomizations)
|
WriteCustomizations = true;
|
||||||
Customizations.Write(a.Address);
|
Load(new CharacterCustomization(a));
|
||||||
if (WriteEquipment != CharacterEquipMask.None)
|
|
||||||
Equipment.Write(a.Address, WriteEquipment, WriteEquipment);
|
Load(new CharacterEquipment(a));
|
||||||
a.SetWetness(IsWet);
|
|
||||||
a.Alpha() = Alpha;
|
SetHatState = true;
|
||||||
if ((_bytes[1] & 0b11100) == 0b11100)
|
SetVisorState = true;
|
||||||
{
|
SetWeaponState = true;
|
||||||
a.StateFlags() = StateFlags;
|
StateFlags = (byte)((a.IsHatVisible() ? 0x00 : 0x01) | (a.IsVisorToggled() ? 0x10 : 0x00) | (a.IsWeaponHidden() ? 0x02 : 0x00));
|
||||||
}
|
|
||||||
else
|
IsWet = a.IsWet();
|
||||||
{
|
Alpha = a.Alpha();
|
||||||
if (SetHatState)
|
}
|
||||||
a.SetHatHidden(HatState);
|
|
||||||
if (SetVisorState)
|
|
||||||
a.SetVisorToggled(VisorState);
|
public void Apply(Character a)
|
||||||
if (SetWeaponState)
|
{
|
||||||
a.SetWeaponHidden(WeaponState);
|
Glamourer.RevertableDesigns.Add(a);
|
||||||
}
|
|
||||||
}
|
if (WriteCustomizations)
|
||||||
|
Customizations.Write(a.Address);
|
||||||
public void ApplyOnlyEquipment(Character a)
|
if (WriteEquipment != CharacterEquipMask.None)
|
||||||
{
|
Equipment.Write(a.Address, WriteEquipment, WriteEquipment);
|
||||||
var oldState = _bytes[1];
|
a.SetWetness(IsWet);
|
||||||
WriteCustomizations = false;
|
a.Alpha() = Alpha;
|
||||||
SetHatState = false;
|
if (SetHatState)
|
||||||
SetVisorState = false;
|
a.SetHatVisible(!HatState);
|
||||||
SetWeaponState = false;
|
if (SetVisorState)
|
||||||
Apply(a);
|
a.SetVisorToggled(VisorState);
|
||||||
_bytes[1] = oldState;
|
if (SetWeaponState)
|
||||||
}
|
a.SetWeaponHidden(WeaponState);
|
||||||
|
}
|
||||||
public void ApplyOnlyCustomizations(Character a)
|
|
||||||
{
|
public void ApplyOnlyEquipment(Character a)
|
||||||
var oldState = _bytes[1];
|
{
|
||||||
SetHatState = false;
|
var oldState = _bytes[1];
|
||||||
SetVisorState = false;
|
WriteCustomizations = false;
|
||||||
SetWeaponState = false;
|
SetHatState = false;
|
||||||
var oldEquip = WriteEquipment;
|
SetVisorState = false;
|
||||||
WriteEquipment = CharacterEquipMask.None;
|
SetWeaponState = false;
|
||||||
Apply(a);
|
Apply(a);
|
||||||
_bytes[1] = oldState;
|
_bytes[1] = oldState;
|
||||||
WriteEquipment = oldEquip;
|
}
|
||||||
}
|
|
||||||
|
public void ApplyOnlyCustomizations(Character a)
|
||||||
public void Load(string base64)
|
{
|
||||||
{
|
var oldState = _bytes[1];
|
||||||
var bytes = Convert.FromBase64String(base64);
|
SetHatState = false;
|
||||||
switch (bytes[0])
|
SetVisorState = false;
|
||||||
{
|
SetWeaponState = false;
|
||||||
case 1:
|
var oldEquip = WriteEquipment;
|
||||||
CheckSize(bytes.Length, TotalSizeVersion1);
|
WriteEquipment = CharacterEquipMask.None;
|
||||||
CheckRange(2, bytes[1], 0, 1);
|
Apply(a);
|
||||||
Alpha = 1.0f;
|
_bytes[1] = oldState;
|
||||||
bytes[0] = CurrentVersion;
|
WriteEquipment = oldEquip;
|
||||||
break;
|
}
|
||||||
case 2:
|
|
||||||
CheckSize(bytes.Length, TotalSizeVersion2);
|
public void Load(string base64)
|
||||||
CheckRange(2, bytes[1], 0, 0x3F);
|
{
|
||||||
break;
|
var bytes = Convert.FromBase64String(base64);
|
||||||
default: throw new Exception($"Can not parse Base64 string into CharacterSave:\n\tInvalid Version {bytes[0]}.");
|
switch (bytes[0])
|
||||||
}
|
{
|
||||||
|
case 1:
|
||||||
CheckCharacterMask(bytes[2], bytes[3]);
|
CheckSize(bytes.Length, TotalSizeVersion1);
|
||||||
bytes.CopyTo(_bytes, 0);
|
CheckRange(2, bytes[1], 0, 1);
|
||||||
}
|
Alpha = 1.0f;
|
||||||
|
bytes[0] = CurrentVersion;
|
||||||
public static CharacterSave FromString(string base64)
|
break;
|
||||||
{
|
case 2:
|
||||||
var ret = new CharacterSave();
|
CheckSize(bytes.Length, TotalSizeVersion2);
|
||||||
ret.Load(base64);
|
CheckRange(2, bytes[1], 0, 0x3F);
|
||||||
return ret;
|
break;
|
||||||
}
|
default: throw new Exception($"Can not parse Base64 string into CharacterSave:\n\tInvalid Version {bytes[0]}.");
|
||||||
|
}
|
||||||
public unsafe ref CharacterCustomization Customizations
|
|
||||||
{
|
CheckCharacterMask(bytes[2], bytes[3]);
|
||||||
get
|
bytes.CopyTo(_bytes, 0);
|
||||||
{
|
}
|
||||||
fixed (byte* ptr = _bytes)
|
|
||||||
{
|
public static CharacterSave FromString(string base64)
|
||||||
return ref *(CharacterCustomization*) (ptr + 4);
|
{
|
||||||
}
|
var ret = new CharacterSave();
|
||||||
}
|
ret.Load(base64);
|
||||||
}
|
return ret;
|
||||||
|
}
|
||||||
public CharacterEquipment Equipment
|
|
||||||
{
|
public unsafe ref CharacterCustomization Customizations
|
||||||
get
|
{
|
||||||
{
|
get
|
||||||
var ret = new CharacterEquipment();
|
{
|
||||||
ret.FromBytes(_bytes, 4 + CharacterCustomization.CustomizationBytes);
|
fixed (byte* ptr = _bytes)
|
||||||
return ret;
|
{
|
||||||
}
|
return ref *(CharacterCustomization*)(ptr + 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CharacterEquipment Equipment
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var ret = new CharacterEquipment();
|
||||||
|
ret.FromBytes(_bytes, 4 + CharacterCustomization.CustomizationBytes);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,7 @@ namespace Glamourer.Gui
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (InputInt($"##text_{id}", ref current, 1, count))
|
if (InputInt($"##text_{id}", ref current, 1, count))
|
||||||
{
|
{
|
||||||
customization[id] = set.Data(id, current).Value;
|
customization[id] = (byte) current;
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -277,8 +277,9 @@ namespace Glamourer.Gui
|
||||||
var label = $"##fsPopup{child.FullName()}";
|
var label = $"##fsPopup{child.FullName()}";
|
||||||
if (ImGui.BeginPopup(label))
|
if (ImGui.BeginPopup(label))
|
||||||
{
|
{
|
||||||
if (ImGui.MenuItem("Delete"))
|
if (ImGui.MenuItem("Delete") && ImGui.GetIO().KeyCtrl && ImGui.GetIO().KeyShift)
|
||||||
_designs.DeleteAllChildren(child, false);
|
_designs.DeleteAllChildren(child, false);
|
||||||
|
ImGuiCustom.HoverTooltip("Hold Control and Shift to delete.");
|
||||||
|
|
||||||
RenameChildInput(child);
|
RenameChildInput(child);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ namespace Glamourer.Gui
|
||||||
ret |= DrawCheckMark("Hat Visible", save.HatState, v =>
|
ret |= DrawCheckMark("Hat Visible", save.HatState, v =>
|
||||||
{
|
{
|
||||||
save.HatState = v;
|
save.HatState = v;
|
||||||
player?.SetHatHidden(!v);
|
player?.SetHatVisible(v);
|
||||||
});
|
});
|
||||||
|
|
||||||
ret |= DrawCheckMark("Weapon Visible", save.WeaponState, v =>
|
ret |= DrawCheckMark("Weapon Visible", save.WeaponState, v =>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue