This commit is contained in:
Ottermandias 2022-07-31 11:23:38 +02:00
parent d7e61d9cfb
commit 15d93830a3
15 changed files with 244 additions and 418 deletions

View file

@ -1,162 +0,0 @@
using System;
using Glamourer.Customization;
using Penumbra.GameData.Enums;
namespace Glamourer;
public readonly unsafe struct CharacterCustomization
{
public static readonly CharacterCustomization Null = new(null);
private readonly CustomizationData* _data;
public IntPtr Address
=> (IntPtr)_data;
public CharacterCustomization(CustomizationData* data)
=> _data = data;
public void Load(CharacterCustomization other)
=> *_data = *other._data;
public ref Race Race
=> ref _data->Race;
public ref SubRace Clan
=> ref _data->Clan;
public Gender Gender
{
get => _data->Gender;
set => _data->Gender = value;
}
public ref byte BodyType
=> ref _data->BodyType;
public ref byte Height
=> ref _data->Height;
public ref byte Face
=> ref _data->Face;
public ref byte Hairstyle
=> ref _data->Hairstyle;
public bool HighlightsOn
{
get => _data->HighlightsOn;
set => _data->HighlightsOn = value;
}
public ref byte SkinColor
=> ref _data->SkinColor;
public ref byte EyeColorRight
=> ref _data->EyeColorRight;
public ref byte HairColor
=> ref _data->HairColor;
public ref byte HighlightsColor
=> ref _data->HighlightsColor;
public ref byte FacialFeatures
=> ref _data->FacialFeatures;
public ref byte TattooColor
=> ref _data->TattooColor;
public ref byte Eyebrow
=> ref _data->Eyebrow;
public ref byte EyeColorLeft
=> ref _data->EyeColorLeft;
public byte EyeShape
{
get => _data->EyeShape;
set => _data->EyeShape = value;
}
public byte FacePaint
{
get => _data->FacePaint;
set => _data->FacePaint = value;
}
public bool FacePaintReversed
{
get => _data->FacePaintReversed;
set => _data->FacePaintReversed = value;
}
public byte Mouth
{
get => _data->Mouth;
set => _data->Mouth = value;
}
public bool SmallIris
{
get => _data->SmallIris;
set => _data->SmallIris = value;
}
public bool Lipstick
{
get => _data->Lipstick;
set => _data->Lipstick = value;
}
public ref byte Nose
=> ref _data->Nose;
public ref byte Jaw
=> ref _data->Jaw;
public ref byte LipColor
=> ref _data->LipColor;
public ref byte MuscleMass
=> ref _data->MuscleMass;
public ref byte TailShape
=> ref _data->TailShape;
public ref byte BustSize
=> ref _data->BustSize;
public ref byte FacePaintColor
=> ref _data->FacePaintColor;
public bool FacialFeature(int idx)
=> _data->FacialFeature(idx);
public void FacialFeature(int idx, bool set)
=> _data->FacialFeature(idx, set);
public byte this[CustomizationId id]
{
get => _data->Get(id);
set => _data->Set(id, value);
}
public static implicit operator CharacterCustomization(CustomizationData* val)
=> new(val);
public static implicit operator CharacterCustomization(IntPtr val)
=> new((CustomizationData*)val);
public static implicit operator bool(CharacterCustomization customize)
=> customize._data != null;
public static bool operator true(CharacterCustomization customize)
=> customize._data != null;
public static bool operator false(CharacterCustomization customize)
=> customize._data == null;
public static bool operator !(CharacterCustomization customize)
=> customize._data == null;
}

View file

@ -1,168 +0,0 @@
using System;
using System.ComponentModel;
using Glamourer.Structs;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
namespace Glamourer;
public static class WriteExtensions
{
private static unsafe void Write(IntPtr characterPtr, EquipSlot slot, SetId? id, WeaponType? type, ushort? variant, StainId? stain)
{
void WriteWeapon(int offset)
{
var address = (byte*)characterPtr + offset;
if (id.HasValue)
*(ushort*)address = (ushort)id.Value;
if (type.HasValue)
*(ushort*)(address + 2) = (ushort)type.Value;
if (variant.HasValue)
*(ushort*)(address + 4) = variant.Value;
if (stain.HasValue)
*(address + 6) = (byte)stain.Value;
}
void WriteEquip(int offset)
{
var address = (uint*)characterPtr + offset;
if (id.HasValue)
*(ushort*)address = (ushort)id.Value;
if (variant < byte.MaxValue)
*(address + 2) = (byte)variant.Value;
if (stain.HasValue)
*(address + 3) = (byte)stain.Value;
}
switch (slot)
{
case EquipSlot.MainHand:
WriteWeapon(CharacterEquipment.MainWeaponOffset);
break;
case EquipSlot.OffHand:
WriteWeapon(CharacterEquipment.OffWeaponOffset);
break;
case EquipSlot.Head:
WriteEquip(CharacterEquipment.EquipmentOffset);
break;
case EquipSlot.Body:
WriteEquip(CharacterEquipment.EquipmentOffset + 4);
break;
case EquipSlot.Hands:
WriteEquip(CharacterEquipment.EquipmentOffset + 8);
break;
case EquipSlot.Legs:
WriteEquip(CharacterEquipment.EquipmentOffset + 12);
break;
case EquipSlot.Feet:
WriteEquip(CharacterEquipment.EquipmentOffset + 16);
break;
case EquipSlot.Ears:
WriteEquip(CharacterEquipment.EquipmentOffset + 20);
break;
case EquipSlot.Neck:
WriteEquip(CharacterEquipment.EquipmentOffset + 24);
break;
case EquipSlot.Wrists:
WriteEquip(CharacterEquipment.EquipmentOffset + 28);
break;
case EquipSlot.RFinger:
WriteEquip(CharacterEquipment.EquipmentOffset + 32);
break;
case EquipSlot.LFinger:
WriteEquip(CharacterEquipment.EquipmentOffset + 36);
break;
default: throw new InvalidEnumArgumentException();
}
}
public static void Write(this Stain stain, IntPtr characterPtr, EquipSlot slot)
=> Write(characterPtr, slot, null, null, null, stain.RowIndex);
public static void Write(this Item item, IntPtr characterAddress)
{
var (id, type, variant) = item.MainModel;
Write(characterAddress, item.EquippableTo, id, type, variant, null);
if (item.EquippableTo == EquipSlot.MainHand && item.HasSubModel)
{
var (subId, subType, subVariant) = item.SubModel;
Write(characterAddress, EquipSlot.OffHand, subId, subType, subVariant, null);
}
}
public static void Write(this CharacterArmor armor, IntPtr characterAddress, EquipSlot slot)
=> Write(characterAddress, slot, armor.Set, null, armor.Variant, armor.Stain);
public static void Write(this CharacterWeapon weapon, IntPtr characterAddress, EquipSlot slot)
=> Write(characterAddress, slot, weapon.Set, weapon.Type, weapon.Variant, weapon.Stain);
public static unsafe void Write(this CharacterEquipment equip, IntPtr characterAddress)
{
if (equip.IsSet == 0)
return;
Write(characterAddress, EquipSlot.MainHand, equip.MainHand.Set, equip.MainHand.Type, equip.MainHand.Variant, equip.MainHand.Stain);
Write(characterAddress, EquipSlot.OffHand, equip.OffHand.Set, equip.OffHand.Type, equip.OffHand.Variant, equip.OffHand.Stain);
fixed (CharacterArmor* equipment = &equip.Head)
{
Buffer.MemoryCopy(equipment, (byte*)characterAddress + CharacterEquipment.EquipmentOffset,
CharacterEquipment.EquipmentSlots * sizeof(CharacterArmor), CharacterEquipment.EquipmentSlots * sizeof(CharacterArmor));
}
}
public static void Write(this CharacterEquipment equip, IntPtr characterAddress, CharacterEquipMask models, CharacterEquipMask stains)
{
if (models == CharacterEquipMask.All && stains == CharacterEquipMask.All)
{
equip.Write(characterAddress);
return;
}
if (models.HasFlag(CharacterEquipMask.MainHand))
Write(characterAddress, EquipSlot.MainHand, equip.MainHand.Set, equip.MainHand.Type, equip.MainHand.Variant, null);
if (stains.HasFlag(CharacterEquipMask.MainHand))
Write(characterAddress, EquipSlot.MainHand, null, null, null, equip.MainHand.Stain);
if (models.HasFlag(CharacterEquipMask.OffHand))
Write(characterAddress, EquipSlot.OffHand, equip.OffHand.Set, equip.OffHand.Type, equip.OffHand.Variant, null);
if (stains.HasFlag(CharacterEquipMask.OffHand))
Write(characterAddress, EquipSlot.OffHand, null, null, null, equip.OffHand.Stain);
if (models.HasFlag(CharacterEquipMask.Head))
Write(characterAddress, EquipSlot.Head, equip.Head.Set, null, equip.Head.Variant, null);
if (stains.HasFlag(CharacterEquipMask.Head))
Write(characterAddress, EquipSlot.Head, null, null, null, equip.Head.Stain);
if (models.HasFlag(CharacterEquipMask.Body))
Write(characterAddress, EquipSlot.Body, equip.Body.Set, null, equip.Body.Variant, null);
if (stains.HasFlag(CharacterEquipMask.Body))
Write(characterAddress, EquipSlot.Body, null, null, null, equip.Body.Stain);
if (models.HasFlag(CharacterEquipMask.Hands))
Write(characterAddress, EquipSlot.Hands, equip.Hands.Set, null, equip.Hands.Variant, null);
if (stains.HasFlag(CharacterEquipMask.Hands))
Write(characterAddress, EquipSlot.Hands, null, null, null, equip.Hands.Stain);
if (models.HasFlag(CharacterEquipMask.Legs))
Write(characterAddress, EquipSlot.Legs, equip.Legs.Set, null, equip.Legs.Variant, null);
if (stains.HasFlag(CharacterEquipMask.Legs))
Write(characterAddress, EquipSlot.Legs, null, null, null, equip.Legs.Stain);
if (models.HasFlag(CharacterEquipMask.Feet))
Write(characterAddress, EquipSlot.Feet, equip.Feet.Set, null, equip.Feet.Variant, null);
if (stains.HasFlag(CharacterEquipMask.Feet))
Write(characterAddress, EquipSlot.Feet, null, null, null, equip.Feet.Stain);
if (models.HasFlag(CharacterEquipMask.Ears))
Write(characterAddress, EquipSlot.Ears, equip.Ears.Set, null, equip.Ears.Variant, null);
if (models.HasFlag(CharacterEquipMask.Neck))
Write(characterAddress, EquipSlot.Neck, equip.Neck.Set, null, equip.Neck.Variant, null);
if (models.HasFlag(CharacterEquipMask.Wrists))
Write(characterAddress, EquipSlot.Wrists, equip.Wrists.Set, null, equip.Wrists.Variant, null);
if (models.HasFlag(CharacterEquipMask.LFinger))
Write(characterAddress, EquipSlot.LFinger, equip.LFinger.Set, null, equip.LFinger.Variant, null);
if (models.HasFlag(CharacterEquipMask.RFinger))
Write(characterAddress, EquipSlot.RFinger, equip.RFinger.Set, null, equip.RFinger.Variant, null);
}
}

View file

@ -4,11 +4,11 @@ using Penumbra.GameData.Structs;
namespace Glamourer.Customization;
public unsafe ref struct Customize
public unsafe struct Customize
{
private readonly CustomizeData* _data;
private Customize(CustomizeData* data)
public Customize(CustomizeData* data)
=> _data = data;
public Race Race
@ -70,7 +70,7 @@ public unsafe ref struct Customize
public bool this[int idx]
{
get => (*_bitfield & (1 << idx)) != 0;
set => *_bitfield = (byte)(value ? *_bitfield | (1 << idx) : *_bitfield & ~(1 << idx));
set => Set(idx, value);
}
public void Clear()
@ -78,6 +78,9 @@ public unsafe ref struct Customize
public void All()
=> *_bitfield = 0xFF;
public void Set(int idx, bool value)
=> *_bitfield = (byte)(value ? *_bitfield | (1 << idx) : *_bitfield & ~(1 << idx));
}
public FacialFeatureStruct FacialFeatures
@ -149,8 +152,8 @@ public unsafe ref struct Customize
public ref byte FacePaintColor
=> ref _data->Data[25];
internal static readonly CustomizeData Default = GenerateDefault();
internal static readonly CustomizeData Empty = new();
public static readonly CustomizeData Default = GenerateDefault();
public static readonly CustomizeData Empty = new();
public byte Get(CustomizationId id)
=> id switch
@ -263,4 +266,7 @@ public unsafe ref struct Customize
return ret;
}
public void Load(Customize other)
=> _data->Read(other._data);
}

View file

@ -39,7 +39,7 @@ public class CustomizationSet
public int NumJawShapes { get; internal init; }
public int NumMouthShapes { get; internal init; }
public string ToHumanReadable(CustomizationData customizationData)
public string ToHumanReadable(Customize customizationData)
{
var sb = new StringBuilder();
foreach (var id in Enum.GetValues<CustomizationId>().Where(IsAvailable))

Binary file not shown.

View file

@ -8,7 +8,119 @@ namespace Glamourer;
public unsafe struct Actor : IEquatable<Actor>
{
public record struct Identifier(Utf8String Name, uint Id, ushort HomeWorld, ushort Index);
public interface IIdentifier : IEquatable<IIdentifier>
{
Utf8String Name { get; }
public IIdentifier CreatePermanent();
}
public class InvalidIdentifier : IIdentifier
{
public Utf8String Name
=> Utf8String.Empty;
public bool Equals(IIdentifier? other)
=> false;
public override int GetHashCode()
=> 0;
public override string ToString()
=> "Invalid";
public IIdentifier CreatePermanent()
=> this;
}
public class PlayerIdentifier : IIdentifier
{
public Utf8String Name { get; }
public readonly ushort HomeWorld;
public PlayerIdentifier(Utf8String name, ushort homeWorld)
{
Name = name;
HomeWorld = homeWorld;
}
public bool Equals(IIdentifier? other)
=> other is PlayerIdentifier p && p.HomeWorld == HomeWorld && p.Name.Equals(Name);
public override int GetHashCode()
=> HashCode.Combine(Name.Crc32, HomeWorld);
public override string ToString()
=> $"{Name} ({HomeWorld})";
public IIdentifier CreatePermanent()
=> new PlayerIdentifier(Name.Clone(), HomeWorld);
}
public class OwnedIdentifier : IIdentifier
{
public Utf8String Name { get; }
public readonly Utf8String OwnerName;
public readonly uint DataId;
public readonly ushort OwnerHomeWorld;
public readonly ObjectKind Kind;
public OwnedIdentifier(Utf8String name, Utf8String ownerName, ushort ownerHomeWorld, uint dataId, ObjectKind kind)
{
Name = name;
OwnerName = ownerName;
OwnerHomeWorld = ownerHomeWorld;
DataId = dataId;
Kind = kind;
}
public bool Equals(IIdentifier? other)
=> other is OwnedIdentifier p
&& p.DataId == DataId
&& p.OwnerHomeWorld == OwnerHomeWorld
&& p.Kind == Kind
&& p.OwnerName.Equals(OwnerName);
public override int GetHashCode()
=> HashCode.Combine(OwnerName.Crc32, OwnerHomeWorld, DataId, Kind);
public override string ToString()
=> $"{OwnerName}s {Name}";
public IIdentifier CreatePermanent()
=> new OwnedIdentifier(Name.Clone(), OwnerName.Clone(), OwnerHomeWorld, DataId, Kind);
}
public class NpcIdentifier : IIdentifier
{
public Utf8String Name { get; }
public readonly uint DataId;
public readonly ushort ObjectIndex;
public NpcIdentifier(Utf8String actorName, ushort objectIndex = ushort.MaxValue, uint dataId = uint.MaxValue)
{
Name = actorName;
ObjectIndex = objectIndex;
DataId = dataId;
}
public bool Equals(IIdentifier? other)
=> other is NpcIdentifier p
&& p.Name.Equals(Name)
&& (p.DataId == uint.MaxValue || DataId == uint.MaxValue || p.DataId == DataId)
&& (p.ObjectIndex == ushort.MaxValue || ObjectIndex == ushort.MaxValue || p.ObjectIndex == ObjectIndex);
public override int GetHashCode()
=> Name.Crc32;
public override string ToString()
=> DataId == uint.MaxValue ? ObjectIndex == ushort.MaxValue ? Name.ToString() : $"{Name} at {ObjectIndex}" :
ObjectIndex == ushort.MaxValue ? $"{Name} ({DataId})" : $"{Name} ({DataId}) at {ObjectIndex}";
public IIdentifier CreatePermanent()
=> new NpcIdentifier(Name.Clone(), ObjectIndex, DataId);
}
public static readonly Actor Null = new() { Pointer = null };
@ -23,13 +135,8 @@ public unsafe struct Actor : IEquatable<Actor>
public static implicit operator IntPtr(Actor actor)
=> actor.Pointer == null ? IntPtr.Zero : (IntPtr)actor.Pointer;
public Identifier GetIdentifier()
{
if (Pointer == null)
return new Identifier(Utf8String.Empty, 0, 0, 0);
return new Identifier(Utf8Name, Pointer->GameObject.ObjectID, Pointer->HomeWorld, Pointer->GameObject.ObjectIndex);
}
public IIdentifier GetIdentifier()
=> CreateIdentifier(this);
public Character? Character
=> Pointer == null ? null : Dalamud.Objects[Pointer->GameObject.ObjectIndex] as Character;
@ -40,11 +147,14 @@ public unsafe struct Actor : IEquatable<Actor>
public bool IsHuman
=> Pointer != null && Pointer->ModelCharaId == 0;
public int ModelId
=> Pointer->ModelCharaId;
public ref int ModelId
=> ref Pointer->ModelCharaId;
public ObjectKind ObjectKind
=> (ObjectKind)Pointer->GameObject.ObjectKind;
{
get => (ObjectKind) Pointer->GameObject.ObjectKind;
set => Pointer->GameObject.ObjectKind = (byte)value;
}
public Utf8String Utf8Name
=> new(Pointer->GameObject.Name);
@ -84,4 +194,48 @@ public unsafe struct Actor : IEquatable<Actor>
public static bool operator !=(Actor lhs, Actor rhs)
=> lhs.Pointer != rhs.Pointer;
private static IIdentifier CreateIdentifier(Actor actor)
{
switch (actor.ObjectKind)
{
case ObjectKind.Player: return new PlayerIdentifier(actor.Utf8Name, actor.Pointer->HomeWorld);
case ObjectKind.BattleNpc:
{
var ownerId = actor.Pointer->GameObject.OwnerID;
if (ownerId != 0xE0000000)
{
var owner = (Actor)Dalamud.Objects.SearchById(ownerId)?.Address;
if (!owner)
return new InvalidIdentifier();
return new OwnedIdentifier(actor.Utf8Name, owner.Utf8Name, owner.Pointer->HomeWorld,
actor.Pointer->GameObject.DataID, ObjectKind.BattleNpc);
}
return new NpcIdentifier(actor.Utf8Name, actor.Pointer->GameObject.ObjectIndex,
actor.Pointer->GameObject.DataID);
}
case ObjectKind.Retainer:
case ObjectKind.EventNpc:
return new NpcIdentifier(actor.Utf8Name, actor.Pointer->GameObject.ObjectIndex,
actor.Pointer->GameObject.DataID);
case ObjectKind.MountType:
case ObjectKind.Companion:
{
var idx = actor.Pointer->GameObject.ObjectIndex;
if (idx % 2 == 0)
return new InvalidIdentifier();
var owner = (Actor)Dalamud.Objects[idx - 1]?.Address;
if (!owner)
return new InvalidIdentifier();
return new OwnedIdentifier(actor.Utf8Name, owner.Utf8Name, owner.Pointer->HomeWorld,
actor.Pointer->GameObject.DataID, actor.ObjectKind);
}
default: return new InvalidIdentifier();
}
}
}

View file

@ -2,7 +2,6 @@
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Logging;
using Dalamud.Plugin.Ipc;
using Glamourer.Structs;
using ImGuiNET;
using Penumbra.GameData.Enums;

View file

@ -44,9 +44,9 @@ public unsafe struct CharacterData
VisorState = 0x80,
}
public const byte TotalSizeVersion1 = 1 + 1 + 2 + 56 + CustomizationData.CustomizationBytes;
public const byte TotalSizeVersion2 = 1 + 1 + 2 + 56 + CustomizationData.CustomizationBytes + 4 + 1;
public const byte TotalSizeVersion3 = 1 + 1 + 2 + 7 + 7 + 2 + 40 + CustomizationData.CustomizationBytes + 4;
public const byte TotalSizeVersion1 = 1 + 1 + 2 + 56 + CustomizeData.Size;
public const byte TotalSizeVersion2 = 1 + 1 + 2 + 56 + CustomizeData.Size + 4 + 1;
public const byte TotalSizeVersion3 = 1 + 1 + 2 + 7 + 7 + 2 + 40 + CustomizeData.Size + 4;
public const byte CurrentVersion = 3;
public byte Version;
@ -65,16 +65,16 @@ public unsafe struct CharacterData
public CharacterArmor Wrist;
public CharacterArmor RFinger;
public CharacterArmor LFinger;
private CustomizationData CustomizationData;
private CustomizeData _customizeData;
public float Alpha;
public CharacterCustomization Customize
public Customize Customize
{
get
{
fixed (CustomizationData* ptr = &CustomizationData)
fixed (CustomizeData* ptr = &_customizeData)
{
return new CharacterCustomization(ptr);
return new Customize(ptr);
}
}
}
@ -93,24 +93,24 @@ public unsafe struct CharacterData
public static readonly CharacterData Default
= new()
{
Version = CurrentVersion,
Flags = SaveFlags.WriteCustomizations,
Equip = CharacterEquipMask.All,
MainHand = CharacterWeapon.Empty,
OffHand = CharacterWeapon.Empty,
Padding = 0,
Head = CharacterArmor.Empty,
Body = CharacterArmor.Empty,
Hands = CharacterArmor.Empty,
Legs = CharacterArmor.Empty,
Feet = CharacterArmor.Empty,
Ears = CharacterArmor.Empty,
Neck = CharacterArmor.Empty,
Wrist = CharacterArmor.Empty,
RFinger = CharacterArmor.Empty,
LFinger = CharacterArmor.Empty,
CustomizationData = CustomizationData.Default,
Alpha = 1f,
Version = CurrentVersion,
Flags = SaveFlags.WriteCustomizations,
Equip = CharacterEquipMask.All,
MainHand = CharacterWeapon.Empty,
OffHand = CharacterWeapon.Empty,
Padding = 0,
Head = CharacterArmor.Empty,
Body = CharacterArmor.Empty,
Hands = CharacterArmor.Empty,
Legs = CharacterArmor.Empty,
Feet = CharacterArmor.Empty,
Ears = CharacterArmor.Empty,
Neck = CharacterArmor.Empty,
Wrist = CharacterArmor.Empty,
RFinger = CharacterArmor.Empty,
LFinger = CharacterArmor.Empty,
_customizeData = Customize.Default,
Alpha = 1f,
};
public void Load(Actor actor)
@ -119,7 +119,7 @@ public unsafe struct CharacterData
return;
var human = (Human*)actor.Pointer->GameObject.DrawObject;
CustomizationData = *(CustomizationData*)human->CustomizeData;
_customizeData.Read(human->CustomizeData);
fixed (void* equip = &Head)
{
Functions.MemCpyUnchecked(equip, human->EquipSlotData, sizeof(CharacterArmor) * 10);
@ -204,7 +204,7 @@ public class CharacterSave
public string ToBase64()
=> _data.ToBase64();
public CharacterCustomization Customization
public Customize Customize
=> _data.Customize;
public CharacterEquip Equipment

View file

@ -52,6 +52,6 @@ public static unsafe class CustomizeExtensions
};
}
public static string ClanName(this CharacterCustomization customize)
public static string ClanName(this Customize customize)
=> ClanName(customize.Clan, customize.Gender);
}

View file

@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Logging;
using Glamourer.Structs;
namespace Glamourer.Designs;

View file

@ -116,7 +116,7 @@
</None>
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="if $(Configuration) == Release powershell Compress-Archive -Force $(TargetPath), $(TargetDir)$(SolutionName).json, $(TargetDir)$(SolutionName).GameData.dll, $(TargetDir)Penumbra.GameData.dll, $(TargetDir)Penumbra.PlayerWatch.dll $(SolutionDir)$(SolutionName).zip" />
<Exec Command="if $(Configuration) == Release powershell Compress-Archive -Force $(TargetPath), $(TargetDir)$(SolutionName).json, $(TargetDir)$(SolutionName).GameData.dll, $(TargetDir)Penumbra.GameData.dll $(SolutionDir)$(SolutionName).zip" />
<Exec Command="if $(Configuration) == Release powershell Copy-Item -Force $(TargetDir)$(SolutionName).json -Destination $(SolutionDir)" />
</Target>
</Project>

View file

@ -14,7 +14,7 @@ internal partial class Interface
{
private class ActorTab
{
private ObjectManager.ActorData _data = new(string.Empty, new Actor.Identifier(), Actor.Null, false, Actor.Null);
private ObjectManager.ActorData _data = new(string.Empty, new Actor.InvalidIdentifier(), Actor.Null, false, Actor.Null);
private Actor _nextSelect = Actor.Null;
public void Draw()
@ -41,7 +41,7 @@ internal partial class Interface
if (!Glamourer.RedrawManager.CurrentManipulations.GetSave(_data.Actor, out var save))
return;
if (DrawCustomization(save.Customization, save.Equipment, !_data.Modifiable))
if (DrawCustomization(save.Customize, save.Equipment, !_data.Modifiable))
{
//Glamourer.RedrawManager.Set(_data.Actor.Address, _character);
Glamourer.Penumbra.RedrawObject(_data.Actor.Character, RedrawType.Redraw, true);
@ -70,9 +70,10 @@ internal partial class Interface
foreach (var (id, data) in models.Models)
{
if (ImGui.Selectable(data.FirstName, id == currentModel) || id == currentModel)
if (ImGui.Selectable(data.FirstName, id == currentModel) && id != currentModel)
{
_data.Actor.SetModelId((int)id);
_data.Actor.ObjectKind =
Glamourer.Penumbra.RedrawObject(_data.Actor.Character, RedrawType.Redraw, true);
}
ImGuiUtil.HoverTooltip(data.AllNames);

View file

@ -8,6 +8,7 @@ using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Race = Penumbra.GameData.Enums.Race;
namespace Glamourer.Gui;
@ -17,7 +18,7 @@ internal partial class Interface
private static byte _tempStorage;
private static CustomizationId _tempType;
private static bool DrawCustomization(CharacterCustomization customize, CharacterEquip equip, bool locked)
private static bool DrawCustomization(Customize customize, CharacterEquip equip, bool locked)
{
if (!ImGui.CollapsingHeader("Character Customization"))
return false;
@ -56,7 +57,7 @@ internal partial class Interface
return ret;
}
private static bool DrawRaceGenderSelector(CharacterCustomization customize, CharacterEquip equip, bool locked)
private static bool DrawRaceGenderSelector(Customize customize, CharacterEquip equip, bool locked)
{
var ret = DrawGenderSelector(customize, equip, locked);
ImGui.SameLine();
@ -68,7 +69,7 @@ internal partial class Interface
return ret;
}
private static bool DrawGenderSelector(CharacterCustomization customize, CharacterEquip equip, bool locked)
private static bool DrawGenderSelector(Customize customize, CharacterEquip equip, bool locked)
{
using var font = ImRaii.PushFont(UiBuilder.IconFont);
var icon = customize.Gender == Gender.Male ? FontAwesomeIcon.Mars : FontAwesomeIcon.Venus;
@ -83,7 +84,7 @@ internal partial class Interface
return false; //customize.ChangeGender(gender, locked ? CharacterEquip.Null : equip);
}
private static bool DrawRaceCombo(CharacterCustomization customize, CharacterEquip equip, bool locked)
private static bool DrawRaceCombo(Customize customize, CharacterEquip equip, bool locked)
{
using var alpha = ImRaii.PushStyle(ImGuiStyleVar.Alpha, 0.5f, locked);
ImGui.SetNextItemWidth(_raceSelectorWidth);
@ -123,7 +124,7 @@ internal partial class Interface
return ret;
}
private static bool PercentageSelector(CustomizationSet set, CustomizationId id, CharacterCustomization customization, bool locked)
private static bool PercentageSelector(CustomizationSet set, CustomizationId id, Customize customization, bool locked)
{
using var bigGroup = ImRaii.Group();
using var _ = ImRaii.PushId((int)id);
@ -175,7 +176,7 @@ internal partial class Interface
return ret;
}
private static bool DrawIconSelector(CustomizationSet set, CustomizationId id, CharacterCustomization customize, bool locked)
private static bool DrawIconSelector(CustomizationSet set, CustomizationId id, Customize customize, bool locked)
{
const string popupName = "Style Picker";
@ -213,7 +214,7 @@ internal partial class Interface
return ret;
}
private static bool DrawIconPickerPopup(string label, CustomizationSet set, CustomizationId id, CharacterCustomization customize)
private static bool DrawIconPickerPopup(string label, CustomizationSet set, CustomizationId id, Customize customize)
{
using var popup = ImRaii.Popup(label, ImGuiWindowFlags.AlwaysAutoResize);
if (!popup)
@ -250,7 +251,7 @@ internal partial class Interface
return ret;
}
private static bool DrawColorPicker(CustomizationSet set, CustomizationId id, CharacterCustomization customize, bool locked)
private static bool DrawColorPicker(CustomizationSet set, CustomizationId id, Customize customize, bool locked)
{
const string popupName = "Color Picker";
using var _ = ImRaii.PushId((int)id);
@ -282,7 +283,7 @@ internal partial class Interface
}
private static (int, Customization.Customization) GetCurrentCustomization(CustomizationSet set, CustomizationId id,
CharacterCustomization customize)
Customize customize)
{
var current = set.DataByValue(id, customize[id], out var custom);
if (set.IsAvailable(id) && current < 0)
@ -295,7 +296,7 @@ internal partial class Interface
return (current, custom!.Value);
}
private static bool DrawColorPickerPopup(string label, CustomizationSet set, CustomizationId id, CharacterCustomization customize)
private static bool DrawColorPickerPopup(string label, CustomizationSet set, CustomizationId id, Customize customize)
{
using var popup = ImRaii.Popup(label, ImGuiWindowFlags.AlwaysAutoResize);
if (!popup)
@ -322,7 +323,7 @@ internal partial class Interface
return ret;
}
private static bool DrawMultiIconSelector(CustomizationSet set, CharacterCustomization customize, bool locked)
private static bool DrawMultiIconSelector(CustomizationSet set, Customize customize, bool locked)
{
using var bigGroup = ImRaii.Group();
using var _ = ImRaii.PushId((int)CustomizationId.FacialFeaturesTattoos);
@ -344,7 +345,7 @@ internal partial class Interface
return ret;
}
private static bool DrawMultiIcons(CustomizationSet set, CharacterCustomization customize, bool locked)
private static bool DrawMultiIcons(CustomizationSet set, Customize customize, bool locked)
{
using var _ = ImRaii.Group();
var face = customize.Face;
@ -355,7 +356,7 @@ internal partial class Interface
var count = set.Count(CustomizationId.FacialFeaturesTattoos);
for (var i = 0; i < count; ++i)
{
var enabled = customize.FacialFeature(i);
var enabled = customize.FacialFeatures[i];
var feature = set.FacialFeature(face, i);
var icon = i == count - 1
? LegacyTattoo ?? Glamourer.Customization.GetIcon(feature.IconId)
@ -364,7 +365,7 @@ internal partial class Interface
Vector4.Zero, enabled ? Vector4.One : RedTint)
&& !locked)
{
customize.FacialFeature(i, !enabled);
customize.FacialFeatures.Set(i, !enabled);
ret = true;
}
@ -378,7 +379,7 @@ internal partial class Interface
return ret;
}
private static bool DrawListSelector(CustomizationSet set, CustomizationId id, CharacterCustomization customize, bool locked)
private static bool DrawListSelector(CustomizationSet set, CustomizationId id, Customize customize, bool locked)
{
using var _ = ImRaii.PushId((int)id);
using var bigGroup = ImRaii.Group();

View file

@ -13,7 +13,7 @@ public static class ObjectManager
private const int DyePreviewIndex = 243;
private static readonly Dictionary<Utf8String, int> NameCounters = new();
private static readonly Dictionary<Actor.Identifier, Actor> GPoseActors = new(CharacterScreenIndex - GPosePlayerIndex);
private static readonly Dictionary<Actor.IIdentifier, Actor> GPoseActors = new(CharacterScreenIndex - GPosePlayerIndex);
public static bool IsInGPose()
=> Dalamud.Objects[GPosePlayerIndex] != null;
@ -24,7 +24,7 @@ public static class ObjectManager
public static Actor Player
=> Dalamud.ClientState.LocalPlayer?.Address;
public record struct ActorData(string Label, Actor.Identifier Identifier, Actor Actor, bool Modifiable, Actor GPose);
public record struct ActorData(string Label, Actor.IIdentifier Identifier, Actor Actor, bool Modifiable, Actor GPose);
public static IEnumerable<ActorData> GetEnumerator()
{
@ -38,7 +38,7 @@ public static class ObjectManager
var identifier = character.GetIdentifier();
GPoseActors[identifier] = character.Address;
yield return new ActorData(GetLabel(character, identifier.Name.ToString(), 0, true), identifier, character.Address, true,
yield return new ActorData(GetLabel(character, character.Utf8Name.ToString(), 0, true), identifier, character.Address, true,
Actor.Null);
}
@ -67,7 +67,7 @@ public static class ObjectManager
continue;
var identifier = actor.GetIdentifier();
if (identifier.Name.Length == 0)
if (actor.Utf8Name.Length == 0)
continue;
if (NameCounters.TryGetValue(identifier.Name, out var num))

View file

@ -14,16 +14,16 @@ namespace Glamourer;
public class CurrentManipulations
{
private readonly RestrictedGear _restrictedGear = GameData.RestrictedGear(Dalamud.GameData);
private readonly Dictionary<Actor.Identifier, CharacterSave> _characterSaves = new();
private readonly Dictionary<Actor.IIdentifier, CharacterSave> _characterSaves = new();
public CharacterSave CreateSave(Actor actor)
{
var id = actor.GetIdentifier();
if (_characterSaves.TryGetValue(actor.GetIdentifier(), out var save))
if (_characterSaves.TryGetValue(id, out var save))
return save;
save = new CharacterSave(actor);
_characterSaves.Add(id with { Name = id.Name.Clone() }, save);
_characterSaves.Add(id.CreatePermanent(), save);
return save;
}
@ -33,13 +33,13 @@ public class CurrentManipulations
return actor && _characterSaves.TryGetValue(actor.GetIdentifier(), out save);
}
public bool GetSave(Actor.Identifier identifier, [NotNullWhen(true)] out CharacterSave? save)
public bool GetSave(Actor.IIdentifier identifier, [NotNullWhen(true)] out CharacterSave? save)
=> _characterSaves.TryGetValue(identifier, out save);
public CharacterArmor? ChangeEquip(Actor actor, EquipSlot slot, CharacterArmor data)
{
var save = CreateSave(actor);
(_, data) = _restrictedGear.ResolveRestricted(data, slot, save.Customization.Race, save.Customization.Gender);
(_, data) = _restrictedGear.ResolveRestricted(data, slot, save.Customize.Race, save.Customize.Gender);
if (save.Equipment[slot] == data)
return null;
@ -68,11 +68,11 @@ public class CurrentManipulations
return true;
}
public void ChangeCustomization(Actor actor, CharacterCustomization customize)
public void ChangeCustomization(Actor actor, Customize customize)
{
var save = CreateSave(actor);
FixRestrictedGear(save, customize.Gender, customize.Race);
save.Customization.Load(customize);
save.Customize.Load(customize);
}
public bool ChangeCustomization(Actor actor, CustomizationId id, byte value)
@ -83,7 +83,7 @@ public class CurrentManipulations
return ChangeGender(actor, (Gender)value);
var save = CreateSave(actor);
var customize = save.Customization;
var customize = save.Customize;
if (customize[id] != value)
return false;
@ -95,10 +95,10 @@ public class CurrentManipulations
public bool ChangeGender(Actor actor, Gender gender)
{
var save = CreateSave(actor);
if (save.Customization.Gender == gender)
if (save.Customize.Gender == gender)
return false;
var customize = save.Customization;
var customize = save.Customize;
FixRestrictedGear(save, gender, customize.Race);
FixUpAttributes(customize);
return true;
@ -108,10 +108,10 @@ public class CurrentManipulations
public bool ChangeRace(Actor actor, SubRace clan)
{
var save = CreateSave(actor);
if (save.Customization.Clan == clan)
if (save.Customize.Clan == clan)
return false;
var customize = save.Customization;
var customize = save.Customize;
var race = clan.ToRace();
var gender = race == Race.Hrothgar ? Gender.Male : customize.Gender; // TODO Female Hrothgar
FixRestrictedGear(save, gender, race);
@ -124,7 +124,7 @@ public class CurrentManipulations
}
// Go through a whole customization struct and fix up all settings that need fixing.
private void FixUpAttributes(CharacterCustomization customize)
private void FixUpAttributes(Customize customize)
{
var set = Glamourer.Customization.GetList(customize.Clan, customize.Gender);
foreach (CustomizationId id in Enum.GetValues(typeof(CustomizationId)))
@ -149,7 +149,7 @@ public class CurrentManipulations
private void FixRestrictedGear(CharacterSave save, Gender gender, Race race)
{
if (race == save.Customization.Race && gender == save.Customization.Gender)
if (race == save.Customize.Race && gender == save.Customize.Gender)
return;
var equip = save.Equipment;